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        Err(format!("raw_copy_bytes: {r:?}").into())
267    }
268}
269
270pub fn step_expert_activation_host(gate: f32, up: f32, limit: Option<f32>) -> f32 {
271    let silu = gate / (1.0 + (-gate).exp());
272    match limit {
273        Some(limit) => silu.min(limit) * up.clamp(-limit, limit),
274        None => silu * up,
275    }
276}
277
278#[derive(Debug, Clone, PartialEq, Eq)]
279struct ExpertOwnerRoutes {
280    rank: usize,
281    selected: Vec<usize>,
282    token_rows: Vec<usize>,
283    global_pairs: Vec<usize>,
284}
285
286fn partition_expert_owner_routes(
287    expert_count: usize,
288    ranks: usize,
289    tokens: usize,
290    experts_per_token: usize,
291    selected: &[usize],
292) -> Result<Vec<ExpertOwnerRoutes>, String> {
293    if expert_count == 0
294        || ranks == 0
295        || tokens == 0
296        || experts_per_token == 0
297        || expert_count % ranks != 0
298    {
299        return Err(format!(
300            "invalid expert-owner route geometry experts={expert_count} ranks={ranks} \
301             tokens={tokens} experts_per_token={experts_per_token}"
302        ));
303    }
304    let pairs = tokens
305        .checked_mul(experts_per_token)
306        .ok_or("expert-owner route count overflow")?;
307    if selected.len() != pairs {
308        return Err(format!(
309            "expert-owner routes {} != {tokens}x{experts_per_token} ({pairs})",
310            selected.len()
311        ));
312    }
313    let per_rank = expert_count / ranks;
314    let mut owners = (0..ranks)
315        .map(|rank| ExpertOwnerRoutes {
316            rank,
317            selected: Vec::new(),
318            token_rows: Vec::new(),
319            global_pairs: Vec::new(),
320        })
321        .collect::<Vec<_>>();
322    for (pair, &expert) in selected.iter().enumerate() {
323        if expert >= expert_count {
324            return Err(format!(
325                "expert-owner route {pair} selects expert {expert} outside 0..{expert_count}"
326            ));
327        }
328        let rank = expert / per_rank;
329        owners[rank].selected.push(expert - rank * per_rank);
330        owners[rank].token_rows.push(pair / experts_per_token);
331        owners[rank].global_pairs.push(pair);
332    }
333    Ok(owners)
334}
335
336fn validate_step_grouped_owner_routes(
337    expert_count: usize,
338    tokens: usize,
339    selected: &[usize],
340) -> Result<usize, String> {
341    if expert_count != STEP_GROUPED_FP8_EXPERTS || tokens == 0 {
342        return Err(format!(
343            "official Step owner-grouped FP8 requires {} experts and nonzero tokens, got \
344             experts={expert_count} tokens={tokens}",
345            STEP_GROUPED_FP8_EXPERTS
346        ));
347    }
348    let pairs = tokens
349        .checked_mul(STEP_GROUPED_FP8_TOP_K)
350        .ok_or("official Step owner-grouped FP8 route count overflow")?;
351    if selected.len() != pairs {
352        return Err(format!(
353            "official Step owner-grouped FP8 routes {} != {tokens}x{} ({pairs})",
354            selected.len(),
355            STEP_GROUPED_FP8_TOP_K,
356        ));
357    }
358    for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
359        let mut unique = routes.to_vec();
360        unique.sort_unstable();
361        unique.dedup();
362        if unique.len() != STEP_GROUPED_FP8_TOP_K {
363            return Err(format!(
364                "official Step owner-grouped FP8 token {token} routes are not top-8 unique: \
365                 {routes:?}"
366            ));
367        }
368    }
369    Ok(pairs)
370}
371
372#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373struct WeightedRouteCombineShape {
374    pairs: usize,
375    max_pairs: usize,
376}
377
378fn validate_weighted_route_combine(
379    width: usize,
380    experts_per_token: usize,
381    max_tokens: usize,
382    tokens: usize,
383    owner_global_pairs: &[&[usize]],
384    route_weights: &[f32],
385) -> Result<WeightedRouteCombineShape, String> {
386    if width == 0
387        || experts_per_token == 0
388        || max_tokens == 0
389        || tokens == 0
390        || tokens > max_tokens
391        || width > i32::MAX as usize
392        || experts_per_token > i32::MAX as usize
393        || tokens > i32::MAX as usize
394    {
395        return Err(format!(
396            "invalid weighted route combine geometry width={width} experts_per_token=\
397             {experts_per_token} tokens={tokens}/{max_tokens}"
398        ));
399    }
400    let pairs = tokens
401        .checked_mul(experts_per_token)
402        .ok_or("weighted route combine pair count overflow")?;
403    let max_pairs = max_tokens
404        .checked_mul(experts_per_token)
405        .ok_or("weighted route combine capacity overflow")?;
406    if route_weights.len() != pairs || !route_weights.iter().all(|weight| weight.is_finite()) {
407        return Err(format!(
408            "weighted route combine weights {} != pairs {pairs} or contain a non-finite value",
409            route_weights.len()
410        ));
411    }
412    let mut seen = vec![false; pairs];
413    let mut observed = 0usize;
414    for pairs_for_owner in owner_global_pairs {
415        observed = observed
416            .checked_add(pairs_for_owner.len())
417            .ok_or("weighted route combine observed pair count overflow")?;
418        for &pair in *pairs_for_owner {
419            if pair >= pairs || std::mem::replace(&mut seen[pair], true) {
420                return Err(format!(
421                    "weighted route combine pair {pair} is outside 0..{pairs} or duplicated"
422                ));
423            }
424        }
425    }
426    if observed != pairs || seen.iter().any(|present| !present) {
427        return Err(format!(
428            "weighted route combine owner schedules cover {observed} of {pairs} canonical pairs"
429        ));
430    }
431    Ok(WeightedRouteCombineShape { pairs, max_pairs })
432}
433
434fn cache_rank_rows(
435    rows: &[u8],
436    tokens: usize,
437    local_token_bytes: usize,
438    ranks: usize,
439    rank: usize,
440) -> Result<Vec<u8>, String> {
441    if ranks == 0 || rank >= ranks {
442        return Err(format!(
443            "TP cache rank {rank} is outside a {ranks}-rank layout"
444        ));
445    }
446    let global_token_bytes = local_token_bytes
447        .checked_mul(ranks)
448        .ok_or("TP cache global token-byte overflow")?;
449    let expected = tokens
450        .checked_mul(global_token_bytes)
451        .ok_or("TP cache row-byte overflow")?;
452    if rows.len() != expected {
453        return Err(format!(
454            "TP cache rows contain {} bytes, expected {tokens}x{global_token_bytes}={expected}",
455            rows.len()
456        ));
457    }
458    let mut shard = Vec::with_capacity(tokens * local_token_bytes);
459    for token in 0..tokens {
460        let start = token * global_token_bytes + rank * local_token_bytes;
461        shard.extend_from_slice(&rows[start..start + local_token_bytes]);
462    }
463    Ok(shard)
464}
465
466fn parse_step_tp_native_p2p(value: Option<&str>) -> Result<bool, String> {
467    match value {
468        None | Some("") | Some("0") => Ok(false),
469        Some("1") => Ok(true),
470        Some(value) => Err(format!(
471            "MEMRA_STEP_TP_NATIVE_P2P={value:?} is invalid; expected 0 or 1"
472        )),
473    }
474}
475
476pub fn step_tp_native_p2p_enabled() -> Result<bool, String> {
477    parse_step_tp_native_p2p(std::env::var("MEMRA_STEP_TP_NATIVE_P2P").ok().as_deref())
478}
479
480fn parse_step_tp_bulk_p2p(value: Option<&str>) -> Result<bool, String> {
481    match value {
482        None | Some("") | Some("0") => Ok(false),
483        Some("1") => Ok(true),
484        Some(value) => Err(format!(
485            "MEMRA_STEP_TP_BULK_P2P={value:?} is invalid; expected 0 or 1"
486        )),
487    }
488}
489
490pub fn step_tp_bulk_p2p_enabled() -> Result<bool, String> {
491    parse_step_tp_bulk_p2p(std::env::var("MEMRA_STEP_TP_BULK_P2P").ok().as_deref())
492}
493
494fn parse_step_ep_device_arithmetic(value: Option<&str>) -> Result<bool, String> {
495    match value {
496        None | Some("") | Some("0") => Ok(false),
497        Some("1") => Ok(true),
498        Some(value) => Err(format!(
499            "MEMRA_STEP_EP_DEVICE_ARITHMETIC={value:?} is invalid; expected 0 or 1"
500        )),
501    }
502}
503
504fn parse_step_nvfp4_dev_routes(value: Option<&str>) -> Result<bool, String> {
505    match value {
506        None | Some("") | Some("0") => Ok(false),
507        Some("1") => Ok(true),
508        Some(value) => Err(format!(
509            "MEMRA_STEP_NVFP4_DEV_ROUTES={value:?} is invalid; expected 0 or 1"
510        )),
511    }
512}
513
514/// Opt-in door for the device-resident NVFP4 TP routed-expert decode program. Default OFF; the
515/// host-canonical program remains the oracle until the device path carries its own gates.
516pub fn step_nvfp4_dev_routes_enabled() -> Result<bool, String> {
517    parse_step_nvfp4_dev_routes(std::env::var("MEMRA_STEP_NVFP4_DEV_ROUTES").ok().as_deref())
518}
519
520pub fn step_ep_device_arithmetic_enabled() -> Result<bool, String> {
521    parse_step_ep_device_arithmetic(
522        std::env::var("MEMRA_STEP_EP_DEVICE_ARITHMETIC")
523            .ok()
524            .as_deref(),
525    )
526}
527
528fn parse_step_tp_f32_mirror(value: Option<&str>) -> Result<bool, String> {
529    match value {
530        None | Some("") | Some("0") => Ok(false),
531        Some("1") => Ok(true),
532        Some(value) => Err(format!(
533            "MEMRA_STEP_TP_F32_MIRROR={value:?} is invalid; expected 0 or 1"
534        )),
535    }
536}
537
538pub fn step_tp_f32_mirror_enabled() -> Result<bool, String> {
539    parse_step_tp_f32_mirror(std::env::var("MEMRA_STEP_TP_F32_MIRROR").ok().as_deref())
540}
541
542fn parse_step_tp_decode_v2(value: Option<&str>) -> Result<bool, String> {
543    match value {
544        None | Some("") | Some("0") => Ok(false),
545        Some("1") => Ok(true),
546        Some(value) => Err(format!(
547            "MEMRA_STEP_TP_DECODE_V2={value:?} is invalid; expected 0 or 1"
548        )),
549    }
550}
551
552/// The v2 rank-local Step decode-attention driver: persistent workspaces, evented cross-stream
553/// ordering, and a root-device O reduction — same kernels, values, and canonical reduction order
554/// as the v1 driver (it requires the F32 mirror so no per-call weight expansion exists on either
555/// side of the comparison).
556pub fn step_tp_decode_v2_enabled() -> Result<bool, String> {
557    parse_step_tp_decode_v2(std::env::var("MEMRA_STEP_TP_DECODE_V2").ok().as_deref())
558}
559
560fn parse_step_tp_qkv_fused(value: Option<&str>) -> Result<bool, String> {
561    match value {
562        None | Some("") | Some("0") => Ok(false),
563        Some("1") => Ok(true),
564        Some(value) => Err(format!(
565            "MEMRA_STEP_TP_QKV_FUSED={value:?} is invalid; expected 0 or 1"
566        )),
567    }
568}
569
570fn parse_step_tp_dev_router(value: Option<&str>) -> Result<bool, String> {
571    match value {
572        None | Some("") | Some("0") => Ok(false),
573        Some("1") => Ok(true),
574        Some(value) => Err(format!(
575            "MEMRA_STEP_TP_DEV_ROUTER={value:?} is invalid; expected 0 or 1"
576        )),
577    }
578}
579
580/// Device-side sigmoid top-k routing for the TP device-IO expert program: the per-layer host
581/// logits readback (the last per-layer host sync) disappears. Selection tie-breaking may
582/// differ from the host router — NUMERIC-CLASS door, run-gen argmax gate + boot battery.
583pub fn step_tp_dev_router_enabled() -> Result<bool, String> {
584    parse_step_tp_dev_router(std::env::var("MEMRA_STEP_TP_DEV_ROUTER").ok().as_deref())
585}
586
587fn parse_step_tp_graph(value: Option<&str>) -> Result<bool, String> {
588    match value {
589        None | Some("") | Some("0") => Ok(false),
590        Some("1") => Ok(true),
591        Some(value) => Err(format!(
592            "MEMRA_STEP_TP_GRAPH={value:?} is invalid; expected 0 or 1"
593        )),
594    }
595}
596
597fn parse_step_tp_dcw(value: Option<&str>) -> Result<bool, String> {
598    match value {
599        None | Some("") | Some("0") => Ok(false),
600        Some("1") => Ok(true),
601        Some(value) => Err(format!(
602            "MEMRA_STEP_TP_DCW={value:?} is invalid; expected 0 or 1"
603        )),
604    }
605}
606
607/// Device-counter attention path (graph increment A run EAGERLY): append at len_d - base_d,
608/// inc_i32, fa over the counter-derived window — with bucket = the effective t_kv this is
609/// bit-identical to the host-row + kvmod path (the one-partition law), and it is the exact
610/// child content the capture wraps. Rebase tokens and sub-vec-floor contexts fall back.
611pub fn step_tp_dcw_enabled() -> Result<bool, String> {
612    parse_step_tp_dcw(std::env::var("MEMRA_STEP_TP_DCW").ok().as_deref())
613}
614
615/// CUDA-graph door for the shape-stable TP segments (first increment: the device-routed
616/// expert program — per-layer multi-device parents built from per-rank children, launched on
617/// the model engine's stream; zero per-token node updates). Mechanism proven by
618/// tp_graph_probe. VALUE-IDENTICAL: the graphs replay exactly the eager kernel/copy sequence.
619pub fn step_tp_graph_enabled() -> Result<bool, String> {
620    parse_step_tp_graph(std::env::var("MEMRA_STEP_TP_GRAPH").ok().as_deref())
621}
622
623/// Fused single-launch QKV projection inside the v2 decode driver — a NUMERIC-CLASS door
624/// (per-row deterministic tree reduce instead of the chunked cuBLASLt program), default OFF,
625/// gated by the run-gen argmax gate + boot battery like MEMRA_STEP_NVFP4_DEV_ROUTES.
626pub fn step_tp_qkv_fused_enabled() -> Result<bool, String> {
627    parse_step_tp_qkv_fused(std::env::var("MEMRA_STEP_TP_QKV_FUSED").ok().as_deref())
628}
629
630#[derive(Debug, Clone, PartialEq, Eq)]
631pub struct StepEpLayerSpec {
632    pub layer: usize,
633    pub devices: Vec<usize>,
634}
635
636pub type StepTpLayerSpec = StepEpLayerSpec;
637
638fn parse_step_layer_specs(
639    flag: &str,
640    value: Option<&str>,
641    allow_full_model: bool,
642) -> Result<Vec<StepEpLayerSpec>, String> {
643    let Some(value) = value else {
644        return Ok(Vec::new());
645    };
646    if value.is_empty() || value == "0" {
647        return Ok(Vec::new());
648    }
649
650    let mut specs = Vec::new();
651    for item in value.split(';') {
652        let (layers, devices) = item.split_once('@').ok_or_else(|| {
653            let layers = if allow_full_model {
654                "LAYER[-LAYER] or all"
655            } else {
656                "LAYER[-LAYER]"
657            };
658            format!("{flag} must be {layers}@DEVICE,DEVICE[;...]")
659        })?;
660        let (first, last) = if layers == "all" {
661            if !allow_full_model {
662                return Err(format!(
663                    "{flag} does not support the full-model shorthand; assign routed layers \
664                     explicitly"
665                ));
666            }
667            (0, STEP37_TRUNK_LAYERS - 1)
668        } else {
669            match layers.split_once('-') {
670                Some((first, last)) => {
671                    let first = first
672                        .parse::<usize>()
673                        .map_err(|_| format!("{flag} layer {first:?} is not an integer"))?;
674                    let last = last
675                        .parse::<usize>()
676                        .map_err(|_| format!("{flag} layer {last:?} is not an integer"))?;
677                    if first > last {
678                        return Err(format!("{flag} layer range {first}-{last} is reversed"));
679                    }
680                    if last - first + 1 > 128 {
681                        return Err(format!(
682                            "{flag} layer range {first}-{last} exceeds the 128-layer parser cap"
683                        ));
684                    }
685                    (first, last)
686                }
687                None => {
688                    let layer = layers
689                        .parse::<usize>()
690                        .map_err(|_| format!("{flag} layer {layers:?} is not an integer"))?;
691                    (layer, layer)
692                }
693            }
694        };
695        let devices = devices
696            .split(',')
697            .map(|device| {
698                device
699                    .parse::<usize>()
700                    .map_err(|_| format!("{flag} device {device:?} is not an integer"))
701            })
702            .collect::<Result<Vec<_>, _>>()?;
703        if !(2..=8).contains(&devices.len()) {
704            return Err(format!(
705                "{flag} requires 2..=8 devices, got {}",
706                devices.len()
707            ));
708        }
709        let mut unique = devices.clone();
710        unique.sort_unstable();
711        unique.dedup();
712        if unique.len() != devices.len() {
713            return Err(format!("{flag} devices must be distinct, got {devices:?}"));
714        }
715        for layer in first..=last {
716            if specs
717                .iter()
718                .any(|existing: &StepEpLayerSpec| existing.layer == layer)
719            {
720                return Err(format!("{flag} assigns layer {layer} more than once"));
721            }
722            specs.push(StepEpLayerSpec {
723                layer,
724                devices: devices.clone(),
725            });
726        }
727    }
728    Ok(specs)
729}
730
731pub fn parse_step_ep_layer_specs(value: Option<&str>) -> Result<Vec<StepEpLayerSpec>, String> {
732    parse_step_layer_specs("MEMRA_STEP_EP", value, false)
733}
734
735pub fn step_ep_layer_specs() -> Result<Vec<StepEpLayerSpec>, String> {
736    parse_step_ep_layer_specs(std::env::var("MEMRA_STEP_EP").ok().as_deref())
737}
738
739pub fn parse_step_tp_layer_specs(value: Option<&str>) -> Result<Vec<StepTpLayerSpec>, String> {
740    parse_step_layer_specs("MEMRA_STEP_TP", value, true)
741}
742
743pub fn step_tp_layer_specs() -> Result<Vec<StepTpLayerSpec>, String> {
744    parse_step_tp_layer_specs(std::env::var("MEMRA_STEP_TP").ok().as_deref())
745}
746
747#[derive(Clone, Copy)]
748pub struct E4m3BlockMatrix<'a> {
749    pub codes: &'a [u8],
750    pub scales: &'a [f32],
751    pub out_features: usize,
752    pub in_features: usize,
753}
754
755impl E4m3BlockMatrix<'_> {
756    fn validate(&self) -> Result<(), String> {
757        let code_count = self
758            .out_features
759            .checked_mul(self.in_features)
760            .ok_or_else(|| "E4M3 matrix size overflow".to_string())?;
761        if self.codes.len() != code_count {
762            return Err(format!(
763                "E4M3 code count {} != {}x{} ({code_count})",
764                self.codes.len(),
765                self.out_features,
766                self.in_features,
767            ));
768        }
769        let scale_count =
770            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
771        if self.scales.len() != scale_count {
772            return Err(format!(
773                "E4M3 scale count {} != {scale_count} for {}x{}",
774                self.scales.len(),
775                self.out_features,
776                self.in_features,
777            ));
778        }
779        if !self
780            .scales
781            .iter()
782            .all(|scale| scale.is_finite() && *scale > 0.0)
783        {
784            return Err("E4M3 scale grid contains a non-finite or non-positive value".to_string());
785        }
786        Ok(())
787    }
788}
789
790#[derive(Clone, Copy)]
791pub struct E4m3ExpertBank<'a> {
792    pub codes: &'a [u8],
793    pub scales: &'a [f32],
794    pub expert_count: usize,
795    pub out_features: usize,
796    pub in_features: usize,
797}
798
799impl E4m3ExpertBank<'_> {
800    fn validate(&self) -> Result<(), String> {
801        if self.expert_count == 0 {
802            return Err("E4M3 expert bank is empty".to_string());
803        }
804        let code_stride = self
805            .out_features
806            .checked_mul(self.in_features)
807            .ok_or_else(|| "E4M3 expert code stride overflow".to_string())?;
808        let code_count = self
809            .expert_count
810            .checked_mul(code_stride)
811            .ok_or_else(|| "E4M3 expert code count overflow".to_string())?;
812        if self.codes.len() != code_count {
813            return Err(format!(
814                "E4M3 expert code count {} != {}x{} ({code_count})",
815                self.codes.len(),
816                self.expert_count,
817                code_stride,
818            ));
819        }
820        let scale_stride =
821            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
822        let scale_count = self
823            .expert_count
824            .checked_mul(scale_stride)
825            .ok_or_else(|| "E4M3 expert scale count overflow".to_string())?;
826        if self.scales.len() != scale_count {
827            return Err(format!(
828                "E4M3 expert scale count {} != {}x{} ({scale_count})",
829                self.scales.len(),
830                self.expert_count,
831                scale_stride,
832            ));
833        }
834        if !self
835            .scales
836            .iter()
837            .all(|scale| scale.is_finite() && *scale > 0.0)
838        {
839            return Err(
840                "E4M3 expert scale grid contains a non-finite or non-positive value".to_string(),
841            );
842        }
843        Ok(())
844    }
845
846    pub fn expert(&self, expert: usize) -> Result<E4m3BlockMatrix<'_>, String> {
847        if expert >= self.expert_count {
848            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
849        }
850        let code_stride = self.out_features * self.in_features;
851        let scale_stride =
852            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
853        Ok(E4m3BlockMatrix {
854            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
855            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
856            out_features: self.out_features,
857            in_features: self.in_features,
858        })
859    }
860}
861
862pub struct ColumnParallelResult {
863    pub gathered: Vec<f32>,
864    pub rank_outputs: Vec<Vec<f32>>,
865}
866
867pub struct RowParallelResult {
868    pub reduced: Vec<f32>,
869    pub rank_partials: Vec<Vec<f32>>,
870}
871
872#[derive(Clone, Copy)]
873pub struct Bf16Matrix<'a> {
874    pub bytes: &'a [u8],
875    pub out_features: usize,
876    pub in_features: usize,
877}
878
879impl Bf16Matrix<'_> {
880    pub fn validate(&self) -> Result<(), String> {
881        if self.out_features == 0 || self.in_features == 0 {
882            return Err("BF16 matrix dimensions must be nonzero".into());
883        }
884        let expected = self
885            .out_features
886            .checked_mul(self.in_features)
887            .and_then(|values| values.checked_mul(2))
888            .ok_or("BF16 matrix byte count overflow")?;
889        if self.bytes.len() != expected {
890            return Err(format!(
891                "BF16 matrix bytes {} != {}x{}x2 ({expected})",
892                self.bytes.len(),
893                self.out_features,
894                self.in_features,
895            ));
896        }
897        Ok(())
898    }
899}
900
901struct ResidentE4m3Rank {
902    codes: CudaSlice<u8>,
903    scales: CudaSlice<f32>,
904    out_features: usize,
905    in_features: usize,
906}
907
908enum ResidentBf16Weight {
909    Bf16(CudaSlice<u8>),
910    F32(CudaSlice<f32>),
911}
912
913impl ResidentBf16Weight {
914    fn ordinal(&self) -> usize {
915        match self {
916            Self::Bf16(bytes) => bytes.ordinal(),
917            Self::F32(values) => values.ordinal(),
918        }
919    }
920}
921
922struct ResidentBf16Rank {
923    weight: ResidentBf16Weight,
924    out_features: usize,
925    in_features: usize,
926}
927
928pub struct ResidentColumnParallel {
929    ranks: Vec<ResidentE4m3Rank>,
930    out_features: usize,
931    in_features: usize,
932}
933
934pub struct ResidentRowParallel {
935    ranks: Vec<ResidentE4m3Rank>,
936    out_features: usize,
937    in_features: usize,
938}
939
940pub struct ResidentBf16ColumnParallel {
941    ranks: Vec<ResidentBf16Rank>,
942    out_features: usize,
943    in_features: usize,
944    canonical_chunk_rows: Option<usize>,
945}
946
947pub struct ResidentBf16RowParallel {
948    ranks: Vec<ResidentBf16Rank>,
949    out_features: usize,
950    in_features: usize,
951}
952
953pub struct ResidentStepBf16RowParallel {
954    ranks: Vec<Vec<ResidentBf16Rank>>,
955    out_features: usize,
956    in_features: usize,
957    canonical_chunk_cols: usize,
958}
959
960/// Root-owned BF16 sigmoid router with persistent F32 weight, bias, and active mask.
961pub struct ResidentSigmoidTopKRouter {
962    weight: CudaSlice<f32>,
963    correction_bias: CudaSlice<f32>,
964    active: CudaSlice<u8>,
965    root_device: usize,
966    input_width: usize,
967    expert_count: usize,
968    experts_per_token: usize,
969    active_count: usize,
970    scaling_factor: f32,
971    route_norm: bool,
972}
973
974pub struct SigmoidTopKHostOutput {
975    pub logits: Vec<f32>,
976    pub selected: Vec<u32>,
977    pub weights: Vec<f32>,
978}
979
980/// Full BF16 SwiGLU weights replicated independently on every runtime rank.
981pub struct ResidentReplicatedBf16SwiGlu {
982    gate: Vec<ResidentBf16Rank>,
983    up: Vec<ResidentBf16Rank>,
984    down: Vec<ResidentBf16Rank>,
985    input_width: usize,
986    intermediate_width: usize,
987}
988
989/// One token-major F32 batch replicated across a native-P2P rank group.
990///
991/// Every allocation is owned by its matching rank CUDA context. This is the generic handoff
992/// substrate between independently sharded operators; it carries no model or topology claim.
993pub struct ResidentReplicatedDeviceRows {
994    ranks: Vec<CudaSlice<f32>>,
995    tokens: usize,
996    width: usize,
997}
998
999impl ResidentReplicatedDeviceRows {
1000    pub fn tokens(&self) -> usize {
1001        self.tokens
1002    }
1003
1004    pub fn width(&self) -> usize {
1005        self.width
1006    }
1007
1008    pub fn ranks(&self) -> usize {
1009        self.ranks.len()
1010    }
1011}
1012
1013/// Canonical MoE output order: routed plus shared, then add the layer residual.
1014pub fn moe_residual_host(
1015    residual: &[f32],
1016    routed: &[f32],
1017    shared: &[f32],
1018) -> Result<Vec<f32>, String> {
1019    if residual.len() != routed.len() || residual.len() != shared.len() {
1020        return Err(format!(
1021            "MoE residual lengths residual={} routed={} shared={}",
1022            residual.len(),
1023            routed.len(),
1024            shared.len()
1025        ));
1026    }
1027    let ffn = routed
1028        .iter()
1029        .zip(shared)
1030        .map(|(&routed, &shared)| routed + shared)
1031        .collect::<Vec<_>>();
1032    Ok(residual
1033        .iter()
1034        .zip(ffn)
1035        .map(|(&residual, ffn)| residual + ffn)
1036        .collect())
1037}
1038
1039pub use memra_kv::{
1040    KvRingAppend, ResidentTpKvCache, ResidentTpKvCacheRank, TpKvAppendPlan, TpKvTransaction,
1041};
1042
1043/// Persistent TP2/TP4/TP8 routed-expert reference.
1044///
1045/// Rank-local checkpoint shards are uploaded once and remain tied to their owning CUDA context.
1046/// Activations and deterministic host-staged collectives remain per invocation. This is the
1047/// correctness substrate for serving TP/EP, not product-throughput evidence.
1048pub struct ResidentTpExpert {
1049    gate: ResidentColumnParallel,
1050    up: ResidentColumnParallel,
1051    down: ResidentRowParallel,
1052    input_width: usize,
1053    expert_width: usize,
1054}
1055
1056struct ResidentE4m3ExpertBankRank {
1057    codes: CudaSlice<u8>,
1058    scales: CudaSlice<f32>,
1059    expert_range: Range<usize>,
1060    out_features: usize,
1061    in_features: usize,
1062    code_stride: usize,
1063    scale_stride: usize,
1064    /// TP row banks are packed by native 128-wide K block so reduction can replay the
1065    /// checkpoint's global block order exactly. Other banks remain row-major.
1066    k_blocks: Option<usize>,
1067}
1068
1069struct PackedE4m3ExpertBankRank {
1070    codes: Vec<u8>,
1071    scales: Vec<f32>,
1072    expert_range: Range<usize>,
1073    out_features: usize,
1074    in_features: usize,
1075    code_stride: usize,
1076    scale_stride: usize,
1077    k_blocks: Option<usize>,
1078}
1079
1080struct ResidentEpRank {
1081    gate: ResidentE4m3ExpertBankRank,
1082    up: ResidentE4m3ExpertBankRank,
1083    down: ResidentE4m3ExpertBankRank,
1084}
1085
1086/// Persistent expert-parallel reference.
1087///
1088/// Every routed expert has exactly one owner rank. Shared experts are deliberately absent from
1089/// this object because Step replicates them per rank. Routes execute on the owner CUDA context.
1090/// The default oracle stages through host memory; the native path peer-dispatches inputs and
1091/// peer-returns owner outputs while preserving host-canonical activation and accumulation.
1092pub struct ResidentExpertParallel {
1093    ranks: Vec<ResidentEpRank>,
1094    expert_count: usize,
1095    input_width: usize,
1096    expert_width: usize,
1097}
1098
1099/// Projection-level output from the opt-in official Step grouped-FP8 gate.
1100///
1101/// Rows remain pair-major. Routing, weighted combine, and production integration are deliberately
1102/// outside this gate-only adapter.
1103pub struct StepGroupedFp8ProjectionOutput {
1104    pub gate: Vec<f32>,
1105    pub up: Vec<f32>,
1106    pub down: Vec<f32>,
1107}
1108
1109/// Prepared official Step grouped-FP8 projection gate.
1110///
1111/// The complete tensor banks, both CSR schedules, input, activation buffer, and three projection
1112/// workspaces are uploaded or allocated once. Repeated execution performs no device allocation.
1113pub struct PreparedStepGroupedFp8Gate {
1114    device: usize,
1115    gate: ResidentE4m3ExpertBankRank,
1116    up: ResidentE4m3ExpertBankRank,
1117    down: ResidentE4m3ExpertBankRank,
1118    input: CudaSlice<f32>,
1119    route_csr: DeviceExpertCsr,
1120    down_csr: DeviceExpertCsr,
1121    gate_workspace: Fp8GroupedWorkspace,
1122    up_workspace: Fp8GroupedWorkspace,
1123    down_workspace: Fp8GroupedWorkspace,
1124    activation: CudaSlice<f32>,
1125    activation_limit: Option<f32>,
1126    tokens: usize,
1127    pairs: usize,
1128}
1129
1130impl PreparedStepGroupedFp8Gate {
1131    pub fn tokens(&self) -> usize {
1132        self.tokens
1133    }
1134
1135    pub fn pairs(&self) -> usize {
1136        self.pairs
1137    }
1138}
1139
1140struct PreparedStepGroupedExpertOwner {
1141    rank: usize,
1142    global_pairs: Vec<usize>,
1143    route_csr: DeviceExpertCsr,
1144    down_csr: DeviceExpertCsr,
1145    gate_workspace: Fp8GroupedWorkspace,
1146    up_workspace: Fp8GroupedWorkspace,
1147    down_workspace: Fp8GroupedWorkspace,
1148    activation: CudaSlice<f32>,
1149}
1150
1151struct StepGroupedExpertOwnerSchedule {
1152    global_pairs: Vec<usize>,
1153    route_csr: ExpertCsr,
1154    down_csr: ExpertCsr,
1155}
1156
1157/// Prepared official Step expert-owner grouped-FP8 projection gate.
1158///
1159/// Route partitioning, owner-local CSR uploads, input dispatch, activation buffers, and grouped
1160/// workspaces are persistent. Projection rows are scattered back to canonical pair order only
1161/// after every owner has completed its rank-local program.
1162pub struct PreparedStepGroupedExpertParallelGate {
1163    rank_inputs: Vec<CudaSlice<f32>>,
1164    owners: Vec<PreparedStepGroupedExpertOwner>,
1165    activation_limit: Option<f32>,
1166    tokens: usize,
1167    pairs: usize,
1168    max_tokens: usize,
1169    max_pairs: usize,
1170    input_width: usize,
1171    expert_width: usize,
1172    generation: u64,
1173    executed_generation: Option<u64>,
1174    ready: bool,
1175}
1176
1177impl PreparedStepGroupedExpertParallelGate {
1178    pub fn tokens(&self) -> usize {
1179        self.tokens
1180    }
1181
1182    pub fn pairs(&self) -> usize {
1183        self.pairs
1184    }
1185
1186    pub fn max_tokens(&self) -> usize {
1187        self.max_tokens
1188    }
1189
1190    pub fn input_width(&self) -> usize {
1191        self.input_width
1192    }
1193
1194    pub fn expert_width(&self) -> usize {
1195        self.expert_width
1196    }
1197
1198    pub fn set_activation_limit(&mut self, limit: Option<f32>) -> Result<(), String> {
1199        validate_step_expert_activation_limit(limit)?;
1200        self.activation_limit = limit;
1201        self.executed_generation = None;
1202        Ok(())
1203    }
1204
1205    pub fn active_owners(&self) -> usize {
1206        self.owners
1207            .iter()
1208            .filter(|owner| !owner.global_pairs.is_empty())
1209            .count()
1210    }
1211
1212    pub fn owner_pair_counts(&self) -> Vec<usize> {
1213        self.owners
1214            .iter()
1215            .map(|owner| owner.global_pairs.len())
1216            .collect()
1217    }
1218
1219    pub fn generation(&self) -> u64 {
1220        self.generation
1221    }
1222}
1223
1224struct PreparedPeerWeightedRouteOwner {
1225    token_rows: CudaSlice<i32>,
1226    slots: CudaSlice<i32>,
1227    weights: CudaSlice<f32>,
1228    active_pairs: usize,
1229}
1230
1231/// Persistent root-side weighted combine for peer-owned canonical route rows.
1232///
1233/// Owner metadata, one reusable peer staging buffer, the canonical slot bank, weight bank, and
1234/// output are allocated once. Refreshes update metadata prefixes; execution peer-copies active
1235/// rows, scatters them by canonical token/slot, and reduces in the requested numeric order.
1236pub struct PreparedPeerWeightedRouteCombine {
1237    root_device: usize,
1238    owners: Vec<PreparedPeerWeightedRouteOwner>,
1239    peer_staging: CudaSlice<f32>,
1240    slots: CudaSlice<f32>,
1241    weights: CudaSlice<f32>,
1242    output: CudaSlice<f32>,
1243    peer_devices: Vec<usize>,
1244    peer_outputs: Vec<CudaSlice<f32>>,
1245    width: usize,
1246    experts_per_token: usize,
1247    max_tokens: usize,
1248    max_pairs: usize,
1249    tokens: usize,
1250    pairs: usize,
1251    projection_generation: u64,
1252    output_generation: Option<u64>,
1253    broadcast_generation: Option<u64>,
1254    ready: bool,
1255}
1256
1257impl PreparedPeerWeightedRouteCombine {
1258    pub fn tokens(&self) -> usize {
1259        self.tokens
1260    }
1261
1262    pub fn pairs(&self) -> usize {
1263        self.pairs
1264    }
1265
1266    pub fn owner_pair_counts(&self) -> Vec<usize> {
1267        self.owners.iter().map(|owner| owner.active_pairs).collect()
1268    }
1269
1270    pub fn distributed_ranks(&self) -> usize {
1271        1 + self.peer_outputs.len()
1272    }
1273}
1274
1275struct ResidentTpExpertBank {
1276    gate: Vec<ResidentE4m3ExpertBankRank>,
1277    up: Vec<ResidentE4m3ExpertBankRank>,
1278    down: Vec<ResidentE4m3ExpertBankRank>,
1279    expert_count: usize,
1280    input_width: usize,
1281    expert_width: usize,
1282}
1283
1284/// Persistent tensor-parallel expert bank.
1285///
1286/// Every rank owns a checkpoint-aligned output-row shard of every gate/up projection and an
1287/// input-column shard of every down projection. Activations cross deterministic host-staged
1288/// collectives on hosts where native peer copies are unavailable or corrupt.
1289pub struct ResidentTensorParallel {
1290    bank: ResidentTpExpertBank,
1291}
1292
1293/// Multi-context TP correctness runtime. Each rank owns an independent `Engine` and CUDA context.
1294///
1295/// Host bounce is the default oracle. Native P2P is opt-in and preserves the oracle's global
1296/// checkpoint-block reduction order; it remains a correctness path until serving gates and
1297/// repeated performance evidence qualify it.
1298pub struct TpE4m3HostBounce {
1299    devices: Vec<usize>,
1300    ranks: Vec<Engine>,
1301    native_p2p: bool,
1302    ep_device_arithmetic: bool,
1303    bulk_p2p: bool,
1304    /// v2 decode-attention workspace (MEMRA_STEP_TP_DECODE_V2). One per runtime, shared by
1305    /// every TP attention layer — the buffer shapes are geometry-constant across the trunk.
1306    decode_v2: std::sync::Mutex<Vec<StepTpDecodeV2Ws>>,
1307}
1308
1309/// Persistent workspace of the v2 rank-local decode-attention driver.
1310///
1311/// Buffers live in their producing rank's CUDA context, are never freed, and events are
1312/// re-recorded per call — the pp.rs `BoundarySlot` discipline — so the per-token path has no
1313/// cuMemAlloc, no cross-stream free, and no host round-trip. Every buffer is fully overwritten
1314/// before its consumers run in the same call; nothing carries state between tokens.
1315/// Per-rank attn_gate row shards for the fused QKV+gate kernel, in the weight class the
1316/// fused kernels read (F32 mirror or raw checkpoint bf16).
1317pub enum StepTpGateShards<'a> {
1318    F32(&'a [crate::CudaSlice<f32>]),
1319    Bf16(&'a [crate::CudaSlice<u8>]),
1320}
1321
1322pub struct StepTpDecodeV2Ws {
1323    /// T-COLUMN verify slabs (spec MTP): per-rank [t, local_dim] projections computed by
1324    /// the weight-amortized qkvg_tcol kernel; the col-select door copies one column into
1325    /// the single-row buffers and everything downstream runs the unmodified t=1 program.
1326    pub(crate) tcol_q: Vec<CudaSlice<f32>>,
1327    pub(crate) tcol_k: Vec<CudaSlice<f32>>,
1328    pub(crate) tcol_v: Vec<CudaSlice<f32>>,
1329    pub(crate) tcol_g: Vec<CudaSlice<f32>>,
1330    pub(crate) tcol_in: Vec<CudaSlice<f32>>,
1331    pub(crate) tcol_cap: usize,
1332    /// MEMRA_TCOL_OPROJ slabs: per-rank stashed `gated` rows ([8, local_q_dim]), per-rank
1333    /// b4_tcol partials ([8, o_out]), a root-side peer pull of rank1's partial slab, and
1334    /// the root-side joined `mixed` slab. Armed lazily by the first stash.
1335    /// MEMRA_SPEC_FA2 slabs: per-rank stashed post-rope q rows ([2, local_q_dim]), gate
1336    /// rows ([2, heads/ranks]) and the two gated outputs the per-row combine writes
1337    /// ([2, local_q_dim]). Armed lazily by the first stash.
1338    pub(crate) fa2_q: Vec<CudaSlice<f32>>,
1339    pub(crate) fa2_gate: Vec<CudaSlice<f32>>,
1340    pub(crate) fa2_gated: Vec<CudaSlice<f32>>,
1341    pub(crate) fa2_cap: usize,
1342    tcol_gated: Vec<CudaSlice<f32>>,
1343    tcol_opart: Vec<CudaSlice<f32>>,
1344    tcol_opeer: Option<CudaSlice<f32>>,
1345    tcol_omix: Option<CudaSlice<f32>>,
1346    tcol_ocap: usize,
1347    // rank-context buffers, indexed by rank (pub(crate): the v2 driver in hybrid_forward
1348    // feeds them to the KV transaction and attention kernels between the two v2 phases)
1349    pub(crate) q_raw: Vec<CudaSlice<f32>>,
1350    pub(crate) k_raw: Vec<CudaSlice<f32>>,
1351    pub(crate) v_raw: Vec<CudaSlice<f32>>,
1352    pub(crate) q: Vec<CudaSlice<f32>>,
1353    pub(crate) k: Vec<CudaSlice<f32>>,
1354    pub(crate) pos: Vec<CudaSlice<i32>>,
1355    /// FUSION #1 last-block counters (one per rank; atomicInc auto-resets per launch).
1356    pub(crate) fuse_ctr: Vec<CudaSlice<u32>>,
1357    pub(crate) gate: Vec<CudaSlice<f32>>,
1358    pub(crate) attn_out: Vec<CudaSlice<f32>>,
1359    pub(crate) gated: Vec<CudaSlice<f32>>,
1360    /// [rank][block] O partials, each `o_out` wide, in the owning rank's context.
1361    o_partials: Vec<Vec<CudaSlice<f32>>>,
1362    /// Recorded on each rank's stream after its per-call work; root waits before peer reads.
1363    ev_rank: Vec<CudaEvent>,
1364    // root-context buffers
1365    peer_partial: CudaSlice<f32>,
1366    reduce_a: CudaSlice<f32>,
1367    reduce_b: CudaSlice<f32>,
1368    /// Never written; the canonical zero start of the v1 add chain.
1369    zeros: CudaSlice<f32>,
1370    pub(crate) k_shadow: CudaSlice<f32>,
1371    pub(crate) v_shadow: CudaSlice<f32>,
1372    ev_refresh: CudaEvent,
1373    ev_oproj: CudaEvent,
1374    // model-engine (e) context
1375    gate_e: CudaSlice<f32>,
1376    /// Per-token stages (e-ctx, fixed addresses): one eager e-stream copy each per layer; the
1377    /// rank flows raw-copy FROM them, which is exactly the shape graph capture needs.
1378    pub(crate) h_stage: Option<CudaSlice<f32>>,
1379    pub(crate) pos_stage: Option<CudaSlice<i32>>,
1380    /// Workspace-owned per-rank attention input rows (the stage flow copies into THESE, not
1381    /// the per-layer decode_input buffers — the workspace is shared across layers, so every
1382    /// captured/raw address it uses must be layer-invariant).
1383    attn_in: Vec<CudaSlice<f32>>,
1384    /// Cached raw pointers of the stage-flow operands (set when the stages arm).
1385    raw_h_stage: u64,
1386    raw_pos_stage: u64,
1387    raw_attn_in: Vec<u64>,
1388    raw_pos: Vec<u64>,
1389    raw_o_partial1: u64,
1390    raw_peer_partial: u64,
1391    raw_k1: u64,
1392    raw_v1: u64,
1393    raw_k_shadow: u64,
1394    raw_v_shadow: u64,
1395    /// Token-graph e-context mirrors (armed by the orchestrator): the root section
1396    /// raw-copies the reduced attention output and the shadow rows here so the e-glue
1397    /// children read same-context memory (cross-context kernel args are capture-illegal).
1398    raw_mixed_stage_e: u64,
1399    raw_reduce_a: u64,
1400    raw_shadow_stage_e: (u64, u64),
1401    ev_entry: CudaEvent,
1402    e_device: usize,
1403    // geometry pins
1404    local_q_dim: usize,
1405    local_kv_dim: usize,
1406    heads: usize,
1407    pub(crate) o_out: usize,
1408    o_block_cols: usize,
1409    blocks_per_rank: usize,
1410}
1411
1412impl TpE4m3HostBounce {
1413    pub fn new(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1414        Self::new_inner(devices, false, false, false, false)
1415    }
1416
1417    pub fn new_native_p2p(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1418        Self::new_inner(devices, false, true, false, false)
1419    }
1420
1421    pub fn new_native_p2p_device_arithmetic(
1422        devices: &[usize],
1423    ) -> Result<Self, Box<dyn std::error::Error>> {
1424        Self::new_inner(devices, false, true, true, false)
1425    }
1426
1427    pub(crate) fn new_configured(
1428        devices: &[usize],
1429        native_p2p: bool,
1430        ep_device_arithmetic: bool,
1431        bulk_p2p: bool,
1432    ) -> Result<Self, Box<dyn std::error::Error>> {
1433        Self::new_inner(devices, false, native_p2p, ep_device_arithmetic, bulk_p2p)
1434    }
1435
1436    /// Single-rank execution of the canonical checkpoint-block TP program.
1437    ///
1438    /// This is an oracle for distributed exactness, not a serving topology. It lets gates compare
1439    /// TP=1 and TP>1 with the same packing, kernel launches, and deterministic reduction order.
1440    pub fn new_single_rank_oracle(device: usize) -> Result<Self, Box<dyn std::error::Error>> {
1441        Self::new_inner(&[device], true, false, false, false)
1442    }
1443
1444    fn new_inner(
1445        devices: &[usize],
1446        allow_single_rank: bool,
1447        native_p2p: bool,
1448        ep_device_arithmetic: bool,
1449        bulk_p2p: bool,
1450    ) -> Result<Self, Box<dyn std::error::Error>> {
1451        if ep_device_arithmetic && !native_p2p {
1452            return Err("device-resident EP arithmetic requires native P2P".into());
1453        }
1454        if bulk_p2p && !native_p2p {
1455            return Err("bulk TP transport requires native P2P".into());
1456        }
1457        let minimum = if allow_single_rank { 1 } else { 2 };
1458        if !(minimum..=8).contains(&devices.len()) {
1459            return Err(format!(
1460                "TP reference requires {minimum}..=8 devices, got {}",
1461                devices.len()
1462            )
1463            .into());
1464        }
1465        let mut unique = devices.to_vec();
1466        unique.sort_unstable();
1467        unique.dedup();
1468        if unique.len() != devices.len() {
1469            return Err(format!("TP devices must be distinct, got {devices:?}").into());
1470        }
1471        let ranks = devices
1472            .iter()
1473            .map(|&device| Engine::new(device))
1474            .collect::<Result<Vec<_>, _>>()?;
1475        if native_p2p {
1476            configure_native_p2p(&ranks, devices)?;
1477        }
1478        if allow_single_rank {
1479            eprintln!(
1480                "[tp] canonical oracle transport=local device={} performance_claim=false",
1481                devices[0]
1482            );
1483        } else if native_p2p {
1484            if ep_device_arithmetic {
1485                eprintln!(
1486                    "[tp] correctness transport=native-p2p devices={devices:?} \
1487                     native_p2p=true activation=device-host-exact \
1488                     accumulation=device-host-exact output=root-readback \
1489                     bulk_p2p={bulk_p2p} performance_claim=false"
1490                );
1491            } else {
1492                eprintln!(
1493                    "[tp] correctness transport=native-p2p devices={devices:?} \
1494                     native_p2p=true activation=host-canonical bulk_p2p={bulk_p2p} \
1495                     performance_claim=false"
1496                );
1497            }
1498        } else {
1499            eprintln!(
1500                "[tp] correctness transport=host-bounce devices={devices:?} \
1501                 native_p2p=false performance_claim=false"
1502            );
1503        }
1504        Ok(Self {
1505            devices: devices.to_vec(),
1506            ranks,
1507            native_p2p,
1508            ep_device_arithmetic,
1509            bulk_p2p,
1510            decode_v2: std::sync::Mutex::new(Vec::new()),
1511        })
1512    }
1513
1514    pub fn devices(&self) -> &[usize] {
1515        &self.devices
1516    }
1517
1518    pub fn native_p2p(&self) -> bool {
1519        self.native_p2p
1520    }
1521
1522    pub fn bulk_p2p(&self) -> bool {
1523        self.bulk_p2p
1524    }
1525
1526    pub fn expert_activation_label(&self) -> &'static str {
1527        if self.ep_device_arithmetic {
1528            "device-host-exact"
1529        } else {
1530            "host-canonical"
1531        }
1532    }
1533
1534    pub fn expert_accumulation_label(&self) -> &'static str {
1535        self.expert_activation_label()
1536    }
1537
1538    pub fn expert_output_label(&self) -> &'static str {
1539        if self.ep_device_arithmetic {
1540            "root-readback"
1541        } else {
1542            "host-accumulated"
1543        }
1544    }
1545
1546    pub fn transport_label(&self) -> &'static str {
1547        if self.devices.len() == 1 {
1548            "local"
1549        } else if self.native_p2p {
1550            "native-p2p"
1551        } else {
1552            "host-bounce"
1553        }
1554    }
1555
1556    pub fn device_names(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
1557        self.ranks
1558            .iter()
1559            .map(|rank| rank.ctx().name().map_err(Into::into))
1560            .collect()
1561    }
1562
1563    /// Correctness-gate access to the engine that owns one TP rank.
1564    ///
1565    /// Model execution should prefer collective methods on this runtime. This accessor exists so
1566    /// focused gates can prove that the rank-local projection outputs remain device-resident
1567    /// through the next ownership boundary before that boundary is wired into serving.
1568    pub fn rank_engine(&self, rank: usize) -> Option<&Engine> {
1569        self.ranks.get(rank)
1570    }
1571
1572    pub fn allocate_tp_kv_cache(
1573        &self,
1574        kv_dim_k: usize,
1575        kv_dim_v: usize,
1576        capacity: usize,
1577    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1578        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, None)
1579    }
1580
1581    pub fn allocate_tp_swa_kv_cache(
1582        &self,
1583        kv_dim_k: usize,
1584        kv_dim_v: usize,
1585        capacity: usize,
1586        window: usize,
1587    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1588        if window == 0 {
1589            return Err("TP SWA KV window must be nonzero".into());
1590        }
1591        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, Some(window))
1592    }
1593
1594    fn allocate_tp_kv_cache_inner(
1595        &self,
1596        kv_dim_k: usize,
1597        kv_dim_v: usize,
1598        capacity: usize,
1599        window: Option<usize>,
1600    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1601        if capacity == 0 || capacity > i32::MAX as usize {
1602            return Err(
1603                format!("TP KV capacity must be in 1..={}, got {capacity}", i32::MAX).into(),
1604            );
1605        }
1606        let tp = self.ranks.len();
1607        let shape = crate::cache::tp_kv_rank_allocation_shape(kv_dim_k, kv_dim_v, tp)?;
1608        let physical_rows = window
1609            .map(|window| crate::cache::swa_ring_rows(window, capacity))
1610            .unwrap_or(capacity);
1611        let k_plane_bytes = physical_rows
1612            .checked_mul(shape.k_token_bytes)
1613            .and_then(|bytes| bytes.checked_add(8))
1614            .ok_or("TP KV K plane-byte overflow")?;
1615        let v_plane_bytes = physical_rows
1616            .checked_mul(shape.v_token_bytes)
1617            .and_then(|bytes| bytes.checked_add(8))
1618            .ok_or("TP KV V plane-byte overflow")?;
1619        let mut ranks = Vec::with_capacity(tp);
1620        for engine in &self.ranks {
1621            let _main = engine.gpu.enter_main()?;
1622            ranks.push(ResidentTpKvCacheRank::new(
1623                engine.alloc_u8(k_plane_bytes)?,
1624                engine.alloc_u8(v_plane_bytes)?,
1625                engine.htod_i32(&[0])?,
1626            ));
1627        }
1628        Ok(match window {
1629            Some(window) => ResidentTpKvCache::new_swa(
1630                ranks,
1631                shape.kv_dim_k,
1632                shape.kv_dim_v,
1633                shape.k_token_bytes,
1634                shape.v_token_bytes,
1635                capacity,
1636                window,
1637            ),
1638            None => ResidentTpKvCache::new(
1639                ranks,
1640                shape.kv_dim_k,
1641                shape.kv_dim_v,
1642                shape.k_token_bytes,
1643                shape.v_token_bytes,
1644                capacity,
1645            ),
1646        })
1647    }
1648
1649    pub fn grow_tp_kv_cache(
1650        &self,
1651        source: &ResidentTpKvCache,
1652        target_capacity: usize,
1653        rows: usize,
1654    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1655        self.validate_tp_kv_cache(source)?;
1656        let plan = source.prepare_grow(target_capacity, rows)?;
1657        let ranks = self.ranks.len();
1658        let global_k = source
1659            .kv_dim_k()
1660            .checked_mul(ranks)
1661            .ok_or("TP KV grow global K dimension overflow")?;
1662        let global_v = source
1663            .kv_dim_v()
1664            .checked_mul(ranks)
1665            .ok_or("TP KV grow global V dimension overflow")?;
1666        let mut target = match source.ring_window() {
1667            Some(window) => {
1668                self.allocate_tp_swa_kv_cache(global_k, global_v, target_capacity, window)?
1669            }
1670            None => self.allocate_tp_kv_cache(global_k, global_v, target_capacity)?,
1671        };
1672        self.validate_tp_kv_cache(&target)?;
1673
1674        for (rank, engine) in self.ranks.iter().enumerate() {
1675            let _main = engine.gpu.enter_main()?;
1676            let src = source
1677                .rank(rank)
1678                .ok_or_else(|| format!("TP KV grow source has no rank {rank}"))?;
1679            let dst = target
1680                .rank_mut(rank)
1681                .ok_or_else(|| format!("TP KV grow target has no rank {rank}"))?;
1682            if plan.k_bytes() > 0 {
1683                engine.copy_u8_range_into(
1684                    dst.k_mut(),
1685                    0,
1686                    src.k(),
1687                    plan.source_row() * source.k_tok_bytes(),
1688                    plan.k_bytes(),
1689                )?;
1690            }
1691            if plan.v_bytes() > 0 {
1692                engine.copy_u8_range_into(
1693                    dst.v_mut(),
1694                    0,
1695                    src.v(),
1696                    plan.source_row() * source.v_tok_bytes(),
1697                    plan.v_bytes(),
1698                )?;
1699            }
1700        }
1701        self.set_tp_kv_len_mirrors(&mut target, plan.rows())?;
1702
1703        // The caller publishes `target` and immediately drops `source`. Drain every rank's
1704        // stream so an async-pool free cannot recycle a source plane under an in-flight D2D copy.
1705        for engine in &self.ranks {
1706            let _main = engine.gpu.enter_main()?;
1707            engine.stream().synchronize()?;
1708        }
1709        let physical_copy_rows = plan.copy_rows();
1710        target.publish_grow(plan)?;
1711        eprintln!(
1712            "[step-tp-kv-grow] rows={} source_capacity={} target_capacity={} ranks={} \
1713             physical_copy_rows={} ring_window={:?} copy=rank-local-dtod \
1714             rank_streams_synchronized=true generation_preserved=true",
1715            rows,
1716            source.capacity(),
1717            target_capacity,
1718            ranks,
1719            physical_copy_rows,
1720            source.ring_window(),
1721        );
1722        Ok(target)
1723    }
1724
1725    pub fn hydrate_tp_kv_cache(
1726        &self,
1727        cache: &mut ResidentTpKvCache,
1728        rows: usize,
1729        k_rows: &[u8],
1730        v_rows: &[u8],
1731    ) -> Result<(), Box<dyn std::error::Error>> {
1732        self.hydrate_tp_kv_cache_from(cache, rows, 0, k_rows, v_rows)
1733    }
1734
1735    pub fn hydrate_tp_kv_cache_from(
1736        &self,
1737        cache: &mut ResidentTpKvCache,
1738        logical_len: usize,
1739        resident_start: usize,
1740        k_rows: &[u8],
1741        v_rows: &[u8],
1742    ) -> Result<(), Box<dyn std::error::Error>> {
1743        self.validate_tp_kv_cache(cache)?;
1744        if cache.committed_len() != 0 || cache.staged_len() != 0 {
1745            return Err(format!(
1746                "TP KV hydration requires an empty cache, got committed/staged={}/{}",
1747                cache.committed_len(),
1748                cache.staged_len()
1749            )
1750            .into());
1751        }
1752        if resident_start > logical_len || logical_len > cache.capacity() {
1753            return Err(format!(
1754                "TP KV hydration range [{resident_start},{logical_len}) exceeds capacity {}",
1755                cache.capacity(),
1756            )
1757            .into());
1758        }
1759        let rows = logical_len - resident_start;
1760        if rows > cache.physical_capacity() {
1761            return Err(format!(
1762                "TP KV hydration rows {rows} exceed physical capacity {}",
1763                cache.physical_capacity()
1764            )
1765            .into());
1766        }
1767        for rank in 0..self.ranks.len() {
1768            let k_rank =
1769                cache_rank_rows(k_rows, rows, cache.k_tok_bytes(), self.ranks.len(), rank)?;
1770            let v_rank =
1771                cache_rank_rows(v_rows, rows, cache.v_tok_bytes(), self.ranks.len(), rank)?;
1772            let engine = &self.ranks[rank];
1773            let _main = engine.gpu.enter_main()?;
1774            let rank_cache = cache
1775                .rank_mut(rank)
1776                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1777            engine.htod_u8_into(rank_cache.k_mut(), 0, &k_rank)?;
1778            engine.htod_u8_into(rank_cache.v_mut(), 0, &v_rank)?;
1779        }
1780        cache.publish_hydration(logical_len, resident_start)?;
1781        Ok(())
1782    }
1783
1784    pub fn append_tp_kv_transaction(
1785        &self,
1786        cache: &mut ResidentTpKvCache,
1787        transaction: TpKvTransaction,
1788        k_shards: &[CudaSlice<f32>],
1789        v_shards: &[CudaSlice<f32>],
1790        rows: usize,
1791    ) -> Result<(), Box<dyn std::error::Error>> {
1792        self.append_tp_kv_transaction_inner(cache, transaction, k_shards, v_shards, rows, false)
1793    }
1794
1795    /// `external_rank_appends`: the dcw path already wrote the rank rows (device-counter
1796    /// append) — run everything EXCEPT the per-rank quantize/append loop (plan validation,
1797    /// rebase arm — unreachable when the caller peeked — and the absolute len-mirror sets,
1798    /// which land the same value the in-stream inc produced).
1799    #[allow(clippy::too_many_arguments)]
1800    pub fn append_tp_kv_transaction_inner(
1801        &self,
1802        cache: &mut ResidentTpKvCache,
1803        transaction: TpKvTransaction,
1804        k_shards: &[CudaSlice<f32>],
1805        v_shards: &[CudaSlice<f32>],
1806        rows: usize,
1807        external_rank_appends: bool,
1808    ) -> Result<(), Box<dyn std::error::Error>> {
1809        self.validate_tp_kv_cache(cache)?;
1810        let plan = cache.prepare_append(transaction, rows)?;
1811        let target = plan.target();
1812        let expected_k = rows
1813            .checked_mul(cache.kv_dim_k())
1814            .ok_or("TP KV K append size overflow")?;
1815        let expected_v = rows
1816            .checked_mul(cache.kv_dim_v())
1817            .ok_or("TP KV V append size overflow")?;
1818        // external_rank_appends passes no shards — the graph's dcw appends already wrote
1819        // the rank rows, so this call is bookkeeping-only and the shard slices are unused.
1820        if !external_rank_appends
1821            && (k_shards.len() != self.ranks.len() || v_shards.len() != self.ranks.len())
1822        {
1823            return Err(format!(
1824                "TP KV append shard counts k={} v={} != ranks {}",
1825                k_shards.len(),
1826                v_shards.len(),
1827                self.ranks.len()
1828            )
1829            .into());
1830        }
1831        let kv_dim_k = cache.kv_dim_k();
1832        let kv_dim_v = cache.kv_dim_v();
1833        let k_tok_bytes = cache.k_tok_bytes();
1834        let v_tok_bytes = cache.v_tok_bytes();
1835        if let Some(KvRingAppend::Rebase {
1836            src_row,
1837            keep_rows,
1838            new_base,
1839            ..
1840        }) = plan.ring_append()
1841        {
1842            for rank in 0..self.ranks.len() {
1843                let engine = &self.ranks[rank];
1844                let _main = engine.gpu.enter_main()?;
1845                let rank_cache = cache
1846                    .rank_mut(rank)
1847                    .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1848                if keep_rows > 0 {
1849                    let k_len = keep_rows
1850                        .checked_mul(k_tok_bytes)
1851                        .ok_or("TP KV K rebase-byte overflow")?;
1852                    let v_len = keep_rows
1853                        .checked_mul(v_tok_bytes)
1854                        .ok_or("TP KV V rebase-byte overflow")?;
1855                    let mut k_tmp = engine.alloc_u8_uninit(k_len)?;
1856                    let mut v_tmp = engine.alloc_u8_uninit(v_len)?;
1857                    engine.copy_u8_range_into(
1858                        &mut k_tmp,
1859                        0,
1860                        rank_cache.k(),
1861                        src_row * k_tok_bytes,
1862                        k_len,
1863                    )?;
1864                    engine.copy_u8_range_into(
1865                        &mut v_tmp,
1866                        0,
1867                        rank_cache.v(),
1868                        src_row * v_tok_bytes,
1869                        v_len,
1870                    )?;
1871                    engine.copy_u8_into(rank_cache.k_mut(), 0, &k_tmp, k_len)?;
1872                    engine.copy_u8_into(rank_cache.v_mut(), 0, &v_tmp, v_len)?;
1873                }
1874                // dcw base mirror (graph increment A): physical row 0 now holds logical
1875                // row `new_base`; armed device mirrors track it (rebases are rare host
1876                // events, so a host set here is the whole maintenance cost).
1877                if rank_cache.base_d().is_some() {
1878                    let value = new_base as i32;
1879                    let rank_cache = cache
1880                        .rank_mut(rank)
1881                        .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1882                    if let Some(base_d) = rank_cache.base_d_mut() {
1883                        engine.set_i32_one(base_d, value)?;
1884                    }
1885                }
1886            }
1887        }
1888        cache.publish_append_rebase(plan)?;
1889        let write_row = plan.write_row();
1890        for rank in 0..self.ranks.len() {
1891            if external_rank_appends {
1892                break;
1893            }
1894            let engine = &self.ranks[rank];
1895            let _main = engine.gpu.enter_main()?;
1896            if k_shards[rank].len() != expected_k
1897                || v_shards[rank].len() != expected_v
1898                || k_shards[rank].ordinal() != engine.ctx().ordinal()
1899                || v_shards[rank].ordinal() != engine.ctx().ordinal()
1900            {
1901                return Err(format!(
1902                    "TP KV rank {rank} shard geometry/device k={}/{} v={}/{} \
1903                     != expected {expected_k}/{expected_v} on device {}",
1904                    k_shards[rank].len(),
1905                    k_shards[rank].ordinal(),
1906                    v_shards[rank].len(),
1907                    v_shards[rank].ordinal(),
1908                    engine.ctx().ordinal(),
1909                )
1910                .into());
1911            }
1912            let rank_cache = cache
1913                .rank_mut(rank)
1914                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1915            let (rank_k, rank_v) = rank_cache.planes_mut();
1916            engine.append_kv_quantized_rows(
1917                &k_shards[rank],
1918                &v_shards[rank],
1919                rank_k,
1920                rank_v,
1921                write_row,
1922                rows,
1923                kv_dim_k,
1924                kv_dim_v,
1925                k_tok_bytes,
1926                v_tok_bytes,
1927                Engine::kv_fp8_on(),
1928            )?;
1929        }
1930        if !external_rank_appends {
1931            // dcw appends advance the device counters with in-stream inc_i32; an absolute set
1932            // here would race the merged per-rank append (it reads len_d for its write row).
1933            self.set_tp_kv_len_mirrors(cache, target)?;
1934        }
1935        cache.publish_append_plan(plan)?;
1936        Ok(())
1937    }
1938
1939    pub fn commit_tp_kv_transaction(
1940        &self,
1941        cache: &mut ResidentTpKvCache,
1942        transaction: TpKvTransaction,
1943        accepted_rows: usize,
1944    ) -> Result<(), Box<dyn std::error::Error>> {
1945        self.validate_tp_kv_cache(cache)?;
1946        let target = cache.commit_target(transaction, accepted_rows)?;
1947        self.set_tp_kv_len_mirrors(cache, target)?;
1948        cache.publish_finalize(transaction, target)?;
1949        Ok(())
1950    }
1951
1952    /// Commit for the external-appends (token graph) path: host bookkeeping only, NO absolute
1953    /// len-mirror sets. The graph's in-stream inc_i32 owns the device counters; a rank-stream
1954    /// set here has no ordering edge against the NEXT token's graph launch (graph children do
1955    /// not wait on the rank streams), so it can land AFTER that graph's inc and drag the
1956    /// counter backward mid-token.
1957    pub fn commit_tp_kv_transaction_external(
1958        &self,
1959        cache: &mut ResidentTpKvCache,
1960        transaction: TpKvTransaction,
1961        accepted_rows: usize,
1962    ) -> Result<(), Box<dyn std::error::Error>> {
1963        self.validate_tp_kv_cache(cache)?;
1964        let target = cache.commit_target(transaction, accepted_rows)?;
1965        cache.publish_finalize(transaction, target)?;
1966        Ok(())
1967    }
1968
1969    pub fn rollback_tp_kv_transaction(
1970        &self,
1971        cache: &mut ResidentTpKvCache,
1972        transaction: TpKvTransaction,
1973    ) -> Result<(), Box<dyn std::error::Error>> {
1974        self.validate_tp_kv_cache(cache)?;
1975        cache.validate_transaction(transaction)?;
1976        let target = transaction.base_len();
1977        self.set_tp_kv_len_mirrors(cache, target)?;
1978        cache.publish_finalize(transaction, target)?;
1979        Ok(())
1980    }
1981
1982    pub fn tp_kv_device_lengths(
1983        &self,
1984        cache: &ResidentTpKvCache,
1985    ) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
1986        self.validate_tp_kv_cache(cache)?;
1987        let mut lengths = Vec::with_capacity(self.ranks.len());
1988        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks()) {
1989            let _main = engine.gpu.enter_main()?;
1990            lengths.push(engine.dtoh_i32_one(rank_cache.len_d())?);
1991        }
1992        Ok(lengths)
1993    }
1994
1995    fn set_tp_kv_len_mirrors(
1996        &self,
1997        cache: &mut ResidentTpKvCache,
1998        len: usize,
1999    ) -> Result<(), Box<dyn std::error::Error>> {
2000        let len = i32::try_from(len).map_err(|_| "TP KV length exceeds i32 device mirror")?;
2001        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks_mut()) {
2002            let _main = engine.gpu.enter_main()?;
2003            engine.set_i32_one(rank_cache.len_d_mut(), len)?;
2004        }
2005        Ok(())
2006    }
2007
2008    fn validate_tp_kv_cache(
2009        &self,
2010        cache: &ResidentTpKvCache,
2011    ) -> Result<(), Box<dyn std::error::Error>> {
2012        if cache.ranks_len() != self.ranks.len() {
2013            return Err(format!(
2014                "TP KV cache ranks {} != runtime ranks {}",
2015                cache.ranks_len(),
2016                self.ranks.len()
2017            )
2018            .into());
2019        }
2020        let expected_k = cache
2021            .physical_capacity()
2022            .checked_mul(cache.k_tok_bytes())
2023            .and_then(|bytes| bytes.checked_add(8))
2024            .ok_or("TP KV K plane validation overflow")?;
2025        let expected_v = cache
2026            .physical_capacity()
2027            .checked_mul(cache.v_tok_bytes())
2028            .and_then(|bytes| bytes.checked_add(8))
2029            .ok_or("TP KV V plane validation overflow")?;
2030        for (rank, (engine, rank_cache)) in self.ranks.iter().zip(cache.ranks()).enumerate() {
2031            let device = engine.ctx().ordinal();
2032            if rank_cache.k().len() != expected_k
2033                || rank_cache.v().len() != expected_v
2034                || rank_cache.len_d().len() != 1
2035                || rank_cache.k().ordinal() != device
2036                || rank_cache.v().ordinal() != device
2037                || rank_cache.len_d().ordinal() != device
2038            {
2039                return Err(format!(
2040                    "TP KV rank {rank} residency does not match device {device} or plane geometry"
2041                )
2042                .into());
2043            }
2044        }
2045        Ok(())
2046    }
2047
2048    pub fn full(
2049        &self,
2050        matrix: E4m3BlockMatrix<'_>,
2051        activations: &[f32],
2052        tokens: usize,
2053    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2054        matrix.validate()?;
2055        validate_activations(activations, tokens, matrix.in_features)?;
2056        run_rank(&self.ranks[0], matrix, activations, tokens)
2057    }
2058
2059    /// Column-parallel projection. Weight output rows and their scale rows are partitioned across
2060    /// ranks. The input is host-broadcast, rank-local projections execute independently, and the
2061    /// output is host-gathered in rank order.
2062    pub fn column_parallel(
2063        &self,
2064        matrix: E4m3BlockMatrix<'_>,
2065        activations: &[f32],
2066        tokens: usize,
2067    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2068        matrix.validate()?;
2069        validate_activations(activations, tokens, matrix.in_features)?;
2070        let tp = self.ranks.len();
2071        if matrix.out_features % tp != 0 {
2072            return Err(format!(
2073                "column-parallel out_features {} is not divisible by TP={tp}",
2074                matrix.out_features
2075            )
2076            .into());
2077        }
2078        let local_out = matrix.out_features / tp;
2079        if local_out % FP8_BLOCK != 0 {
2080            return Err(format!(
2081                "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
2082                 E4M3 scale block"
2083            )
2084            .into());
2085        }
2086
2087        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2088        let mut rank_outputs = Vec::with_capacity(tp);
2089        for (rank_index, rank) in self.ranks.iter().enumerate() {
2090            let shard = column_shard(matrix, tp, rank_index)?;
2091            let output = run_rank(rank, shard, activations, tokens)?;
2092            let row_start = rank_index * local_out;
2093            for token in 0..tokens {
2094                gathered[token * matrix.out_features + row_start
2095                    ..token * matrix.out_features + row_start + local_out]
2096                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2097            }
2098            rank_outputs.push(output);
2099        }
2100        Ok(ColumnParallelResult {
2101            gathered,
2102            rank_outputs,
2103        })
2104    }
2105
2106    pub fn upload_column_parallel(
2107        &self,
2108        matrix: E4m3BlockMatrix<'_>,
2109    ) -> Result<ResidentColumnParallel, Box<dyn std::error::Error>> {
2110        matrix.validate()?;
2111        let tp = self.ranks.len();
2112        validate_column_shape(matrix, tp)?;
2113        let mut ranks = Vec::with_capacity(tp);
2114        for (rank_index, engine) in self.ranks.iter().enumerate() {
2115            ranks.push(upload_rank(engine, column_shard(matrix, tp, rank_index)?)?);
2116        }
2117        Ok(ResidentColumnParallel {
2118            ranks,
2119            out_features: matrix.out_features,
2120            in_features: matrix.in_features,
2121        })
2122    }
2123
2124    pub fn column_parallel_resident(
2125        &self,
2126        matrix: &ResidentColumnParallel,
2127        activations: &[f32],
2128        tokens: usize,
2129    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2130        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2131        validate_activations(activations, tokens, matrix.in_features)?;
2132        let local_out = matrix.out_features / self.ranks.len();
2133        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2134        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2135        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2136            let output = run_resident_rank(engine, shard, activations, tokens)?;
2137            let row_start = rank_index * local_out;
2138            for token in 0..tokens {
2139                gathered[token * matrix.out_features + row_start
2140                    ..token * matrix.out_features + row_start + local_out]
2141                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2142            }
2143            rank_outputs.push(output);
2144        }
2145        Ok(ColumnParallelResult {
2146            gathered,
2147            rank_outputs,
2148        })
2149    }
2150
2151    /// Row-parallel projection. Weight/input columns and their scale columns are partitioned
2152    /// across ranks. Rank-local partials return through host memory and are reduced in stable
2153    /// rank order.
2154    pub fn row_parallel(
2155        &self,
2156        matrix: E4m3BlockMatrix<'_>,
2157        activations: &[f32],
2158        tokens: usize,
2159    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2160        matrix.validate()?;
2161        validate_activations(activations, tokens, matrix.in_features)?;
2162        let tp = self.ranks.len();
2163        if matrix.in_features % tp != 0 {
2164            return Err(format!(
2165                "row-parallel in_features {} is not divisible by TP={tp}",
2166                matrix.in_features
2167            )
2168            .into());
2169        }
2170        let local_in = matrix.in_features / tp;
2171        if local_in % FP8_BLOCK != 0 {
2172            return Err(format!(
2173                "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
2174                 E4M3 scale block"
2175            )
2176            .into());
2177        }
2178
2179        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2180        let mut rank_partials = Vec::with_capacity(tp);
2181        for (rank_index, rank) in self.ranks.iter().enumerate() {
2182            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2183            let local_activations =
2184                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2185            let shard = E4m3BlockMatrix {
2186                codes: &codes,
2187                scales: &scales,
2188                out_features: matrix.out_features,
2189                in_features: local_in,
2190            };
2191            let partial = run_rank(rank, shard, &local_activations, tokens)?;
2192            for (sum, value) in reduced.iter_mut().zip(&partial) {
2193                *sum += *value;
2194            }
2195            rank_partials.push(partial);
2196        }
2197        Ok(RowParallelResult {
2198            reduced,
2199            rank_partials,
2200        })
2201    }
2202
2203    pub fn upload_row_parallel(
2204        &self,
2205        matrix: E4m3BlockMatrix<'_>,
2206    ) -> Result<ResidentRowParallel, Box<dyn std::error::Error>> {
2207        matrix.validate()?;
2208        let tp = self.ranks.len();
2209        validate_row_shape(matrix, tp)?;
2210        let local_in = matrix.in_features / tp;
2211        let mut ranks = Vec::with_capacity(tp);
2212        for (rank_index, engine) in self.ranks.iter().enumerate() {
2213            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2214            ranks.push(upload_rank(
2215                engine,
2216                E4m3BlockMatrix {
2217                    codes: &codes,
2218                    scales: &scales,
2219                    out_features: matrix.out_features,
2220                    in_features: local_in,
2221                },
2222            )?);
2223        }
2224        Ok(ResidentRowParallel {
2225            ranks,
2226            out_features: matrix.out_features,
2227            in_features: matrix.in_features,
2228        })
2229    }
2230
2231    pub fn row_parallel_resident(
2232        &self,
2233        matrix: &ResidentRowParallel,
2234        activations: &[f32],
2235        tokens: usize,
2236    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2237        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2238        validate_activations(activations, tokens, matrix.in_features)?;
2239        let tp = self.ranks.len();
2240        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2241        let mut rank_partials = Vec::with_capacity(tp);
2242        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2243            let local_activations =
2244                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2245            let partial = run_resident_rank(engine, shard, &local_activations, tokens)?;
2246            for (sum, value) in reduced.iter_mut().zip(&partial) {
2247                *sum += *value;
2248            }
2249            rank_partials.push(partial);
2250        }
2251        Ok(RowParallelResult {
2252            reduced,
2253            rank_partials,
2254        })
2255    }
2256
2257    pub fn upload_bf16_column_parallel(
2258        &self,
2259        matrix: Bf16Matrix<'_>,
2260    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2261        self.upload_bf16_column_parallel_inner(matrix, None, false)
2262    }
2263
2264    /// Step-3.7 column projection with one numerical program across TP1/TP2/TP4/TP8.
2265    pub fn upload_step_bf16_column_parallel(
2266        &self,
2267        matrix: Bf16Matrix<'_>,
2268    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2269        self.upload_step_bf16_column_parallel_inner(matrix, false)
2270    }
2271
2272    /// Load-time exact F32 expansion of a Step BF16 shard.
2273    ///
2274    /// The original BF16 allocation is released after the stream-ordered conversion. Decode then
2275    /// reuses the resident F32 values with the same topology-invariant output-row chunks.
2276    pub fn upload_step_bf16_column_parallel_f32_mirror(
2277        &self,
2278        matrix: Bf16Matrix<'_>,
2279    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2280        self.upload_step_bf16_column_parallel_inner(matrix, true)
2281    }
2282
2283    fn upload_step_bf16_column_parallel_inner(
2284        &self,
2285        matrix: Bf16Matrix<'_>,
2286        f32_mirror: bool,
2287    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2288        let canonical_chunk_rows =
2289            step_bf16_canonical_chunk_rows(matrix.out_features, self.ranks.len())?;
2290        self.upload_bf16_column_parallel_inner(matrix, Some(canonical_chunk_rows), f32_mirror)
2291    }
2292
2293    fn upload_bf16_column_parallel_inner(
2294        &self,
2295        matrix: Bf16Matrix<'_>,
2296        canonical_chunk_rows: Option<usize>,
2297        f32_mirror: bool,
2298    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2299        matrix.validate()?;
2300        let tp = self.ranks.len();
2301        if matrix.out_features % tp != 0 {
2302            return Err(format!(
2303                "BF16 column-parallel out_features {} is not divisible by TP={tp}",
2304                matrix.out_features
2305            )
2306            .into());
2307        }
2308        let mut ranks = Vec::with_capacity(tp);
2309        for (rank, engine) in self.ranks.iter().enumerate() {
2310            ranks.push(upload_bf16_rank(
2311                engine,
2312                bf16_column_shard(matrix, tp, rank)?,
2313                f32_mirror,
2314            )?);
2315        }
2316        Ok(ResidentBf16ColumnParallel {
2317            ranks,
2318            out_features: matrix.out_features,
2319            in_features: matrix.in_features,
2320            canonical_chunk_rows,
2321        })
2322    }
2323
2324    pub fn bf16_column_parallel_resident(
2325        &self,
2326        matrix: &ResidentBf16ColumnParallel,
2327        activations: &[f32],
2328        tokens: usize,
2329    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2330        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2331        validate_activations(activations, tokens, matrix.in_features)?;
2332        let local_out = matrix.out_features / self.ranks.len();
2333        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2334        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2335        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2336            let output = run_resident_bf16_rank(
2337                engine,
2338                shard,
2339                activations,
2340                tokens,
2341                matrix.canonical_chunk_rows,
2342            )?;
2343            for token in 0..tokens {
2344                let src = &output[token * local_out..(token + 1) * local_out];
2345                let dst_start = token * matrix.out_features + rank * local_out;
2346                gathered[dst_start..dst_start + local_out].copy_from_slice(src);
2347            }
2348            rank_outputs.push(output);
2349        }
2350        Ok(ColumnParallelResult {
2351            gathered,
2352            rank_outputs,
2353        })
2354    }
2355
2356    /// Native-P2P twin of [`Self::bf16_column_parallel_resident`].
2357    ///
2358    /// The host-canonical activation is uploaded once on rank zero and peer-broadcast to the
2359    /// remaining ranks. Rank-local outputs are peer-gathered in token-major order before one root
2360    /// readback. This removes per-rank host staging but deliberately still returns a host oracle;
2361    /// attention and KV ownership are separate milestones.
2362    pub fn bf16_column_parallel_resident_native(
2363        &self,
2364        matrix: &ResidentBf16ColumnParallel,
2365        activations: &[f32],
2366        tokens: usize,
2367    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2368        let rank_outputs =
2369            self.bf16_column_parallel_resident_device_shards(matrix, activations, tokens)?;
2370        let local_out = matrix.out_features / self.ranks.len();
2371        self.gather_native_column_shards(&rank_outputs, tokens, local_out)
2372    }
2373
2374    /// Keep Step BF16 column outputs resident on their owning TP ranks.
2375    ///
2376    /// Rank zero receives the host-canonical activation once and peer-broadcasts it when TP>1.
2377    /// Unlike [`Self::bf16_column_parallel_resident_native`], this method performs no output
2378    /// gather or readback. It is the correctness substrate for rank-local norm, RoPE, attention,
2379    /// and cache ownership; callers must not treat its existence as serving qualification.
2380    pub fn bf16_column_parallel_resident_device_shards(
2381        &self,
2382        matrix: &ResidentBf16ColumnParallel,
2383        activations: &[f32],
2384        tokens: usize,
2385    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2386        if self.ranks.len() > 1 && !self.native_p2p {
2387            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
2388        }
2389        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2390        validate_activations(activations, tokens, matrix.in_features)?;
2391
2392        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
2393        let root_input = {
2394            let root = &self.ranks[0];
2395            let _main = root.gpu.enter_main()?;
2396            root.htod(activations)?
2397        };
2398        // PRODUCER FENCE (2026-08-20 flake fix): the peer broadcast below reads this buffer from
2399        // the OTHER ranks' streams, and clone_htod is asynchronous on the root stream. Without
2400        // this fence a peer copy can overtake the in-flight H2D and replicate stale bytes — the
2401        // measured ~30%-of-boots prefill/decode argmax flake. Same discipline as
2402        // `upload_replicated_device_rows`.
2403        {
2404            let root = &self.ranks[0];
2405            let _main = root.gpu.enter_main()?;
2406            root.stream().synchronize()?;
2407        }
2408        rank_inputs.push(root_input);
2409        for engine in &self.ranks[1..] {
2410            let peer_input = {
2411                let _main = engine.gpu.enter_main()?;
2412                let mut peer_input = engine.uninit(activations.len())?;
2413                engine
2414                    .stream()
2415                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
2416                peer_input
2417            };
2418            rank_inputs.push(peer_input);
2419        }
2420
2421        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2422        for rank in 0..self.ranks.len() {
2423            rank_outputs.push(run_resident_bf16_rank_device(
2424                &self.ranks[rank],
2425                &matrix.ranks[rank],
2426                &rank_inputs[rank],
2427                tokens,
2428                matrix.canonical_chunk_rows,
2429                self.bulk_p2p,
2430            )?);
2431        }
2432        Ok(rank_outputs)
2433    }
2434
2435    /// Allocate one fixed-shape replicated batch without initializing its contents.
2436    ///
2437    /// Callers must refresh every rank before passing the batch to an operator.
2438    pub fn allocate_replicated_device_rows(
2439        &self,
2440        tokens: usize,
2441        width: usize,
2442    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2443        if self.ranks.len() > 1 && !self.native_p2p {
2444            return Err("replicated device rows require native P2P ranks".into());
2445        }
2446        let values = tokens
2447            .checked_mul(width)
2448            .ok_or("replicated device row size overflow")?;
2449        let rank_lengths = vec![values; self.ranks.len()];
2450        replicated_device_row_values(tokens, width, self.ranks.len(), &rank_lengths)?;
2451        let mut ranks = Vec::with_capacity(self.ranks.len());
2452        for engine in &self.ranks {
2453            let _main = engine.gpu.enter_main()?;
2454            ranks.push(engine.uninit(values)?);
2455        }
2456        Ok(ResidentReplicatedDeviceRows {
2457            ranks,
2458            tokens,
2459            width,
2460        })
2461    }
2462
2463    /// Replace a fixed-shape replicated batch from a root-device source.
2464    pub fn refresh_replicated_device_rows_from_root(
2465        &self,
2466        rows: &mut ResidentReplicatedDeviceRows,
2467        source: &CudaSlice<f32>,
2468    ) -> Result<(), Box<dyn std::error::Error>> {
2469        if self.ranks.len() > 1 && !self.native_p2p {
2470            return Err("replicated device rows require native P2P ranks".into());
2471        }
2472        validate_replicated_device_rows(&self.ranks, rows)?;
2473        let root = self
2474            .ranks
2475            .first()
2476            .ok_or("replicated rows have no root rank")?;
2477        let values = replicated_device_row_source_values(
2478            rows.tokens,
2479            rows.width,
2480            source.len(),
2481            source.ordinal(),
2482            root.ctx().ordinal(),
2483        )?;
2484        let (root_rows, peer_rows) = rows
2485            .ranks
2486            .split_first_mut()
2487            .ok_or("replicated rows have no root allocation")?;
2488        {
2489            let _main = root.gpu.enter_main()?;
2490            let mut destination = root_rows.slice_mut(0..values);
2491            root.stream()
2492                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
2493            root.stream().synchronize()?;
2494        }
2495        for (engine, peer_rows) in self.ranks.iter().skip(1).zip(peer_rows) {
2496            let _main = engine.gpu.enter_main()?;
2497            let mut destination = peer_rows.slice_mut(0..values);
2498            engine
2499                .stream()
2500                .memcpy_dtod(&root_rows.slice(0..values), &mut destination)?;
2501        }
2502        Ok(())
2503    }
2504
2505    /// Upload one canonical batch on rank zero and replicate it over native P2P.
2506    pub fn upload_replicated_device_rows(
2507        &self,
2508        rows: &[f32],
2509        tokens: usize,
2510        width: usize,
2511    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2512        if self.ranks.len() > 1 && !self.native_p2p {
2513            return Err("replicated device rows require native P2P ranks".into());
2514        }
2515        validate_activations(rows, tokens, width)?;
2516        let root = self
2517            .ranks
2518            .first()
2519            .ok_or("replicated rows have no root rank")?;
2520        let root_rows = {
2521            let _main = root.gpu.enter_main()?;
2522            root.htod(rows)?
2523        };
2524        {
2525            let _main = root.gpu.enter_main()?;
2526            root.stream().synchronize()?;
2527        }
2528        let mut ranks = Vec::with_capacity(self.ranks.len());
2529        ranks.push(root_rows);
2530        for engine in self.ranks.iter().skip(1) {
2531            let _main = engine.gpu.enter_main()?;
2532            let mut peer_rows = engine.uninit(rows.len())?;
2533            engine.stream().memcpy_dtod(&ranks[0], &mut peer_rows)?;
2534            ranks.push(peer_rows);
2535        }
2536        Ok(ResidentReplicatedDeviceRows {
2537            ranks,
2538            tokens,
2539            width,
2540        })
2541    }
2542
2543    /// Execute a column-parallel BF16 matrix directly from rank-local replicated inputs.
2544    pub fn bf16_column_parallel_resident_replicated_device_shards(
2545        &self,
2546        matrix: &ResidentBf16ColumnParallel,
2547        activations: &ResidentReplicatedDeviceRows,
2548    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2549        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2550        validate_replicated_device_rows(&self.ranks, activations)?;
2551        if activations.width != matrix.in_features {
2552            return Err(format!(
2553                "replicated BF16 column input width {} != matrix width {}",
2554                activations.width, matrix.in_features
2555            )
2556            .into());
2557        }
2558        let mut outputs = Vec::with_capacity(self.ranks.len());
2559        for rank in 0..self.ranks.len() {
2560            outputs.push(run_resident_bf16_rank_device(
2561                &self.ranks[rank],
2562                &matrix.ranks[rank],
2563                &activations.ranks[rank],
2564                activations.tokens,
2565                matrix.canonical_chunk_rows,
2566                self.bulk_p2p,
2567            )?);
2568        }
2569        Ok(outputs)
2570    }
2571
2572    /// Upload a BF16 router once on rank zero and retain its exact F32 expansion.
2573    #[allow(clippy::too_many_arguments)]
2574    pub fn upload_sigmoid_topk_router(
2575        &self,
2576        weight: Bf16Matrix<'_>,
2577        correction_bias: &[f32],
2578        active: Option<&[bool]>,
2579        experts_per_token: usize,
2580        scaling_factor: f32,
2581        route_norm: bool,
2582    ) -> Result<ResidentSigmoidTopKRouter, Box<dyn std::error::Error>> {
2583        weight.validate()?;
2584        if correction_bias.len() != weight.out_features
2585            || experts_per_token == 0
2586            || experts_per_token > weight.out_features
2587            || !correction_bias.iter().all(|value| value.is_finite())
2588            || !scaling_factor.is_finite()
2589            || scaling_factor <= 0.0
2590        {
2591            return Err(format!(
2592                "sigmoid router geometry weight={}x{} bias={} top_k={} scale={scaling_factor}",
2593                weight.out_features,
2594                weight.in_features,
2595                correction_bias.len(),
2596                experts_per_token,
2597            )
2598            .into());
2599        }
2600        let active_row = active
2601            .map(|mask| {
2602                if mask.len() != weight.out_features {
2603                    return Err(format!(
2604                        "sigmoid router active mask {} != experts {}",
2605                        mask.len(),
2606                        weight.out_features
2607                    ));
2608                }
2609                Ok(mask
2610                    .iter()
2611                    .map(|&enabled| u8::from(enabled))
2612                    .collect::<Vec<_>>())
2613            })
2614            .transpose()?
2615            .unwrap_or_else(|| vec![1; weight.out_features]);
2616        let active_count = active_row.iter().filter(|&&enabled| enabled != 0).count();
2617        crate::sigrouter_contract::validate_active_count(experts_per_token, active_count)?;
2618
2619        let root = self
2620            .ranks
2621            .first()
2622            .ok_or("sigmoid router runtime has no root rank")?;
2623        let _main = root.gpu.enter_main()?;
2624        let bf16 = root.htod_bytes(weight.bytes)?;
2625        let weight_f32 = root.bf16_to_f32(
2626            &bf16.slice(0..bf16.len()),
2627            weight.out_features * weight.in_features,
2628        )?;
2629        Ok(ResidentSigmoidTopKRouter {
2630            weight: weight_f32,
2631            correction_bias: root.htod(correction_bias)?,
2632            active: root.htod_bytes(&active_row)?,
2633            root_device: root.ctx().ordinal(),
2634            input_width: weight.in_features,
2635            expert_count: weight.out_features,
2636            experts_per_token,
2637            active_count,
2638            scaling_factor,
2639            route_norm,
2640        })
2641    }
2642
2643    /// Route rank-zero replicated rows and return the narrow host control result plus logits.
2644    ///
2645    /// The logits readback exists for independent oracle comparison. This method is a correctness
2646    /// surface; a serving scheduler may retain logits and selected routes on device.
2647    pub fn sigmoid_topk_replicated_device_rows_host(
2648        &self,
2649        router: &ResidentSigmoidTopKRouter,
2650        input: &ResidentReplicatedDeviceRows,
2651    ) -> Result<SigmoidTopKHostOutput, Box<dyn std::error::Error>> {
2652        validate_replicated_device_rows(&self.ranks, input)?;
2653        if input.width != router.input_width {
2654            return Err(format!(
2655                "sigmoid router input width {} != resident width {}",
2656                input.width, router.input_width
2657            )
2658            .into());
2659        }
2660        let root = self
2661            .ranks
2662            .first()
2663            .ok_or("sigmoid router runtime has no root rank")?;
2664        let _main = root.gpu.enter_main()?;
2665        if root.ctx().ordinal() != router.root_device
2666            || router.weight.ordinal() != router.root_device
2667            || router.correction_bias.ordinal() != router.root_device
2668            || router.active.ordinal() != router.root_device
2669        {
2670            return Err("sigmoid router root residency changed".into());
2671        }
2672        let logits = root.router_gemv(
2673            &router.weight,
2674            &input.ranks[0],
2675            router.input_width,
2676            router.expert_count,
2677            input.tokens,
2678        )?;
2679        let (selected, weights) = root.moe_router_sigmoid_topk_host(
2680            &logits,
2681            input.tokens,
2682            router.expert_count,
2683            router.experts_per_token,
2684            router.active_count,
2685            &router.correction_bias,
2686            &router.active,
2687            router.scaling_factor,
2688            router.route_norm,
2689        )?;
2690        Ok(SigmoidTopKHostOutput {
2691            logits: root.dtoh(&logits)?,
2692            selected,
2693            weights,
2694        })
2695    }
2696
2697    /// Replicate a full BF16 SwiGLU bank on every rank.
2698    pub fn upload_replicated_bf16_swiglu(
2699        &self,
2700        gate: Bf16Matrix<'_>,
2701        up: Bf16Matrix<'_>,
2702        down: Bf16Matrix<'_>,
2703    ) -> Result<ResidentReplicatedBf16SwiGlu, Box<dyn std::error::Error>> {
2704        gate.validate()?;
2705        up.validate()?;
2706        down.validate()?;
2707        if gate.in_features != up.in_features
2708            || gate.out_features != up.out_features
2709            || down.in_features != gate.out_features
2710            || down.out_features != gate.in_features
2711        {
2712            return Err(format!(
2713                "replicated BF16 SwiGLU geometry gate={}x{} up={}x{} down={}x{}",
2714                gate.out_features,
2715                gate.in_features,
2716                up.out_features,
2717                up.in_features,
2718                down.out_features,
2719                down.in_features,
2720            )
2721            .into());
2722        }
2723        let mut gate_ranks = Vec::with_capacity(self.ranks.len());
2724        let mut up_ranks = Vec::with_capacity(self.ranks.len());
2725        let mut down_ranks = Vec::with_capacity(self.ranks.len());
2726        for engine in &self.ranks {
2727            gate_ranks.push(upload_bf16_rank(engine, gate, false)?);
2728            up_ranks.push(upload_bf16_rank(engine, up, false)?);
2729            down_ranks.push(upload_bf16_rank(engine, down, false)?);
2730        }
2731        Ok(ResidentReplicatedBf16SwiGlu {
2732            gate: gate_ranks,
2733            up: up_ranks,
2734            down: down_ranks,
2735            input_width: gate.in_features,
2736            intermediate_width: gate.out_features,
2737        })
2738    }
2739
2740    /// Execute a fully replicated BF16 SwiGLU directly from replicated device rows.
2741    pub fn replicated_bf16_swiglu_resident_device(
2742        &self,
2743        mlp: &ResidentReplicatedBf16SwiGlu,
2744        input: &ResidentReplicatedDeviceRows,
2745        activation_limit: Option<f32>,
2746    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2747        validate_step_expert_activation_limit(activation_limit)?;
2748        validate_replicated_device_rows(&self.ranks, input)?;
2749        validate_resident_bf16_ranks(&self.ranks, &mlp.gate)?;
2750        validate_resident_bf16_ranks(&self.ranks, &mlp.up)?;
2751        validate_resident_bf16_ranks(&self.ranks, &mlp.down)?;
2752        if input.width != mlp.input_width
2753            || mlp.gate.len() != self.ranks.len()
2754            || mlp.up.len() != self.ranks.len()
2755            || mlp.down.len() != self.ranks.len()
2756        {
2757            return Err("replicated BF16 SwiGLU residency or input width changed".into());
2758        }
2759
2760        let mut outputs = Vec::with_capacity(self.ranks.len());
2761        for rank in 0..self.ranks.len() {
2762            let engine = &self.ranks[rank];
2763            let gate = run_resident_bf16_rank_device(
2764                engine,
2765                &mlp.gate[rank],
2766                &input.ranks[rank],
2767                input.tokens,
2768                None,
2769                self.bulk_p2p,
2770            )?;
2771            let up = run_resident_bf16_rank_device(
2772                engine,
2773                &mlp.up[rank],
2774                &input.ranks[rank],
2775                input.tokens,
2776                None,
2777                self.bulk_p2p,
2778            )?;
2779            let _main = engine.gpu.enter_main()?;
2780            let values = input
2781                .tokens
2782                .checked_mul(mlp.intermediate_width)
2783                .ok_or("replicated BF16 SwiGLU activation size overflow")?;
2784            let mut activation = engine.uninit(values)?;
2785            if let Some(limit) = activation_limit {
2786                engine.silu_clamped_mul_host_expf(&gate, &up, limit, &mut activation, values)?;
2787            } else {
2788                engine.silu_mul_host_expf(&gate, &up, &mut activation, values)?;
2789            }
2790            outputs.push(run_resident_bf16_rank_device(
2791                engine,
2792                &mlp.down[rank],
2793                &activation,
2794                input.tokens,
2795                None,
2796                self.bulk_p2p,
2797            )?);
2798        }
2799        Ok(ResidentReplicatedDeviceRows {
2800            ranks: outputs,
2801            tokens: input.tokens,
2802            width: mlp.input_width,
2803        })
2804    }
2805
2806    /// Apply the same RMS-norm row program independently on every replicated rank.
2807    pub fn rms_norm_replicated_device_rows(
2808        &self,
2809        input: &ResidentReplicatedDeviceRows,
2810        weight: &[f32],
2811        eps: f32,
2812    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2813        validate_replicated_device_rows(&self.ranks, input)?;
2814        if weight.len() != input.width || !eps.is_finite() || eps <= 0.0 {
2815            return Err(format!(
2816                "replicated RMS norm weight/eps {}/{} != width {}",
2817                weight.len(),
2818                eps,
2819                input.width
2820            )
2821            .into());
2822        }
2823        let mut ranks = Vec::with_capacity(self.ranks.len());
2824        for (rank, engine) in self.ranks.iter().enumerate() {
2825            let _main = engine.gpu.enter_main()?;
2826            let weight = engine.htod(weight)?;
2827            let mut output = engine.uninit(input.tokens * input.width)?;
2828            engine.rms_norm(
2829                &input.ranks[rank],
2830                &weight,
2831                &mut output,
2832                input.width,
2833                input.tokens,
2834                eps,
2835            )?;
2836            ranks.push(output);
2837        }
2838        Ok(ResidentReplicatedDeviceRows {
2839            ranks,
2840            tokens: input.tokens,
2841            width: input.width,
2842        })
2843    }
2844
2845    /// Add two replicated batches and RMS-normalize the exact residual on every rank.
2846    pub fn add_rms_norm_replicated_device_rows(
2847        &self,
2848        input: &ResidentReplicatedDeviceRows,
2849        update: &ResidentReplicatedDeviceRows,
2850        weight: &[f32],
2851        eps: f32,
2852    ) -> Result<
2853        (ResidentReplicatedDeviceRows, ResidentReplicatedDeviceRows),
2854        Box<dyn std::error::Error>,
2855    > {
2856        validate_replicated_device_rows(&self.ranks, input)?;
2857        validate_replicated_device_rows(&self.ranks, update)?;
2858        if input.tokens != update.tokens
2859            || input.width != update.width
2860            || weight.len() != input.width
2861            || !eps.is_finite()
2862            || eps <= 0.0
2863        {
2864            return Err(format!(
2865                "replicated add/RMS geometry input={}x{} update={}x{} weight={} eps={eps}",
2866                input.tokens,
2867                input.width,
2868                update.tokens,
2869                update.width,
2870                weight.len(),
2871            )
2872            .into());
2873        }
2874        let values = input.tokens * input.width;
2875        let mut residual_ranks = Vec::with_capacity(self.ranks.len());
2876        let mut normalized_ranks = Vec::with_capacity(self.ranks.len());
2877        for (rank, engine) in self.ranks.iter().enumerate() {
2878            let _main = engine.gpu.enter_main()?;
2879            let weight = engine.htod(weight)?;
2880            let mut residual = engine.uninit(values)?;
2881            let mut normalized = engine.uninit(values)?;
2882            engine.add_rms_norm(
2883                &input.ranks[rank],
2884                &update.ranks[rank],
2885                &weight,
2886                &mut residual,
2887                &mut normalized,
2888                input.width,
2889                input.tokens,
2890                eps,
2891            )?;
2892            residual_ranks.push(residual);
2893            normalized_ranks.push(normalized);
2894        }
2895        Ok((
2896            ResidentReplicatedDeviceRows {
2897                ranks: residual_ranks,
2898                tokens: input.tokens,
2899                width: input.width,
2900            },
2901            ResidentReplicatedDeviceRows {
2902                ranks: normalized_ranks,
2903                tokens: input.tokens,
2904                width: input.width,
2905            },
2906        ))
2907    }
2908
2909    pub fn collect_replicated_device_rows(
2910        &self,
2911        rows: &ResidentReplicatedDeviceRows,
2912    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
2913        validate_replicated_device_rows(&self.ranks, rows)?;
2914        let mut outputs = Vec::with_capacity(self.ranks.len());
2915        for (rank, engine) in self.ranks.iter().enumerate() {
2916            let _main = engine.gpu.enter_main()?;
2917            outputs.push(engine.dtoh(&rows.ranks[rank])?);
2918        }
2919        Ok(outputs)
2920    }
2921
2922    pub fn upload_bf16_row_parallel(
2923        &self,
2924        matrix: Bf16Matrix<'_>,
2925    ) -> Result<ResidentBf16RowParallel, Box<dyn std::error::Error>> {
2926        matrix.validate()?;
2927        let tp = self.ranks.len();
2928        if matrix.in_features % tp != 0 {
2929            return Err(format!(
2930                "BF16 row-parallel in_features {} is not divisible by TP={tp}",
2931                matrix.in_features
2932            )
2933            .into());
2934        }
2935        let mut ranks = Vec::with_capacity(tp);
2936        for (rank, engine) in self.ranks.iter().enumerate() {
2937            let shard = bf16_row_shard(matrix, tp, rank)?;
2938            ranks.push(upload_bf16_rank(
2939                engine,
2940                Bf16Matrix {
2941                    bytes: &shard,
2942                    out_features: matrix.out_features,
2943                    in_features: matrix.in_features / tp,
2944                },
2945                false,
2946            )?);
2947        }
2948        Ok(ResidentBf16RowParallel {
2949            ranks,
2950            out_features: matrix.out_features,
2951            in_features: matrix.in_features,
2952        })
2953    }
2954
2955    pub fn bf16_row_parallel_resident(
2956        &self,
2957        matrix: &ResidentBf16RowParallel,
2958        activations: &[f32],
2959        tokens: usize,
2960    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2961        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2962        validate_activations(activations, tokens, matrix.in_features)?;
2963        let tp = self.ranks.len();
2964        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2965        let mut rank_partials = Vec::with_capacity(tp);
2966        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2967            let local_activations =
2968                activation_shard(activations, tokens, matrix.in_features, tp, rank);
2969            let partial = run_resident_bf16_rank(engine, shard, &local_activations, tokens, None)?;
2970            for (sum, value) in reduced.iter_mut().zip(&partial) {
2971                *sum += value;
2972            }
2973            rank_partials.push(partial);
2974        }
2975        Ok(RowParallelResult {
2976            reduced,
2977            rank_partials,
2978        })
2979    }
2980
2981    /// Step-3.7 row projection split into the same eight global K blocks for TP1/TP2/TP4/TP8.
2982    pub fn upload_step_bf16_row_parallel(
2983        &self,
2984        matrix: Bf16Matrix<'_>,
2985    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
2986        self.upload_step_bf16_row_parallel_inner(matrix, false)
2987    }
2988
2989    pub fn upload_step_bf16_row_parallel_f32_mirror(
2990        &self,
2991        matrix: Bf16Matrix<'_>,
2992    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
2993        self.upload_step_bf16_row_parallel_inner(matrix, true)
2994    }
2995
2996    fn upload_step_bf16_row_parallel_inner(
2997        &self,
2998        matrix: Bf16Matrix<'_>,
2999        f32_mirror: bool,
3000    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3001        matrix.validate()?;
3002        let tp = self.ranks.len();
3003        let canonical_chunk_cols = step_bf16_canonical_chunk_cols(matrix.in_features, tp)?;
3004        let local_in = matrix.in_features / tp;
3005        let blocks_per_rank = local_in / canonical_chunk_cols;
3006        let mut ranks = Vec::with_capacity(tp);
3007        for (rank, engine) in self.ranks.iter().enumerate() {
3008            let mut blocks = Vec::with_capacity(blocks_per_rank);
3009            for block in 0..blocks_per_rank {
3010                let global_block = rank * blocks_per_rank + block;
3011                let col_start = global_block * canonical_chunk_cols;
3012                let bytes = bf16_row_block(matrix, col_start, canonical_chunk_cols)?;
3013                blocks.push(upload_bf16_rank(
3014                    engine,
3015                    Bf16Matrix {
3016                        bytes: &bytes,
3017                        out_features: matrix.out_features,
3018                        in_features: canonical_chunk_cols,
3019                    },
3020                    f32_mirror,
3021                )?);
3022            }
3023            ranks.push(blocks);
3024        }
3025        Ok(ResidentStepBf16RowParallel {
3026            ranks,
3027            out_features: matrix.out_features,
3028            in_features: matrix.in_features,
3029            canonical_chunk_cols,
3030        })
3031    }
3032
3033    /// Host-staged exactness twin of [`Self::step_bf16_row_parallel_resident_native`].
3034    ///
3035    /// Block inputs and partials cross host memory, but every partial is added on the root device
3036    /// in global checkpoint-column order. Native transport must reproduce this result bitwise.
3037    pub fn step_bf16_row_parallel_resident(
3038        &self,
3039        matrix: &ResidentStepBf16RowParallel,
3040        activations: &[f32],
3041        tokens: usize,
3042    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3043        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3044        validate_activations(activations, tokens, matrix.in_features)?;
3045        let root = &self.ranks[0];
3046        let output_len = tokens
3047            .checked_mul(matrix.out_features)
3048            .ok_or("Step BF16 row output size overflow")?;
3049        let mut reduced = {
3050            let _main = root.gpu.enter_main()?;
3051            root.htod(&vec![0.0f32; output_len])?
3052        };
3053        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3054        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3055            for (block, resident) in blocks.iter().enumerate() {
3056                let global_block = rank * blocks_per_rank + block;
3057                let input = activation_shard(
3058                    activations,
3059                    tokens,
3060                    matrix.in_features,
3061                    PRODUCT_MAX_CARDS,
3062                    global_block,
3063                );
3064                let partial =
3065                    run_resident_bf16_rank(&self.ranks[rank], resident, &input, tokens, None)?;
3066                let next = {
3067                    let _main = root.gpu.enter_main()?;
3068                    let partial = root.htod(&partial)?;
3069                    let mut next = root.uninit(output_len)?;
3070                    root.add(&reduced, &partial, &mut next, output_len)?;
3071                    next
3072                };
3073                reduced = next;
3074            }
3075        }
3076        let _main = root.gpu.enter_main()?;
3077        root.dtoh(&reduced)
3078    }
3079
3080    /// Native-P2P Step row projection with canonical global K-block reduction.
3081    ///
3082    /// The full activation is uploaded once on the root. Each TP8-sized block is peer-scattered
3083    /// to its owning rank, its BF16 partial is peer-returned to the root, and root-device adds
3084    /// replay the same eight-block order as TP1 and the host-staged oracle.
3085    pub fn step_bf16_row_parallel_resident_native(
3086        &self,
3087        matrix: &ResidentStepBf16RowParallel,
3088        activations: &[f32],
3089        tokens: usize,
3090    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3091        if self.ranks.len() > 1 && !self.native_p2p {
3092            return Err("native Step BF16 row parallelism requires P2P ranks".into());
3093        }
3094        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3095        validate_activations(activations, tokens, matrix.in_features)?;
3096        let root = &self.ranks[0];
3097        let root_input = {
3098            let _main = root.gpu.enter_main()?;
3099            root.htod(activations)?
3100        };
3101        let output_len = tokens
3102            .checked_mul(matrix.out_features)
3103            .ok_or("native Step BF16 row output size overflow")?;
3104        let mut reduced = {
3105            let _main = root.gpu.enter_main()?;
3106            root.htod(&vec![0.0f32; output_len])?
3107        };
3108        // PRODUCER FENCE (2026-08-20 flake fix): the non-bulk arm below peer-reads root_input
3109        // from the other ranks' streams while root's clone_htod may still be in flight.
3110        {
3111            let _main = root.gpu.enter_main()?;
3112            root.stream().synchronize()?;
3113        }
3114        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3115        let mut block_input_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3116        let mut root_packed_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3117        let mut remote_partial_keepalive = Vec::new();
3118        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3119            for (block, resident) in blocks.iter().enumerate() {
3120                let global_block = rank * blocks_per_rank + block;
3121                let col_start = global_block * matrix.canonical_chunk_cols;
3122                let block_len = tokens
3123                    .checked_mul(matrix.canonical_chunk_cols)
3124                    .ok_or("native Step BF16 row block size overflow")?;
3125                let block_input = if self.bulk_p2p {
3126                    let root_packed = {
3127                        let _main = root.gpu.enter_main()?;
3128                        let mut root_packed = root.uninit(block_len)?;
3129                        root.copy_rows_strided(
3130                            &root_input,
3131                            &mut root_packed,
3132                            matrix.canonical_chunk_cols,
3133                            tokens,
3134                            matrix.in_features,
3135                            col_start,
3136                        )?;
3137                        root_packed
3138                    };
3139                    if rank == 0 {
3140                        root_packed
3141                    } else {
3142                        // PRODUCER FENCE (2026-08-20 flake fix): the pack kernel runs on the
3143                        // root stream; this rank's peer read must not overtake it.
3144                        {
3145                            let _main = root.gpu.enter_main()?;
3146                            root.stream().synchronize()?;
3147                        }
3148                        let engine = &self.ranks[rank];
3149                        let _main = engine.gpu.enter_main()?;
3150                        let mut block_input = engine.uninit(block_len)?;
3151                        engine
3152                            .stream()
3153                            .memcpy_dtod(&root_packed, &mut block_input)?;
3154                        root_packed_keepalive.push(root_packed);
3155                        block_input
3156                    }
3157                } else {
3158                    let engine = &self.ranks[rank];
3159                    let _main = engine.gpu.enter_main()?;
3160                    let mut block_input = engine.uninit(block_len)?;
3161                    for token in 0..tokens {
3162                        let source_start = token * matrix.in_features + col_start;
3163                        let source = root_input
3164                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3165                        let destination_start = token * matrix.canonical_chunk_cols;
3166                        let mut destination = block_input.slice_mut(
3167                            destination_start..destination_start + matrix.canonical_chunk_cols,
3168                        );
3169                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3170                    }
3171                    block_input
3172                };
3173                let partial = run_resident_bf16_rank_device(
3174                    &self.ranks[rank],
3175                    resident,
3176                    &block_input,
3177                    tokens,
3178                    None,
3179                    self.bulk_p2p,
3180                )?;
3181                block_input_keepalive.push(block_input);
3182                let root_partial = if rank == 0 {
3183                    partial
3184                } else {
3185                    // PRODUCER FENCE (2026-08-20 flake fix): the partial was produced by this
3186                    // rank's kernel on its own stream; root's peer read must not overtake it.
3187                    {
3188                        let engine = &self.ranks[rank];
3189                        let _main = engine.gpu.enter_main()?;
3190                        engine.stream().synchronize()?;
3191                    }
3192                    let _main = root.gpu.enter_main()?;
3193                    let mut peer_partial = root.uninit(output_len)?;
3194                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3195                    remote_partial_keepalive.push(partial);
3196                    peer_partial
3197                };
3198                let next = {
3199                    let _main = root.gpu.enter_main()?;
3200                    let mut next = root.uninit(output_len)?;
3201                    root.add(&reduced, &root_partial, &mut next, output_len)?;
3202                    next
3203                };
3204                reduced = next;
3205            }
3206        }
3207        let output = {
3208            let _main = root.gpu.enter_main()?;
3209            root.dtoh(&reduced)?
3210        };
3211        drop(remote_partial_keepalive);
3212        drop(root_packed_keepalive);
3213        drop(block_input_keepalive);
3214        Ok(output)
3215    }
3216
3217    /// Reduce rank-local Step attention shards in canonical TP8 K-block order and keep the result
3218    /// on the root device.
3219    pub fn step_bf16_row_parallel_resident_root_device(
3220        &self,
3221        matrix: &ResidentStepBf16RowParallel,
3222        rank_activations: &[CudaSlice<f32>],
3223        tokens: usize,
3224    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3225        if self.ranks.len() > 1 && !self.native_p2p {
3226            return Err(
3227                "device-resident Step BF16 row parallelism requires native P2P ranks".into(),
3228            );
3229        }
3230        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3231        let local_width = matrix.in_features / self.ranks.len();
3232        let shard_len = tokens
3233            .checked_mul(local_width)
3234            .ok_or("device Step BF16 row shard size overflow")?;
3235        if tokens == 0
3236            || rank_activations.len() != self.ranks.len()
3237            || rank_activations
3238                .iter()
3239                .zip(&self.ranks)
3240                .any(|(rows, engine)| {
3241                    rows.len() != shard_len || rows.ordinal() != engine.ctx().ordinal()
3242                })
3243        {
3244            return Err("device Step BF16 row activation shard geometry changed".into());
3245        }
3246
3247        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3248        let mut block_inputs = Vec::with_capacity(self.ranks.len());
3249        let mut partials = Vec::with_capacity(self.ranks.len());
3250        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3251            if blocks.len() != blocks_per_rank {
3252                return Err(format!(
3253                    "device Step BF16 row rank {rank} blocks {} != {blocks_per_rank}",
3254                    blocks.len()
3255                )
3256                .into());
3257            }
3258            let engine = &self.ranks[rank];
3259            let _main = engine.gpu.enter_main()?;
3260            let mut rank_inputs = Vec::with_capacity(blocks_per_rank);
3261            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
3262            for (block, resident) in blocks.iter().enumerate() {
3263                let block_len = tokens
3264                    .checked_mul(matrix.canonical_chunk_cols)
3265                    .ok_or("device Step BF16 row block size overflow")?;
3266                let mut block_input = engine.uninit(block_len)?;
3267                let local_col_start = block * matrix.canonical_chunk_cols;
3268                if self.bulk_p2p {
3269                    engine.copy_rows_strided(
3270                        &rank_activations[rank],
3271                        &mut block_input,
3272                        matrix.canonical_chunk_cols,
3273                        tokens,
3274                        local_width,
3275                        local_col_start,
3276                    )?;
3277                } else {
3278                    for token in 0..tokens {
3279                        let source_start = token * local_width + local_col_start;
3280                        let source = rank_activations[rank]
3281                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3282                        let destination_start = token * matrix.canonical_chunk_cols;
3283                        let mut destination = block_input.slice_mut(
3284                            destination_start..destination_start + matrix.canonical_chunk_cols,
3285                        );
3286                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3287                    }
3288                }
3289                let partial = run_resident_bf16_rank_device(
3290                    engine,
3291                    resident,
3292                    &block_input,
3293                    tokens,
3294                    None,
3295                    self.bulk_p2p,
3296                )?;
3297                rank_inputs.push(block_input);
3298                rank_partials.push(partial);
3299            }
3300            block_inputs.push(rank_inputs);
3301            partials.push(rank_partials);
3302        }
3303        for engine in self.ranks.iter().skip(1) {
3304            let _main = engine.gpu.enter_main()?;
3305            engine.stream().synchronize()?;
3306        }
3307
3308        let output_len = tokens
3309            .checked_mul(matrix.out_features)
3310            .ok_or("device Step BF16 row output size overflow")?;
3311        let root = &self.ranks[0];
3312        let _main = root.gpu.enter_main()?;
3313        let mut reduced = root.htod(&vec![0.0f32; output_len])?;
3314        let mut remote_partials = Vec::new();
3315        for (rank, rank_partials) in partials.into_iter().enumerate() {
3316            for partial in rank_partials {
3317                let root_partial = if rank == 0 {
3318                    partial
3319                } else {
3320                    let mut peer_partial = root.uninit(output_len)?;
3321                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3322                    remote_partials.push(partial);
3323                    peer_partial
3324                };
3325                let mut next = root.uninit(output_len)?;
3326                root.add(&reduced, &root_partial, &mut next, output_len)?;
3327                reduced = next;
3328            }
3329        }
3330        root.stream().synchronize()?;
3331        drop(remote_partials);
3332        drop(block_inputs);
3333        Ok(reduced)
3334    }
3335
3336    /// Reduce rank-local Step attention shards, then replicate the canonical root result.
3337    pub fn step_bf16_row_parallel_resident_replicated_device(
3338        &self,
3339        matrix: &ResidentStepBf16RowParallel,
3340        rank_activations: &[CudaSlice<f32>],
3341        tokens: usize,
3342    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3343        let reduced =
3344            self.step_bf16_row_parallel_resident_root_device(matrix, rank_activations, tokens)?;
3345        let output_len = tokens
3346            .checked_mul(matrix.out_features)
3347            .ok_or("device Step BF16 row output size overflow")?;
3348        let mut ranks = Vec::with_capacity(self.ranks.len());
3349        ranks.push(reduced);
3350        for engine in self.ranks.iter().skip(1) {
3351            let _main = engine.gpu.enter_main()?;
3352            let mut peer_output = engine.uninit(output_len)?;
3353            engine.stream().memcpy_dtod(&ranks[0], &mut peer_output)?;
3354            ranks.push(peer_output);
3355        }
3356        Ok(ResidentReplicatedDeviceRows {
3357            ranks,
3358            tokens,
3359            width: matrix.out_features,
3360        })
3361    }
3362
3363    pub fn upload_expert(
3364        &self,
3365        gate: E4m3BlockMatrix<'_>,
3366        up: E4m3BlockMatrix<'_>,
3367        down: E4m3BlockMatrix<'_>,
3368    ) -> Result<ResidentTpExpert, Box<dyn std::error::Error>> {
3369        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3370            return Err("TP expert gate/up dimensions differ".into());
3371        }
3372        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3373            return Err(format!(
3374                "TP expert down {}x{} does not invert gate/up {}x{}",
3375                down.out_features, down.in_features, gate.out_features, gate.in_features
3376            )
3377            .into());
3378        }
3379        Ok(ResidentTpExpert {
3380            gate: self.upload_column_parallel(gate)?,
3381            up: self.upload_column_parallel(up)?,
3382            down: self.upload_row_parallel(down)?,
3383            input_width: gate.in_features,
3384            expert_width: gate.out_features,
3385        })
3386    }
3387
3388    pub fn run_expert(
3389        &self,
3390        expert: &ResidentTpExpert,
3391        input: &[f32],
3392        tokens: usize,
3393    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3394        validate_activations(input, tokens, expert.input_width)?;
3395        let gate = self.column_parallel_resident(&expert.gate, input, tokens)?;
3396        let up = self.column_parallel_resident(&expert.up, input, tokens)?;
3397        let activated: Vec<f32> = gate
3398            .gathered
3399            .iter()
3400            .zip(&up.gathered)
3401            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
3402            .collect();
3403        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
3404        Ok(self
3405            .row_parallel_resident(&expert.down, &activated, tokens)?
3406            .reduced)
3407    }
3408
3409    pub fn upload_expert_parallel(
3410        &self,
3411        gate: E4m3ExpertBank<'_>,
3412        up: E4m3ExpertBank<'_>,
3413        down: E4m3ExpertBank<'_>,
3414    ) -> Result<ResidentExpertParallel, Box<dyn std::error::Error>> {
3415        gate.validate()?;
3416        up.validate()?;
3417        down.validate()?;
3418        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
3419            return Err("EP gate/up/down expert counts differ".into());
3420        }
3421        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3422            return Err("EP gate/up dimensions differ".into());
3423        }
3424        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3425            return Err(format!(
3426                "EP down {}x{} does not invert gate/up {}x{}",
3427                down.out_features, down.in_features, gate.out_features, gate.in_features
3428            )
3429            .into());
3430        }
3431        if gate.expert_count % self.ranks.len() != 0 {
3432            return Err(format!(
3433                "EP expert count {} is not divisible by {} ranks",
3434                gate.expert_count,
3435                self.ranks.len()
3436            )
3437            .into());
3438        }
3439
3440        let per_rank = gate.expert_count / self.ranks.len();
3441        let mut ranks = Vec::with_capacity(self.ranks.len());
3442        for (rank, engine) in self.ranks.iter().enumerate() {
3443            let expert_range = rank * per_rank..(rank + 1) * per_rank;
3444            ranks.push(ResidentEpRank {
3445                gate: upload_expert_bank_rank(engine, gate, expert_range.clone())?,
3446                up: upload_expert_bank_rank(engine, up, expert_range.clone())?,
3447                down: upload_expert_bank_rank(engine, down, expert_range)?,
3448            });
3449        }
3450        Ok(ResidentExpertParallel {
3451            ranks,
3452            expert_count: gate.expert_count,
3453            input_width: gate.in_features,
3454            expert_width: gate.out_features,
3455        })
3456    }
3457
3458    /// Prepare the official Step gate-only grouped-FP8 projection oracle on rank zero.
3459    ///
3460    /// This intentionally does not alter the resident EP path. It owns a full rank-local tensor
3461    /// bank solely so the grouped projection can be compared with the existing per-route oracle
3462    /// without routing, transport, or combine changing underneath it.
3463    #[allow(clippy::too_many_arguments)]
3464    pub fn prepare_step_grouped_fp8_gate(
3465        &self,
3466        gate: E4m3ExpertBank<'_>,
3467        up: E4m3ExpertBank<'_>,
3468        down: E4m3ExpertBank<'_>,
3469        input: &[f32],
3470        tokens: usize,
3471        selected: &[usize],
3472        activation_limit: Option<f32>,
3473    ) -> Result<PreparedStepGroupedFp8Gate, Box<dyn std::error::Error>> {
3474        gate.validate()?;
3475        up.validate()?;
3476        down.validate()?;
3477        validate_step_expert_activation_limit(activation_limit)?;
3478        if gate.expert_count != STEP_GROUPED_FP8_EXPERTS
3479            || up.expert_count != STEP_GROUPED_FP8_EXPERTS
3480            || down.expert_count != STEP_GROUPED_FP8_EXPERTS
3481        {
3482            return Err(format!(
3483                "official Step grouped FP8 gate requires {STEP_GROUPED_FP8_EXPERTS} experts, \
3484                 got gate/up/down={}/{}/{}",
3485                gate.expert_count, up.expert_count, down.expert_count,
3486            )
3487            .into());
3488        }
3489        if gate.in_features != up.in_features
3490            || gate.out_features != STEP_GROUPED_FP8_WIDTH
3491            || up.out_features != STEP_GROUPED_FP8_WIDTH
3492            || down.in_features != STEP_GROUPED_FP8_WIDTH
3493            || down.out_features != gate.in_features
3494        {
3495            return Err(format!(
3496                "official Step grouped FP8 geometry gate={}x{} up={}x{} down={}x{}",
3497                gate.out_features,
3498                gate.in_features,
3499                up.out_features,
3500                up.in_features,
3501                down.out_features,
3502                down.in_features,
3503            )
3504            .into());
3505        }
3506        validate_activations(input, tokens, gate.in_features)?;
3507        let pairs = tokens
3508            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3509            .ok_or("official Step grouped FP8 route count overflow")?;
3510        if selected.len() != pairs {
3511            return Err(format!(
3512                "official Step grouped FP8 routes {} != {tokens}x{STEP_GROUPED_FP8_TOP_K} \
3513                 ({pairs})",
3514                selected.len()
3515            )
3516            .into());
3517        }
3518        for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
3519            let mut unique = routes.to_vec();
3520            unique.sort_unstable();
3521            unique.dedup();
3522            if unique.len() != STEP_GROUPED_FP8_TOP_K {
3523                return Err(format!(
3524                    "official Step grouped FP8 token {token} routes are not top-8 unique: \
3525                     {routes:?}"
3526                )
3527                .into());
3528            }
3529        }
3530
3531        let engine = self
3532            .ranks
3533            .first()
3534            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3535        let _main = engine.gpu.enter_main()?;
3536        let expert_range = 0..STEP_GROUPED_FP8_EXPERTS;
3537        let gate = upload_expert_bank_rank(engine, gate, expert_range.clone())?;
3538        let up = upload_expert_bank_rank(engine, up, expert_range.clone())?;
3539        let down = upload_expert_bank_rank(engine, down, expert_range)?;
3540        let input = engine.htod(input)?;
3541        let route_csr = ExpertCsr::from_token_routes(
3542            STEP_GROUPED_FP8_EXPERTS,
3543            tokens,
3544            STEP_GROUPED_FP8_TOP_K,
3545            selected,
3546        )?
3547        .upload(engine)?;
3548        let pair_rows = (0..pairs).collect::<Vec<_>>();
3549        let down_csr =
3550            ExpertCsr::from_pair_rows(STEP_GROUPED_FP8_EXPERTS, pairs, selected, &pair_rows)?
3551                .upload(engine)?;
3552        let gate_workspace =
3553            Fp8GroupedWorkspace::new(engine, gate.in_features, gate.out_features, tokens, pairs)?;
3554        let up_workspace =
3555            Fp8GroupedWorkspace::new(engine, up.in_features, up.out_features, tokens, pairs)?;
3556        let down_workspace =
3557            Fp8GroupedWorkspace::new(engine, down.in_features, down.out_features, pairs, pairs)?;
3558        let activation = engine.uninit(pairs * STEP_GROUPED_FP8_WIDTH)?;
3559        Ok(PreparedStepGroupedFp8Gate {
3560            device: engine.ctx().ordinal(),
3561            gate,
3562            up,
3563            down,
3564            input,
3565            route_csr,
3566            down_csr,
3567            gate_workspace,
3568            up_workspace,
3569            down_workspace,
3570            activation,
3571            activation_limit,
3572            tokens,
3573            pairs,
3574        })
3575    }
3576
3577    /// Execute one prepared gate/up/activation/down projection sequence on rank zero.
3578    pub fn run_step_grouped_fp8_gate(
3579        &self,
3580        plan: &mut PreparedStepGroupedFp8Gate,
3581    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
3582        let engine = self
3583            .ranks
3584            .first()
3585            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3586        if engine.ctx().ordinal() != plan.device {
3587            return Err(format!(
3588                "official Step grouped FP8 plan device {} != rank-zero device {}",
3589                plan.device,
3590                engine.ctx().ordinal()
3591            )
3592            .into());
3593        }
3594        let _main = engine.gpu.enter_main()?;
3595
3596        plan.gate_workspace.quantize(engine, &plan.input)?;
3597        plan.gate_workspace.project(
3598            engine,
3599            &plan.gate.codes,
3600            &plan.gate.scales,
3601            &plan.route_csr,
3602            plan.gate.code_stride,
3603            plan.gate.scale_stride,
3604            1.0,
3605        )?;
3606        plan.up_workspace.quantize(engine, &plan.input)?;
3607        plan.up_workspace.project(
3608            engine,
3609            &plan.up.codes,
3610            &plan.up.scales,
3611            &plan.route_csr,
3612            plan.up.code_stride,
3613            plan.up.scale_stride,
3614            1.0,
3615        )?;
3616        if let Some(limit) = plan.activation_limit {
3617            engine.silu_clamped_mul_host_expf(
3618                plan.gate_workspace.output(),
3619                plan.up_workspace.output(),
3620                limit,
3621                &mut plan.activation,
3622                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3623            )?;
3624        } else {
3625            engine.silu_mul_host_expf(
3626                plan.gate_workspace.output(),
3627                plan.up_workspace.output(),
3628                &mut plan.activation,
3629                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3630            )?;
3631        }
3632        plan.down_workspace.quantize(engine, &plan.activation)?;
3633        plan.down_workspace.project(
3634            engine,
3635            &plan.down.codes,
3636            &plan.down.scales,
3637            &plan.down_csr,
3638            plan.down.code_stride,
3639            plan.down.scale_stride,
3640            1.0,
3641        )?;
3642
3643        Ok(StepGroupedFp8ProjectionOutput {
3644            gate: engine.dtoh(plan.gate_workspace.output())?,
3645            up: engine.dtoh(plan.up_workspace.output())?,
3646            down: engine.dtoh(plan.down_workspace.output())?,
3647        })
3648    }
3649
3650    pub fn prepare_step_grouped_expert_parallel_gate(
3651        &self,
3652        experts: &ResidentExpertParallel,
3653        input: &[f32],
3654        tokens: usize,
3655        selected: &[usize],
3656        activation_limit: Option<f32>,
3657    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3658        self.prepare_step_grouped_expert_parallel_gate_with_capacity(
3659            experts,
3660            input,
3661            tokens,
3662            selected,
3663            activation_limit,
3664            tokens,
3665        )
3666    }
3667
3668    #[allow(clippy::too_many_arguments)]
3669    pub fn prepare_step_grouped_expert_parallel_gate_with_capacity(
3670        &self,
3671        experts: &ResidentExpertParallel,
3672        input: &[f32],
3673        tokens: usize,
3674        selected: &[usize],
3675        activation_limit: Option<f32>,
3676        max_tokens: usize,
3677    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3678        if !self.native_p2p || !self.ep_device_arithmetic {
3679            return Err(
3680                "Step owner-grouped FP8 requires native P2P and device-resident arithmetic".into(),
3681            );
3682        }
3683        validate_step_expert_activation_limit(activation_limit)?;
3684        validate_ep_residency(&self.ranks, experts)?;
3685        validate_activations(input, tokens, experts.input_width)?;
3686        if max_tokens < tokens || max_tokens > i32::MAX as usize {
3687            return Err(format!(
3688                "official Step owner-grouped FP8 tokens {tokens} exceed capacity {max_tokens}"
3689            )
3690            .into());
3691        }
3692        if experts.expert_count != STEP_GROUPED_FP8_EXPERTS
3693            || experts.expert_width != STEP_GROUPED_FP8_WIDTH
3694        {
3695            return Err(format!(
3696                "official Step owner-grouped FP8 requires {} experts at width {}, got {} at {}",
3697                STEP_GROUPED_FP8_EXPERTS,
3698                STEP_GROUPED_FP8_WIDTH,
3699                experts.expert_count,
3700                experts.expert_width,
3701            )
3702            .into());
3703        }
3704        validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
3705        let max_pairs = max_tokens
3706            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3707            .ok_or("official Step owner-grouped FP8 capacity route count overflow")?;
3708        let input_capacity = max_tokens
3709            .checked_mul(experts.input_width)
3710            .ok_or("official Step owner-grouped FP8 input capacity overflow")?;
3711
3712        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
3713        for engine in &self.ranks {
3714            let _main = engine.gpu.enter_main()?;
3715            rank_inputs.push(engine.uninit(input_capacity)?);
3716        }
3717
3718        let mut owners = Vec::with_capacity(self.ranks.len());
3719        for (owner_rank, rank) in experts.ranks.iter().enumerate() {
3720            if rank.gate.expert_range != rank.up.expert_range
3721                || rank.gate.expert_range != rank.down.expert_range
3722            {
3723                return Err(format!(
3724                    "owner-grouped FP8 rank {} gate/up/down expert ranges differ",
3725                    owner_rank
3726                )
3727                .into());
3728            }
3729            let local_experts = rank.gate.expert_range.len();
3730            let engine = &self.ranks[owner_rank];
3731            let _main = engine.gpu.enter_main()?;
3732            let route_csr =
3733                DeviceExpertCsr::with_capacity(engine, local_experts, max_tokens, max_pairs)?;
3734            let down_csr =
3735                DeviceExpertCsr::with_capacity(engine, local_experts, max_pairs, max_pairs)?;
3736            let gate_workspace = Fp8GroupedWorkspace::new(
3737                engine,
3738                experts.input_width,
3739                experts.expert_width,
3740                max_tokens,
3741                max_pairs,
3742            )?;
3743            let up_workspace = Fp8GroupedWorkspace::new(
3744                engine,
3745                experts.input_width,
3746                experts.expert_width,
3747                max_tokens,
3748                max_pairs,
3749            )?;
3750            let down_workspace = Fp8GroupedWorkspace::new(
3751                engine,
3752                experts.expert_width,
3753                experts.input_width,
3754                max_pairs,
3755                max_pairs,
3756            )?;
3757            let activation = engine.uninit(
3758                max_pairs
3759                    .checked_mul(experts.expert_width)
3760                    .ok_or("official Step owner-grouped FP8 activation capacity overflow")?,
3761            )?;
3762            owners.push(PreparedStepGroupedExpertOwner {
3763                rank: owner_rank,
3764                global_pairs: Vec::new(),
3765                route_csr,
3766                down_csr,
3767                gate_workspace,
3768                up_workspace,
3769                down_workspace,
3770                activation,
3771            });
3772        }
3773
3774        let mut plan = PreparedStepGroupedExpertParallelGate {
3775            rank_inputs,
3776            owners,
3777            activation_limit,
3778            tokens: 0,
3779            pairs: 0,
3780            max_tokens,
3781            max_pairs,
3782            input_width: experts.input_width,
3783            expert_width: experts.expert_width,
3784            generation: 0,
3785            executed_generation: None,
3786            ready: false,
3787        };
3788        self.refresh_step_grouped_expert_parallel_gate(
3789            experts, &mut plan, input, tokens, selected,
3790        )?;
3791        Ok(plan)
3792    }
3793
3794    fn prepare_step_grouped_expert_parallel_refresh(
3795        &self,
3796        experts: &ResidentExpertParallel,
3797        plan: &PreparedStepGroupedExpertParallelGate,
3798        tokens: usize,
3799        selected: &[usize],
3800    ) -> Result<(usize, u64, Vec<Option<StepGroupedExpertOwnerSchedule>>), Box<dyn std::error::Error>>
3801    {
3802        validate_ep_residency(&self.ranks, experts)?;
3803        if plan.rank_inputs.len() != self.ranks.len()
3804            || plan.owners.len() != self.ranks.len()
3805            || plan.input_width != experts.input_width
3806            || plan.expert_width != experts.expert_width
3807            || tokens > plan.max_tokens
3808        {
3809            return Err(format!(
3810                "Step owner-grouped FP8 refresh geometry changed ranks={}/{} owners={}/{} \
3811                 input={}/{} expert={}/{} tokens={}/{}",
3812                plan.rank_inputs.len(),
3813                self.ranks.len(),
3814                plan.owners.len(),
3815                self.ranks.len(),
3816                plan.input_width,
3817                experts.input_width,
3818                plan.expert_width,
3819                experts.expert_width,
3820                tokens,
3821                plan.max_tokens,
3822            )
3823            .into());
3824        }
3825        let pairs = validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
3826        if pairs > plan.max_pairs {
3827            return Err(format!(
3828                "Step owner-grouped FP8 route count {pairs} exceeds capacity {}",
3829                plan.max_pairs
3830            )
3831            .into());
3832        }
3833        let next_generation = plan
3834            .generation
3835            .checked_add(1)
3836            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
3837        let owner_routes = partition_expert_owner_routes(
3838            experts.expert_count,
3839            self.ranks.len(),
3840            tokens,
3841            STEP_GROUPED_FP8_TOP_K,
3842            selected,
3843        )?;
3844        let mut schedules = Vec::with_capacity(self.ranks.len());
3845        for routes in owner_routes {
3846            if routes.selected.is_empty() {
3847                schedules.push(None);
3848                continue;
3849            }
3850            let local_experts = experts.ranks[routes.rank].gate.expert_range.len();
3851            let local_pairs = routes.selected.len();
3852            let route_csr = ExpertCsr::from_pair_rows(
3853                local_experts,
3854                tokens,
3855                &routes.selected,
3856                &routes.token_rows,
3857            )?;
3858            let down_rows = (0..local_pairs).collect::<Vec<_>>();
3859            let down_csr = ExpertCsr::from_pair_rows(
3860                local_experts,
3861                local_pairs,
3862                &routes.selected,
3863                &down_rows,
3864            )?;
3865            schedules.push(Some(StepGroupedExpertOwnerSchedule {
3866                global_pairs: routes.global_pairs,
3867                route_csr,
3868                down_csr,
3869            }));
3870        }
3871        Ok((pairs, next_generation, schedules))
3872    }
3873
3874    fn commit_step_grouped_expert_parallel_refresh(
3875        &self,
3876        plan: &mut PreparedStepGroupedExpertParallelGate,
3877        tokens: usize,
3878        pairs: usize,
3879        next_generation: u64,
3880        schedules: Vec<Option<StepGroupedExpertOwnerSchedule>>,
3881    ) -> Result<(), Box<dyn std::error::Error>> {
3882        for (owner, schedule) in plan.owners.iter_mut().zip(schedules) {
3883            let engine = &self.ranks[owner.rank];
3884            let _main = engine.gpu.enter_main()?;
3885            if let Some(schedule) = schedule {
3886                owner.route_csr.refresh(engine, &schedule.route_csr)?;
3887                owner.down_csr.refresh(engine, &schedule.down_csr)?;
3888                owner.global_pairs = schedule.global_pairs;
3889            } else {
3890                owner.route_csr.clear();
3891                owner.down_csr.clear();
3892                owner.global_pairs.clear();
3893            }
3894        }
3895        plan.tokens = tokens;
3896        plan.pairs = pairs;
3897        plan.generation = next_generation;
3898        plan.ready = true;
3899        Ok(())
3900    }
3901
3902    pub fn refresh_step_grouped_expert_parallel_gate(
3903        &self,
3904        experts: &ResidentExpertParallel,
3905        plan: &mut PreparedStepGroupedExpertParallelGate,
3906        input: &[f32],
3907        tokens: usize,
3908        selected: &[usize],
3909    ) -> Result<(), Box<dyn std::error::Error>> {
3910        validate_activations(input, tokens, experts.input_width)?;
3911        let (pairs, next_generation, schedules) =
3912            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
3913
3914        plan.ready = false;
3915        plan.executed_generation = None;
3916        {
3917            let root = &self.ranks[0];
3918            let _main = root.gpu.enter_main()?;
3919            let mut destination = plan.rank_inputs[0].slice_mut(0..input.len());
3920            root.stream().memcpy_htod(input, &mut destination)?;
3921            root.stream().synchronize()?;
3922        }
3923        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
3924        let root_input = &root_inputs[0];
3925        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
3926            let engine = &self.ranks[rank + 1];
3927            let _main = engine.gpu.enter_main()?;
3928            let mut destination = peer_input.slice_mut(0..input.len());
3929            engine
3930                .stream()
3931                .memcpy_dtod(&root_input.slice(0..input.len()), &mut destination)?;
3932        }
3933        self.commit_step_grouped_expert_parallel_refresh(
3934            plan,
3935            tokens,
3936            pairs,
3937            next_generation,
3938            schedules,
3939        )
3940    }
3941
3942    /// Refresh routes and inputs from an already-resident rank-zero activation.
3943    ///
3944    /// The caller must order the source producer before this call. The root copy is completed
3945    /// before peer dispatch, while CSR and workspace allocations retain their stable addresses.
3946    pub fn refresh_step_grouped_expert_parallel_gate_from_root_device(
3947        &self,
3948        experts: &ResidentExpertParallel,
3949        plan: &mut PreparedStepGroupedExpertParallelGate,
3950        input: &CudaSlice<f32>,
3951        tokens: usize,
3952        selected: &[usize],
3953    ) -> Result<(), Box<dyn std::error::Error>> {
3954        let input_values = tokens
3955            .checked_mul(experts.input_width)
3956            .ok_or("Step owner-grouped FP8 input size overflow")?;
3957        let root = self
3958            .ranks
3959            .first()
3960            .ok_or("Step owner-grouped FP8 runtime has no root rank")?;
3961        if input.len() < input_values || input.ordinal() != root.ctx().ordinal() {
3962            return Err(format!(
3963                "Step owner-grouped FP8 root input len/device {}/{} does not cover {} values on \
3964                 device {}",
3965                input.len(),
3966                input.ordinal(),
3967                input_values,
3968                root.ctx().ordinal(),
3969            )
3970            .into());
3971        }
3972        let (pairs, next_generation, schedules) =
3973            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
3974
3975        plan.ready = false;
3976        plan.executed_generation = None;
3977        {
3978            let _main = root.gpu.enter_main()?;
3979            let mut destination = plan.rank_inputs[0].slice_mut(0..input_values);
3980            root.stream()
3981                .memcpy_dtod(&input.slice(0..input_values), &mut destination)?;
3982            root.stream().synchronize()?;
3983        }
3984        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
3985        let root_input = &root_inputs[0];
3986        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
3987            let engine = &self.ranks[rank + 1];
3988            let _main = engine.gpu.enter_main()?;
3989            let mut destination = peer_input.slice_mut(0..input_values);
3990            engine
3991                .stream()
3992                .memcpy_dtod(&root_input.slice(0..input_values), &mut destination)?;
3993        }
3994        self.commit_step_grouped_expert_parallel_refresh(
3995            plan,
3996            tokens,
3997            pairs,
3998            next_generation,
3999            schedules,
4000        )
4001    }
4002
4003    /// Replace a fixed route plan's rank inputs from an already replicated device batch.
4004    ///
4005    /// Route CSR remains unchanged. Advancing the generation invalidates every prior projection
4006    /// and combine result, so callers must refresh combine metadata before executing again.
4007    pub fn refresh_step_grouped_expert_parallel_inputs_from_replicated(
4008        &self,
4009        experts: &ResidentExpertParallel,
4010        plan: &mut PreparedStepGroupedExpertParallelGate,
4011        input: &ResidentReplicatedDeviceRows,
4012    ) -> Result<(), Box<dyn std::error::Error>> {
4013        validate_ep_residency(&self.ranks, experts)?;
4014        validate_replicated_device_rows(&self.ranks, input)?;
4015        if !plan.ready
4016            || input.tokens != plan.tokens
4017            || input.width != plan.input_width
4018            || input.tokens > plan.max_tokens
4019            || plan.rank_inputs.len() != self.ranks.len()
4020            || plan.owners.len() != self.ranks.len()
4021            || plan.input_width != experts.input_width
4022            || plan.expert_width != experts.expert_width
4023        {
4024            return Err("Step owner-grouped replicated input geometry changed".into());
4025        }
4026        let values = input
4027            .tokens
4028            .checked_mul(input.width)
4029            .ok_or("Step owner-grouped replicated input size overflow")?;
4030        let next_generation = plan
4031            .generation
4032            .checked_add(1)
4033            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
4034        plan.ready = false;
4035        plan.executed_generation = None;
4036        for (rank, engine) in self.ranks.iter().enumerate() {
4037            let _main = engine.gpu.enter_main()?;
4038            let mut destination = plan.rank_inputs[rank].slice_mut(0..values);
4039            engine
4040                .stream()
4041                .memcpy_dtod(&input.ranks[rank], &mut destination)?;
4042        }
4043        plan.generation = next_generation;
4044        plan.ready = true;
4045        Ok(())
4046    }
4047
4048    pub fn execute_step_grouped_expert_parallel_gate(
4049        &self,
4050        experts: &ResidentExpertParallel,
4051        plan: &mut PreparedStepGroupedExpertParallelGate,
4052    ) -> Result<(), Box<dyn std::error::Error>> {
4053        validate_ep_residency(&self.ranks, experts)?;
4054        if !plan.ready
4055            || plan.rank_inputs.len() != self.ranks.len()
4056            || plan.owners.len() != self.ranks.len()
4057            || plan.input_width != experts.input_width
4058            || plan.expert_width != experts.expert_width
4059        {
4060            return Err("Step owner-grouped FP8 plan is not ready or its geometry changed".into());
4061        }
4062        plan.executed_generation = None;
4063
4064        for owner in &mut plan.owners {
4065            if owner.global_pairs.is_empty() {
4066                continue;
4067            }
4068            let engine = &self.ranks[owner.rank];
4069            let bank = &experts.ranks[owner.rank];
4070            let _main = engine.gpu.enter_main()?;
4071            let local_pairs = owner.global_pairs.len();
4072            owner.gate_workspace.quantize_for_shape(
4073                engine,
4074                &plan.rank_inputs[owner.rank],
4075                plan.tokens,
4076                local_pairs,
4077            )?;
4078            owner.gate_workspace.project(
4079                engine,
4080                &bank.gate.codes,
4081                &bank.gate.scales,
4082                &owner.route_csr,
4083                bank.gate.code_stride,
4084                bank.gate.scale_stride,
4085                1.0,
4086            )?;
4087            owner.up_workspace.quantize_for_shape(
4088                engine,
4089                &plan.rank_inputs[owner.rank],
4090                plan.tokens,
4091                local_pairs,
4092            )?;
4093            owner.up_workspace.project(
4094                engine,
4095                &bank.up.codes,
4096                &bank.up.scales,
4097                &owner.route_csr,
4098                bank.up.code_stride,
4099                bank.up.scale_stride,
4100                1.0,
4101            )?;
4102        }
4103        for owner in &mut plan.owners {
4104            if owner.global_pairs.is_empty() {
4105                continue;
4106            }
4107            let engine = &self.ranks[owner.rank];
4108            let _main = engine.gpu.enter_main()?;
4109            let values = owner.global_pairs.len() * plan.expert_width;
4110            if let Some(limit) = plan.activation_limit {
4111                engine.silu_clamped_mul_host_expf(
4112                    owner.gate_workspace.output(),
4113                    owner.up_workspace.output(),
4114                    limit,
4115                    &mut owner.activation,
4116                    values,
4117                )?;
4118            } else {
4119                engine.silu_mul_host_expf(
4120                    owner.gate_workspace.output(),
4121                    owner.up_workspace.output(),
4122                    &mut owner.activation,
4123                    values,
4124                )?;
4125            }
4126        }
4127        for owner in &mut plan.owners {
4128            if owner.global_pairs.is_empty() {
4129                continue;
4130            }
4131            let engine = &self.ranks[owner.rank];
4132            let bank = &experts.ranks[owner.rank];
4133            let _main = engine.gpu.enter_main()?;
4134            let local_pairs = owner.global_pairs.len();
4135            owner.down_workspace.quantize_for_shape(
4136                engine,
4137                &owner.activation,
4138                local_pairs,
4139                local_pairs,
4140            )?;
4141            owner.down_workspace.project(
4142                engine,
4143                &bank.down.codes,
4144                &bank.down.scales,
4145                &owner.down_csr,
4146                bank.down.code_stride,
4147                bank.down.scale_stride,
4148                1.0,
4149            )?;
4150        }
4151        plan.executed_generation = Some(plan.generation);
4152        Ok(())
4153    }
4154
4155    pub fn collect_step_grouped_expert_parallel_gate(
4156        &self,
4157        plan: &PreparedStepGroupedExpertParallelGate,
4158    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4159        if !plan.ready || plan.executed_generation != Some(plan.generation) {
4160            return Err("Step owner-grouped FP8 projection is stale or has not executed".into());
4161        }
4162        let mut gate = vec![0.0f32; plan.pairs * plan.expert_width];
4163        let mut up = vec![0.0f32; plan.pairs * plan.expert_width];
4164        let mut down = vec![0.0f32; plan.pairs * plan.input_width];
4165        for owner in &plan.owners {
4166            if owner.global_pairs.is_empty() {
4167                continue;
4168            }
4169            let engine = &self.ranks[owner.rank];
4170            let _main = engine.gpu.enter_main()?;
4171            let owner_gate = engine.dtoh_view(
4172                &owner
4173                    .gate_workspace
4174                    .output()
4175                    .slice(0..owner.gate_workspace.output_len()),
4176            )?;
4177            let owner_up = engine.dtoh_view(
4178                &owner
4179                    .up_workspace
4180                    .output()
4181                    .slice(0..owner.up_workspace.output_len()),
4182            )?;
4183            let owner_down = engine.dtoh_view(
4184                &owner
4185                    .down_workspace
4186                    .output()
4187                    .slice(0..owner.down_workspace.output_len()),
4188            )?;
4189            for (local_pair, &global_pair) in owner.global_pairs.iter().enumerate() {
4190                let local_expert = local_pair * plan.expert_width;
4191                let global_expert = global_pair * plan.expert_width;
4192                gate[global_expert..global_expert + plan.expert_width]
4193                    .copy_from_slice(&owner_gate[local_expert..local_expert + plan.expert_width]);
4194                up[global_expert..global_expert + plan.expert_width]
4195                    .copy_from_slice(&owner_up[local_expert..local_expert + plan.expert_width]);
4196
4197                let local_hidden = local_pair * plan.input_width;
4198                let global_hidden = global_pair * plan.input_width;
4199                down[global_hidden..global_hidden + plan.input_width]
4200                    .copy_from_slice(&owner_down[local_hidden..local_hidden + plan.input_width]);
4201            }
4202        }
4203        Ok(StepGroupedFp8ProjectionOutput { gate, up, down })
4204    }
4205
4206    pub fn run_step_grouped_expert_parallel_gate(
4207        &self,
4208        experts: &ResidentExpertParallel,
4209        plan: &mut PreparedStepGroupedExpertParallelGate,
4210    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4211        self.execute_step_grouped_expert_parallel_gate(experts, plan)?;
4212        self.collect_step_grouped_expert_parallel_gate(plan)
4213    }
4214
4215    pub fn prepare_step_grouped_expert_parallel_combine(
4216        &self,
4217        plan: &PreparedStepGroupedExpertParallelGate,
4218        route_weights: &[f32],
4219    ) -> Result<PreparedPeerWeightedRouteCombine, Box<dyn std::error::Error>> {
4220        if !self.native_p2p || !self.ep_device_arithmetic || !plan.ready {
4221            return Err(
4222                "Step owner-grouped combine requires a ready native-P2P device plan".into(),
4223            );
4224        }
4225        let owner_pairs = plan
4226            .owners
4227            .iter()
4228            .map(|owner| owner.global_pairs.as_slice())
4229            .collect::<Vec<_>>();
4230        let shape = validate_weighted_route_combine(
4231            plan.input_width,
4232            STEP_GROUPED_FP8_TOP_K,
4233            plan.max_tokens,
4234            plan.tokens,
4235            &owner_pairs,
4236            route_weights,
4237        )?;
4238        if shape.max_pairs != plan.max_pairs {
4239            return Err(format!(
4240                "Step owner-grouped combine capacity {} != projection capacity {}",
4241                shape.max_pairs, plan.max_pairs
4242            )
4243            .into());
4244        }
4245        let root = self
4246            .ranks
4247            .first()
4248            .ok_or("Step owner-grouped combine has no root rank")?;
4249        let slot_values = shape
4250            .max_pairs
4251            .checked_mul(plan.input_width)
4252            .ok_or("Step owner-grouped combine slot capacity overflow")?;
4253        let output_values = plan
4254            .max_tokens
4255            .checked_mul(plan.input_width)
4256            .ok_or("Step owner-grouped combine output capacity overflow")?;
4257        let (root_device, owners, peer_staging, slots, weights, output) = {
4258            let _main = root.gpu.enter_main()?;
4259            let mut owners = Vec::with_capacity(plan.owners.len());
4260            for _ in &plan.owners {
4261                owners.push(PreparedPeerWeightedRouteOwner {
4262                    token_rows: root.htod_i32(&vec![0; shape.max_pairs])?,
4263                    slots: root.htod_i32(&vec![0; shape.max_pairs])?,
4264                    weights: root.htod(&vec![0.0; shape.max_pairs])?,
4265                    active_pairs: 0,
4266                });
4267            }
4268            (
4269                root.ctx().ordinal(),
4270                owners,
4271                root.uninit(slot_values)?,
4272                root.uninit(slot_values)?,
4273                root.uninit(shape.max_pairs)?,
4274                root.uninit(output_values)?,
4275            )
4276        };
4277        let mut peer_devices = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4278        let mut peer_outputs = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4279        for engine in self.ranks.iter().skip(1) {
4280            let _main = engine.gpu.enter_main()?;
4281            peer_devices.push(engine.ctx().ordinal());
4282            peer_outputs.push(engine.uninit(output_values)?);
4283        }
4284        let mut combine = PreparedPeerWeightedRouteCombine {
4285            root_device,
4286            owners,
4287            peer_staging,
4288            slots,
4289            weights,
4290            output,
4291            peer_devices,
4292            peer_outputs,
4293            width: plan.input_width,
4294            experts_per_token: STEP_GROUPED_FP8_TOP_K,
4295            max_tokens: plan.max_tokens,
4296            max_pairs: shape.max_pairs,
4297            tokens: 0,
4298            pairs: 0,
4299            projection_generation: 0,
4300            output_generation: None,
4301            broadcast_generation: None,
4302            ready: false,
4303        };
4304        self.refresh_step_grouped_expert_parallel_combine(plan, &mut combine, route_weights)?;
4305        Ok(combine)
4306    }
4307
4308    pub fn refresh_step_grouped_expert_parallel_combine(
4309        &self,
4310        plan: &PreparedStepGroupedExpertParallelGate,
4311        combine: &mut PreparedPeerWeightedRouteCombine,
4312        route_weights: &[f32],
4313    ) -> Result<(), Box<dyn std::error::Error>> {
4314        let output_capacity = combine
4315            .max_tokens
4316            .checked_mul(combine.width)
4317            .ok_or("Step owner-grouped combine output capacity overflow")?;
4318        if !plan.ready
4319            || combine.owners.len() != plan.owners.len()
4320            || combine.peer_devices.len() + 1 != self.ranks.len()
4321            || combine.peer_outputs.len() + 1 != self.ranks.len()
4322            || combine.width != plan.input_width
4323            || combine.experts_per_token != STEP_GROUPED_FP8_TOP_K
4324            || combine.max_tokens != plan.max_tokens
4325            || combine.max_pairs != plan.max_pairs
4326            || combine.output.len() < output_capacity
4327            || combine
4328                .peer_outputs
4329                .iter()
4330                .any(|output| output.len() < output_capacity)
4331        {
4332            return Err("Step owner-grouped combine/projection geometry changed".into());
4333        }
4334        if self
4335            .ranks
4336            .iter()
4337            .skip(1)
4338            .zip(&combine.peer_devices)
4339            .any(|(engine, &device)| engine.ctx().ordinal() != device)
4340        {
4341            return Err("Step owner-grouped combine peer devices changed".into());
4342        }
4343        let owner_pairs = plan
4344            .owners
4345            .iter()
4346            .map(|owner| owner.global_pairs.as_slice())
4347            .collect::<Vec<_>>();
4348        let shape = validate_weighted_route_combine(
4349            combine.width,
4350            combine.experts_per_token,
4351            combine.max_tokens,
4352            plan.tokens,
4353            &owner_pairs,
4354            route_weights,
4355        )?;
4356        if shape.max_pairs != combine.max_pairs {
4357            return Err("Step owner-grouped combine capacity changed during refresh".into());
4358        }
4359        let metadata = owner_pairs
4360            .iter()
4361            .map(|pairs| {
4362                let token_rows = pairs
4363                    .iter()
4364                    .map(|&pair| (pair / combine.experts_per_token) as i32)
4365                    .collect::<Vec<_>>();
4366                let slots = pairs
4367                    .iter()
4368                    .map(|&pair| (pair % combine.experts_per_token) as i32)
4369                    .collect::<Vec<_>>();
4370                let weights = pairs
4371                    .iter()
4372                    .map(|&pair| route_weights[pair])
4373                    .collect::<Vec<_>>();
4374                (token_rows, slots, weights)
4375            })
4376            .collect::<Vec<_>>();
4377
4378        combine.ready = false;
4379        combine.output_generation = None;
4380        combine.broadcast_generation = None;
4381        let root = self
4382            .ranks
4383            .first()
4384            .ok_or("Step owner-grouped combine has no root rank")?;
4385        let _main = root.gpu.enter_main()?;
4386        if root.ctx().ordinal() != combine.root_device {
4387            return Err(format!(
4388                "Step owner-grouped combine root device changed {} != {}",
4389                root.ctx().ordinal(),
4390                combine.root_device
4391            )
4392            .into());
4393        }
4394        for (owner, (token_rows, slots, weights)) in combine.owners.iter_mut().zip(metadata) {
4395            if token_rows.is_empty() {
4396                owner.active_pairs = 0;
4397                continue;
4398            }
4399            root.htod_i32_into(&mut owner.token_rows, &token_rows)?;
4400            root.htod_i32_into(&mut owner.slots, &slots)?;
4401            let mut weight_prefix = owner.weights.slice_mut(0..weights.len());
4402            root.stream().memcpy_htod(&weights, &mut weight_prefix)?;
4403            owner.active_pairs = token_rows.len();
4404        }
4405        combine.tokens = plan.tokens;
4406        combine.pairs = shape.pairs;
4407        combine.projection_generation = plan.generation;
4408        combine.ready = true;
4409        Ok(())
4410    }
4411
4412    pub fn execute_step_grouped_expert_parallel_combine(
4413        &self,
4414        plan: &PreparedStepGroupedExpertParallelGate,
4415        combine: &mut PreparedPeerWeightedRouteCombine,
4416    ) -> Result<(), Box<dyn std::error::Error>> {
4417        if !plan.ready
4418            || plan.executed_generation != Some(plan.generation)
4419            || !combine.ready
4420            || combine.tokens != plan.tokens
4421            || combine.pairs != plan.pairs
4422            || combine.width != plan.input_width
4423            || combine.owners.len() != plan.owners.len()
4424            || combine.projection_generation != plan.generation
4425        {
4426            return Err("Step owner-grouped combine is stale or its geometry changed".into());
4427        }
4428        combine.output_generation = None;
4429        combine.broadcast_generation = None;
4430        for owner in &plan.owners {
4431            if owner.rank == 0 || owner.global_pairs.is_empty() {
4432                continue;
4433            }
4434            let engine = &self.ranks[owner.rank];
4435            let _main = engine.gpu.enter_main()?;
4436            engine.stream().synchronize()?;
4437        }
4438        let root = self
4439            .ranks
4440            .first()
4441            .ok_or("Step owner-grouped combine has no root rank")?;
4442        let _main = root.gpu.enter_main()?;
4443        if root.ctx().ordinal() != combine.root_device {
4444            return Err("Step owner-grouped combine is not resident on the root device".into());
4445        }
4446        for (index, owner) in plan.owners.iter().enumerate() {
4447            let metadata = &combine.owners[index];
4448            if owner.global_pairs.len() != metadata.active_pairs {
4449                return Err(format!(
4450                    "Step owner-grouped combine owner {index} rows {} != metadata {}",
4451                    owner.global_pairs.len(),
4452                    metadata.active_pairs
4453                )
4454                .into());
4455            }
4456            if metadata.active_pairs == 0 {
4457                continue;
4458            }
4459            let values = metadata
4460                .active_pairs
4461                .checked_mul(combine.width)
4462                .ok_or("Step owner-grouped combine peer value count overflow")?;
4463            if owner.rank == 0 {
4464                root.scatter_slot(
4465                    owner.down_workspace.output(),
4466                    &metadata.token_rows,
4467                    &metadata.slots,
4468                    &metadata.weights,
4469                    &mut combine.slots,
4470                    &mut combine.weights,
4471                    combine.width,
4472                    combine.experts_per_token,
4473                    metadata.active_pairs,
4474                )?;
4475            } else {
4476                let source = owner.down_workspace.output().slice(0..values);
4477                let mut destination = combine.peer_staging.slice_mut(0..values);
4478                root.stream().memcpy_dtod(&source, &mut destination)?;
4479                root.scatter_slot(
4480                    &combine.peer_staging,
4481                    &metadata.token_rows,
4482                    &metadata.slots,
4483                    &metadata.weights,
4484                    &mut combine.slots,
4485                    &mut combine.weights,
4486                    combine.width,
4487                    combine.experts_per_token,
4488                    metadata.active_pairs,
4489                )?;
4490            }
4491        }
4492        root.reduce_slots_host(
4493            &combine.slots,
4494            &combine.weights,
4495            &mut combine.output,
4496            combine.width,
4497            combine.experts_per_token,
4498            combine.tokens,
4499        )?;
4500        combine.output_generation = Some(plan.generation);
4501        Ok(())
4502    }
4503
4504    pub fn collect_step_grouped_expert_parallel_combine(
4505        &self,
4506        plan: &PreparedStepGroupedExpertParallelGate,
4507        combine: &PreparedPeerWeightedRouteCombine,
4508    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4509        if !plan.ready
4510            || combine.output_generation != Some(plan.generation)
4511            || combine.projection_generation != plan.generation
4512        {
4513            return Err("Step owner-grouped combine output is stale or has not executed".into());
4514        }
4515        let root = self
4516            .ranks
4517            .first()
4518            .ok_or("Step owner-grouped combine has no root rank")?;
4519        let _main = root.gpu.enter_main()?;
4520        if root.ctx().ordinal() != combine.root_device {
4521            return Err("Step owner-grouped combine is not resident on the root device".into());
4522        }
4523        root.dtoh_view(&combine.output.slice(0..combine.tokens * combine.width))
4524    }
4525
4526    /// Copy the active root combine result into a caller-owned engine on the same CUDA device.
4527    ///
4528    /// The persistent combine buffer remains reusable by the next route generation; the returned
4529    /// allocation follows the serving runtime's ordinary transient-output ownership.
4530    pub fn copy_step_grouped_expert_parallel_combine_root(
4531        &self,
4532        plan: &PreparedStepGroupedExpertParallelGate,
4533        combine: &PreparedPeerWeightedRouteCombine,
4534        destination: &Engine,
4535    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4536        if !plan.ready
4537            || combine.output_generation != Some(plan.generation)
4538            || combine.projection_generation != plan.generation
4539        {
4540            return Err("Step owner-grouped combine output is stale or has not executed".into());
4541        }
4542        let root = self
4543            .ranks
4544            .first()
4545            .ok_or("Step owner-grouped combine has no root rank")?;
4546        if root.ctx().ordinal() != combine.root_device
4547            || destination.ctx().ordinal() != combine.root_device
4548        {
4549            return Err(format!(
4550                "Step owner-grouped combine root/destination devices {}/{} != {}",
4551                root.ctx().ordinal(),
4552                destination.ctx().ordinal(),
4553                combine.root_device,
4554            )
4555            .into());
4556        }
4557        let values = combine
4558            .tokens
4559            .checked_mul(combine.width)
4560            .ok_or("Step owner-grouped combine copy size overflow")?;
4561        {
4562            let _main = root.gpu.enter_main()?;
4563            root.stream().synchronize()?;
4564        }
4565        let _main = destination.gpu.enter_main()?;
4566        let mut output = destination.uninit(values)?;
4567        destination
4568            .stream()
4569            .memcpy_dtod(&combine.output.slice(0..values), &mut output)?;
4570        Ok(output)
4571    }
4572
4573    pub fn broadcast_step_grouped_expert_parallel_combine(
4574        &self,
4575        plan: &PreparedStepGroupedExpertParallelGate,
4576        combine: &mut PreparedPeerWeightedRouteCombine,
4577    ) -> Result<(), Box<dyn std::error::Error>> {
4578        if !plan.ready
4579            || combine.output_generation != Some(plan.generation)
4580            || combine.projection_generation != plan.generation
4581            || combine.peer_devices.len() + 1 != self.ranks.len()
4582            || combine.peer_outputs.len() + 1 != self.ranks.len()
4583        {
4584            return Err("Step owner-grouped combine output cannot be broadcast".into());
4585        }
4586        combine.broadcast_generation = None;
4587        let values = combine
4588            .tokens
4589            .checked_mul(combine.width)
4590            .ok_or("Step owner-grouped combine broadcast size overflow")?;
4591        {
4592            let root = self
4593                .ranks
4594                .first()
4595                .ok_or("Step owner-grouped combine has no root rank")?;
4596            let _main = root.gpu.enter_main()?;
4597            if root.ctx().ordinal() != combine.root_device {
4598                return Err("Step owner-grouped combine root device changed".into());
4599            }
4600            root.stream().synchronize()?;
4601        }
4602        let source = &combine.output;
4603        for (index, destination_buffer) in combine.peer_outputs.iter_mut().enumerate() {
4604            let engine = &self.ranks[index + 1];
4605            let _main = engine.gpu.enter_main()?;
4606            if engine.ctx().ordinal() != combine.peer_devices[index] {
4607                return Err(format!(
4608                    "Step owner-grouped combine peer {} device changed",
4609                    index + 1
4610                )
4611                .into());
4612            }
4613            let mut destination = destination_buffer.slice_mut(0..values);
4614            engine
4615                .stream()
4616                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
4617        }
4618        combine.broadcast_generation = Some(plan.generation);
4619        Ok(())
4620    }
4621
4622    pub fn collect_step_grouped_expert_parallel_broadcast(
4623        &self,
4624        plan: &PreparedStepGroupedExpertParallelGate,
4625        combine: &PreparedPeerWeightedRouteCombine,
4626    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
4627        if !plan.ready
4628            || combine.output_generation != Some(plan.generation)
4629            || combine.broadcast_generation != Some(plan.generation)
4630            || combine.peer_outputs.len() + 1 != self.ranks.len()
4631        {
4632            return Err("Step owner-grouped combine broadcast is stale or incomplete".into());
4633        }
4634        let values = combine
4635            .tokens
4636            .checked_mul(combine.width)
4637            .ok_or("Step owner-grouped combine collection size overflow")?;
4638        let mut outputs = Vec::with_capacity(self.ranks.len());
4639        {
4640            let root = &self.ranks[0];
4641            let _main = root.gpu.enter_main()?;
4642            outputs.push(root.dtoh_view(&combine.output.slice(0..values))?);
4643        }
4644        for (index, output) in combine.peer_outputs.iter().enumerate() {
4645            let engine = &self.ranks[index + 1];
4646            let _main = engine.gpu.enter_main()?;
4647            outputs.push(engine.dtoh_view(&output.slice(0..values))?);
4648        }
4649        Ok(outputs)
4650    }
4651
4652    /// Add routed and replicated shared-expert outputs, then add the attention residual.
4653    pub fn finish_step_grouped_expert_parallel_layer(
4654        &self,
4655        plan: &PreparedStepGroupedExpertParallelGate,
4656        combine: &PreparedPeerWeightedRouteCombine,
4657        shared: &ResidentReplicatedDeviceRows,
4658        residual: &ResidentReplicatedDeviceRows,
4659    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
4660        validate_replicated_device_rows(&self.ranks, shared)?;
4661        validate_replicated_device_rows(&self.ranks, residual)?;
4662        if !plan.ready
4663            || plan.executed_generation != Some(plan.generation)
4664            || combine.output_generation != Some(plan.generation)
4665            || combine.broadcast_generation != Some(plan.generation)
4666            || combine.projection_generation != plan.generation
4667            || combine.peer_outputs.len() + 1 != self.ranks.len()
4668            || shared.tokens != combine.tokens
4669            || residual.tokens != combine.tokens
4670            || shared.width != combine.width
4671            || residual.width != combine.width
4672        {
4673            return Err("Step full-layer finish inputs are stale or their geometry changed".into());
4674        }
4675        let values = combine
4676            .tokens
4677            .checked_mul(combine.width)
4678            .ok_or("Step full-layer output size overflow")?;
4679        let mut ranks = Vec::with_capacity(self.ranks.len());
4680        for rank in 0..self.ranks.len() {
4681            let engine = &self.ranks[rank];
4682            let _main = engine.gpu.enter_main()?;
4683            let routed = if rank == 0 {
4684                &combine.output
4685            } else {
4686                &combine.peer_outputs[rank - 1]
4687            };
4688            let mut ffn = engine.uninit(values)?;
4689            engine.add(routed, &shared.ranks[rank], &mut ffn, values)?;
4690            let mut output = engine.uninit(values)?;
4691            engine.add(&residual.ranks[rank], &ffn, &mut output, values)?;
4692            ranks.push(output);
4693        }
4694        Ok(ResidentReplicatedDeviceRows {
4695            ranks,
4696            tokens: combine.tokens,
4697            width: combine.width,
4698        })
4699    }
4700
4701    pub fn run_step_grouped_expert_parallel_combine(
4702        &self,
4703        plan: &PreparedStepGroupedExpertParallelGate,
4704        combine: &mut PreparedPeerWeightedRouteCombine,
4705    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4706        self.execute_step_grouped_expert_parallel_combine(plan, combine)?;
4707        self.collect_step_grouped_expert_parallel_combine(plan, combine)
4708    }
4709
4710    pub fn upload_tensor_parallel(
4711        &self,
4712        gate: E4m3ExpertBank<'_>,
4713        up: E4m3ExpertBank<'_>,
4714        down: E4m3ExpertBank<'_>,
4715    ) -> Result<ResidentTensorParallel, Box<dyn std::error::Error>> {
4716        gate.validate()?;
4717        up.validate()?;
4718        down.validate()?;
4719        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
4720            return Err("TP gate/up/down expert counts differ".into());
4721        }
4722        if gate.in_features != up.in_features || gate.out_features != up.out_features {
4723            return Err("TP gate/up dimensions differ".into());
4724        }
4725        if down.in_features != gate.out_features || down.out_features != gate.in_features {
4726            return Err(format!(
4727                "TP down {}x{} does not invert gate/up {}x{}",
4728                down.out_features, down.in_features, gate.out_features, gate.in_features
4729            )
4730            .into());
4731        }
4732        let tp = self.ranks.len();
4733        validate_column_bank_shape(gate, tp)?;
4734        validate_column_bank_shape(up, tp)?;
4735        validate_row_bank_shape(down, tp)?;
4736
4737        let mut gate_ranks = Vec::with_capacity(tp);
4738        let mut up_ranks = Vec::with_capacity(tp);
4739        let mut down_ranks = Vec::with_capacity(tp);
4740        for (rank, engine) in self.ranks.iter().enumerate() {
4741            gate_ranks.push(upload_column_bank_rank(engine, gate, tp, rank)?);
4742            up_ranks.push(upload_column_bank_rank(engine, up, tp, rank)?);
4743            down_ranks.push(upload_row_bank_rank(engine, down, tp, rank)?);
4744        }
4745        Ok(ResidentTensorParallel {
4746            bank: ResidentTpExpertBank {
4747                gate: gate_ranks,
4748                up: up_ranks,
4749                down: down_ranks,
4750                expert_count: gate.expert_count,
4751                input_width: gate.in_features,
4752                expert_width: gate.out_features,
4753            },
4754        })
4755    }
4756
4757    pub fn run_tensor_parallel_routes(
4758        &self,
4759        experts: &ResidentTensorParallel,
4760        input: &[f32],
4761        tokens: usize,
4762        selected: &[usize],
4763        route_weights: &[f32],
4764        experts_per_token: usize,
4765    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4766        validate_tp_bank_residency(&self.ranks, &experts.bank)?;
4767        validate_activations(input, tokens, experts.bank.input_width)?;
4768        let pairs = tokens
4769            .checked_mul(experts_per_token)
4770            .ok_or("TP route count overflow")?;
4771        if selected.len() != pairs || route_weights.len() != pairs {
4772            return Err(format!(
4773                "TP routes selected={} weights={} != tokens {tokens} x experts/token \
4774                 {experts_per_token} ({pairs})",
4775                selected.len(),
4776                route_weights.len(),
4777            )
4778            .into());
4779        }
4780        if !route_weights.iter().all(|weight| weight.is_finite()) {
4781            return Err("TP route weights contain a non-finite value".into());
4782        }
4783
4784        let mut output = vec![0.0f32; tokens * experts.bank.input_width];
4785        for token in 0..tokens {
4786            let input_row =
4787                &input[token * experts.bank.input_width..(token + 1) * experts.bank.input_width];
4788            for slot in 0..experts_per_token {
4789                let pair = token * experts_per_token + slot;
4790                let expert = selected[pair];
4791                if expert >= experts.bank.expert_count {
4792                    return Err(format!(
4793                        "TP selected expert {expert} outside 0..{}",
4794                        experts.bank.expert_count
4795                    )
4796                    .into());
4797                }
4798                let down = if self.native_p2p {
4799                    self.run_tensor_parallel_expert_native(&experts.bank, expert, input_row)?
4800                } else {
4801                    let gate =
4802                        self.run_column_bank_expert(&experts.bank.gate, expert, input_row)?;
4803                    let up = self.run_column_bank_expert(&experts.bank.up, expert, input_row)?;
4804                    let activated: Vec<f32> = gate
4805                        .iter()
4806                        .zip(&up)
4807                        .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
4808                        .collect();
4809                    debug_assert_eq!(activated.len(), experts.bank.expert_width);
4810                    self.run_row_bank_expert(&experts.bank.down, expert, &activated)?
4811                };
4812                let weight = route_weights[pair];
4813                for (sum, value) in output
4814                    [token * experts.bank.input_width..(token + 1) * experts.bank.input_width]
4815                    .iter_mut()
4816                    .zip(down)
4817                {
4818                    *sum += weight * value;
4819                }
4820            }
4821        }
4822        Ok(output)
4823    }
4824
4825    fn run_column_bank_expert(
4826        &self,
4827        ranks: &[ResidentE4m3ExpertBankRank],
4828        expert: usize,
4829        input: &[f32],
4830    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4831        let local_out = ranks
4832            .first()
4833            .ok_or("TP column bank has no ranks")?
4834            .out_features;
4835        let mut gathered = vec![0.0f32; local_out * ranks.len()];
4836        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
4837            let shard = run_resident_bank_expert(engine, bank, expert, input, 1)?;
4838            gathered[rank * local_out..(rank + 1) * local_out].copy_from_slice(&shard);
4839        }
4840        Ok(gathered)
4841    }
4842
4843    fn run_row_bank_expert(
4844        &self,
4845        ranks: &[ResidentE4m3ExpertBankRank],
4846        expert: usize,
4847        input: &[f32],
4848    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4849        let local_in = ranks.first().ok_or("TP row bank has no ranks")?.in_features;
4850        if input.len() != local_in * ranks.len() {
4851            return Err(format!(
4852                "TP row input {} != {} ranks x {local_in}",
4853                input.len(),
4854                ranks.len()
4855            )
4856            .into());
4857        }
4858        let out_features = ranks[0].out_features;
4859        let mut reduced = vec![0.0f32; out_features];
4860        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
4861            let blocks = bank
4862                .k_blocks
4863                .ok_or("TP row bank is not packed in native K-block order")?;
4864            if blocks * FP8_BLOCK != local_in {
4865                return Err(format!(
4866                    "TP row bank has {blocks} blocks but local input width is {local_in}"
4867                )
4868                .into());
4869            }
4870            for block in 0..blocks {
4871                let global_start = rank * local_in + block * FP8_BLOCK;
4872                let partial = run_resident_bank_expert_block(
4873                    engine,
4874                    bank,
4875                    expert,
4876                    block,
4877                    &input[global_start..global_start + FP8_BLOCK],
4878                )?;
4879                for (sum, value) in reduced.iter_mut().zip(partial) {
4880                    *sum += value;
4881                }
4882            }
4883        }
4884        Ok(reduced)
4885    }
4886
4887    fn run_tensor_parallel_expert_native(
4888        &self,
4889        bank: &ResidentTpExpertBank,
4890        expert: usize,
4891        input: &[f32],
4892    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4893        if !self.native_p2p || self.ranks.len() < 2 {
4894            return Err("native TP expert execution requires at least two P2P ranks".into());
4895        }
4896        let local_out = bank
4897            .gate
4898            .first()
4899            .ok_or("native TP gate bank has no ranks")?
4900            .out_features;
4901        if local_out * self.ranks.len() != bank.expert_width {
4902            return Err(format!(
4903                "native TP gate shards {}x{local_out} != expert width {}",
4904                self.ranks.len(),
4905                bank.expert_width
4906            )
4907            .into());
4908        }
4909
4910        // The caller's routed input is already host-canonical. Upload once on rank zero, then
4911        // broadcast over peer copies so no other rank receives a host-staged duplicate.
4912        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
4913        let root_input = {
4914            let root = &self.ranks[0];
4915            let _main = root.gpu.enter_main()?;
4916            root.htod(input)?
4917        };
4918        rank_inputs.push(root_input);
4919        for engine in &self.ranks[1..] {
4920            let peer_input = {
4921                let _main = engine.gpu.enter_main()?;
4922                let mut peer_input = engine.uninit(input.len())?;
4923                engine
4924                    .stream()
4925                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
4926                peer_input
4927            };
4928            rank_inputs.push(peer_input);
4929        }
4930
4931        let mut gate_shards = Vec::with_capacity(self.ranks.len());
4932        let mut up_shards = Vec::with_capacity(self.ranks.len());
4933        for rank in 0..self.ranks.len() {
4934            gate_shards.push(run_resident_bank_expert_device(
4935                &self.ranks[rank],
4936                &bank.gate[rank],
4937                expert,
4938                &rank_inputs[rank],
4939                1,
4940            )?);
4941            up_shards.push(run_resident_bank_expert_device(
4942                &self.ranks[rank],
4943                &bank.up[rank],
4944                expert,
4945                &rank_inputs[rank],
4946                1,
4947            )?);
4948        }
4949
4950        // Preserve the established canonical activation program for the first native transport
4951        // milestone. The shards move to rank zero over P2P; only the scalar activation expression
4952        // executes on host. A later device-activation increment must earn its own exactness gate.
4953        let gate = self.gather_native_column_shards(&gate_shards, 1, local_out)?;
4954        let up = self.gather_native_column_shards(&up_shards, 1, local_out)?;
4955        let activated = gate
4956            .iter()
4957            .zip(&up)
4958            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
4959            .collect::<Vec<_>>();
4960        debug_assert_eq!(activated.len(), bank.expert_width);
4961
4962        let root_activated = {
4963            let root = &self.ranks[0];
4964            let _main = root.gpu.enter_main()?;
4965            root.htod(&activated)?
4966        };
4967        let mut rank_activated = Vec::with_capacity(self.ranks.len());
4968        for (rank, engine) in self.ranks.iter().enumerate() {
4969            let start = rank * local_out;
4970            let source = root_activated.slice(start..start + local_out);
4971            let local = {
4972                let _main = engine.gpu.enter_main()?;
4973                let mut local = engine.uninit(local_out)?;
4974                engine.stream().memcpy_dtod(&source, &mut local)?;
4975                local
4976            };
4977            rank_activated.push(local);
4978        }
4979
4980        let out_features = bank
4981            .down
4982            .first()
4983            .ok_or("native TP down bank has no ranks")?
4984            .out_features;
4985        let mut reduced = {
4986            let root = &self.ranks[0];
4987            let _main = root.gpu.enter_main()?;
4988            root.htod(&vec![0.0f32; out_features])?
4989        };
4990        let mut remote_partial_keepalive = Vec::new();
4991        for rank in 0..self.ranks.len() {
4992            let down = &bank.down[rank];
4993            let blocks = down
4994                .k_blocks
4995                .ok_or("native TP row bank is not packed in checkpoint-block order")?;
4996            if blocks * FP8_BLOCK != local_out {
4997                return Err(format!(
4998                    "native TP rank {rank} has {blocks} blocks but local activation width is \
4999                     {local_out}"
5000                )
5001                .into());
5002            }
5003            for block in 0..blocks {
5004                let start = block * FP8_BLOCK;
5005                let input_block = rank_activated[rank].slice(start..start + FP8_BLOCK);
5006                let partial = run_resident_bank_expert_block_device(
5007                    &self.ranks[rank],
5008                    down,
5009                    expert,
5010                    block,
5011                    &input_block,
5012                )?;
5013                let root_partial = if rank == 0 {
5014                    partial
5015                } else {
5016                    let root = &self.ranks[0];
5017                    let _main = root.gpu.enter_main()?;
5018                    let mut peer_partial = root.uninit(out_features)?;
5019                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
5020                    remote_partial_keepalive.push(partial);
5021                    peer_partial
5022                };
5023                let next = {
5024                    let root = &self.ranks[0];
5025                    let _main = root.gpu.enter_main()?;
5026                    let mut next = root.uninit(out_features)?;
5027                    root.add(&reduced, &root_partial, &mut next, out_features)?;
5028                    next
5029                };
5030                reduced = next;
5031            }
5032        }
5033        let output = {
5034            let root = &self.ranks[0];
5035            let _main = root.gpu.enter_main()?;
5036            root.dtoh(&reduced)?
5037        };
5038        drop(remote_partial_keepalive);
5039        Ok(output)
5040    }
5041
5042    /// Gather token-major rank-local columns into one canonical root-device matrix.
5043    pub fn gather_native_column_shards_device(
5044        &self,
5045        shards: &[CudaSlice<f32>],
5046        tokens: usize,
5047        local_out: usize,
5048    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5049        let shard_len = tokens
5050            .checked_mul(local_out)
5051            .ok_or("native TP gather shard size overflow")?;
5052        if shards.len() != self.ranks.len() || shards.iter().any(|shard| shard.len() != shard_len) {
5053            return Err("native TP gather shard geometry mismatch".into());
5054        }
5055        // PRODUCER FENCE (2026-08-20 flake fix): the root stream peer-reads shards produced on
5056        // the other ranks' streams; without fencing those producers the copy can read a partial
5057        // kernel output.
5058        for engine in &self.ranks[1..] {
5059            let _main = engine.gpu.enter_main()?;
5060            engine.stream().synchronize()?;
5061        }
5062        let root = &self.ranks[0];
5063        let _main = root.gpu.enter_main()?;
5064        let global_out = shards
5065            .len()
5066            .checked_mul(local_out)
5067            .ok_or("native TP gather output width overflow")?;
5068        let gathered_len = tokens
5069            .checked_mul(global_out)
5070            .ok_or("native TP gather output size overflow")?;
5071        let mut gathered = root.uninit(gathered_len)?;
5072        if self.bulk_p2p {
5073            root.place_rows_strided(&shards[0], &mut gathered, local_out, tokens, global_out, 0)?;
5074            if shards.len() > 1 {
5075                let mut staging = root.uninit(shard_len)?;
5076                for (rank, shard) in shards.iter().enumerate().skip(1) {
5077                    root.stream().memcpy_dtod(shard, &mut staging)?;
5078                    root.place_rows_strided(
5079                        &staging,
5080                        &mut gathered,
5081                        local_out,
5082                        tokens,
5083                        global_out,
5084                        rank * local_out,
5085                    )?;
5086                }
5087            }
5088        } else {
5089            for token in 0..tokens {
5090                for (rank, shard) in shards.iter().enumerate() {
5091                    let source = shard.slice(token * local_out..(token + 1) * local_out);
5092                    let start = token * global_out + rank * local_out;
5093                    let mut destination = gathered.slice_mut(start..start + local_out);
5094                    root.stream().memcpy_dtod(&source, &mut destination)?;
5095                }
5096            }
5097        }
5098        Ok(gathered)
5099    }
5100
5101    pub fn gather_native_column_shards(
5102        &self,
5103        shards: &[CudaSlice<f32>],
5104        tokens: usize,
5105        local_out: usize,
5106    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5107        let gathered = self.gather_native_column_shards_device(shards, tokens, local_out)?;
5108        let root = &self.ranks[0];
5109        let _main = root.gpu.enter_main()?;
5110        root.dtoh(&gathered)
5111    }
5112
5113    pub(crate) fn decode_v2_workspace(&self) -> &std::sync::Mutex<Vec<StepTpDecodeV2Ws>> {
5114        &self.decode_v2
5115    }
5116
5117    /// Build the v2 decode-attention workspace for this layer's geometry on first use, or
5118    /// return the index of the matching one. Attention geometry varies across the trunk
5119    /// (per-layer query-head counts), so workspaces are keyed by their geometry pins — a
5120    /// handful exist per model, never one per layer.
5121    ///
5122    /// Refuses non-F32-resident projections: the v2 driver's bit-exactness claim against v1
5123    /// holds per residency class, and only the mirror class has no per-call weight expansion
5124    /// to hide allocation churn behind.
5125    pub(crate) fn decode_v2_ensure(
5126        &self,
5127        e: &Engine,
5128        q_m: &ResidentBf16ColumnParallel,
5129        k_m: &ResidentBf16ColumnParallel,
5130        v_m: &ResidentBf16ColumnParallel,
5131        o_m: &ResidentStepBf16RowParallel,
5132        heads: usize,
5133    ) -> Result<usize, Box<dyn std::error::Error>> {
5134        if self.ranks.len() > 1 && !self.native_p2p {
5135            return Err("step TP decode v2 requires native P2P ranks".into());
5136        }
5137        let ranks = self.ranks.len();
5138        // Residency contract: the canonical-chunk (non-fused) program needs the F32 mirror;
5139        // the fused-kernel door also reads raw checkpoint bf16 directly (halving the weight
5140        // traffic), so bf16 residency is accepted when that door is on.
5141        let fused_door = step_tp_qkv_fused_enabled()?;
5142        let arm_ok = |weight: &ResidentBf16Weight| match weight {
5143            ResidentBf16Weight::F32(_) => true,
5144            ResidentBf16Weight::Bf16(_) => fused_door,
5145        };
5146        for matrix in [q_m, k_m, v_m] {
5147            validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
5148            if matrix.out_features % ranks != 0 || matrix.in_features != q_m.in_features {
5149                return Err("step TP decode v2 QKV geometry mismatch".into());
5150            }
5151            for rank in &matrix.ranks {
5152                if !arm_ok(&rank.weight) {
5153                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5154                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5155                        .into());
5156                }
5157            }
5158        }
5159        validate_step_bf16_row_residency(&self.ranks, o_m)?;
5160        for blocks in &o_m.ranks {
5161            for block in blocks {
5162                if !arm_ok(&block.weight) {
5163                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5164                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5165                        .into());
5166                }
5167            }
5168        }
5169        if v_m.out_features != k_m.out_features
5170            || o_m.in_features != q_m.out_features
5171            || heads == 0
5172            || heads % ranks != 0
5173        {
5174            return Err("step TP decode v2 K/V/O geometry mismatch".into());
5175        }
5176        let local_q_dim = q_m.out_features / ranks;
5177        let local_kv_dim = k_m.out_features / ranks;
5178        let o_out = o_m.out_features;
5179        let o_block_cols = o_m.canonical_chunk_cols;
5180        let blocks_per_rank = o_m.ranks.first().map(Vec::len).unwrap_or(0);
5181        if blocks_per_rank == 0
5182            || o_m
5183                .ranks
5184                .iter()
5185                .any(|blocks| blocks.len() != blocks_per_rank)
5186            || blocks_per_rank * o_block_cols * ranks != o_m.in_features
5187        {
5188            return Err("step TP decode v2 O canonical block grid mismatch".into());
5189        }
5190
5191        let mut guard = self
5192            .decode_v2
5193            .lock()
5194            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5195        if let Some(index) = guard.iter().position(|ws| {
5196            ws.local_q_dim == local_q_dim
5197                && ws.local_kv_dim == local_kv_dim
5198                && ws.heads == heads
5199                && ws.o_out == o_out
5200                && ws.o_block_cols == o_block_cols
5201                && ws.blocks_per_rank == blocks_per_rank
5202                && ws.e_device == e.ctx().ordinal()
5203                && ws.q.len() == ranks
5204        }) {
5205            return Ok(index);
5206        }
5207
5208        let mut q_raw = Vec::with_capacity(ranks);
5209        let mut k_raw = Vec::with_capacity(ranks);
5210        let mut v_raw = Vec::with_capacity(ranks);
5211        let mut q = Vec::with_capacity(ranks);
5212        let mut k = Vec::with_capacity(ranks);
5213        let mut pos = Vec::with_capacity(ranks);
5214        let mut gate = Vec::with_capacity(ranks);
5215        let mut attn_out = Vec::with_capacity(ranks);
5216        let mut gated = Vec::with_capacity(ranks);
5217        let mut fuse_ctr = Vec::with_capacity(ranks);
5218        let mut o_partials = Vec::with_capacity(ranks);
5219        let mut ev_rank = Vec::with_capacity(ranks);
5220        let direct_join = oproj_direct_on();
5221        for (rank, engine) in self.ranks.iter().enumerate() {
5222            let _main = engine.gpu.enter_main()?;
5223            q_raw.push(engine.uninit(local_q_dim)?);
5224            k_raw.push(engine.uninit(local_kv_dim)?);
5225            v_raw.push(engine.uninit(local_kv_dim)?);
5226            q.push(engine.uninit(local_q_dim)?);
5227            k.push(engine.uninit(local_kv_dim)?);
5228            pos.push(engine.htod_i32(&[0])?);
5229            fuse_ctr.push(engine.stream().clone_htod(&[0u32])?);
5230            gate.push(engine.uninit(heads / ranks)?);
5231            attn_out.push(engine.uninit(local_q_dim)?);
5232            gated.push(engine.uninit(local_q_dim)?);
5233            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
5234            for _ in 0..blocks_per_rank {
5235                // Direct join: peer ranks' partials live on ROOT so the b4 kernel's
5236                // stores land there over P2P (UVA) and no pull copy is needed.
5237                if direct_join && rank != 0 {
5238                    let root = &self.ranks[0];
5239                    let _root_main = root.gpu.enter_main()?;
5240                    rank_partials.push(root.uninit(o_out)?);
5241                } else {
5242                    rank_partials.push(engine.uninit(o_out)?);
5243                }
5244            }
5245            o_partials.push(rank_partials);
5246            ev_rank.push(engine.ctx().new_event(None)?);
5247        }
5248        let root = &self.ranks[0];
5249        let (peer_partial, reduce_a, reduce_b, zeros, k_shadow, v_shadow, ev_refresh, ev_oproj) = {
5250            let _main = root.gpu.enter_main()?;
5251            (
5252                root.uninit(o_out)?,
5253                root.uninit(o_out)?,
5254                root.uninit(o_out)?,
5255                root.htod(&vec![0.0f32; o_out])?,
5256                root.uninit(ranks * local_kv_dim)?,
5257                root.uninit(ranks * local_kv_dim)?,
5258                root.ctx().new_event(None)?,
5259                root.ctx().new_event(None)?,
5260            )
5261        };
5262        let (gate_e, ev_entry) = {
5263            let _main = e.gpu.enter_main()?;
5264            (e.uninit(heads)?, e.ctx().new_event(None)?)
5265        };
5266        let raw_attn_in = Vec::new();
5267        let raw_pos = Vec::new();
5268        guard.push(StepTpDecodeV2Ws {
5269            tcol_q: Vec::new(),
5270            tcol_k: Vec::new(),
5271            tcol_v: Vec::new(),
5272            tcol_g: Vec::new(),
5273            tcol_in: Vec::new(),
5274            tcol_cap: 0,
5275            fa2_q: Vec::new(),
5276            fa2_gate: Vec::new(),
5277            fa2_gated: Vec::new(),
5278            fa2_cap: 0,
5279            tcol_gated: Vec::new(),
5280            tcol_opart: Vec::new(),
5281            tcol_opeer: None,
5282            tcol_omix: None,
5283            tcol_ocap: 0,
5284            q_raw,
5285            k_raw,
5286            v_raw,
5287            q,
5288            k,
5289            pos,
5290            fuse_ctr,
5291            gate,
5292            attn_out,
5293            gated,
5294            o_partials,
5295            ev_rank,
5296            peer_partial,
5297            reduce_a,
5298            reduce_b,
5299            zeros,
5300            k_shadow,
5301            v_shadow,
5302            ev_refresh,
5303            ev_oproj,
5304            gate_e,
5305            attn_in: Vec::new(),
5306            h_stage: None,
5307            pos_stage: None,
5308            raw_h_stage: 0,
5309            raw_pos_stage: 0,
5310            raw_attn_in,
5311            raw_pos,
5312            raw_o_partial1: 0,
5313            raw_peer_partial: 0,
5314            raw_k1: 0,
5315            raw_v1: 0,
5316            raw_k_shadow: 0,
5317            raw_v_shadow: 0,
5318            raw_mixed_stage_e: 0,
5319            raw_reduce_a: 0,
5320            raw_shadow_stage_e: (0, 0),
5321            ev_entry,
5322            e_device: e.ctx().ordinal(),
5323            local_q_dim,
5324            local_kv_dim,
5325            heads,
5326            o_out,
5327            o_block_cols,
5328            blocks_per_rank,
5329        });
5330        eprintln!(
5331            "[step-tp-decode-v2] workspace ranks={ranks} local_q={local_q_dim} \
5332             local_kv={local_kv_dim} heads={heads} o_blocks={blocks_per_rank}x{o_block_cols} \
5333             residency=persistent ordering=evented performance_claim=false"
5334        );
5335        Ok(guard.len() - 1)
5336    }
5337
5338    /// v2 phase 1: replicate the layer input, project QKV, norm, rope, and stage the gate —
5339    /// all into the persistent workspace, ordered by events instead of host syncs.
5340    ///
5341    /// The caller must have queued every producer of `h`, `pos_d`, and `gate_raw` on `e`'s
5342    /// stream BEFORE this call: `ev_entry` is recorded once here and every rank stream waits
5343    /// on it (the entry fence also guards workspace reuse across layers — any consumer of the
5344    /// previous layer's outputs was queued on `e`'s stream before this record).
5345    #[allow(clippy::too_many_arguments)]
5346    /// T-COLUMN verify precompute (spec MTP): stage T input rows to every rank and run the
5347    /// weight-amortized qkvg_tcol per rank into the ws slabs. Rope/norm/append stay per
5348    /// column in the unmodified t=1 program (defer_norm_rope contract). Bit-exact per
5349    /// column vs the t=1 kernel by construction.
5350    #[allow(clippy::too_many_arguments)]
5351    pub fn decode_v2_input_qkv_tcol(
5352        &self,
5353        ws_index: usize,
5354        e: &Engine,
5355        h_t: &CudaSlice<f32>,
5356        t: usize,
5357        q_m: &ResidentBf16ColumnParallel,
5358        k_m: &ResidentBf16ColumnParallel,
5359        v_m: &ResidentBf16ColumnParallel,
5360        gate_shards: Option<StepTpGateShards<'_>>,
5361    ) -> Result<(), Box<dyn std::error::Error>> {
5362        let ranks = self.ranks.len();
5363        let mut guard = self
5364            .decode_v2
5365            .lock()
5366            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5367        let ws = guard
5368            .get_mut(ws_index)
5369            .ok_or("step TP decode v2 workspace index out of range")?;
5370        let in_f = q_m.in_features;
5371        if h_t.len() < t * in_f || t == 0 || t > 8 {
5372            return Err("decode_v2_input_qkv_tcol geometry".into());
5373        }
5374        // Lazily arm the slabs to capacity.
5375        if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
5376            ws.tcol_q.clear();
5377            ws.tcol_k.clear();
5378            ws.tcol_v.clear();
5379            ws.tcol_g.clear();
5380            ws.tcol_in.clear();
5381            for engine in &self.ranks {
5382                let _m = engine.gpu.enter_main()?;
5383                ws.tcol_q.push(engine.uninit(8 * ws.local_q_dim)?);
5384                ws.tcol_k.push(engine.uninit(8 * ws.local_kv_dim)?);
5385                ws.tcol_v.push(engine.uninit(8 * ws.local_kv_dim)?);
5386                ws.tcol_g
5387                    .push(engine.uninit(8 * (ws.heads / ranks).max(1))?);
5388                ws.tcol_in.push(engine.uninit(8 * in_f)?);
5389            }
5390            ws.tcol_cap = 8;
5391        }
5392        // Stage the T input rows on e, fence, per-rank pull + tcol launch.
5393        use cudarc::driver::DevicePtr;
5394        let raw_src = {
5395            let _main = e.gpu.enter_main()?;
5396            let stream = e.stream();
5397            let (p, _g) = h_t.device_ptr(&stream);
5398            ws.ev_entry.record(&stream)?;
5399            p as u64
5400        };
5401        for rank in 0..ranks {
5402            let engine = &self.ranks[rank];
5403            let _main = engine.gpu.enter_main()?;
5404            engine.stream().wait(&ws.ev_entry)?;
5405            let raw_dst = {
5406                let stream = engine.stream();
5407                let (p, _g) = ws.tcol_in[rank].device_ptr(&stream);
5408                p as u64
5409            };
5410            raw_copy_bytes(raw_dst, raw_src, t * in_f * 4, engine)?;
5411            let out_g = match &gate_shards {
5412                Some(_) => ws.heads / ranks,
5413                None => 0,
5414            };
5415            match (
5416                &q_m.ranks[rank].weight,
5417                &k_m.ranks[rank].weight,
5418                &v_m.ranks[rank].weight,
5419            ) {
5420                (
5421                    ResidentBf16Weight::Bf16(wq),
5422                    ResidentBf16Weight::Bf16(wk),
5423                    ResidentBf16Weight::Bf16(wv),
5424                ) => {
5425                    let wg = match &gate_shards {
5426                        Some(StepTpGateShards::Bf16(shards)) => &shards[rank],
5427                        Some(StepTpGateShards::F32(_)) => {
5428                            return Err(
5429                                "tcol verify: gate shard class does not match bf16 QKV".into()
5430                            );
5431                        }
5432                        None => wq,
5433                    };
5434                    let StepTpDecodeV2Ws {
5435                        tcol_q,
5436                        tcol_k,
5437                        tcol_v,
5438                        tcol_g,
5439                        tcol_in,
5440                        local_q_dim,
5441                        local_kv_dim,
5442                        ..
5443                    } = &mut *ws;
5444                    // MEMRA_TCOL_REFKERN=1 (bisect): fill the slabs via the t=1 kernel per
5445                    // column — separates driver bugs from tcol-kernel bugs.
5446                    static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5447                    let refk = *REFK
5448                        .get_or_init(|| std::env::var("MEMRA_TCOL_REFKERN").as_deref() == Ok("1"));
5449                    if refk {
5450                        let lq = *local_q_dim;
5451                        let lkv = *local_kv_dim;
5452                        let mut hrow = engine.uninit(in_f)?;
5453                        let mut qr = engine.uninit(lq)?;
5454                        let mut kr = engine.uninit(lkv)?;
5455                        let mut vr = engine.uninit(lkv)?;
5456                        let mut gr = engine.uninit(out_g.max(1))?;
5457                        for c in 0..t {
5458                            {
5459                                let mut dst = hrow.slice_mut(0..in_f);
5460                                engine.stream().memcpy_dtod(
5461                                    &tcol_in[rank].slice(c * in_f..(c + 1) * in_f),
5462                                    &mut dst,
5463                                )?;
5464                            }
5465                            engine.matvec_bf16_qkvg_into(
5466                                wq, wk, wv, wg, &hrow, &mut qr, &mut kr, &mut vr, &mut gr, in_f,
5467                                lq, lkv, out_g,
5468                            )?;
5469                            let stream = engine.stream();
5470                            {
5471                                let mut dst = tcol_q[rank].slice_mut(c * lq..(c + 1) * lq);
5472                                stream.memcpy_dtod(&qr.slice(0..lq), &mut dst)?;
5473                            }
5474                            {
5475                                let mut dst = tcol_k[rank].slice_mut(c * lkv..(c + 1) * lkv);
5476                                stream.memcpy_dtod(&kr.slice(0..lkv), &mut dst)?;
5477                            }
5478                            {
5479                                let mut dst = tcol_v[rank].slice_mut(c * lkv..(c + 1) * lkv);
5480                                stream.memcpy_dtod(&vr.slice(0..lkv), &mut dst)?;
5481                            }
5482                            if out_g > 0 {
5483                                let mut dst = tcol_g[rank].slice_mut(c * out_g..(c + 1) * out_g);
5484                                stream.memcpy_dtod(&gr.slice(0..out_g), &mut dst)?;
5485                            }
5486                        }
5487                    } else {
5488                        engine.matvec_bf16_qkvg_tcol_into(
5489                            wq,
5490                            wk,
5491                            wv,
5492                            wg,
5493                            &tcol_in[rank],
5494                            &mut tcol_q[rank],
5495                            &mut tcol_k[rank],
5496                            &mut tcol_v[rank],
5497                            &mut tcol_g[rank],
5498                            in_f,
5499                            *local_q_dim,
5500                            *local_kv_dim,
5501                            out_g,
5502                            t,
5503                        )?;
5504                    }
5505                }
5506                _ => return Err("tcol verify requires bf16-resident fused QKV".into()),
5507            }
5508        }
5509        Ok(())
5510    }
5511
5512    /// MEMRA_TCOL_OPROJ eligibility: the defer replaces exactly the o_fused direct-join
5513    /// finish (bf16 b4 kernel, 2 ranks, 4 canonical blocks) with the shadow gathers
5514    /// skipped — so it requires the same doors that arm dictate that finish shape.
5515    pub(crate) fn decode_v2_oproj_tcol_eligible(
5516        &self,
5517        ws: &StepTpDecodeV2Ws,
5518        o_m: &ResidentStepBf16RowParallel,
5519    ) -> bool {
5520        self.ranks.len() == 2
5521            && ws.blocks_per_rank == 4
5522            && step_tp_qkv_fused_enabled().unwrap_or(false)
5523            && no_local_shadow_on()
5524            && std::env::var("MEMRA_B4_X2").as_deref() != Ok("1")
5525            && o_m
5526                .ranks
5527                .iter()
5528                .flatten()
5529                .all(|block| matches!(block.weight, ResidentBf16Weight::Bf16(_)))
5530    }
5531
5532    /// MEMRA_SPEC_FA2 stash: copy this column's per-rank post-rope q and gate rows into
5533    /// the fa2 slabs (rank-stream ordered behind the rope/append that produced them), and
5534    /// give `e` the same anti-dependency wait the skipped finish provided (next column's
5535    /// h/pos re-staging must not overtake this column's rank pulls).
5536    pub(crate) fn decode_v2_stash_fa2(
5537        &self,
5538        ws: &mut StepTpDecodeV2Ws,
5539        e: &Engine,
5540        col: usize,
5541    ) -> Result<(), Box<dyn std::error::Error>> {
5542        let ranks = self.ranks.len();
5543        if col >= 2 {
5544            return Err("decode_v2_stash_fa2 column out of range".into());
5545        }
5546        let lq = ws.local_q_dim;
5547        let lg = (ws.heads / ranks).max(1);
5548        if ws.fa2_cap == 0 || ws.fa2_q.len() != ranks {
5549            ws.fa2_q.clear();
5550            ws.fa2_gate.clear();
5551            ws.fa2_gated.clear();
5552            for engine in &self.ranks {
5553                let _m = engine.gpu.enter_main()?;
5554                ws.fa2_q.push(engine.uninit(2 * lq)?);
5555                ws.fa2_gate.push(engine.uninit(2 * lg)?);
5556                ws.fa2_gated.push(engine.uninit(2 * lq)?);
5557            }
5558            ws.fa2_cap = 2;
5559        }
5560        for rank in 0..ranks {
5561            let engine = &self.ranks[rank];
5562            let _main = engine.gpu.enter_main()?;
5563            {
5564                let mut dst = ws.fa2_q[rank].slice_mut(col * lq..(col + 1) * lq);
5565                engine
5566                    .stream()
5567                    .memcpy_dtod(&ws.q[rank].slice(0..lq), &mut dst)?;
5568            }
5569            {
5570                let mut dst = ws.fa2_gate[rank].slice_mut(col * lg..(col + 1) * lg);
5571                engine
5572                    .stream()
5573                    .memcpy_dtod(&ws.gate[rank].slice(0..lg), &mut dst)?;
5574            }
5575            ws.ev_rank[rank].record(&engine.stream())?;
5576        }
5577        {
5578            let _main = e.gpu.enter_main()?;
5579            for ev in ws.ev_rank.iter() {
5580                e.stream().wait(ev)?;
5581            }
5582        }
5583        Ok(())
5584    }
5585
5586    /// MEMRA_SPEC_FA2 join: after BOTH verify columns stashed (their appends landed in
5587    /// rank-stream order), run ONE fa_decode_dcw2 per rank over the shared KV stream —
5588    /// two query rows, per-row causal bounds, per-row combine+gate — then land the two
5589    /// gated rows in the o-tcol slabs and reuse the weight-amortized o_proj join.
5590    /// Returns the [2, o_out] `mixed` slab on `e`. The caller's precheck enforced the
5591    /// equal-partition guard (boundary rounds never arm the defer).
5592    #[allow(clippy::too_many_arguments)]
5593    pub(crate) fn decode_v2_spec_fa2_join(
5594        &self,
5595        ws_index: usize,
5596        e: &Engine,
5597        o_m: &ResidentStepBf16RowParallel,
5598        kv: &ResidentTpKvCache,
5599        head_dim: usize,
5600        window: usize,
5601        bucket_max: usize,
5602        scale: f32,
5603    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5604        let ranks = self.ranks.len();
5605        // Engagement receipt: a vacuous gate (precheck never passing) must be visible.
5606        static ONCE: std::sync::Once = std::sync::Once::new();
5607        ONCE.call_once(|| eprintln!("[spec-fa2] joined T=2 attention ENGAGED"));
5608        {
5609            let mut guard = self
5610                .decode_v2
5611                .lock()
5612                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5613            let ws = guard
5614                .get_mut(ws_index)
5615                .ok_or("step TP decode v2 workspace index out of range")?;
5616            if ws.fa2_cap < 2 || ws.fa2_q.len() != ranks {
5617                return Err("spec fa2 join without stashed columns".into());
5618            }
5619            let lq = ws.local_q_dim;
5620            let local_heads = (ws.heads / ranks).max(1);
5621            let local_kv_heads = (ws.local_kv_dim / head_dim).max(1);
5622            let capacity = kv.physical_capacity();
5623            let (k_tok_bytes, v_tok_bytes) = (kv.k_tok_bytes(), kv.v_tok_bytes());
5624            // Arm the o-tcol slabs if the oproj door never ran this boot (same shapes).
5625            if ws.tcol_ocap < 2 || ws.tcol_gated.len() != ranks {
5626                ws.tcol_gated.clear();
5627                ws.tcol_opart.clear();
5628                for engine in &self.ranks {
5629                    let _m = engine.gpu.enter_main()?;
5630                    ws.tcol_gated.push(engine.uninit(8 * lq)?);
5631                    ws.tcol_opart.push(engine.uninit(8 * ws.o_out)?);
5632                }
5633                let root = &self.ranks[0];
5634                let _m = root.gpu.enter_main()?;
5635                ws.tcol_opeer = Some(root.uninit(8 * ws.o_out)?);
5636                ws.tcol_omix = Some(root.uninit(8 * ws.o_out)?);
5637                ws.tcol_ocap = 8;
5638            }
5639            for rank in 0..ranks {
5640                let engine = &self.ranks[rank];
5641                let _main = engine.gpu.enter_main()?;
5642                let rank_cache = kv
5643                    .rank(rank)
5644                    .ok_or("spec fa2 join lost its KV cache rank")?;
5645                let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes);
5646                let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes);
5647                {
5648                    let StepTpDecodeV2Ws {
5649                        fa2_q,
5650                        fa2_gate,
5651                        fa2_gated,
5652                        ..
5653                    } = &mut *ws;
5654                    engine.fa_decode_dcw2(
5655                        &fa2_q[rank],
5656                        &k_ring,
5657                        &v_ring,
5658                        &mut fa2_gated[rank],
5659                        head_dim,
5660                        local_heads,
5661                        local_kv_heads,
5662                        rank_cache.len_d(),
5663                        rank_cache.base_d(),
5664                        window,
5665                        bucket_max,
5666                        scale,
5667                        k_tok_bytes,
5668                        v_tok_bytes,
5669                        &fa2_gate[rank],
5670                    )?;
5671                }
5672                // Both gated rows are contiguous [2, lq] — exactly columns 0..2 of the
5673                // o-tcol slab layout. One dtod, in rank-stream order behind the fa.
5674                let StepTpDecodeV2Ws {
5675                    fa2_gated,
5676                    tcol_gated,
5677                    ..
5678                } = &mut *ws;
5679                let mut dst = tcol_gated[rank].slice_mut(0..2 * lq);
5680                engine
5681                    .stream()
5682                    .memcpy_dtod(&fa2_gated[rank].slice(0..2 * lq), &mut dst)?;
5683            }
5684        }
5685        self.decode_v2_oproj_tcol(ws_index, e, o_m, 2)
5686    }
5687
5688    /// MEMRA_TCOL_OPROJ stash: copy this column's per-rank `gated` rows into the o-tcol
5689    /// slabs (rank-stream ordered behind the attention kernels that produced them). The
5690    /// per-column finish choreography is skipped entirely; `decode_v2_oproj_tcol` joins
5691    /// every column afterwards.
5692    pub(crate) fn decode_v2_stash_gated(
5693        &self,
5694        ws: &mut StepTpDecodeV2Ws,
5695        e: &Engine,
5696        col: usize,
5697    ) -> Result<(), Box<dyn std::error::Error>> {
5698        let ranks = self.ranks.len();
5699        if col >= 8 {
5700            return Err("decode_v2_stash_gated column out of range".into());
5701        }
5702        let lq = ws.local_q_dim;
5703        if ws.tcol_ocap == 0 || ws.tcol_gated.len() != ranks {
5704            ws.tcol_gated.clear();
5705            ws.tcol_opart.clear();
5706            for engine in &self.ranks {
5707                let _m = engine.gpu.enter_main()?;
5708                ws.tcol_gated.push(engine.uninit(8 * lq)?);
5709                ws.tcol_opart.push(engine.uninit(8 * ws.o_out)?);
5710            }
5711            let root = &self.ranks[0];
5712            let _m = root.gpu.enter_main()?;
5713            ws.tcol_opeer = Some(root.uninit(8 * ws.o_out)?);
5714            ws.tcol_omix = Some(root.uninit(8 * ws.o_out)?);
5715            ws.tcol_ocap = 8;
5716        }
5717        for rank in 0..ranks {
5718            let engine = &self.ranks[rank];
5719            let _main = engine.gpu.enter_main()?;
5720            let mut dst = ws.tcol_gated[rank].slice_mut(col * lq..(col + 1) * lq);
5721            engine
5722                .stream()
5723                .memcpy_dtod(&ws.gated[rank].slice(0..lq), &mut dst)?;
5724            // The skipped finish's e-wait was ALSO the anti-dependency guard: it ordered
5725            // e's NEXT column's h/pos re-staging behind this column's rank-side raw pulls.
5726            // Record each rank here and make e wait — same protection, no o_proj work.
5727            ws.ev_rank[rank].record(&engine.stream())?;
5728        }
5729        {
5730            let _main = e.gpu.enter_main()?;
5731            for ev in ws.ev_rank.iter() {
5732                e.stream().wait(ev)?;
5733            }
5734        }
5735        Ok(())
5736    }
5737
5738    /// MEMRA_TCOL_OPROJ join: one weight-amortized b4_tcol per rank over the stashed
5739    /// `gated` slabs (per-column FP order == the t=1 b4 kernel), one peer pull of rank1's
5740    /// partial slab, one elementwise slab add on the root (independent elements — each
5741    /// column's add is the exact direct-join `add(p0, p1)`), then the joined `mixed` slab
5742    /// lands on `e`. Returns [t, o_out] on the model engine.
5743    pub(crate) fn decode_v2_oproj_tcol(
5744        &self,
5745        ws_index: usize,
5746        e: &Engine,
5747        o_m: &ResidentStepBf16RowParallel,
5748        t: usize,
5749    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5750        let ranks = self.ranks.len();
5751        let mut guard = self
5752            .decode_v2
5753            .lock()
5754            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5755        let ws = guard
5756            .get_mut(ws_index)
5757            .ok_or("step TP decode v2 workspace index out of range")?;
5758        if ranks != 2 || ws.blocks_per_rank != 4 || t == 0 || t > 8 || ws.tcol_ocap < t {
5759            return Err("decode_v2_oproj_tcol geometry".into());
5760        }
5761        for rank in 0..ranks {
5762            let engine = &self.ranks[rank];
5763            let _main = engine.gpu.enter_main()?;
5764            let mut weights = Vec::with_capacity(4);
5765            for block in 0..4 {
5766                let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
5767                    return Err("tcol o_proj requires bf16-resident O blocks".into());
5768                };
5769                weights.push(weight);
5770            }
5771            {
5772                let StepTpDecodeV2Ws {
5773                    tcol_gated,
5774                    tcol_opart,
5775                    local_q_dim,
5776                    o_block_cols,
5777                    o_out,
5778                    ..
5779                } = &mut *ws;
5780                // MEMRA_TCOL_OPROJ_REF=1 (bisect): fill the partial slab via the t=1 b4
5781                // kernel per column — separates choreography bugs from tcol-kernel bugs.
5782                static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5783                let refk = *REFK
5784                    .get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ_REF").as_deref() == Ok("1"));
5785                if refk {
5786                    let lq = *local_q_dim;
5787                    let mut xr = engine.uninit(lq)?;
5788                    let mut yr = engine.uninit(*o_out)?;
5789                    for c in 0..t {
5790                        {
5791                            let mut dst = xr.slice_mut(0..lq);
5792                            engine.stream().memcpy_dtod(
5793                                &tcol_gated[rank].slice(c * lq..(c + 1) * lq),
5794                                &mut dst,
5795                            )?;
5796                        }
5797                        engine.matvec_bf16_b4_into(
5798                            [weights[0], weights[1], weights[2], weights[3]],
5799                            &xr,
5800                            &mut yr,
5801                            *o_block_cols,
5802                            *o_out,
5803                        )?;
5804                        let mut dst = tcol_opart[rank].slice_mut(c * *o_out..(c + 1) * *o_out);
5805                        engine
5806                            .stream()
5807                            .memcpy_dtod(&yr.slice(0..*o_out), &mut dst)?;
5808                    }
5809                } else {
5810                    engine.matvec_bf16_b4_tcol_into(
5811                        [weights[0], weights[1], weights[2], weights[3]],
5812                        &tcol_gated[rank],
5813                        &mut tcol_opart[rank],
5814                        *o_block_cols,
5815                        *o_out,
5816                        t,
5817                    )?;
5818                }
5819            }
5820            if rank != 0 {
5821                ws.ev_rank[rank].record(&engine.stream())?;
5822            }
5823        }
5824        let root = &self.ranks[0];
5825        {
5826            let _main = root.gpu.enter_main()?;
5827            for ev in ws.ev_rank.iter().skip(1) {
5828                root.stream().wait(ev)?;
5829            }
5830            {
5831                let StepTpDecodeV2Ws {
5832                    tcol_opart,
5833                    tcol_opeer,
5834                    tcol_omix,
5835                    o_out,
5836                    ..
5837                } = &mut *ws;
5838                let opeer = tcol_opeer.as_mut().ok_or("tcol o_proj slabs not armed")?;
5839                let omix = tcol_omix.as_mut().ok_or("tcol o_proj slabs not armed")?;
5840                {
5841                    let mut dst = opeer.slice_mut(0..t * *o_out);
5842                    root.stream()
5843                        .memcpy_dtod(&tcol_opart[1].slice(0..t * *o_out), &mut dst)?;
5844                }
5845                // Elementwise over the whole slab: per element identical to the per-column
5846                // direct-join add (independent lanes, same operand values).
5847                root.add(&tcol_opart[0], opeer, omix, t * *o_out)?;
5848            }
5849            ws.ev_oproj.record(&root.stream())?;
5850        }
5851        let _main = e.gpu.enter_main()?;
5852        e.stream().wait(&ws.ev_oproj)?;
5853        let mut out = e.uninit(t * ws.o_out)?;
5854        let omix = ws.tcol_omix.as_ref().ok_or("tcol o_proj slabs not armed")?;
5855        e.stream().memcpy_dtod(
5856            &omix.slice(0..t * ws.o_out),
5857            &mut out.slice_mut(0..t * ws.o_out),
5858        )?;
5859        Ok(out)
5860    }
5861
5862    pub(crate) fn decode_v2_input_qkv(
5863        &self,
5864        ws: &mut StepTpDecodeV2Ws,
5865        e: &Engine,
5866        h: &CudaSlice<f32>,
5867        pos_d: &CudaSlice<i32>,
5868        gate_raw: Option<&CudaSlice<f32>>,
5869        gate_shards: Option<StepTpGateShards<'_>>,
5870        decode_input: &mut ResidentReplicatedDeviceRows,
5871        q_m: &ResidentBf16ColumnParallel,
5872        k_m: &ResidentBf16ColumnParallel,
5873        v_m: &ResidentBf16ColumnParallel,
5874        q_norm: &[CudaSlice<f32>],
5875        k_norm: &[CudaSlice<f32>],
5876        head_dim: usize,
5877        n_rot: usize,
5878        rope_base: f32,
5879        rope_freqs: &[Option<&CudaSlice<f32>>],
5880        rms_eps: f32,
5881        defer_norm_rope: bool,
5882        tcol_col: Option<usize>,
5883    ) -> Result<(), Box<dyn std::error::Error>> {
5884        let ranks = self.ranks.len();
5885        validate_replicated_device_rows(&self.ranks, decode_input)?;
5886        if decode_input.tokens != 1
5887            || decode_input.width != q_m.in_features
5888            || pos_d.len() != 1
5889            || gate_raw.is_some_and(|gate| gate.len() != ws.heads)
5890            || gate_raw.is_none() != gate_shards.is_some()
5891            || gate_shards.as_ref().is_some_and(|shards| match shards {
5892                StepTpGateShards::F32(shards) => shards.len() != ranks,
5893                StepTpGateShards::Bf16(shards) => shards.len() != ranks,
5894            })
5895            || q_norm.len() != ranks
5896            || k_norm.len() != ranks
5897            || rope_freqs.len() != ranks
5898            || e.ctx().ordinal() != ws.e_device
5899        {
5900            return Err("step TP decode v2 input geometry mismatch".into());
5901        }
5902
5903        let qkv_fused = step_tp_qkv_fused_enabled()?;
5904        if gate_shards.is_some() && !qkv_fused {
5905            return Err("step TP decode v2 gate shards require MEMRA_STEP_TP_QKV_FUSED=1".into());
5906        }
5907        let values = decode_input.width;
5908        if h.len() != values {
5909            return Err(format!(
5910                "step TP decode v2 hidden width {} != replicated width {values}",
5911                h.len()
5912            )
5913            .into());
5914        }
5915
5916        if qkv_fused {
5917            // STAGE-BASED flow (graph increment A): h and pos land in fixed e-context stages
5918            // (one e-stream copy each), the entry event covers them, and every rank raw-copies
5919            // from the stages on its own stream — exactly the shape graph capture wraps.
5920            if ws.h_stage.is_none() {
5921                use cudarc::driver::DevicePtr;
5922                let _main = e.gpu.enter_main()?;
5923                let h_stage = e.uninit(values)?;
5924                let pos_stage = e.htod_i32(&[0])?;
5925                {
5926                    let stream = e.stream();
5927                    let (hp, _g0) = h_stage.device_ptr(&stream);
5928                    let (pp, _g1) = pos_stage.device_ptr(&stream);
5929                    ws.raw_h_stage = hp as u64;
5930                    ws.raw_pos_stage = pp as u64;
5931                }
5932                ws.h_stage = Some(h_stage);
5933                ws.pos_stage = Some(pos_stage);
5934                for rank in 0..ranks {
5935                    use cudarc::driver::DevicePtr;
5936                    let engine = &self.ranks[rank];
5937                    let _rmain = engine.gpu.enter_main()?;
5938                    let attn_in = engine.uninit(values)?;
5939                    let (dp, pp) = {
5940                        let stream = engine.stream();
5941                        let (dp, _g2) = attn_in.device_ptr(&stream);
5942                        let (pp, _g3) = ws.pos[rank].device_ptr(&stream);
5943                        (dp as u64, pp as u64)
5944                    };
5945                    ws.raw_attn_in.push(dp);
5946                    ws.raw_pos.push(pp);
5947                    ws.attn_in.push(attn_in);
5948                }
5949                {
5950                    use cudarc::driver::DevicePtr;
5951                    let root = &self.ranks[0];
5952                    let _rmain = root.gpu.enter_main()?;
5953                    let stream = root.stream();
5954                    let (a, _g) = ws.peer_partial.device_ptr(&stream);
5955                    let (b, _g) = ws.k_shadow.device_ptr(&stream);
5956                    let (c, _g) = ws.v_shadow.device_ptr(&stream);
5957                    ws.raw_peer_partial = a as u64;
5958                    ws.raw_k_shadow = b as u64;
5959                    ws.raw_v_shadow = c as u64;
5960                }
5961                {
5962                    use cudarc::driver::DevicePtr;
5963                    let rank1 = &self.ranks[1];
5964                    let _rmain = rank1.gpu.enter_main()?;
5965                    let stream = rank1.stream();
5966                    let (a, _g) = ws.o_partials[1][0].device_ptr(&stream);
5967                    let (b, _g) = ws.k[1].device_ptr(&stream);
5968                    let (c, _g) = ws.v_raw[1].device_ptr(&stream);
5969                    ws.raw_o_partial1 = a as u64;
5970                    ws.raw_k1 = b as u64;
5971                    ws.raw_v1 = c as u64;
5972                }
5973            }
5974            {
5975                let _main = e.gpu.enter_main()?;
5976                {
5977                    // (Always staged: a tcol column below the dcw floor falls back to the
5978                    // normal fused arm, which reads h through this stage.)
5979                    let h_stage = ws.h_stage.as_mut().expect("stage armed above");
5980                    let mut dst = h_stage.slice_mut(0..values);
5981                    e.stream().memcpy_dtod(&h.slice(0..values), &mut dst)?;
5982                }
5983                {
5984                    let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
5985                    let mut dst = pos_stage.slice_mut(0..1);
5986                    e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
5987                }
5988                ws.ev_entry.record(&e.stream())?;
5989            }
5990            for rank in 0..ranks {
5991                let engine = &self.ranks[rank];
5992                let _main = engine.gpu.enter_main()?;
5993                engine.stream().wait(&ws.ev_entry)?;
5994            }
5995        } else {
5996            // Evented replicate flow (the pre-stage shape, kept for the non-fused class).
5997            {
5998                let _main = e.gpu.enter_main()?;
5999                if let Some(gate_raw) = gate_raw {
6000                    let mut gate_dst = ws.gate_e.slice_mut(0..ws.heads);
6001                    e.stream()
6002                        .memcpy_dtod(&gate_raw.slice(0..ws.heads), &mut gate_dst)?;
6003                }
6004                ws.ev_entry.record(&e.stream())?;
6005            }
6006            {
6007                let root = &self.ranks[0];
6008                let _main = root.gpu.enter_main()?;
6009                root.stream().wait(&ws.ev_entry)?;
6010                let mut destination = decode_input.ranks[0].slice_mut(0..values);
6011                root.stream()
6012                    .memcpy_dtod(&h.slice(0..values), &mut destination)?;
6013                ws.ev_refresh.record(&root.stream())?;
6014            }
6015            for rank in 1..ranks {
6016                let engine = &self.ranks[rank];
6017                let _main = engine.gpu.enter_main()?;
6018                engine.stream().wait(&ws.ev_refresh)?;
6019                let (root_rows, peer_rows) = decode_input.ranks.split_at_mut(rank);
6020                let mut destination = peer_rows[0].slice_mut(0..values);
6021                engine
6022                    .stream()
6023                    .memcpy_dtod(&root_rows[0].slice(0..values), &mut destination)?;
6024            }
6025        }
6026        for rank in 0..ranks {
6027            self.decode_v2_input_qkv_rank(
6028                ws,
6029                pos_d,
6030                decode_input,
6031                q_m,
6032                k_m,
6033                v_m,
6034                q_norm,
6035                k_norm,
6036                head_dim,
6037                n_rot,
6038                rope_base,
6039                rope_freqs,
6040                rms_eps,
6041                gate_shards.as_ref(),
6042                qkv_fused,
6043                defer_norm_rope,
6044                rank,
6045                tcol_col,
6046            )?;
6047        }
6048        Ok(())
6049    }
6050
6051    /// One rank's slice of `decode_v2_input_qkv` (projection, norm+rope, gate staging) — the
6052    /// per-device issue unit the whole-token graph captures on that rank's stream.
6053    #[allow(clippy::too_many_arguments)]
6054    pub(crate) fn decode_v2_input_qkv_rank(
6055        &self,
6056        ws: &mut StepTpDecodeV2Ws,
6057        pos_d: &CudaSlice<i32>,
6058        decode_input: &mut ResidentReplicatedDeviceRows,
6059        q_m: &ResidentBf16ColumnParallel,
6060        k_m: &ResidentBf16ColumnParallel,
6061        v_m: &ResidentBf16ColumnParallel,
6062        q_norm: &[CudaSlice<f32>],
6063        k_norm: &[CudaSlice<f32>],
6064        head_dim: usize,
6065        n_rot: usize,
6066        rope_base: f32,
6067        rope_freqs: &[Option<&CudaSlice<f32>>],
6068        rms_eps: f32,
6069        gate_shards: Option<&StepTpGateShards<'_>>,
6070        qkv_fused: bool,
6071        defer_norm_rope: bool,
6072        rank: usize,
6073        tcol_col: Option<usize>,
6074    ) -> Result<(), Box<dyn std::error::Error>> {
6075        let ranks = self.ranks.len();
6076        let local_heads = ws.local_q_dim / head_dim;
6077        let local_kv_heads = ws.local_kv_dim / head_dim;
6078        let engine = &self.ranks[rank];
6079        let _main = engine.gpu.enter_main()?;
6080        let ws_e_device = ws.e_device;
6081        // T-COLUMN SELECT (spec verify): the projections for this column were precomputed
6082        // by the weight-amortized tcol kernel — copy the column into the single-row buffers
6083        // (pure f32 moves, bit-exact) and skip the per-column matvec. Rope/norm/append run
6084        // below exactly as in the t=1 program.
6085        if qkv_fused && tcol_col.is_some() {
6086            let c = tcol_col.expect("checked");
6087            if ws.tcol_cap == 0 || ws.tcol_q.len() != ranks {
6088                return Err("tcol select without precompute".into());
6089            }
6090            // The select skips the matvec but NOT the position: rope/append below still
6091            // read this rank's pos buffer, which only the (skipped) stage path fills for
6092            // peer-device ranks. Stage it here or rank1 ropes at the previous position.
6093            if engine.ctx().ordinal() != ws_e_device {
6094                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
6095            }
6096            let StepTpDecodeV2Ws {
6097                tcol_q,
6098                tcol_k,
6099                tcol_v,
6100                tcol_g,
6101                q_raw,
6102                k_raw,
6103                v_raw,
6104                gate,
6105                local_q_dim,
6106                local_kv_dim,
6107                heads,
6108                ..
6109            } = &mut *ws;
6110            let lg = *heads / ranks;
6111            let stream = engine.stream();
6112            {
6113                let mut dst = q_raw[rank].slice_mut(0..*local_q_dim);
6114                stream.memcpy_dtod(
6115                    &tcol_q[rank].slice(c * *local_q_dim..(c + 1) * *local_q_dim),
6116                    &mut dst,
6117                )?;
6118            }
6119            {
6120                let mut dst = k_raw[rank].slice_mut(0..*local_kv_dim);
6121                stream.memcpy_dtod(
6122                    &tcol_k[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
6123                    &mut dst,
6124                )?;
6125            }
6126            {
6127                let mut dst = v_raw[rank].slice_mut(0..*local_kv_dim);
6128                stream.memcpy_dtod(
6129                    &tcol_v[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
6130                    &mut dst,
6131                )?;
6132            }
6133            if lg > 0 {
6134                let mut dst = gate[rank].slice_mut(0..lg);
6135                stream.memcpy_dtod(&tcol_g[rank].slice(c * lg..(c + 1) * lg), &mut dst)?;
6136            }
6137            if !defer_norm_rope {
6138                // Below the dcw floor (or a non-defer shape) the col-select cannot apply:
6139                // fall through and recompute this column's QKV from the REAL h row — the
6140                // caller always passes it. The slab copies above are dead stores.
6141            } else {
6142                return Ok(());
6143            }
6144        }
6145        if qkv_fused {
6146            // Stage-based input: raw copies from the fixed e-context stages (capture-safe;
6147            // eager ordering comes from the caller's ev_entry wait on this stream). The rank
6148            // SHARING e's device reads the stages directly — same context (probed), ordering
6149            // identical (ev_entry / graph edge), bytes identical: the copies are pure waste.
6150            let same_dev = engine.ctx().ordinal() == ws.e_device;
6151            if !same_dev {
6152                raw_copy_bytes(
6153                    ws.raw_attn_in[rank],
6154                    ws.raw_h_stage,
6155                    q_m.in_features * 4,
6156                    engine,
6157                )?;
6158                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
6159            }
6160            let StepTpDecodeV2Ws {
6161                q_raw,
6162                k_raw,
6163                v_raw,
6164                gate,
6165                gate_e,
6166                attn_in,
6167                h_stage,
6168                heads,
6169                local_q_dim,
6170                local_kv_dim,
6171                ..
6172            } = &mut *ws;
6173            let input_ref: &CudaSlice<f32> = if same_dev {
6174                h_stage
6175                    .as_ref()
6176                    .ok_or("step TP decode v2 stage not armed")?
6177            } else {
6178                &attn_in[rank]
6179            };
6180            match (
6181                &q_m.ranks[rank].weight,
6182                &k_m.ranks[rank].weight,
6183                &v_m.ranks[rank].weight,
6184            ) {
6185                (
6186                    ResidentBf16Weight::F32(wq),
6187                    ResidentBf16Weight::F32(wk),
6188                    ResidentBf16Weight::F32(wv),
6189                ) => {
6190                    let (wg, out_g) = match &gate_shards {
6191                        Some(StepTpGateShards::F32(shards)) => (&shards[rank], *heads / ranks),
6192                        Some(StepTpGateShards::Bf16(_)) => {
6193                            return Err("step TP decode v2 gate shard class does not \
6194                                            match the F32 projections"
6195                                .into());
6196                        }
6197                        // out_g = 0: the kernel never reads wg; any resident buffer works.
6198                        None => (&*gate_e, 0),
6199                    };
6200                    engine.matvec_f32_qkv_into(
6201                        wq,
6202                        wk,
6203                        wv,
6204                        wg,
6205                        input_ref,
6206                        &mut q_raw[rank],
6207                        &mut k_raw[rank],
6208                        &mut v_raw[rank],
6209                        &mut gate[rank],
6210                        q_m.in_features,
6211                        *local_q_dim,
6212                        *local_kv_dim,
6213                        out_g,
6214                    )?;
6215                }
6216                (
6217                    ResidentBf16Weight::Bf16(wq),
6218                    ResidentBf16Weight::Bf16(wk),
6219                    ResidentBf16Weight::Bf16(wv),
6220                ) => {
6221                    let (wg, out_g) = match &gate_shards {
6222                        Some(StepTpGateShards::Bf16(shards)) => (&shards[rank], *heads / ranks),
6223                        Some(StepTpGateShards::F32(_)) => {
6224                            return Err("step TP decode v2 gate shard class does not \
6225                                            match the bf16 projections"
6226                                .into());
6227                        }
6228                        None => (wq, 0),
6229                    };
6230                    engine.matvec_bf16_qkvg_into(
6231                        wq,
6232                        wk,
6233                        wv,
6234                        wg,
6235                        input_ref,
6236                        &mut q_raw[rank],
6237                        &mut k_raw[rank],
6238                        &mut v_raw[rank],
6239                        &mut gate[rank],
6240                        q_m.in_features,
6241                        *local_q_dim,
6242                        *local_kv_dim,
6243                        out_g,
6244                    )?;
6245                }
6246                _ => {
6247                    return Err("step TP decode v2 QKV projections mix residency classes".into());
6248                }
6249            }
6250        } else {
6251            for (matrix, local_out, raw) in [
6252                (q_m, ws.local_q_dim, &mut ws.q_raw),
6253                (k_m, ws.local_kv_dim, &mut ws.k_raw),
6254                (v_m, ws.local_kv_dim, &mut ws.v_raw),
6255            ] {
6256                let ResidentBf16Weight::F32(values_w) = &matrix.ranks[rank].weight else {
6257                    return Err("step TP decode v2 lost its F32 projection residency".into());
6258                };
6259                let chunk_rows = matrix.canonical_chunk_rows.unwrap_or(local_out);
6260                engine.linear_f32_resident_canonical_rows_t1_into(
6261                    &decode_input.ranks[rank],
6262                    values_w,
6263                    &mut raw[rank],
6264                    matrix.in_features,
6265                    local_out,
6266                    chunk_rows,
6267                )?;
6268            }
6269        }
6270        if qkv_fused && defer_norm_rope {
6271            // FUSION #1 defers norm+rope to the caller's fused rope+append+inc launch.
6272        } else if qkv_fused {
6273            // Fused norm+rope: one launch; the position comes from the rank-local staged
6274            // copy (raw-copied above from the fixed e-context pos stage — capture-safe).
6275            let StepTpDecodeV2Ws {
6276                q_raw,
6277                k_raw,
6278                q,
6279                k,
6280                pos,
6281                pos_stage,
6282                ..
6283            } = &mut *ws;
6284            let same_dev = engine.ctx().ordinal() == ws_e_device;
6285            let pos_ref: &CudaSlice<i32> = if same_dev {
6286                pos_stage
6287                    .as_ref()
6288                    .ok_or("step TP decode v2 pos stage not armed")?
6289            } else {
6290                &pos[rank]
6291            };
6292            engine.qk_norm_rope_into(
6293                &q_raw[rank],
6294                &k_raw[rank],
6295                &q_norm[rank],
6296                &k_norm[rank],
6297                &mut q[rank],
6298                &mut k[rank],
6299                pos_ref,
6300                head_dim,
6301                n_rot,
6302                local_heads,
6303                local_kv_heads,
6304                rms_eps,
6305                rope_base,
6306                1.0,
6307                rope_freqs[rank],
6308            )?;
6309        } else {
6310            engine.rms_norm(
6311                &ws.q_raw[rank],
6312                &q_norm[rank],
6313                &mut ws.q[rank],
6314                head_dim,
6315                local_heads,
6316                rms_eps,
6317            )?;
6318            engine.rms_norm(
6319                &ws.k_raw[rank],
6320                &k_norm[rank],
6321                &mut ws.k[rank],
6322                head_dim,
6323                local_kv_heads,
6324                rms_eps,
6325            )?;
6326            {
6327                let mut pos_dst = ws.pos[rank].slice_mut(0..1);
6328                engine
6329                    .stream()
6330                    .memcpy_dtod(&pos_d.slice(0..1), &mut pos_dst)?;
6331            }
6332            engine.rope_neox2(
6333                &mut ws.q[rank],
6334                &mut ws.k[rank],
6335                &ws.pos[rank],
6336                head_dim,
6337                n_rot,
6338                local_heads,
6339                local_kv_heads,
6340                1,
6341                rope_base,
6342                1.0,
6343                rope_freqs[rank],
6344            )?;
6345        }
6346        if gate_shards.is_none() {
6347            let gate_start = rank * (ws.heads / ranks);
6348            let mut gate_dst = ws.gate[rank].slice_mut(0..ws.heads / ranks);
6349            engine.stream().memcpy_dtod(
6350                &ws.gate_e.slice(gate_start..gate_start + ws.heads / ranks),
6351                &mut gate_dst,
6352            )?;
6353        }
6354        Ok(())
6355    }
6356
6357    /// One rank's O-partial slice of `decode_v2_finish` — the per-device issue unit the
6358    /// whole-token graph captures on that rank's stream (the rank-done event stays with the
6359    /// eager caller; graphs order via parent edges instead).
6360    pub(crate) fn decode_v2_finish_rank_partial(
6361        &self,
6362        ws: &mut StepTpDecodeV2Ws,
6363        o_m: &ResidentStepBf16RowParallel,
6364        o_fused: bool,
6365        rank: usize,
6366    ) -> Result<(), Box<dyn std::error::Error>> {
6367        let engine = &self.ranks[rank];
6368        let _main = engine.gpu.enter_main()?;
6369        if o_fused {
6370            let StepTpDecodeV2Ws {
6371                gated,
6372                o_partials,
6373                o_block_cols,
6374                o_out,
6375                ..
6376            } = &mut *ws;
6377            let all_f32 = o_m.ranks[rank]
6378                .iter()
6379                .all(|block| matches!(block.weight, ResidentBf16Weight::F32(_)));
6380            if all_f32 {
6381                let mut weights = Vec::with_capacity(4);
6382                for block in 0..4 {
6383                    let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
6384                        unreachable!("all_f32 checked above");
6385                    };
6386                    weights.push(weight);
6387                }
6388                engine.matvec_f32_b4_into(
6389                    [weights[0], weights[1], weights[2], weights[3]],
6390                    &gated[rank],
6391                    &mut o_partials[rank][0],
6392                    *o_block_cols,
6393                    *o_out,
6394                )?;
6395            } else {
6396                let mut weights = Vec::with_capacity(4);
6397                for block in 0..4 {
6398                    let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
6399                        return Err("step TP decode v2 O projections mix residency classes".into());
6400                    };
6401                    weights.push(weight);
6402                }
6403                engine.matvec_bf16_b4_into(
6404                    [weights[0], weights[1], weights[2], weights[3]],
6405                    &gated[rank],
6406                    &mut o_partials[rank][0],
6407                    *o_block_cols,
6408                    *o_out,
6409                )?;
6410            }
6411        } else {
6412            for block in 0..ws.blocks_per_rank {
6413                let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
6414                    return Err("step TP decode v2 lost its F32 O residency".into());
6415                };
6416                let x =
6417                    ws.gated[rank].slice(block * ws.o_block_cols..(block + 1) * ws.o_block_cols);
6418                let w = weight.slice(0..weight.len());
6419                let mut y = ws.o_partials[rank][block].slice_mut(0..ws.o_out);
6420                engine.linear_t1_into(&x, &w, &mut y, ws.o_block_cols, ws.o_out)?;
6421            }
6422        }
6423        Ok(())
6424    }
6425
6426    /// v2 phase 2: canonical-block O reduction on the root device plus the K/V shadow gathers,
6427    /// returning a fresh model-engine output ordered behind `ev_oproj` on `e`'s stream.
6428    ///
6429    /// The caller must have queued every rank's attention work (reading `ws.gated`, `ws.k`,
6430    /// `ws.v_raw`) on the rank streams before this call. Reduction order is identical to
6431    /// `step_bf16_row_parallel_resident_native`: zeros, then rank 0's blocks, then each peer
6432    /// rank's blocks, one `add` per block.
6433    pub(crate) fn decode_v2_finish(
6434        &self,
6435        ws: &mut StepTpDecodeV2Ws,
6436        e: &Engine,
6437        o_m: &ResidentStepBf16RowParallel,
6438    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6439        let ranks = self.ranks.len();
6440        if e.ctx().ordinal() != ws.e_device {
6441            return Err("step TP decode v2 finish engine changed".into());
6442        }
6443        // MEMRA_STEP_TP_QKV_FUSED extends to the O path: one matvec_f32_b4 launch per rank
6444        // (in-order canonical block accumulation per element) and a single peer-copy + add on
6445        // the root, replacing 4 cuBLASLt launches per rank + the 4-copy/8-add chain. Same
6446        // numeric-class door and gate as the fused QKV projection.
6447        let o_fused = step_tp_qkv_fused_enabled()? && ws.blocks_per_rank == 4 && ranks == 2;
6448
6449        // Per-rank O block partials on the owning rank's stream (serial after the attention
6450        // kernels the driver queued there), then the rank-done event for root's peer reads.
6451        for rank in 0..ranks {
6452            self.decode_v2_finish_rank_partial(ws, o_m, o_fused, rank)?;
6453            if rank == 0 {
6454                // root == rank0: its own stream order covers the partial; only peers need
6455                // the record/wait pair (host-op diet, matches the routes-arm skip).
6456                continue;
6457            }
6458            let engine = &self.ranks[rank];
6459            let _main = engine.gpu.enter_main()?;
6460            ws.ev_rank[rank].record(&engine.stream())?;
6461        }
6462
6463        // Root reduce in canonical order + shadow gathers, all on the root stream.
6464        let root = &self.ranks[0];
6465        #[allow(unused_assignments)]
6466        let mut final_in_a = false;
6467        {
6468            let _main = root.gpu.enter_main()?;
6469            for ev in ws.ev_rank.iter().skip(1) {
6470                root.stream().wait(ev)?;
6471            }
6472            if o_fused && oproj_direct_on() && ranks == 2 && no_local_shadow_on() {
6473                // DIRECT JOIN: rank1's partial already sits in root memory (P2P kernel
6474                // stores; visibility guaranteed by the ev_rank[1] wait above), rank0's
6475                // partial is root-stream-ordered — record ONE event and let the model
6476                // engine do the single add itself, straight into its own output row.
6477                // Same operands, same add order as finish_root_fused: BIT-IDENTICAL.
6478                ws.ev_oproj.record(&root.stream())?;
6479                let _main = e.gpu.enter_main()?;
6480                e.stream().wait(&ws.ev_oproj)?;
6481                let mut output = e.uninit(ws.o_out)?;
6482                if oproj_tail_on() && oproj_tail_eligible() {
6483                    // M2: defer the add into the residual+norm consumer (waits stay HERE;
6484                    // only the arithmetic moves). `output` is returned unwritten.
6485                    use cudarc::driver::DevicePtr;
6486                    let stream = e.stream();
6487                    let (p0, _g0) = ws.o_partials[0][0].device_ptr(&stream);
6488                    let (p1, _g1) = ws.o_partials[1][0].device_ptr(&stream);
6489                    set_oproj_tail((p0 as u64, p1 as u64));
6490                    return Ok(output);
6491                }
6492                e.add(
6493                    &ws.o_partials[0][0],
6494                    &ws.o_partials[1][0],
6495                    &mut output,
6496                    ws.o_out,
6497                )?;
6498                return Ok(output);
6499            }
6500            if o_fused {
6501                self.decode_v2_finish_root_fused(ws)?;
6502                ws.ev_oproj.record(&root.stream())?;
6503                let _main = e.gpu.enter_main()?;
6504                e.stream().wait(&ws.ev_oproj)?;
6505                let mut output = e.uninit(ws.o_out)?;
6506                e.stream().memcpy_dtod(
6507                    &ws.reduce_a.slice(0..ws.o_out),
6508                    &mut output.slice_mut(0..ws.o_out),
6509                )?;
6510                return Ok(output);
6511            }
6512            let mut first = true;
6513            let mut current_is_a = false;
6514            for rank in 0..ranks {
6515                for block in 0..ws.blocks_per_rank {
6516                    let use_peer = rank != 0;
6517                    if use_peer {
6518                        root.stream()
6519                            .memcpy_dtod(&ws.o_partials[rank][block], &mut ws.peer_partial)?;
6520                    }
6521                    // add(prev, partial) -> the other reduce buffer, exactly one add per block
6522                    match (first, current_is_a, use_peer) {
6523                        (true, _, true) => {
6524                            root.add(&ws.zeros, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
6525                        }
6526                        (true, _, false) => root.add(
6527                            &ws.zeros,
6528                            &ws.o_partials[0][block],
6529                            &mut ws.reduce_a,
6530                            ws.o_out,
6531                        )?,
6532                        (false, true, true) => {
6533                            root.add(&ws.reduce_a, &ws.peer_partial, &mut ws.reduce_b, ws.o_out)?
6534                        }
6535                        (false, true, false) => root.add(
6536                            &ws.reduce_a,
6537                            &ws.o_partials[0][block],
6538                            &mut ws.reduce_b,
6539                            ws.o_out,
6540                        )?,
6541                        (false, false, true) => {
6542                            root.add(&ws.reduce_b, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
6543                        }
6544                        (false, false, false) => root.add(
6545                            &ws.reduce_b,
6546                            &ws.o_partials[0][block],
6547                            &mut ws.reduce_a,
6548                            ws.o_out,
6549                        )?,
6550                    }
6551                    current_is_a = first || !current_is_a;
6552                    first = false;
6553                }
6554            }
6555            final_in_a = current_is_a;
6556
6557            for rank in 0..ranks {
6558                let start = rank * ws.local_kv_dim;
6559                let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
6560                root.stream().memcpy_dtod(&ws.k[rank], &mut k_dst)?;
6561                let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
6562                root.stream().memcpy_dtod(&ws.v_raw[rank], &mut v_dst)?;
6563            }
6564            ws.ev_oproj.record(&root.stream())?;
6565        }
6566
6567        // Model-engine output: e waits the root event, then copies the reduced row into a
6568        // fresh e-context buffer (same ownership contract as v1's `e.htod`). The same wait
6569        // orders the driver's shadow append (it reads ws.k_shadow/ws.v_shadow on e's stream).
6570        let _main = e.gpu.enter_main()?;
6571        e.stream().wait(&ws.ev_oproj)?;
6572        let mut output = e.uninit(ws.o_out)?;
6573        let source = if final_in_a {
6574            &ws.reduce_a
6575        } else {
6576            &ws.reduce_b
6577        };
6578        e.stream().memcpy_dtod(
6579            &source.slice(0..ws.o_out),
6580            &mut output.slice_mut(0..ws.o_out),
6581        )?;
6582        Ok(output)
6583    }
6584
6585    pub fn run_routed_experts(
6586        &self,
6587        experts: &ResidentExpertParallel,
6588        input: &[f32],
6589        tokens: usize,
6590        selected: &[usize],
6591        route_weights: &[f32],
6592        experts_per_token: usize,
6593        activation_limit: Option<f32>,
6594    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6595        validate_step_expert_activation_limit(activation_limit)?;
6596        validate_ep_residency(&self.ranks, experts)?;
6597        validate_activations(input, tokens, experts.input_width)?;
6598        let pairs = tokens
6599            .checked_mul(experts_per_token)
6600            .ok_or("EP route count overflow")?;
6601        if selected.len() != pairs || route_weights.len() != pairs {
6602            return Err(format!(
6603                "EP routes selected={} weights={} != tokens {tokens} x experts/token \
6604                 {experts_per_token} ({pairs})",
6605                selected.len(),
6606                route_weights.len(),
6607            )
6608            .into());
6609        }
6610        if !route_weights.iter().all(|weight| weight.is_finite()) {
6611            return Err("EP route weights contain a non-finite value".into());
6612        }
6613        if self.native_p2p {
6614            return self.run_routed_experts_native(
6615                experts,
6616                input,
6617                tokens,
6618                selected,
6619                route_weights,
6620                experts_per_token,
6621                activation_limit,
6622            );
6623        }
6624
6625        let mut output = vec![0.0f32; tokens * experts.input_width];
6626        let per_rank = experts.expert_count / experts.ranks.len();
6627        for token in 0..tokens {
6628            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
6629            for slot in 0..experts_per_token {
6630                let pair = token * experts_per_token + slot;
6631                let expert = selected[pair];
6632                if expert >= experts.expert_count {
6633                    return Err(format!(
6634                        "EP selected expert {expert} outside 0..{}",
6635                        experts.expert_count
6636                    )
6637                    .into());
6638                }
6639                let owner = expert / per_rank;
6640                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
6641                let rank = &experts.ranks[owner];
6642                let engine = &self.ranks[owner];
6643                let gate =
6644                    run_resident_bank_expert(engine, &rank.gate, local_expert, input_row, 1)?;
6645                let up = run_resident_bank_expert(engine, &rank.up, local_expert, input_row, 1)?;
6646                let activated: Vec<f32> = gate
6647                    .iter()
6648                    .zip(&up)
6649                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
6650                    .collect();
6651                debug_assert_eq!(activated.len(), experts.expert_width);
6652                let down =
6653                    run_resident_bank_expert(engine, &rank.down, local_expert, &activated, 1)?;
6654                let weight = route_weights[pair];
6655                for (sum, value) in output
6656                    [token * experts.input_width..(token + 1) * experts.input_width]
6657                    .iter_mut()
6658                    .zip(down)
6659                {
6660                    *sum += weight * value;
6661                }
6662            }
6663        }
6664        Ok(output)
6665    }
6666
6667    fn run_routed_experts_native(
6668        &self,
6669        experts: &ResidentExpertParallel,
6670        input: &[f32],
6671        tokens: usize,
6672        selected: &[usize],
6673        route_weights: &[f32],
6674        experts_per_token: usize,
6675        activation_limit: Option<f32>,
6676    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6677        if !self.native_p2p || self.ranks.len() < 2 {
6678            return Err("native EP execution requires at least two P2P ranks".into());
6679        }
6680        if self.ep_device_arithmetic {
6681            return self.run_routed_experts_native_device(
6682                experts,
6683                input,
6684                tokens,
6685                selected,
6686                route_weights,
6687                experts_per_token,
6688                activation_limit,
6689            );
6690        }
6691        let mut output = vec![0.0f32; tokens * experts.input_width];
6692        let per_rank = experts.expert_count / experts.ranks.len();
6693        for token in 0..tokens {
6694            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
6695            let mut rank_inputs = (0..self.ranks.len())
6696                .map(|_| None)
6697                .collect::<Vec<Option<CudaSlice<f32>>>>();
6698            rank_inputs[0] = Some({
6699                let root = &self.ranks[0];
6700                let _main = root.gpu.enter_main()?;
6701                root.htod(input_row)?
6702            });
6703
6704            for slot in 0..experts_per_token {
6705                let pair = token * experts_per_token + slot;
6706                let expert = selected[pair];
6707                if expert >= experts.expert_count {
6708                    return Err(format!(
6709                        "EP selected expert {expert} outside 0..{}",
6710                        experts.expert_count
6711                    )
6712                    .into());
6713                }
6714                let owner = expert / per_rank;
6715                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
6716                if rank_inputs[owner].is_none() {
6717                    let peer_input = {
6718                        let root_input = rank_inputs[0]
6719                            .as_ref()
6720                            .ok_or("native EP lost its root input")?;
6721                        let engine = &self.ranks[owner];
6722                        let _main = engine.gpu.enter_main()?;
6723                        let mut peer_input = engine.uninit(experts.input_width)?;
6724                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
6725                        peer_input
6726                    };
6727                    rank_inputs[owner] = Some(peer_input);
6728                }
6729
6730                let rank = &experts.ranks[owner];
6731                let engine = &self.ranks[owner];
6732                let owner_input = rank_inputs[owner]
6733                    .as_ref()
6734                    .ok_or("native EP owner input is absent after dispatch")?;
6735                let gate = run_resident_bank_expert_device(
6736                    engine,
6737                    &rank.gate,
6738                    local_expert,
6739                    owner_input,
6740                    1,
6741                )?;
6742                let up = run_resident_bank_expert_device(
6743                    engine,
6744                    &rank.up,
6745                    local_expert,
6746                    owner_input,
6747                    1,
6748                )?;
6749                let (gate, up) = {
6750                    let _main = engine.gpu.enter_main()?;
6751                    (engine.dtoh(&gate)?, engine.dtoh(&up)?)
6752                };
6753                let activated = gate
6754                    .iter()
6755                    .zip(&up)
6756                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
6757                    .collect::<Vec<_>>();
6758                debug_assert_eq!(activated.len(), experts.expert_width);
6759                let activated = {
6760                    let _main = engine.gpu.enter_main()?;
6761                    engine.htod(&activated)?
6762                };
6763                let down = run_resident_bank_expert_device(
6764                    engine,
6765                    &rank.down,
6766                    local_expert,
6767                    &activated,
6768                    1,
6769                )?;
6770                let down = if owner == 0 {
6771                    let _main = engine.gpu.enter_main()?;
6772                    engine.dtoh(&down)?
6773                } else {
6774                    let root = &self.ranks[0];
6775                    let _main = root.gpu.enter_main()?;
6776                    let mut root_down = root.uninit(experts.input_width)?;
6777                    root.stream().memcpy_dtod(&down, &mut root_down)?;
6778                    root.dtoh(&root_down)?
6779                };
6780                let weight = route_weights[pair];
6781                for (sum, value) in output
6782                    [token * experts.input_width..(token + 1) * experts.input_width]
6783                    .iter_mut()
6784                    .zip(down)
6785                {
6786                    *sum += weight * value;
6787                }
6788            }
6789        }
6790        Ok(output)
6791    }
6792
6793    fn run_routed_experts_native_device(
6794        &self,
6795        experts: &ResidentExpertParallel,
6796        input: &[f32],
6797        tokens: usize,
6798        selected: &[usize],
6799        route_weights: &[f32],
6800        experts_per_token: usize,
6801        activation_limit: Option<f32>,
6802    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6803        if !self.native_p2p || !self.ep_device_arithmetic || self.ranks.len() < 2 {
6804            return Err(
6805                "device-resident EP arithmetic requires at least two native P2P ranks".into(),
6806            );
6807        }
6808        let mut output = Vec::with_capacity(tokens * experts.input_width);
6809        let per_rank = experts.expert_count / experts.ranks.len();
6810        let root = &self.ranks[0];
6811        for token in 0..tokens {
6812            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
6813            let mut rank_inputs = (0..self.ranks.len())
6814                .map(|_| None)
6815                .collect::<Vec<Option<CudaSlice<f32>>>>();
6816            rank_inputs[0] = Some({
6817                let _main = root.gpu.enter_main()?;
6818                root.htod(input_row)?
6819            });
6820            let mut root_output = {
6821                let _main = root.gpu.enter_main()?;
6822                root.zeros(experts.input_width)?
6823            };
6824            let mut remote_down_keepalive = Vec::new();
6825
6826            for slot in 0..experts_per_token {
6827                let pair = token * experts_per_token + slot;
6828                let expert = selected[pair];
6829                if expert >= experts.expert_count {
6830                    return Err(format!(
6831                        "EP selected expert {expert} outside 0..{}",
6832                        experts.expert_count
6833                    )
6834                    .into());
6835                }
6836                let owner = expert / per_rank;
6837                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
6838                if rank_inputs[owner].is_none() {
6839                    let peer_input = {
6840                        let root_input = rank_inputs[0]
6841                            .as_ref()
6842                            .ok_or("native EP lost its root input")?;
6843                        let engine = &self.ranks[owner];
6844                        let _main = engine.gpu.enter_main()?;
6845                        let mut peer_input = engine.uninit(experts.input_width)?;
6846                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
6847                        peer_input
6848                    };
6849                    rank_inputs[owner] = Some(peer_input);
6850                }
6851
6852                let rank = &experts.ranks[owner];
6853                let engine = &self.ranks[owner];
6854                let owner_input = rank_inputs[owner]
6855                    .as_ref()
6856                    .ok_or("native EP owner input is absent after dispatch")?;
6857                let gate = run_resident_bank_expert_device(
6858                    engine,
6859                    &rank.gate,
6860                    local_expert,
6861                    owner_input,
6862                    1,
6863                )?;
6864                let up = run_resident_bank_expert_device(
6865                    engine,
6866                    &rank.up,
6867                    local_expert,
6868                    owner_input,
6869                    1,
6870                )?;
6871                let activated = {
6872                    let _main = engine.gpu.enter_main()?;
6873                    let mut activated = engine.uninit(experts.expert_width)?;
6874                    if let Some(limit) = activation_limit {
6875                        engine.silu_clamped_mul_host_expf(
6876                            &gate,
6877                            &up,
6878                            limit,
6879                            &mut activated,
6880                            experts.expert_width,
6881                        )?;
6882                    } else {
6883                        engine.silu_mul_host_expf(
6884                            &gate,
6885                            &up,
6886                            &mut activated,
6887                            experts.expert_width,
6888                        )?;
6889                    }
6890                    activated
6891                };
6892                let down = run_resident_bank_expert_device(
6893                    engine,
6894                    &rank.down,
6895                    local_expert,
6896                    &activated,
6897                    1,
6898                )?;
6899                let root_down = if owner == 0 {
6900                    down
6901                } else {
6902                    let _main = root.gpu.enter_main()?;
6903                    let mut root_down = root.uninit(experts.input_width)?;
6904                    root.stream().memcpy_dtod(&down, &mut root_down)?;
6905                    // The peer copy runs on the root stream. Keep its remote source alive until
6906                    // the final root readback synchronizes that stream; otherwise async free can
6907                    // recycle the owner's allocation while cuMemcpyPeerAsync is still reading it.
6908                    remote_down_keepalive.push(down);
6909                    root_down
6910                };
6911                let _main = root.gpu.enter_main()?;
6912                let mut destination = root_output.slice_mut(0..experts.input_width);
6913                root.axpy_host_into(
6914                    &root_down.slice(0..root_down.len()),
6915                    route_weights[pair],
6916                    &mut destination,
6917                    experts.input_width,
6918                )?;
6919            }
6920
6921            let _main = root.gpu.enter_main()?;
6922            let root_output = root.dtoh(&root_output)?;
6923            drop(remote_down_keepalive);
6924            output.extend(root_output);
6925        }
6926        Ok(output)
6927    }
6928}
6929
6930fn validate_column_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
6931    if matrix.out_features % tp != 0 {
6932        return Err(format!(
6933            "column-parallel out_features {} is not divisible by TP={tp}",
6934            matrix.out_features
6935        ));
6936    }
6937    let local_out = matrix.out_features / tp;
6938    if local_out % FP8_BLOCK != 0 {
6939        return Err(format!(
6940            "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
6941             E4M3 scale block"
6942        ));
6943    }
6944    Ok(())
6945}
6946
6947fn step_bf16_canonical_chunk_rows(out_features: usize, tp: usize) -> Result<usize, String> {
6948    if !matches!(tp, 1 | 2 | 4 | 8) {
6949        return Err(format!(
6950            "Step BF16 canonical projection requires TP1/TP2/TP4/TP8, got TP={tp}"
6951        ));
6952    }
6953    if out_features == 0 || out_features % PRODUCT_MAX_CARDS != 0 {
6954        return Err(format!(
6955            "Step BF16 output width {out_features} is not divisible by the TP8 product envelope"
6956        ));
6957    }
6958    let canonical_rows = out_features / PRODUCT_MAX_CARDS;
6959    let local_out = out_features / tp;
6960    if local_out % canonical_rows != 0 {
6961        return Err(format!(
6962            "Step BF16 TP={tp} output shard {local_out} is not divisible by canonical \
6963             {canonical_rows}-row chunks"
6964        ));
6965    }
6966    Ok(canonical_rows)
6967}
6968
6969fn step_bf16_canonical_chunk_cols(in_features: usize, tp: usize) -> Result<usize, String> {
6970    if !matches!(tp, 1 | 2 | 4 | 8) {
6971        return Err(format!(
6972            "Step BF16 canonical row projection requires TP1/TP2/TP4/TP8, got TP={tp}"
6973        ));
6974    }
6975    if in_features == 0 || in_features % PRODUCT_MAX_CARDS != 0 {
6976        return Err(format!(
6977            "Step BF16 input width {in_features} is not divisible by the TP8 product envelope"
6978        ));
6979    }
6980    let canonical_cols = in_features / PRODUCT_MAX_CARDS;
6981    let local_in = in_features / tp;
6982    if local_in % canonical_cols != 0 {
6983        return Err(format!(
6984            "Step BF16 TP={tp} input shard {local_in} is not divisible by canonical \
6985             {canonical_cols}-column chunks"
6986        ));
6987    }
6988    Ok(canonical_cols)
6989}
6990
6991fn validate_row_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
6992    if matrix.in_features % tp != 0 {
6993        return Err(format!(
6994            "row-parallel in_features {} is not divisible by TP={tp}",
6995            matrix.in_features
6996        ));
6997    }
6998    let local_in = matrix.in_features / tp;
6999    if local_in % FP8_BLOCK != 0 {
7000        return Err(format!(
7001            "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
7002             E4M3 scale block"
7003        ));
7004    }
7005    Ok(())
7006}
7007
7008fn upload_rank(
7009    engine: &Engine,
7010    matrix: E4m3BlockMatrix<'_>,
7011) -> Result<ResidentE4m3Rank, Box<dyn std::error::Error>> {
7012    let _main = engine.gpu.enter_main()?;
7013    matrix.validate()?;
7014    Ok(ResidentE4m3Rank {
7015        codes: engine.htod_bytes(matrix.codes)?,
7016        scales: engine.htod(matrix.scales)?,
7017        out_features: matrix.out_features,
7018        in_features: matrix.in_features,
7019    })
7020}
7021
7022fn upload_bf16_rank(
7023    engine: &Engine,
7024    matrix: Bf16Matrix<'_>,
7025    f32_mirror: bool,
7026) -> Result<ResidentBf16Rank, Box<dyn std::error::Error>> {
7027    let _main = engine.gpu.enter_main()?;
7028    matrix.validate()?;
7029    let bytes = engine.htod_bytes(matrix.bytes)?;
7030    let weight = if f32_mirror {
7031        let values = matrix
7032            .out_features
7033            .checked_mul(matrix.in_features)
7034            .ok_or("resident BF16 mirror element count overflow")?;
7035        ResidentBf16Weight::F32(engine.bf16_to_f32(&bytes.slice(0..bytes.len()), values)?)
7036    } else {
7037        ResidentBf16Weight::Bf16(bytes)
7038    };
7039    Ok(ResidentBf16Rank {
7040        weight,
7041        out_features: matrix.out_features,
7042        in_features: matrix.in_features,
7043    })
7044}
7045
7046fn upload_expert_bank_rank(
7047    engine: &Engine,
7048    bank: E4m3ExpertBank<'_>,
7049    expert_range: Range<usize>,
7050) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
7051    let _main = engine.gpu.enter_main()?;
7052    bank.validate()?;
7053    if expert_range.start >= expert_range.end || expert_range.end > bank.expert_count {
7054        return Err(format!(
7055            "invalid EP expert range {expert_range:?} for {} experts",
7056            bank.expert_count
7057        )
7058        .into());
7059    }
7060    let code_stride = bank.out_features * bank.in_features;
7061    let scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * bank.in_features.div_ceil(FP8_BLOCK);
7062    Ok(ResidentE4m3ExpertBankRank {
7063        codes: engine.htod_bytes(
7064            &bank.codes[expert_range.start * code_stride..expert_range.end * code_stride],
7065        )?,
7066        scales: engine.htod(
7067            &bank.scales[expert_range.start * scale_stride..expert_range.end * scale_stride],
7068        )?,
7069        expert_range,
7070        out_features: bank.out_features,
7071        in_features: bank.in_features,
7072        code_stride,
7073        scale_stride,
7074        k_blocks: None,
7075    })
7076}
7077
7078fn validate_column_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
7079    if bank.out_features % tp != 0 {
7080        return Err(format!(
7081            "TP expert output width {} is not divisible by TP={tp}",
7082            bank.out_features
7083        ));
7084    }
7085    let local_out = bank.out_features / tp;
7086    if local_out % FP8_BLOCK != 0 {
7087        return Err(format!(
7088            "TP expert output shard {local_out} cuts through a {FP8_BLOCK}-row E4M3 scale block"
7089        ));
7090    }
7091    Ok(())
7092}
7093
7094fn validate_row_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
7095    if bank.in_features % tp != 0 {
7096        return Err(format!(
7097            "TP expert input width {} is not divisible by TP={tp}",
7098            bank.in_features
7099        ));
7100    }
7101    let local_in = bank.in_features / tp;
7102    if local_in % FP8_BLOCK != 0 {
7103        return Err(format!(
7104            "TP expert input shard {local_in} cuts through a {FP8_BLOCK}-column E4M3 scale block"
7105        ));
7106    }
7107    Ok(())
7108}
7109
7110fn upload_column_bank_rank(
7111    engine: &Engine,
7112    bank: E4m3ExpertBank<'_>,
7113    tp: usize,
7114    rank: usize,
7115) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
7116    let _main = engine.gpu.enter_main()?;
7117    let packed = pack_column_bank_rank(bank, tp, rank)?;
7118    Ok(ResidentE4m3ExpertBankRank {
7119        codes: engine.htod_bytes(&packed.codes)?,
7120        scales: engine.htod(&packed.scales)?,
7121        expert_range: packed.expert_range,
7122        out_features: packed.out_features,
7123        in_features: packed.in_features,
7124        code_stride: packed.code_stride,
7125        scale_stride: packed.scale_stride,
7126        k_blocks: packed.k_blocks,
7127    })
7128}
7129
7130fn pack_column_bank_rank(
7131    bank: E4m3ExpertBank<'_>,
7132    tp: usize,
7133    rank: usize,
7134) -> Result<PackedE4m3ExpertBankRank, String> {
7135    bank.validate()?;
7136    validate_column_bank_shape(bank, tp)?;
7137    if rank >= tp {
7138        return Err(format!("TP rank {rank} outside 0..{tp}"));
7139    }
7140    let local_out = bank.out_features / tp;
7141    let full_code_stride = bank.out_features * bank.in_features;
7142    let local_code_stride = local_out * bank.in_features;
7143    let scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
7144    let full_scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * scale_cols;
7145    let local_scale_rows = local_out / FP8_BLOCK;
7146    let local_scale_stride = local_scale_rows * scale_cols;
7147    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
7148    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
7149    let row_start = rank * local_out;
7150    let scale_row_start = rank * local_scale_rows;
7151    for expert in 0..bank.expert_count {
7152        let code_start = expert * full_code_stride + row_start * bank.in_features;
7153        codes.extend_from_slice(&bank.codes[code_start..code_start + local_code_stride]);
7154        let scale_start = expert * full_scale_stride + scale_row_start * scale_cols;
7155        scales.extend_from_slice(&bank.scales[scale_start..scale_start + local_scale_stride]);
7156    }
7157    Ok(PackedE4m3ExpertBankRank {
7158        codes,
7159        scales,
7160        expert_range: 0..bank.expert_count,
7161        out_features: local_out,
7162        in_features: bank.in_features,
7163        code_stride: local_code_stride,
7164        scale_stride: local_scale_stride,
7165        k_blocks: None,
7166    })
7167}
7168
7169fn upload_row_bank_rank(
7170    engine: &Engine,
7171    bank: E4m3ExpertBank<'_>,
7172    tp: usize,
7173    rank: usize,
7174) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
7175    let _main = engine.gpu.enter_main()?;
7176    let packed = pack_row_bank_rank(bank, tp, rank)?;
7177    Ok(ResidentE4m3ExpertBankRank {
7178        codes: engine.htod_bytes(&packed.codes)?,
7179        scales: engine.htod(&packed.scales)?,
7180        expert_range: packed.expert_range,
7181        out_features: packed.out_features,
7182        in_features: packed.in_features,
7183        code_stride: packed.code_stride,
7184        scale_stride: packed.scale_stride,
7185        k_blocks: packed.k_blocks,
7186    })
7187}
7188
7189fn pack_row_bank_rank(
7190    bank: E4m3ExpertBank<'_>,
7191    tp: usize,
7192    rank: usize,
7193) -> Result<PackedE4m3ExpertBankRank, String> {
7194    bank.validate()?;
7195    validate_row_bank_shape(bank, tp)?;
7196    if rank >= tp {
7197        return Err(format!("TP rank {rank} outside 0..{tp}"));
7198    }
7199    let local_in = bank.in_features / tp;
7200    let full_code_stride = bank.out_features * bank.in_features;
7201    let local_code_stride = bank.out_features * local_in;
7202    let full_scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
7203    let local_scale_cols = local_in / FP8_BLOCK;
7204    let scale_rows = bank.out_features.div_ceil(FP8_BLOCK);
7205    let full_scale_stride = scale_rows * full_scale_cols;
7206    let local_scale_stride = scale_rows * local_scale_cols;
7207    let global_block_start = rank * local_scale_cols;
7208    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
7209    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
7210    for expert in 0..bank.expert_count {
7211        let expert_code_start = expert * full_code_stride;
7212        let expert_scale_start = expert * full_scale_stride;
7213        for local_block in 0..local_scale_cols {
7214            let global_block = global_block_start + local_block;
7215            let column_start = global_block * FP8_BLOCK;
7216            for row in 0..bank.out_features {
7217                let start = expert_code_start + row * bank.in_features + column_start;
7218                codes.extend_from_slice(&bank.codes[start..start + FP8_BLOCK]);
7219            }
7220            for row in 0..scale_rows {
7221                scales.push(bank.scales[expert_scale_start + row * full_scale_cols + global_block]);
7222            }
7223        }
7224    }
7225    Ok(PackedE4m3ExpertBankRank {
7226        codes,
7227        scales,
7228        expert_range: 0..bank.expert_count,
7229        out_features: bank.out_features,
7230        in_features: local_in,
7231        code_stride: local_code_stride,
7232        scale_stride: local_scale_stride,
7233        k_blocks: Some(local_scale_cols),
7234    })
7235}
7236
7237fn validate_resident_ranks(engines: &[Engine], ranks: &[ResidentE4m3Rank]) -> Result<(), String> {
7238    if engines.len() != ranks.len() {
7239        return Err(format!(
7240            "resident TP rank count {} != runtime rank count {}",
7241            ranks.len(),
7242            engines.len()
7243        ));
7244    }
7245    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
7246        let device = engine.ctx().ordinal();
7247        if matrix.codes.ordinal() != device || matrix.scales.ordinal() != device {
7248            return Err(format!(
7249                "resident TP rank {rank} is not owned by runtime device {device}"
7250            ));
7251        }
7252    }
7253    Ok(())
7254}
7255
7256fn validate_tp_bank_residency(
7257    engines: &[Engine],
7258    experts: &ResidentTpExpertBank,
7259) -> Result<(), String> {
7260    if engines.len() != experts.gate.len()
7261        || engines.len() != experts.up.len()
7262        || engines.len() != experts.down.len()
7263    {
7264        return Err(format!(
7265            "resident TP expert-bank rank counts gate={} up={} down={} != runtime {}",
7266            experts.gate.len(),
7267            experts.up.len(),
7268            experts.down.len(),
7269            engines.len()
7270        ));
7271    }
7272    for (rank, engine) in engines.iter().enumerate() {
7273        let device = engine.ctx().ordinal();
7274        for (projection, bank) in [
7275            ("gate", &experts.gate[rank]),
7276            ("up", &experts.up[rank]),
7277            ("down", &experts.down[rank]),
7278        ] {
7279            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
7280                return Err(format!(
7281                    "resident TP rank {rank} {projection} bank is not owned by runtime device \
7282                     {device}"
7283                ));
7284            }
7285        }
7286    }
7287    Ok(())
7288}
7289
7290fn validate_ep_residency(
7291    engines: &[Engine],
7292    experts: &ResidentExpertParallel,
7293) -> Result<(), String> {
7294    if engines.len() != experts.ranks.len() {
7295        return Err(format!(
7296            "resident EP rank count {} != runtime rank count {}",
7297            experts.ranks.len(),
7298            engines.len()
7299        ));
7300    }
7301    for (rank, (engine, resident)) in engines.iter().zip(&experts.ranks).enumerate() {
7302        let device = engine.ctx().ordinal();
7303        for (projection, bank) in [
7304            ("gate", &resident.gate),
7305            ("up", &resident.up),
7306            ("down", &resident.down),
7307        ] {
7308            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
7309                return Err(format!(
7310                    "resident EP rank {rank} {projection} bank is not owned by runtime device \
7311                     {device}"
7312                ));
7313            }
7314        }
7315    }
7316    Ok(())
7317}
7318
7319fn run_rank(
7320    engine: &Engine,
7321    matrix: E4m3BlockMatrix<'_>,
7322    activations: &[f32],
7323    tokens: usize,
7324) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7325    let _main = engine.gpu.enter_main()?;
7326    let codes = engine.htod_bytes(matrix.codes)?;
7327    let scales = engine.htod(matrix.scales)?;
7328    let activations = engine.htod(activations)?;
7329    let output = engine.qmatvec_mmq_fp8_blk(
7330        &codes,
7331        &scales,
7332        &activations,
7333        tokens,
7334        matrix.in_features,
7335        matrix.out_features,
7336    )?;
7337    engine.dtoh(&output)
7338}
7339
7340fn run_resident_rank(
7341    engine: &Engine,
7342    matrix: &ResidentE4m3Rank,
7343    activations: &[f32],
7344    tokens: usize,
7345) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7346    let _main = engine.gpu.enter_main()?;
7347    let activations = engine.htod(activations)?;
7348    let output = engine.qmatvec_mmq_fp8_blk(
7349        &matrix.codes,
7350        &matrix.scales,
7351        &activations,
7352        tokens,
7353        matrix.in_features,
7354        matrix.out_features,
7355    )?;
7356    engine.dtoh(&output)
7357}
7358
7359fn run_resident_bf16_rank(
7360    engine: &Engine,
7361    matrix: &ResidentBf16Rank,
7362    activations: &[f32],
7363    tokens: usize,
7364    canonical_chunk_rows: Option<usize>,
7365) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7366    let _main = engine.gpu.enter_main()?;
7367    let activations = engine.htod(activations)?;
7368    let output = run_resident_bf16_rank_device(
7369        engine,
7370        matrix,
7371        &activations,
7372        tokens,
7373        canonical_chunk_rows,
7374        false,
7375    )?;
7376    engine.dtoh(&output)
7377}
7378
7379fn run_resident_bf16_rank_device(
7380    engine: &Engine,
7381    matrix: &ResidentBf16Rank,
7382    activations: &CudaSlice<f32>,
7383    tokens: usize,
7384    canonical_chunk_rows: Option<usize>,
7385    strided_chunk_output: bool,
7386) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7387    let _main = engine.gpu.enter_main()?;
7388    if activations.ordinal() != engine.ctx().ordinal() {
7389        return Err(format!(
7390            "resident BF16 activation device {} != rank device {}",
7391            activations.ordinal(),
7392            engine.ctx().ordinal()
7393        )
7394        .into());
7395    }
7396    if activations.len() != tokens * matrix.in_features {
7397        return Err(format!(
7398            "resident BF16 activation count {} != {tokens}x{}",
7399            activations.len(),
7400            matrix.in_features
7401        )
7402        .into());
7403    }
7404    match (&matrix.weight, canonical_chunk_rows) {
7405        (ResidentBf16Weight::Bf16(bytes), Some(rows)) => engine
7406            .linear_bf16_resident_canonical_rows(
7407                activations,
7408                bytes,
7409                tokens,
7410                matrix.in_features,
7411                matrix.out_features,
7412                rows,
7413            ),
7414        (ResidentBf16Weight::Bf16(bytes), None) => engine.linear_bf16_resident(
7415            activations,
7416            bytes,
7417            tokens,
7418            matrix.in_features,
7419            matrix.out_features,
7420        ),
7421        (ResidentBf16Weight::F32(values), Some(rows)) if strided_chunk_output => engine
7422            .linear_f32_resident_canonical_rows_strided(
7423                activations,
7424                values,
7425                tokens,
7426                matrix.in_features,
7427                matrix.out_features,
7428                rows,
7429            ),
7430        (ResidentBf16Weight::F32(values), Some(rows)) => engine.linear_f32_resident_canonical_rows(
7431            activations,
7432            values,
7433            tokens,
7434            matrix.in_features,
7435            matrix.out_features,
7436            rows,
7437        ),
7438        (ResidentBf16Weight::F32(values), None) => engine.linear(
7439            activations,
7440            values,
7441            tokens,
7442            matrix.in_features,
7443            matrix.out_features,
7444        ),
7445    }
7446}
7447
7448fn validate_resident_bf16_ranks(
7449    engines: &[Engine],
7450    ranks: &[ResidentBf16Rank],
7451) -> Result<(), String> {
7452    if engines.len() != ranks.len() {
7453        return Err(format!(
7454            "resident BF16 TP rank count {} != runtime rank count {}",
7455            ranks.len(),
7456            engines.len(),
7457        ));
7458    }
7459    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
7460        let device = engine.ctx().ordinal();
7461        if matrix.weight.ordinal() != device {
7462            return Err(format!(
7463                "resident BF16 TP rank {rank} is not owned by runtime device {device}"
7464            ));
7465        }
7466    }
7467    Ok(())
7468}
7469
7470fn validate_step_bf16_row_residency(
7471    engines: &[Engine],
7472    matrix: &ResidentStepBf16RowParallel,
7473) -> Result<(), String> {
7474    if engines.len() != matrix.ranks.len() {
7475        return Err(format!(
7476            "resident Step BF16 row rank count {} != runtime rank count {}",
7477            matrix.ranks.len(),
7478            engines.len(),
7479        ));
7480    }
7481    let canonical_cols = step_bf16_canonical_chunk_cols(matrix.in_features, engines.len())?;
7482    if matrix.canonical_chunk_cols != canonical_cols {
7483        return Err(format!(
7484            "resident Step BF16 row canonical columns {} != registered {canonical_cols}",
7485            matrix.canonical_chunk_cols
7486        ));
7487    }
7488    let blocks_per_rank = PRODUCT_MAX_CARDS / engines.len();
7489    for (rank, (engine, blocks)) in engines.iter().zip(&matrix.ranks).enumerate() {
7490        if blocks.len() != blocks_per_rank {
7491            return Err(format!(
7492                "resident Step BF16 row rank {rank} has {} blocks, expected {blocks_per_rank}",
7493                blocks.len()
7494            ));
7495        }
7496        let device = engine.ctx().ordinal();
7497        for (block, resident) in blocks.iter().enumerate() {
7498            if resident.weight.ordinal() != device
7499                || resident.in_features != canonical_cols
7500                || resident.out_features != matrix.out_features
7501            {
7502                return Err(format!(
7503                    "resident Step BF16 row rank {rank} block {block} has inconsistent \
7504                     device or geometry"
7505                ));
7506            }
7507        }
7508    }
7509    Ok(())
7510}
7511
7512fn validate_replicated_device_rows(
7513    engines: &[Engine],
7514    rows: &ResidentReplicatedDeviceRows,
7515) -> Result<(), String> {
7516    let rank_lengths = rows
7517        .ranks
7518        .iter()
7519        .map(|rank_rows| rank_rows.len())
7520        .collect::<Vec<_>>();
7521    replicated_device_row_values(rows.tokens, rows.width, engines.len(), &rank_lengths)?;
7522    if rows
7523        .ranks
7524        .iter()
7525        .zip(engines)
7526        .any(|(rank_rows, engine)| rank_rows.ordinal() != engine.ctx().ordinal())
7527    {
7528        return Err("replicated device rows are owned by the wrong CUDA contexts".into());
7529    }
7530    Ok(())
7531}
7532
7533fn replicated_device_row_values(
7534    tokens: usize,
7535    width: usize,
7536    expected_ranks: usize,
7537    rank_lengths: &[usize],
7538) -> Result<usize, String> {
7539    let values = tokens
7540        .checked_mul(width)
7541        .ok_or("replicated device row size overflow")?;
7542    if tokens == 0
7543        || width == 0
7544        || expected_ranks == 0
7545        || rank_lengths.len() != expected_ranks
7546        || rank_lengths.iter().any(|&rank_len| rank_len != values)
7547    {
7548        return Err(format!(
7549            "replicated device rows have inconsistent geometry tokens={} width={} ranks={}/{}",
7550            tokens,
7551            width,
7552            rank_lengths.len(),
7553            expected_ranks
7554        ));
7555    }
7556    Ok(values)
7557}
7558
7559fn replicated_device_row_source_values(
7560    tokens: usize,
7561    width: usize,
7562    source_len: usize,
7563    source_device: usize,
7564    root_device: usize,
7565) -> Result<usize, String> {
7566    let values = tokens
7567        .checked_mul(width)
7568        .ok_or("replicated device row size overflow")?;
7569    if tokens == 0 || width == 0 || source_len != values || source_device != root_device {
7570        return Err(format!(
7571            "replicated device row source has inconsistent geometry/device \
7572             tokens={tokens} width={width} source={source_len}@{source_device} root={root_device}"
7573        ));
7574    }
7575    Ok(values)
7576}
7577
7578fn bf16_column_shard(
7579    matrix: Bf16Matrix<'_>,
7580    tp: usize,
7581    rank: usize,
7582) -> Result<Bf16Matrix<'_>, String> {
7583    matrix.validate()?;
7584    if tp == 0 || rank >= tp || matrix.out_features % tp != 0 {
7585        return Err(format!(
7586            "invalid BF16 column shard out={} TP={tp} rank={rank}",
7587            matrix.out_features
7588        ));
7589    }
7590    let local_out = matrix.out_features / tp;
7591    let row_bytes = matrix.in_features * 2;
7592    let start = rank * local_out * row_bytes;
7593    Ok(Bf16Matrix {
7594        bytes: &matrix.bytes[start..start + local_out * row_bytes],
7595        out_features: local_out,
7596        in_features: matrix.in_features,
7597    })
7598}
7599
7600fn bf16_row_shard(matrix: Bf16Matrix<'_>, tp: usize, rank: usize) -> Result<Vec<u8>, String> {
7601    matrix.validate()?;
7602    if tp == 0 || rank >= tp || matrix.in_features % tp != 0 {
7603        return Err(format!(
7604            "invalid BF16 row shard in={} TP={tp} rank={rank}",
7605            matrix.in_features
7606        ));
7607    }
7608    let local_in = matrix.in_features / tp;
7609    let mut bytes = Vec::with_capacity(matrix.out_features * local_in * 2);
7610    for row in 0..matrix.out_features {
7611        let start = (row * matrix.in_features + rank * local_in) * 2;
7612        bytes.extend_from_slice(&matrix.bytes[start..start + local_in * 2]);
7613    }
7614    Ok(bytes)
7615}
7616
7617fn bf16_row_block(
7618    matrix: Bf16Matrix<'_>,
7619    col_start: usize,
7620    block_cols: usize,
7621) -> Result<Vec<u8>, String> {
7622    matrix.validate()?;
7623    let col_end = col_start
7624        .checked_add(block_cols)
7625        .ok_or("BF16 row block column overflow")?;
7626    if block_cols == 0 || col_end > matrix.in_features {
7627        return Err(format!(
7628            "invalid BF16 row block columns {col_start}..{col_end} for input width {}",
7629            matrix.in_features
7630        ));
7631    }
7632    let mut bytes = Vec::with_capacity(matrix.out_features * block_cols * 2);
7633    for row in 0..matrix.out_features {
7634        let start = (row * matrix.in_features + col_start) * 2;
7635        bytes.extend_from_slice(&matrix.bytes[start..start + block_cols * 2]);
7636    }
7637    Ok(bytes)
7638}
7639
7640fn run_resident_bank_expert(
7641    engine: &Engine,
7642    bank: &ResidentE4m3ExpertBankRank,
7643    local_expert: usize,
7644    activations: &[f32],
7645    tokens: usize,
7646) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7647    let _main = engine.gpu.enter_main()?;
7648    if bank.k_blocks.is_some() {
7649        return Err("block-major TP row bank requires canonical block execution".into());
7650    }
7651    let local_count = bank.expert_range.end - bank.expert_range.start;
7652    if local_expert >= local_count {
7653        return Err(format!(
7654            "local EP expert {local_expert} outside 0..{local_count} for range {:?}",
7655            bank.expert_range
7656        )
7657        .into());
7658    }
7659    validate_activations(activations, tokens, bank.in_features)?;
7660    let activations = engine.htod(activations)?;
7661    let weight = bank
7662        .codes
7663        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
7664    let scales = bank
7665        .scales
7666        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
7667    let input = activations.slice(0..activations.len());
7668    let output = engine.qmatvec_mmq_fp8_blk_view(
7669        &weight,
7670        &scales,
7671        &input,
7672        tokens,
7673        bank.in_features,
7674        bank.out_features,
7675    )?;
7676    engine.dtoh(&output)
7677}
7678
7679fn run_resident_bank_expert_block(
7680    engine: &Engine,
7681    bank: &ResidentE4m3ExpertBankRank,
7682    local_expert: usize,
7683    block: usize,
7684    activations: &[f32],
7685) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7686    let _main = engine.gpu.enter_main()?;
7687    let local_count = bank.expert_range.end - bank.expert_range.start;
7688    if local_expert >= local_count {
7689        return Err(format!(
7690            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
7691            bank.expert_range
7692        )
7693        .into());
7694    }
7695    let blocks = bank
7696        .k_blocks
7697        .ok_or("TP row bank is not packed in native K-block order")?;
7698    if block >= blocks {
7699        return Err(format!("TP row block {block} outside 0..{blocks}").into());
7700    }
7701    validate_activations(activations, 1, FP8_BLOCK)?;
7702    let block_code_stride = bank.out_features * FP8_BLOCK;
7703    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
7704    if bank.in_features != blocks * FP8_BLOCK
7705        || bank.code_stride != blocks * block_code_stride
7706        || bank.scale_stride != blocks * block_scale_stride
7707    {
7708        return Err("TP row bank block-major geometry is inconsistent".into());
7709    }
7710
7711    let expert_code_start = local_expert * bank.code_stride;
7712    let expert_scale_start = local_expert * bank.scale_stride;
7713    let weight = bank.codes.slice(
7714        expert_code_start + block * block_code_stride
7715            ..expert_code_start + (block + 1) * block_code_stride,
7716    );
7717    let scales = bank.scales.slice(
7718        expert_scale_start + block * block_scale_stride
7719            ..expert_scale_start + (block + 1) * block_scale_stride,
7720    );
7721    let activations = engine.htod(activations)?;
7722    let input = activations.slice(0..activations.len());
7723    let output = engine.qmatvec_mmq_fp8_blk_view(
7724        &weight,
7725        &scales,
7726        &input,
7727        1,
7728        FP8_BLOCK,
7729        bank.out_features,
7730    )?;
7731    engine.dtoh(&output)
7732}
7733
7734fn run_resident_bank_expert_device(
7735    engine: &Engine,
7736    bank: &ResidentE4m3ExpertBankRank,
7737    local_expert: usize,
7738    activations: &CudaSlice<f32>,
7739    tokens: usize,
7740) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7741    let _main = engine.gpu.enter_main()?;
7742    if bank.k_blocks.is_some() {
7743        return Err("block-major TP row bank requires canonical block execution".into());
7744    }
7745    let local_count = bank.expert_range.end - bank.expert_range.start;
7746    if local_expert >= local_count {
7747        return Err(format!(
7748            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
7749            bank.expert_range
7750        )
7751        .into());
7752    }
7753    let expected = tokens
7754        .checked_mul(bank.in_features)
7755        .ok_or("native TP activation size overflow")?;
7756    if activations.len() != expected || activations.ordinal() != engine.ctx().ordinal() {
7757        return Err(format!(
7758            "native TP activation len/device {}/{} != expected {expected}/{}",
7759            activations.len(),
7760            activations.ordinal(),
7761            engine.ctx().ordinal()
7762        )
7763        .into());
7764    }
7765    let weight = bank
7766        .codes
7767        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
7768    let scales = bank
7769        .scales
7770        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
7771    let input = activations.slice(0..activations.len());
7772    engine.qmatvec_mmq_fp8_blk_view(
7773        &weight,
7774        &scales,
7775        &input,
7776        tokens,
7777        bank.in_features,
7778        bank.out_features,
7779    )
7780}
7781
7782fn run_resident_bank_expert_block_device(
7783    engine: &Engine,
7784    bank: &ResidentE4m3ExpertBankRank,
7785    local_expert: usize,
7786    block: usize,
7787    activations: &cudarc::driver::CudaView<'_, f32>,
7788) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7789    let _main = engine.gpu.enter_main()?;
7790    let local_count = bank.expert_range.end - bank.expert_range.start;
7791    if local_expert >= local_count {
7792        return Err(format!(
7793            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
7794            bank.expert_range
7795        )
7796        .into());
7797    }
7798    let blocks = bank
7799        .k_blocks
7800        .ok_or("native TP row bank is not packed in checkpoint-block order")?;
7801    if block >= blocks {
7802        return Err(format!("native TP row block {block} outside 0..{blocks}").into());
7803    }
7804    let activation_device = activations.stream().context().ordinal();
7805    if activations.len() != FP8_BLOCK || activation_device != engine.ctx().ordinal() {
7806        return Err(format!(
7807            "native TP block activation len/device {}/{} != expected {FP8_BLOCK}/{}",
7808            activations.len(),
7809            activation_device,
7810            engine.ctx().ordinal()
7811        )
7812        .into());
7813    }
7814    let block_code_stride = bank.out_features * FP8_BLOCK;
7815    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
7816    if bank.in_features != blocks * FP8_BLOCK
7817        || bank.code_stride != blocks * block_code_stride
7818        || bank.scale_stride != blocks * block_scale_stride
7819    {
7820        return Err("native TP row bank block-major geometry is inconsistent".into());
7821    }
7822    let expert_code_start = local_expert * bank.code_stride;
7823    let expert_scale_start = local_expert * bank.scale_stride;
7824    let weight = bank.codes.slice(
7825        expert_code_start + block * block_code_stride
7826            ..expert_code_start + (block + 1) * block_code_stride,
7827    );
7828    let scales = bank.scales.slice(
7829        expert_scale_start + block * block_scale_stride
7830            ..expert_scale_start + (block + 1) * block_scale_stride,
7831    );
7832    engine.qmatvec_mmq_fp8_blk_view(
7833        &weight,
7834        &scales,
7835        activations,
7836        1,
7837        FP8_BLOCK,
7838        bank.out_features,
7839    )
7840}
7841
7842fn configure_native_p2p(
7843    ranks: &[Engine],
7844    devices: &[usize],
7845) -> Result<(), Box<dyn std::error::Error>> {
7846    if ranks.len() != devices.len() || ranks.len() < 2 {
7847        return Err("native TP P2P setup requires matching multi-rank devices".into());
7848    }
7849    for (rank, (&device, engine)) in devices.iter().zip(ranks).enumerate() {
7850        if engine.ctx().ordinal() != device {
7851            return Err(format!(
7852                "native TP rank {rank} context device {} != requested device {device}",
7853                engine.ctx().ordinal()
7854            )
7855            .into());
7856        }
7857    }
7858
7859    for src in 0..ranks.len() {
7860        for dst in 0..ranks.len() {
7861            if src == dst {
7862                continue;
7863            }
7864            let mut can_access = 0;
7865            unsafe {
7866                cudarc::driver::sys::cuDeviceCanAccessPeer(
7867                    &mut can_access,
7868                    ranks[src].ctx().cu_device(),
7869                    ranks[dst].ctx().cu_device(),
7870                )
7871                .result()?;
7872            }
7873            if can_access == 0 {
7874                return Err(format!(
7875                    "native TP requires P2P, but dev{} cannot access dev{}",
7876                    devices[src], devices[dst]
7877                )
7878                .into());
7879            }
7880            ranks[src].ctx().bind_to_thread()?;
7881            let rc =
7882                unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(ranks[dst].ctx().cu_ctx(), 0) };
7883            use cudarc::driver::sys::cudaError_enum as E;
7884            if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
7885                return Err(format!(
7886                    "native TP cuCtxEnablePeerAccess(dev{} -> dev{}) failed: {rc:?}",
7887                    devices[src], devices[dst]
7888                )
7889                .into());
7890            }
7891        }
7892    }
7893
7894    for &owner in devices {
7895        for &accessor in devices {
7896            if owner == accessor {
7897                continue;
7898            }
7899            let device = cudarc::driver::result::device::get(owner as i32)?;
7900            let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
7901            unsafe {
7902                cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, device).result()?;
7903            }
7904            let desc = cudarc::driver::sys::CUmemAccessDesc {
7905                location: cudarc::driver::sys::CUmemLocation {
7906                    type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
7907                    id: accessor as i32,
7908                },
7909                flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
7910            };
7911            let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
7912            if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
7913                return Err(format!(
7914                    "native TP cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: \
7915                     {rc:?}"
7916                )
7917                .into());
7918            }
7919        }
7920    }
7921
7922    for src in 0..ranks.len() {
7923        for dst in 0..ranks.len() {
7924            if src == dst {
7925                continue;
7926            }
7927            let expected = (0..NATIVE_P2P_PROBE_WORDS)
7928                .map(|index| {
7929                    (index as u32)
7930                        .wrapping_mul(0x9e37_79b9)
7931                        .wrapping_add(((src as u32) << 16) | dst as u32)
7932                })
7933                .collect::<Vec<_>>();
7934            let poison = expected.iter().map(|value| !value).collect::<Vec<_>>();
7935            let source = ranks[src].htod_u32_v(&expected)?;
7936            let mut destination = ranks[dst].htod_u32_v(&poison)?;
7937            ranks[dst].stream().memcpy_dtod(&source, &mut destination)?;
7938            let actual = ranks[dst].dtoh_u32(&destination)?;
7939            if actual != expected {
7940                let mismatches = actual
7941                    .iter()
7942                    .zip(&expected)
7943                    .filter(|(actual, expected)| actual != expected)
7944                    .count();
7945                return Err(format!(
7946                    "native TP peer probe dev{}->dev{} failed: {mismatches}/{} words differ",
7947                    devices[src],
7948                    devices[dst],
7949                    expected.len()
7950                )
7951                .into());
7952            }
7953        }
7954    }
7955    ranks[0].ctx().bind_to_thread()?;
7956    eprintln!(
7957        "[tp] native peer byte-integrity probe PASS: devices={devices:?} \
7958         directions={} bytes={} mismatches=0",
7959        ranks.len() * (ranks.len() - 1),
7960        NATIVE_P2P_PROBE_WORDS * std::mem::size_of::<u32>(),
7961    );
7962    Ok(())
7963}
7964
7965fn validate_activations(
7966    activations: &[f32],
7967    tokens: usize,
7968    in_features: usize,
7969) -> Result<(), String> {
7970    let expected = tokens
7971        .checked_mul(in_features)
7972        .ok_or_else(|| "activation size overflow".to_string())?;
7973    if activations.len() != expected {
7974        return Err(format!(
7975            "activation count {} != {tokens}x{in_features} ({expected})",
7976            activations.len()
7977        ));
7978    }
7979    if !activations.iter().all(|value| value.is_finite()) {
7980        return Err("activations contain a non-finite value".to_string());
7981    }
7982    Ok(())
7983}
7984
7985fn column_shard(
7986    matrix: E4m3BlockMatrix<'_>,
7987    tp: usize,
7988    rank: usize,
7989) -> Result<E4m3BlockMatrix<'_>, String> {
7990    let local_out = matrix.out_features / tp;
7991    let row_start = rank * local_out;
7992    let code_start = row_start * matrix.in_features;
7993    let code_end = code_start + local_out * matrix.in_features;
7994    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
7995    let local_scale_rows = local_out / FP8_BLOCK;
7996    let scale_start = rank * local_scale_rows * scale_cols;
7997    let scale_end = scale_start + local_scale_rows * scale_cols;
7998    Ok(E4m3BlockMatrix {
7999        codes: &matrix.codes[code_start..code_end],
8000        scales: &matrix.scales[scale_start..scale_end],
8001        out_features: local_out,
8002        in_features: matrix.in_features,
8003    })
8004}
8005
8006fn row_shard(
8007    matrix: E4m3BlockMatrix<'_>,
8008    tp: usize,
8009    rank: usize,
8010) -> Result<(Vec<u8>, Vec<f32>), String> {
8011    let local_in = matrix.in_features / tp;
8012    let col_start = rank * local_in;
8013    let mut codes = Vec::with_capacity(matrix.out_features * local_in);
8014    for row in 0..matrix.out_features {
8015        let start = row * matrix.in_features + col_start;
8016        codes.extend_from_slice(&matrix.codes[start..start + local_in]);
8017    }
8018
8019    let scale_rows = matrix.out_features.div_ceil(FP8_BLOCK);
8020    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
8021    let local_scale_cols = local_in / FP8_BLOCK;
8022    let scale_col_start = rank * local_scale_cols;
8023    let mut scales = Vec::with_capacity(scale_rows * local_scale_cols);
8024    for row in 0..scale_rows {
8025        let start = row * scale_cols + scale_col_start;
8026        scales.extend_from_slice(&matrix.scales[start..start + local_scale_cols]);
8027    }
8028    Ok((codes, scales))
8029}
8030
8031fn activation_shard(
8032    activations: &[f32],
8033    tokens: usize,
8034    in_features: usize,
8035    tp: usize,
8036    rank: usize,
8037) -> Vec<f32> {
8038    let local_in = in_features / tp;
8039    let col_start = rank * local_in;
8040    let mut shard = Vec::with_capacity(tokens * local_in);
8041    for token in 0..tokens {
8042        let start = token * in_features + col_start;
8043        shard.extend_from_slice(&activations[start..start + local_in]);
8044    }
8045    shard
8046}
8047
8048// ─── Step NVFP4 expert TP program (official Step-3.7-Flash-NVFP4 checkpoint class) ─────────────
8049//
8050// The routed experts of the NVFP4 checkpoint are modelopt-packed: e2m1 codes (2/byte), per-16
8051// UE4M3 sub-scales, and a per-EXPERT `weight_scale_2` f32 macro (~1e-5..1e-4, LOAD-BEARING).
8052// Rank compute repacks each shard host-side into memra block_nvfp4 rows (nibble reorder only —
8053// value-exact, see nvfp4_repack.rs) and runs the proven `qmatvec_nvfp4_fast` dp4a kernel; the
8054// activation q8_1 quantization uses per-32 blocks, and every shard cut here is 64-aligned, so a
8055// rank-local partial is bit-identical to the corresponding slice of the unsharded kernel.
8056//
8057// MACRO CANONICAL ORDER: the macro multiplies each assembled f32 output exactly ONCE — after the
8058// column gather (gate/up) and after the FULL row-parallel reduce (down), never per-partial.
8059// `(a + b) * m` and `a * m + b * m` differ in f32, so applying it per-rank would break the
8060// TP1-vs-TP2 bit gate. Every entry point below follows this order.
8061//
8062// TP2 shard legality is NVFP4-native: column parallelism splits whole output rows (scale rows
8063// ride along, nothing cuts), row parallelism splits input columns at 64-element superblock
8064// boundaries (16-element scale groups nest inside). The 128-block E4M3 constraint does not apply.
8065
8066/// One expert's modelopt NVFP4 projection: packed codes + per-16 UE4M3 scale bytes + macro.
8067#[derive(Clone, Copy)]
8068pub struct Nvfp4BlockMatrix<'a> {
8069    pub codes: &'a [u8],  // [out_features, in_features/2] packed e2m1, row-major
8070    pub scales: &'a [u8], // [out_features, in_features/16] UE4M3 bytes, row-major
8071    pub macro_scale: f32, // per-expert weight_scale_2 dequant multiplier
8072    pub out_features: usize,
8073    pub in_features: usize,
8074}
8075
8076impl Nvfp4BlockMatrix<'_> {
8077    pub fn validate(&self) -> Result<(), String> {
8078        if self.in_features == 0 || self.out_features == 0 {
8079            return Err("NVFP4 matrix has a zero dimension".to_string());
8080        }
8081        if self.in_features % 64 != 0 {
8082            return Err(format!(
8083                "NVFP4 in_features {} is not 64-aligned (memra block_nvfp4 superblock)",
8084                self.in_features
8085            ));
8086        }
8087        if self.codes.len() != self.out_features * self.in_features / 2 {
8088            return Err(format!(
8089                "NVFP4 code bytes {} != {}x{}/2",
8090                self.codes.len(),
8091                self.out_features,
8092                self.in_features
8093            ));
8094        }
8095        if self.scales.len() != self.out_features * self.in_features / 16 {
8096            return Err(format!(
8097                "NVFP4 scale bytes {} != {}x{}/16",
8098                self.scales.len(),
8099                self.out_features,
8100                self.in_features
8101            ));
8102        }
8103        if !self.macro_scale.is_finite() || self.macro_scale <= 0.0 {
8104            return Err(format!(
8105                "NVFP4 macro scale {} is not finite-positive",
8106                self.macro_scale
8107            ));
8108        }
8109        Ok(())
8110    }
8111}
8112
8113/// Stacked modelopt NVFP4 expert bank (host view over the checkpoint bytes).
8114#[derive(Clone, Copy)]
8115pub struct Nvfp4ExpertBank<'a> {
8116    pub codes: &'a [u8],   // [expert_count, out_features, in_features/2]
8117    pub scales: &'a [u8],  // [expert_count, out_features, in_features/16]
8118    pub macros: &'a [f32], // [expert_count] weight_scale_2
8119    pub expert_count: usize,
8120    pub out_features: usize,
8121    pub in_features: usize,
8122}
8123
8124impl Nvfp4ExpertBank<'_> {
8125    pub fn validate(&self) -> Result<(), String> {
8126        if self.expert_count == 0 {
8127            return Err("NVFP4 expert bank is empty".to_string());
8128        }
8129        if self.macros.len() != self.expert_count {
8130            return Err(format!(
8131                "NVFP4 bank macros {} != expert count {}",
8132                self.macros.len(),
8133                self.expert_count
8134            ));
8135        }
8136        self.expert(0).map(|_| ())
8137    }
8138
8139    pub fn expert(&self, expert: usize) -> Result<Nvfp4BlockMatrix<'_>, String> {
8140        if expert >= self.expert_count {
8141            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
8142        }
8143        let code_stride = self.out_features * self.in_features / 2;
8144        let scale_stride = self.out_features * self.in_features / 16;
8145        if self.codes.len() != self.expert_count * code_stride
8146            || self.scales.len() != self.expert_count * scale_stride
8147        {
8148            return Err("NVFP4 bank byte extents do not match the declared geometry".to_string());
8149        }
8150        let matrix = Nvfp4BlockMatrix {
8151            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
8152            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
8153            macro_scale: self.macros[expert],
8154            out_features: self.out_features,
8155            in_features: self.in_features,
8156        };
8157        matrix.validate()?;
8158        Ok(matrix)
8159    }
8160}
8161
8162/// One rank's resident repacked NVFP4 shard: memra block_nvfp4 rows on device.
8163pub struct ResidentNvfp4Rank {
8164    blocks: crate::CudaSlice<u8>,
8165    macro_scale: f32,
8166    out_features: usize,
8167    in_features: usize,
8168    row_bytes: usize,
8169}
8170
8171pub struct ResidentNvfp4ColumnParallel {
8172    ranks: Vec<ResidentNvfp4Rank>,
8173    pub out_features: usize,
8174    pub in_features: usize,
8175}
8176
8177pub struct ResidentNvfp4RowParallel {
8178    ranks: Vec<ResidentNvfp4Rank>,
8179    pub out_features: usize,
8180    pub in_features: usize,
8181}
8182
8183pub struct ResidentTpNvfp4Expert {
8184    gate: ResidentNvfp4ColumnParallel,
8185    up: ResidentNvfp4ColumnParallel,
8186    down: ResidentNvfp4RowParallel,
8187    pub input_width: usize,
8188    pub expert_width: usize,
8189}
8190
8191/// One rank's resident NVFP4 expert bank shard: one repacked block buffer PER expert (per-expert
8192/// device allocations keep this increment off any new strided-kernel API; the strided twin is a
8193/// later perf rung, mirroring the FP8 bank's history).
8194pub struct ResidentNvfp4ColumnBankRank {
8195    /// Contiguous per-rank expert bank: `expert_count` repacked shards of `expert_bytes` each.
8196    /// Contiguity is what lets the device-routes program cover every selected expert with ONE
8197    /// launch (`qmatvec_nvfp4_dp4a_sel` indexes `sel[t] * expert_bytes`).
8198    bank: crate::CudaSlice<u8>,
8199    expert_bytes: usize,
8200    local_out: usize,
8201    in_features: usize,
8202    row_bytes: usize,
8203}
8204
8205impl ResidentNvfp4ColumnBankRank {
8206    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
8207        self.bank
8208            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
8209    }
8210}
8211
8212/// Canonical row-shard count for the NVFP4 down projection. The down reduction ALWAYS executes
8213/// as exactly this many input-column windows summed in shard order, at every world size: a
8214/// single full-width dot and a two-half-dots-plus-add differ in f32 parenthesization, so pinning
8215/// the shard grid (not the world size) is what makes the TP1-oracle-vs-TP2 bit gate meaningful.
8216/// This is the NVFP4 twin of the FP8 bank's canonical checkpoint-block reduction.
8217pub const NVFP4_CANONICAL_ROW_SHARDS: usize = 2;
8218
8219pub struct ResidentNvfp4RowBankRank {
8220    /// Contiguous per-shard expert bank (see `ResidentNvfp4ColumnBankRank::bank`).
8221    bank: crate::CudaSlice<u8>,
8222    expert_bytes: usize,
8223    device_rank: usize, // index into the runtime's rank engines this canonical shard lives on
8224    out_features: usize,
8225    local_in: usize,
8226    row_bytes: usize,
8227}
8228
8229impl ResidentNvfp4RowBankRank {
8230    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
8231        self.bank
8232            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
8233    }
8234}
8235
8236impl ResidentNvfp4TensorParallel {
8237    pub(crate) fn device_workspace_handle(
8238        &self,
8239    ) -> &std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>> {
8240        &self.device_workspace
8241    }
8242}
8243
8244pub struct ResidentNvfp4TensorParallel {
8245    gate: Vec<ResidentNvfp4ColumnBankRank>,
8246    up: Vec<ResidentNvfp4ColumnBankRank>,
8247    down: Vec<ResidentNvfp4RowBankRank>,
8248    macros_gate: Vec<f32>,
8249    macros_up: Vec<f32>,
8250    macros_down: Vec<f32>,
8251    /// Per-rank device copies of the gate/up macro-scales (E f32 each), indexed by the
8252    /// batched SwiGLU kernel via the selection array. Down macros stay host-side — they fold
8253    /// into the route-weight axpy scalar.
8254    macros_gate_dev: Vec<crate::CudaSlice<f32>>,
8255    macros_up_dev: Vec<crate::CudaSlice<f32>>,
8256    macros_down_dev: Vec<crate::CudaSlice<f32>>,
8257    pub expert_count: usize,
8258    pub input_width: usize,
8259    pub expert_width: usize,
8260    /// Lazily-built persistent decode workspace (device routes program). Interior mutability
8261    /// mirrors StepEpGroupedDecode: the forward holds the bank behind a shared reference.
8262    device_workspace: std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>>,
8263    /// Lazily-built spec-verify t=2 workspace (MEMRA_TCOL_FFN): the two-column routed
8264    /// sweep's slabs and events, kept apart from the serving workspace so the verify walk
8265    /// never perturbs serving state.
8266    t2_workspace: std::sync::Mutex<Option<Nvfp4T2Workspace>>,
8267    /// MEMRA_STEP_NVFP4_EP2: the rank banks above hold WHOLE experts (owner = id & 1,
8268    /// slot = id >> 1) at full width instead of TP shards. Consumers must branch on this;
8269    /// shard-semantics paths refuse loudly.
8270    pub(crate) ep2: bool,
8271}
8272
8273/// Persistent buffers for the two-column (spec verify) NVFP4 device-routed program: every
8274/// slab is the t=1 workspace shape doubled along the pair axis, plus per-column
8275/// accumulators. One per expert bank, reused every (round, layer) call.
8276pub struct Nvfp4T2Workspace {
8277    input2: Vec<crate::CudaSlice<f32>>,
8278    in_q2: Vec<crate::CudaSlice<i8>>,
8279    in_d2: Vec<crate::CudaSlice<f32>>,
8280    sel2: Vec<crate::CudaSlice<i32>>,
8281    route_w2: Vec<crate::CudaSlice<f32>>,
8282    gate_out2: Vec<crate::CudaSlice<f32>>,
8283    up_out2: Vec<crate::CudaSlice<f32>>,
8284    act_q2: Vec<crate::CudaSlice<i8>>,
8285    act_d2: Vec<crate::CudaSlice<f32>>,
8286    partial2: Vec<crate::CudaSlice<f32>>,
8287    /// Per-rank per-column combine accumulators ([width] each).
8288    acc_a: Vec<crate::CudaSlice<f32>>,
8289    acc_b: Vec<crate::CudaSlice<f32>>,
8290    /// Root-side pulls of rank1's accumulators and the joined columns.
8291    peer_a: crate::CudaSlice<f32>,
8292    peer_b: crate::CudaSlice<f32>,
8293    omix_a: crate::CudaSlice<f32>,
8294    omix_b: crate::CudaSlice<f32>,
8295    ev_entry: CudaEvent,
8296    ev_rank: Vec<CudaEvent>,
8297    ev_root: CudaEvent,
8298    n_sel: usize,
8299    e_device: usize,
8300}
8301
8302/// Persistent per-call device buffers for the NVFP4 device routes program: one gate/up output,
8303/// one down partial, and one shard accumulator per rank, plus root combine staging. Reused every
8304/// (token, layer) call so the decode loop performs zero output allocations.
8305/// A stitched multi-device parent graph for one layer's device-routed expert program, plus
8306/// the children it was built from (retained: AddChildGraphNode clones, but the probe retains
8307/// conservatively) and the persistent e-context input staging its copies read.
8308struct RoutesGraph {
8309    exec: cudarc::driver::sys::CUgraphExec,
8310    parent: cudarc::driver::sys::CUgraph,
8311    _children: Vec<cudarc::driver::CudaGraph>,
8312}
8313// SAFETY: the raw handles are only used from the single decode thread; CUDA graph handles are
8314// context-agnostic process handles.
8315unsafe impl Send for RoutesGraph {}
8316
8317impl Drop for RoutesGraph {
8318    fn drop(&mut self) {
8319        unsafe {
8320            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
8321            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
8322        }
8323    }
8324}
8325
8326impl Nvfp4DeviceRoutesWorkspace {
8327    pub(crate) fn in_stage_handle(&self) -> Option<&crate::CudaSlice<f32>> {
8328        self.in_stage_e.as_ref()
8329    }
8330    pub(crate) fn in_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
8331        self.in_stage_e.as_mut()
8332    }
8333    pub(crate) fn out_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
8334        self.out_stage_e.as_mut()
8335    }
8336    /// Arm the e-context stages + router staging pair when absent (token-graph entry).
8337    pub(crate) fn arm_stages(
8338        &mut self,
8339        e: &Engine,
8340        width: usize,
8341        n_sel: usize,
8342    ) -> Result<(), Box<dyn std::error::Error>> {
8343        let _main = e.gpu.enter_main()?;
8344        if self.in_stage_e.is_none() {
8345            self.in_stage_e = Some(e.htod(&vec![0.0f32; width])?);
8346            self.out_stage_e = Some(e.htod(&vec![0.0f32; width])?);
8347        }
8348        if self.dev_route_e.is_none() {
8349            self.dev_route_e = Some((
8350                e.htod_i32(&vec![0i32; n_sel])?,
8351                e.htod(&vec![0.0f32; n_sel])?,
8352            ));
8353        }
8354        Ok(())
8355    }
8356
8357    /// Split-borrow: the routes input (shared) + output (mut) stages together.
8358    pub(crate) fn in_and_out_stages_mut(
8359        &mut self,
8360    ) -> Option<(&crate::CudaSlice<f32>, &mut crate::CudaSlice<f32>)> {
8361        match (self.in_stage_e.as_ref(), self.out_stage_e.as_mut()) {
8362            (Some(input), Some(output)) => Some((input, output)),
8363            _ => None,
8364        }
8365    }
8366    pub(crate) fn dev_route_e_mut(
8367        &mut self,
8368    ) -> Option<(&mut crate::CudaSlice<i32>, &mut crate::CudaSlice<f32>)> {
8369        self.dev_route_e.as_mut().map(|(a, b)| (a, b))
8370    }
8371}
8372
8373pub struct Nvfp4DeviceRoutesWorkspace {
8374    /// [n_sel, local_out] batched gate/up outputs and the SwiGLU q8_1 pair; [n_sel, width]
8375    /// down partials. Sized for `n_sel` selected experts per token (pinned at first call).
8376    gate_out: Vec<crate::CudaSlice<f32>>,
8377    up_out: Vec<crate::CudaSlice<f32>>,
8378    act_q: Vec<crate::CudaSlice<i8>>,
8379    act_d: Vec<crate::CudaSlice<f32>>,
8380    sel: Vec<crate::CudaSlice<i32>>,
8381    partial: Vec<crate::CudaSlice<f32>>,
8382    accumulator: Vec<crate::CudaSlice<f32>>,
8383    /// Per-rank folded combine weights (route_weight x down macro), one htod per call.
8384    combine_w: Vec<crate::CudaSlice<f32>>,
8385    /// Device-routed extension: per-rank raw route weights (the down-macro fold happens
8386    /// in-kernel via sel + macros_down_dev).
8387    route_w: Vec<crate::CudaSlice<f32>>,
8388    /// Persistent q8_1 pair of the shared layer input (one quantize per rank per call, no
8389    /// per-call allocation).
8390    in_q: Vec<crate::CudaSlice<i8>>,
8391    in_d: Vec<crate::CudaSlice<f32>>,
8392    /// e-context staging for the device router outputs (persistent — rank streams peer-read
8393    /// them, so the router's fresh outputs are copied here on e's stream first; the pp.rs
8394    /// never-free discipline).
8395    dev_route_e: Option<(crate::CudaSlice<i32>, crate::CudaSlice<f32>)>,
8396    /// Prestage door state: input pull + quantize already issued for this layer's call
8397    /// (nvfp4_routes_prestage), so the routed run skips them. Reset per call.
8398    prestaged: bool,
8399    /// Peer-router door state: rank1's sel/route_w were computed locally in prestage;
8400    /// the routed run skips rank1's sel pull. Reset per call.
8401    rank1_routed: bool,
8402    /// Doorbell fences (MEMRA_FENCE_MEMOPS): raw cuMemAlloc'd [rank1_flag, root_flag]
8403    /// u32 pair in ROOT memory (async-pool memory is memop-INELIGIBLE — receipted
8404    /// CUDA_ERROR_INVALID_VALUE) + the host-side monotonic ticket. 0 = unarmed.
8405    fence_flags_raw: u64,
8406    fence_ticket: u32,
8407    /// Prestage input fence, recorded on e after the input's producer.
8408    ev_input: Option<(CudaEvent, usize)>,
8409    /// Graph-door staging: persistent e-context input row + output row (fixed addresses the
8410    /// captured copies read/write), and the per-layer stitched parent.
8411    in_stage_e: Option<crate::CudaSlice<f32>>,
8412    out_stage_e: Option<crate::CudaSlice<f32>>,
8413    routes_graph: Option<RoutesGraph>,
8414    /// Token-graph raw pointer sets (armed once by routes_arm_raw).
8415    raw_dev_route_e: Option<(u64, u64)>,
8416    raw_combine: Option<(u64, u64, u64, u64)>,
8417    raw_input: Vec<u64>,
8418    raw_sel: Vec<u64>,
8419    raw_route_w: Vec<u64>,
8420    remote: crate::CudaSlice<f32>,
8421    combined: crate::CudaSlice<f32>,
8422    n_sel: usize,
8423    /// Device-IO extension (lazily built by `run_tensor_parallel_routes_nvfp4_device_io`):
8424    /// persistent per-rank input rows plus the evented ordering pair — the pp.rs
8425    /// BoundarySlot discipline, same as the v2 attention workspace.
8426    input: Vec<crate::CudaSlice<f32>>,
8427    ev_rank: Vec<CudaEvent>,
8428    ev_done: Option<CudaEvent>,
8429    ev_entry: Option<(CudaEvent, usize)>,
8430}
8431
8432/// One rank's whole-expert NVFP4 residency (expert-parallel ownership).
8433struct ResidentNvfp4EpRank {
8434    gate: Vec<crate::CudaSlice<u8>>,
8435    up: Vec<crate::CudaSlice<u8>>,
8436    down: Vec<crate::CudaSlice<u8>>,
8437    #[allow(dead_code)]
8438    expert_range: Range<usize>,
8439}
8440
8441pub struct ResidentNvfp4ExpertParallel {
8442    ranks: Vec<ResidentNvfp4EpRank>,
8443    macros_gate: Vec<f32>,
8444    macros_up: Vec<f32>,
8445    macros_down: Vec<f32>,
8446    pub expert_count: usize,
8447    pub input_width: usize,
8448    pub expert_width: usize,
8449    gate_row_bytes: usize,
8450    down_row_bytes: usize,
8451}
8452
8453fn nvfp4_repack_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
8454    memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
8455        matrix.codes,
8456        matrix.scales,
8457        matrix.out_features,
8458        matrix.in_features,
8459    )
8460}
8461
8462fn nvfp4_row_bytes(in_features: usize) -> usize {
8463    in_features / 64 * 36 // memra block_nvfp4: 64 elems -> 36 bytes (4 UE4M3 + 32 packed e2m1)
8464}
8465
8466/// MEMRA_NVFP4_BANK_V2=1: store the contiguous expert banks in the slot-major layout the
8467/// coalesced `*_v2` kernels read (see qmatvec.cu). Pure byte permutation — value-exact.
8468/// MEMRA_NO_LOCAL_SHADOW=1: skip the per-layer local-KV shadow gathers and appends in the
8469/// eager v2 decode (lengths still advance) — the graph door proved contents-stale local KV
8470/// is decode-identical (12/12). The local contents feed spec/MTP scratch only.
8471/// MEMRA_FUSE_ROPE_APPEND=1: fuse qk norms + rope + dcw KV append + len inc into one
8472/// launch per rank per layer (bit-identical; identity-gated). dcw path only.
8473pub(crate) fn fuse_rope_append_on() -> bool {
8474    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8475    *ON.get_or_init(|| std::env::var("MEMRA_FUSE_ROPE_APPEND").as_deref() == Ok("1"))
8476}
8477
8478pub(crate) fn no_local_shadow_on() -> bool {
8479    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8480    *ON.get_or_init(|| std::env::var("MEMRA_NO_LOCAL_SHADOW").as_deref() == Ok("1"))
8481}
8482
8483pub(crate) fn nvfp4_bank_v2_on() -> bool {
8484    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8485    *ON.get_or_init(|| std::env::var("MEMRA_NVFP4_BANK_V2").as_deref() == Ok("1"))
8486}
8487
8488/// Permute one repacked block_nvfp4 matrix (out_features rows of `nvfp4_row_bytes(in_f)`)
8489/// into the slot-major v2 row layout: per row, slot g's 16 qs bytes at g*16, then the two
8490/// UE4M3 scale bytes per slot at nslots*16 + g*2. Row byte count unchanged.
8491fn nvfp4_matrix_v2_permute(v1: &[u8], out_features: usize, in_features: usize) -> Vec<u8> {
8492    let row_bytes = nvfp4_row_bytes(in_features);
8493    assert_eq!(v1.len(), out_features * row_bytes, "v2 permute geometry");
8494    let n_slots = in_features / 32;
8495    let mut out = Vec::with_capacity(v1.len());
8496    for row in 0..out_features {
8497        let r = &v1[row * row_bytes..(row + 1) * row_bytes];
8498        for g in 0..n_slots {
8499            let (sblk, h) = (g / 2, g % 2);
8500            let b = &r[sblk * 36..sblk * 36 + 36];
8501            out.extend_from_slice(&b[4 + 16 * h..4 + 16 * h + 16]);
8502        }
8503        for g in 0..n_slots {
8504            let (sblk, h) = (g / 2, g % 2);
8505            let b = &r[sblk * 36..sblk * 36 + 36];
8506            out.push(b[2 * h]);
8507            out.push(b[2 * h + 1]);
8508        }
8509    }
8510    out
8511}
8512
8513/// Repack + (optionally) v2-permute one expert shard for the contiguous banks.
8514fn nvfp4_repack_bank_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
8515    let (out_features, in_features) = (matrix.out_features, matrix.in_features);
8516    let v1 = nvfp4_repack_matrix(matrix);
8517    if nvfp4_bank_v2_on() {
8518        nvfp4_matrix_v2_permute(&v1, out_features, in_features)
8519    } else {
8520        v1
8521    }
8522}
8523
8524/// Column shard: whole output rows per rank (codes and scales are row-major, so both slices are
8525/// contiguous borrows). The macro rides unchanged — it is applied post-gather by the caller.
8526fn nvfp4_column_shard<'a>(
8527    matrix: Nvfp4BlockMatrix<'a>,
8528    tp: usize,
8529    rank: usize,
8530) -> Result<Nvfp4BlockMatrix<'a>, String> {
8531    if matrix.out_features % tp != 0 {
8532        return Err(format!(
8533            "NVFP4 column-parallel out_features {} is not divisible by TP={tp}",
8534            matrix.out_features
8535        ));
8536    }
8537    let local_out = matrix.out_features / tp;
8538    let code_row = matrix.in_features / 2;
8539    let scale_row = matrix.in_features / 16;
8540    Ok(Nvfp4BlockMatrix {
8541        codes: &matrix.codes[rank * local_out * code_row..(rank + 1) * local_out * code_row],
8542        scales: &matrix.scales[rank * local_out * scale_row..(rank + 1) * local_out * scale_row],
8543        macro_scale: matrix.macro_scale,
8544        out_features: local_out,
8545        in_features: matrix.in_features,
8546    })
8547}
8548
8549/// Row shard: input-column windows per rank, 64-superblock aligned. Owned buffers: each output
8550/// row contributes one contiguous byte window, gathered across rows.
8551fn nvfp4_row_shard(
8552    matrix: Nvfp4BlockMatrix<'_>,
8553    tp: usize,
8554    rank: usize,
8555) -> Result<(Vec<u8>, Vec<u8>, usize), String> {
8556    if matrix.in_features % tp != 0 {
8557        return Err(format!(
8558            "NVFP4 row-parallel in_features {} is not divisible by TP={tp}",
8559            matrix.in_features
8560        ));
8561    }
8562    let local_in = matrix.in_features / tp;
8563    if local_in % 64 != 0 {
8564        return Err(format!(
8565            "NVFP4 row-parallel input shard {local_in} cuts through a 64-element superblock"
8566        ));
8567    }
8568    let code_row = matrix.in_features / 2;
8569    let scale_row = matrix.in_features / 16;
8570    let local_code = local_in / 2;
8571    let local_scale = local_in / 16;
8572    let mut codes = Vec::with_capacity(matrix.out_features * local_code);
8573    let mut scales = Vec::with_capacity(matrix.out_features * local_scale);
8574    for row in 0..matrix.out_features {
8575        let code_start = row * code_row + rank * local_code;
8576        codes.extend_from_slice(&matrix.codes[code_start..code_start + local_code]);
8577        let scale_start = row * scale_row + rank * local_scale;
8578        scales.extend_from_slice(&matrix.scales[scale_start..scale_start + local_scale]);
8579    }
8580    Ok((codes, scales, local_in))
8581}
8582
8583/// Rank compute leaf: repack modelopt -> block_nvfp4, upload, run the proven dp4a kernel. The
8584/// macro is NOT applied here — callers apply it once at the canonical post-gather/post-reduce
8585/// point (see the section header).
8586fn run_rank_nvfp4(
8587    engine: &Engine,
8588    matrix: Nvfp4BlockMatrix<'_>,
8589    activations: &[f32],
8590    tokens: usize,
8591) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8592    matrix.validate()?;
8593    validate_activations(activations, tokens, matrix.in_features)?;
8594    let _main = engine.gpu.enter_main()?;
8595    let blocks = engine.htod_bytes(&nvfp4_repack_matrix(matrix))?;
8596    let activations = engine.htod(activations)?;
8597    let output = engine.qmatvec_nvfp4_fast(
8598        &blocks.slice(0..blocks.len()),
8599        &activations,
8600        tokens,
8601        matrix.in_features,
8602        matrix.out_features,
8603        nvfp4_row_bytes(matrix.in_features),
8604    )?;
8605    engine.dtoh(&output)
8606}
8607
8608fn upload_rank_nvfp4(
8609    engine: &Engine,
8610    matrix: Nvfp4BlockMatrix<'_>,
8611) -> Result<ResidentNvfp4Rank, Box<dyn std::error::Error>> {
8612    matrix.validate()?;
8613    let _main = engine.gpu.enter_main()?;
8614    Ok(ResidentNvfp4Rank {
8615        blocks: engine.htod_bytes(&nvfp4_repack_matrix(matrix))?,
8616        macro_scale: matrix.macro_scale,
8617        out_features: matrix.out_features,
8618        in_features: matrix.in_features,
8619        row_bytes: nvfp4_row_bytes(matrix.in_features),
8620    })
8621}
8622
8623fn run_resident_rank_nvfp4(
8624    engine: &Engine,
8625    rank: &ResidentNvfp4Rank,
8626    activations: &[f32],
8627    tokens: usize,
8628) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8629    validate_activations(activations, tokens, rank.in_features)?;
8630    let _main = engine.gpu.enter_main()?;
8631    let activations = engine.htod(activations)?;
8632    let output = engine.qmatvec_nvfp4_fast(
8633        &rank.blocks.slice(0..rank.blocks.len()),
8634        &activations,
8635        tokens,
8636        rank.in_features,
8637        rank.out_features,
8638        rank.row_bytes,
8639    )?;
8640    engine.dtoh(&output)
8641}
8642
8643fn apply_macro(values: &mut [f32], macro_scale: f32) {
8644    for value in values.iter_mut() {
8645        *value *= macro_scale;
8646    }
8647}
8648
8649impl TpE4m3HostBounce {
8650    /// Unsharded NVFP4 projection on rank 0 (compatibility oracle). Macro applied post-kernel.
8651    pub fn full_nvfp4(
8652        &self,
8653        matrix: Nvfp4BlockMatrix<'_>,
8654        activations: &[f32],
8655        tokens: usize,
8656    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8657        let mut output = run_rank_nvfp4(&self.ranks[0], matrix, activations, tokens)?;
8658        apply_macro(&mut output, matrix.macro_scale);
8659        Ok(output)
8660    }
8661
8662    /// Column-parallel NVFP4 projection: output rows partition across ranks, host gather in rank
8663    /// order, macro applied ONCE post-gather.
8664    pub fn column_parallel_nvfp4(
8665        &self,
8666        matrix: Nvfp4BlockMatrix<'_>,
8667        activations: &[f32],
8668        tokens: usize,
8669    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
8670        matrix.validate()?;
8671        validate_activations(activations, tokens, matrix.in_features)?;
8672        let tp = self.ranks.len();
8673        let local_out = matrix.out_features / tp;
8674        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
8675        let mut rank_outputs = Vec::with_capacity(tp);
8676        for (rank_index, rank) in self.ranks.iter().enumerate() {
8677            let shard = nvfp4_column_shard(matrix, tp, rank_index)?;
8678            let output = run_rank_nvfp4(rank, shard, activations, tokens)?;
8679            let row_start = rank_index * local_out;
8680            for token in 0..tokens {
8681                gathered[token * matrix.out_features + row_start
8682                    ..token * matrix.out_features + row_start + local_out]
8683                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
8684            }
8685            rank_outputs.push(output);
8686        }
8687        apply_macro(&mut gathered, matrix.macro_scale);
8688        Ok(ColumnParallelResult {
8689            gathered,
8690            rank_outputs,
8691        })
8692    }
8693
8694    /// Row-parallel NVFP4 projection: input columns partition at 64-superblock boundaries,
8695    /// rank-local partials reduce in stable rank order, macro applied ONCE post-reduce.
8696    pub fn row_parallel_nvfp4(
8697        &self,
8698        matrix: Nvfp4BlockMatrix<'_>,
8699        activations: &[f32],
8700        tokens: usize,
8701    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
8702        matrix.validate()?;
8703        validate_activations(activations, tokens, matrix.in_features)?;
8704        let tp = self.ranks.len();
8705        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
8706        let mut rank_partials = Vec::with_capacity(tp);
8707        for (rank_index, rank) in self.ranks.iter().enumerate() {
8708            let (codes, scales, local_in) = nvfp4_row_shard(matrix, tp, rank_index)?;
8709            let local_activations =
8710                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
8711            let shard = Nvfp4BlockMatrix {
8712                codes: &codes,
8713                scales: &scales,
8714                macro_scale: matrix.macro_scale,
8715                out_features: matrix.out_features,
8716                in_features: local_in,
8717            };
8718            let partial = run_rank_nvfp4(rank, shard, &local_activations, tokens)?;
8719            for (sum, value) in reduced.iter_mut().zip(&partial) {
8720                *sum += *value;
8721            }
8722            rank_partials.push(partial);
8723        }
8724        apply_macro(&mut reduced, matrix.macro_scale);
8725        Ok(RowParallelResult {
8726            reduced,
8727            rank_partials,
8728        })
8729    }
8730
8731    pub fn upload_expert_nvfp4(
8732        &self,
8733        gate: Nvfp4BlockMatrix<'_>,
8734        up: Nvfp4BlockMatrix<'_>,
8735        down: Nvfp4BlockMatrix<'_>,
8736    ) -> Result<ResidentTpNvfp4Expert, Box<dyn std::error::Error>> {
8737        if gate.in_features != up.in_features || gate.out_features != up.out_features {
8738            return Err("NVFP4 TP expert gate/up dimensions differ".into());
8739        }
8740        if down.in_features != gate.out_features || down.out_features != gate.in_features {
8741            return Err(format!(
8742                "NVFP4 TP expert down {}x{} does not invert gate/up {}x{}",
8743                down.out_features, down.in_features, gate.out_features, gate.in_features
8744            )
8745            .into());
8746        }
8747        let tp = self.ranks.len();
8748        let mut gate_ranks = Vec::with_capacity(tp);
8749        let mut up_ranks = Vec::with_capacity(tp);
8750        let mut down_ranks = Vec::with_capacity(tp);
8751        for (rank_index, engine) in self.ranks.iter().enumerate() {
8752            gate_ranks.push(upload_rank_nvfp4(
8753                engine,
8754                nvfp4_column_shard(gate, tp, rank_index)?,
8755            )?);
8756            up_ranks.push(upload_rank_nvfp4(
8757                engine,
8758                nvfp4_column_shard(up, tp, rank_index)?,
8759            )?);
8760            let (codes, scales, local_in) = nvfp4_row_shard(down, tp, rank_index)?;
8761            down_ranks.push(upload_rank_nvfp4(
8762                engine,
8763                Nvfp4BlockMatrix {
8764                    codes: &codes,
8765                    scales: &scales,
8766                    macro_scale: down.macro_scale,
8767                    out_features: down.out_features,
8768                    in_features: local_in,
8769                },
8770            )?);
8771        }
8772        Ok(ResidentTpNvfp4Expert {
8773            gate: ResidentNvfp4ColumnParallel {
8774                ranks: gate_ranks,
8775                out_features: gate.out_features,
8776                in_features: gate.in_features,
8777            },
8778            up: ResidentNvfp4ColumnParallel {
8779                ranks: up_ranks,
8780                out_features: up.out_features,
8781                in_features: up.in_features,
8782            },
8783            down: ResidentNvfp4RowParallel {
8784                ranks: down_ranks,
8785                out_features: down.out_features,
8786                in_features: down.in_features,
8787            },
8788            input_width: gate.in_features,
8789            expert_width: gate.out_features,
8790        })
8791    }
8792
8793    fn column_parallel_resident_nvfp4(
8794        &self,
8795        matrix: &ResidentNvfp4ColumnParallel,
8796        activations: &[f32],
8797        tokens: usize,
8798    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8799        validate_activations(activations, tokens, matrix.in_features)?;
8800        let local_out = matrix.out_features / self.ranks.len();
8801        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
8802        let mut macro_scale = None;
8803        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
8804            let output = run_resident_rank_nvfp4(engine, shard, activations, tokens)?;
8805            let row_start = rank_index * local_out;
8806            for token in 0..tokens {
8807                gathered[token * matrix.out_features + row_start
8808                    ..token * matrix.out_features + row_start + local_out]
8809                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
8810            }
8811            macro_scale = Some(shard.macro_scale);
8812        }
8813        apply_macro(
8814            &mut gathered,
8815            macro_scale.ok_or("NVFP4 column-parallel matrix has no ranks")?,
8816        );
8817        Ok(gathered)
8818    }
8819
8820    fn row_parallel_resident_nvfp4(
8821        &self,
8822        matrix: &ResidentNvfp4RowParallel,
8823        activations: &[f32],
8824        tokens: usize,
8825    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8826        validate_activations(activations, tokens, matrix.in_features)?;
8827        let tp = self.ranks.len();
8828        let local_in = matrix.in_features / tp;
8829        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
8830        let mut macro_scale = None;
8831        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
8832            if shard.in_features != local_in {
8833                return Err(format!(
8834                    "NVFP4 resident row shard in_features {} != expected {local_in}",
8835                    shard.in_features
8836                )
8837                .into());
8838            }
8839            let local_activations =
8840                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
8841            let partial = run_resident_rank_nvfp4(engine, shard, &local_activations, tokens)?;
8842            for (sum, value) in reduced.iter_mut().zip(&partial) {
8843                *sum += *value;
8844            }
8845            macro_scale = Some(shard.macro_scale);
8846        }
8847        apply_macro(
8848            &mut reduced,
8849            macro_scale.ok_or("NVFP4 row-parallel matrix has no ranks")?,
8850        );
8851        Ok(reduced)
8852    }
8853
8854    pub fn run_expert_nvfp4(
8855        &self,
8856        expert: &ResidentTpNvfp4Expert,
8857        input: &[f32],
8858        tokens: usize,
8859    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8860        validate_activations(input, tokens, expert.input_width)?;
8861        let gate = self.column_parallel_resident_nvfp4(&expert.gate, input, tokens)?;
8862        let up = self.column_parallel_resident_nvfp4(&expert.up, input, tokens)?;
8863        let activated: Vec<f32> = gate
8864            .iter()
8865            .zip(&up)
8866            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
8867            .collect();
8868        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
8869        self.row_parallel_resident_nvfp4(&expert.down, &activated, tokens)
8870    }
8871
8872    /// Upload every expert's TP shards resident (one repacked block buffer per expert per rank).
8873    pub fn upload_tensor_parallel_nvfp4(
8874        &self,
8875        gate: Nvfp4ExpertBank<'_>,
8876        up: Nvfp4ExpertBank<'_>,
8877        down: Nvfp4ExpertBank<'_>,
8878    ) -> Result<ResidentNvfp4TensorParallel, Box<dyn std::error::Error>> {
8879        gate.validate()?;
8880        up.validate()?;
8881        down.validate()?;
8882        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
8883            return Err("NVFP4 TP gate/up/down expert counts differ".into());
8884        }
8885        if gate.in_features != up.in_features || gate.out_features != up.out_features {
8886            return Err("NVFP4 TP gate/up dimensions differ".into());
8887        }
8888        if down.in_features != gate.out_features || down.out_features != gate.in_features {
8889            return Err(format!(
8890                "NVFP4 TP down {}x{} does not invert gate/up {}x{}",
8891                down.out_features, down.in_features, gate.out_features, gate.in_features
8892            )
8893            .into());
8894        }
8895        let tp = self.ranks.len();
8896        if gate.out_features % tp != 0 {
8897            return Err(format!(
8898                "NVFP4 TP expert output width {} is not divisible by TP={tp}",
8899                gate.out_features
8900            )
8901            .into());
8902        }
8903        if down.in_features % NVFP4_CANONICAL_ROW_SHARDS != 0
8904            || (down.in_features / NVFP4_CANONICAL_ROW_SHARDS) % 64 != 0
8905        {
8906            return Err(format!(
8907                "NVFP4 TP expert input width {} does not split into 64-aligned canonical \
8908                 shards ({NVFP4_CANONICAL_ROW_SHARDS})",
8909                down.in_features
8910            )
8911            .into());
8912        }
8913        if tp > NVFP4_CANONICAL_ROW_SHARDS {
8914            return Err(format!(
8915                "NVFP4 TP world {tp} exceeds the canonical row-shard grid \
8916                 ({NVFP4_CANONICAL_ROW_SHARDS})"
8917            )
8918            .into());
8919        }
8920
8921        let ep2 = step_nvfp4_ep2_on() && tp == 2;
8922        let mut gate_ranks = Vec::with_capacity(tp);
8923        let mut up_ranks = Vec::with_capacity(tp);
8924        let mut macros_gate_dev = Vec::with_capacity(tp);
8925        let mut macros_up_dev = Vec::with_capacity(tp);
8926        let mut macros_down_dev = Vec::with_capacity(tp);
8927        for (rank_index, engine) in self.ranks.iter().enumerate() {
8928            let _main = engine.gpu.enter_main()?;
8929            // Contiguous per-rank banks: repack every expert shard into one host buffer, one
8930            // upload. Contiguity feeds the batched selected-experts launch; per-expert bytes
8931            // are unchanged (same repack).
8932            // EP2: this rank holds the FULL matrices of the experts it owns (id & 1 ==
8933            // rank_index), stacked at slot id >> 1 — same total bytes as the shard bank.
8934            let mut gate_host: Vec<u8> = Vec::new();
8935            let mut up_host: Vec<u8> = Vec::new();
8936            let mut owned = 0usize;
8937            for expert in 0..gate.expert_count {
8938                if ep2 {
8939                    if expert % 2 != rank_index {
8940                        continue;
8941                    }
8942                    owned += 1;
8943                    gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate.expert(expert)?));
8944                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up.expert(expert)?));
8945                } else {
8946                    let gate_shard = nvfp4_column_shard(gate.expert(expert)?, tp, rank_index)?;
8947                    gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate_shard));
8948                    let up_shard = nvfp4_column_shard(up.expert(expert)?, tp, rank_index)?;
8949                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up_shard));
8950                }
8951            }
8952            let bank_experts = if ep2 { owned } else { gate.expert_count };
8953            let gate_expert_bytes = gate_host.len() / bank_experts.max(1);
8954            let up_expert_bytes = up_host.len() / bank_experts.max(1);
8955            let local_out = if ep2 {
8956                gate.out_features
8957            } else {
8958                gate.out_features / tp
8959            };
8960            gate_ranks.push(ResidentNvfp4ColumnBankRank {
8961                bank: engine.htod_bytes(&gate_host)?,
8962                expert_bytes: gate_expert_bytes,
8963                local_out,
8964                in_features: gate.in_features,
8965                row_bytes: nvfp4_row_bytes(gate.in_features),
8966            });
8967            up_ranks.push(ResidentNvfp4ColumnBankRank {
8968                bank: engine.htod_bytes(&up_host)?,
8969                expert_bytes: up_expert_bytes,
8970                local_out,
8971                in_features: up.in_features,
8972                row_bytes: nvfp4_row_bytes(up.in_features),
8973            });
8974            macros_gate_dev.push(engine.htod(gate.macros)?);
8975            macros_up_dev.push(engine.htod(up.macros)?);
8976            macros_down_dev.push(engine.htod(down.macros)?);
8977        }
8978        // Down: canonical shard grid, NOT the world size (see NVFP4_CANONICAL_ROW_SHARDS).
8979        // Shard s lives on rank s % world, so TP1 holds both shards and TP2 one each, while the
8980        // execution and reduction order stay identical.
8981        let mut down_ranks = Vec::with_capacity(NVFP4_CANONICAL_ROW_SHARDS);
8982        for shard_index in 0..NVFP4_CANONICAL_ROW_SHARDS {
8983            let device_rank = shard_index % tp;
8984            let engine = &self.ranks[device_rank];
8985            let _main = engine.gpu.enter_main()?;
8986            let mut down_host: Vec<u8> = Vec::new();
8987            let mut owned = 0usize;
8988            for expert in 0..down.expert_count {
8989                let down_matrix = down.expert(expert)?;
8990                if ep2 {
8991                    // EP2: shard_index doubles as the owner rank; full-width down matrices
8992                    // of the owned experts, stacked at slot id >> 1.
8993                    if expert % 2 != device_rank {
8994                        continue;
8995                    }
8996                    owned += 1;
8997                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(down_matrix));
8998                } else {
8999                    let (codes, scales, local_in) =
9000                        nvfp4_row_shard(down_matrix, NVFP4_CANONICAL_ROW_SHARDS, shard_index)?;
9001                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(Nvfp4BlockMatrix {
9002                        codes: &codes,
9003                        scales: &scales,
9004                        macro_scale: down_matrix.macro_scale,
9005                        out_features: down_matrix.out_features,
9006                        in_features: local_in,
9007                    }));
9008                }
9009            }
9010            let bank_experts = if ep2 { owned } else { down.expert_count };
9011            let down_expert_bytes = down_host.len() / bank_experts.max(1);
9012            let local_in = if ep2 {
9013                down.in_features
9014            } else {
9015                down.in_features / NVFP4_CANONICAL_ROW_SHARDS
9016            };
9017            down_ranks.push(ResidentNvfp4RowBankRank {
9018                bank: engine.htod_bytes(&down_host)?,
9019                expert_bytes: down_expert_bytes,
9020                device_rank,
9021                out_features: down.out_features,
9022                local_in,
9023                row_bytes: nvfp4_row_bytes(local_in),
9024            });
9025        }
9026        Ok(ResidentNvfp4TensorParallel {
9027            gate: gate_ranks,
9028            up: up_ranks,
9029            down: down_ranks,
9030            macros_gate: gate.macros.to_vec(),
9031            macros_up: up.macros.to_vec(),
9032            macros_down: down.macros.to_vec(),
9033            macros_gate_dev,
9034            macros_up_dev,
9035            macros_down_dev,
9036            expert_count: gate.expert_count,
9037            input_width: gate.in_features,
9038            expert_width: gate.out_features,
9039            device_workspace: std::sync::Mutex::new(None),
9040            t2_workspace: std::sync::Mutex::new(None),
9041            ep2,
9042        })
9043    }
9044
9045    /// EP2 host-canonical: the whole expert executes on its owning rank at full width
9046    /// (owner = expert & 1, bank slot = expert >> 1). Per-row program == the column-bank
9047    /// path's kernel, so gate/up are bit-equal to the TP layout.
9048    fn run_full_bank_expert_nvfp4(
9049        &self,
9050        ranks: &[ResidentNvfp4ColumnBankRank],
9051        macros: &[f32],
9052        expert: usize,
9053        input: &[f32],
9054    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9055        let owner = expert & 1;
9056        let slot = expert >> 1;
9057        let bank = ranks
9058            .get(owner)
9059            .ok_or("NVFP4 EP2 column bank missing owner rank")?;
9060        let engine = &self.ranks[owner];
9061        let _main = engine.gpu.enter_main()?;
9062        let activations = engine.htod(input)?;
9063        let output = if nvfp4_bank_v2_on() {
9064            engine.qmatvec_nvfp4_fast_v2(
9065                &bank.expert(slot),
9066                &activations,
9067                1,
9068                bank.in_features,
9069                bank.local_out,
9070                bank.row_bytes,
9071            )?
9072        } else {
9073            engine.qmatvec_nvfp4_fast(
9074                &bank.expert(slot),
9075                &activations,
9076                1,
9077                bank.in_features,
9078                bank.local_out,
9079                bank.row_bytes,
9080            )?
9081        };
9082        let mut out = engine.dtoh(&output)?;
9083        apply_macro(&mut out, macros[expert]);
9084        Ok(out)
9085    }
9086
9087    /// EP2 host-canonical down: one full-width dot on the owner (NUMERIC-CLASS vs the
9088    /// canonical 2-shard sum — the parenthesization this door declares).
9089    fn run_full_down_expert_nvfp4(
9090        &self,
9091        shards: &[ResidentNvfp4RowBankRank],
9092        macros: &[f32],
9093        expert: usize,
9094        input: &[f32],
9095    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9096        let owner = expert & 1;
9097        let slot = expert >> 1;
9098        let shard = shards
9099            .get(owner)
9100            .ok_or("NVFP4 EP2 down bank missing owner rank")?;
9101        let engine = &self.ranks[owner];
9102        let _main = engine.gpu.enter_main()?;
9103        let activations = engine.htod(input)?;
9104        let output = if nvfp4_bank_v2_on() {
9105            engine.qmatvec_nvfp4_fast_v2(
9106                &shard.expert(slot),
9107                &activations,
9108                1,
9109                shard.local_in,
9110                shard.out_features,
9111                shard.row_bytes,
9112            )?
9113        } else {
9114            engine.qmatvec_nvfp4_fast(
9115                &shard.expert(slot),
9116                &activations,
9117                1,
9118                shard.local_in,
9119                shard.out_features,
9120                shard.row_bytes,
9121            )?
9122        };
9123        let mut out = engine.dtoh(&output)?;
9124        apply_macro(&mut out, macros[expert]);
9125        Ok(out)
9126    }
9127
9128    fn run_column_bank_expert_nvfp4(
9129        &self,
9130        ranks: &[ResidentNvfp4ColumnBankRank],
9131        macros: &[f32],
9132        expert: usize,
9133        input: &[f32],
9134    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9135        let local_out = ranks
9136            .first()
9137            .ok_or("NVFP4 TP column bank has no ranks")?
9138            .local_out;
9139        let mut gathered = vec![0.0f32; local_out * ranks.len()];
9140        for (rank_index, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
9141            let _main = engine.gpu.enter_main()?;
9142            let activations = engine.htod(input)?;
9143            let output = if nvfp4_bank_v2_on() {
9144                engine.qmatvec_nvfp4_fast_v2(
9145                    &bank.expert(expert),
9146                    &activations,
9147                    1,
9148                    bank.in_features,
9149                    bank.local_out,
9150                    bank.row_bytes,
9151                )?
9152            } else {
9153                engine.qmatvec_nvfp4_fast(
9154                    &bank.expert(expert),
9155                    &activations,
9156                    1,
9157                    bank.in_features,
9158                    bank.local_out,
9159                    bank.row_bytes,
9160                )?
9161            };
9162            let output = engine.dtoh(&output)?;
9163            gathered[rank_index * local_out..(rank_index + 1) * local_out].copy_from_slice(&output);
9164        }
9165        apply_macro(&mut gathered, macros[expert]);
9166        Ok(gathered)
9167    }
9168
9169    /// Canonical-shard row reduction: iterate the FIXED shard grid in shard order (each shard
9170    /// executes on its owning rank engine), so the reduction parenthesization is identical at
9171    /// every world size — that identity is what the TP1-oracle-vs-TP2 bit gate proves.
9172    fn run_row_bank_expert_nvfp4(
9173        &self,
9174        shards: &[ResidentNvfp4RowBankRank],
9175        macros: &[f32],
9176        expert: usize,
9177        input: &[f32],
9178    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9179        let out_features = shards
9180            .first()
9181            .ok_or("NVFP4 TP row bank has no canonical shards")?
9182            .out_features;
9183        let in_features = shards.iter().map(|shard| shard.local_in).sum::<usize>();
9184        let mut reduced = vec![0.0f32; out_features];
9185        for (shard_index, shard) in shards.iter().enumerate() {
9186            let engine = self
9187                .ranks
9188                .get(shard.device_rank)
9189                .ok_or("NVFP4 canonical shard names a rank outside this runtime")?;
9190            let _main = engine.gpu.enter_main()?;
9191            let local_activations =
9192                activation_shard(input, 1, in_features, shards.len(), shard_index);
9193            let activations = engine.htod(&local_activations)?;
9194            let output = if nvfp4_bank_v2_on() {
9195                engine.qmatvec_nvfp4_fast_v2(
9196                    &shard.expert(expert),
9197                    &activations,
9198                    1,
9199                    shard.local_in,
9200                    shard.out_features,
9201                    shard.row_bytes,
9202                )?
9203            } else {
9204                engine.qmatvec_nvfp4_fast(
9205                    &shard.expert(expert),
9206                    &activations,
9207                    1,
9208                    shard.local_in,
9209                    shard.out_features,
9210                    shard.row_bytes,
9211                )?
9212            };
9213            let partial = engine.dtoh(&output)?;
9214            for (sum, value) in reduced.iter_mut().zip(&partial) {
9215                *sum += *value;
9216            }
9217        }
9218        apply_macro(&mut reduced, macros[expert]);
9219        Ok(reduced)
9220    }
9221
9222    /// Upload whole experts per owning rank (NVFP4 expert-parallel: the layout the clamped tail
9223    /// layers require — clamp semantics do not distribute across a tensor shard). Each owned
9224    /// expert keeps its full gate/up/down as one repacked block buffer on its owner.
9225    pub fn upload_expert_parallel_nvfp4(
9226        &self,
9227        gate: Nvfp4ExpertBank<'_>,
9228        up: Nvfp4ExpertBank<'_>,
9229        down: Nvfp4ExpertBank<'_>,
9230    ) -> Result<ResidentNvfp4ExpertParallel, Box<dyn std::error::Error>> {
9231        gate.validate()?;
9232        up.validate()?;
9233        down.validate()?;
9234        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
9235            return Err("NVFP4 EP gate/up/down expert counts differ".into());
9236        }
9237        if gate.in_features != up.in_features || gate.out_features != up.out_features {
9238            return Err("NVFP4 EP gate/up dimensions differ".into());
9239        }
9240        if down.in_features != gate.out_features || down.out_features != gate.in_features {
9241            return Err(format!(
9242                "NVFP4 EP down {}x{} does not invert gate/up {}x{}",
9243                down.out_features, down.in_features, gate.out_features, gate.in_features
9244            )
9245            .into());
9246        }
9247        let world = self.ranks.len();
9248        if gate.expert_count % world != 0 {
9249            return Err(format!(
9250                "NVFP4 EP expert count {} is not divisible by {world} ranks",
9251                gate.expert_count
9252            )
9253            .into());
9254        }
9255        let experts_per_rank = gate.expert_count / world;
9256        let mut ranks = Vec::with_capacity(world);
9257        for (rank_index, engine) in self.ranks.iter().enumerate() {
9258            let _main = engine.gpu.enter_main()?;
9259            let expert_range = rank_index * experts_per_rank..(rank_index + 1) * experts_per_rank;
9260            let mut gate_experts = Vec::with_capacity(experts_per_rank);
9261            let mut up_experts = Vec::with_capacity(experts_per_rank);
9262            let mut down_experts = Vec::with_capacity(experts_per_rank);
9263            for expert in expert_range.clone() {
9264                gate_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(gate.expert(expert)?))?);
9265                up_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(up.expert(expert)?))?);
9266                down_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(down.expert(expert)?))?);
9267            }
9268            ranks.push(ResidentNvfp4EpRank {
9269                gate: gate_experts,
9270                up: up_experts,
9271                down: down_experts,
9272                expert_range,
9273            });
9274        }
9275        Ok(ResidentNvfp4ExpertParallel {
9276            ranks,
9277            macros_gate: gate.macros.to_vec(),
9278            macros_up: up.macros.to_vec(),
9279            macros_down: down.macros.to_vec(),
9280            expert_count: gate.expert_count,
9281            input_width: gate.in_features,
9282            expert_width: gate.out_features,
9283            gate_row_bytes: nvfp4_row_bytes(gate.in_features),
9284            down_row_bytes: nvfp4_row_bytes(down.in_features),
9285        })
9286    }
9287
9288    /// Routed NVFP4 expert-parallel program, host-canonical: every selected expert executes WHOLE
9289    /// on its owning rank (gate -> up -> clamped-or-plain SwiGLU on host -> down), each projection
9290    /// macro applied once post-kernel, route-weighted accumulate on the host in slot order. The
9291    /// activation uses `step_expert_activation_host`, so the clamped tail layers keep the official
9292    /// contract. Exactness-first; no throughput claim.
9293    #[allow(clippy::too_many_arguments)]
9294    pub fn run_routed_experts_nvfp4(
9295        &self,
9296        experts: &ResidentNvfp4ExpertParallel,
9297        input: &[f32],
9298        tokens: usize,
9299        selected: &[usize],
9300        route_weights: &[f32],
9301        experts_per_token: usize,
9302        activation_limit: Option<f32>,
9303    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9304        validate_activations(input, tokens, experts.input_width)?;
9305        let pairs = tokens
9306            .checked_mul(experts_per_token)
9307            .ok_or("NVFP4 EP route count overflow")?;
9308        if selected.len() != pairs || route_weights.len() != pairs {
9309            return Err(format!(
9310                "NVFP4 EP routes selected={} weights={} != tokens {tokens} x experts/token \
9311                 {experts_per_token} ({pairs})",
9312                selected.len(),
9313                route_weights.len(),
9314            )
9315            .into());
9316        }
9317        if !route_weights.iter().all(|weight| weight.is_finite()) {
9318            return Err("NVFP4 EP route weights contain a non-finite value".into());
9319        }
9320        let experts_per_rank = experts.expert_count / experts.ranks.len();
9321        let mut output = vec![0.0f32; tokens * experts.input_width];
9322        for token in 0..tokens {
9323            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
9324            for slot in 0..experts_per_token {
9325                let pair = token * experts_per_token + slot;
9326                let expert = selected[pair];
9327                if expert >= experts.expert_count {
9328                    return Err(format!(
9329                        "NVFP4 EP selected expert {expert} outside 0..{}",
9330                        experts.expert_count
9331                    )
9332                    .into());
9333                }
9334                let owner = expert / experts_per_rank;
9335                let local = expert - owner * experts_per_rank;
9336                let rank = &experts.ranks[owner];
9337                let engine = &self.ranks[owner];
9338                let _main = engine.gpu.enter_main()?;
9339                let device_input = engine.htod(input_row)?;
9340                let gate_out = engine.qmatvec_nvfp4_fast(
9341                    &rank.gate[local].slice(0..rank.gate[local].len()),
9342                    &device_input,
9343                    1,
9344                    experts.input_width,
9345                    experts.expert_width,
9346                    experts.gate_row_bytes,
9347                )?;
9348                let up_out = engine.qmatvec_nvfp4_fast(
9349                    &rank.up[local].slice(0..rank.up[local].len()),
9350                    &device_input,
9351                    1,
9352                    experts.input_width,
9353                    experts.expert_width,
9354                    experts.gate_row_bytes,
9355                )?;
9356                let mut gate_host = engine.dtoh(&gate_out)?;
9357                let mut up_host = engine.dtoh(&up_out)?;
9358                apply_macro(&mut gate_host, experts.macros_gate[expert]);
9359                apply_macro(&mut up_host, experts.macros_up[expert]);
9360                let activated: Vec<f32> = gate_host
9361                    .iter()
9362                    .zip(&up_host)
9363                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
9364                    .collect();
9365                let device_activated = engine.htod(&activated)?;
9366                let down_out = engine.qmatvec_nvfp4_fast(
9367                    &rank.down[local].slice(0..rank.down[local].len()),
9368                    &device_activated,
9369                    1,
9370                    experts.expert_width,
9371                    experts.input_width,
9372                    experts.down_row_bytes,
9373                )?;
9374                let mut down_host = engine.dtoh(&down_out)?;
9375                apply_macro(&mut down_host, experts.macros_down[expert]);
9376                let weight = route_weights[pair];
9377                for (sum, value) in output
9378                    [token * experts.input_width..(token + 1) * experts.input_width]
9379                    .iter_mut()
9380                    .zip(down_host)
9381                {
9382                    *sum += weight * value;
9383                }
9384            }
9385        }
9386        Ok(output)
9387    }
9388
9389    /// Device-resident routed NVFP4 expert program (decode shape, t=1 rows). The geometry gift
9390    /// this exploits: gate/up column halves land on the SAME rank that owns the matching down
9391    /// canonical shard (act[rank r] is exactly down-shard r's input-column window), so the whole
9392    /// expert interior — gate, up, macro-scaled SwiGLU, down partial, route-weighted accumulate —
9393    /// runs rank-local with ZERO cross-rank transfer. Per (token, layer): one input upload per
9394    /// rank, one fenced peer copy of the remote accumulator, one root add, one readback.
9395    ///
9396    /// Numeric class: device silu (silu_mul_scaled) with gate/up macros folded as gs/us and the
9397    /// down macro folded into the accumulate scalar (weight * macro_down — exact, both are
9398    /// per-expert constants). This matches the owning-stage MoE dev-path semantics, NOT the
9399    /// host-canonical program bit-for-bit; gate it with argmax + relative bounds against the
9400    /// host-canonical oracle, and with repeat determinism against itself.
9401    /// Clamped layers refuse (they stay on the EP program).
9402    pub fn run_tensor_parallel_routes_nvfp4_device(
9403        &self,
9404        experts: &ResidentNvfp4TensorParallel,
9405        input: &[f32],
9406        selected: &[usize],
9407        route_weights: &[f32],
9408        experts_per_token: usize,
9409        activation_limit: Option<f32>,
9410    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9411        validate_activations(input, 1, experts.input_width)?;
9412        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
9413            return Err(format!(
9414                "NVFP4 device routes selected={} weights={} != experts/token {experts_per_token}",
9415                selected.len(),
9416                route_weights.len(),
9417            )
9418            .into());
9419        }
9420        if !route_weights.iter().all(|weight| weight.is_finite()) {
9421            return Err("NVFP4 device route weights contain a non-finite value".into());
9422        }
9423        let world = self.ranks.len();
9424        if world != NVFP4_CANONICAL_ROW_SHARDS {
9425            return Err(format!(
9426                "NVFP4 device routes require world == canonical shard grid \
9427                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
9428            )
9429            .into());
9430        }
9431        let local_out = if experts.ep2 {
9432            experts.expert_width
9433        } else {
9434            experts.expert_width / world
9435        };
9436
9437        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of this program, printed every 430 calls
9438        // (~one 43-layer decode step's worth) so a bench run decomposes expert-program time vs
9439        // everything else without Nsight.
9440        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9441        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9442        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
9443        let started = timing.then(std::time::Instant::now);
9444
9445        let n_sel = experts_per_token;
9446        let mut workspace_guard = experts
9447            .device_workspace
9448            .lock()
9449            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
9450        if workspace_guard.is_none() {
9451            let mut gate_out = Vec::with_capacity(world);
9452            let mut up_out = Vec::with_capacity(world);
9453            let mut act_q = Vec::with_capacity(world);
9454            let mut act_d = Vec::with_capacity(world);
9455            let mut sel = Vec::with_capacity(world);
9456            let mut partial = Vec::with_capacity(world);
9457            let mut accumulator = Vec::with_capacity(world);
9458            let mut combine_w = Vec::with_capacity(world);
9459            let mut route_w = Vec::with_capacity(world);
9460            let mut in_q = Vec::with_capacity(world);
9461            let mut in_d = Vec::with_capacity(world);
9462            let mut input = Vec::with_capacity(world);
9463            let mut ev_rank = Vec::with_capacity(world);
9464            let moe_direct = moe_direct_on();
9465            for (rank, engine) in self.ranks.iter().enumerate() {
9466                let _main = engine.gpu.enter_main()?;
9467                gate_out.push(engine.uninit(n_sel * local_out)?);
9468                up_out.push(engine.uninit(n_sel * local_out)?);
9469                act_q.push(engine.uninit_i8(n_sel * local_out)?);
9470                act_d.push(engine.uninit(n_sel * local_out / 32)?);
9471                sel.push(engine.htod_i32(&vec![0i32; n_sel])?);
9472                partial.push(engine.uninit(n_sel * experts.input_width)?);
9473                // Direct join: peer accumulators live on ROOT (single P2P store pass).
9474                if moe_direct && rank != 0 {
9475                    let root = &self.ranks[0];
9476                    let _root_main = root.gpu.enter_main()?;
9477                    accumulator.push(root.zeros(experts.input_width)?);
9478                } else {
9479                    accumulator.push(engine.zeros(experts.input_width)?);
9480                }
9481                combine_w.push(engine.htod(&vec![0.0f32; n_sel])?);
9482                route_w.push(engine.htod(&vec![0.0f32; n_sel])?);
9483                in_q.push(engine.uninit_i8(experts.input_width)?);
9484                in_d.push(engine.uninit(experts.input_width / 32)?);
9485                input.push(engine.uninit(experts.input_width)?);
9486                ev_rank.push(engine.ctx().new_event(None)?);
9487            }
9488            let root = &self.ranks[0];
9489            let _main = root.gpu.enter_main()?;
9490            *workspace_guard = Some(Nvfp4DeviceRoutesWorkspace {
9491                prestaged: false,
9492                rank1_routed: false,
9493                ev_input: None,
9494                fence_flags_raw: 0,
9495                fence_ticket: 0,
9496                gate_out,
9497                up_out,
9498                act_q,
9499                act_d,
9500                sel,
9501                partial,
9502                accumulator,
9503                combine_w,
9504                route_w,
9505                in_q,
9506                in_d,
9507                dev_route_e: None,
9508                in_stage_e: None,
9509                out_stage_e: None,
9510                routes_graph: None,
9511                raw_dev_route_e: None,
9512                raw_combine: None,
9513                raw_input: Vec::new(),
9514                raw_sel: Vec::new(),
9515                raw_route_w: Vec::new(),
9516                remote: root.uninit(experts.input_width)?,
9517                combined: root.uninit(experts.input_width)?,
9518                n_sel,
9519                input,
9520                ev_rank,
9521                ev_done: Some(root.ctx().new_event(None)?),
9522                ev_entry: None,
9523            });
9524        }
9525        let workspace = workspace_guard
9526            .as_mut()
9527            .expect("NVFP4 device routes workspace initialized above");
9528        // EP2 uses this call only as the workspace-arming warmup (the prejoin path drives
9529        // decode); its host-routed sweep semantics do not apply to whole-expert banks.
9530        if experts.ep2 {
9531            return Ok(vec![0.0f32; experts.input_width]);
9532        }
9533        if workspace.n_sel != n_sel {
9534            return Err(format!(
9535                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
9536                workspace.n_sel
9537            )
9538            .into());
9539        }
9540        for &expert in selected {
9541            if expert >= experts.expert_count {
9542                return Err(format!(
9543                    "NVFP4 device selected expert {expert} outside 0..{}",
9544                    experts.expert_count
9545                )
9546                .into());
9547            }
9548        }
9549        let sel_i32 = selected
9550            .iter()
9551            .map(|&expert| expert as i32)
9552            .collect::<Vec<_>>();
9553
9554        // BATCHED program (2026-08-20): per rank, ONE launch per sweep (gate, up, SwiGLU,
9555        // down) covers every selected expert via the selection array and the contiguous bank —
9556        // the per-expert launch loop was pure host latency (~100 sequential launches/layer,
9557        // 291us wall for ~35us of arithmetic). Per (expert, row) the kernels are bit-identical
9558        // to the per-expert forms, and the route-weight axpy chain keeps its exact sequential
9559        // accumulation order — the program's values are unchanged.
9560        for (rank_index, engine) in self.ranks.iter().enumerate() {
9561            let _main = engine.gpu.enter_main()?;
9562            let device_input = engine.htod(input)?;
9563            let Nvfp4DeviceRoutesWorkspace { in_q, in_d, .. } = &mut *workspace;
9564            engine.quantize_q8_1_into(
9565                &device_input,
9566                1,
9567                experts.input_width,
9568                &mut in_q[rank_index],
9569                &mut in_d[rank_index],
9570            )?;
9571            // device_input frees on this rank's stream after the quantize — same-stream order.
9572        }
9573        self.nvfp4_routes_batched_sweeps(
9574            experts,
9575            workspace,
9576            selected,
9577            route_weights,
9578            &sel_i32,
9579            local_out,
9580            n_sel,
9581            activation_limit,
9582            false,
9583        )?;
9584
9585        // Combine: fence the remote shard's producer stream, peer-copy its accumulator to root,
9586        // reduce in canonical shard order, read back once.
9587        let root = &self.ranks[0];
9588        for engine in &self.ranks[1..] {
9589            let _main = engine.gpu.enter_main()?;
9590            engine.stream().synchronize()?;
9591        }
9592        let _main = root.gpu.enter_main()?;
9593        root.stream()
9594            .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
9595        root.add(
9596            &workspace.accumulator[0],
9597            &workspace.remote,
9598            &mut workspace.combined,
9599            experts.input_width,
9600        )?;
9601        let output = root.dtoh(&workspace.combined)?;
9602        if let Some(started) = started {
9603            use std::sync::atomic::Ordering;
9604            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9605                + started.elapsed().as_nanos() as u64;
9606            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9607            if calls % 430 == 0 {
9608                eprintln!(
9609                    "[nvfp4-dev-routes-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9610                    ns as f64 / 1.0e6,
9611                    ns as f64 / calls as f64 / 1.0e3,
9612                );
9613            }
9614        }
9615        Ok(output)
9616    }
9617
9618    /// The shared batched sweeps of the device routes program: per rank, upload the selection,
9619    /// reset the accumulator, run the gate/up/SwiGLU/down batched launches, then the
9620    /// route-weight axpy chain in exact sequential per-pair order. Every op queues on the
9621    /// owning rank's stream; callers own input acquisition and the combine.
9622    #[allow(clippy::too_many_arguments)]
9623    fn nvfp4_routes_batched_sweeps(
9624        &self,
9625        experts: &ResidentNvfp4TensorParallel,
9626        workspace: &mut Nvfp4DeviceRoutesWorkspace,
9627        selected: &[usize],
9628        route_weights: &[f32],
9629        sel_i32: &[i32],
9630        local_out: usize,
9631        n_sel: usize,
9632        activation_limit: Option<f32>,
9633        device_routed: bool,
9634    ) -> Result<(), Box<dyn std::error::Error>> {
9635        for rank_index in 0..self.ranks.len() {
9636            self.nvfp4_routes_batched_sweeps_rank(
9637                experts,
9638                workspace,
9639                selected,
9640                route_weights,
9641                sel_i32,
9642                local_out,
9643                n_sel,
9644                activation_limit,
9645                device_routed,
9646                rank_index,
9647            )?;
9648        }
9649        Ok(())
9650    }
9651
9652    /// One rank's sweeps (the per-rank body of `nvfp4_routes_batched_sweeps`) — separated so
9653    /// the graph door can capture each rank's segment on its own stream.
9654    #[allow(clippy::too_many_arguments)]
9655    fn nvfp4_routes_batched_sweeps_rank(
9656        &self,
9657        experts: &ResidentNvfp4TensorParallel,
9658        workspace: &mut Nvfp4DeviceRoutesWorkspace,
9659        selected: &[usize],
9660        route_weights: &[f32],
9661        sel_i32: &[i32],
9662        local_out: usize,
9663        n_sel: usize,
9664        activation_limit: Option<f32>,
9665        device_routed: bool,
9666        rank_index: usize,
9667    ) -> Result<(), Box<dyn std::error::Error>> {
9668        {
9669            let engine = &self.ranks[rank_index];
9670            let _main = engine.gpu.enter_main()?;
9671            // EP2: whole-expert full-width sweep, owner-guarded; down+combine fused writes
9672            // this rank's slot-ordered partial straight into its accumulator (the join is
9673            // unchanged). Device-routed only — the host-routed arm and the graph door refuse
9674            // at the caller.
9675            if experts.ep2 {
9676                if !device_routed {
9677                    return Err("NVFP4 EP2 banks support the device-routed decode arm only".into());
9678                }
9679                let gate_bank = &experts.gate[rank_index];
9680                let up_bank = &experts.up[rank_index];
9681                if gate_bank.local_out != experts.expert_width
9682                    || gate_bank.expert_bytes != up_bank.expert_bytes
9683                {
9684                    return Err("NVFP4 EP2 bank geometry drifted".into());
9685                }
9686                {
9687                    let Nvfp4DeviceRoutesWorkspace {
9688                        sel,
9689                        gate_out,
9690                        up_out,
9691                        in_q,
9692                        in_d,
9693                        ..
9694                    } = &mut *workspace;
9695                    engine.qmatvec_nvfp4_sel_gu_ep_into(
9696                        &gate_bank.bank,
9697                        &up_bank.bank,
9698                        &sel[rank_index],
9699                        &in_q[rank_index],
9700                        &in_d[rank_index],
9701                        &mut gate_out[rank_index],
9702                        &mut up_out[rank_index],
9703                        n_sel,
9704                        gate_bank.in_features,
9705                        gate_bank.local_out,
9706                        gate_bank.row_bytes,
9707                        gate_bank.expert_bytes,
9708                        rank_index,
9709                    )?;
9710                }
9711                {
9712                    let Nvfp4DeviceRoutesWorkspace {
9713                        gate_out,
9714                        up_out,
9715                        sel,
9716                        act_q,
9717                        act_d,
9718                        ..
9719                    } = &mut *workspace;
9720                    engine.silu_mul_scaled_q8_1_sel_ep_into(
9721                        &gate_out[rank_index],
9722                        &up_out[rank_index],
9723                        &experts.macros_gate_dev[rank_index],
9724                        &experts.macros_up_dev[rank_index],
9725                        &sel[rank_index],
9726                        activation_limit,
9727                        &mut act_q[rank_index],
9728                        &mut act_d[rank_index],
9729                        local_out,
9730                        n_sel,
9731                        rank_index,
9732                    )?;
9733                }
9734                let shard = &experts.down[rank_index];
9735                if shard.device_rank != rank_index || shard.local_in != local_out {
9736                    return Err("NVFP4 EP2 down bank placement drifted".into());
9737                }
9738                {
9739                    let Nvfp4DeviceRoutesWorkspace {
9740                        sel,
9741                        act_q,
9742                        act_d,
9743                        route_w,
9744                        accumulator,
9745                        ..
9746                    } = &mut *workspace;
9747                    engine.qmatvec_nvfp4_sel_down8_ep_into(
9748                        &shard.bank,
9749                        &sel[rank_index],
9750                        &act_q[rank_index],
9751                        &act_d[rank_index],
9752                        &route_w[rank_index],
9753                        &experts.macros_down_dev[rank_index],
9754                        &mut accumulator[rank_index],
9755                        n_sel,
9756                        shard.local_in,
9757                        shard.out_features,
9758                        shard.row_bytes,
9759                        shard.expert_bytes,
9760                        local_out,
9761                        local_out / 32,
9762                        rank_index,
9763                    )?;
9764                }
9765                return Ok(());
9766            }
9767            if !device_routed {
9768                engine.htod_i32_into(&mut workspace.sel[rank_index], sel_i32)?;
9769                // Folded combine weights (route_weight x down macro) — one 40-byte upload
9770                // replaces the accumulator reset + n_sel sequential axpy launches below.
9771                let folded = (0..n_sel)
9772                    .map(|pair| route_weights[pair] * experts.macros_down[selected[pair]])
9773                    .collect::<Vec<_>>();
9774                let mut view = workspace.combine_w[rank_index].slice_mut(0..n_sel);
9775                engine.stream().memcpy_htod(&folded, &mut view)?;
9776            }
9777            let gate_bank = &experts.gate[rank_index];
9778            let up_bank = &experts.up[rank_index];
9779            let (aq, ad) = (&workspace.in_q[rank_index], &workspace.in_d[rank_index]);
9780            // FUSION #2a (v2 banks): the two sweeps share sel/aq/ad and identical geometry
9781            // — one launch, per-row bit-identical, double the grid fill.
9782            let gu_fused = nvfp4_bank_v2_on()
9783                && gate_bank.in_features == up_bank.in_features
9784                && gate_bank.local_out == up_bank.local_out
9785                && gate_bank.row_bytes == up_bank.row_bytes
9786                && gate_bank.expert_bytes == up_bank.expert_bytes;
9787            if gu_fused {
9788                let Nvfp4DeviceRoutesWorkspace {
9789                    sel,
9790                    gate_out,
9791                    up_out,
9792                    in_q,
9793                    in_d,
9794                    ..
9795                } = &mut *workspace;
9796                engine.qmatvec_nvfp4_sel_gu_into(
9797                    &gate_bank.bank,
9798                    &up_bank.bank,
9799                    &sel[rank_index],
9800                    &in_q[rank_index],
9801                    &in_d[rank_index],
9802                    &mut gate_out[rank_index],
9803                    &mut up_out[rank_index],
9804                    n_sel,
9805                    gate_bank.in_features,
9806                    gate_bank.local_out,
9807                    gate_bank.row_bytes,
9808                    gate_bank.expert_bytes,
9809                )?;
9810            } else {
9811                engine.qmatvec_nvfp4_sel_into(
9812                    &gate_bank.bank,
9813                    &workspace.sel[rank_index],
9814                    aq,
9815                    ad,
9816                    &mut workspace.gate_out[rank_index],
9817                    n_sel,
9818                    gate_bank.in_features,
9819                    gate_bank.local_out,
9820                    gate_bank.row_bytes,
9821                    gate_bank.expert_bytes,
9822                    0,
9823                    0,
9824                )?;
9825                engine.qmatvec_nvfp4_sel_into(
9826                    &up_bank.bank,
9827                    &workspace.sel[rank_index],
9828                    aq,
9829                    ad,
9830                    &mut workspace.up_out[rank_index],
9831                    n_sel,
9832                    up_bank.in_features,
9833                    up_bank.local_out,
9834                    up_bank.row_bytes,
9835                    up_bank.expert_bytes,
9836                    0,
9837                    0,
9838                )?;
9839            }
9840            // Fused macro-scaled SwiGLU that EMITS q8_1 directly — down consumes it with no
9841            // separate quantize launch. act[rank] IS down canonical shard `rank_index`'s
9842            // input-column window (the geometry gift; see the method doc).
9843            {
9844                let Nvfp4DeviceRoutesWorkspace {
9845                    gate_out,
9846                    up_out,
9847                    sel,
9848                    act_q,
9849                    act_d,
9850                    ..
9851                } = &mut *workspace;
9852                engine.silu_mul_scaled_q8_1_sel_into(
9853                    &gate_out[rank_index],
9854                    &up_out[rank_index],
9855                    &experts.macros_gate_dev[rank_index],
9856                    &experts.macros_up_dev[rank_index],
9857                    &sel[rank_index],
9858                    activation_limit,
9859                    &mut act_q[rank_index],
9860                    &mut act_d[rank_index],
9861                    local_out,
9862                    n_sel,
9863                )?;
9864            }
9865            let shard = &experts.down[rank_index];
9866            if shard.device_rank != rank_index || shard.local_in != local_out {
9867                return Err(
9868                    "NVFP4 device routes: down canonical shard placement drifted from \
9869                     the gate/up column split"
9870                        .into(),
9871                );
9872            }
9873            // MEMRA_SEL_DOWN8=1: down sweep + route-weight combine in ONE launch, one warp
9874            // per SLOT instead of one warp per (row, slot) — the q8 `down8 w8` occupancy arm
9875            // (cx-downkernel: waves/SM 0.91 -> 4.36) ported to the NVFP4 banks. Bit-identical
9876            // (same dot program, same reduce tree, same slot-ordered chain), and the
9877            // n_sel x out_f partial buffer round trip disappears. Device-routed only: the
9878            // host-routed arm folds the macro into combine_w instead of reading md on device.
9879            let down8 = device_routed && sel_down8_on() && (shard.local_in >> 5) <= 32;
9880            if down8 {
9881                let Nvfp4DeviceRoutesWorkspace {
9882                    sel,
9883                    act_q,
9884                    act_d,
9885                    route_w,
9886                    accumulator,
9887                    ..
9888                } = &mut *workspace;
9889                engine.qmatvec_nvfp4_sel_down8_into(
9890                    &shard.bank,
9891                    &sel[rank_index],
9892                    &act_q[rank_index],
9893                    &act_d[rank_index],
9894                    &route_w[rank_index],
9895                    &experts.macros_down_dev[rank_index],
9896                    &mut accumulator[rank_index],
9897                    n_sel,
9898                    shard.local_in,
9899                    shard.out_features,
9900                    shard.row_bytes,
9901                    shard.expert_bytes,
9902                    local_out,
9903                    local_out / 32,
9904                )?;
9905            } else {
9906                let Nvfp4DeviceRoutesWorkspace {
9907                    sel,
9908                    act_q,
9909                    act_d,
9910                    partial,
9911                    ..
9912                } = &mut *workspace;
9913                engine.qmatvec_nvfp4_sel_into(
9914                    &shard.bank,
9915                    &sel[rank_index],
9916                    &act_q[rank_index],
9917                    &act_d[rank_index],
9918                    &mut partial[rank_index],
9919                    n_sel,
9920                    shard.local_in,
9921                    shard.out_features,
9922                    shard.row_bytes,
9923                    shard.expert_bytes,
9924                    local_out,
9925                    local_out / 32,
9926                )?;
9927            }
9928            // Route-weight accumulation: axpy_rows_seq keeps the exact sequential per-pair
9929            // FP chain of the reset + n_sel axpy launches in ONE launch. Device-routed calls
9930            // fold the down macro in-kernel from the device selection. (down8 already
9931            // produced the accumulator inside the sweep.)
9932            if !down8 {
9933                let Nvfp4DeviceRoutesWorkspace {
9934                    partial,
9935                    combine_w,
9936                    route_w,
9937                    sel,
9938                    accumulator,
9939                    ..
9940                } = &mut *workspace;
9941                if device_routed {
9942                    engine.axpy_rows_seq_md_into(
9943                        &partial[rank_index],
9944                        &route_w[rank_index],
9945                        &experts.macros_down_dev[rank_index],
9946                        &sel[rank_index],
9947                        &mut accumulator[rank_index],
9948                        experts.input_width,
9949                        n_sel,
9950                    )?;
9951                } else {
9952                    engine.axpy_rows_seq_into(
9953                        &partial[rank_index],
9954                        &combine_w[rank_index],
9955                        &mut accumulator[rank_index],
9956                        experts.input_width,
9957                        n_sel,
9958                    )?;
9959                }
9960            }
9961        }
9962        Ok(())
9963    }
9964
9965    /// Device-IO twin of `run_tensor_parallel_routes_nvfp4_device`: the layer input arrives as
9966    /// a device row on the model engine `e` and the combined output returns as a fresh
9967    /// `e`-context row — no host round-trip, no host stream sync. Ordering is evented (the v2
9968    /// attention discipline): `ev_entry` is recorded on `e`'s stream AFTER the caller queued
9969    /// the input's producer; each rank waits it before its peer read; the root reduce waits
9970    /// every rank's done event; `e` waits the root's done event before copying out. The
9971    /// program bytes are identical to the host-IO twin — dtoh/htod and dtod preserve f32 bits.
9972    pub fn run_tensor_parallel_routes_nvfp4_device_io(
9973        &self,
9974        experts: &ResidentNvfp4TensorParallel,
9975        e: &Engine,
9976        input_dev: &crate::CudaSlice<f32>,
9977        selected: &[usize],
9978        route_weights: &[f32],
9979        experts_per_token: usize,
9980        activation_limit: Option<f32>,
9981    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
9982        if input_dev.len() != experts.input_width {
9983            return Err(format!(
9984                "NVFP4 device-io routes input {} != width {}",
9985                input_dev.len(),
9986                experts.input_width
9987            )
9988            .into());
9989        }
9990        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
9991            return Err(format!(
9992                "NVFP4 device-io routes selected={} weights={} != experts/token {experts_per_token}",
9993                selected.len(),
9994                route_weights.len(),
9995            )
9996            .into());
9997        }
9998        if !route_weights.iter().all(|weight| weight.is_finite()) {
9999            return Err("NVFP4 device route weights contain a non-finite value".into());
10000        }
10001        let world = self.ranks.len();
10002        if world != NVFP4_CANONICAL_ROW_SHARDS {
10003            return Err(format!(
10004                "NVFP4 device routes require world == canonical shard grid \
10005                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
10006            )
10007            .into());
10008        }
10009        let local_out = experts.expert_width / world;
10010        let n_sel = experts_per_token;
10011
10012        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10013        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10014        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10015        let started = timing.then(std::time::Instant::now);
10016
10017        let mut workspace_guard = experts
10018            .device_workspace
10019            .lock()
10020            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10021        if workspace_guard.is_none() {
10022            drop(workspace_guard);
10023            // Build through the host-IO ensure path exactly once: run it with a zero input.
10024            // Cheaper than duplicating the init; the first real call overwrites everything.
10025            let zero = vec![0.0f32; experts.input_width];
10026            let zero_sel = vec![0usize; n_sel];
10027            let zero_w = vec![0.0f32; n_sel];
10028            let _ = self.run_tensor_parallel_routes_nvfp4_device(
10029                experts,
10030                &zero,
10031                &zero_sel,
10032                &zero_w,
10033                n_sel,
10034                activation_limit,
10035            )?;
10036            workspace_guard = experts
10037                .device_workspace
10038                .lock()
10039                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10040        }
10041        let workspace = workspace_guard
10042            .as_mut()
10043            .expect("NVFP4 device routes workspace initialized above");
10044        if workspace.n_sel != n_sel {
10045            return Err(format!(
10046                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10047                workspace.n_sel
10048            )
10049            .into());
10050        }
10051        for &expert in selected {
10052            if expert >= experts.expert_count {
10053                return Err(format!(
10054                    "NVFP4 device selected expert {expert} outside 0..{}",
10055                    experts.expert_count
10056                )
10057                .into());
10058            }
10059        }
10060        let sel_i32 = selected
10061            .iter()
10062            .map(|&expert| expert as i32)
10063            .collect::<Vec<_>>();
10064
10065        // Entry fence: e's stream position covers the input's producer AND every consumer of
10066        // the previous layer's output (queued on e's stream before this call), guarding the
10067        // workspace reuse exactly like the v2 attention driver.
10068        if let Some((_, device)) = workspace.ev_entry.as_ref() {
10069            if *device != e.ctx().ordinal() {
10070                return Err("NVFP4 device-io routes engine changed".into());
10071            }
10072        } else {
10073            let _main = e.gpu.enter_main()?;
10074            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
10075        }
10076        {
10077            let _main = e.gpu.enter_main()?;
10078            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10079            ev_entry.record(&e.stream())?;
10080        }
10081        for (rank_index, engine) in self.ranks.iter().enumerate() {
10082            let _main = engine.gpu.enter_main()?;
10083            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10084            engine.stream().wait(ev_entry)?;
10085            {
10086                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
10087                engine
10088                    .stream()
10089                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
10090            }
10091            {
10092                let Nvfp4DeviceRoutesWorkspace {
10093                    input, in_q, in_d, ..
10094                } = &mut *workspace;
10095                engine.quantize_q8_1_into(
10096                    &input[rank_index],
10097                    1,
10098                    experts.input_width,
10099                    &mut in_q[rank_index],
10100                    &mut in_d[rank_index],
10101                )?;
10102            }
10103        }
10104        self.nvfp4_routes_batched_sweeps(
10105            experts,
10106            workspace,
10107            selected,
10108            route_weights,
10109            &sel_i32,
10110            local_out,
10111            n_sel,
10112            activation_limit,
10113            false,
10114        )?;
10115
10116        // Evented combine: rank done events replace the host stream syncs, the reduce runs on
10117        // the root stream in canonical shard order, and e copies the combined row out behind
10118        // the root's done event.
10119        // rank0 == root: its own stream order already covers its sweep; only the PEER
10120        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
10121        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
10122            let _main = engine.gpu.enter_main()?;
10123            workspace.ev_rank[rank_index].record(&engine.stream())?;
10124        }
10125        if moe_direct_on() && self.ranks.len() == 2 {
10126            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
10127            // rank0's is root-stream-ordered. One root event + rank1's own event order
10128            // the model engine's single add — same operand order as root's add
10129            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
10130            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
10131            // hazard class does not apply).
10132            {
10133                let root = &self.ranks[0];
10134                let _main = root.gpu.enter_main()?;
10135                workspace
10136                    .ev_done
10137                    .as_ref()
10138                    .expect("device routes done event")
10139                    .record(&root.stream())?;
10140            }
10141            let _main = e.gpu.enter_main()?;
10142            e.stream().wait(
10143                workspace
10144                    .ev_done
10145                    .as_ref()
10146                    .expect("device routes done event"),
10147            )?;
10148            for ev in workspace.ev_rank.iter().skip(1) {
10149                e.stream().wait(ev)?;
10150            }
10151            let mut output = e.uninit(experts.input_width)?;
10152            e.add(
10153                &workspace.accumulator[0],
10154                &workspace.accumulator[1],
10155                &mut output,
10156                experts.input_width,
10157            )?;
10158            let output = output;
10159            if let Some(started) = started {
10160                use std::sync::atomic::Ordering;
10161                let ns = TIMING_NS
10162                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10163                    + started.elapsed().as_nanos() as u64;
10164                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10165                if calls % 430 == 0 {
10166                    eprintln!(
10167                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10168                        ns as f64 / 1.0e6,
10169                        ns as f64 / calls as f64 / 1.0e3,
10170                    );
10171                }
10172            }
10173            return Ok(output);
10174        }
10175        {
10176            let root = &self.ranks[0];
10177            let _main = root.gpu.enter_main()?;
10178            for ev in workspace.ev_rank.iter().skip(1) {
10179                root.stream().wait(ev)?;
10180            }
10181            root.stream()
10182                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
10183            {
10184                let Nvfp4DeviceRoutesWorkspace {
10185                    accumulator,
10186                    remote,
10187                    combined,
10188                    ..
10189                } = &mut *workspace;
10190                root.add(&accumulator[0], remote, combined, experts.input_width)?;
10191            }
10192            workspace
10193                .ev_done
10194                .as_ref()
10195                .expect("device routes done event")
10196                .record(&root.stream())?;
10197        }
10198        let output = {
10199            let _main = e.gpu.enter_main()?;
10200            e.stream().wait(
10201                workspace
10202                    .ev_done
10203                    .as_ref()
10204                    .expect("device routes done event"),
10205            )?;
10206            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
10207            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
10208            let mut output = e.uninit(experts.input_width)?;
10209            e.stream().memcpy_dtod(
10210                &workspace.combined.slice(0..experts.input_width),
10211                &mut output.slice_mut(0..experts.input_width),
10212            )?;
10213            output
10214        };
10215        if let Some(started) = started {
10216            use std::sync::atomic::Ordering;
10217            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10218                + started.elapsed().as_nanos() as u64;
10219            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10220            if calls % 430 == 0 {
10221                eprintln!(
10222                    "[nvfp4-dev-routes-io-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10223                    ns as f64 / 1.0e6,
10224                    ns as f64 / calls as f64 / 1.0e3,
10225                );
10226            }
10227        }
10228        Ok(output)
10229    }
10230
10231    /// Device-routed twin of `run_tensor_parallel_routes_nvfp4_device_io`: the selection and
10232    /// route weights arrive as the device router's e-context outputs — the per-layer host
10233    /// logits readback disappears. The fresh router outputs are staged into persistent
10234    /// e-context buffers on e's stream (never-free discipline) before the entry event; each
10235    /// rank peer-reads them behind it. The down-macro fold happens in-kernel.
10236    #[allow(clippy::too_many_arguments)]
10237    /// Prestage the routed-expert input: pull the shared row to every rank and quantize it
10238    /// there, WITHOUT the selection — callable before the router so the rank chains overlap
10239    /// it. No-op (returns false) when the workspace is not built yet or the door is off;
10240    /// the routed run then does its own staging as before.
10241    pub fn nvfp4_routes_prestage(
10242        &self,
10243        experts: &ResidentNvfp4TensorParallel,
10244        e: &Engine,
10245        input_dev: &crate::CudaSlice<f32>,
10246    ) -> Result<bool, Box<dyn std::error::Error>> {
10247        self.nvfp4_routes_prestage_with(experts, e, input_dev, |_, _, _, _| Ok(false))
10248    }
10249
10250    /// `nvfp4_routes_prestage` with a PEER-ROUTER hook: after rank1's input pull +
10251    /// quantize, the hook may compute rank1's route selection LOCALLY (replicated router —
10252    /// deterministic kernels on identical input bits produce identical sel/w, so the
10253    /// selection is bit-equal to the root's). Returns true when it wrote sel/route_w; the
10254    /// routed run then skips rank1's sel pull.
10255    pub fn nvfp4_routes_prestage_with(
10256        &self,
10257        experts: &ResidentNvfp4TensorParallel,
10258        e: &Engine,
10259        input_dev: &crate::CudaSlice<f32>,
10260        rank1_router: impl FnOnce(
10261            &Engine,
10262            &crate::CudaSlice<f32>,
10263            &mut crate::CudaSlice<i32>,
10264            &mut crate::CudaSlice<f32>,
10265        ) -> Result<bool, Box<dyn std::error::Error>>,
10266    ) -> Result<bool, Box<dyn std::error::Error>> {
10267        if !routes_prestage_on() || step_tp_graph_enabled()? {
10268            return Ok(false);
10269        }
10270        if input_dev.len() != experts.input_width {
10271            return Err("NVFP4 prestage input width mismatch".into());
10272        }
10273        let mut workspace_guard = experts
10274            .device_workspace
10275            .lock()
10276            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10277        let Some(workspace) = workspace_guard.as_mut() else {
10278            return Ok(false);
10279        };
10280        if workspace.ev_input.is_none() {
10281            let _main = e.gpu.enter_main()?;
10282            workspace.ev_input = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
10283        } else if workspace.ev_input.as_ref().map(|(_, d)| *d) != Some(e.ctx().ordinal()) {
10284            return Err("NVFP4 prestage engine changed".into());
10285        }
10286        {
10287            let _main = e.gpu.enter_main()?;
10288            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
10289            ev.record(&e.stream())?;
10290        }
10291        for (rank_index, engine) in self.ranks.iter().enumerate() {
10292            let _main = engine.gpu.enter_main()?;
10293            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
10294            engine.stream().wait(ev)?;
10295            {
10296                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
10297                engine
10298                    .stream()
10299                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
10300            }
10301            {
10302                let Nvfp4DeviceRoutesWorkspace {
10303                    input, in_q, in_d, ..
10304                } = &mut *workspace;
10305                engine.quantize_q8_1_into(
10306                    &input[rank_index],
10307                    1,
10308                    experts.input_width,
10309                    &mut in_q[rank_index],
10310                    &mut in_d[rank_index],
10311                )?;
10312            }
10313        }
10314        if self.ranks.len() == 2 {
10315            let rank1 = &self.ranks[1];
10316            let _r1 = rank1.gpu.enter_main()?;
10317            let Nvfp4DeviceRoutesWorkspace {
10318                input,
10319                sel,
10320                route_w,
10321                ..
10322            } = &mut *workspace;
10323            let (in1, rest_sel) = (&input[1], &mut sel[1]);
10324            if rank1_router(rank1, in1, rest_sel, &mut route_w[1])? {
10325                workspace.rank1_routed = true;
10326            }
10327        }
10328        workspace.prestaged = true;
10329        Ok(true)
10330    }
10331
10332    /// TWO-COLUMN device-routed expert program (spec verify, MEMRA_TCOL_FFN): one gu_tcol
10333    /// sweep over 2*n_sel_col pairs (pair t reads activation row t/n_sel_col — weights the
10334    /// two columns share dedup through L2), the UNCHANGED silu/down kernels at n_sel=16
10335    /// (both already index per pair), and one offset-axpy combine per column (the exact
10336    /// t=1 sequential chain over that column's 8 pairs). No serving doors: no graph, no
10337    /// prestage, no shexp folding — plain evented ordering. Returns [2, input_width] on e.
10338    ///
10339    /// EXACTNESS: every kernel body is the t=1 program per (pair,row) or per element; the
10340    /// per-column combine order equals the t=1 combine; the cross-rank join adds the same
10341    /// operand values elementwise. Gated by the greedy tape like every verify arm.
10342    #[allow(clippy::too_many_arguments)]
10343    pub fn run_tensor_parallel_routes_nvfp4_device_routed_t2(
10344        &self,
10345        experts: &ResidentNvfp4TensorParallel,
10346        e: &Engine,
10347        z2: &crate::CudaSlice<f32>,
10348        sel_d: &crate::CudaSlice<i32>,
10349        w_d: &crate::CudaSlice<f32>,
10350        n_sel_col: usize,
10351        activation_limit: Option<f32>,
10352    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10353        let world = self.ranks.len();
10354        if world != NVFP4_CANONICAL_ROW_SHARDS {
10355            return Err("NVFP4 t2 routes require the canonical 2-shard grid".into());
10356        }
10357        let width = experts.input_width;
10358        let n_sel = 2 * n_sel_col;
10359        if z2.len() < 2 * width || sel_d.len() < n_sel || w_d.len() < n_sel {
10360            return Err("NVFP4 t2 routes geometry".into());
10361        }
10362        if !nvfp4_bank_v2_on() {
10363            return Err("NVFP4 t2 routes require the v2 banks (MEMRA_NVFP4_BANK_V2=1)".into());
10364        }
10365        let local_out = experts.expert_width / world;
10366        let mut guard = experts
10367            .t2_workspace
10368            .lock()
10369            .map_err(|_| "NVFP4 t2 workspace lock is poisoned")?;
10370        if guard.as_ref().is_none_or(|ws| ws.n_sel != n_sel) {
10371            let mut input2 = Vec::new();
10372            let mut in_q2 = Vec::new();
10373            let mut in_d2 = Vec::new();
10374            let mut sel2 = Vec::new();
10375            let mut route_w2 = Vec::new();
10376            let mut gate_out2 = Vec::new();
10377            let mut up_out2 = Vec::new();
10378            let mut act_q2 = Vec::new();
10379            let mut act_d2 = Vec::new();
10380            let mut partial2 = Vec::new();
10381            let mut acc_a = Vec::new();
10382            let mut acc_b = Vec::new();
10383            let mut ev_rank = Vec::new();
10384            for engine in &self.ranks {
10385                let _m = engine.gpu.enter_main()?;
10386                input2.push(engine.uninit(2 * width)?);
10387                in_q2.push(engine.alloc_i8_uninit(2 * width)?);
10388                in_d2.push(engine.uninit(2 * (width / 32))?);
10389                sel2.push(engine.htod_i32(&vec![0i32; n_sel])?);
10390                route_w2.push(engine.uninit(n_sel)?);
10391                gate_out2.push(engine.uninit(n_sel * local_out)?);
10392                up_out2.push(engine.uninit(n_sel * local_out)?);
10393                act_q2.push(engine.alloc_i8_uninit(n_sel * local_out)?);
10394                act_d2.push(engine.uninit(n_sel * (local_out / 32))?);
10395                partial2.push(engine.uninit(n_sel * width)?);
10396                acc_a.push(engine.uninit(width)?);
10397                acc_b.push(engine.uninit(width)?);
10398                ev_rank.push(engine.ctx().new_event(None)?);
10399            }
10400            let root = &self.ranks[0];
10401            let (peer_a, peer_b, omix_a, omix_b, ev_root) = {
10402                let _m = root.gpu.enter_main()?;
10403                (
10404                    root.uninit(width)?,
10405                    root.uninit(width)?,
10406                    root.uninit(width)?,
10407                    root.uninit(width)?,
10408                    root.ctx().new_event(None)?,
10409                )
10410            };
10411            let ev_entry = {
10412                let _m = e.gpu.enter_main()?;
10413                e.ctx().new_event(None)?
10414            };
10415            *guard = Some(Nvfp4T2Workspace {
10416                input2,
10417                in_q2,
10418                in_d2,
10419                sel2,
10420                route_w2,
10421                gate_out2,
10422                up_out2,
10423                act_q2,
10424                act_d2,
10425                partial2,
10426                acc_a,
10427                acc_b,
10428                peer_a,
10429                peer_b,
10430                omix_a,
10431                omix_b,
10432                ev_entry,
10433                ev_rank,
10434                ev_root,
10435                n_sel,
10436                e_device: e.ctx().ordinal(),
10437            });
10438        }
10439        let ws = guard.as_mut().expect("armed above");
10440        if ws.e_device != e.ctx().ordinal() {
10441            return Err("NVFP4 t2 routes engine changed".into());
10442        }
10443        {
10444            let _main = e.gpu.enter_main()?;
10445            ws.ev_entry.record(&e.stream())?;
10446        }
10447        for rank in 0..world {
10448            let engine = &self.ranks[rank];
10449            let _main = engine.gpu.enter_main()?;
10450            engine.stream().wait(&ws.ev_entry)?;
10451            {
10452                let mut dst = ws.input2[rank].slice_mut(0..2 * width);
10453                engine
10454                    .stream()
10455                    .memcpy_dtod(&z2.slice(0..2 * width), &mut dst)?;
10456            }
10457            {
10458                let mut dst = ws.sel2[rank].slice_mut(0..n_sel);
10459                engine
10460                    .stream()
10461                    .memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
10462            }
10463            {
10464                let mut dst = ws.route_w2[rank].slice_mut(0..n_sel);
10465                engine
10466                    .stream()
10467                    .memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
10468            }
10469            {
10470                let Nvfp4T2Workspace {
10471                    input2,
10472                    in_q2,
10473                    in_d2,
10474                    ..
10475                } = &mut *ws;
10476                engine.quantize_q8_1_into(
10477                    &input2[rank],
10478                    2,
10479                    width,
10480                    &mut in_q2[rank],
10481                    &mut in_d2[rank],
10482                )?;
10483            }
10484            let gate_bank = &experts.gate[rank];
10485            let up_bank = &experts.up[rank];
10486            if gate_bank.in_features != up_bank.in_features
10487                || gate_bank.local_out != up_bank.local_out
10488                || gate_bank.row_bytes != up_bank.row_bytes
10489                || gate_bank.expert_bytes != up_bank.expert_bytes
10490            {
10491                return Err("NVFP4 t2 routes need matched gate/up bank geometry".into());
10492            }
10493            {
10494                let Nvfp4T2Workspace {
10495                    sel2,
10496                    in_q2,
10497                    in_d2,
10498                    gate_out2,
10499                    up_out2,
10500                    ..
10501                } = &mut *ws;
10502                engine.qmatvec_nvfp4_sel_gu_tcol_into(
10503                    &gate_bank.bank,
10504                    &up_bank.bank,
10505                    &sel2[rank],
10506                    &in_q2[rank],
10507                    &in_d2[rank],
10508                    &mut gate_out2[rank],
10509                    &mut up_out2[rank],
10510                    n_sel,
10511                    n_sel_col,
10512                    gate_bank.in_features,
10513                    gate_bank.local_out,
10514                    gate_bank.row_bytes,
10515                    gate_bank.expert_bytes,
10516                    width,
10517                    width / 32,
10518                )?;
10519            }
10520            {
10521                let Nvfp4T2Workspace {
10522                    gate_out2,
10523                    up_out2,
10524                    sel2,
10525                    act_q2,
10526                    act_d2,
10527                    ..
10528                } = &mut *ws;
10529                engine.silu_mul_scaled_q8_1_sel_into(
10530                    &gate_out2[rank],
10531                    &up_out2[rank],
10532                    &experts.macros_gate_dev[rank],
10533                    &experts.macros_up_dev[rank],
10534                    &sel2[rank],
10535                    activation_limit,
10536                    &mut act_q2[rank],
10537                    &mut act_d2[rank],
10538                    local_out,
10539                    n_sel,
10540                )?;
10541            }
10542            let shard = &experts.down[rank];
10543            if shard.device_rank != rank || shard.local_in != local_out {
10544                return Err("NVFP4 t2 routes: down shard placement drifted".into());
10545            }
10546            {
10547                let Nvfp4T2Workspace {
10548                    sel2,
10549                    act_q2,
10550                    act_d2,
10551                    partial2,
10552                    ..
10553                } = &mut *ws;
10554                engine.qmatvec_nvfp4_sel_into(
10555                    &shard.bank,
10556                    &sel2[rank],
10557                    &act_q2[rank],
10558                    &act_d2[rank],
10559                    &mut partial2[rank],
10560                    n_sel,
10561                    shard.local_in,
10562                    shard.out_features,
10563                    shard.row_bytes,
10564                    shard.expert_bytes,
10565                    local_out,
10566                    local_out / 32,
10567                )?;
10568            }
10569            {
10570                let Nvfp4T2Workspace {
10571                    partial2,
10572                    route_w2,
10573                    sel2,
10574                    acc_a,
10575                    acc_b,
10576                    ..
10577                } = &mut *ws;
10578                engine.axpy_rows_seq_md_off_into(
10579                    &partial2[rank],
10580                    &route_w2[rank],
10581                    &experts.macros_down_dev[rank],
10582                    &sel2[rank],
10583                    &mut acc_a[rank],
10584                    width,
10585                    n_sel_col,
10586                    0,
10587                )?;
10588                engine.axpy_rows_seq_md_off_into(
10589                    &partial2[rank],
10590                    &route_w2[rank],
10591                    &experts.macros_down_dev[rank],
10592                    &sel2[rank],
10593                    &mut acc_b[rank],
10594                    width,
10595                    n_sel_col,
10596                    n_sel_col,
10597                )?;
10598            }
10599            if rank != 0 {
10600                ws.ev_rank[rank].record(&engine.stream())?;
10601            }
10602        }
10603        let root = &self.ranks[0];
10604        {
10605            let _main = root.gpu.enter_main()?;
10606            for ev in ws.ev_rank.iter().skip(1) {
10607                root.stream().wait(ev)?;
10608            }
10609            {
10610                let Nvfp4T2Workspace {
10611                    acc_a,
10612                    acc_b,
10613                    peer_a,
10614                    peer_b,
10615                    omix_a,
10616                    omix_b,
10617                    ..
10618                } = &mut *ws;
10619                {
10620                    let mut dst = peer_a.slice_mut(0..width);
10621                    root.stream()
10622                        .memcpy_dtod(&acc_a[1].slice(0..width), &mut dst)?;
10623                }
10624                {
10625                    let mut dst = peer_b.slice_mut(0..width);
10626                    root.stream()
10627                        .memcpy_dtod(&acc_b[1].slice(0..width), &mut dst)?;
10628                }
10629                root.add(&acc_a[0], peer_a, omix_a, width)?;
10630                root.add(&acc_b[0], peer_b, omix_b, width)?;
10631            }
10632            ws.ev_root.record(&root.stream())?;
10633        }
10634        let _main = e.gpu.enter_main()?;
10635        e.stream().wait(&ws.ev_root)?;
10636        let mut out = e.uninit(2 * width)?;
10637        e.stream()
10638            .memcpy_dtod(&ws.omix_a.slice(0..width), &mut out.slice_mut(0..width))?;
10639        e.stream().memcpy_dtod(
10640            &ws.omix_b.slice(0..width),
10641            &mut out.slice_mut(width..2 * width),
10642        )?;
10643        Ok(out)
10644    }
10645
10646    pub fn run_tensor_parallel_routes_nvfp4_device_routed(
10647        &self,
10648        experts: &ResidentNvfp4TensorParallel,
10649        e: &Engine,
10650        input_dev: &crate::CudaSlice<f32>,
10651        sel_d: &crate::CudaSlice<i32>,
10652        w_d: &crate::CudaSlice<f32>,
10653        experts_per_token: usize,
10654        activation_limit: Option<f32>,
10655    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10656        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
10657            experts,
10658            e,
10659            input_dev,
10660            sel_d,
10661            w_d,
10662            experts_per_token,
10663            activation_limit,
10664            || Ok(()),
10665        )
10666    }
10667
10668    /// `run_tensor_parallel_routes_nvfp4_device_routed` with a PREJOIN hook: `pre_join`
10669    /// runs on the host right before the join wait is enqueued on e's stream — work it
10670    /// issues there (e.g. the shexp overlap) executes WHILE the peer rank finishes its
10671    /// sweep, instead of after the join. Value-neutral by construction (the hook only
10672    /// reorders independent host issue).
10673    #[allow(clippy::too_many_arguments)]
10674    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
10675        &self,
10676        experts: &ResidentNvfp4TensorParallel,
10677        e: &Engine,
10678        input_dev: &crate::CudaSlice<f32>,
10679        sel_d: &crate::CudaSlice<i32>,
10680        w_d: &crate::CudaSlice<f32>,
10681        experts_per_token: usize,
10682        activation_limit: Option<f32>,
10683        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
10684    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10685        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
10686            experts,
10687            e,
10688            input_dev,
10689            sel_d,
10690            w_d,
10691            experts_per_token,
10692            activation_limit,
10693            pre_join,
10694            None,
10695        )
10696    }
10697
10698    /// The prejoin variant with MOE TAIL FUSION M1: when `post_add = Some((sh_raw,
10699    /// scale_raw))`, the direct-join arm folds the shexp apply into the join add
10700    /// (`dst = (acc0+acc1) + sh*scale[0]`, exact split-pair sequence) — the caller skips
10701    /// its apply launch. Raw UVA pointers so no lock is held across the call.
10702    #[allow(clippy::too_many_arguments)]
10703    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
10704        &self,
10705        experts: &ResidentNvfp4TensorParallel,
10706        e: &Engine,
10707        input_dev: &crate::CudaSlice<f32>,
10708        sel_d: &crate::CudaSlice<i32>,
10709        w_d: &crate::CudaSlice<f32>,
10710        experts_per_token: usize,
10711        activation_limit: Option<f32>,
10712        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
10713        post_add: Option<(u64, u64)>,
10714    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10715        if input_dev.len() != experts.input_width {
10716            return Err(format!(
10717                "NVFP4 device-routed input {} != width {}",
10718                input_dev.len(),
10719                experts.input_width
10720            )
10721            .into());
10722        }
10723        let n_sel = experts_per_token;
10724        if sel_d.len() < n_sel || w_d.len() < n_sel {
10725            return Err(format!(
10726                "NVFP4 device-routed routes sel={} w={} < experts/token {n_sel}",
10727                sel_d.len(),
10728                w_d.len()
10729            )
10730            .into());
10731        }
10732        let world = self.ranks.len();
10733        if world != NVFP4_CANONICAL_ROW_SHARDS {
10734            return Err(format!(
10735                "NVFP4 device routes require world == canonical shard grid \
10736                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
10737            )
10738            .into());
10739        }
10740        let local_out = if experts.ep2 {
10741            experts.expert_width
10742        } else {
10743            experts.expert_width / world
10744        };
10745
10746        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10747        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10748        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10749        let started = timing.then(std::time::Instant::now);
10750
10751        let mut workspace_guard = experts
10752            .device_workspace
10753            .lock()
10754            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10755        if workspace_guard.is_none() {
10756            drop(workspace_guard);
10757            let zero = vec![0.0f32; experts.input_width];
10758            let zero_sel = vec![0usize; n_sel];
10759            let zero_w = vec![0.0f32; n_sel];
10760            let _ = self.run_tensor_parallel_routes_nvfp4_device(
10761                experts,
10762                &zero,
10763                &zero_sel,
10764                &zero_w,
10765                n_sel,
10766                activation_limit,
10767            )?;
10768            workspace_guard = experts
10769                .device_workspace
10770                .lock()
10771                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10772        }
10773        let workspace = workspace_guard
10774            .as_mut()
10775            .expect("NVFP4 device routes workspace initialized above");
10776        if workspace.n_sel != n_sel {
10777            return Err(format!(
10778                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10779                workspace.n_sel
10780            )
10781            .into());
10782        }
10783
10784        // GRAPH DOOR (MEMRA_STEP_TP_GRAPH=1): the whole rank+root segment replays as one
10785        // stitched multi-device parent launched on e's stream — no events, no per-token node
10786        // updates (every address is persistent staging). VALUE-IDENTICAL to the eager path:
10787        // the children replay exactly the same kernel/copy sequence.
10788        if step_tp_graph_enabled()? {
10789            if experts.ep2 {
10790                return Err(
10791                    "MEMRA_STEP_TP_GRAPH=1 with MEMRA_STEP_NVFP4_EP2=1 has never been \
10792                     co-gated; unset one"
10793                        .into(),
10794                );
10795            }
10796            if workspace.dev_route_e.is_none() {
10797                let _main = e.gpu.enter_main()?;
10798                workspace.dev_route_e = Some((
10799                    e.htod_i32(&vec![0i32; n_sel])?,
10800                    e.htod(&vec![0.0f32; n_sel])?,
10801                ));
10802            }
10803            if workspace.in_stage_e.is_none() {
10804                let _main = e.gpu.enter_main()?;
10805                workspace.in_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
10806                workspace.out_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
10807            }
10808            if workspace.routes_graph.is_none() {
10809                let graph = self.nvfp4_routes_build_graph(
10810                    experts,
10811                    workspace,
10812                    local_out,
10813                    n_sel,
10814                    activation_limit,
10815                )?;
10816                workspace.routes_graph = Some(graph);
10817                eprintln!(
10818                    "[step-tp-graph] routes segment captured: ranks={world} n_sel={n_sel} \
10819                     children=3 updates=none performance_claim=false"
10820                );
10821            }
10822            let output = {
10823                let _main = e.gpu.enter_main()?;
10824                {
10825                    let (sel_e, w_e) = workspace
10826                        .dev_route_e
10827                        .as_mut()
10828                        .expect("device route staging set above");
10829                    {
10830                        let mut dst = sel_e.slice_mut(0..n_sel);
10831                        e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
10832                    }
10833                    {
10834                        let mut dst = w_e.slice_mut(0..n_sel);
10835                        e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
10836                    }
10837                }
10838                {
10839                    let in_stage = workspace
10840                        .in_stage_e
10841                        .as_mut()
10842                        .expect("graph staging set above");
10843                    let mut dst = in_stage.slice_mut(0..experts.input_width);
10844                    e.stream()
10845                        .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut dst)?;
10846                }
10847                unsafe {
10848                    let r = cudarc::driver::sys::cuGraphLaunch(
10849                        workspace
10850                            .routes_graph
10851                            .as_ref()
10852                            .expect("routes graph built above")
10853                            .exec,
10854                        e.stream().cu_stream() as cudarc::driver::sys::CUstream,
10855                    );
10856                    if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
10857                        return Err(format!("routes graph launch: {r:?}").into());
10858                    }
10859                }
10860                let mut output = e.uninit(experts.input_width)?;
10861                {
10862                    let out_stage = workspace
10863                        .out_stage_e
10864                        .as_ref()
10865                        .expect("graph staging set above");
10866                    e.stream().memcpy_dtod(
10867                        &out_stage.slice(0..experts.input_width),
10868                        &mut output.slice_mut(0..experts.input_width),
10869                    )?;
10870                }
10871                output
10872            };
10873            if let Some(started) = started {
10874                use std::sync::atomic::Ordering;
10875                let ns = TIMING_NS
10876                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10877                    + started.elapsed().as_nanos() as u64;
10878                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10879                if calls % 430 == 0 {
10880                    eprintln!(
10881                        "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10882                        ns as f64 / 1.0e6,
10883                        ns as f64 / calls as f64 / 1.0e3,
10884                    );
10885                }
10886            }
10887            return Ok(output);
10888        }
10889
10890        // Entry fence + router-output staging, all on e's stream: the fresh sel/w slices are
10891        // copied into the persistent e-context pair, then the event is recorded — the caller's
10892        // sel_d/w_d can free on e's stream with no cross-stream reader.
10893        if let Some((_, device)) = workspace.ev_entry.as_ref() {
10894            if *device != e.ctx().ordinal() {
10895                return Err("NVFP4 device-routed routes engine changed".into());
10896            }
10897        } else {
10898            let _main = e.gpu.enter_main()?;
10899            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
10900        }
10901        if workspace.dev_route_e.is_none() {
10902            let _main = e.gpu.enter_main()?;
10903            workspace.dev_route_e = Some((
10904                e.htod_i32(&vec![0i32; n_sel])?,
10905                e.htod(&vec![0.0f32; n_sel])?,
10906            ));
10907        }
10908        // MEMRA_SEL_MIRROR: the staging pair exists so the rank streams read a persistent
10909        // e-context address. The caller's sel_d/w_d ARE persistent (the process-static
10910        // selection rows), so when every consuming rank shares e's device the ranks can read
10911        // them directly and this hop disappears. The graph door keeps the staging (its
10912        // captured copies read the fixed addresses).
10913        let mirror = sel_mirror_on() && !step_tp_graph_enabled()?;
10914        let e_device = e.ctx().ordinal();
10915        // rank1_routed is consumed (taken) below; peek it here for the staging decision.
10916        let rank1_routed_peek = workspace.rank1_routed;
10917        let stage_needed = !mirror
10918            || self.ranks.iter().enumerate().any(|(rank_index, engine)| {
10919                !(rank1_routed_peek && rank_index == 1) && engine.ctx().ordinal() != e_device
10920            });
10921        {
10922            let _main = e.gpu.enter_main()?;
10923            if stage_needed {
10924                let (sel_e, w_e) = workspace
10925                    .dev_route_e
10926                    .as_mut()
10927                    .expect("device route staging set above");
10928                {
10929                    let mut dst = sel_e.slice_mut(0..n_sel);
10930                    e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
10931                }
10932                {
10933                    let mut dst = w_e.slice_mut(0..n_sel);
10934                    e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
10935                }
10936            }
10937            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10938            ev_entry.record(&e.stream())?;
10939        }
10940        // Prestage door: input pull + quantize were already issued on the rank streams
10941        // (before the router) — the rank stream order suffices, skip them here.
10942        let prestaged = std::mem::take(&mut workspace.prestaged);
10943        let rank1_routed = std::mem::take(&mut workspace.rank1_routed);
10944        for (rank_index, engine) in self.ranks.iter().enumerate() {
10945            let _main = engine.gpu.enter_main()?;
10946            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10947            engine.stream().wait(ev_entry)?;
10948            if !prestaged {
10949                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
10950                engine
10951                    .stream()
10952                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
10953            }
10954            if !(rank1_routed && rank_index == 1) {
10955                // ONE mirror launch instead of two 32-byte copy-engine dispatches; source is
10956                // the caller's persistent rows when this rank shares e's device (UVA, ordered
10957                // by ev_entry), else the staged e-context pair.
10958                let same_dev = engine.ctx().ordinal() == e_device;
10959                if mirror {
10960                    // Split the workspace borrow so the source (the staged pair, when this
10961                    // rank is off-device) and the destination rows coexist.
10962                    let Nvfp4DeviceRoutesWorkspace {
10963                        sel,
10964                        route_w,
10965                        dev_route_e,
10966                        ..
10967                    } = &mut *workspace;
10968                    let (src_sel, src_w): (&crate::CudaSlice<i32>, &crate::CudaSlice<f32>) =
10969                        if same_dev {
10970                            (sel_d, w_d)
10971                        } else {
10972                            let (sel_e, w_e) = dev_route_e
10973                                .as_ref()
10974                                .expect("device route staging set above");
10975                            (sel_e, w_e)
10976                        };
10977                    engine.moe_sel_w_mirror(
10978                        src_sel,
10979                        src_w,
10980                        &mut sel[rank_index],
10981                        &mut route_w[rank_index],
10982                        n_sel,
10983                    )?;
10984                } else {
10985                    let (sel_e, w_e) = workspace
10986                        .dev_route_e
10987                        .as_ref()
10988                        .expect("device route staging set above");
10989                    {
10990                        let mut dst = workspace.sel[rank_index].slice_mut(0..n_sel);
10991                        engine
10992                            .stream()
10993                            .memcpy_dtod(&sel_e.slice(0..n_sel), &mut dst)?;
10994                    }
10995                    {
10996                        let mut dst = workspace.route_w[rank_index].slice_mut(0..n_sel);
10997                        engine
10998                            .stream()
10999                            .memcpy_dtod(&w_e.slice(0..n_sel), &mut dst)?;
11000                    }
11001                }
11002            }
11003            if !prestaged {
11004                let Nvfp4DeviceRoutesWorkspace {
11005                    input, in_q, in_d, ..
11006                } = &mut *workspace;
11007                engine.quantize_q8_1_into(
11008                    &input[rank_index],
11009                    1,
11010                    experts.input_width,
11011                    &mut in_q[rank_index],
11012                    &mut in_d[rank_index],
11013                )?;
11014            }
11015        }
11016        self.nvfp4_routes_batched_sweeps(
11017            experts,
11018            workspace,
11019            &[],
11020            &[],
11021            &[],
11022            local_out,
11023            n_sel,
11024            activation_limit,
11025            true,
11026        )?;
11027
11028        // rank0 == root: its own stream order already covers its sweep; only the PEER
11029        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
11030        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
11031            let _main = engine.gpu.enter_main()?;
11032            workspace.ev_rank[rank_index].record(&engine.stream())?;
11033        }
11034        // Doorbell fences (MEMRA_FENCE_MEMOPS=1): rank1 + root ring their flags; e waits
11035        // the tickets instead of the two events. Arm lazily; 0-len = unsupported.
11036        let memops = fence_memops_on() && moe_direct_on() && self.ranks.len() == 2;
11037        let mut ticket = 0u32;
11038        if memops {
11039            use cudarc::driver::sys;
11040            if workspace.fence_flags_raw == 0 {
11041                let root = &self.ranks[0];
11042                let _main = root.gpu.enter_main()?;
11043                let mut ptr: sys::CUdeviceptr = 0;
11044                let r = unsafe { sys::cuMemAlloc_v2(&mut ptr, 8) };
11045                if r != sys::CUresult::CUDA_SUCCESS {
11046                    return Err(format!("fence flag alloc: {r:?}").into());
11047                }
11048                let r = unsafe { sys::cuMemsetD8_v2(ptr, 0, 8) };
11049                if r != sys::CUresult::CUDA_SUCCESS {
11050                    return Err(format!("fence flag memset: {r:?}").into());
11051                }
11052                workspace.fence_flags_raw = ptr as u64;
11053            }
11054            workspace.fence_ticket = workspace.fence_ticket.wrapping_add(1).max(1);
11055            ticket = workspace.fence_ticket;
11056            let base = workspace.fence_flags_raw;
11057            // rank1's fence: a peer stream MEMOP is rejected over PCIe P2P
11058            // (CUDA_ERROR_INVALID_VALUE, receipted 2026-08-23), but a peer KERNEL STORE into
11059            // root memory is legal — the direct join already relies on it. Under
11060            // MEMRA_FENCE_RANK1 rank1 rings flag[0] that way and e waits it same-device,
11061            // replacing the cross-device event wait below.
11062            if fence_rank1_on() {
11063                let peer = &self.ranks[1];
11064                let _pmain = peer.gpu.enter_main()?;
11065                peer.ring_flag_raw(base, ticket)?;
11066            }
11067            {
11068                let root = &self.ranks[0];
11069                let _main = root.gpu.enter_main()?;
11070                let r = unsafe {
11071                    sys::cuStreamWriteValue32_v2(
11072                        root.stream().cu_stream() as sys::CUstream,
11073                        (base + 4) as sys::CUdeviceptr,
11074                        ticket,
11075                        0,
11076                    )
11077                };
11078                if r != sys::CUresult::CUDA_SUCCESS {
11079                    return Err(format!("fence write root: {r:?}").into());
11080                }
11081            }
11082        }
11083        // PREJOIN hook: rank work is fully issued (dev1 running); independent e-stream
11084        // kernels queued here execute while the peer rank drains its sweep.
11085        pre_join()?;
11086
11087        if moe_direct_on() && self.ranks.len() == 2 {
11088            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
11089            // rank0's is root-stream-ordered. One root event + rank1's own event order
11090            // the model engine's single add — same operand order as root's add
11091            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
11092            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
11093            // hazard class does not apply).
11094            let _main = e.gpu.enter_main()?;
11095            if memops {
11096                use cudarc::driver::sys;
11097                let base = workspace.fence_flags_raw;
11098                let r = unsafe {
11099                    sys::cuStreamWaitValue32_v2(
11100                        e.stream().cu_stream() as sys::CUstream,
11101                        (base + 4) as sys::CUdeviceptr,
11102                        ticket,
11103                        sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
11104                    )
11105                };
11106                if r != sys::CUresult::CUDA_SUCCESS {
11107                    return Err(format!("fence wait: {r:?}").into());
11108                }
11109                if fence_rank1_on() {
11110                    // Same-device wait on the flag rank1 rang over P2P.
11111                    let r = unsafe {
11112                        sys::cuStreamWaitValue32_v2(
11113                            e.stream().cu_stream() as sys::CUstream,
11114                            base as sys::CUdeviceptr,
11115                            ticket,
11116                            sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
11117                        )
11118                    };
11119                    if r != sys::CUresult::CUDA_SUCCESS {
11120                        return Err(format!("fence wait rank1: {r:?}").into());
11121                    }
11122                } else {
11123                    for ev in workspace.ev_rank.iter().skip(1) {
11124                        e.stream().wait(ev)?;
11125                    }
11126                }
11127            } else {
11128                {
11129                    let root = &self.ranks[0];
11130                    let _rmain = root.gpu.enter_main()?;
11131                    workspace
11132                        .ev_done
11133                        .as_ref()
11134                        .expect("device routes done event")
11135                        .record(&root.stream())?;
11136                }
11137                e.stream().wait(
11138                    workspace
11139                        .ev_done
11140                        .as_ref()
11141                        .expect("device routes done event"),
11142                )?;
11143                for ev in workspace.ev_rank.iter().skip(1) {
11144                    e.stream().wait(ev)?;
11145                }
11146            }
11147            let mut output = e.uninit(experts.input_width)?;
11148            if let Some((sh_raw, scale_raw)) = post_add {
11149                // MOE TAIL FUSION M1: fold the shexp apply into the join add —
11150                // dst = (acc0 + acc1) + sh*scale[0], the exact split-pair sequence.
11151                e.add3_raw(
11152                    &workspace.accumulator[0],
11153                    &workspace.accumulator[1],
11154                    sh_raw,
11155                    scale_raw,
11156                    &mut output,
11157                    experts.input_width,
11158                )?;
11159            } else {
11160                e.add(
11161                    &workspace.accumulator[0],
11162                    &workspace.accumulator[1],
11163                    &mut output,
11164                    experts.input_width,
11165                )?;
11166            }
11167            let output = output;
11168            if let Some(started) = started {
11169                use std::sync::atomic::Ordering;
11170                let ns = TIMING_NS
11171                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
11172                    + started.elapsed().as_nanos() as u64;
11173                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11174                if calls % 430 == 0 {
11175                    eprintln!(
11176                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11177                        ns as f64 / 1.0e6,
11178                        ns as f64 / calls as f64 / 1.0e3,
11179                    );
11180                }
11181            }
11182            return Ok(output);
11183        }
11184        {
11185            let root = &self.ranks[0];
11186            let _main = root.gpu.enter_main()?;
11187            for ev in workspace.ev_rank.iter().skip(1) {
11188                root.stream().wait(ev)?;
11189            }
11190            root.stream()
11191                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
11192            {
11193                let Nvfp4DeviceRoutesWorkspace {
11194                    accumulator,
11195                    remote,
11196                    combined,
11197                    ..
11198                } = &mut *workspace;
11199                root.add(&accumulator[0], remote, combined, experts.input_width)?;
11200            }
11201            workspace
11202                .ev_done
11203                .as_ref()
11204                .expect("device routes done event")
11205                .record(&root.stream())?;
11206        }
11207        let output = {
11208            let _main = e.gpu.enter_main()?;
11209            e.stream().wait(
11210                workspace
11211                    .ev_done
11212                    .as_ref()
11213                    .expect("device routes done event"),
11214            )?;
11215            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
11216            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
11217            let mut output = e.uninit(experts.input_width)?;
11218            e.stream().memcpy_dtod(
11219                &workspace.combined.slice(0..experts.input_width),
11220                &mut output.slice_mut(0..experts.input_width),
11221            )?;
11222            output
11223        };
11224        if let Some(started) = started {
11225            use std::sync::atomic::Ordering;
11226            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
11227                + started.elapsed().as_nanos() as u64;
11228            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11229            if calls % 430 == 0 {
11230                eprintln!(
11231                    "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11232                    ns as f64 / 1.0e6,
11233                    ns as f64 / calls as f64 / 1.0e3,
11234                );
11235            }
11236        }
11237        Ok(output)
11238    }
11239
11240    /// The fused finish's ROOT section (combine + shadow gathers), event-free: the eager
11241    /// caller wraps it with rank-event waits + the done record; the token graph captures it
11242    /// verbatim (parent edges provide the ordering).
11243    pub(crate) fn decode_v2_finish_root_fused(
11244        &self,
11245        ws: &mut StepTpDecodeV2Ws,
11246    ) -> Result<(), Box<dyn std::error::Error>> {
11247        let root = &self.ranks[0];
11248        let _main = root.gpu.enter_main()?;
11249        if ws.raw_peer_partial != 0 {
11250            // Capture-safe raw seams (arming happened in the stage flow).
11251            raw_copy_bytes(ws.raw_peer_partial, ws.raw_o_partial1, ws.o_out * 4, root)?;
11252        } else {
11253            root.stream()
11254                .memcpy_dtod(&ws.o_partials[1][0], &mut ws.peer_partial)?;
11255        }
11256        {
11257            let StepTpDecodeV2Ws {
11258                o_partials,
11259                peer_partial,
11260                reduce_a,
11261                o_out,
11262                ..
11263            } = &mut *ws;
11264            root.add(&o_partials[0][0], peer_partial, reduce_a, *o_out)?;
11265        }
11266        let shadows = !no_local_shadow_on() || ws.raw_mixed_stage_e != 0;
11267        if shadows {
11268            // rank0's shadows are same-context (root) copies; rank1's cross-context reads go
11269            // raw when armed.
11270            let mut k_dst = ws.k_shadow.slice_mut(0..ws.local_kv_dim);
11271            root.stream().memcpy_dtod(&ws.k[0], &mut k_dst)?;
11272            let mut v_dst = ws.v_shadow.slice_mut(0..ws.local_kv_dim);
11273            root.stream().memcpy_dtod(&ws.v_raw[0], &mut v_dst)?;
11274        }
11275        if shadows && ws.raw_peer_partial != 0 {
11276            raw_copy_bytes(
11277                ws.raw_k_shadow + (ws.local_kv_dim * 4) as u64,
11278                ws.raw_k1,
11279                ws.local_kv_dim * 4,
11280                root,
11281            )?;
11282            raw_copy_bytes(
11283                ws.raw_v_shadow + (ws.local_kv_dim * 4) as u64,
11284                ws.raw_v1,
11285                ws.local_kv_dim * 4,
11286                root,
11287            )?;
11288        } else if shadows {
11289            let start = ws.local_kv_dim;
11290            let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
11291            root.stream().memcpy_dtod(&ws.k[1], &mut k_dst)?;
11292            let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
11293            root.stream().memcpy_dtod(&ws.v_raw[1], &mut v_dst)?;
11294        }
11295        if ws.raw_mixed_stage_e != 0 {
11296            // Token-graph mirrors: the e-glue children read same-context copies of the
11297            // root-produced rows.
11298            raw_copy_bytes(ws.raw_mixed_stage_e, ws.raw_reduce_a, ws.o_out * 4, root)?;
11299            let (k_stage, v_stage) = ws.raw_shadow_stage_e;
11300            raw_copy_bytes(k_stage, ws.raw_k_shadow, 2 * ws.local_kv_dim * 4, root)?;
11301            raw_copy_bytes(v_stage, ws.raw_v_shadow, 2 * ws.local_kv_dim * 4, root)?;
11302        }
11303        Ok(())
11304    }
11305
11306    /// Arm the token-graph e-context mirrors (orchestrator-supplied fixed addresses) plus
11307    /// reduce_a's own pointer.
11308    pub(crate) fn decode_v2_arm_token_mirrors(
11309        &self,
11310        ws: &mut StepTpDecodeV2Ws,
11311        mixed_stage_e: u64,
11312        shadow_stage_e: (u64, u64),
11313    ) -> Result<(), Box<dyn std::error::Error>> {
11314        use cudarc::driver::DevicePtr;
11315        let root = &self.ranks[0];
11316        let _main = root.gpu.enter_main()?;
11317        let stream = root.stream();
11318        let (a, _g) = ws.reduce_a.device_ptr(&stream);
11319        ws.raw_reduce_a = a as u64;
11320        ws.raw_mixed_stage_e = mixed_stage_e;
11321        ws.raw_shadow_stage_e = shadow_stage_e;
11322        Ok(())
11323    }
11324
11325    /// Build one layer's stitched routes graph: per-rank children captured on their own
11326    /// streams (raw cuMemcpyAsync at every cross-context seam — cudarc's slice tracking is
11327    /// capture-illegal there), a root combine child, and a multi-device parent with
11328    /// {rank0, rank1} -> root dependency edges. Zero per-token updates: every address the
11329    /// nodes touch is persistent workspace/staging.
11330    fn nvfp4_routes_build_graph(
11331        &self,
11332        experts: &ResidentNvfp4TensorParallel,
11333        workspace: &mut Nvfp4DeviceRoutesWorkspace,
11334        local_out: usize,
11335        n_sel: usize,
11336        activation_limit: Option<f32>,
11337    ) -> Result<RoutesGraph, Box<dyn std::error::Error>> {
11338        use cudarc::driver::DevicePtr;
11339        use cudarc::driver::sys;
11340        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
11341            if r == sys::CUresult::CUDA_SUCCESS {
11342                Ok(())
11343            } else {
11344                Err(format!("{what}: {r:?}").into())
11345            }
11346        }
11347        let world = self.ranks.len();
11348        if world != 2 {
11349            return Err("routes graph door is built for the TP2 pair".into());
11350        }
11351        let width = experts.input_width;
11352
11353        // Raw pointers cached before capture (each read with its owner's stream).
11354        let ptr_f32 = |buf: &crate::CudaSlice<f32>, engine: &Engine| -> u64 {
11355            let stream = engine.stream();
11356            let (ptr, _g) = buf.device_ptr(&stream);
11357            ptr as u64
11358        };
11359        let ptr_i32 = |buf: &crate::CudaSlice<i32>, engine: &Engine| -> u64 {
11360            let stream = engine.stream();
11361            let (ptr, _g) = buf.device_ptr(&stream);
11362            ptr as u64
11363        };
11364        let (sel_e, w_e) = workspace
11365            .dev_route_e
11366            .as_ref()
11367            .expect("device route staging set before graph build");
11368        let root_engine = &self.ranks[0];
11369        let p_in_stage = ptr_f32(
11370            workspace.in_stage_e.as_ref().expect("graph staging"),
11371            root_engine,
11372        );
11373        let p_out_stage = ptr_f32(
11374            workspace.out_stage_e.as_ref().expect("graph staging"),
11375            root_engine,
11376        );
11377        let p_sel_e = ptr_i32(sel_e, root_engine);
11378        let p_w_e = ptr_f32(w_e, root_engine);
11379        let p_input: Vec<u64> = (0..world)
11380            .map(|r| ptr_f32(&workspace.input[r], &self.ranks[r]))
11381            .collect();
11382        let p_sel: Vec<u64> = (0..world)
11383            .map(|r| ptr_i32(&workspace.sel[r], &self.ranks[r]))
11384            .collect();
11385        let p_route_w: Vec<u64> = (0..world)
11386            .map(|r| ptr_f32(&workspace.route_w[r], &self.ranks[r]))
11387            .collect();
11388        let p_acc1 = ptr_f32(&workspace.accumulator[1], &self.ranks[1]);
11389        let p_remote = ptr_f32(&workspace.remote, root_engine);
11390        let p_combined = ptr_f32(&workspace.combined, root_engine);
11391
11392        let raw_copy = |dst: u64,
11393                        src: u64,
11394                        bytes: usize,
11395                        engine: &Engine|
11396         -> Result<(), Box<dyn std::error::Error>> {
11397            unsafe {
11398                cu_try(
11399                    sys::cuMemcpyAsync(
11400                        dst as sys::CUdeviceptr,
11401                        src as sys::CUdeviceptr,
11402                        bytes,
11403                        engine.stream().cu_stream() as sys::CUstream,
11404                    ),
11405                    "routes graph cuMemcpyAsync",
11406                )
11407            }
11408        };
11409
11410        let mut children = Vec::with_capacity(3);
11411        for rank in 0..world {
11412            let engine = &self.ranks[rank];
11413            let _main = engine.gpu.enter_main()?;
11414            let (child, _retained) = engine.capture_graph_retained(|_| {
11415                raw_copy(p_input[rank], p_in_stage, width * 4, engine)?;
11416                raw_copy(p_sel[rank], p_sel_e, n_sel * 4, engine)?;
11417                raw_copy(p_route_w[rank], p_w_e, n_sel * 4, engine)?;
11418                {
11419                    let Nvfp4DeviceRoutesWorkspace {
11420                        input, in_q, in_d, ..
11421                    } = &mut *workspace;
11422                    engine.quantize_q8_1_into(
11423                        &input[rank],
11424                        1,
11425                        width,
11426                        &mut in_q[rank],
11427                        &mut in_d[rank],
11428                    )?;
11429                }
11430                self.nvfp4_routes_batched_sweeps_rank(
11431                    experts,
11432                    workspace,
11433                    &[],
11434                    &[],
11435                    &[],
11436                    local_out,
11437                    n_sel,
11438                    activation_limit,
11439                    true,
11440                    rank,
11441                )?;
11442                Ok(())
11443            })?;
11444            children.push(child);
11445        }
11446        {
11447            let root = &self.ranks[0];
11448            let _main = root.gpu.enter_main()?;
11449            let (child, _retained) = root.capture_graph_retained(|_| {
11450                raw_copy(p_remote, p_acc1, width * 4, root)?;
11451                {
11452                    let Nvfp4DeviceRoutesWorkspace {
11453                        accumulator,
11454                        remote,
11455                        combined,
11456                        ..
11457                    } = &mut *workspace;
11458                    root.add(&accumulator[0], remote, combined, width)?;
11459                }
11460                raw_copy(p_out_stage, p_combined, width * 4, root)?;
11461                Ok(())
11462            })?;
11463            children.push(child);
11464        }
11465
11466        let mut parent: sys::CUgraph = std::ptr::null_mut();
11467        unsafe {
11468            cu_try(sys::cuGraphCreate(&mut parent, 0), "routes cuGraphCreate")?;
11469        }
11470        let mut n0: sys::CUgraphNode = std::ptr::null_mut();
11471        let mut n1: sys::CUgraphNode = std::ptr::null_mut();
11472        let mut n2: sys::CUgraphNode = std::ptr::null_mut();
11473        unsafe {
11474            cu_try(
11475                sys::cuGraphAddChildGraphNode(
11476                    &mut n0,
11477                    parent,
11478                    std::ptr::null(),
11479                    0,
11480                    children[0].cu_graph(),
11481                ),
11482                "routes child r0",
11483            )?;
11484            cu_try(
11485                sys::cuGraphAddChildGraphNode(
11486                    &mut n1,
11487                    parent,
11488                    std::ptr::null(),
11489                    0,
11490                    children[1].cu_graph(),
11491                ),
11492                "routes child r1",
11493            )?;
11494            let deps = [n0, n1];
11495            cu_try(
11496                sys::cuGraphAddChildGraphNode(
11497                    &mut n2,
11498                    parent,
11499                    deps.as_ptr(),
11500                    2,
11501                    children[2].cu_graph(),
11502                ),
11503                "routes child root",
11504            )?;
11505        }
11506        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
11507        unsafe {
11508            cu_try(
11509                sys::cuGraphInstantiateWithFlags(&mut exec, parent, 0),
11510                "routes instantiate",
11511            )?;
11512        }
11513        Ok(RoutesGraph {
11514            exec,
11515            parent,
11516            _children: children,
11517        })
11518    }
11519
11520    /// One rank's routes section for the token graph (event-free): staged input copy (raw
11521    /// when the caller supplies the source pointer), quantize, and the batched sweeps.
11522    /// Eager device_routed wraps it with the entry-event wait.
11523    #[allow(clippy::too_many_arguments)]
11524    pub(crate) fn routes_rank_section(
11525        &self,
11526        experts: &ResidentNvfp4TensorParallel,
11527        workspace: &mut Nvfp4DeviceRoutesWorkspace,
11528        raw_input_src: u64,
11529        local_out: usize,
11530        n_sel: usize,
11531        activation_limit: Option<f32>,
11532        rank_index: usize,
11533    ) -> Result<(), Box<dyn std::error::Error>> {
11534        let engine = &self.ranks[rank_index];
11535        {
11536            let _main = engine.gpu.enter_main()?;
11537            // sel/route_w land via raw copies from the e staging (fixed addresses).
11538            let (sel_e_ptr, w_e_ptr) = workspace
11539                .raw_dev_route_e
11540                .ok_or("routes rank section requires armed staging pointers")?;
11541            raw_copy_bytes(
11542                workspace.raw_input[rank_index],
11543                raw_input_src,
11544                experts.input_width * 4,
11545                engine,
11546            )?;
11547            raw_copy_bytes(workspace.raw_sel[rank_index], sel_e_ptr, n_sel * 4, engine)?;
11548            raw_copy_bytes(
11549                workspace.raw_route_w[rank_index],
11550                w_e_ptr,
11551                n_sel * 4,
11552                engine,
11553            )?;
11554            {
11555                let Nvfp4DeviceRoutesWorkspace {
11556                    input, in_q, in_d, ..
11557                } = &mut *workspace;
11558                engine.quantize_q8_1_into(
11559                    &input[rank_index],
11560                    1,
11561                    experts.input_width,
11562                    &mut in_q[rank_index],
11563                    &mut in_d[rank_index],
11564                )?;
11565            }
11566        }
11567        self.nvfp4_routes_batched_sweeps_rank(
11568            experts,
11569            workspace,
11570            &[],
11571            &[],
11572            &[],
11573            local_out,
11574            n_sel,
11575            activation_limit,
11576            true,
11577            rank_index,
11578        )
11579    }
11580
11581    /// The routes ROOT combine section (event-free): peer accumulator read (raw), canonical
11582    /// add, combined row raw-copied into the fixed e-context out stage.
11583    pub(crate) fn routes_root_section(
11584        &self,
11585        experts: &ResidentNvfp4TensorParallel,
11586        workspace: &mut Nvfp4DeviceRoutesWorkspace,
11587    ) -> Result<(), Box<dyn std::error::Error>> {
11588        let root = &self.ranks[0];
11589        let _main = root.gpu.enter_main()?;
11590        let (acc1_ptr, remote_ptr, combined_ptr, out_stage_ptr) = workspace
11591            .raw_combine
11592            .ok_or("routes root section requires armed combine pointers")?;
11593        raw_copy_bytes(remote_ptr, acc1_ptr, experts.input_width * 4, root)?;
11594        {
11595            let Nvfp4DeviceRoutesWorkspace {
11596                accumulator,
11597                remote,
11598                combined,
11599                ..
11600            } = &mut *workspace;
11601            root.add(&accumulator[0], remote, combined, experts.input_width)?;
11602        }
11603        raw_copy_bytes(out_stage_ptr, combined_ptr, experts.input_width * 4, root)?;
11604        Ok(())
11605    }
11606
11607    /// Arm the routes raw pointers (once): staging pair, per-rank input/sel/route_w, and the
11608    /// combine set. Requires dev_route_e + in/out stages already allocated.
11609    pub(crate) fn routes_arm_raw(
11610        &self,
11611        experts: &ResidentNvfp4TensorParallel,
11612        workspace: &mut Nvfp4DeviceRoutesWorkspace,
11613    ) -> Result<(), Box<dyn std::error::Error>> {
11614        use cudarc::driver::DevicePtr;
11615        if workspace.raw_dev_route_e.is_some() {
11616            return Ok(());
11617        }
11618        let _ = experts;
11619        let (sel_e, w_e) = workspace
11620            .dev_route_e
11621            .as_ref()
11622            .ok_or("routes staging not armed")?;
11623        let root = &self.ranks[0];
11624        {
11625            let _main = root.gpu.enter_main()?;
11626            let stream = root.stream();
11627            let (a, _g) = sel_e.device_ptr(&stream);
11628            let (b, _g) = w_e.device_ptr(&stream);
11629            workspace.raw_dev_route_e = Some((a as u64, b as u64));
11630            let (c, _g) = workspace.accumulator[1].device_ptr(&stream);
11631            let (d, _g) = workspace.remote.device_ptr(&stream);
11632            let (f, _g) = workspace.combined.device_ptr(&stream);
11633            let out_stage = workspace
11634                .out_stage_e
11635                .as_ref()
11636                .ok_or("routes out stage not armed")?;
11637            let (g_, _g) = out_stage.device_ptr(&stream);
11638            workspace.raw_combine = Some((c as u64, d as u64, f as u64, g_ as u64));
11639        }
11640        for rank in 0..self.ranks.len() {
11641            let engine = &self.ranks[rank];
11642            let _main = engine.gpu.enter_main()?;
11643            let stream = engine.stream();
11644            let (a, _g) = workspace.input[rank].device_ptr(&stream);
11645            let (b, _g) = workspace.sel[rank].device_ptr(&stream);
11646            let (c, _g) = workspace.route_w[rank].device_ptr(&stream);
11647            workspace.raw_input.push(a as u64);
11648            workspace.raw_sel.push(b as u64);
11649            workspace.raw_route_w.push(c as u64);
11650        }
11651        Ok(())
11652    }
11653
11654    /// Routed NVFP4 expert program, host-canonical transport. Native/bulk P2P transport for the
11655    /// NVFP4 bank is a separate increment; this entry point is exactness-first and reports no
11656    /// throughput claim.
11657    pub fn run_tensor_parallel_routes_nvfp4(
11658        &self,
11659        experts: &ResidentNvfp4TensorParallel,
11660        input: &[f32],
11661        tokens: usize,
11662        selected: &[usize],
11663        route_weights: &[f32],
11664        experts_per_token: usize,
11665        activation_limit: Option<f32>,
11666    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
11667        validate_activations(input, tokens, experts.input_width)?;
11668        let pairs = tokens
11669            .checked_mul(experts_per_token)
11670            .ok_or("NVFP4 TP route count overflow")?;
11671        if selected.len() != pairs || route_weights.len() != pairs {
11672            return Err(format!(
11673                "NVFP4 TP routes selected={} weights={} != tokens {tokens} x experts/token \
11674                 {experts_per_token} ({pairs})",
11675                selected.len(),
11676                route_weights.len(),
11677            )
11678            .into());
11679        }
11680        if !route_weights.iter().all(|weight| weight.is_finite()) {
11681            return Err("NVFP4 TP route weights contain a non-finite value".into());
11682        }
11683
11684        let mut output = vec![0.0f32; tokens * experts.input_width];
11685        for token in 0..tokens {
11686            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
11687            for slot in 0..experts_per_token {
11688                let pair = token * experts_per_token + slot;
11689                let expert = selected[pair];
11690                if expert >= experts.expert_count {
11691                    return Err(format!(
11692                        "NVFP4 TP selected expert {expert} outside 0..{}",
11693                        experts.expert_count
11694                    )
11695                    .into());
11696                }
11697                // EP2 banks hold the WHOLE expert on rank (expert & 1) at slot (expert >> 1);
11698                // per-row dots are the same full-width program either way (a column shard
11699                // splits ROWS, not the dot), so gate/up are bit-equal across layouts. Only
11700                // down's parenthesization moves (full-width dot vs canonical 2-shard sum) —
11701                // the numeric-class this door declares.
11702                let gate = if experts.ep2 {
11703                    self.run_full_bank_expert_nvfp4(
11704                        &experts.gate,
11705                        &experts.macros_gate,
11706                        expert,
11707                        input_row,
11708                    )?
11709                } else {
11710                    self.run_column_bank_expert_nvfp4(
11711                        &experts.gate,
11712                        &experts.macros_gate,
11713                        expert,
11714                        input_row,
11715                    )?
11716                };
11717                let up = if experts.ep2 {
11718                    self.run_full_bank_expert_nvfp4(
11719                        &experts.up,
11720                        &experts.macros_up,
11721                        expert,
11722                        input_row,
11723                    )?
11724                } else {
11725                    self.run_column_bank_expert_nvfp4(
11726                        &experts.up,
11727                        &experts.macros_up,
11728                        expert,
11729                        input_row,
11730                    )?
11731                };
11732                let activated: Vec<f32> = gate
11733                    .iter()
11734                    .zip(&up)
11735                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
11736                    .collect();
11737                debug_assert_eq!(activated.len(), experts.expert_width);
11738                let down = if experts.ep2 {
11739                    self.run_full_down_expert_nvfp4(
11740                        &experts.down,
11741                        &experts.macros_down,
11742                        expert,
11743                        &activated,
11744                    )?
11745                } else {
11746                    self.run_row_bank_expert_nvfp4(
11747                        &experts.down,
11748                        &experts.macros_down,
11749                        expert,
11750                        &activated,
11751                    )?
11752                };
11753                let weight = route_weights[pair];
11754                for (sum, value) in output
11755                    [token * experts.input_width..(token + 1) * experts.input_width]
11756                    .iter_mut()
11757                    .zip(down)
11758                {
11759                    *sum += weight * value;
11760                }
11761            }
11762        }
11763        Ok(output)
11764    }
11765}
11766
11767#[cfg(test)]
11768mod tests {
11769    use super::*;
11770
11771    #[test]
11772    fn step_expert_activation_clamps_each_arm_by_the_official_contract() {
11773        let limit = Some(7.0);
11774        assert_eq!(step_expert_activation_host(20.0, 9.0, limit), 49.0);
11775        assert_eq!(step_expert_activation_host(20.0, -9.0, limit), -49.0);
11776        assert!(
11777            step_expert_activation_host(-20.0, 9.0, limit).abs()
11778                < step_expert_activation_host(-20.0, 9.0, None).abs()
11779        );
11780        assert!(validate_step_expert_activation_limit(Some(f32::NAN)).is_err());
11781        assert!(validate_step_expert_activation_limit(Some(0.0)).is_err());
11782        assert!(validate_step_expert_activation_limit(limit).is_ok());
11783    }
11784
11785    #[test]
11786    fn moe_residual_host_preserves_official_add_order() {
11787        let output = moe_residual_host(&[1.0e20], &[-1.0e20], &[1.0]).unwrap();
11788        assert_eq!(output, [0.0]);
11789        assert_eq!(
11790            moe_residual_host(&[0.0], &[0.0, 1.0], &[0.0]).unwrap_err(),
11791            "MoE residual lengths residual=1 routed=2 shared=1"
11792        );
11793    }
11794
11795    #[test]
11796    fn expert_owner_routes_preserve_global_pair_order_with_local_expert_ids() {
11797        let selected = [0, 36, 72, 108, 144, 180, 216, 252];
11798        let owners = partition_expert_owner_routes(288, 4, 1, 8, &selected).unwrap();
11799        assert_eq!(owners.len(), 4);
11800        for (rank, owner) in owners.iter().enumerate() {
11801            assert_eq!(owner.rank, rank);
11802            assert_eq!(owner.selected, vec![0, 36]);
11803            assert_eq!(owner.token_rows, vec![0, 0]);
11804            assert_eq!(owner.global_pairs, vec![rank * 2, rank * 2 + 1]);
11805        }
11806    }
11807
11808    #[test]
11809    fn expert_owner_routes_validate_geometry_and_selected_experts() {
11810        assert!(partition_expert_owner_routes(288, 5, 1, 8, &[0; 8]).is_err());
11811        assert!(partition_expert_owner_routes(288, 4, 2, 8, &[0; 8]).is_err());
11812        let error = partition_expert_owner_routes(288, 4, 1, 8, &[288; 8]).unwrap_err();
11813        assert!(error.contains("outside 0..288"));
11814    }
11815
11816    #[test]
11817    fn step_grouped_owner_routes_validate_dynamic_top8_shapes() {
11818        let selected = [
11819            1, 73, 80, 145, 152, 159, 217, 224, 12, 84, 91, 156, 163, 170, 228, 235,
11820        ];
11821        assert_eq!(
11822            validate_step_grouped_owner_routes(288, 2, &selected).unwrap(),
11823            16
11824        );
11825        let owners = partition_expert_owner_routes(288, 4, 2, 8, &selected).unwrap();
11826        assert_eq!(
11827            owners
11828                .iter()
11829                .map(|owner| owner.selected.len())
11830                .collect::<Vec<_>>(),
11831            vec![2, 4, 6, 4]
11832        );
11833        assert!(validate_step_grouped_owner_routes(288, 2, &selected[..8]).is_err());
11834        assert!(validate_step_grouped_owner_routes(288, 1, &[0; 8]).is_err());
11835        assert!(validate_step_grouped_owner_routes(287, 2, &selected).is_err());
11836    }
11837
11838    #[test]
11839    fn weighted_route_combine_requires_a_canonical_pair_permutation() {
11840        let owner0 = [0usize, 3];
11841        let owner1 = [1usize, 2];
11842        let owners = [owner0.as_slice(), owner1.as_slice()];
11843        assert_eq!(
11844            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, 0.2, 0.3, 0.4],)
11845                .unwrap(),
11846            WeightedRouteCombineShape {
11847                pairs: 4,
11848                max_pairs: 12,
11849            }
11850        );
11851        let duplicate = [owner0.as_slice(), &[1usize, 1][..]];
11852        assert!(
11853            validate_weighted_route_combine(4096, 4, 3, 1, &duplicate, &[0.1, 0.2, 0.3, 0.4],)
11854                .is_err()
11855        );
11856        assert!(
11857            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, f32::NAN, 0.3, 0.4],)
11858                .is_err()
11859        );
11860        assert!(
11861            validate_weighted_route_combine(4096, 4, 1, 2, &owners, &[0.1, 0.2, 0.3, 0.4],)
11862                .is_err()
11863        );
11864    }
11865
11866    #[test]
11867    fn native_p2p_door_is_strict_and_default_off() {
11868        assert!(!parse_step_tp_native_p2p(None).unwrap());
11869        assert!(!parse_step_tp_native_p2p(Some("")).unwrap());
11870        assert!(!parse_step_tp_native_p2p(Some("0")).unwrap());
11871        assert!(parse_step_tp_native_p2p(Some("1")).unwrap());
11872        assert!(parse_step_tp_native_p2p(Some("true")).is_err());
11873        assert!(parse_step_tp_native_p2p(Some("2")).is_err());
11874    }
11875
11876    #[test]
11877    fn bulk_p2p_door_is_strict_and_default_off() {
11878        assert!(!parse_step_tp_bulk_p2p(None).unwrap());
11879        assert!(!parse_step_tp_bulk_p2p(Some("")).unwrap());
11880        assert!(!parse_step_tp_bulk_p2p(Some("0")).unwrap());
11881        assert!(parse_step_tp_bulk_p2p(Some("1")).unwrap());
11882        assert!(parse_step_tp_bulk_p2p(Some("true")).is_err());
11883        assert!(parse_step_tp_bulk_p2p(Some("2")).is_err());
11884    }
11885
11886    #[test]
11887    fn ep_device_arithmetic_door_is_strict_and_default_off() {
11888        assert!(!parse_step_ep_device_arithmetic(None).unwrap());
11889        assert!(!parse_step_ep_device_arithmetic(Some("")).unwrap());
11890        assert!(!parse_step_ep_device_arithmetic(Some("0")).unwrap());
11891        assert!(parse_step_ep_device_arithmetic(Some("1")).unwrap());
11892        assert!(parse_step_ep_device_arithmetic(Some("true")).is_err());
11893        assert!(parse_step_ep_device_arithmetic(Some("2")).is_err());
11894    }
11895
11896    #[test]
11897    fn f32_mirror_door_is_strict_and_default_off() {
11898        assert!(!parse_step_tp_f32_mirror(None).unwrap());
11899        assert!(!parse_step_tp_f32_mirror(Some("")).unwrap());
11900        assert!(!parse_step_tp_f32_mirror(Some("0")).unwrap());
11901        assert!(parse_step_tp_f32_mirror(Some("1")).unwrap());
11902        assert!(parse_step_tp_f32_mirror(Some("true")).is_err());
11903        assert!(parse_step_tp_f32_mirror(Some("2")).is_err());
11904    }
11905
11906    fn matrix(out_features: usize, in_features: usize) -> (Vec<u8>, Vec<f32>) {
11907        let codes = (0..out_features * in_features)
11908            .map(|index| (index % 251) as u8)
11909            .collect();
11910        let scales = (0..out_features.div_ceil(FP8_BLOCK) * in_features.div_ceil(FP8_BLOCK))
11911            .map(|index| index as f32 + 1.0)
11912            .collect();
11913        (codes, scales)
11914    }
11915
11916    fn bf16_matrix_bytes(out_features: usize, in_features: usize) -> Vec<u8> {
11917        (0..out_features * in_features)
11918            .flat_map(|value| (value as u16).to_le_bytes())
11919            .collect()
11920    }
11921
11922    fn decode_u16(bytes: &[u8]) -> Vec<u16> {
11923        bytes
11924            .chunks_exact(2)
11925            .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
11926            .collect()
11927    }
11928
11929    #[test]
11930    fn bf16_matrix_rejects_wrong_byte_count() {
11931        let bytes = vec![0u8; 4 * 4 * 2 - 1];
11932        let matrix = Bf16Matrix {
11933            bytes: &bytes,
11934            out_features: 4,
11935            in_features: 4,
11936        };
11937        assert!(matrix.validate().unwrap_err().contains("4x4x2"));
11938    }
11939
11940    #[test]
11941    fn replicated_device_rows_require_exact_rank_local_shapes() {
11942        assert_eq!(
11943            replicated_device_row_values(3, 4096, 4, &[12_288; 4]).unwrap(),
11944            12_288
11945        );
11946        assert!(replicated_device_row_values(0, 4096, 4, &[0; 4]).is_err());
11947        assert!(replicated_device_row_values(3, 0, 4, &[0; 4]).is_err());
11948        assert!(replicated_device_row_values(3, 4096, 4, &[12_288; 3]).is_err());
11949        assert!(
11950            replicated_device_row_values(3, 4096, 4, &[12_288, 12_288, 12_287, 12_288]).is_err()
11951        );
11952        assert!(replicated_device_row_values(usize::MAX, 2, 1, &[0]).is_err());
11953    }
11954
11955    #[test]
11956    fn replicated_device_row_refresh_requires_exact_root_source() {
11957        assert_eq!(
11958            replicated_device_row_source_values(1, 12_288, 12_288, 3, 3).unwrap(),
11959            12_288
11960        );
11961        assert!(replicated_device_row_source_values(0, 12_288, 0, 3, 3).is_err());
11962        assert!(replicated_device_row_source_values(1, 0, 0, 3, 3).is_err());
11963        assert!(replicated_device_row_source_values(1, 12_288, 12_287, 3, 3).is_err());
11964        assert!(replicated_device_row_source_values(1, 12_288, 12_288, 2, 3).is_err());
11965        assert!(replicated_device_row_source_values(usize::MAX, 2, 0, 3, 3).is_err());
11966    }
11967
11968    #[test]
11969    fn step_bf16_canonical_rows_are_topology_invariant_through_tp8() {
11970        for tp in [1, 2, 4, 8] {
11971            assert_eq!(step_bf16_canonical_chunk_rows(8_192, tp).unwrap(), 1_024);
11972            assert_eq!(step_bf16_canonical_chunk_rows(12_288, tp).unwrap(), 1_536);
11973            assert_eq!(step_bf16_canonical_chunk_rows(1_024, tp).unwrap(), 128);
11974            assert_eq!(step_bf16_canonical_chunk_cols(8_192, tp).unwrap(), 1_024);
11975            assert_eq!(step_bf16_canonical_chunk_cols(12_288, tp).unwrap(), 1_536);
11976        }
11977        assert!(step_bf16_canonical_chunk_rows(12_288, 3).is_err());
11978        assert!(step_bf16_canonical_chunk_rows(1_001, 2).is_err());
11979        assert!(step_bf16_canonical_chunk_cols(12_288, 3).is_err());
11980        assert!(step_bf16_canonical_chunk_cols(1_001, 2).is_err());
11981    }
11982
11983    #[test]
11984    fn cache_rows_split_by_token_then_rank() {
11985        let rows = (0u8..24).collect::<Vec<_>>();
11986        assert_eq!(
11987            cache_rank_rows(&rows, 3, 4, 2, 0).unwrap(),
11988            vec![0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19]
11989        );
11990        assert_eq!(
11991            cache_rank_rows(&rows, 3, 4, 2, 1).unwrap(),
11992            vec![4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23]
11993        );
11994        assert!(cache_rank_rows(&rows[..23], 3, 4, 2, 0).is_err());
11995        assert!(cache_rank_rows(&rows, 3, 4, 2, 2).is_err());
11996    }
11997
11998    #[test]
11999    fn bf16_column_shard_preserves_contiguous_output_rows() {
12000        let bytes = bf16_matrix_bytes(4, 4);
12001        let matrix = Bf16Matrix {
12002            bytes: &bytes,
12003            out_features: 4,
12004            in_features: 4,
12005        };
12006        let shard = bf16_column_shard(matrix, 2, 1).unwrap();
12007        assert_eq!(shard.out_features, 2);
12008        assert_eq!(shard.in_features, 4);
12009        assert_eq!(decode_u16(shard.bytes), (8..16).collect::<Vec<_>>());
12010    }
12011
12012    #[test]
12013    fn bf16_row_shard_preserves_each_input_column_window() {
12014        let bytes = bf16_matrix_bytes(3, 4);
12015        let matrix = Bf16Matrix {
12016            bytes: &bytes,
12017            out_features: 3,
12018            in_features: 4,
12019        };
12020        let shard = bf16_row_shard(matrix, 2, 1).unwrap();
12021        assert_eq!(decode_u16(&shard), vec![2, 3, 6, 7, 10, 11]);
12022    }
12023
12024    #[test]
12025    fn bf16_row_block_preserves_global_column_order() {
12026        let bytes = bf16_matrix_bytes(3, 8);
12027        let matrix = Bf16Matrix {
12028            bytes: &bytes,
12029            out_features: 3,
12030            in_features: 8,
12031        };
12032        let block = bf16_row_block(matrix, 2, 3).unwrap();
12033        assert_eq!(decode_u16(&block), vec![2, 3, 4, 10, 11, 12, 18, 19, 20]);
12034    }
12035
12036    #[test]
12037    fn column_shard_preserves_contiguous_weight_and_scale_rows() {
12038        let (codes, scales) = matrix(1280, 4096);
12039        let matrix = E4m3BlockMatrix {
12040            codes: &codes,
12041            scales: &scales,
12042            out_features: 1280,
12043            in_features: 4096,
12044        };
12045        let shard = column_shard(matrix, 2, 1).unwrap();
12046        assert_eq!(shard.out_features, 640);
12047        assert_eq!(shard.codes, &codes[640 * 4096..]);
12048        assert_eq!(shard.scales, &scales[5 * 32..]);
12049    }
12050
12051    #[test]
12052    fn row_shard_preserves_each_weight_and_scale_column_window() {
12053        let (codes, scales) = matrix(4096, 1280);
12054        let matrix = E4m3BlockMatrix {
12055            codes: &codes,
12056            scales: &scales,
12057            out_features: 4096,
12058            in_features: 1280,
12059        };
12060        let (shard_codes, shard_scales) = row_shard(matrix, 2, 1).unwrap();
12061        assert_eq!(shard_codes.len(), 4096 * 640);
12062        assert_eq!(&shard_codes[..640], &codes[640..1280]);
12063        assert_eq!(&shard_codes[640..1280], &codes[1280 + 640..2560]);
12064        assert_eq!(shard_scales.len(), 32 * 5);
12065        assert_eq!(&shard_scales[..5], &scales[5..10]);
12066        assert_eq!(&shard_scales[5..10], &scales[15..20]);
12067    }
12068
12069    #[test]
12070    fn activation_shards_keep_token_rows_separate() {
12071        let activations: Vec<f32> = (0..2 * 8).map(|value| value as f32).collect();
12072        assert_eq!(
12073            activation_shard(&activations, 2, 8, 2, 1),
12074            vec![4.0, 5.0, 6.0, 7.0, 12.0, 13.0, 14.0, 15.0],
12075        );
12076    }
12077
12078    #[test]
12079    fn expert_bank_selects_expert_major_code_and_scale_planes() {
12080        let expert_count = 2;
12081        let out_features = 128;
12082        let in_features = 128;
12083        let code_stride = out_features * in_features;
12084        let codes: Vec<u8> = (0..expert_count * code_stride)
12085            .map(|index| (index % 251) as u8)
12086            .collect();
12087        let scales = vec![1.0f32, 2.0];
12088        let bank = E4m3ExpertBank {
12089            codes: &codes,
12090            scales: &scales,
12091            expert_count,
12092            out_features,
12093            in_features,
12094        };
12095        bank.validate().unwrap();
12096        let expert = bank.expert(1).unwrap();
12097        assert_eq!(expert.codes, &codes[code_stride..]);
12098        assert_eq!(expert.scales, &[2.0]);
12099    }
12100
12101    #[test]
12102    fn expert_bank_rejects_non_positive_scale() {
12103        let codes = vec![0u8; 128 * 128];
12104        let scales = vec![0.0f32];
12105        let bank = E4m3ExpertBank {
12106            codes: &codes,
12107            scales: &scales,
12108            expert_count: 1,
12109            out_features: 128,
12110            in_features: 128,
12111        };
12112        assert!(bank.validate().unwrap_err().contains("non-positive"));
12113    }
12114
12115    #[test]
12116    fn tensor_parallel_column_bank_keeps_each_expert_scale_plane_separate() {
12117        let expert_count = 2;
12118        let out_features = 256;
12119        let in_features = 128;
12120        let code_stride = out_features * in_features;
12121        let scale_stride = 2;
12122        let codes = (0..expert_count * code_stride)
12123            .map(|index| (index % 251) as u8)
12124            .collect::<Vec<_>>();
12125        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
12126        let bank = E4m3ExpertBank {
12127            codes: &codes,
12128            scales: &scales,
12129            expert_count,
12130            out_features,
12131            in_features,
12132        };
12133
12134        let rank = pack_column_bank_rank(bank, 2, 1).unwrap();
12135        assert_eq!(rank.out_features, 128);
12136        assert_eq!(rank.in_features, 128);
12137        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
12138        assert_eq!(rank.scales, vec![11.0, 21.0]);
12139        assert_eq!(&rank.codes[..128 * 128], &codes[128 * 128..256 * 128]);
12140        assert_eq!(
12141            &rank.codes[128 * 128..],
12142            &codes[code_stride + 128 * 128..2 * code_stride]
12143        );
12144        assert_eq!(scale_stride, scales.len() / expert_count);
12145    }
12146
12147    #[test]
12148    fn tensor_parallel_row_bank_keeps_each_expert_scale_plane_separate() {
12149        let expert_count = 2;
12150        let out_features = 128;
12151        let in_features = 256;
12152        let code_stride = out_features * in_features;
12153        let codes = (0..expert_count * code_stride)
12154            .map(|index| (index % 251) as u8)
12155            .collect::<Vec<_>>();
12156        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
12157        let bank = E4m3ExpertBank {
12158            codes: &codes,
12159            scales: &scales,
12160            expert_count,
12161            out_features,
12162            in_features,
12163        };
12164
12165        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
12166        assert_eq!(rank.out_features, 128);
12167        assert_eq!(rank.in_features, 128);
12168        assert_eq!(rank.k_blocks, Some(1));
12169        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
12170        assert_eq!(rank.scales, vec![11.0, 21.0]);
12171        assert_eq!(&rank.codes[..128], &codes[128..256]);
12172        assert_eq!(
12173            &rank.codes[128 * 128..128 * 128 + 128],
12174            &codes[code_stride + 128..code_stride + 256]
12175        );
12176    }
12177
12178    #[test]
12179    fn tensor_parallel_row_bank_preserves_global_k_block_order() {
12180        let expert_count = 2;
12181        let out_features = 256;
12182        let in_features = 512;
12183        let code_stride = out_features * in_features;
12184        let mut codes = vec![0u8; expert_count * code_stride];
12185        for expert in 0..expert_count {
12186            for row in 0..out_features {
12187                for block in 0..4 {
12188                    let value = (expert * 80 + block * 16 + row % 16) as u8;
12189                    let start = expert * code_stride + row * in_features + block * FP8_BLOCK;
12190                    codes[start..start + FP8_BLOCK].fill(value);
12191                }
12192            }
12193        }
12194        let scales = vec![
12195            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,
12196            112.0, 113.0, 114.0,
12197        ];
12198        let bank = E4m3ExpertBank {
12199            codes: &codes,
12200            scales: &scales,
12201            expert_count,
12202            out_features,
12203            in_features,
12204        };
12205
12206        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
12207        assert_eq!(rank.out_features, out_features);
12208        assert_eq!(rank.in_features, 256);
12209        assert_eq!(rank.k_blocks, Some(2));
12210        assert_eq!(rank.code_stride, out_features * 256);
12211        assert_eq!(rank.scale_stride, 4);
12212        assert_eq!(&rank.scales[..4], &[3.0, 13.0, 4.0, 14.0]);
12213        assert_eq!(&rank.scales[4..], &[103.0, 113.0, 104.0, 114.0]);
12214
12215        let block_stride = out_features * FP8_BLOCK;
12216        assert!(rank.codes[..FP8_BLOCK].iter().all(|&code| code == 32));
12217        assert!(
12218            rank.codes[block_stride..block_stride + FP8_BLOCK]
12219                .iter()
12220                .all(|&code| code == 48)
12221        );
12222        assert!(
12223            rank.codes[rank.code_stride..rank.code_stride + FP8_BLOCK]
12224                .iter()
12225                .all(|&code| code == 112)
12226        );
12227        assert!(
12228            rank.codes
12229                [rank.code_stride + block_stride..rank.code_stride + block_stride + FP8_BLOCK]
12230                .iter()
12231                .all(|&code| code == 128)
12232        );
12233    }
12234
12235    #[test]
12236    fn step_ep_layer_specs_are_literal_and_fail_closed() {
12237        assert!(parse_step_ep_layer_specs(None).unwrap().is_empty());
12238        assert!(parse_step_ep_layer_specs(Some("0")).unwrap().is_empty());
12239        assert_eq!(
12240            parse_step_ep_layer_specs(Some("24@1,2")).unwrap(),
12241            vec![StepEpLayerSpec {
12242                layer: 24,
12243                devices: vec![1, 2],
12244            }]
12245        );
12246        assert_eq!(
12247            parse_step_ep_layer_specs(Some("24-25@1,2;31@0,2")).unwrap(),
12248            vec![
12249                StepEpLayerSpec {
12250                    layer: 24,
12251                    devices: vec![1, 2],
12252                },
12253                StepEpLayerSpec {
12254                    layer: 25,
12255                    devices: vec![1, 2],
12256                },
12257                StepEpLayerSpec {
12258                    layer: 31,
12259                    devices: vec![0, 2],
12260                },
12261            ]
12262        );
12263        assert!(parse_step_ep_layer_specs(Some("24@1")).is_err());
12264        assert!(parse_step_ep_layer_specs(Some("24@1,1")).is_err());
12265        assert!(parse_step_ep_layer_specs(Some("layer@1,2")).is_err());
12266        assert!(parse_step_ep_layer_specs(Some("25-24@1,2")).is_err());
12267        assert!(parse_step_ep_layer_specs(Some("0-128@1,2")).is_err());
12268        assert!(parse_step_ep_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
12269        assert!(parse_step_ep_layer_specs(Some("all@0,1")).is_err());
12270    }
12271
12272    #[test]
12273    fn step_tp_layer_specs_share_the_fail_closed_layer_contract() {
12274        assert!(parse_step_tp_layer_specs(None).unwrap().is_empty());
12275        assert!(parse_step_tp_layer_specs(Some("0")).unwrap().is_empty());
12276        assert_eq!(
12277            parse_step_tp_layer_specs(Some("24-25@1,2")).unwrap(),
12278            vec![
12279                StepTpLayerSpec {
12280                    layer: 24,
12281                    devices: vec![1, 2],
12282                },
12283                StepTpLayerSpec {
12284                    layer: 25,
12285                    devices: vec![1, 2],
12286                },
12287            ]
12288        );
12289        let error = parse_step_tp_layer_specs(Some("24@1")).unwrap_err();
12290        assert!(error.contains("MEMRA_STEP_TP"));
12291        assert!(parse_step_tp_layer_specs(Some("24@1,1")).is_err());
12292        assert!(parse_step_tp_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
12293
12294        let all = parse_step_tp_layer_specs(Some("all@0,1,2,3,4,5,6,7")).unwrap();
12295        assert_eq!(all.len(), STEP37_TRUNK_LAYERS);
12296        assert_eq!(all.first().unwrap().layer, 0);
12297        assert_eq!(all.last().unwrap().layer, STEP37_TRUNK_LAYERS - 1);
12298        let devices = (0..8).collect::<Vec<_>>();
12299        assert!(all.iter().all(|spec| spec.devices == devices));
12300        assert!(parse_step_tp_layer_specs(Some("all@0,1;44@0,1")).is_err());
12301    }
12302}
12303
12304// ===== Whole-token graph builder (increment B) ==================================================
12305//
12306// The decode fns are already sectioned at every e/rank/root seam (the stage flow, sweeps_rank,
12307// finish splits, the dcw arm). `graph_section` is the one annotation those seams call: eager
12308// mode runs the closure verbatim; build mode wraps it in a stream capture on the section's
12309// device and records a child + its dependency edges. A token then assembles as ONE multi-device
12310// parent (children per section per layer), launched once per token — the launch-collapse the
12311// per-layer minis could not reach (routes-mini negative, 2026-08-21).
12312
12313/// One captured section: the child graph plus which parent node it became, and the CUDA
12314/// context it was captured under (exec memset updates need it).
12315struct TokenGraphChild {
12316    graph: cudarc::driver::CudaGraph,
12317    node: cudarc::driver::sys::CUgraphNode,
12318    ctx: cudarc::driver::sys::CUcontext,
12319}
12320
12321/// Exec-updatable fa geometry discovered in one attention rank child: the three partial-pool
12322/// memsets, the dcw fa kernel, and its combine — everything a bucket change touches. Node
12323/// handles address the parent's CLONED child graphs (the M1-probed update path).
12324struct TokenGraphFaSite {
12325    ctx: cudarc::driver::sys::CUcontext,
12326    memset_o: cudarc::driver::sys::CUgraphNode,
12327    memset_m: [cudarc::driver::sys::CUgraphNode; 2],
12328    fa: cudarc::driver::sys::CUgraphNode,
12329    combine: cudarc::driver::sys::CUgraphNode,
12330    window: usize,
12331    n_head: usize,
12332    n_head_kv: usize,
12333    head_dim: usize,
12334}
12335
12336pub struct TokenGraphBuilder {
12337    parent: cudarc::driver::sys::CUgraph,
12338    children: Vec<TokenGraphChild>,
12339    /// Nodes every NEXT section must depend on (the frontier): one node for serial flow,
12340    /// several while a parallel group is open.
12341    frontier: Vec<cudarc::driver::sys::CUgraphNode>,
12342    /// Detached sections: forked from the frontier at issue time, joined ONLY by the next
12343    /// non-group section (they never gate a parallel group merge — the SH1 shape).
12344    pending_detached: Vec<cudarc::driver::sys::CUgraphNode>,
12345    /// Open parallel group: sections issued under the same group id fork from the SAME
12346    /// predecessor set and merge into the frontier together when the group closes.
12347    group: Option<(
12348        u32,
12349        Vec<cudarc::driver::sys::CUgraphNode>,
12350        Vec<cudarc::driver::sys::CUgraphNode>,
12351    )>,
12352}
12353
12354// SAFETY: single decode thread; graph handles are process handles.
12355unsafe impl Send for TokenGraphBuilder {}
12356
12357impl TokenGraphBuilder {
12358    pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
12359        use cudarc::driver::sys;
12360        let mut parent: sys::CUgraph = std::ptr::null_mut();
12361        let r = unsafe { sys::cuGraphCreate(&mut parent, 0) };
12362        if r != sys::CUresult::CUDA_SUCCESS {
12363            return Err(format!("token graph create: {r:?}").into());
12364        }
12365        Ok(Self {
12366            parent,
12367            children: Vec::new(),
12368            frontier: Vec::new(),
12369            pending_detached: Vec::new(),
12370            group: None,
12371        })
12372    }
12373
12374    fn push_child(
12375        &mut self,
12376        graph: cudarc::driver::CudaGraph,
12377        parallel_group: Option<u32>,
12378        detached: bool,
12379        absorb: bool,
12380        ctx: cudarc::driver::sys::CUcontext,
12381    ) -> Result<(), Box<dyn std::error::Error>> {
12382        use cudarc::driver::sys;
12383        // Resolve the dependency set: serial sections depend on the current frontier; a
12384        // parallel-group section depends on the frontier AS OF the group opening; a
12385        // DETACHED section forks like a group member but joins only the next serial section.
12386        let deps: Vec<sys::CUgraphNode> = match (&mut self.group, parallel_group) {
12387            (Some((open, base, _)), Some(group)) if *open == group => base.clone(),
12388            (state, Some(group)) => {
12389                // opening a new group (closing any previous one first)
12390                if let Some((_, _, members)) = state.take() {
12391                    self.frontier = members;
12392                }
12393                let base = self.frontier.clone();
12394                *state = Some((group, base.clone(), Vec::new()));
12395                base
12396            }
12397            (state, None) if detached => match state.as_ref() {
12398                Some((_, base, _)) => base.clone(),
12399                None => self.frontier.clone(),
12400            },
12401            (state, None) => {
12402                if let Some((_, _, members)) = state.take() {
12403                    self.frontier = members;
12404                }
12405                let mut deps = self.frontier.clone();
12406                if absorb {
12407                    deps.append(&mut self.pending_detached);
12408                }
12409                deps
12410            }
12411        };
12412        let mut node: sys::CUgraphNode = std::ptr::null_mut();
12413        let r = unsafe {
12414            sys::cuGraphAddChildGraphNode(
12415                &mut node,
12416                self.parent,
12417                if deps.is_empty() {
12418                    std::ptr::null()
12419                } else {
12420                    deps.as_ptr()
12421                },
12422                deps.len(),
12423                graph.cu_graph(),
12424            )
12425        };
12426        if r != sys::CUresult::CUDA_SUCCESS {
12427            return Err(format!("token graph child: {r:?}").into());
12428        }
12429        match (&mut self.group, parallel_group, detached) {
12430            (_, None, true) => self.pending_detached.push(node),
12431            (Some((_, _, members)), Some(_), _) => members.push(node),
12432            _ => self.frontier = vec![node],
12433        }
12434        self.children.push(TokenGraphChild { graph, node, ctx });
12435        Ok(())
12436    }
12437
12438    pub fn finish(mut self) -> Result<TokenGraph, Box<dyn std::error::Error>> {
12439        use cudarc::driver::sys;
12440        if let Some((_, _, members)) = self.group.take() {
12441            self.frontier = members;
12442        }
12443        // Discover the fa sites BEFORE instantiate: the parent's cloned child graphs hold
12444        // the node handles the exec update path (M1) addresses.
12445        let mut fa_sites = Vec::new();
12446        for child in &self.children {
12447            if let Some(site) = discover_fa_site(child.node, child.ctx)? {
12448                fa_sites.push(site);
12449            }
12450        }
12451        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
12452        let r = unsafe { sys::cuGraphInstantiateWithFlags(&mut exec, self.parent, 0) };
12453        if r != sys::CUresult::CUDA_SUCCESS {
12454            return Err(format!("token graph instantiate: {r:?}").into());
12455        }
12456        Ok(TokenGraph {
12457            exec,
12458            parent: self.parent,
12459            _children: self.children,
12460            fa_sites,
12461        })
12462    }
12463}
12464
12465/// Walk one child graph; if it carries the attention-section signature (exactly three MEMSET
12466/// nodes chained memset->memset->memset->fa_kernel->combine_kernel), return its update site.
12467fn discover_fa_site(
12468    child_node: cudarc::driver::sys::CUgraphNode,
12469    ctx: cudarc::driver::sys::CUcontext,
12470) -> Result<Option<TokenGraphFaSite>, Box<dyn std::error::Error>> {
12471    use cudarc::driver::sys;
12472    fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
12473        if r == sys::CUresult::CUDA_SUCCESS {
12474            Ok(())
12475        } else {
12476            Err(format!("{what}: {r:?}").into())
12477        }
12478    }
12479    let mut graph: sys::CUgraph = std::ptr::null_mut();
12480    unsafe {
12481        cu_try(
12482            sys::cuGraphChildGraphNodeGetGraph(child_node, &mut graph),
12483            "fa-site child GetGraph",
12484        )?;
12485    }
12486    let mut count: usize = 0;
12487    unsafe {
12488        cu_try(
12489            sys::cuGraphGetNodes(graph, std::ptr::null_mut(), &mut count),
12490            "fa-site GetNodes(count)",
12491        )?;
12492    }
12493    let mut nodes: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); count];
12494    unsafe {
12495        cu_try(
12496            sys::cuGraphGetNodes(graph, nodes.as_mut_ptr(), &mut count),
12497            "fa-site GetNodes",
12498        )?;
12499    }
12500    nodes.truncate(count);
12501    let node_type =
12502        |node: sys::CUgraphNode| -> Result<sys::CUgraphNodeType, Box<dyn std::error::Error>> {
12503            let mut ty = sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_EMPTY;
12504            unsafe {
12505                cu_try(
12506                    sys::cuGraphNodeGetType(node, &mut ty),
12507                    "fa-site NodeGetType",
12508                )?;
12509            }
12510            Ok(ty)
12511        };
12512    let memsets: Vec<sys::CUgraphNode> = {
12513        let mut v = Vec::new();
12514        for &node in &nodes {
12515            if node_type(node)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_MEMSET {
12516                v.push(node);
12517            }
12518        }
12519        v
12520    };
12521    if memsets.len() != 3 {
12522        return Ok(None);
12523    }
12524    // Single-stream capture makes the chain linear: follow dependent edges from each memset.
12525    let dependents =
12526        |node: sys::CUgraphNode| -> Result<Vec<sys::CUgraphNode>, Box<dyn std::error::Error>> {
12527            let mut n: usize = 0;
12528            unsafe {
12529                cu_try(
12530                    sys::cuGraphNodeGetDependentNodes_v2(
12531                        node,
12532                        std::ptr::null_mut(),
12533                        std::ptr::null_mut(),
12534                        &mut n,
12535                    ),
12536                    "fa-site GetDependentNodes(count)",
12537                )?;
12538            }
12539            let mut v: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); n];
12540            unsafe {
12541                cu_try(
12542                    sys::cuGraphNodeGetDependentNodes_v2(
12543                        node,
12544                        v.as_mut_ptr(),
12545                        std::ptr::null_mut(),
12546                        &mut n,
12547                    ),
12548                    "fa-site GetDependentNodes",
12549                )?;
12550            }
12551            v.truncate(n);
12552            Ok(v)
12553        };
12554    // The LAST memset is the one whose direct dependent is a kernel (fa); the other two are
12555    // ordered among themselves but interchangeable for width updates.
12556    let mut fa: Option<sys::CUgraphNode> = None;
12557    let mut last_memset: Option<sys::CUgraphNode> = None;
12558    for &ms in &memsets {
12559        for dep in dependents(ms)? {
12560            if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
12561                fa = Some(dep);
12562                last_memset = Some(ms);
12563            }
12564        }
12565    }
12566    let (Some(fa), Some(_last)) = (fa, last_memset) else {
12567        return Ok(None);
12568    };
12569    let mut combine: Option<sys::CUgraphNode> = None;
12570    for dep in dependents(fa)? {
12571        if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
12572            combine = Some(dep);
12573        }
12574    }
12575    let Some(combine) = combine else {
12576        return Ok(None);
12577    };
12578    // Read the fa launch geometry from its baked args (arg order pinned by fa_decode_dcw):
12579    // 6=hd 7=nh 8=nhkv 11=win 13=nsp 14=ski.
12580    let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
12581    unsafe {
12582        cu_try(
12583            sys::cuGraphKernelNodeGetParams_v2(fa, &mut params),
12584            "fa-site KernelNodeGetParams",
12585        )?;
12586    }
12587    let arg_i32 =
12588        |slot: usize| -> i32 { unsafe { *(*params.kernelParams.add(slot) as *const i32) } };
12589    let (hd, nh, nhkv, win) = (arg_i32(6), arg_i32(7), arg_i32(8), arg_i32(11));
12590    // Identify the o-partial memset (hd x wider than the m/l pair).
12591    let width_of = |node: sys::CUgraphNode| -> Result<usize, Box<dyn std::error::Error>> {
12592        let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
12593        unsafe {
12594            cu_try(
12595                sys::cuGraphMemsetNodeGetParams(node, &mut mp),
12596                "fa-site MemsetNodeGetParams",
12597            )?;
12598        }
12599        Ok(mp.width)
12600    };
12601    let mut widest = memsets[0];
12602    for &ms in &memsets[1..] {
12603        if width_of(ms)? > width_of(widest)? {
12604            widest = ms;
12605        }
12606    }
12607    let memset_m: Vec<sys::CUgraphNode> =
12608        memsets.iter().copied().filter(|&m| m != widest).collect();
12609    Ok(Some(TokenGraphFaSite {
12610        ctx,
12611        memset_o: widest,
12612        memset_m: [memset_m[0], memset_m[1]],
12613        fa,
12614        combine,
12615        window: win as usize,
12616        n_head: nh as usize,
12617        n_head_kv: nhkv as usize,
12618        head_dim: hd as usize,
12619    }))
12620}
12621
12622pub struct TokenGraph {
12623    exec: cudarc::driver::sys::CUgraphExec,
12624    parent: cudarc::driver::sys::CUgraph,
12625    _children: Vec<TokenGraphChild>,
12626    fa_sites: Vec<TokenGraphFaSite>,
12627}
12628
12629unsafe impl Send for TokenGraph {}
12630
12631impl TokenGraph {
12632    /// Retarget every fa site to a new bucket via exec param updates (M1 path) — replaces the
12633    /// per-bucket whole-graph rebuild (~55ms) with ~450 node updates (~1ms). Per site the
12634    /// bucket caps at the layer window; nsp/ski/gridDimY and the partial-pool memset widths
12635    /// move together so the exec always matches what a fresh build at `bucket` would bake.
12636    pub fn retarget_bucket(&mut self, bucket: usize) -> Result<(), Box<dyn std::error::Error>> {
12637        use cudarc::driver::sys;
12638        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
12639            if r == sys::CUresult::CUDA_SUCCESS {
12640                Ok(())
12641            } else {
12642                Err(format!("{what}: {r:?}").into())
12643            }
12644        }
12645        for site in &self.fa_sites {
12646            let layer_bucket = if site.window > 0 {
12647                bucket.min(site.window)
12648            } else {
12649                bucket
12650            };
12651            let sp = crate::fa_split_keys(layer_bucket, site.n_head_kv);
12652            let nsp = layer_bucket.div_ceil(sp).max(1);
12653            // fa kernel: nsp (slot 13), ski (slot 14), gridDimY = nsp.
12654            let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
12655            unsafe {
12656                cu_try(
12657                    sys::cuGraphKernelNodeGetParams_v2(site.fa, &mut params),
12658                    "retarget fa GetParams",
12659                )?;
12660                *(*params.kernelParams.add(13) as *mut i32) = nsp as i32;
12661                *(*params.kernelParams.add(14) as *mut i32) = sp as i32;
12662                params.gridDimY = nsp as u32;
12663                cu_try(
12664                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.fa, &params),
12665                    "retarget fa SetParams",
12666                )?;
12667            }
12668            // combine: nsp (slot 6).
12669            let mut cparams: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
12670            unsafe {
12671                cu_try(
12672                    sys::cuGraphKernelNodeGetParams_v2(site.combine, &mut cparams),
12673                    "retarget combine GetParams",
12674                )?;
12675                *(*cparams.kernelParams.add(6) as *mut i32) = nsp as i32;
12676                cu_try(
12677                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.combine, &cparams),
12678                    "retarget combine SetParams",
12679                )?;
12680            }
12681            // partial-pool memsets: o = nh*nsp*hd elements, m/l = nh*nsp.
12682            let set_width =
12683                |node: sys::CUgraphNode, width: usize| -> Result<(), Box<dyn std::error::Error>> {
12684                    let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
12685                    unsafe {
12686                        cu_try(
12687                            sys::cuGraphMemsetNodeGetParams(node, &mut mp),
12688                            "retarget memset GetParams",
12689                        )?;
12690                    }
12691                    mp.width = width;
12692                    unsafe {
12693                        cu_try(
12694                            sys::cuGraphExecMemsetNodeSetParams(self.exec, node, &mp, site.ctx),
12695                            "retarget memset SetParams",
12696                        )?;
12697                    }
12698                    Ok(())
12699                };
12700            set_width(site.memset_o, site.n_head * nsp * site.head_dim)?;
12701            set_width(site.memset_m[0], site.n_head * nsp)?;
12702            set_width(site.memset_m[1], site.n_head * nsp)?;
12703        }
12704        Ok(())
12705    }
12706
12707    pub fn launch(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
12708        use cudarc::driver::sys;
12709        let _main = e.gpu.enter_main()?;
12710        let r = unsafe { sys::cuGraphLaunch(self.exec, e.stream().cu_stream() as sys::CUstream) };
12711        if r != sys::CUresult::CUDA_SUCCESS {
12712            return Err(format!("token graph launch: {r:?}").into());
12713        }
12714        Ok(())
12715    }
12716}
12717
12718impl Drop for TokenGraph {
12719    fn drop(&mut self) {
12720        unsafe {
12721            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
12722            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
12723        }
12724    }
12725}
12726
12727std::thread_local! {
12728    static TOKEN_GRAPH_BUILDER: std::cell::RefCell<Option<TokenGraphBuilder>> =
12729        const { std::cell::RefCell::new(None) };
12730}
12731
12732/// Arm the thread-local builder (build mode) — the next `graph_section` calls capture.
12733pub fn token_graph_build_begin() -> Result<(), Box<dyn std::error::Error>> {
12734    let builder = TokenGraphBuilder::new()?;
12735    TOKEN_GRAPH_BUILDER.with(|cell| *cell.borrow_mut() = Some(builder));
12736    Ok(())
12737}
12738
12739/// Take the finished parent (ends build mode).
12740pub fn token_graph_build_finish() -> Result<TokenGraph, Box<dyn std::error::Error>> {
12741    let builder = TOKEN_GRAPH_BUILDER
12742        .with(|cell| cell.borrow_mut().take())
12743        .ok_or("token graph build was not begun")?;
12744    builder.finish()
12745}
12746
12747/// True while the thread-local builder is armed.
12748pub fn token_graph_building() -> bool {
12749    TOKEN_GRAPH_BUILDER.with(|cell| cell.borrow().is_some())
12750}
12751
12752/// The section annotation: eager mode runs the closure verbatim; build mode wraps it in a
12753/// stream capture on `engine`'s stream and records the child. Sections sharing a
12754/// `parallel_group` id fork from the same predecessor set and merge together. The closure
12755/// must be capture-safe (raw copies at cross-context seams, no host syncs, no events).
12756pub fn graph_section<F>(
12757    engine: &Engine,
12758    parallel_group: Option<u32>,
12759    f: F,
12760) -> Result<(), Box<dyn std::error::Error>>
12761where
12762    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12763{
12764    graph_section_opts(engine, parallel_group, false, false, f)
12765}
12766
12767/// Serial section that ALSO joins every pending detached section (the SH1 consumer shape).
12768pub fn graph_section_absorbing<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
12769where
12770    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12771{
12772    graph_section_opts(engine, None, false, true, f)
12773}
12774
12775/// `graph_section` with the DETACHED shape: forks from the current frontier (or the open
12776/// group base) and is joined only by the next serial section — never gates a group merge.
12777pub fn graph_section_detached<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
12778where
12779    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12780{
12781    graph_section_opts(engine, None, true, false, f)
12782}
12783
12784pub fn graph_section_opts<F>(
12785    engine: &Engine,
12786    parallel_group: Option<u32>,
12787    detached: bool,
12788    absorb: bool,
12789    f: F,
12790) -> Result<(), Box<dyn std::error::Error>>
12791where
12792    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12793{
12794    let building = token_graph_building();
12795    if !building {
12796        let mut f = f;
12797        return f();
12798    }
12799    let (child, ctx) = {
12800        let _main = engine.gpu.enter_main()?;
12801        let mut ctx: cudarc::driver::sys::CUcontext = std::ptr::null_mut();
12802        let r = unsafe { cudarc::driver::sys::cuCtxGetCurrent(&mut ctx) };
12803        if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
12804            return Err(format!("graph section ctx query: {r:?}").into());
12805        }
12806        let mut f = f;
12807        // NO WARMUP RUNS: section bodies carry device side effects (dcw appends, counter
12808        // incs) that a warmup would really execute — the len_d-drift crash of 2026-08-21.
12809        let (child, _retained) = engine.capture_graph_retained_nowarm(|_| f())?;
12810        (child, ctx)
12811    };
12812    TOKEN_GRAPH_BUILDER.with(|cell| {
12813        cell.borrow_mut()
12814            .as_mut()
12815            .expect("builder checked above")
12816            .push_child(child, parallel_group, detached, absorb, ctx)
12817    })
12818}