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, 16_384, 262_144, 16_777_216];
16const STEP_GROUPED_FP8_EXPERTS: usize = 288;
17const STEP_GROUPED_FP8_TOP_K: usize = 8;
18const STEP_GROUPED_FP8_WIDTH: usize = 1280;
19
20fn validate_step_expert_activation_limit(limit: Option<f32>) -> Result<(), String> {
21    if let Some(limit) = limit {
22        if !limit.is_finite() || limit <= 0.0 {
23            return Err(format!(
24                "Step routed-expert activation limit must be positive and finite, got {limit}"
25            ));
26        }
27    }
28    Ok(())
29}
30
31/// Host-canonical Step routed-expert SwiGLU operation.
32///
33/// Step's final routed layers clamp the linear arm symmetrically and the SiLU arm only above.
34/// Keeping this scalar order explicit also defines the device-host-exact CUDA gate.
35/// Raw stream-ordered device copy for capture-safe cross-context seams (cudarc's slice-use
36/// tracking creates capture-illegal dependencies there). Pointers must be pre-cached with
37/// their owners' streams; bytes flow identically to the tracked copy.
38/// MEMRA_OPROJ_DIRECT=1 (o-proj direct join, default OFF until gated): peer ranks write
39/// their fused O partial OVER P2P into a root-resident buffer (UVA kernel stores), and the
40/// model engine adds the two partials itself — the root stream leaves the join entirely
41/// (no peer pull copy, no root add, no second event hop, no final 16KB ownership copy).
42/// Reduction order and kernel programs are unchanged, so the row is BIT-IDENTICAL.
43/// MEMRA_MOE_DIRECT=1 (moe direct join, default OFF until gated): the o-proj direct-join
44/// recipe on the expert combine — peer ranks' accumulators live root-side (the axpy twin
45/// register-accumulates and stores ONCE, so the P2P cost is a single 16KB store pass), and
46/// the model engine adds the two shard rows itself. Operand order matches root's add:
47/// BIT-IDENTICAL.
48/// MEMRA_ROUTES_PRESTAGE=1 (default OFF until gated): stage the shared layer input to
49/// every rank and quantize it BEFORE the router runs — neither depends on the selection,
50/// so the rank streams' pull+quantize overlaps dev0's router gemv+topk instead of chaining
51/// behind it (the router->quantize and axpy->add gap edges). Same copies, same quantize
52/// kernel, same operands: BIT-IDENTICAL.
53pub(crate) fn routes_prestage_on() -> bool {
54    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
55    *ON.get_or_init(|| std::env::var("MEMRA_ROUTES_PRESTAGE").as_deref() == Ok("1"))
56}
57
58/// MEMRA_FENCE_MEMOPS=1 (default OFF until gated): the moe direct join's two event
59/// fences become cuStreamWriteValue32/cuStreamWaitValue32 doorbells — hardware stream
60/// memops with lower signal->wake latency than cross-device cuStreamWaitEvent. Ordering:
61/// PCIe posted writes from one device arrive in order, so rank1's accumulator stores are
62/// visible before its flag write lands; e's GEQ wait then covers them. Falls back to
63/// events when the device rejects stream memops. Scheduling-only: BIT-IDENTICAL values.
64/// MEMRA_LEN_MIRROR_LAZY=1 (default OFF until gated): skip redundant per-layer 4B len
65/// htods — the local device mirror is unread in TP decode, and under FUSE_ROPE_APPEND the
66/// fused append's atomicInc owns the rank counters. Every one of those tiny copies is a
67/// compute->copy engine turnaround in the middle of the layer stream.
68/// MEMRA_RANK0_MERGE=1 (default OFF until gated): same-device rank0 rides e's stream via
69/// the runtime redirect — see decode_step_h.
70/// MEMRA_OPROJ_TAIL=1 (default OFF until gated): the o-proj direct-join add is DEFERRED —
71/// the finish arm keeps its waits, stores the two partial pointers here, and the residual
72/// add_rms_norm consumer composes mixed = a0+a1 in-register (join_add_rms_norm, verbatim
73/// program: BIT-IDENTICAL). The returned `mixed` buffer is UNWRITTEN in this mode; its
74/// only live consumer is the residual_norm_ffn seam, which takes the handoff.
75pub(crate) fn oproj_tail_on() -> bool {
76    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
77    *ON.get_or_init(|| std::env::var("MEMRA_OPROJ_TAIL").as_deref() == Ok("1"))
78}
79thread_local! {
80    static OPROJ_TAIL_PENDING: std::cell::Cell<Option<(u64, u64)>> =
81        const { std::cell::Cell::new(None) };
82}
83thread_local! {
84    /// The deferral is legal ONLY under callers whose walk flows into
85    /// residual_norm_ffn (decode_step_h / decode_step_chain arm this) — the verify
86    /// prefill reaches the same finish and would consume unwritten `mixed` otherwise
87    /// (M2-MISMATCH receipt: prefill argmax corrupted while decode stayed exact).
88    static OPROJ_TAIL_ELIGIBLE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
89}
90/// RAII eligibility scope for the o-proj tail deferral.
91pub(crate) struct OprojTailScope(());
92pub(crate) fn oproj_tail_scope() -> OprojTailScope {
93    OPROJ_TAIL_ELIGIBLE.with(|c| c.set(true));
94    OprojTailScope(())
95}
96impl Drop for OprojTailScope {
97    fn drop(&mut self) {
98        OPROJ_TAIL_ELIGIBLE.with(|c| c.set(false));
99        // A leftover un-consumed handoff must never leak across calls.
100        OPROJ_TAIL_PENDING.with(|c| c.set(None));
101    }
102}
103thread_local! {
104    /// T-COLUMN verify select: the verify driver sets the column before each per-column
105    /// attention call; decode_v2_input_qkv takes it (once) and selects from the slabs.
106    static VERIFY_TCOL: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
107}
108pub(crate) fn set_verify_tcol(c: Option<usize>) {
109    VERIFY_TCOL.with(|x| x.set(c));
110}
111pub(crate) fn take_verify_tcol() -> Option<usize> {
112    VERIFY_TCOL.with(|x| x.take())
113}
114
115/// MEMRA_TCOL_OPROJ=1 (spec verify): defer each column's o_proj out of the per-column
116/// walk — the finish seam stashes the column's `gated` rows instead of running the
117/// per-column finish choreography (rank events, P2P join, engine handoff), and one
118/// weight-amortized b4_tcol per rank + one elementwise join produce every column's
119/// `mixed` afterwards. Bit-exact per column: the tcol kernel is the t=1 b4 program per
120/// column, and the slab join adds the same operand values elementwise.
121pub(crate) fn tcol_oproj_on() -> bool {
122    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
123    *ON.get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ").as_deref() == Ok("1"))
124}
125thread_local! {
126    /// The verify driver arms the column before each per-column attention call; the
127    /// finish seam takes it (once). Stashed=true reports the defer actually happened
128    /// (the seam falls back to the normal finish when the config is ineligible).
129    static TCOL_OPROJ_DEFER: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
130    static TCOL_OPROJ_STASHED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
131}
132pub(crate) fn set_tcol_oproj_defer(c: Option<usize>) {
133    TCOL_OPROJ_DEFER.with(|x| x.set(c));
134}
135pub(crate) fn take_tcol_oproj_defer() -> Option<usize> {
136    TCOL_OPROJ_DEFER.with(|x| x.take())
137}
138pub(crate) fn set_tcol_oproj_stashed() {
139    TCOL_OPROJ_STASHED.with(|x| x.set(true));
140}
141pub(crate) fn take_tcol_oproj_stashed() -> bool {
142    TCOL_OPROJ_STASHED.with(|x| x.replace(false))
143}
144
145pub(crate) fn oproj_tail_eligible() -> bool {
146    OPROJ_TAIL_ELIGIBLE.with(|c| c.get())
147}
148pub(crate) fn take_oproj_tail() -> Option<(u64, u64)> {
149    OPROJ_TAIL_PENDING.with(|c| c.take())
150}
151pub(crate) fn set_oproj_tail(v: (u64, u64)) {
152    OPROJ_TAIL_PENDING.with(|c| c.set(Some(v)));
153}
154
155pub(crate) fn rank0_merge_on() -> bool {
156    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
157    *ON.get_or_init(|| std::env::var("MEMRA_RANK0_MERGE").as_deref() == Ok("1"))
158}
159
160pub(crate) fn len_mirror_lazy_on() -> bool {
161    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
162    *ON.get_or_init(|| std::env::var("MEMRA_LEN_MIRROR_LAZY").as_deref() == Ok("1"))
163}
164
165pub(crate) fn fence_memops_on() -> bool {
166    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
167    *ON.get_or_init(|| std::env::var("MEMRA_FENCE_MEMOPS").as_deref() == Ok("1"))
168}
169
170pub(crate) fn moe_direct_on() -> bool {
171    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
172    *ON.get_or_init(|| std::env::var("MEMRA_MOE_DIRECT").as_deref() == Ok("1"))
173}
174
175/// MEMRA_SEL_DOWN8=1: fuse the NVFP4 down sweep with the route-weight combine and run one
176/// warp per routed slot (the q8 `down8 w8` occupancy arm). Bit-identical; default OFF until
177/// receipted on this bank family.
178/// MEMRA_SEL_MIRROR=1: the per-rank routed-selection pull runs as ONE `moe_sel_w_mirror`
179/// launch instead of two 32-byte D2D copies, and when every consuming rank shares e's device
180/// the intermediate e-context staging pair is skipped entirely (the caller's sel/route_w rows
181/// are process-persistent, so the ranks read them directly). Bit-identical: same bytes, one
182/// fewer hop. Refused under the graph door, whose captured copies need the fixed staging
183/// addresses. Default OFF until receipted.
184/// MEMRA_FENCE_RANK1=1: the peer rank rings a doorbell in ROOT memory with a kernel store
185/// (`memra_ring_flag`) and the model engine waits it with a SAME-DEVICE stream memop, instead
186/// of waiting a cross-device event. Completes the half the memops receipt left open (peer
187/// memops are rejected; peer kernel stores are the direct-join mechanism). Ordering only —
188/// values are untouched. Requires MEMRA_FENCE_MEMOPS=1 (it owns the flag allocation).
189pub(crate) fn fence_rank1_on() -> bool {
190    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
191    *ON.get_or_init(|| std::env::var("MEMRA_FENCE_RANK1").as_deref() == Ok("1"))
192}
193
194/// MEMRA_SPEC_FA2=1 (the DSpark verify lesson): the T=2 verify walk defers each column's
195/// ATTENTION CORE — the dcw arm appends the column's K/V and stashes its post-rope q and
196/// gate rows, then ONE fa_decode_dcw2 per rank walks the KV stream once for both columns
197/// (per-row causal bounds; bit-identical per row under the equal-partition guard), the
198/// per-row combine writes both gated rows, and the o_proj join runs on the TCOL slabs.
199pub(crate) fn spec_fa2_on() -> bool {
200    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
201    *ON.get_or_init(|| std::env::var("MEMRA_SPEC_FA2").as_deref() == Ok("1"))
202}
203thread_local! {
204    /// The verify driver arms the column before each per-column attention call; the dcw
205    /// arm takes it (once) and stashes q/gate instead of running fa+finish.
206    static SPEC_FA2_DEFER: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
207    static SPEC_FA2_STASHED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
208}
209pub(crate) fn set_spec_fa2_defer(c: Option<usize>) {
210    SPEC_FA2_DEFER.with(|x| x.set(c));
211}
212pub(crate) fn take_spec_fa2_defer() -> Option<usize> {
213    SPEC_FA2_DEFER.with(|x| x.take())
214}
215pub(crate) fn set_spec_fa2_stashed() {
216    SPEC_FA2_STASHED.with(|x| x.set(true));
217}
218pub(crate) fn take_spec_fa2_stashed() -> bool {
219    SPEC_FA2_STASHED.with(|x| x.replace(false))
220}
221
222pub(crate) fn sel_mirror_on() -> bool {
223    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
224    *ON.get_or_init(|| std::env::var("MEMRA_SEL_MIRROR").as_deref() == Ok("1"))
225}
226
227/// MEMRA_STEP_NVFP4_EP2=1: whole-expert (expert-parallel) NVFP4 banks at 2 ranks — expert e
228/// lives ENTIRE on rank (e & 1) at bank slot (e >> 1), replacing the TP column/row shards
229/// (same total VRAM; both sets cannot coexist). Decode rides owner-guarded full-width
230/// sweeps with per-rank slot-ordered partial sums; the cross-rank join is unchanged.
231/// NUMERIC-CLASS door (the slot chain regroups per rank): run-gen argmax gate + battery +
232/// fresh tape, the DEV_ROUTES acceptance class.
233pub(crate) fn step_nvfp4_ep2_on() -> bool {
234    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
235    *ON.get_or_init(|| std::env::var("MEMRA_STEP_NVFP4_EP2").as_deref() == Ok("1"))
236}
237
238pub(crate) fn sel_down8_on() -> bool {
239    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
240    *ON.get_or_init(|| std::env::var("MEMRA_SEL_DOWN8").as_deref() == Ok("1"))
241}
242
243pub(crate) fn oproj_direct_on() -> bool {
244    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
245    *ON.get_or_init(|| std::env::var("MEMRA_OPROJ_DIRECT").as_deref() == Ok("1"))
246}
247
248pub(crate) fn raw_copy_bytes(
249    dst: u64,
250    src: u64,
251    bytes: usize,
252    engine: &Engine,
253) -> Result<(), Box<dyn std::error::Error>> {
254    use cudarc::driver::sys;
255    let r = unsafe {
256        sys::cuMemcpyAsync(
257            dst as sys::CUdeviceptr,
258            src as sys::CUdeviceptr,
259            bytes,
260            engine.stream().cu_stream() as sys::CUstream,
261        )
262    };
263    if r == sys::CUresult::CUDA_SUCCESS {
264        Ok(())
265    } else {
266        // MEMRA_RAW_COPY_TRACE=1: a raw D2D failure carries no call site by itself, and
267        // every slab-width bug in the t-row family surfaces here. Operands + backtrace.
268        if std::env::var("MEMRA_RAW_COPY_TRACE").as_deref() == Ok("1") {
269            eprintln!(
270                "[raw-copy-fail] dst={dst:#x} src={src:#x} bytes={bytes} {r:?}\n{}",
271                std::backtrace::Backtrace::force_capture()
272            );
273        }
274        Err(format!("raw_copy_bytes: {r:?} bytes={bytes} dst={dst:#x} src={src:#x}").into())
275    }
276}
277
278pub fn step_expert_activation_host(gate: f32, up: f32, limit: Option<f32>) -> f32 {
279    let silu = gate / (1.0 + (-gate).exp());
280    match limit {
281        Some(limit) => silu.min(limit) * up.clamp(-limit, limit),
282        None => silu * up,
283    }
284}
285
286#[derive(Debug, Clone, PartialEq, Eq)]
287struct ExpertOwnerRoutes {
288    rank: usize,
289    selected: Vec<usize>,
290    token_rows: Vec<usize>,
291    global_pairs: Vec<usize>,
292}
293
294fn partition_expert_owner_routes(
295    expert_count: usize,
296    ranks: usize,
297    tokens: usize,
298    experts_per_token: usize,
299    selected: &[usize],
300) -> Result<Vec<ExpertOwnerRoutes>, String> {
301    if expert_count == 0
302        || ranks == 0
303        || tokens == 0
304        || experts_per_token == 0
305        || expert_count % ranks != 0
306    {
307        return Err(format!(
308            "invalid expert-owner route geometry experts={expert_count} ranks={ranks} \
309             tokens={tokens} experts_per_token={experts_per_token}"
310        ));
311    }
312    let pairs = tokens
313        .checked_mul(experts_per_token)
314        .ok_or("expert-owner route count overflow")?;
315    if selected.len() != pairs {
316        return Err(format!(
317            "expert-owner routes {} != {tokens}x{experts_per_token} ({pairs})",
318            selected.len()
319        ));
320    }
321    let per_rank = expert_count / ranks;
322    let mut owners = (0..ranks)
323        .map(|rank| ExpertOwnerRoutes {
324            rank,
325            selected: Vec::new(),
326            token_rows: Vec::new(),
327            global_pairs: Vec::new(),
328        })
329        .collect::<Vec<_>>();
330    for (pair, &expert) in selected.iter().enumerate() {
331        if expert >= expert_count {
332            return Err(format!(
333                "expert-owner route {pair} selects expert {expert} outside 0..{expert_count}"
334            ));
335        }
336        let rank = expert / per_rank;
337        owners[rank].selected.push(expert - rank * per_rank);
338        owners[rank].token_rows.push(pair / experts_per_token);
339        owners[rank].global_pairs.push(pair);
340    }
341    Ok(owners)
342}
343
344fn validate_step_grouped_owner_routes(
345    expert_count: usize,
346    tokens: usize,
347    selected: &[usize],
348) -> Result<usize, String> {
349    if expert_count != STEP_GROUPED_FP8_EXPERTS || tokens == 0 {
350        return Err(format!(
351            "official Step owner-grouped FP8 requires {} experts and nonzero tokens, got \
352             experts={expert_count} tokens={tokens}",
353            STEP_GROUPED_FP8_EXPERTS
354        ));
355    }
356    let pairs = tokens
357        .checked_mul(STEP_GROUPED_FP8_TOP_K)
358        .ok_or("official Step owner-grouped FP8 route count overflow")?;
359    if selected.len() != pairs {
360        return Err(format!(
361            "official Step owner-grouped FP8 routes {} != {tokens}x{} ({pairs})",
362            selected.len(),
363            STEP_GROUPED_FP8_TOP_K,
364        ));
365    }
366    for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
367        let mut unique = routes.to_vec();
368        unique.sort_unstable();
369        unique.dedup();
370        if unique.len() != STEP_GROUPED_FP8_TOP_K {
371            return Err(format!(
372                "official Step owner-grouped FP8 token {token} routes are not top-8 unique: \
373                 {routes:?}"
374            ));
375        }
376    }
377    Ok(pairs)
378}
379
380#[derive(Debug, Clone, Copy, PartialEq, Eq)]
381struct WeightedRouteCombineShape {
382    pairs: usize,
383    max_pairs: usize,
384}
385
386fn validate_weighted_route_combine(
387    width: usize,
388    experts_per_token: usize,
389    max_tokens: usize,
390    tokens: usize,
391    owner_global_pairs: &[&[usize]],
392    route_weights: &[f32],
393) -> Result<WeightedRouteCombineShape, String> {
394    if width == 0
395        || experts_per_token == 0
396        || max_tokens == 0
397        || tokens == 0
398        || tokens > max_tokens
399        || width > i32::MAX as usize
400        || experts_per_token > i32::MAX as usize
401        || tokens > i32::MAX as usize
402    {
403        return Err(format!(
404            "invalid weighted route combine geometry width={width} experts_per_token=\
405             {experts_per_token} tokens={tokens}/{max_tokens}"
406        ));
407    }
408    let pairs = tokens
409        .checked_mul(experts_per_token)
410        .ok_or("weighted route combine pair count overflow")?;
411    let max_pairs = max_tokens
412        .checked_mul(experts_per_token)
413        .ok_or("weighted route combine capacity overflow")?;
414    if route_weights.len() != pairs || !route_weights.iter().all(|weight| weight.is_finite()) {
415        return Err(format!(
416            "weighted route combine weights {} != pairs {pairs} or contain a non-finite value",
417            route_weights.len()
418        ));
419    }
420    let mut seen = vec![false; pairs];
421    let mut observed = 0usize;
422    for pairs_for_owner in owner_global_pairs {
423        observed = observed
424            .checked_add(pairs_for_owner.len())
425            .ok_or("weighted route combine observed pair count overflow")?;
426        for &pair in *pairs_for_owner {
427            if pair >= pairs || std::mem::replace(&mut seen[pair], true) {
428                return Err(format!(
429                    "weighted route combine pair {pair} is outside 0..{pairs} or duplicated"
430                ));
431            }
432        }
433    }
434    if observed != pairs || seen.iter().any(|present| !present) {
435        return Err(format!(
436            "weighted route combine owner schedules cover {observed} of {pairs} canonical pairs"
437        ));
438    }
439    Ok(WeightedRouteCombineShape { pairs, max_pairs })
440}
441
442fn cache_rank_rows(
443    rows: &[u8],
444    tokens: usize,
445    local_token_bytes: usize,
446    ranks: usize,
447    rank: usize,
448) -> Result<Vec<u8>, String> {
449    if ranks == 0 || rank >= ranks {
450        return Err(format!(
451            "TP cache rank {rank} is outside a {ranks}-rank layout"
452        ));
453    }
454    let global_token_bytes = local_token_bytes
455        .checked_mul(ranks)
456        .ok_or("TP cache global token-byte overflow")?;
457    let expected = tokens
458        .checked_mul(global_token_bytes)
459        .ok_or("TP cache row-byte overflow")?;
460    if rows.len() != expected {
461        return Err(format!(
462            "TP cache rows contain {} bytes, expected {tokens}x{global_token_bytes}={expected}",
463            rows.len()
464        ));
465    }
466    let mut shard = Vec::with_capacity(tokens * local_token_bytes);
467    for token in 0..tokens {
468        let start = token * global_token_bytes + rank * local_token_bytes;
469        shard.extend_from_slice(&rows[start..start + local_token_bytes]);
470    }
471    Ok(shard)
472}
473
474fn parse_step_tp_native_p2p(value: Option<&str>) -> Result<bool, String> {
475    match value {
476        None | Some("") | Some("0") => Ok(false),
477        Some("1") => Ok(true),
478        Some(value) => Err(format!(
479            "MEMRA_STEP_TP_NATIVE_P2P={value:?} is invalid; expected 0 or 1"
480        )),
481    }
482}
483
484pub fn step_tp_native_p2p_enabled() -> Result<bool, String> {
485    parse_step_tp_native_p2p(std::env::var("MEMRA_STEP_TP_NATIVE_P2P").ok().as_deref())
486}
487
488fn parse_step_tp_bulk_p2p(value: Option<&str>) -> Result<bool, String> {
489    match value {
490        None | Some("") | Some("0") => Ok(false),
491        Some("1") => Ok(true),
492        Some(value) => Err(format!(
493            "MEMRA_STEP_TP_BULK_P2P={value:?} is invalid; expected 0 or 1"
494        )),
495    }
496}
497
498pub fn step_tp_bulk_p2p_enabled() -> Result<bool, String> {
499    parse_step_tp_bulk_p2p(std::env::var("MEMRA_STEP_TP_BULK_P2P").ok().as_deref())
500}
501
502fn parse_step_ep_device_arithmetic(value: Option<&str>) -> Result<bool, String> {
503    match value {
504        None | Some("") | Some("0") => Ok(false),
505        Some("1") => Ok(true),
506        Some(value) => Err(format!(
507            "MEMRA_STEP_EP_DEVICE_ARITHMETIC={value:?} is invalid; expected 0 or 1"
508        )),
509    }
510}
511
512fn parse_step_nvfp4_dev_routes(value: Option<&str>) -> Result<bool, String> {
513    match value {
514        None | Some("") | Some("0") => Ok(false),
515        Some("1") => Ok(true),
516        Some(value) => Err(format!(
517            "MEMRA_STEP_NVFP4_DEV_ROUTES={value:?} is invalid; expected 0 or 1"
518        )),
519    }
520}
521
522/// Opt-in door for the device-resident NVFP4 TP routed-expert decode program. Default OFF; the
523/// host-canonical program remains the oracle until the device path carries its own gates.
524pub fn step_nvfp4_dev_routes_enabled() -> Result<bool, String> {
525    parse_step_nvfp4_dev_routes(std::env::var("MEMRA_STEP_NVFP4_DEV_ROUTES").ok().as_deref())
526}
527
528pub fn step_ep_device_arithmetic_enabled() -> Result<bool, String> {
529    parse_step_ep_device_arithmetic(
530        std::env::var("MEMRA_STEP_EP_DEVICE_ARITHMETIC")
531            .ok()
532            .as_deref(),
533    )
534}
535
536fn parse_step_tp_f32_mirror(value: Option<&str>) -> Result<bool, String> {
537    match value {
538        None | Some("") | Some("0") => Ok(false),
539        Some("1") => Ok(true),
540        Some(value) => Err(format!(
541            "MEMRA_STEP_TP_F32_MIRROR={value:?} is invalid; expected 0 or 1"
542        )),
543    }
544}
545
546pub fn step_tp_f32_mirror_enabled() -> Result<bool, String> {
547    parse_step_tp_f32_mirror(std::env::var("MEMRA_STEP_TP_F32_MIRROR").ok().as_deref())
548}
549
550fn parse_step_tp_decode_v2(value: Option<&str>) -> Result<bool, String> {
551    match value {
552        None | Some("") | Some("0") => Ok(false),
553        Some("1") => Ok(true),
554        Some(value) => Err(format!(
555            "MEMRA_STEP_TP_DECODE_V2={value:?} is invalid; expected 0 or 1"
556        )),
557    }
558}
559
560/// The v2 rank-local Step decode-attention driver: persistent workspaces, evented cross-stream
561/// ordering, and a root-device O reduction — same kernels, values, and canonical reduction order
562/// as the v1 driver (it requires the F32 mirror so no per-call weight expansion exists on either
563/// side of the comparison).
564pub fn step_tp_decode_v2_enabled() -> Result<bool, String> {
565    parse_step_tp_decode_v2(std::env::var("MEMRA_STEP_TP_DECODE_V2").ok().as_deref())
566}
567
568fn parse_step_tp_qkv_fused(value: Option<&str>) -> Result<bool, String> {
569    match value {
570        None | Some("") | Some("0") => Ok(false),
571        Some("1") => Ok(true),
572        Some(value) => Err(format!(
573            "MEMRA_STEP_TP_QKV_FUSED={value:?} is invalid; expected 0 or 1"
574        )),
575    }
576}
577
578fn parse_step_tp_dev_router(value: Option<&str>) -> Result<bool, String> {
579    match value {
580        None | Some("") | Some("0") => Ok(false),
581        Some("1") => Ok(true),
582        Some(value) => Err(format!(
583            "MEMRA_STEP_TP_DEV_ROUTER={value:?} is invalid; expected 0 or 1"
584        )),
585    }
586}
587
588/// Device-side sigmoid top-k routing for the TP device-IO expert program: the per-layer host
589/// logits readback (the last per-layer host sync) disappears. Selection tie-breaking may
590/// differ from the host router — NUMERIC-CLASS door, run-gen argmax gate + boot battery.
591pub fn step_tp_dev_router_enabled() -> Result<bool, String> {
592    parse_step_tp_dev_router(std::env::var("MEMRA_STEP_TP_DEV_ROUTER").ok().as_deref())
593}
594
595fn parse_step_tp_graph(value: Option<&str>) -> Result<bool, String> {
596    match value {
597        None | Some("") | Some("0") => Ok(false),
598        Some("1") => Ok(true),
599        Some(value) => Err(format!(
600            "MEMRA_STEP_TP_GRAPH={value:?} is invalid; expected 0 or 1"
601        )),
602    }
603}
604
605fn parse_step_tp_dcw(value: Option<&str>) -> Result<bool, String> {
606    match value {
607        None | Some("") | Some("0") => Ok(false),
608        Some("1") => Ok(true),
609        Some(value) => Err(format!(
610            "MEMRA_STEP_TP_DCW={value:?} is invalid; expected 0 or 1"
611        )),
612    }
613}
614
615/// Device-counter attention path (graph increment A run EAGERLY): append at len_d - base_d,
616/// inc_i32, fa over the counter-derived window — with bucket = the effective t_kv this is
617/// bit-identical to the host-row + kvmod path (the one-partition law), and it is the exact
618/// child content the capture wraps. Rebase tokens and sub-vec-floor contexts fall back.
619pub fn step_tp_dcw_enabled() -> Result<bool, String> {
620    parse_step_tp_dcw(std::env::var("MEMRA_STEP_TP_DCW").ok().as_deref())
621}
622
623/// CUDA-graph door for the shape-stable TP segments (first increment: the device-routed
624/// expert program — per-layer multi-device parents built from per-rank children, launched on
625/// the model engine's stream; zero per-token node updates). Mechanism proven by
626/// tp_graph_probe. VALUE-IDENTICAL: the graphs replay exactly the eager kernel/copy sequence.
627pub fn step_tp_graph_enabled() -> Result<bool, String> {
628    parse_step_tp_graph(std::env::var("MEMRA_STEP_TP_GRAPH").ok().as_deref())
629}
630
631/// Fused single-launch QKV projection inside the v2 decode driver — a NUMERIC-CLASS door
632/// (per-row deterministic tree reduce instead of the chunked cuBLASLt program), default OFF,
633/// gated by the run-gen argmax gate + boot battery like MEMRA_STEP_NVFP4_DEV_ROUTES.
634pub fn step_tp_qkv_fused_enabled() -> Result<bool, String> {
635    parse_step_tp_qkv_fused(std::env::var("MEMRA_STEP_TP_QKV_FUSED").ok().as_deref())
636}
637
638#[derive(Debug, Clone, PartialEq, Eq)]
639pub struct StepEpLayerSpec {
640    pub layer: usize,
641    pub devices: Vec<usize>,
642}
643
644pub type StepTpLayerSpec = StepEpLayerSpec;
645
646fn parse_step_layer_specs(
647    flag: &str,
648    value: Option<&str>,
649    allow_full_model: bool,
650) -> Result<Vec<StepEpLayerSpec>, String> {
651    let Some(value) = value else {
652        return Ok(Vec::new());
653    };
654    if value.is_empty() || value == "0" {
655        return Ok(Vec::new());
656    }
657
658    let mut specs = Vec::new();
659    for item in value.split(';') {
660        let (layers, devices) = item.split_once('@').ok_or_else(|| {
661            let layers = if allow_full_model {
662                "LAYER[-LAYER] or all"
663            } else {
664                "LAYER[-LAYER]"
665            };
666            format!("{flag} must be {layers}@DEVICE,DEVICE[;...]")
667        })?;
668        let (first, last) = if layers == "all" {
669            if !allow_full_model {
670                return Err(format!(
671                    "{flag} does not support the full-model shorthand; assign routed layers \
672                     explicitly"
673                ));
674            }
675            (0, STEP37_TRUNK_LAYERS - 1)
676        } else {
677            match layers.split_once('-') {
678                Some((first, last)) => {
679                    let first = first
680                        .parse::<usize>()
681                        .map_err(|_| format!("{flag} layer {first:?} is not an integer"))?;
682                    let last = last
683                        .parse::<usize>()
684                        .map_err(|_| format!("{flag} layer {last:?} is not an integer"))?;
685                    if first > last {
686                        return Err(format!("{flag} layer range {first}-{last} is reversed"));
687                    }
688                    if last - first + 1 > 128 {
689                        return Err(format!(
690                            "{flag} layer range {first}-{last} exceeds the 128-layer parser cap"
691                        ));
692                    }
693                    (first, last)
694                }
695                None => {
696                    let layer = layers
697                        .parse::<usize>()
698                        .map_err(|_| format!("{flag} layer {layers:?} is not an integer"))?;
699                    (layer, layer)
700                }
701            }
702        };
703        let devices = devices
704            .split(',')
705            .map(|device| {
706                device
707                    .parse::<usize>()
708                    .map_err(|_| format!("{flag} device {device:?} is not an integer"))
709            })
710            .collect::<Result<Vec<_>, _>>()?;
711        if !(2..=8).contains(&devices.len()) {
712            return Err(format!(
713                "{flag} requires 2..=8 devices, got {}",
714                devices.len()
715            ));
716        }
717        let mut unique = devices.clone();
718        unique.sort_unstable();
719        unique.dedup();
720        if unique.len() != devices.len() {
721            return Err(format!("{flag} devices must be distinct, got {devices:?}"));
722        }
723        for layer in first..=last {
724            if specs
725                .iter()
726                .any(|existing: &StepEpLayerSpec| existing.layer == layer)
727            {
728                return Err(format!("{flag} assigns layer {layer} more than once"));
729            }
730            specs.push(StepEpLayerSpec {
731                layer,
732                devices: devices.clone(),
733            });
734        }
735    }
736    Ok(specs)
737}
738
739pub fn parse_step_ep_layer_specs(value: Option<&str>) -> Result<Vec<StepEpLayerSpec>, String> {
740    parse_step_layer_specs("MEMRA_STEP_EP", value, false)
741}
742
743pub fn step_ep_layer_specs() -> Result<Vec<StepEpLayerSpec>, String> {
744    parse_step_ep_layer_specs(std::env::var("MEMRA_STEP_EP").ok().as_deref())
745}
746
747pub fn parse_step_tp_layer_specs(value: Option<&str>) -> Result<Vec<StepTpLayerSpec>, String> {
748    parse_step_layer_specs("MEMRA_STEP_TP", value, true)
749}
750
751pub fn step_tp_layer_specs() -> Result<Vec<StepTpLayerSpec>, String> {
752    parse_step_tp_layer_specs(std::env::var("MEMRA_STEP_TP").ok().as_deref())
753}
754
755#[derive(Clone, Copy)]
756pub struct E4m3BlockMatrix<'a> {
757    pub codes: &'a [u8],
758    pub scales: &'a [f32],
759    pub out_features: usize,
760    pub in_features: usize,
761}
762
763impl E4m3BlockMatrix<'_> {
764    fn validate(&self) -> Result<(), String> {
765        let code_count = self
766            .out_features
767            .checked_mul(self.in_features)
768            .ok_or_else(|| "E4M3 matrix size overflow".to_string())?;
769        if self.codes.len() != code_count {
770            return Err(format!(
771                "E4M3 code count {} != {}x{} ({code_count})",
772                self.codes.len(),
773                self.out_features,
774                self.in_features,
775            ));
776        }
777        let scale_count =
778            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
779        if self.scales.len() != scale_count {
780            return Err(format!(
781                "E4M3 scale count {} != {scale_count} for {}x{}",
782                self.scales.len(),
783                self.out_features,
784                self.in_features,
785            ));
786        }
787        if !self
788            .scales
789            .iter()
790            .all(|scale| scale.is_finite() && *scale > 0.0)
791        {
792            return Err("E4M3 scale grid contains a non-finite or non-positive value".to_string());
793        }
794        Ok(())
795    }
796}
797
798#[derive(Clone, Copy)]
799pub struct E4m3ExpertBank<'a> {
800    pub codes: &'a [u8],
801    pub scales: &'a [f32],
802    pub expert_count: usize,
803    pub out_features: usize,
804    pub in_features: usize,
805}
806
807impl E4m3ExpertBank<'_> {
808    fn validate(&self) -> Result<(), String> {
809        if self.expert_count == 0 {
810            return Err("E4M3 expert bank is empty".to_string());
811        }
812        let code_stride = self
813            .out_features
814            .checked_mul(self.in_features)
815            .ok_or_else(|| "E4M3 expert code stride overflow".to_string())?;
816        let code_count = self
817            .expert_count
818            .checked_mul(code_stride)
819            .ok_or_else(|| "E4M3 expert code count overflow".to_string())?;
820        if self.codes.len() != code_count {
821            return Err(format!(
822                "E4M3 expert code count {} != {}x{} ({code_count})",
823                self.codes.len(),
824                self.expert_count,
825                code_stride,
826            ));
827        }
828        let scale_stride =
829            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
830        let scale_count = self
831            .expert_count
832            .checked_mul(scale_stride)
833            .ok_or_else(|| "E4M3 expert scale count overflow".to_string())?;
834        if self.scales.len() != scale_count {
835            return Err(format!(
836                "E4M3 expert scale count {} != {}x{} ({scale_count})",
837                self.scales.len(),
838                self.expert_count,
839                scale_stride,
840            ));
841        }
842        if !self
843            .scales
844            .iter()
845            .all(|scale| scale.is_finite() && *scale > 0.0)
846        {
847            return Err(
848                "E4M3 expert scale grid contains a non-finite or non-positive value".to_string(),
849            );
850        }
851        Ok(())
852    }
853
854    pub fn expert(&self, expert: usize) -> Result<E4m3BlockMatrix<'_>, String> {
855        if expert >= self.expert_count {
856            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
857        }
858        let code_stride = self.out_features * self.in_features;
859        let scale_stride =
860            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
861        Ok(E4m3BlockMatrix {
862            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
863            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
864            out_features: self.out_features,
865            in_features: self.in_features,
866        })
867    }
868}
869
870pub struct ColumnParallelResult {
871    pub gathered: Vec<f32>,
872    pub rank_outputs: Vec<Vec<f32>>,
873}
874
875pub struct RowParallelResult {
876    pub reduced: Vec<f32>,
877    pub rank_partials: Vec<Vec<f32>>,
878}
879
880#[derive(Clone, Copy)]
881pub struct Bf16Matrix<'a> {
882    pub bytes: &'a [u8],
883    pub out_features: usize,
884    pub in_features: usize,
885}
886
887impl Bf16Matrix<'_> {
888    pub fn validate(&self) -> Result<(), String> {
889        if self.out_features == 0 || self.in_features == 0 {
890            return Err("BF16 matrix dimensions must be nonzero".into());
891        }
892        let expected = self
893            .out_features
894            .checked_mul(self.in_features)
895            .and_then(|values| values.checked_mul(2))
896            .ok_or("BF16 matrix byte count overflow")?;
897        if self.bytes.len() != expected {
898            return Err(format!(
899                "BF16 matrix bytes {} != {}x{}x2 ({expected})",
900                self.bytes.len(),
901                self.out_features,
902                self.in_features,
903            ));
904        }
905        Ok(())
906    }
907}
908
909struct ResidentE4m3Rank {
910    codes: CudaSlice<u8>,
911    scales: CudaSlice<f32>,
912    out_features: usize,
913    in_features: usize,
914}
915
916enum ResidentBf16Weight {
917    Bf16(CudaSlice<u8>),
918    F32(CudaSlice<f32>),
919}
920
921impl ResidentBf16Weight {
922    fn ordinal(&self) -> usize {
923        match self {
924            Self::Bf16(bytes) => bytes.ordinal(),
925            Self::F32(values) => values.ordinal(),
926        }
927    }
928}
929
930struct ResidentBf16Rank {
931    weight: ResidentBf16Weight,
932    out_features: usize,
933    in_features: usize,
934    /// q8_0 mirror built at load under MEMRA_STEP_TP_W8 (numeric-class door; the bf16 slab
935    /// stays resident because every prefill/verify path is qualified against it).
936    q8: Option<CudaSlice<u8>>,
937}
938
939pub struct ResidentColumnParallel {
940    ranks: Vec<ResidentE4m3Rank>,
941    out_features: usize,
942    in_features: usize,
943}
944
945pub struct ResidentRowParallel {
946    ranks: Vec<ResidentE4m3Rank>,
947    out_features: usize,
948    in_features: usize,
949}
950
951pub struct ResidentBf16ColumnParallel {
952    ranks: Vec<ResidentBf16Rank>,
953    out_features: usize,
954    in_features: usize,
955    canonical_chunk_rows: Option<usize>,
956}
957
958pub struct ResidentBf16RowParallel {
959    ranks: Vec<ResidentBf16Rank>,
960    out_features: usize,
961    in_features: usize,
962}
963
964pub struct ResidentStepBf16RowParallel {
965    ranks: Vec<Vec<ResidentBf16Rank>>,
966    out_features: usize,
967    in_features: usize,
968    canonical_chunk_cols: usize,
969}
970
971/// Root-owned BF16 sigmoid router with persistent F32 weight, bias, and active mask.
972pub struct ResidentSigmoidTopKRouter {
973    weight: CudaSlice<f32>,
974    correction_bias: CudaSlice<f32>,
975    active: CudaSlice<u8>,
976    root_device: usize,
977    input_width: usize,
978    expert_count: usize,
979    experts_per_token: usize,
980    active_count: usize,
981    scaling_factor: f32,
982    route_norm: bool,
983}
984
985pub struct SigmoidTopKHostOutput {
986    pub logits: Vec<f32>,
987    pub selected: Vec<u32>,
988    pub weights: Vec<f32>,
989}
990
991/// Full BF16 SwiGLU weights replicated independently on every runtime rank.
992pub struct ResidentReplicatedBf16SwiGlu {
993    gate: Vec<ResidentBf16Rank>,
994    up: Vec<ResidentBf16Rank>,
995    down: Vec<ResidentBf16Rank>,
996    input_width: usize,
997    intermediate_width: usize,
998}
999
1000/// One token-major F32 batch replicated across a native-P2P rank group.
1001///
1002/// Every allocation is owned by its matching rank CUDA context. This is the generic handoff
1003/// substrate between independently sharded operators; it carries no model or topology claim.
1004pub struct ResidentReplicatedDeviceRows {
1005    ranks: Vec<CudaSlice<f32>>,
1006    tokens: usize,
1007    width: usize,
1008}
1009
1010impl ResidentReplicatedDeviceRows {
1011    pub fn tokens(&self) -> usize {
1012        self.tokens
1013    }
1014
1015    pub fn width(&self) -> usize {
1016        self.width
1017    }
1018
1019    pub fn ranks(&self) -> usize {
1020        self.ranks.len()
1021    }
1022}
1023
1024/// Canonical MoE output order: routed plus shared, then add the layer residual.
1025pub fn moe_residual_host(
1026    residual: &[f32],
1027    routed: &[f32],
1028    shared: &[f32],
1029) -> Result<Vec<f32>, String> {
1030    if residual.len() != routed.len() || residual.len() != shared.len() {
1031        return Err(format!(
1032            "MoE residual lengths residual={} routed={} shared={}",
1033            residual.len(),
1034            routed.len(),
1035            shared.len()
1036        ));
1037    }
1038    let ffn = routed
1039        .iter()
1040        .zip(shared)
1041        .map(|(&routed, &shared)| routed + shared)
1042        .collect::<Vec<_>>();
1043    Ok(residual
1044        .iter()
1045        .zip(ffn)
1046        .map(|(&residual, ffn)| residual + ffn)
1047        .collect())
1048}
1049
1050pub use memra_kv::{
1051    KvRingAppend, ResidentTpKvCache, ResidentTpKvCacheRank, TpKvAppendPlan, TpKvTransaction,
1052};
1053
1054/// Persistent TP2/TP4/TP8 routed-expert reference.
1055///
1056/// Rank-local checkpoint shards are uploaded once and remain tied to their owning CUDA context.
1057/// Activations and deterministic host-staged collectives remain per invocation. This is the
1058/// correctness substrate for serving TP/EP, not product-throughput evidence.
1059pub struct ResidentTpExpert {
1060    gate: ResidentColumnParallel,
1061    up: ResidentColumnParallel,
1062    down: ResidentRowParallel,
1063    input_width: usize,
1064    expert_width: usize,
1065}
1066
1067struct ResidentE4m3ExpertBankRank {
1068    codes: CudaSlice<u8>,
1069    scales: CudaSlice<f32>,
1070    expert_range: Range<usize>,
1071    out_features: usize,
1072    in_features: usize,
1073    code_stride: usize,
1074    scale_stride: usize,
1075    /// TP row banks are packed by native 128-wide K block so reduction can replay the
1076    /// checkpoint's global block order exactly. Other banks remain row-major.
1077    k_blocks: Option<usize>,
1078}
1079
1080struct PackedE4m3ExpertBankRank {
1081    codes: Vec<u8>,
1082    scales: Vec<f32>,
1083    expert_range: Range<usize>,
1084    out_features: usize,
1085    in_features: usize,
1086    code_stride: usize,
1087    scale_stride: usize,
1088    k_blocks: Option<usize>,
1089}
1090
1091struct ResidentEpRank {
1092    gate: ResidentE4m3ExpertBankRank,
1093    up: ResidentE4m3ExpertBankRank,
1094    down: ResidentE4m3ExpertBankRank,
1095}
1096
1097/// Persistent expert-parallel reference.
1098///
1099/// Every routed expert has exactly one owner rank. Shared experts are deliberately absent from
1100/// this object because Step replicates them per rank. Routes execute on the owner CUDA context.
1101/// The default oracle stages through host memory; the native path peer-dispatches inputs and
1102/// peer-returns owner outputs while preserving host-canonical activation and accumulation.
1103pub struct ResidentExpertParallel {
1104    ranks: Vec<ResidentEpRank>,
1105    expert_count: usize,
1106    input_width: usize,
1107    expert_width: usize,
1108}
1109
1110/// Projection-level output from the opt-in official Step grouped-FP8 gate.
1111///
1112/// Rows remain pair-major. Routing, weighted combine, and production integration are deliberately
1113/// outside this gate-only adapter.
1114pub struct StepGroupedFp8ProjectionOutput {
1115    pub gate: Vec<f32>,
1116    pub up: Vec<f32>,
1117    pub down: Vec<f32>,
1118}
1119
1120/// Prepared official Step grouped-FP8 projection gate.
1121///
1122/// The complete tensor banks, both CSR schedules, input, activation buffer, and three projection
1123/// workspaces are uploaded or allocated once. Repeated execution performs no device allocation.
1124pub struct PreparedStepGroupedFp8Gate {
1125    device: usize,
1126    gate: ResidentE4m3ExpertBankRank,
1127    up: ResidentE4m3ExpertBankRank,
1128    down: ResidentE4m3ExpertBankRank,
1129    input: CudaSlice<f32>,
1130    route_csr: DeviceExpertCsr,
1131    down_csr: DeviceExpertCsr,
1132    gate_workspace: Fp8GroupedWorkspace,
1133    up_workspace: Fp8GroupedWorkspace,
1134    down_workspace: Fp8GroupedWorkspace,
1135    activation: CudaSlice<f32>,
1136    activation_limit: Option<f32>,
1137    tokens: usize,
1138    pairs: usize,
1139}
1140
1141impl PreparedStepGroupedFp8Gate {
1142    pub fn tokens(&self) -> usize {
1143        self.tokens
1144    }
1145
1146    pub fn pairs(&self) -> usize {
1147        self.pairs
1148    }
1149}
1150
1151struct PreparedStepGroupedExpertOwner {
1152    rank: usize,
1153    global_pairs: Vec<usize>,
1154    route_csr: DeviceExpertCsr,
1155    down_csr: DeviceExpertCsr,
1156    gate_workspace: Fp8GroupedWorkspace,
1157    up_workspace: Fp8GroupedWorkspace,
1158    down_workspace: Fp8GroupedWorkspace,
1159    activation: CudaSlice<f32>,
1160}
1161
1162struct StepGroupedExpertOwnerSchedule {
1163    global_pairs: Vec<usize>,
1164    route_csr: ExpertCsr,
1165    down_csr: ExpertCsr,
1166}
1167
1168/// Prepared official Step expert-owner grouped-FP8 projection gate.
1169///
1170/// Route partitioning, owner-local CSR uploads, input dispatch, activation buffers, and grouped
1171/// workspaces are persistent. Projection rows are scattered back to canonical pair order only
1172/// after every owner has completed its rank-local program.
1173pub struct PreparedStepGroupedExpertParallelGate {
1174    rank_inputs: Vec<CudaSlice<f32>>,
1175    owners: Vec<PreparedStepGroupedExpertOwner>,
1176    activation_limit: Option<f32>,
1177    tokens: usize,
1178    pairs: usize,
1179    max_tokens: usize,
1180    max_pairs: usize,
1181    input_width: usize,
1182    expert_width: usize,
1183    generation: u64,
1184    executed_generation: Option<u64>,
1185    ready: bool,
1186}
1187
1188impl PreparedStepGroupedExpertParallelGate {
1189    pub fn tokens(&self) -> usize {
1190        self.tokens
1191    }
1192
1193    pub fn pairs(&self) -> usize {
1194        self.pairs
1195    }
1196
1197    pub fn max_tokens(&self) -> usize {
1198        self.max_tokens
1199    }
1200
1201    pub fn input_width(&self) -> usize {
1202        self.input_width
1203    }
1204
1205    pub fn expert_width(&self) -> usize {
1206        self.expert_width
1207    }
1208
1209    pub fn set_activation_limit(&mut self, limit: Option<f32>) -> Result<(), String> {
1210        validate_step_expert_activation_limit(limit)?;
1211        self.activation_limit = limit;
1212        self.executed_generation = None;
1213        Ok(())
1214    }
1215
1216    pub fn active_owners(&self) -> usize {
1217        self.owners
1218            .iter()
1219            .filter(|owner| !owner.global_pairs.is_empty())
1220            .count()
1221    }
1222
1223    pub fn owner_pair_counts(&self) -> Vec<usize> {
1224        self.owners
1225            .iter()
1226            .map(|owner| owner.global_pairs.len())
1227            .collect()
1228    }
1229
1230    pub fn generation(&self) -> u64 {
1231        self.generation
1232    }
1233}
1234
1235struct PreparedPeerWeightedRouteOwner {
1236    token_rows: CudaSlice<i32>,
1237    slots: CudaSlice<i32>,
1238    weights: CudaSlice<f32>,
1239    active_pairs: usize,
1240}
1241
1242/// Persistent root-side weighted combine for peer-owned canonical route rows.
1243///
1244/// Owner metadata, one reusable peer staging buffer, the canonical slot bank, weight bank, and
1245/// output are allocated once. Refreshes update metadata prefixes; execution peer-copies active
1246/// rows, scatters them by canonical token/slot, and reduces in the requested numeric order.
1247pub struct PreparedPeerWeightedRouteCombine {
1248    root_device: usize,
1249    owners: Vec<PreparedPeerWeightedRouteOwner>,
1250    peer_staging: CudaSlice<f32>,
1251    slots: CudaSlice<f32>,
1252    weights: CudaSlice<f32>,
1253    output: CudaSlice<f32>,
1254    peer_devices: Vec<usize>,
1255    peer_outputs: Vec<CudaSlice<f32>>,
1256    width: usize,
1257    experts_per_token: usize,
1258    max_tokens: usize,
1259    max_pairs: usize,
1260    tokens: usize,
1261    pairs: usize,
1262    projection_generation: u64,
1263    output_generation: Option<u64>,
1264    broadcast_generation: Option<u64>,
1265    ready: bool,
1266}
1267
1268impl PreparedPeerWeightedRouteCombine {
1269    pub fn tokens(&self) -> usize {
1270        self.tokens
1271    }
1272
1273    pub fn pairs(&self) -> usize {
1274        self.pairs
1275    }
1276
1277    pub fn owner_pair_counts(&self) -> Vec<usize> {
1278        self.owners.iter().map(|owner| owner.active_pairs).collect()
1279    }
1280
1281    pub fn distributed_ranks(&self) -> usize {
1282        1 + self.peer_outputs.len()
1283    }
1284}
1285
1286struct ResidentTpExpertBank {
1287    gate: Vec<ResidentE4m3ExpertBankRank>,
1288    up: Vec<ResidentE4m3ExpertBankRank>,
1289    down: Vec<ResidentE4m3ExpertBankRank>,
1290    expert_count: usize,
1291    input_width: usize,
1292    expert_width: usize,
1293}
1294
1295/// Persistent tensor-parallel expert bank.
1296///
1297/// Every rank owns a checkpoint-aligned output-row shard of every gate/up projection and an
1298/// input-column shard of every down projection. Activations cross deterministic host-staged
1299/// collectives on hosts where native peer copies are unavailable or corrupt.
1300pub struct ResidentTensorParallel {
1301    bank: ResidentTpExpertBank,
1302}
1303
1304/// Multi-context TP correctness runtime. Each rank owns an independent `Engine` and CUDA context.
1305///
1306/// Host bounce is the default oracle. Native P2P is opt-in and preserves the oracle's global
1307/// checkpoint-block reduction order; it remains a correctness path until serving gates and
1308/// repeated performance evidence qualify it.
1309pub struct TpE4m3HostBounce {
1310    devices: Vec<usize>,
1311    ranks: Vec<Engine>,
1312    native_p2p: bool,
1313    ep_device_arithmetic: bool,
1314    bulk_p2p: bool,
1315    /// v2 decode-attention workspace (MEMRA_STEP_TP_DECODE_V2). One per runtime, shared by
1316    /// every TP attention layer — the buffer shapes are geometry-constant across the trunk.
1317    decode_v2: std::sync::Mutex<Vec<StepTpDecodeV2Ws>>,
1318}
1319
1320/// Persistent workspace of the v2 rank-local decode-attention driver.
1321///
1322/// Buffers live in their producing rank's CUDA context, are never freed, and events are
1323/// re-recorded per call — the pp.rs `BoundarySlot` discipline — so the per-token path has no
1324/// cuMemAlloc, no cross-stream free, and no host round-trip. Every buffer is fully overwritten
1325/// before its consumers run in the same call; nothing carries state between tokens.
1326/// Per-rank attn_gate row shards for the fused QKV+gate kernel, in the weight class the
1327/// fused kernels read (F32 mirror or raw checkpoint bf16).
1328pub enum StepTpGateShards<'a> {
1329    F32(&'a [crate::CudaSlice<f32>]),
1330    Bf16(&'a [crate::CudaSlice<u8>]),
1331}
1332
1333pub struct StepTpDecodeV2Ws {
1334    /// T-COLUMN verify slabs (spec MTP): per-rank [t, local_dim] projections computed by
1335    /// the weight-amortized qkvg_tcol kernel; the col-select door copies one column into
1336    /// the single-row buffers and everything downstream runs the unmodified t=1 program.
1337    pub(crate) tcol_q: Vec<CudaSlice<f32>>,
1338    pub(crate) tcol_k: Vec<CudaSlice<f32>>,
1339    pub(crate) tcol_v: Vec<CudaSlice<f32>>,
1340    pub(crate) tcol_g: Vec<CudaSlice<f32>>,
1341    pub(crate) tcol_in: Vec<CudaSlice<f32>>,
1342    pub(crate) tcol_cap: usize,
1343    /// MEMRA_STEP_TP_W8 activation scratch: per-rank q8_1 quantized attention input
1344    /// ([in_f] i8 + one f32 scale pair per 32). Persistent because the alternative is an
1345    /// allocation per rank per layer per token.
1346    w8_aq: Vec<CudaSlice<i8>>,
1347    w8_ad: Vec<CudaSlice<f32>>,
1348    w8_in: usize,
1349    /// o_proj-side twin of the same scratch (its activation is the gated attention output,
1350    /// a different vector from the QKV input, so it needs its own buffers).
1351    w8o_aq: Vec<CudaSlice<i8>>,
1352    w8o_ad: Vec<CudaSlice<f32>>,
1353    w8o_in: usize,
1354    /// MEMRA_TCOL_OPROJ slabs: per-rank stashed `gated` rows ([8, local_q_dim]), per-rank
1355    /// b4_tcol partials ([8, o_out]), a root-side peer pull of rank1's partial slab, and
1356    /// the root-side joined `mixed` slab. Armed lazily by the first stash.
1357    /// MEMRA_SPEC_FA2 slabs: per-rank stashed post-rope q rows ([2, local_q_dim]), gate
1358    /// rows ([2, heads/ranks]) and the two gated outputs the per-row combine writes
1359    /// ([2, local_q_dim]). Armed lazily by the first stash.
1360    pub(crate) fa2_q: Vec<CudaSlice<f32>>,
1361    pub(crate) fa2_gate: Vec<CudaSlice<f32>>,
1362    pub(crate) fa2_gated: Vec<CudaSlice<f32>>,
1363    pub(crate) fa2_cap: usize,
1364    /// T-ROW rope/append twin scratch: per-rank roped-k rows ([8, local_kv]), per-row
1365    /// last-block counters ([8]) and the per-tick position slab ([8]). Armed with the
1366    /// fa2 slabs.
1367    rope_k_t: Vec<CudaSlice<f32>>,
1368    rope_ctr_t: Vec<CudaSlice<u32>>,
1369    rope_pos_t: Vec<CudaSlice<i32>>,
1370    /// Per-rank combined 6-word row tables, keyed by the caller's (layer, session-set,
1371    /// base-arming) signature.
1372    rows_tabs: Vec<std::collections::HashMap<u64, CudaSlice<u64>>>,
1373    tcol_gated: Vec<CudaSlice<f32>>,
1374    tcol_opart: Vec<CudaSlice<f32>>,
1375    tcol_opeer: Option<CudaSlice<f32>>,
1376    tcol_omix: Option<CudaSlice<f32>>,
1377    tcol_ocap: usize,
1378    // rank-context buffers, indexed by rank (pub(crate): the v2 driver in hybrid_forward
1379    // feeds them to the KV transaction and attention kernels between the two v2 phases)
1380    pub(crate) q_raw: Vec<CudaSlice<f32>>,
1381    pub(crate) k_raw: Vec<CudaSlice<f32>>,
1382    pub(crate) v_raw: Vec<CudaSlice<f32>>,
1383    pub(crate) q: Vec<CudaSlice<f32>>,
1384    pub(crate) k: Vec<CudaSlice<f32>>,
1385    pub(crate) pos: Vec<CudaSlice<i32>>,
1386    /// FUSION #1 last-block counters (one per rank; atomicInc auto-resets per launch).
1387    pub(crate) fuse_ctr: Vec<CudaSlice<u32>>,
1388    pub(crate) gate: Vec<CudaSlice<f32>>,
1389    pub(crate) attn_out: Vec<CudaSlice<f32>>,
1390    pub(crate) gated: Vec<CudaSlice<f32>>,
1391    /// [rank][block] O partials, each `o_out` wide, in the owning rank's context.
1392    o_partials: Vec<Vec<CudaSlice<f32>>>,
1393    /// Recorded on each rank's stream after its per-call work; root waits before peer reads.
1394    ev_rank: Vec<CudaEvent>,
1395    // root-context buffers
1396    peer_partial: CudaSlice<f32>,
1397    reduce_a: CudaSlice<f32>,
1398    reduce_b: CudaSlice<f32>,
1399    /// Never written; the canonical zero start of the v1 add chain.
1400    zeros: CudaSlice<f32>,
1401    pub(crate) k_shadow: CudaSlice<f32>,
1402    pub(crate) v_shadow: CudaSlice<f32>,
1403    ev_refresh: CudaEvent,
1404    ev_oproj: CudaEvent,
1405    // model-engine (e) context
1406    gate_e: CudaSlice<f32>,
1407    /// Per-token stages (e-ctx, fixed addresses): one eager e-stream copy each per layer; the
1408    /// rank flows raw-copy FROM them, which is exactly the shape graph capture needs.
1409    pub(crate) h_stage: Option<CudaSlice<f32>>,
1410    pub(crate) pos_stage: Option<CudaSlice<i32>>,
1411    /// Workspace-owned per-rank attention input rows (the stage flow copies into THESE, not
1412    /// the per-layer decode_input buffers — the workspace is shared across layers, so every
1413    /// captured/raw address it uses must be layer-invariant).
1414    attn_in: Vec<CudaSlice<f32>>,
1415    /// Cached raw pointers of the stage-flow operands (set when the stages arm).
1416    raw_h_stage: u64,
1417    raw_pos_stage: u64,
1418    raw_attn_in: Vec<u64>,
1419    raw_pos: Vec<u64>,
1420    raw_o_partial1: u64,
1421    raw_peer_partial: u64,
1422    raw_k1: u64,
1423    raw_v1: u64,
1424    raw_k_shadow: u64,
1425    raw_v_shadow: u64,
1426    /// Token-graph e-context mirrors (armed by the orchestrator): the root section
1427    /// raw-copies the reduced attention output and the shadow rows here so the e-glue
1428    /// children read same-context memory (cross-context kernel args are capture-illegal).
1429    raw_mixed_stage_e: u64,
1430    raw_reduce_a: u64,
1431    raw_shadow_stage_e: (u64, u64),
1432    ev_entry: CudaEvent,
1433    e_device: usize,
1434    // geometry pins
1435    local_q_dim: usize,
1436    local_kv_dim: usize,
1437    heads: usize,
1438    pub(crate) o_out: usize,
1439    o_block_cols: usize,
1440    blocks_per_rank: usize,
1441}
1442
1443impl TpE4m3HostBounce {
1444    pub fn new(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1445        Self::new_inner(devices, false, false, false, false)
1446    }
1447
1448    pub fn new_native_p2p(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1449        Self::new_inner(devices, false, true, false, false)
1450    }
1451
1452    pub fn new_native_p2p_device_arithmetic(
1453        devices: &[usize],
1454    ) -> Result<Self, Box<dyn std::error::Error>> {
1455        Self::new_inner(devices, false, true, true, false)
1456    }
1457
1458    pub(crate) fn new_configured(
1459        devices: &[usize],
1460        native_p2p: bool,
1461        ep_device_arithmetic: bool,
1462        bulk_p2p: bool,
1463    ) -> Result<Self, Box<dyn std::error::Error>> {
1464        Self::new_inner(devices, false, native_p2p, ep_device_arithmetic, bulk_p2p)
1465    }
1466
1467    /// Single-rank execution of the canonical checkpoint-block TP program.
1468    ///
1469    /// This is an oracle for distributed exactness, not a serving topology. It lets gates compare
1470    /// TP=1 and TP>1 with the same packing, kernel launches, and deterministic reduction order.
1471    pub fn new_single_rank_oracle(device: usize) -> Result<Self, Box<dyn std::error::Error>> {
1472        Self::new_inner(&[device], true, false, false, false)
1473    }
1474
1475    fn new_inner(
1476        devices: &[usize],
1477        allow_single_rank: bool,
1478        native_p2p: bool,
1479        ep_device_arithmetic: bool,
1480        bulk_p2p: bool,
1481    ) -> Result<Self, Box<dyn std::error::Error>> {
1482        if ep_device_arithmetic && !native_p2p {
1483            return Err("device-resident EP arithmetic requires native P2P".into());
1484        }
1485        if bulk_p2p && !native_p2p {
1486            return Err("bulk TP transport requires native P2P".into());
1487        }
1488        let minimum = if allow_single_rank { 1 } else { 2 };
1489        if !(minimum..=8).contains(&devices.len()) {
1490            return Err(format!(
1491                "TP reference requires {minimum}..=8 devices, got {}",
1492                devices.len()
1493            )
1494            .into());
1495        }
1496        let mut unique = devices.to_vec();
1497        unique.sort_unstable();
1498        unique.dedup();
1499        if unique.len() != devices.len() {
1500            return Err(format!("TP devices must be distinct, got {devices:?}").into());
1501        }
1502        let ranks = devices
1503            .iter()
1504            .map(|&device| Engine::new(device))
1505            .collect::<Result<Vec<_>, _>>()?;
1506        if native_p2p {
1507            configure_native_p2p(&ranks, devices)?;
1508        }
1509        if allow_single_rank {
1510            eprintln!(
1511                "[tp] canonical oracle transport=local device={} performance_claim=false",
1512                devices[0]
1513            );
1514        } else if native_p2p {
1515            if ep_device_arithmetic {
1516                eprintln!(
1517                    "[tp] correctness transport=native-p2p devices={devices:?} \
1518                     native_p2p=true activation=device-host-exact \
1519                     accumulation=device-host-exact output=root-readback \
1520                     bulk_p2p={bulk_p2p} performance_claim=false"
1521                );
1522            } else {
1523                eprintln!(
1524                    "[tp] correctness transport=native-p2p devices={devices:?} \
1525                     native_p2p=true activation=host-canonical bulk_p2p={bulk_p2p} \
1526                     performance_claim=false"
1527                );
1528            }
1529        } else {
1530            eprintln!(
1531                "[tp] correctness transport=host-bounce devices={devices:?} \
1532                 native_p2p=false performance_claim=false"
1533            );
1534        }
1535        Ok(Self {
1536            devices: devices.to_vec(),
1537            ranks,
1538            native_p2p,
1539            ep_device_arithmetic,
1540            bulk_p2p,
1541            decode_v2: std::sync::Mutex::new(Vec::new()),
1542        })
1543    }
1544
1545    pub fn devices(&self) -> &[usize] {
1546        &self.devices
1547    }
1548
1549    pub fn native_p2p(&self) -> bool {
1550        self.native_p2p
1551    }
1552
1553    pub fn bulk_p2p(&self) -> bool {
1554        self.bulk_p2p
1555    }
1556
1557    pub fn expert_activation_label(&self) -> &'static str {
1558        if self.ep_device_arithmetic {
1559            "device-host-exact"
1560        } else {
1561            "host-canonical"
1562        }
1563    }
1564
1565    pub fn expert_accumulation_label(&self) -> &'static str {
1566        self.expert_activation_label()
1567    }
1568
1569    pub fn expert_output_label(&self) -> &'static str {
1570        if self.ep_device_arithmetic {
1571            "root-readback"
1572        } else {
1573            "host-accumulated"
1574        }
1575    }
1576
1577    pub fn transport_label(&self) -> &'static str {
1578        if self.devices.len() == 1 {
1579            "local"
1580        } else if self.native_p2p {
1581            "native-p2p"
1582        } else {
1583            "host-bounce"
1584        }
1585    }
1586
1587    pub fn device_names(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
1588        self.ranks
1589            .iter()
1590            .map(|rank| rank.ctx().name().map_err(Into::into))
1591            .collect()
1592    }
1593
1594    /// Correctness-gate access to the engine that owns one TP rank.
1595    ///
1596    /// Model execution should prefer collective methods on this runtime. This accessor exists so
1597    /// focused gates can prove that the rank-local projection outputs remain device-resident
1598    /// through the next ownership boundary before that boundary is wired into serving.
1599    pub fn rank_engine(&self, rank: usize) -> Option<&Engine> {
1600        self.ranks.get(rank)
1601    }
1602
1603    pub fn allocate_tp_kv_cache(
1604        &self,
1605        kv_dim_k: usize,
1606        kv_dim_v: usize,
1607        capacity: usize,
1608    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1609        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, None)
1610    }
1611
1612    pub fn allocate_tp_swa_kv_cache(
1613        &self,
1614        kv_dim_k: usize,
1615        kv_dim_v: usize,
1616        capacity: usize,
1617        window: usize,
1618    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1619        if window == 0 {
1620            return Err("TP SWA KV window must be nonzero".into());
1621        }
1622        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, Some(window))
1623    }
1624
1625    fn allocate_tp_kv_cache_inner(
1626        &self,
1627        kv_dim_k: usize,
1628        kv_dim_v: usize,
1629        capacity: usize,
1630        window: Option<usize>,
1631    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1632        if capacity == 0 || capacity > i32::MAX as usize {
1633            return Err(
1634                format!("TP KV capacity must be in 1..={}, got {capacity}", i32::MAX).into(),
1635            );
1636        }
1637        let tp = self.ranks.len();
1638        let shape = crate::cache::tp_kv_rank_allocation_shape(kv_dim_k, kv_dim_v, tp)?;
1639        let physical_rows = window
1640            .map(|window| crate::cache::swa_ring_rows(window, capacity))
1641            .unwrap_or(capacity);
1642        let k_plane_bytes = physical_rows
1643            .checked_mul(shape.k_token_bytes)
1644            .and_then(|bytes| bytes.checked_add(8))
1645            .ok_or("TP KV K plane-byte overflow")?;
1646        let v_plane_bytes = physical_rows
1647            .checked_mul(shape.v_token_bytes)
1648            .and_then(|bytes| bytes.checked_add(8))
1649            .ok_or("TP KV V plane-byte overflow")?;
1650        let mut ranks = Vec::with_capacity(tp);
1651        for engine in &self.ranks {
1652            let _main = engine.gpu.enter_main()?;
1653            ranks.push(ResidentTpKvCacheRank::new(
1654                engine.alloc_u8(k_plane_bytes)?,
1655                engine.alloc_u8(v_plane_bytes)?,
1656                engine.htod_i32(&[0])?,
1657            ));
1658        }
1659        Ok(match window {
1660            Some(window) => ResidentTpKvCache::new_swa(
1661                ranks,
1662                shape.kv_dim_k,
1663                shape.kv_dim_v,
1664                shape.k_token_bytes,
1665                shape.v_token_bytes,
1666                capacity,
1667                window,
1668            ),
1669            None => ResidentTpKvCache::new(
1670                ranks,
1671                shape.kv_dim_k,
1672                shape.kv_dim_v,
1673                shape.k_token_bytes,
1674                shape.v_token_bytes,
1675                capacity,
1676            ),
1677        })
1678    }
1679
1680    pub fn grow_tp_kv_cache(
1681        &self,
1682        source: &ResidentTpKvCache,
1683        target_capacity: usize,
1684        rows: usize,
1685    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1686        self.validate_tp_kv_cache(source)?;
1687        let plan = source.prepare_grow(target_capacity, rows)?;
1688        let ranks = self.ranks.len();
1689        let global_k = source
1690            .kv_dim_k()
1691            .checked_mul(ranks)
1692            .ok_or("TP KV grow global K dimension overflow")?;
1693        let global_v = source
1694            .kv_dim_v()
1695            .checked_mul(ranks)
1696            .ok_or("TP KV grow global V dimension overflow")?;
1697        let mut target = match source.ring_window() {
1698            Some(window) => {
1699                self.allocate_tp_swa_kv_cache(global_k, global_v, target_capacity, window)?
1700            }
1701            None => self.allocate_tp_kv_cache(global_k, global_v, target_capacity)?,
1702        };
1703        self.validate_tp_kv_cache(&target)?;
1704
1705        for (rank, engine) in self.ranks.iter().enumerate() {
1706            let _main = engine.gpu.enter_main()?;
1707            let src = source
1708                .rank(rank)
1709                .ok_or_else(|| format!("TP KV grow source has no rank {rank}"))?;
1710            let dst = target
1711                .rank_mut(rank)
1712                .ok_or_else(|| format!("TP KV grow target has no rank {rank}"))?;
1713            if plan.k_bytes() > 0 {
1714                engine.copy_u8_range_into(
1715                    dst.k_mut(),
1716                    0,
1717                    src.k(),
1718                    plan.source_row() * source.k_tok_bytes(),
1719                    plan.k_bytes(),
1720                )?;
1721            }
1722            if plan.v_bytes() > 0 {
1723                engine.copy_u8_range_into(
1724                    dst.v_mut(),
1725                    0,
1726                    src.v(),
1727                    plan.source_row() * source.v_tok_bytes(),
1728                    plan.v_bytes(),
1729                )?;
1730            }
1731        }
1732        self.set_tp_kv_len_mirrors(&mut target, plan.rows())?;
1733
1734        // The caller publishes `target` and immediately drops `source`. Drain every rank's
1735        // stream so an async-pool free cannot recycle a source plane under an in-flight D2D copy.
1736        for engine in &self.ranks {
1737            let _main = engine.gpu.enter_main()?;
1738            engine.stream().synchronize()?;
1739        }
1740        let physical_copy_rows = plan.copy_rows();
1741        target.publish_grow(plan)?;
1742        eprintln!(
1743            "[step-tp-kv-grow] rows={} source_capacity={} target_capacity={} ranks={} \
1744             physical_copy_rows={} ring_window={:?} copy=rank-local-dtod \
1745             rank_streams_synchronized=true generation_preserved=true",
1746            rows,
1747            source.capacity(),
1748            target_capacity,
1749            ranks,
1750            physical_copy_rows,
1751            source.ring_window(),
1752        );
1753        Ok(target)
1754    }
1755
1756    pub fn hydrate_tp_kv_cache(
1757        &self,
1758        cache: &mut ResidentTpKvCache,
1759        rows: usize,
1760        k_rows: &[u8],
1761        v_rows: &[u8],
1762    ) -> Result<(), Box<dyn std::error::Error>> {
1763        self.hydrate_tp_kv_cache_from(cache, rows, 0, k_rows, v_rows)
1764    }
1765
1766    pub fn hydrate_tp_kv_cache_from(
1767        &self,
1768        cache: &mut ResidentTpKvCache,
1769        logical_len: usize,
1770        resident_start: usize,
1771        k_rows: &[u8],
1772        v_rows: &[u8],
1773    ) -> Result<(), Box<dyn std::error::Error>> {
1774        self.validate_tp_kv_cache(cache)?;
1775        if cache.committed_len() != 0 || cache.staged_len() != 0 {
1776            return Err(format!(
1777                "TP KV hydration requires an empty cache, got committed/staged={}/{}",
1778                cache.committed_len(),
1779                cache.staged_len()
1780            )
1781            .into());
1782        }
1783        if resident_start > logical_len || logical_len > cache.capacity() {
1784            return Err(format!(
1785                "TP KV hydration range [{resident_start},{logical_len}) exceeds capacity {}",
1786                cache.capacity(),
1787            )
1788            .into());
1789        }
1790        let rows = logical_len - resident_start;
1791        if rows > cache.physical_capacity() {
1792            return Err(format!(
1793                "TP KV hydration rows {rows} exceed physical capacity {}",
1794                cache.physical_capacity()
1795            )
1796            .into());
1797        }
1798        for rank in 0..self.ranks.len() {
1799            let k_rank =
1800                cache_rank_rows(k_rows, rows, cache.k_tok_bytes(), self.ranks.len(), rank)?;
1801            let v_rank =
1802                cache_rank_rows(v_rows, rows, cache.v_tok_bytes(), self.ranks.len(), rank)?;
1803            let engine = &self.ranks[rank];
1804            let _main = engine.gpu.enter_main()?;
1805            let rank_cache = cache
1806                .rank_mut(rank)
1807                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1808            engine.htod_u8_into(rank_cache.k_mut(), 0, &k_rank)?;
1809            engine.htod_u8_into(rank_cache.v_mut(), 0, &v_rank)?;
1810        }
1811        cache.publish_hydration(logical_len, resident_start)?;
1812        Ok(())
1813    }
1814
1815    pub fn append_tp_kv_transaction(
1816        &self,
1817        cache: &mut ResidentTpKvCache,
1818        transaction: TpKvTransaction,
1819        k_shards: &[CudaSlice<f32>],
1820        v_shards: &[CudaSlice<f32>],
1821        rows: usize,
1822    ) -> Result<(), Box<dyn std::error::Error>> {
1823        self.append_tp_kv_transaction_inner(cache, transaction, k_shards, v_shards, rows, false)
1824    }
1825
1826    /// `external_rank_appends`: the dcw path already wrote the rank rows (device-counter
1827    /// append) — run everything EXCEPT the per-rank quantize/append loop (plan validation,
1828    /// rebase arm — unreachable when the caller peeked — and the absolute len-mirror sets,
1829    /// which land the same value the in-stream inc produced).
1830    #[allow(clippy::too_many_arguments)]
1831    pub fn append_tp_kv_transaction_inner(
1832        &self,
1833        cache: &mut ResidentTpKvCache,
1834        transaction: TpKvTransaction,
1835        k_shards: &[CudaSlice<f32>],
1836        v_shards: &[CudaSlice<f32>],
1837        rows: usize,
1838        external_rank_appends: bool,
1839    ) -> Result<(), Box<dyn std::error::Error>> {
1840        self.validate_tp_kv_cache(cache)?;
1841        let plan = cache.prepare_append(transaction, rows)?;
1842        let target = plan.target();
1843        let expected_k = rows
1844            .checked_mul(cache.kv_dim_k())
1845            .ok_or("TP KV K append size overflow")?;
1846        let expected_v = rows
1847            .checked_mul(cache.kv_dim_v())
1848            .ok_or("TP KV V append size overflow")?;
1849        // external_rank_appends passes no shards — the graph's dcw appends already wrote
1850        // the rank rows, so this call is bookkeeping-only and the shard slices are unused.
1851        if !external_rank_appends
1852            && (k_shards.len() != self.ranks.len() || v_shards.len() != self.ranks.len())
1853        {
1854            return Err(format!(
1855                "TP KV append shard counts k={} v={} != ranks {}",
1856                k_shards.len(),
1857                v_shards.len(),
1858                self.ranks.len()
1859            )
1860            .into());
1861        }
1862        let kv_dim_k = cache.kv_dim_k();
1863        let kv_dim_v = cache.kv_dim_v();
1864        let k_tok_bytes = cache.k_tok_bytes();
1865        let v_tok_bytes = cache.v_tok_bytes();
1866        if let Some(KvRingAppend::Rebase {
1867            src_row,
1868            keep_rows,
1869            new_base,
1870            ..
1871        }) = plan.ring_append()
1872        {
1873            for rank in 0..self.ranks.len() {
1874                let engine = &self.ranks[rank];
1875                let _main = engine.gpu.enter_main()?;
1876                let rank_cache = cache
1877                    .rank_mut(rank)
1878                    .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1879                if keep_rows > 0 {
1880                    let k_len = keep_rows
1881                        .checked_mul(k_tok_bytes)
1882                        .ok_or("TP KV K rebase-byte overflow")?;
1883                    let v_len = keep_rows
1884                        .checked_mul(v_tok_bytes)
1885                        .ok_or("TP KV V rebase-byte overflow")?;
1886                    let mut k_tmp = engine.alloc_u8_uninit(k_len)?;
1887                    let mut v_tmp = engine.alloc_u8_uninit(v_len)?;
1888                    engine.copy_u8_range_into(
1889                        &mut k_tmp,
1890                        0,
1891                        rank_cache.k(),
1892                        src_row * k_tok_bytes,
1893                        k_len,
1894                    )?;
1895                    engine.copy_u8_range_into(
1896                        &mut v_tmp,
1897                        0,
1898                        rank_cache.v(),
1899                        src_row * v_tok_bytes,
1900                        v_len,
1901                    )?;
1902                    engine.copy_u8_into(rank_cache.k_mut(), 0, &k_tmp, k_len)?;
1903                    engine.copy_u8_into(rank_cache.v_mut(), 0, &v_tmp, v_len)?;
1904                }
1905                // dcw base mirror (graph increment A): physical row 0 now holds logical
1906                // row `new_base`; armed device mirrors track it (rebases are rare host
1907                // events, so a host set here is the whole maintenance cost).
1908                if rank_cache.base_d().is_some() {
1909                    let value = new_base as i32;
1910                    let rank_cache = cache
1911                        .rank_mut(rank)
1912                        .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1913                    if let Some(base_d) = rank_cache.base_d_mut() {
1914                        engine.set_i32_one(base_d, value)?;
1915                    }
1916                }
1917            }
1918        }
1919        cache.publish_append_rebase(plan)?;
1920        let write_row = plan.write_row();
1921        for rank in 0..self.ranks.len() {
1922            if external_rank_appends {
1923                break;
1924            }
1925            let engine = &self.ranks[rank];
1926            let _main = engine.gpu.enter_main()?;
1927            if k_shards[rank].len() != expected_k
1928                || v_shards[rank].len() != expected_v
1929                || k_shards[rank].ordinal() != engine.ctx().ordinal()
1930                || v_shards[rank].ordinal() != engine.ctx().ordinal()
1931            {
1932                return Err(format!(
1933                    "TP KV rank {rank} shard geometry/device k={}/{} v={}/{} \
1934                     != expected {expected_k}/{expected_v} on device {}",
1935                    k_shards[rank].len(),
1936                    k_shards[rank].ordinal(),
1937                    v_shards[rank].len(),
1938                    v_shards[rank].ordinal(),
1939                    engine.ctx().ordinal(),
1940                )
1941                .into());
1942            }
1943            let rank_cache = cache
1944                .rank_mut(rank)
1945                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1946            let (rank_k, rank_v) = rank_cache.planes_mut();
1947            engine.append_kv_quantized_rows(
1948                &k_shards[rank],
1949                &v_shards[rank],
1950                rank_k,
1951                rank_v,
1952                write_row,
1953                rows,
1954                kv_dim_k,
1955                kv_dim_v,
1956                k_tok_bytes,
1957                v_tok_bytes,
1958                Engine::kv_fp8_on(),
1959            )?;
1960        }
1961        if !external_rank_appends {
1962            // dcw appends advance the device counters with in-stream inc_i32; an absolute set
1963            // here would race the merged per-rank append (it reads len_d for its write row).
1964            self.set_tp_kv_len_mirrors(cache, target)?;
1965        }
1966        cache.publish_append_plan(plan)?;
1967        Ok(())
1968    }
1969
1970    pub fn commit_tp_kv_transaction(
1971        &self,
1972        cache: &mut ResidentTpKvCache,
1973        transaction: TpKvTransaction,
1974        accepted_rows: usize,
1975    ) -> Result<(), Box<dyn std::error::Error>> {
1976        self.validate_tp_kv_cache(cache)?;
1977        let target = cache.commit_target(transaction, accepted_rows)?;
1978        self.set_tp_kv_len_mirrors(cache, target)?;
1979        cache.publish_finalize(transaction, target)?;
1980        Ok(())
1981    }
1982
1983    /// Commit for the external-appends (token graph) path: host bookkeeping only, NO absolute
1984    /// len-mirror sets. The graph's in-stream inc_i32 owns the device counters; a rank-stream
1985    /// set here has no ordering edge against the NEXT token's graph launch (graph children do
1986    /// not wait on the rank streams), so it can land AFTER that graph's inc and drag the
1987    /// counter backward mid-token.
1988    pub fn commit_tp_kv_transaction_external(
1989        &self,
1990        cache: &mut ResidentTpKvCache,
1991        transaction: TpKvTransaction,
1992        accepted_rows: usize,
1993    ) -> Result<(), Box<dyn std::error::Error>> {
1994        self.validate_tp_kv_cache(cache)?;
1995        let target = cache.commit_target(transaction, accepted_rows)?;
1996        cache.publish_finalize(transaction, target)?;
1997        Ok(())
1998    }
1999
2000    pub fn rollback_tp_kv_transaction(
2001        &self,
2002        cache: &mut ResidentTpKvCache,
2003        transaction: TpKvTransaction,
2004    ) -> Result<(), Box<dyn std::error::Error>> {
2005        self.validate_tp_kv_cache(cache)?;
2006        cache.validate_transaction(transaction)?;
2007        let target = transaction.base_len();
2008        self.set_tp_kv_len_mirrors(cache, target)?;
2009        cache.publish_finalize(transaction, target)?;
2010        Ok(())
2011    }
2012
2013    pub fn tp_kv_device_lengths(
2014        &self,
2015        cache: &ResidentTpKvCache,
2016    ) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
2017        self.validate_tp_kv_cache(cache)?;
2018        let mut lengths = Vec::with_capacity(self.ranks.len());
2019        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks()) {
2020            let _main = engine.gpu.enter_main()?;
2021            lengths.push(engine.dtoh_i32_one(rank_cache.len_d())?);
2022        }
2023        Ok(lengths)
2024    }
2025
2026    fn set_tp_kv_len_mirrors(
2027        &self,
2028        cache: &mut ResidentTpKvCache,
2029        len: usize,
2030    ) -> Result<(), Box<dyn std::error::Error>> {
2031        let len = i32::try_from(len).map_err(|_| "TP KV length exceeds i32 device mirror")?;
2032        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks_mut()) {
2033            let _main = engine.gpu.enter_main()?;
2034            engine.set_i32_one(rank_cache.len_d_mut(), len)?;
2035        }
2036        Ok(())
2037    }
2038
2039    fn validate_tp_kv_cache(
2040        &self,
2041        cache: &ResidentTpKvCache,
2042    ) -> Result<(), Box<dyn std::error::Error>> {
2043        if cache.ranks_len() != self.ranks.len() {
2044            return Err(format!(
2045                "TP KV cache ranks {} != runtime ranks {}",
2046                cache.ranks_len(),
2047                self.ranks.len()
2048            )
2049            .into());
2050        }
2051        let expected_k = cache
2052            .physical_capacity()
2053            .checked_mul(cache.k_tok_bytes())
2054            .and_then(|bytes| bytes.checked_add(8))
2055            .ok_or("TP KV K plane validation overflow")?;
2056        let expected_v = cache
2057            .physical_capacity()
2058            .checked_mul(cache.v_tok_bytes())
2059            .and_then(|bytes| bytes.checked_add(8))
2060            .ok_or("TP KV V plane validation overflow")?;
2061        for (rank, (engine, rank_cache)) in self.ranks.iter().zip(cache.ranks()).enumerate() {
2062            let device = engine.ctx().ordinal();
2063            if rank_cache.k().len() != expected_k
2064                || rank_cache.v().len() != expected_v
2065                || rank_cache.len_d().len() != 1
2066                || rank_cache.k().ordinal() != device
2067                || rank_cache.v().ordinal() != device
2068                || rank_cache.len_d().ordinal() != device
2069            {
2070                return Err(format!(
2071                    "TP KV rank {rank} residency does not match device {device} or plane geometry"
2072                )
2073                .into());
2074            }
2075        }
2076        Ok(())
2077    }
2078
2079    pub fn full(
2080        &self,
2081        matrix: E4m3BlockMatrix<'_>,
2082        activations: &[f32],
2083        tokens: usize,
2084    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2085        matrix.validate()?;
2086        validate_activations(activations, tokens, matrix.in_features)?;
2087        run_rank(&self.ranks[0], matrix, activations, tokens)
2088    }
2089
2090    /// Column-parallel projection. Weight output rows and their scale rows are partitioned across
2091    /// ranks. The input is host-broadcast, rank-local projections execute independently, and the
2092    /// output is host-gathered in rank order.
2093    pub fn column_parallel(
2094        &self,
2095        matrix: E4m3BlockMatrix<'_>,
2096        activations: &[f32],
2097        tokens: usize,
2098    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2099        matrix.validate()?;
2100        validate_activations(activations, tokens, matrix.in_features)?;
2101        let tp = self.ranks.len();
2102        if matrix.out_features % tp != 0 {
2103            return Err(format!(
2104                "column-parallel out_features {} is not divisible by TP={tp}",
2105                matrix.out_features
2106            )
2107            .into());
2108        }
2109        let local_out = matrix.out_features / tp;
2110        if local_out % FP8_BLOCK != 0 {
2111            return Err(format!(
2112                "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
2113                 E4M3 scale block"
2114            )
2115            .into());
2116        }
2117
2118        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2119        let mut rank_outputs = Vec::with_capacity(tp);
2120        for (rank_index, rank) in self.ranks.iter().enumerate() {
2121            let shard = column_shard(matrix, tp, rank_index)?;
2122            let output = run_rank(rank, shard, activations, tokens)?;
2123            let row_start = rank_index * local_out;
2124            for token in 0..tokens {
2125                gathered[token * matrix.out_features + row_start
2126                    ..token * matrix.out_features + row_start + local_out]
2127                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2128            }
2129            rank_outputs.push(output);
2130        }
2131        Ok(ColumnParallelResult {
2132            gathered,
2133            rank_outputs,
2134        })
2135    }
2136
2137    pub fn upload_column_parallel(
2138        &self,
2139        matrix: E4m3BlockMatrix<'_>,
2140    ) -> Result<ResidentColumnParallel, Box<dyn std::error::Error>> {
2141        matrix.validate()?;
2142        let tp = self.ranks.len();
2143        validate_column_shape(matrix, tp)?;
2144        let mut ranks = Vec::with_capacity(tp);
2145        for (rank_index, engine) in self.ranks.iter().enumerate() {
2146            ranks.push(upload_rank(engine, column_shard(matrix, tp, rank_index)?)?);
2147        }
2148        Ok(ResidentColumnParallel {
2149            ranks,
2150            out_features: matrix.out_features,
2151            in_features: matrix.in_features,
2152        })
2153    }
2154
2155    pub fn column_parallel_resident(
2156        &self,
2157        matrix: &ResidentColumnParallel,
2158        activations: &[f32],
2159        tokens: usize,
2160    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2161        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2162        validate_activations(activations, tokens, matrix.in_features)?;
2163        let local_out = matrix.out_features / self.ranks.len();
2164        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2165        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2166        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2167            let output = run_resident_rank(engine, shard, activations, tokens)?;
2168            let row_start = rank_index * local_out;
2169            for token in 0..tokens {
2170                gathered[token * matrix.out_features + row_start
2171                    ..token * matrix.out_features + row_start + local_out]
2172                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2173            }
2174            rank_outputs.push(output);
2175        }
2176        Ok(ColumnParallelResult {
2177            gathered,
2178            rank_outputs,
2179        })
2180    }
2181
2182    /// Row-parallel projection. Weight/input columns and their scale columns are partitioned
2183    /// across ranks. Rank-local partials return through host memory and are reduced in stable
2184    /// rank order.
2185    pub fn row_parallel(
2186        &self,
2187        matrix: E4m3BlockMatrix<'_>,
2188        activations: &[f32],
2189        tokens: usize,
2190    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2191        matrix.validate()?;
2192        validate_activations(activations, tokens, matrix.in_features)?;
2193        let tp = self.ranks.len();
2194        if matrix.in_features % tp != 0 {
2195            return Err(format!(
2196                "row-parallel in_features {} is not divisible by TP={tp}",
2197                matrix.in_features
2198            )
2199            .into());
2200        }
2201        let local_in = matrix.in_features / tp;
2202        if local_in % FP8_BLOCK != 0 {
2203            return Err(format!(
2204                "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
2205                 E4M3 scale block"
2206            )
2207            .into());
2208        }
2209
2210        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2211        let mut rank_partials = Vec::with_capacity(tp);
2212        for (rank_index, rank) in self.ranks.iter().enumerate() {
2213            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2214            let local_activations =
2215                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2216            let shard = E4m3BlockMatrix {
2217                codes: &codes,
2218                scales: &scales,
2219                out_features: matrix.out_features,
2220                in_features: local_in,
2221            };
2222            let partial = run_rank(rank, shard, &local_activations, tokens)?;
2223            for (sum, value) in reduced.iter_mut().zip(&partial) {
2224                *sum += *value;
2225            }
2226            rank_partials.push(partial);
2227        }
2228        Ok(RowParallelResult {
2229            reduced,
2230            rank_partials,
2231        })
2232    }
2233
2234    pub fn upload_row_parallel(
2235        &self,
2236        matrix: E4m3BlockMatrix<'_>,
2237    ) -> Result<ResidentRowParallel, Box<dyn std::error::Error>> {
2238        matrix.validate()?;
2239        let tp = self.ranks.len();
2240        validate_row_shape(matrix, tp)?;
2241        let local_in = matrix.in_features / tp;
2242        let mut ranks = Vec::with_capacity(tp);
2243        for (rank_index, engine) in self.ranks.iter().enumerate() {
2244            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2245            ranks.push(upload_rank(
2246                engine,
2247                E4m3BlockMatrix {
2248                    codes: &codes,
2249                    scales: &scales,
2250                    out_features: matrix.out_features,
2251                    in_features: local_in,
2252                },
2253            )?);
2254        }
2255        Ok(ResidentRowParallel {
2256            ranks,
2257            out_features: matrix.out_features,
2258            in_features: matrix.in_features,
2259        })
2260    }
2261
2262    pub fn row_parallel_resident(
2263        &self,
2264        matrix: &ResidentRowParallel,
2265        activations: &[f32],
2266        tokens: usize,
2267    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2268        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2269        validate_activations(activations, tokens, matrix.in_features)?;
2270        let tp = self.ranks.len();
2271        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2272        let mut rank_partials = Vec::with_capacity(tp);
2273        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2274            let local_activations =
2275                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2276            let partial = run_resident_rank(engine, shard, &local_activations, tokens)?;
2277            for (sum, value) in reduced.iter_mut().zip(&partial) {
2278                *sum += *value;
2279            }
2280            rank_partials.push(partial);
2281        }
2282        Ok(RowParallelResult {
2283            reduced,
2284            rank_partials,
2285        })
2286    }
2287
2288    pub fn upload_bf16_column_parallel(
2289        &self,
2290        matrix: Bf16Matrix<'_>,
2291    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2292        self.upload_bf16_column_parallel_inner(matrix, None, false)
2293    }
2294
2295    /// Step-3.7 column projection with one numerical program across TP1/TP2/TP4/TP8.
2296    pub fn upload_step_bf16_column_parallel(
2297        &self,
2298        matrix: Bf16Matrix<'_>,
2299    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2300        self.upload_step_bf16_column_parallel_inner(matrix, false)
2301    }
2302
2303    /// Load-time exact F32 expansion of a Step BF16 shard.
2304    ///
2305    /// The original BF16 allocation is released after the stream-ordered conversion. Decode then
2306    /// reuses the resident F32 values with the same topology-invariant output-row chunks.
2307    pub fn upload_step_bf16_column_parallel_f32_mirror(
2308        &self,
2309        matrix: Bf16Matrix<'_>,
2310    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2311        self.upload_step_bf16_column_parallel_inner(matrix, true)
2312    }
2313
2314    fn upload_step_bf16_column_parallel_inner(
2315        &self,
2316        matrix: Bf16Matrix<'_>,
2317        f32_mirror: bool,
2318    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2319        let canonical_chunk_rows =
2320            step_bf16_canonical_chunk_rows(matrix.out_features, self.ranks.len())?;
2321        self.upload_bf16_column_parallel_inner(matrix, Some(canonical_chunk_rows), f32_mirror)
2322    }
2323
2324    fn upload_bf16_column_parallel_inner(
2325        &self,
2326        matrix: Bf16Matrix<'_>,
2327        canonical_chunk_rows: Option<usize>,
2328        f32_mirror: bool,
2329    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2330        matrix.validate()?;
2331        let tp = self.ranks.len();
2332        if matrix.out_features % tp != 0 {
2333            return Err(format!(
2334                "BF16 column-parallel out_features {} is not divisible by TP={tp}",
2335                matrix.out_features
2336            )
2337            .into());
2338        }
2339        let mut ranks = Vec::with_capacity(tp);
2340        for (rank, engine) in self.ranks.iter().enumerate() {
2341            ranks.push(upload_bf16_rank(
2342                engine,
2343                bf16_column_shard(matrix, tp, rank)?,
2344                f32_mirror,
2345            )?);
2346        }
2347        Ok(ResidentBf16ColumnParallel {
2348            ranks,
2349            out_features: matrix.out_features,
2350            in_features: matrix.in_features,
2351            canonical_chunk_rows,
2352        })
2353    }
2354
2355    pub fn bf16_column_parallel_resident(
2356        &self,
2357        matrix: &ResidentBf16ColumnParallel,
2358        activations: &[f32],
2359        tokens: usize,
2360    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2361        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2362        validate_activations(activations, tokens, matrix.in_features)?;
2363        let local_out = matrix.out_features / self.ranks.len();
2364        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2365        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2366        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2367            let output = run_resident_bf16_rank(
2368                engine,
2369                shard,
2370                activations,
2371                tokens,
2372                matrix.canonical_chunk_rows,
2373            )?;
2374            for token in 0..tokens {
2375                let src = &output[token * local_out..(token + 1) * local_out];
2376                let dst_start = token * matrix.out_features + rank * local_out;
2377                gathered[dst_start..dst_start + local_out].copy_from_slice(src);
2378            }
2379            rank_outputs.push(output);
2380        }
2381        Ok(ColumnParallelResult {
2382            gathered,
2383            rank_outputs,
2384        })
2385    }
2386
2387    /// Native-P2P twin of [`Self::bf16_column_parallel_resident`].
2388    ///
2389    /// The host-canonical activation is uploaded once on rank zero and peer-broadcast to the
2390    /// remaining ranks. Rank-local outputs are peer-gathered in token-major order before one root
2391    /// readback. This removes per-rank host staging but deliberately still returns a host oracle;
2392    /// attention and KV ownership are separate milestones.
2393    pub fn bf16_column_parallel_resident_native(
2394        &self,
2395        matrix: &ResidentBf16ColumnParallel,
2396        activations: &[f32],
2397        tokens: usize,
2398    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2399        let rank_outputs =
2400            self.bf16_column_parallel_resident_device_shards(matrix, activations, tokens)?;
2401        let local_out = matrix.out_features / self.ranks.len();
2402        self.gather_native_column_shards(&rank_outputs, tokens, local_out)
2403    }
2404
2405    /// Does the serving engine live in the SAME CUDA context as this runtime's root rank?
2406    /// The device-resident input/output seams below hand raw device buffers across the
2407    /// Engine boundary, which is only addressable when both sides share the root device's
2408    /// primary context — the seam `step35_tp_qkv` keys its residency dispatch on.
2409    pub fn root_shares_ctx(&self, e: &Engine) -> bool {
2410        self.ranks
2411            .first()
2412            .is_some_and(|root| root.ctx().cu_ctx() == e.ctx().cu_ctx())
2413    }
2414
2415    /// Device-input twin of [`Self::bf16_column_parallel_resident_native`] (lane/
2416    /// hermes-perf-fixes, 2026-08-23 — the step QKV TP host-bounce finding). The activation
2417    /// arrives as a ROOT-DEVICE buffer (first `tokens * in_features` values) instead of a
2418    /// host slice, and the gathered output stays root-resident: no DtoH of the hidden state,
2419    /// no host q/k/v staging, no re-upload. BYTE-IDENTICAL to the host-canonical native arm
2420    /// by construction — the root input bytes are dtod-copied where the host arm htod'd the
2421    /// same bytes, and every kernel, peer copy, and gather order is shared.
2422    ///
2423    /// FENCES: caller must have synchronized the producer stream that wrote
2424    /// `root_activation` (the serving engine's — a DIFFERENT stream in the same context);
2425    /// this method synchronizes the root stream before returning so the caller's stream can
2426    /// consume the gathered output immediately.
2427    pub fn bf16_column_parallel_resident_native_device(
2428        &self,
2429        matrix: &ResidentBf16ColumnParallel,
2430        root_activation: &CudaSlice<f32>,
2431        tokens: usize,
2432    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2433        let rank_outputs = self.bf16_column_parallel_resident_device_shards_from_root(
2434            matrix,
2435            root_activation,
2436            tokens,
2437        )?;
2438        let local_out = matrix.out_features / self.ranks.len();
2439        let gathered = self.gather_native_column_shards_device(&rank_outputs, tokens, local_out)?;
2440        let root = &self.ranks[0];
2441        let _main = root.gpu.enter_main()?;
2442        root.stream().synchronize()?;
2443        Ok(gathered)
2444    }
2445
2446    /// Root-device-input twin of [`Self::bf16_column_parallel_resident_device_shards`]:
2447    /// the canonical activation is already resident on the root device (len >=
2448    /// `tokens * in_features`; extra tail values beyond the active prefix are ignored,
2449    /// the reused-prime-slab contract of `active_matrix_values`).
2450    pub fn bf16_column_parallel_resident_device_shards_from_root(
2451        &self,
2452        matrix: &ResidentBf16ColumnParallel,
2453        root_activation: &CudaSlice<f32>,
2454        tokens: usize,
2455    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2456        if self.ranks.len() > 1 && !self.native_p2p {
2457            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
2458        }
2459        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2460        let values = tokens
2461            .checked_mul(matrix.in_features)
2462            .ok_or("device BF16 column activation size overflow")?;
2463        let root = &self.ranks[0];
2464        if tokens == 0
2465            || root_activation.len() < values
2466            || root_activation.ordinal() != root.ctx().ordinal()
2467        {
2468            return Err("device BF16 column root activation geometry mismatch".into());
2469        }
2470
2471        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
2472        let root_input = {
2473            let _main = root.gpu.enter_main()?;
2474            let mut root_input = root.uninit(values)?;
2475            root.stream()
2476                .memcpy_dtod(&root_activation.slice(0..values), &mut root_input)?;
2477            root_input
2478        };
2479        // PRODUCER FENCE (same discipline as the host-input twin): the peer broadcast
2480        // below reads this buffer from the OTHER ranks' streams while the root dtod may
2481        // still be in flight.
2482        {
2483            let _main = root.gpu.enter_main()?;
2484            root.stream().synchronize()?;
2485        }
2486        rank_inputs.push(root_input);
2487        for engine in &self.ranks[1..] {
2488            let peer_input = {
2489                let _main = engine.gpu.enter_main()?;
2490                let mut peer_input = engine.uninit(values)?;
2491                engine
2492                    .stream()
2493                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
2494                peer_input
2495            };
2496            rank_inputs.push(peer_input);
2497        }
2498
2499        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2500        for rank in 0..self.ranks.len() {
2501            rank_outputs.push(run_resident_bf16_rank_device(
2502                &self.ranks[rank],
2503                &matrix.ranks[rank],
2504                &rank_inputs[rank],
2505                tokens,
2506                matrix.canonical_chunk_rows,
2507                self.bulk_p2p,
2508            )?);
2509        }
2510        Ok(rank_outputs)
2511    }
2512
2513    /// Keep Step BF16 column outputs resident on their owning TP ranks.
2514    ///
2515    /// Rank zero receives the host-canonical activation once and peer-broadcasts it when TP>1.
2516    /// Unlike [`Self::bf16_column_parallel_resident_native`], this method performs no output
2517    /// gather or readback. It is the correctness substrate for rank-local norm, RoPE, attention,
2518    /// and cache ownership; callers must not treat its existence as serving qualification.
2519    pub fn bf16_column_parallel_resident_device_shards(
2520        &self,
2521        matrix: &ResidentBf16ColumnParallel,
2522        activations: &[f32],
2523        tokens: usize,
2524    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2525        if self.ranks.len() > 1 && !self.native_p2p {
2526            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
2527        }
2528        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2529        validate_activations(activations, tokens, matrix.in_features)?;
2530
2531        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
2532        let root_input = {
2533            let root = &self.ranks[0];
2534            let _main = root.gpu.enter_main()?;
2535            root.htod(activations)?
2536        };
2537        // PRODUCER FENCE (2026-08-20 flake fix): the peer broadcast below reads this buffer from
2538        // the OTHER ranks' streams, and clone_htod is asynchronous on the root stream. Without
2539        // this fence a peer copy can overtake the in-flight H2D and replicate stale bytes — the
2540        // measured ~30%-of-boots prefill/decode argmax flake. Same discipline as
2541        // `upload_replicated_device_rows`.
2542        {
2543            let root = &self.ranks[0];
2544            let _main = root.gpu.enter_main()?;
2545            root.stream().synchronize()?;
2546        }
2547        rank_inputs.push(root_input);
2548        for engine in &self.ranks[1..] {
2549            let peer_input = {
2550                let _main = engine.gpu.enter_main()?;
2551                let mut peer_input = engine.uninit(activations.len())?;
2552                engine
2553                    .stream()
2554                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
2555                peer_input
2556            };
2557            rank_inputs.push(peer_input);
2558        }
2559
2560        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2561        for rank in 0..self.ranks.len() {
2562            rank_outputs.push(run_resident_bf16_rank_device(
2563                &self.ranks[rank],
2564                &matrix.ranks[rank],
2565                &rank_inputs[rank],
2566                tokens,
2567                matrix.canonical_chunk_rows,
2568                self.bulk_p2p,
2569            )?);
2570        }
2571        Ok(rank_outputs)
2572    }
2573
2574    /// Allocate one fixed-shape replicated batch without initializing its contents.
2575    ///
2576    /// Callers must refresh every rank before passing the batch to an operator.
2577    pub fn allocate_replicated_device_rows(
2578        &self,
2579        tokens: usize,
2580        width: usize,
2581    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2582        if self.ranks.len() > 1 && !self.native_p2p {
2583            return Err("replicated device rows require native P2P ranks".into());
2584        }
2585        let values = tokens
2586            .checked_mul(width)
2587            .ok_or("replicated device row size overflow")?;
2588        let rank_lengths = vec![values; self.ranks.len()];
2589        replicated_device_row_values(tokens, width, self.ranks.len(), &rank_lengths)?;
2590        let mut ranks = Vec::with_capacity(self.ranks.len());
2591        for engine in &self.ranks {
2592            let _main = engine.gpu.enter_main()?;
2593            ranks.push(engine.uninit(values)?);
2594        }
2595        Ok(ResidentReplicatedDeviceRows {
2596            ranks,
2597            tokens,
2598            width,
2599        })
2600    }
2601
2602    /// Replace a fixed-shape replicated batch from a root-device source.
2603    pub fn refresh_replicated_device_rows_from_root(
2604        &self,
2605        rows: &mut ResidentReplicatedDeviceRows,
2606        source: &CudaSlice<f32>,
2607    ) -> Result<(), Box<dyn std::error::Error>> {
2608        if self.ranks.len() > 1 && !self.native_p2p {
2609            return Err("replicated device rows require native P2P ranks".into());
2610        }
2611        validate_replicated_device_rows(&self.ranks, rows)?;
2612        let root = self
2613            .ranks
2614            .first()
2615            .ok_or("replicated rows have no root rank")?;
2616        let values = replicated_device_row_source_values(
2617            rows.tokens,
2618            rows.width,
2619            source.len(),
2620            source.ordinal(),
2621            root.ctx().ordinal(),
2622        )?;
2623        let (root_rows, peer_rows) = rows
2624            .ranks
2625            .split_first_mut()
2626            .ok_or("replicated rows have no root allocation")?;
2627        {
2628            let _main = root.gpu.enter_main()?;
2629            let mut destination = root_rows.slice_mut(0..values);
2630            root.stream()
2631                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
2632            root.stream().synchronize()?;
2633        }
2634        for (engine, peer_rows) in self.ranks.iter().skip(1).zip(peer_rows) {
2635            let _main = engine.gpu.enter_main()?;
2636            let mut destination = peer_rows.slice_mut(0..values);
2637            engine
2638                .stream()
2639                .memcpy_dtod(&root_rows.slice(0..values), &mut destination)?;
2640        }
2641        Ok(())
2642    }
2643
2644    /// Upload one canonical batch on rank zero and replicate it over native P2P.
2645    pub fn upload_replicated_device_rows(
2646        &self,
2647        rows: &[f32],
2648        tokens: usize,
2649        width: usize,
2650    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2651        if self.ranks.len() > 1 && !self.native_p2p {
2652            return Err("replicated device rows require native P2P ranks".into());
2653        }
2654        validate_activations(rows, tokens, width)?;
2655        let root = self
2656            .ranks
2657            .first()
2658            .ok_or("replicated rows have no root rank")?;
2659        let root_rows = {
2660            let _main = root.gpu.enter_main()?;
2661            root.htod(rows)?
2662        };
2663        {
2664            let _main = root.gpu.enter_main()?;
2665            root.stream().synchronize()?;
2666        }
2667        let mut ranks = Vec::with_capacity(self.ranks.len());
2668        ranks.push(root_rows);
2669        for engine in self.ranks.iter().skip(1) {
2670            let _main = engine.gpu.enter_main()?;
2671            let mut peer_rows = engine.uninit(rows.len())?;
2672            engine.stream().memcpy_dtod(&ranks[0], &mut peer_rows)?;
2673            ranks.push(peer_rows);
2674        }
2675        Ok(ResidentReplicatedDeviceRows {
2676            ranks,
2677            tokens,
2678            width,
2679        })
2680    }
2681
2682    /// Execute a column-parallel BF16 matrix directly from rank-local replicated inputs.
2683    pub fn bf16_column_parallel_resident_replicated_device_shards(
2684        &self,
2685        matrix: &ResidentBf16ColumnParallel,
2686        activations: &ResidentReplicatedDeviceRows,
2687    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2688        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2689        validate_replicated_device_rows(&self.ranks, activations)?;
2690        if activations.width != matrix.in_features {
2691            return Err(format!(
2692                "replicated BF16 column input width {} != matrix width {}",
2693                activations.width, matrix.in_features
2694            )
2695            .into());
2696        }
2697        let mut outputs = Vec::with_capacity(self.ranks.len());
2698        for rank in 0..self.ranks.len() {
2699            outputs.push(run_resident_bf16_rank_device(
2700                &self.ranks[rank],
2701                &matrix.ranks[rank],
2702                &activations.ranks[rank],
2703                activations.tokens,
2704                matrix.canonical_chunk_rows,
2705                self.bulk_p2p,
2706            )?);
2707        }
2708        Ok(outputs)
2709    }
2710
2711    /// Upload a BF16 router once on rank zero and retain its exact F32 expansion.
2712    #[allow(clippy::too_many_arguments)]
2713    pub fn upload_sigmoid_topk_router(
2714        &self,
2715        weight: Bf16Matrix<'_>,
2716        correction_bias: &[f32],
2717        active: Option<&[bool]>,
2718        experts_per_token: usize,
2719        scaling_factor: f32,
2720        route_norm: bool,
2721    ) -> Result<ResidentSigmoidTopKRouter, Box<dyn std::error::Error>> {
2722        weight.validate()?;
2723        if correction_bias.len() != weight.out_features
2724            || experts_per_token == 0
2725            || experts_per_token > weight.out_features
2726            || !correction_bias.iter().all(|value| value.is_finite())
2727            || !scaling_factor.is_finite()
2728            || scaling_factor <= 0.0
2729        {
2730            return Err(format!(
2731                "sigmoid router geometry weight={}x{} bias={} top_k={} scale={scaling_factor}",
2732                weight.out_features,
2733                weight.in_features,
2734                correction_bias.len(),
2735                experts_per_token,
2736            )
2737            .into());
2738        }
2739        let active_row = active
2740            .map(|mask| {
2741                if mask.len() != weight.out_features {
2742                    return Err(format!(
2743                        "sigmoid router active mask {} != experts {}",
2744                        mask.len(),
2745                        weight.out_features
2746                    ));
2747                }
2748                Ok(mask
2749                    .iter()
2750                    .map(|&enabled| u8::from(enabled))
2751                    .collect::<Vec<_>>())
2752            })
2753            .transpose()?
2754            .unwrap_or_else(|| vec![1; weight.out_features]);
2755        let active_count = active_row.iter().filter(|&&enabled| enabled != 0).count();
2756        crate::sigrouter_contract::validate_active_count(experts_per_token, active_count)?;
2757
2758        let root = self
2759            .ranks
2760            .first()
2761            .ok_or("sigmoid router runtime has no root rank")?;
2762        let _main = root.gpu.enter_main()?;
2763        let bf16 = root.htod_bytes(weight.bytes)?;
2764        let weight_f32 = root.bf16_to_f32(
2765            &bf16.slice(0..bf16.len()),
2766            weight.out_features * weight.in_features,
2767        )?;
2768        Ok(ResidentSigmoidTopKRouter {
2769            weight: weight_f32,
2770            correction_bias: root.htod(correction_bias)?,
2771            active: root.htod_bytes(&active_row)?,
2772            root_device: root.ctx().ordinal(),
2773            input_width: weight.in_features,
2774            expert_count: weight.out_features,
2775            experts_per_token,
2776            active_count,
2777            scaling_factor,
2778            route_norm,
2779        })
2780    }
2781
2782    /// Route rank-zero replicated rows and return the narrow host control result plus logits.
2783    ///
2784    /// The logits readback exists for independent oracle comparison. This method is a correctness
2785    /// surface; a serving scheduler may retain logits and selected routes on device.
2786    pub fn sigmoid_topk_replicated_device_rows_host(
2787        &self,
2788        router: &ResidentSigmoidTopKRouter,
2789        input: &ResidentReplicatedDeviceRows,
2790    ) -> Result<SigmoidTopKHostOutput, Box<dyn std::error::Error>> {
2791        validate_replicated_device_rows(&self.ranks, input)?;
2792        if input.width != router.input_width {
2793            return Err(format!(
2794                "sigmoid router input width {} != resident width {}",
2795                input.width, router.input_width
2796            )
2797            .into());
2798        }
2799        let root = self
2800            .ranks
2801            .first()
2802            .ok_or("sigmoid router runtime has no root rank")?;
2803        let _main = root.gpu.enter_main()?;
2804        if root.ctx().ordinal() != router.root_device
2805            || router.weight.ordinal() != router.root_device
2806            || router.correction_bias.ordinal() != router.root_device
2807            || router.active.ordinal() != router.root_device
2808        {
2809            return Err("sigmoid router root residency changed".into());
2810        }
2811        let logits = root.router_gemv(
2812            &router.weight,
2813            &input.ranks[0],
2814            router.input_width,
2815            router.expert_count,
2816            input.tokens,
2817        )?;
2818        let (selected, weights) = root.moe_router_sigmoid_topk_host(
2819            &logits,
2820            input.tokens,
2821            router.expert_count,
2822            router.experts_per_token,
2823            router.active_count,
2824            &router.correction_bias,
2825            &router.active,
2826            router.scaling_factor,
2827            router.route_norm,
2828        )?;
2829        Ok(SigmoidTopKHostOutput {
2830            logits: root.dtoh(&logits)?,
2831            selected,
2832            weights,
2833        })
2834    }
2835
2836    /// Replicate a full BF16 SwiGLU bank on every rank.
2837    pub fn upload_replicated_bf16_swiglu(
2838        &self,
2839        gate: Bf16Matrix<'_>,
2840        up: Bf16Matrix<'_>,
2841        down: Bf16Matrix<'_>,
2842    ) -> Result<ResidentReplicatedBf16SwiGlu, Box<dyn std::error::Error>> {
2843        gate.validate()?;
2844        up.validate()?;
2845        down.validate()?;
2846        if gate.in_features != up.in_features
2847            || gate.out_features != up.out_features
2848            || down.in_features != gate.out_features
2849            || down.out_features != gate.in_features
2850        {
2851            return Err(format!(
2852                "replicated BF16 SwiGLU geometry gate={}x{} up={}x{} down={}x{}",
2853                gate.out_features,
2854                gate.in_features,
2855                up.out_features,
2856                up.in_features,
2857                down.out_features,
2858                down.in_features,
2859            )
2860            .into());
2861        }
2862        let mut gate_ranks = Vec::with_capacity(self.ranks.len());
2863        let mut up_ranks = Vec::with_capacity(self.ranks.len());
2864        let mut down_ranks = Vec::with_capacity(self.ranks.len());
2865        for engine in &self.ranks {
2866            gate_ranks.push(upload_bf16_rank(engine, gate, false)?);
2867            up_ranks.push(upload_bf16_rank(engine, up, false)?);
2868            down_ranks.push(upload_bf16_rank(engine, down, false)?);
2869        }
2870        Ok(ResidentReplicatedBf16SwiGlu {
2871            gate: gate_ranks,
2872            up: up_ranks,
2873            down: down_ranks,
2874            input_width: gate.in_features,
2875            intermediate_width: gate.out_features,
2876        })
2877    }
2878
2879    /// Execute a fully replicated BF16 SwiGLU directly from replicated device rows.
2880    pub fn replicated_bf16_swiglu_resident_device(
2881        &self,
2882        mlp: &ResidentReplicatedBf16SwiGlu,
2883        input: &ResidentReplicatedDeviceRows,
2884        activation_limit: Option<f32>,
2885    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2886        validate_step_expert_activation_limit(activation_limit)?;
2887        validate_replicated_device_rows(&self.ranks, input)?;
2888        validate_resident_bf16_ranks(&self.ranks, &mlp.gate)?;
2889        validate_resident_bf16_ranks(&self.ranks, &mlp.up)?;
2890        validate_resident_bf16_ranks(&self.ranks, &mlp.down)?;
2891        if input.width != mlp.input_width
2892            || mlp.gate.len() != self.ranks.len()
2893            || mlp.up.len() != self.ranks.len()
2894            || mlp.down.len() != self.ranks.len()
2895        {
2896            return Err("replicated BF16 SwiGLU residency or input width changed".into());
2897        }
2898
2899        let mut outputs = Vec::with_capacity(self.ranks.len());
2900        for rank in 0..self.ranks.len() {
2901            let engine = &self.ranks[rank];
2902            let gate = run_resident_bf16_rank_device(
2903                engine,
2904                &mlp.gate[rank],
2905                &input.ranks[rank],
2906                input.tokens,
2907                None,
2908                self.bulk_p2p,
2909            )?;
2910            let up = run_resident_bf16_rank_device(
2911                engine,
2912                &mlp.up[rank],
2913                &input.ranks[rank],
2914                input.tokens,
2915                None,
2916                self.bulk_p2p,
2917            )?;
2918            let _main = engine.gpu.enter_main()?;
2919            let values = input
2920                .tokens
2921                .checked_mul(mlp.intermediate_width)
2922                .ok_or("replicated BF16 SwiGLU activation size overflow")?;
2923            let mut activation = engine.uninit(values)?;
2924            if let Some(limit) = activation_limit {
2925                engine.silu_clamped_mul_host_expf(&gate, &up, limit, &mut activation, values)?;
2926            } else {
2927                engine.silu_mul_host_expf(&gate, &up, &mut activation, values)?;
2928            }
2929            outputs.push(run_resident_bf16_rank_device(
2930                engine,
2931                &mlp.down[rank],
2932                &activation,
2933                input.tokens,
2934                None,
2935                self.bulk_p2p,
2936            )?);
2937        }
2938        Ok(ResidentReplicatedDeviceRows {
2939            ranks: outputs,
2940            tokens: input.tokens,
2941            width: mlp.input_width,
2942        })
2943    }
2944
2945    /// Apply the same RMS-norm row program independently on every replicated rank.
2946    pub fn rms_norm_replicated_device_rows(
2947        &self,
2948        input: &ResidentReplicatedDeviceRows,
2949        weight: &[f32],
2950        eps: f32,
2951    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2952        validate_replicated_device_rows(&self.ranks, input)?;
2953        if weight.len() != input.width || !eps.is_finite() || eps <= 0.0 {
2954            return Err(format!(
2955                "replicated RMS norm weight/eps {}/{} != width {}",
2956                weight.len(),
2957                eps,
2958                input.width
2959            )
2960            .into());
2961        }
2962        let mut ranks = Vec::with_capacity(self.ranks.len());
2963        for (rank, engine) in self.ranks.iter().enumerate() {
2964            let _main = engine.gpu.enter_main()?;
2965            let weight = engine.htod(weight)?;
2966            let mut output = engine.uninit(input.tokens * input.width)?;
2967            engine.rms_norm(
2968                &input.ranks[rank],
2969                &weight,
2970                &mut output,
2971                input.width,
2972                input.tokens,
2973                eps,
2974            )?;
2975            ranks.push(output);
2976        }
2977        Ok(ResidentReplicatedDeviceRows {
2978            ranks,
2979            tokens: input.tokens,
2980            width: input.width,
2981        })
2982    }
2983
2984    /// Add two replicated batches and RMS-normalize the exact residual on every rank.
2985    pub fn add_rms_norm_replicated_device_rows(
2986        &self,
2987        input: &ResidentReplicatedDeviceRows,
2988        update: &ResidentReplicatedDeviceRows,
2989        weight: &[f32],
2990        eps: f32,
2991    ) -> Result<
2992        (ResidentReplicatedDeviceRows, ResidentReplicatedDeviceRows),
2993        Box<dyn std::error::Error>,
2994    > {
2995        validate_replicated_device_rows(&self.ranks, input)?;
2996        validate_replicated_device_rows(&self.ranks, update)?;
2997        if input.tokens != update.tokens
2998            || input.width != update.width
2999            || weight.len() != input.width
3000            || !eps.is_finite()
3001            || eps <= 0.0
3002        {
3003            return Err(format!(
3004                "replicated add/RMS geometry input={}x{} update={}x{} weight={} eps={eps}",
3005                input.tokens,
3006                input.width,
3007                update.tokens,
3008                update.width,
3009                weight.len(),
3010            )
3011            .into());
3012        }
3013        let values = input.tokens * input.width;
3014        let mut residual_ranks = Vec::with_capacity(self.ranks.len());
3015        let mut normalized_ranks = Vec::with_capacity(self.ranks.len());
3016        for (rank, engine) in self.ranks.iter().enumerate() {
3017            let _main = engine.gpu.enter_main()?;
3018            let weight = engine.htod(weight)?;
3019            let mut residual = engine.uninit(values)?;
3020            let mut normalized = engine.uninit(values)?;
3021            engine.add_rms_norm(
3022                &input.ranks[rank],
3023                &update.ranks[rank],
3024                &weight,
3025                &mut residual,
3026                &mut normalized,
3027                input.width,
3028                input.tokens,
3029                eps,
3030            )?;
3031            residual_ranks.push(residual);
3032            normalized_ranks.push(normalized);
3033        }
3034        Ok((
3035            ResidentReplicatedDeviceRows {
3036                ranks: residual_ranks,
3037                tokens: input.tokens,
3038                width: input.width,
3039            },
3040            ResidentReplicatedDeviceRows {
3041                ranks: normalized_ranks,
3042                tokens: input.tokens,
3043                width: input.width,
3044            },
3045        ))
3046    }
3047
3048    pub fn collect_replicated_device_rows(
3049        &self,
3050        rows: &ResidentReplicatedDeviceRows,
3051    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
3052        validate_replicated_device_rows(&self.ranks, rows)?;
3053        let mut outputs = Vec::with_capacity(self.ranks.len());
3054        for (rank, engine) in self.ranks.iter().enumerate() {
3055            let _main = engine.gpu.enter_main()?;
3056            outputs.push(engine.dtoh(&rows.ranks[rank])?);
3057        }
3058        Ok(outputs)
3059    }
3060
3061    pub fn upload_bf16_row_parallel(
3062        &self,
3063        matrix: Bf16Matrix<'_>,
3064    ) -> Result<ResidentBf16RowParallel, Box<dyn std::error::Error>> {
3065        matrix.validate()?;
3066        let tp = self.ranks.len();
3067        if matrix.in_features % tp != 0 {
3068            return Err(format!(
3069                "BF16 row-parallel in_features {} is not divisible by TP={tp}",
3070                matrix.in_features
3071            )
3072            .into());
3073        }
3074        let mut ranks = Vec::with_capacity(tp);
3075        for (rank, engine) in self.ranks.iter().enumerate() {
3076            let shard = bf16_row_shard(matrix, tp, rank)?;
3077            ranks.push(upload_bf16_rank(
3078                engine,
3079                Bf16Matrix {
3080                    bytes: &shard,
3081                    out_features: matrix.out_features,
3082                    in_features: matrix.in_features / tp,
3083                },
3084                false,
3085            )?);
3086        }
3087        Ok(ResidentBf16RowParallel {
3088            ranks,
3089            out_features: matrix.out_features,
3090            in_features: matrix.in_features,
3091        })
3092    }
3093
3094    pub fn bf16_row_parallel_resident(
3095        &self,
3096        matrix: &ResidentBf16RowParallel,
3097        activations: &[f32],
3098        tokens: usize,
3099    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
3100        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
3101        validate_activations(activations, tokens, matrix.in_features)?;
3102        let tp = self.ranks.len();
3103        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
3104        let mut rank_partials = Vec::with_capacity(tp);
3105        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
3106            let local_activations =
3107                activation_shard(activations, tokens, matrix.in_features, tp, rank);
3108            let partial = run_resident_bf16_rank(engine, shard, &local_activations, tokens, None)?;
3109            for (sum, value) in reduced.iter_mut().zip(&partial) {
3110                *sum += value;
3111            }
3112            rank_partials.push(partial);
3113        }
3114        Ok(RowParallelResult {
3115            reduced,
3116            rank_partials,
3117        })
3118    }
3119
3120    /// Step-3.7 row projection split into the same eight global K blocks for TP1/TP2/TP4/TP8.
3121    pub fn upload_step_bf16_row_parallel(
3122        &self,
3123        matrix: Bf16Matrix<'_>,
3124    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3125        self.upload_step_bf16_row_parallel_inner(matrix, false)
3126    }
3127
3128    pub fn upload_step_bf16_row_parallel_f32_mirror(
3129        &self,
3130        matrix: Bf16Matrix<'_>,
3131    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3132        self.upload_step_bf16_row_parallel_inner(matrix, true)
3133    }
3134
3135    fn upload_step_bf16_row_parallel_inner(
3136        &self,
3137        matrix: Bf16Matrix<'_>,
3138        f32_mirror: bool,
3139    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3140        matrix.validate()?;
3141        let tp = self.ranks.len();
3142        let canonical_chunk_cols = step_bf16_canonical_chunk_cols(matrix.in_features, tp)?;
3143        let local_in = matrix.in_features / tp;
3144        let blocks_per_rank = local_in / canonical_chunk_cols;
3145        let mut ranks = Vec::with_capacity(tp);
3146        for (rank, engine) in self.ranks.iter().enumerate() {
3147            let mut blocks = Vec::with_capacity(blocks_per_rank);
3148            for block in 0..blocks_per_rank {
3149                let global_block = rank * blocks_per_rank + block;
3150                let col_start = global_block * canonical_chunk_cols;
3151                let bytes = bf16_row_block(matrix, col_start, canonical_chunk_cols)?;
3152                blocks.push(upload_bf16_rank(
3153                    engine,
3154                    Bf16Matrix {
3155                        bytes: &bytes,
3156                        out_features: matrix.out_features,
3157                        in_features: canonical_chunk_cols,
3158                    },
3159                    f32_mirror,
3160                )?);
3161            }
3162            ranks.push(blocks);
3163        }
3164        Ok(ResidentStepBf16RowParallel {
3165            ranks,
3166            out_features: matrix.out_features,
3167            in_features: matrix.in_features,
3168            canonical_chunk_cols,
3169        })
3170    }
3171
3172    /// Host-staged exactness twin of [`Self::step_bf16_row_parallel_resident_native`].
3173    ///
3174    /// Block inputs and partials cross host memory, but every partial is added on the root device
3175    /// in global checkpoint-column order. Native transport must reproduce this result bitwise.
3176    pub fn step_bf16_row_parallel_resident(
3177        &self,
3178        matrix: &ResidentStepBf16RowParallel,
3179        activations: &[f32],
3180        tokens: usize,
3181    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3182        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3183        validate_activations(activations, tokens, matrix.in_features)?;
3184        let root = &self.ranks[0];
3185        let output_len = tokens
3186            .checked_mul(matrix.out_features)
3187            .ok_or("Step BF16 row output size overflow")?;
3188        let mut reduced = {
3189            let _main = root.gpu.enter_main()?;
3190            root.htod(&vec![0.0f32; output_len])?
3191        };
3192        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3193        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3194            for (block, resident) in blocks.iter().enumerate() {
3195                let global_block = rank * blocks_per_rank + block;
3196                let input = activation_shard(
3197                    activations,
3198                    tokens,
3199                    matrix.in_features,
3200                    PRODUCT_MAX_CARDS,
3201                    global_block,
3202                );
3203                let partial =
3204                    run_resident_bf16_rank(&self.ranks[rank], resident, &input, tokens, None)?;
3205                let next = {
3206                    let _main = root.gpu.enter_main()?;
3207                    let partial = root.htod(&partial)?;
3208                    let mut next = root.uninit(output_len)?;
3209                    root.add(&reduced, &partial, &mut next, output_len)?;
3210                    next
3211                };
3212                reduced = next;
3213            }
3214        }
3215        let _main = root.gpu.enter_main()?;
3216        root.dtoh(&reduced)
3217    }
3218
3219    /// Native-P2P Step row projection with canonical global K-block reduction.
3220    ///
3221    /// The full activation is uploaded once on the root. Each TP8-sized block is peer-scattered
3222    /// to its owning rank, its BF16 partial is peer-returned to the root, and root-device adds
3223    /// replay the same eight-block order as TP1 and the host-staged oracle.
3224    pub fn step_bf16_row_parallel_resident_native(
3225        &self,
3226        matrix: &ResidentStepBf16RowParallel,
3227        activations: &[f32],
3228        tokens: usize,
3229    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3230        if self.ranks.len() > 1 && !self.native_p2p {
3231            return Err("native Step BF16 row parallelism requires P2P ranks".into());
3232        }
3233        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3234        validate_activations(activations, tokens, matrix.in_features)?;
3235        let root = &self.ranks[0];
3236        let root_input = {
3237            let _main = root.gpu.enter_main()?;
3238            root.htod(activations)?
3239        };
3240        // PRODUCER FENCE (2026-08-20 flake fix): the non-bulk arm below peer-reads root_input
3241        // from the other ranks' streams while root's clone_htod may still be in flight.
3242        {
3243            let _main = root.gpu.enter_main()?;
3244            root.stream().synchronize()?;
3245        }
3246        let reduced = self.step_bf16_row_native_reduce_from_root(matrix, &root_input, tokens)?;
3247        let _main = root.gpu.enter_main()?;
3248        root.dtoh(&reduced)
3249    }
3250
3251    /// Device-input twin of [`Self::step_bf16_row_parallel_resident_native`] (lane/
3252    /// hermes-perf-fixes, 2026-08-23): the full activation arrives as a ROOT-DEVICE buffer
3253    /// and the reduced output stays root-resident — no DtoH of the attention output, no
3254    /// host O staging, no re-upload. Byte-identical to the host-canonical arm by
3255    /// construction (same block scatter, kernels, and global TP8 reduction order; the root
3256    /// bytes are dtod-copied where the host arm htod'd the same bytes). Caller must have
3257    /// synchronized the producer stream; the root stream is synchronized before returning.
3258    pub fn step_bf16_row_parallel_resident_native_device(
3259        &self,
3260        matrix: &ResidentStepBf16RowParallel,
3261        root_activation: &CudaSlice<f32>,
3262        tokens: usize,
3263    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3264        if self.ranks.len() > 1 && !self.native_p2p {
3265            return Err("native Step BF16 row parallelism requires P2P ranks".into());
3266        }
3267        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3268        let values = tokens
3269            .checked_mul(matrix.in_features)
3270            .ok_or("device Step BF16 row activation size overflow")?;
3271        let root = &self.ranks[0];
3272        if tokens == 0
3273            || root_activation.len() < values
3274            || root_activation.ordinal() != root.ctx().ordinal()
3275        {
3276            return Err("device Step BF16 row root activation geometry mismatch".into());
3277        }
3278        let root_input = {
3279            let _main = root.gpu.enter_main()?;
3280            let mut root_input = root.uninit(values)?;
3281            root.stream()
3282                .memcpy_dtod(&root_activation.slice(0..values), &mut root_input)?;
3283            root.stream().synchronize()?; // producer fence, as the host-input twin
3284            root_input
3285        };
3286        let reduced = self.step_bf16_row_native_reduce_from_root(matrix, &root_input, tokens)?;
3287        let _main = root.gpu.enter_main()?;
3288        root.stream().synchronize()?;
3289        Ok(reduced)
3290    }
3291
3292    /// Shared core of the two native Step row arms above: block scatter + rank GEMMs +
3293    /// canonical global TP8-order root reduction, from a root-resident input, returning the
3294    /// root-resident reduced output. Extracted verbatim so the host and device twins cannot
3295    /// drift numerically.
3296    fn step_bf16_row_native_reduce_from_root(
3297        &self,
3298        matrix: &ResidentStepBf16RowParallel,
3299        root_input: &CudaSlice<f32>,
3300        tokens: usize,
3301    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3302        let root = &self.ranks[0];
3303        let output_len = tokens
3304            .checked_mul(matrix.out_features)
3305            .ok_or("native Step BF16 row output size overflow")?;
3306        let mut reduced = {
3307            let _main = root.gpu.enter_main()?;
3308            root.htod(&vec![0.0f32; output_len])?
3309        };
3310        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3311        let mut block_input_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3312        let mut root_packed_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3313        let mut remote_partial_keepalive = Vec::new();
3314        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3315            for (block, resident) in blocks.iter().enumerate() {
3316                let global_block = rank * blocks_per_rank + block;
3317                let col_start = global_block * matrix.canonical_chunk_cols;
3318                let block_len = tokens
3319                    .checked_mul(matrix.canonical_chunk_cols)
3320                    .ok_or("native Step BF16 row block size overflow")?;
3321                let block_input = if self.bulk_p2p {
3322                    let root_packed = {
3323                        let _main = root.gpu.enter_main()?;
3324                        let mut root_packed = root.uninit(block_len)?;
3325                        root.copy_rows_strided(
3326                            &root_input,
3327                            &mut root_packed,
3328                            matrix.canonical_chunk_cols,
3329                            tokens,
3330                            matrix.in_features,
3331                            col_start,
3332                        )?;
3333                        root_packed
3334                    };
3335                    if rank == 0 {
3336                        root_packed
3337                    } else {
3338                        // PRODUCER FENCE (2026-08-20 flake fix): the pack kernel runs on the
3339                        // root stream; this rank's peer read must not overtake it.
3340                        {
3341                            let _main = root.gpu.enter_main()?;
3342                            root.stream().synchronize()?;
3343                        }
3344                        let engine = &self.ranks[rank];
3345                        let _main = engine.gpu.enter_main()?;
3346                        let mut block_input = engine.uninit(block_len)?;
3347                        engine
3348                            .stream()
3349                            .memcpy_dtod(&root_packed, &mut block_input)?;
3350                        root_packed_keepalive.push(root_packed);
3351                        block_input
3352                    }
3353                } else {
3354                    let engine = &self.ranks[rank];
3355                    let _main = engine.gpu.enter_main()?;
3356                    let mut block_input = engine.uninit(block_len)?;
3357                    for token in 0..tokens {
3358                        let source_start = token * matrix.in_features + col_start;
3359                        let source = root_input
3360                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3361                        let destination_start = token * matrix.canonical_chunk_cols;
3362                        let mut destination = block_input.slice_mut(
3363                            destination_start..destination_start + matrix.canonical_chunk_cols,
3364                        );
3365                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3366                    }
3367                    block_input
3368                };
3369                let partial = run_resident_bf16_rank_device(
3370                    &self.ranks[rank],
3371                    resident,
3372                    &block_input,
3373                    tokens,
3374                    None,
3375                    self.bulk_p2p,
3376                )?;
3377                block_input_keepalive.push(block_input);
3378                let root_partial = if rank == 0 {
3379                    partial
3380                } else {
3381                    // PRODUCER FENCE (2026-08-20 flake fix): the partial was produced by this
3382                    // rank's kernel on its own stream; root's peer read must not overtake it.
3383                    {
3384                        let engine = &self.ranks[rank];
3385                        let _main = engine.gpu.enter_main()?;
3386                        engine.stream().synchronize()?;
3387                    }
3388                    let _main = root.gpu.enter_main()?;
3389                    let mut peer_partial = root.uninit(output_len)?;
3390                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3391                    remote_partial_keepalive.push(partial);
3392                    peer_partial
3393                };
3394                let next = {
3395                    let _main = root.gpu.enter_main()?;
3396                    let mut next = root.uninit(output_len)?;
3397                    root.add(&reduced, &root_partial, &mut next, output_len)?;
3398                    next
3399                };
3400                reduced = next;
3401            }
3402        }
3403        {
3404            let _main = root.gpu.enter_main()?;
3405            root.stream().synchronize()?;
3406        }
3407        drop(remote_partial_keepalive);
3408        drop(root_packed_keepalive);
3409        drop(block_input_keepalive);
3410        Ok(reduced)
3411    }
3412
3413    /// Reduce rank-local Step attention shards in canonical TP8 K-block order and keep the result
3414    /// on the root device.
3415    pub fn step_bf16_row_parallel_resident_root_device(
3416        &self,
3417        matrix: &ResidentStepBf16RowParallel,
3418        rank_activations: &[CudaSlice<f32>],
3419        tokens: usize,
3420    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3421        if self.ranks.len() > 1 && !self.native_p2p {
3422            return Err(
3423                "device-resident Step BF16 row parallelism requires native P2P ranks".into(),
3424            );
3425        }
3426        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3427        let local_width = matrix.in_features / self.ranks.len();
3428        let shard_len = tokens
3429            .checked_mul(local_width)
3430            .ok_or("device Step BF16 row shard size overflow")?;
3431        if tokens == 0
3432            || rank_activations.len() != self.ranks.len()
3433            || rank_activations
3434                .iter()
3435                .zip(&self.ranks)
3436                .any(|(rows, engine)| {
3437                    rows.len() != shard_len || rows.ordinal() != engine.ctx().ordinal()
3438                })
3439        {
3440            return Err("device Step BF16 row activation shard geometry changed".into());
3441        }
3442
3443        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3444        let mut block_inputs = Vec::with_capacity(self.ranks.len());
3445        let mut partials = Vec::with_capacity(self.ranks.len());
3446        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3447            if blocks.len() != blocks_per_rank {
3448                return Err(format!(
3449                    "device Step BF16 row rank {rank} blocks {} != {blocks_per_rank}",
3450                    blocks.len()
3451                )
3452                .into());
3453            }
3454            let engine = &self.ranks[rank];
3455            let _main = engine.gpu.enter_main()?;
3456            let mut rank_inputs = Vec::with_capacity(blocks_per_rank);
3457            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
3458            for (block, resident) in blocks.iter().enumerate() {
3459                let block_len = tokens
3460                    .checked_mul(matrix.canonical_chunk_cols)
3461                    .ok_or("device Step BF16 row block size overflow")?;
3462                let mut block_input = engine.uninit(block_len)?;
3463                let local_col_start = block * matrix.canonical_chunk_cols;
3464                if self.bulk_p2p {
3465                    engine.copy_rows_strided(
3466                        &rank_activations[rank],
3467                        &mut block_input,
3468                        matrix.canonical_chunk_cols,
3469                        tokens,
3470                        local_width,
3471                        local_col_start,
3472                    )?;
3473                } else {
3474                    for token in 0..tokens {
3475                        let source_start = token * local_width + local_col_start;
3476                        let source = rank_activations[rank]
3477                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3478                        let destination_start = token * matrix.canonical_chunk_cols;
3479                        let mut destination = block_input.slice_mut(
3480                            destination_start..destination_start + matrix.canonical_chunk_cols,
3481                        );
3482                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3483                    }
3484                }
3485                let partial = run_resident_bf16_rank_device(
3486                    engine,
3487                    resident,
3488                    &block_input,
3489                    tokens,
3490                    None,
3491                    self.bulk_p2p,
3492                )?;
3493                rank_inputs.push(block_input);
3494                rank_partials.push(partial);
3495            }
3496            block_inputs.push(rank_inputs);
3497            partials.push(rank_partials);
3498        }
3499        for engine in self.ranks.iter().skip(1) {
3500            let _main = engine.gpu.enter_main()?;
3501            engine.stream().synchronize()?;
3502        }
3503
3504        let output_len = tokens
3505            .checked_mul(matrix.out_features)
3506            .ok_or("device Step BF16 row output size overflow")?;
3507        let root = &self.ranks[0];
3508        let _main = root.gpu.enter_main()?;
3509        let mut reduced = root.htod(&vec![0.0f32; output_len])?;
3510        let mut remote_partials = Vec::new();
3511        for (rank, rank_partials) in partials.into_iter().enumerate() {
3512            for partial in rank_partials {
3513                let root_partial = if rank == 0 {
3514                    partial
3515                } else {
3516                    let mut peer_partial = root.uninit(output_len)?;
3517                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3518                    remote_partials.push(partial);
3519                    peer_partial
3520                };
3521                let mut next = root.uninit(output_len)?;
3522                root.add(&reduced, &root_partial, &mut next, output_len)?;
3523                reduced = next;
3524            }
3525        }
3526        root.stream().synchronize()?;
3527        drop(remote_partials);
3528        drop(block_inputs);
3529        Ok(reduced)
3530    }
3531
3532    /// Reduce rank-local Step attention shards, then replicate the canonical root result.
3533    pub fn step_bf16_row_parallel_resident_replicated_device(
3534        &self,
3535        matrix: &ResidentStepBf16RowParallel,
3536        rank_activations: &[CudaSlice<f32>],
3537        tokens: usize,
3538    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3539        let reduced =
3540            self.step_bf16_row_parallel_resident_root_device(matrix, rank_activations, tokens)?;
3541        let output_len = tokens
3542            .checked_mul(matrix.out_features)
3543            .ok_or("device Step BF16 row output size overflow")?;
3544        let mut ranks = Vec::with_capacity(self.ranks.len());
3545        ranks.push(reduced);
3546        for engine in self.ranks.iter().skip(1) {
3547            let _main = engine.gpu.enter_main()?;
3548            let mut peer_output = engine.uninit(output_len)?;
3549            engine.stream().memcpy_dtod(&ranks[0], &mut peer_output)?;
3550            ranks.push(peer_output);
3551        }
3552        Ok(ResidentReplicatedDeviceRows {
3553            ranks,
3554            tokens,
3555            width: matrix.out_features,
3556        })
3557    }
3558
3559    pub fn upload_expert(
3560        &self,
3561        gate: E4m3BlockMatrix<'_>,
3562        up: E4m3BlockMatrix<'_>,
3563        down: E4m3BlockMatrix<'_>,
3564    ) -> Result<ResidentTpExpert, Box<dyn std::error::Error>> {
3565        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3566            return Err("TP expert gate/up dimensions differ".into());
3567        }
3568        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3569            return Err(format!(
3570                "TP expert down {}x{} does not invert gate/up {}x{}",
3571                down.out_features, down.in_features, gate.out_features, gate.in_features
3572            )
3573            .into());
3574        }
3575        Ok(ResidentTpExpert {
3576            gate: self.upload_column_parallel(gate)?,
3577            up: self.upload_column_parallel(up)?,
3578            down: self.upload_row_parallel(down)?,
3579            input_width: gate.in_features,
3580            expert_width: gate.out_features,
3581        })
3582    }
3583
3584    pub fn run_expert(
3585        &self,
3586        expert: &ResidentTpExpert,
3587        input: &[f32],
3588        tokens: usize,
3589    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3590        validate_activations(input, tokens, expert.input_width)?;
3591        let gate = self.column_parallel_resident(&expert.gate, input, tokens)?;
3592        let up = self.column_parallel_resident(&expert.up, input, tokens)?;
3593        let activated: Vec<f32> = gate
3594            .gathered
3595            .iter()
3596            .zip(&up.gathered)
3597            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
3598            .collect();
3599        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
3600        Ok(self
3601            .row_parallel_resident(&expert.down, &activated, tokens)?
3602            .reduced)
3603    }
3604
3605    pub fn upload_expert_parallel(
3606        &self,
3607        gate: E4m3ExpertBank<'_>,
3608        up: E4m3ExpertBank<'_>,
3609        down: E4m3ExpertBank<'_>,
3610    ) -> Result<ResidentExpertParallel, Box<dyn std::error::Error>> {
3611        gate.validate()?;
3612        up.validate()?;
3613        down.validate()?;
3614        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
3615            return Err("EP gate/up/down expert counts differ".into());
3616        }
3617        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3618            return Err("EP gate/up dimensions differ".into());
3619        }
3620        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3621            return Err(format!(
3622                "EP down {}x{} does not invert gate/up {}x{}",
3623                down.out_features, down.in_features, gate.out_features, gate.in_features
3624            )
3625            .into());
3626        }
3627        if gate.expert_count % self.ranks.len() != 0 {
3628            return Err(format!(
3629                "EP expert count {} is not divisible by {} ranks",
3630                gate.expert_count,
3631                self.ranks.len()
3632            )
3633            .into());
3634        }
3635
3636        let per_rank = gate.expert_count / self.ranks.len();
3637        let mut ranks = Vec::with_capacity(self.ranks.len());
3638        for (rank, engine) in self.ranks.iter().enumerate() {
3639            let expert_range = rank * per_rank..(rank + 1) * per_rank;
3640            ranks.push(ResidentEpRank {
3641                gate: upload_expert_bank_rank(engine, gate, expert_range.clone())?,
3642                up: upload_expert_bank_rank(engine, up, expert_range.clone())?,
3643                down: upload_expert_bank_rank(engine, down, expert_range)?,
3644            });
3645        }
3646        Ok(ResidentExpertParallel {
3647            ranks,
3648            expert_count: gate.expert_count,
3649            input_width: gate.in_features,
3650            expert_width: gate.out_features,
3651        })
3652    }
3653
3654    /// Prepare the official Step gate-only grouped-FP8 projection oracle on rank zero.
3655    ///
3656    /// This intentionally does not alter the resident EP path. It owns a full rank-local tensor
3657    /// bank solely so the grouped projection can be compared with the existing per-route oracle
3658    /// without routing, transport, or combine changing underneath it.
3659    #[allow(clippy::too_many_arguments)]
3660    pub fn prepare_step_grouped_fp8_gate(
3661        &self,
3662        gate: E4m3ExpertBank<'_>,
3663        up: E4m3ExpertBank<'_>,
3664        down: E4m3ExpertBank<'_>,
3665        input: &[f32],
3666        tokens: usize,
3667        selected: &[usize],
3668        activation_limit: Option<f32>,
3669    ) -> Result<PreparedStepGroupedFp8Gate, Box<dyn std::error::Error>> {
3670        gate.validate()?;
3671        up.validate()?;
3672        down.validate()?;
3673        validate_step_expert_activation_limit(activation_limit)?;
3674        if gate.expert_count != STEP_GROUPED_FP8_EXPERTS
3675            || up.expert_count != STEP_GROUPED_FP8_EXPERTS
3676            || down.expert_count != STEP_GROUPED_FP8_EXPERTS
3677        {
3678            return Err(format!(
3679                "official Step grouped FP8 gate requires {STEP_GROUPED_FP8_EXPERTS} experts, \
3680                 got gate/up/down={}/{}/{}",
3681                gate.expert_count, up.expert_count, down.expert_count,
3682            )
3683            .into());
3684        }
3685        if gate.in_features != up.in_features
3686            || gate.out_features != STEP_GROUPED_FP8_WIDTH
3687            || up.out_features != STEP_GROUPED_FP8_WIDTH
3688            || down.in_features != STEP_GROUPED_FP8_WIDTH
3689            || down.out_features != gate.in_features
3690        {
3691            return Err(format!(
3692                "official Step grouped FP8 geometry gate={}x{} up={}x{} down={}x{}",
3693                gate.out_features,
3694                gate.in_features,
3695                up.out_features,
3696                up.in_features,
3697                down.out_features,
3698                down.in_features,
3699            )
3700            .into());
3701        }
3702        validate_activations(input, tokens, gate.in_features)?;
3703        let pairs = tokens
3704            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3705            .ok_or("official Step grouped FP8 route count overflow")?;
3706        if selected.len() != pairs {
3707            return Err(format!(
3708                "official Step grouped FP8 routes {} != {tokens}x{STEP_GROUPED_FP8_TOP_K} \
3709                 ({pairs})",
3710                selected.len()
3711            )
3712            .into());
3713        }
3714        for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
3715            let mut unique = routes.to_vec();
3716            unique.sort_unstable();
3717            unique.dedup();
3718            if unique.len() != STEP_GROUPED_FP8_TOP_K {
3719                return Err(format!(
3720                    "official Step grouped FP8 token {token} routes are not top-8 unique: \
3721                     {routes:?}"
3722                )
3723                .into());
3724            }
3725        }
3726
3727        let engine = self
3728            .ranks
3729            .first()
3730            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3731        let _main = engine.gpu.enter_main()?;
3732        let expert_range = 0..STEP_GROUPED_FP8_EXPERTS;
3733        let gate = upload_expert_bank_rank(engine, gate, expert_range.clone())?;
3734        let up = upload_expert_bank_rank(engine, up, expert_range.clone())?;
3735        let down = upload_expert_bank_rank(engine, down, expert_range)?;
3736        let input = engine.htod(input)?;
3737        let route_csr = ExpertCsr::from_token_routes(
3738            STEP_GROUPED_FP8_EXPERTS,
3739            tokens,
3740            STEP_GROUPED_FP8_TOP_K,
3741            selected,
3742        )?
3743        .upload(engine)?;
3744        let pair_rows = (0..pairs).collect::<Vec<_>>();
3745        let down_csr =
3746            ExpertCsr::from_pair_rows(STEP_GROUPED_FP8_EXPERTS, pairs, selected, &pair_rows)?
3747                .upload(engine)?;
3748        let gate_workspace =
3749            Fp8GroupedWorkspace::new(engine, gate.in_features, gate.out_features, tokens, pairs)?;
3750        let up_workspace =
3751            Fp8GroupedWorkspace::new(engine, up.in_features, up.out_features, tokens, pairs)?;
3752        let down_workspace =
3753            Fp8GroupedWorkspace::new(engine, down.in_features, down.out_features, pairs, pairs)?;
3754        let activation = engine.uninit(pairs * STEP_GROUPED_FP8_WIDTH)?;
3755        Ok(PreparedStepGroupedFp8Gate {
3756            device: engine.ctx().ordinal(),
3757            gate,
3758            up,
3759            down,
3760            input,
3761            route_csr,
3762            down_csr,
3763            gate_workspace,
3764            up_workspace,
3765            down_workspace,
3766            activation,
3767            activation_limit,
3768            tokens,
3769            pairs,
3770        })
3771    }
3772
3773    /// Execute one prepared gate/up/activation/down projection sequence on rank zero.
3774    pub fn run_step_grouped_fp8_gate(
3775        &self,
3776        plan: &mut PreparedStepGroupedFp8Gate,
3777    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
3778        let engine = self
3779            .ranks
3780            .first()
3781            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3782        if engine.ctx().ordinal() != plan.device {
3783            return Err(format!(
3784                "official Step grouped FP8 plan device {} != rank-zero device {}",
3785                plan.device,
3786                engine.ctx().ordinal()
3787            )
3788            .into());
3789        }
3790        let _main = engine.gpu.enter_main()?;
3791
3792        plan.gate_workspace.quantize(engine, &plan.input)?;
3793        plan.gate_workspace.project(
3794            engine,
3795            &plan.gate.codes,
3796            &plan.gate.scales,
3797            &plan.route_csr,
3798            plan.gate.code_stride,
3799            plan.gate.scale_stride,
3800            1.0,
3801        )?;
3802        plan.up_workspace.quantize(engine, &plan.input)?;
3803        plan.up_workspace.project(
3804            engine,
3805            &plan.up.codes,
3806            &plan.up.scales,
3807            &plan.route_csr,
3808            plan.up.code_stride,
3809            plan.up.scale_stride,
3810            1.0,
3811        )?;
3812        if let Some(limit) = plan.activation_limit {
3813            engine.silu_clamped_mul_host_expf(
3814                plan.gate_workspace.output(),
3815                plan.up_workspace.output(),
3816                limit,
3817                &mut plan.activation,
3818                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3819            )?;
3820        } else {
3821            engine.silu_mul_host_expf(
3822                plan.gate_workspace.output(),
3823                plan.up_workspace.output(),
3824                &mut plan.activation,
3825                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3826            )?;
3827        }
3828        plan.down_workspace.quantize(engine, &plan.activation)?;
3829        plan.down_workspace.project(
3830            engine,
3831            &plan.down.codes,
3832            &plan.down.scales,
3833            &plan.down_csr,
3834            plan.down.code_stride,
3835            plan.down.scale_stride,
3836            1.0,
3837        )?;
3838
3839        Ok(StepGroupedFp8ProjectionOutput {
3840            gate: engine.dtoh(plan.gate_workspace.output())?,
3841            up: engine.dtoh(plan.up_workspace.output())?,
3842            down: engine.dtoh(plan.down_workspace.output())?,
3843        })
3844    }
3845
3846    pub fn prepare_step_grouped_expert_parallel_gate(
3847        &self,
3848        experts: &ResidentExpertParallel,
3849        input: &[f32],
3850        tokens: usize,
3851        selected: &[usize],
3852        activation_limit: Option<f32>,
3853    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3854        self.prepare_step_grouped_expert_parallel_gate_with_capacity(
3855            experts,
3856            input,
3857            tokens,
3858            selected,
3859            activation_limit,
3860            tokens,
3861        )
3862    }
3863
3864    #[allow(clippy::too_many_arguments)]
3865    pub fn prepare_step_grouped_expert_parallel_gate_with_capacity(
3866        &self,
3867        experts: &ResidentExpertParallel,
3868        input: &[f32],
3869        tokens: usize,
3870        selected: &[usize],
3871        activation_limit: Option<f32>,
3872        max_tokens: usize,
3873    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3874        if !self.native_p2p || !self.ep_device_arithmetic {
3875            return Err(
3876                "Step owner-grouped FP8 requires native P2P and device-resident arithmetic".into(),
3877            );
3878        }
3879        validate_step_expert_activation_limit(activation_limit)?;
3880        validate_ep_residency(&self.ranks, experts)?;
3881        validate_activations(input, tokens, experts.input_width)?;
3882        if max_tokens < tokens || max_tokens > i32::MAX as usize {
3883            return Err(format!(
3884                "official Step owner-grouped FP8 tokens {tokens} exceed capacity {max_tokens}"
3885            )
3886            .into());
3887        }
3888        if experts.expert_count != STEP_GROUPED_FP8_EXPERTS
3889            || experts.expert_width != STEP_GROUPED_FP8_WIDTH
3890        {
3891            return Err(format!(
3892                "official Step owner-grouped FP8 requires {} experts at width {}, got {} at {}",
3893                STEP_GROUPED_FP8_EXPERTS,
3894                STEP_GROUPED_FP8_WIDTH,
3895                experts.expert_count,
3896                experts.expert_width,
3897            )
3898            .into());
3899        }
3900        validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
3901        let max_pairs = max_tokens
3902            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3903            .ok_or("official Step owner-grouped FP8 capacity route count overflow")?;
3904        let input_capacity = max_tokens
3905            .checked_mul(experts.input_width)
3906            .ok_or("official Step owner-grouped FP8 input capacity overflow")?;
3907
3908        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
3909        for engine in &self.ranks {
3910            let _main = engine.gpu.enter_main()?;
3911            rank_inputs.push(engine.uninit(input_capacity)?);
3912        }
3913
3914        let mut owners = Vec::with_capacity(self.ranks.len());
3915        for (owner_rank, rank) in experts.ranks.iter().enumerate() {
3916            if rank.gate.expert_range != rank.up.expert_range
3917                || rank.gate.expert_range != rank.down.expert_range
3918            {
3919                return Err(format!(
3920                    "owner-grouped FP8 rank {} gate/up/down expert ranges differ",
3921                    owner_rank
3922                )
3923                .into());
3924            }
3925            let local_experts = rank.gate.expert_range.len();
3926            let engine = &self.ranks[owner_rank];
3927            let _main = engine.gpu.enter_main()?;
3928            let route_csr =
3929                DeviceExpertCsr::with_capacity(engine, local_experts, max_tokens, max_pairs)?;
3930            let down_csr =
3931                DeviceExpertCsr::with_capacity(engine, local_experts, max_pairs, max_pairs)?;
3932            let gate_workspace = Fp8GroupedWorkspace::new(
3933                engine,
3934                experts.input_width,
3935                experts.expert_width,
3936                max_tokens,
3937                max_pairs,
3938            )?;
3939            let up_workspace = Fp8GroupedWorkspace::new(
3940                engine,
3941                experts.input_width,
3942                experts.expert_width,
3943                max_tokens,
3944                max_pairs,
3945            )?;
3946            let down_workspace = Fp8GroupedWorkspace::new(
3947                engine,
3948                experts.expert_width,
3949                experts.input_width,
3950                max_pairs,
3951                max_pairs,
3952            )?;
3953            let activation = engine.uninit(
3954                max_pairs
3955                    .checked_mul(experts.expert_width)
3956                    .ok_or("official Step owner-grouped FP8 activation capacity overflow")?,
3957            )?;
3958            owners.push(PreparedStepGroupedExpertOwner {
3959                rank: owner_rank,
3960                global_pairs: Vec::new(),
3961                route_csr,
3962                down_csr,
3963                gate_workspace,
3964                up_workspace,
3965                down_workspace,
3966                activation,
3967            });
3968        }
3969
3970        let mut plan = PreparedStepGroupedExpertParallelGate {
3971            rank_inputs,
3972            owners,
3973            activation_limit,
3974            tokens: 0,
3975            pairs: 0,
3976            max_tokens,
3977            max_pairs,
3978            input_width: experts.input_width,
3979            expert_width: experts.expert_width,
3980            generation: 0,
3981            executed_generation: None,
3982            ready: false,
3983        };
3984        self.refresh_step_grouped_expert_parallel_gate(
3985            experts, &mut plan, input, tokens, selected,
3986        )?;
3987        Ok(plan)
3988    }
3989
3990    fn prepare_step_grouped_expert_parallel_refresh(
3991        &self,
3992        experts: &ResidentExpertParallel,
3993        plan: &PreparedStepGroupedExpertParallelGate,
3994        tokens: usize,
3995        selected: &[usize],
3996    ) -> Result<(usize, u64, Vec<Option<StepGroupedExpertOwnerSchedule>>), Box<dyn std::error::Error>>
3997    {
3998        validate_ep_residency(&self.ranks, experts)?;
3999        if plan.rank_inputs.len() != self.ranks.len()
4000            || plan.owners.len() != self.ranks.len()
4001            || plan.input_width != experts.input_width
4002            || plan.expert_width != experts.expert_width
4003            || tokens > plan.max_tokens
4004        {
4005            return Err(format!(
4006                "Step owner-grouped FP8 refresh geometry changed ranks={}/{} owners={}/{} \
4007                 input={}/{} expert={}/{} tokens={}/{}",
4008                plan.rank_inputs.len(),
4009                self.ranks.len(),
4010                plan.owners.len(),
4011                self.ranks.len(),
4012                plan.input_width,
4013                experts.input_width,
4014                plan.expert_width,
4015                experts.expert_width,
4016                tokens,
4017                plan.max_tokens,
4018            )
4019            .into());
4020        }
4021        let pairs = validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
4022        if pairs > plan.max_pairs {
4023            return Err(format!(
4024                "Step owner-grouped FP8 route count {pairs} exceeds capacity {}",
4025                plan.max_pairs
4026            )
4027            .into());
4028        }
4029        let next_generation = plan
4030            .generation
4031            .checked_add(1)
4032            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
4033        let owner_routes = partition_expert_owner_routes(
4034            experts.expert_count,
4035            self.ranks.len(),
4036            tokens,
4037            STEP_GROUPED_FP8_TOP_K,
4038            selected,
4039        )?;
4040        let mut schedules = Vec::with_capacity(self.ranks.len());
4041        for routes in owner_routes {
4042            if routes.selected.is_empty() {
4043                schedules.push(None);
4044                continue;
4045            }
4046            let local_experts = experts.ranks[routes.rank].gate.expert_range.len();
4047            let local_pairs = routes.selected.len();
4048            let route_csr = ExpertCsr::from_pair_rows(
4049                local_experts,
4050                tokens,
4051                &routes.selected,
4052                &routes.token_rows,
4053            )?;
4054            let down_rows = (0..local_pairs).collect::<Vec<_>>();
4055            let down_csr = ExpertCsr::from_pair_rows(
4056                local_experts,
4057                local_pairs,
4058                &routes.selected,
4059                &down_rows,
4060            )?;
4061            schedules.push(Some(StepGroupedExpertOwnerSchedule {
4062                global_pairs: routes.global_pairs,
4063                route_csr,
4064                down_csr,
4065            }));
4066        }
4067        Ok((pairs, next_generation, schedules))
4068    }
4069
4070    fn commit_step_grouped_expert_parallel_refresh(
4071        &self,
4072        plan: &mut PreparedStepGroupedExpertParallelGate,
4073        tokens: usize,
4074        pairs: usize,
4075        next_generation: u64,
4076        schedules: Vec<Option<StepGroupedExpertOwnerSchedule>>,
4077    ) -> Result<(), Box<dyn std::error::Error>> {
4078        for (owner, schedule) in plan.owners.iter_mut().zip(schedules) {
4079            let engine = &self.ranks[owner.rank];
4080            let _main = engine.gpu.enter_main()?;
4081            if let Some(schedule) = schedule {
4082                owner.route_csr.refresh(engine, &schedule.route_csr)?;
4083                owner.down_csr.refresh(engine, &schedule.down_csr)?;
4084                owner.global_pairs = schedule.global_pairs;
4085            } else {
4086                owner.route_csr.clear();
4087                owner.down_csr.clear();
4088                owner.global_pairs.clear();
4089            }
4090        }
4091        plan.tokens = tokens;
4092        plan.pairs = pairs;
4093        plan.generation = next_generation;
4094        plan.ready = true;
4095        Ok(())
4096    }
4097
4098    pub fn refresh_step_grouped_expert_parallel_gate(
4099        &self,
4100        experts: &ResidentExpertParallel,
4101        plan: &mut PreparedStepGroupedExpertParallelGate,
4102        input: &[f32],
4103        tokens: usize,
4104        selected: &[usize],
4105    ) -> Result<(), Box<dyn std::error::Error>> {
4106        validate_activations(input, tokens, experts.input_width)?;
4107        let (pairs, next_generation, schedules) =
4108            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
4109
4110        plan.ready = false;
4111        plan.executed_generation = None;
4112        {
4113            let root = &self.ranks[0];
4114            let _main = root.gpu.enter_main()?;
4115            let mut destination = plan.rank_inputs[0].slice_mut(0..input.len());
4116            root.stream().memcpy_htod(input, &mut destination)?;
4117            root.stream().synchronize()?;
4118        }
4119        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
4120        let root_input = &root_inputs[0];
4121        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
4122            let engine = &self.ranks[rank + 1];
4123            let _main = engine.gpu.enter_main()?;
4124            let mut destination = peer_input.slice_mut(0..input.len());
4125            engine
4126                .stream()
4127                .memcpy_dtod(&root_input.slice(0..input.len()), &mut destination)?;
4128        }
4129        self.commit_step_grouped_expert_parallel_refresh(
4130            plan,
4131            tokens,
4132            pairs,
4133            next_generation,
4134            schedules,
4135        )
4136    }
4137
4138    /// Refresh routes and inputs from an already-resident rank-zero activation.
4139    ///
4140    /// The caller must order the source producer before this call. The root copy is completed
4141    /// before peer dispatch, while CSR and workspace allocations retain their stable addresses.
4142    pub fn refresh_step_grouped_expert_parallel_gate_from_root_device(
4143        &self,
4144        experts: &ResidentExpertParallel,
4145        plan: &mut PreparedStepGroupedExpertParallelGate,
4146        input: &CudaSlice<f32>,
4147        tokens: usize,
4148        selected: &[usize],
4149    ) -> Result<(), Box<dyn std::error::Error>> {
4150        let input_values = tokens
4151            .checked_mul(experts.input_width)
4152            .ok_or("Step owner-grouped FP8 input size overflow")?;
4153        let root = self
4154            .ranks
4155            .first()
4156            .ok_or("Step owner-grouped FP8 runtime has no root rank")?;
4157        if input.len() < input_values || input.ordinal() != root.ctx().ordinal() {
4158            return Err(format!(
4159                "Step owner-grouped FP8 root input len/device {}/{} does not cover {} values on \
4160                 device {}",
4161                input.len(),
4162                input.ordinal(),
4163                input_values,
4164                root.ctx().ordinal(),
4165            )
4166            .into());
4167        }
4168        let (pairs, next_generation, schedules) =
4169            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
4170
4171        plan.ready = false;
4172        plan.executed_generation = None;
4173        {
4174            let _main = root.gpu.enter_main()?;
4175            let mut destination = plan.rank_inputs[0].slice_mut(0..input_values);
4176            root.stream()
4177                .memcpy_dtod(&input.slice(0..input_values), &mut destination)?;
4178            root.stream().synchronize()?;
4179        }
4180        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
4181        let root_input = &root_inputs[0];
4182        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
4183            let engine = &self.ranks[rank + 1];
4184            let _main = engine.gpu.enter_main()?;
4185            let mut destination = peer_input.slice_mut(0..input_values);
4186            engine
4187                .stream()
4188                .memcpy_dtod(&root_input.slice(0..input_values), &mut destination)?;
4189        }
4190        self.commit_step_grouped_expert_parallel_refresh(
4191            plan,
4192            tokens,
4193            pairs,
4194            next_generation,
4195            schedules,
4196        )
4197    }
4198
4199    /// Replace a fixed route plan's rank inputs from an already replicated device batch.
4200    ///
4201    /// Route CSR remains unchanged. Advancing the generation invalidates every prior projection
4202    /// and combine result, so callers must refresh combine metadata before executing again.
4203    pub fn refresh_step_grouped_expert_parallel_inputs_from_replicated(
4204        &self,
4205        experts: &ResidentExpertParallel,
4206        plan: &mut PreparedStepGroupedExpertParallelGate,
4207        input: &ResidentReplicatedDeviceRows,
4208    ) -> Result<(), Box<dyn std::error::Error>> {
4209        validate_ep_residency(&self.ranks, experts)?;
4210        validate_replicated_device_rows(&self.ranks, input)?;
4211        if !plan.ready
4212            || input.tokens != plan.tokens
4213            || input.width != plan.input_width
4214            || input.tokens > plan.max_tokens
4215            || plan.rank_inputs.len() != self.ranks.len()
4216            || plan.owners.len() != self.ranks.len()
4217            || plan.input_width != experts.input_width
4218            || plan.expert_width != experts.expert_width
4219        {
4220            return Err("Step owner-grouped replicated input geometry changed".into());
4221        }
4222        let values = input
4223            .tokens
4224            .checked_mul(input.width)
4225            .ok_or("Step owner-grouped replicated input size overflow")?;
4226        let next_generation = plan
4227            .generation
4228            .checked_add(1)
4229            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
4230        plan.ready = false;
4231        plan.executed_generation = None;
4232        for (rank, engine) in self.ranks.iter().enumerate() {
4233            let _main = engine.gpu.enter_main()?;
4234            let mut destination = plan.rank_inputs[rank].slice_mut(0..values);
4235            engine
4236                .stream()
4237                .memcpy_dtod(&input.ranks[rank], &mut destination)?;
4238        }
4239        plan.generation = next_generation;
4240        plan.ready = true;
4241        Ok(())
4242    }
4243
4244    pub fn execute_step_grouped_expert_parallel_gate(
4245        &self,
4246        experts: &ResidentExpertParallel,
4247        plan: &mut PreparedStepGroupedExpertParallelGate,
4248    ) -> Result<(), Box<dyn std::error::Error>> {
4249        validate_ep_residency(&self.ranks, experts)?;
4250        if !plan.ready
4251            || plan.rank_inputs.len() != self.ranks.len()
4252            || plan.owners.len() != self.ranks.len()
4253            || plan.input_width != experts.input_width
4254            || plan.expert_width != experts.expert_width
4255        {
4256            return Err("Step owner-grouped FP8 plan is not ready or its geometry changed".into());
4257        }
4258        plan.executed_generation = None;
4259
4260        for owner in &mut plan.owners {
4261            if owner.global_pairs.is_empty() {
4262                continue;
4263            }
4264            let engine = &self.ranks[owner.rank];
4265            let bank = &experts.ranks[owner.rank];
4266            let _main = engine.gpu.enter_main()?;
4267            let local_pairs = owner.global_pairs.len();
4268            owner.gate_workspace.quantize_for_shape(
4269                engine,
4270                &plan.rank_inputs[owner.rank],
4271                plan.tokens,
4272                local_pairs,
4273            )?;
4274            owner.gate_workspace.project(
4275                engine,
4276                &bank.gate.codes,
4277                &bank.gate.scales,
4278                &owner.route_csr,
4279                bank.gate.code_stride,
4280                bank.gate.scale_stride,
4281                1.0,
4282            )?;
4283            owner.up_workspace.quantize_for_shape(
4284                engine,
4285                &plan.rank_inputs[owner.rank],
4286                plan.tokens,
4287                local_pairs,
4288            )?;
4289            owner.up_workspace.project(
4290                engine,
4291                &bank.up.codes,
4292                &bank.up.scales,
4293                &owner.route_csr,
4294                bank.up.code_stride,
4295                bank.up.scale_stride,
4296                1.0,
4297            )?;
4298        }
4299        for owner in &mut plan.owners {
4300            if owner.global_pairs.is_empty() {
4301                continue;
4302            }
4303            let engine = &self.ranks[owner.rank];
4304            let _main = engine.gpu.enter_main()?;
4305            let values = owner.global_pairs.len() * plan.expert_width;
4306            if let Some(limit) = plan.activation_limit {
4307                engine.silu_clamped_mul_host_expf(
4308                    owner.gate_workspace.output(),
4309                    owner.up_workspace.output(),
4310                    limit,
4311                    &mut owner.activation,
4312                    values,
4313                )?;
4314            } else {
4315                engine.silu_mul_host_expf(
4316                    owner.gate_workspace.output(),
4317                    owner.up_workspace.output(),
4318                    &mut owner.activation,
4319                    values,
4320                )?;
4321            }
4322        }
4323        for owner in &mut plan.owners {
4324            if owner.global_pairs.is_empty() {
4325                continue;
4326            }
4327            let engine = &self.ranks[owner.rank];
4328            let bank = &experts.ranks[owner.rank];
4329            let _main = engine.gpu.enter_main()?;
4330            let local_pairs = owner.global_pairs.len();
4331            owner.down_workspace.quantize_for_shape(
4332                engine,
4333                &owner.activation,
4334                local_pairs,
4335                local_pairs,
4336            )?;
4337            owner.down_workspace.project(
4338                engine,
4339                &bank.down.codes,
4340                &bank.down.scales,
4341                &owner.down_csr,
4342                bank.down.code_stride,
4343                bank.down.scale_stride,
4344                1.0,
4345            )?;
4346        }
4347        plan.executed_generation = Some(plan.generation);
4348        Ok(())
4349    }
4350
4351    pub fn collect_step_grouped_expert_parallel_gate(
4352        &self,
4353        plan: &PreparedStepGroupedExpertParallelGate,
4354    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4355        if !plan.ready || plan.executed_generation != Some(plan.generation) {
4356            return Err("Step owner-grouped FP8 projection is stale or has not executed".into());
4357        }
4358        let mut gate = vec![0.0f32; plan.pairs * plan.expert_width];
4359        let mut up = vec![0.0f32; plan.pairs * plan.expert_width];
4360        let mut down = vec![0.0f32; plan.pairs * plan.input_width];
4361        for owner in &plan.owners {
4362            if owner.global_pairs.is_empty() {
4363                continue;
4364            }
4365            let engine = &self.ranks[owner.rank];
4366            let _main = engine.gpu.enter_main()?;
4367            let owner_gate = engine.dtoh_view(
4368                &owner
4369                    .gate_workspace
4370                    .output()
4371                    .slice(0..owner.gate_workspace.output_len()),
4372            )?;
4373            let owner_up = engine.dtoh_view(
4374                &owner
4375                    .up_workspace
4376                    .output()
4377                    .slice(0..owner.up_workspace.output_len()),
4378            )?;
4379            let owner_down = engine.dtoh_view(
4380                &owner
4381                    .down_workspace
4382                    .output()
4383                    .slice(0..owner.down_workspace.output_len()),
4384            )?;
4385            for (local_pair, &global_pair) in owner.global_pairs.iter().enumerate() {
4386                let local_expert = local_pair * plan.expert_width;
4387                let global_expert = global_pair * plan.expert_width;
4388                gate[global_expert..global_expert + plan.expert_width]
4389                    .copy_from_slice(&owner_gate[local_expert..local_expert + plan.expert_width]);
4390                up[global_expert..global_expert + plan.expert_width]
4391                    .copy_from_slice(&owner_up[local_expert..local_expert + plan.expert_width]);
4392
4393                let local_hidden = local_pair * plan.input_width;
4394                let global_hidden = global_pair * plan.input_width;
4395                down[global_hidden..global_hidden + plan.input_width]
4396                    .copy_from_slice(&owner_down[local_hidden..local_hidden + plan.input_width]);
4397            }
4398        }
4399        Ok(StepGroupedFp8ProjectionOutput { gate, up, down })
4400    }
4401
4402    pub fn run_step_grouped_expert_parallel_gate(
4403        &self,
4404        experts: &ResidentExpertParallel,
4405        plan: &mut PreparedStepGroupedExpertParallelGate,
4406    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4407        self.execute_step_grouped_expert_parallel_gate(experts, plan)?;
4408        self.collect_step_grouped_expert_parallel_gate(plan)
4409    }
4410
4411    pub fn prepare_step_grouped_expert_parallel_combine(
4412        &self,
4413        plan: &PreparedStepGroupedExpertParallelGate,
4414        route_weights: &[f32],
4415    ) -> Result<PreparedPeerWeightedRouteCombine, Box<dyn std::error::Error>> {
4416        if !self.native_p2p || !self.ep_device_arithmetic || !plan.ready {
4417            return Err(
4418                "Step owner-grouped combine requires a ready native-P2P device plan".into(),
4419            );
4420        }
4421        let owner_pairs = plan
4422            .owners
4423            .iter()
4424            .map(|owner| owner.global_pairs.as_slice())
4425            .collect::<Vec<_>>();
4426        let shape = validate_weighted_route_combine(
4427            plan.input_width,
4428            STEP_GROUPED_FP8_TOP_K,
4429            plan.max_tokens,
4430            plan.tokens,
4431            &owner_pairs,
4432            route_weights,
4433        )?;
4434        if shape.max_pairs != plan.max_pairs {
4435            return Err(format!(
4436                "Step owner-grouped combine capacity {} != projection capacity {}",
4437                shape.max_pairs, plan.max_pairs
4438            )
4439            .into());
4440        }
4441        let root = self
4442            .ranks
4443            .first()
4444            .ok_or("Step owner-grouped combine has no root rank")?;
4445        let slot_values = shape
4446            .max_pairs
4447            .checked_mul(plan.input_width)
4448            .ok_or("Step owner-grouped combine slot capacity overflow")?;
4449        let output_values = plan
4450            .max_tokens
4451            .checked_mul(plan.input_width)
4452            .ok_or("Step owner-grouped combine output capacity overflow")?;
4453        let (root_device, owners, peer_staging, slots, weights, output) = {
4454            let _main = root.gpu.enter_main()?;
4455            let mut owners = Vec::with_capacity(plan.owners.len());
4456            for _ in &plan.owners {
4457                owners.push(PreparedPeerWeightedRouteOwner {
4458                    token_rows: root.htod_i32(&vec![0; shape.max_pairs])?,
4459                    slots: root.htod_i32(&vec![0; shape.max_pairs])?,
4460                    weights: root.htod(&vec![0.0; shape.max_pairs])?,
4461                    active_pairs: 0,
4462                });
4463            }
4464            (
4465                root.ctx().ordinal(),
4466                owners,
4467                root.uninit(slot_values)?,
4468                root.uninit(slot_values)?,
4469                root.uninit(shape.max_pairs)?,
4470                root.uninit(output_values)?,
4471            )
4472        };
4473        let mut peer_devices = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4474        let mut peer_outputs = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4475        for engine in self.ranks.iter().skip(1) {
4476            let _main = engine.gpu.enter_main()?;
4477            peer_devices.push(engine.ctx().ordinal());
4478            peer_outputs.push(engine.uninit(output_values)?);
4479        }
4480        let mut combine = PreparedPeerWeightedRouteCombine {
4481            root_device,
4482            owners,
4483            peer_staging,
4484            slots,
4485            weights,
4486            output,
4487            peer_devices,
4488            peer_outputs,
4489            width: plan.input_width,
4490            experts_per_token: STEP_GROUPED_FP8_TOP_K,
4491            max_tokens: plan.max_tokens,
4492            max_pairs: shape.max_pairs,
4493            tokens: 0,
4494            pairs: 0,
4495            projection_generation: 0,
4496            output_generation: None,
4497            broadcast_generation: None,
4498            ready: false,
4499        };
4500        self.refresh_step_grouped_expert_parallel_combine(plan, &mut combine, route_weights)?;
4501        Ok(combine)
4502    }
4503
4504    pub fn refresh_step_grouped_expert_parallel_combine(
4505        &self,
4506        plan: &PreparedStepGroupedExpertParallelGate,
4507        combine: &mut PreparedPeerWeightedRouteCombine,
4508        route_weights: &[f32],
4509    ) -> Result<(), Box<dyn std::error::Error>> {
4510        let output_capacity = combine
4511            .max_tokens
4512            .checked_mul(combine.width)
4513            .ok_or("Step owner-grouped combine output capacity overflow")?;
4514        if !plan.ready
4515            || combine.owners.len() != plan.owners.len()
4516            || combine.peer_devices.len() + 1 != self.ranks.len()
4517            || combine.peer_outputs.len() + 1 != self.ranks.len()
4518            || combine.width != plan.input_width
4519            || combine.experts_per_token != STEP_GROUPED_FP8_TOP_K
4520            || combine.max_tokens != plan.max_tokens
4521            || combine.max_pairs != plan.max_pairs
4522            || combine.output.len() < output_capacity
4523            || combine
4524                .peer_outputs
4525                .iter()
4526                .any(|output| output.len() < output_capacity)
4527        {
4528            return Err("Step owner-grouped combine/projection geometry changed".into());
4529        }
4530        if self
4531            .ranks
4532            .iter()
4533            .skip(1)
4534            .zip(&combine.peer_devices)
4535            .any(|(engine, &device)| engine.ctx().ordinal() != device)
4536        {
4537            return Err("Step owner-grouped combine peer devices changed".into());
4538        }
4539        let owner_pairs = plan
4540            .owners
4541            .iter()
4542            .map(|owner| owner.global_pairs.as_slice())
4543            .collect::<Vec<_>>();
4544        let shape = validate_weighted_route_combine(
4545            combine.width,
4546            combine.experts_per_token,
4547            combine.max_tokens,
4548            plan.tokens,
4549            &owner_pairs,
4550            route_weights,
4551        )?;
4552        if shape.max_pairs != combine.max_pairs {
4553            return Err("Step owner-grouped combine capacity changed during refresh".into());
4554        }
4555        let metadata = owner_pairs
4556            .iter()
4557            .map(|pairs| {
4558                let token_rows = pairs
4559                    .iter()
4560                    .map(|&pair| (pair / combine.experts_per_token) as i32)
4561                    .collect::<Vec<_>>();
4562                let slots = pairs
4563                    .iter()
4564                    .map(|&pair| (pair % combine.experts_per_token) as i32)
4565                    .collect::<Vec<_>>();
4566                let weights = pairs
4567                    .iter()
4568                    .map(|&pair| route_weights[pair])
4569                    .collect::<Vec<_>>();
4570                (token_rows, slots, weights)
4571            })
4572            .collect::<Vec<_>>();
4573
4574        combine.ready = false;
4575        combine.output_generation = None;
4576        combine.broadcast_generation = None;
4577        let root = self
4578            .ranks
4579            .first()
4580            .ok_or("Step owner-grouped combine has no root rank")?;
4581        let _main = root.gpu.enter_main()?;
4582        if root.ctx().ordinal() != combine.root_device {
4583            return Err(format!(
4584                "Step owner-grouped combine root device changed {} != {}",
4585                root.ctx().ordinal(),
4586                combine.root_device
4587            )
4588            .into());
4589        }
4590        for (owner, (token_rows, slots, weights)) in combine.owners.iter_mut().zip(metadata) {
4591            if token_rows.is_empty() {
4592                owner.active_pairs = 0;
4593                continue;
4594            }
4595            root.htod_i32_into(&mut owner.token_rows, &token_rows)?;
4596            root.htod_i32_into(&mut owner.slots, &slots)?;
4597            let mut weight_prefix = owner.weights.slice_mut(0..weights.len());
4598            root.stream().memcpy_htod(&weights, &mut weight_prefix)?;
4599            owner.active_pairs = token_rows.len();
4600        }
4601        combine.tokens = plan.tokens;
4602        combine.pairs = shape.pairs;
4603        combine.projection_generation = plan.generation;
4604        combine.ready = true;
4605        Ok(())
4606    }
4607
4608    pub fn execute_step_grouped_expert_parallel_combine(
4609        &self,
4610        plan: &PreparedStepGroupedExpertParallelGate,
4611        combine: &mut PreparedPeerWeightedRouteCombine,
4612    ) -> Result<(), Box<dyn std::error::Error>> {
4613        if !plan.ready
4614            || plan.executed_generation != Some(plan.generation)
4615            || !combine.ready
4616            || combine.tokens != plan.tokens
4617            || combine.pairs != plan.pairs
4618            || combine.width != plan.input_width
4619            || combine.owners.len() != plan.owners.len()
4620            || combine.projection_generation != plan.generation
4621        {
4622            return Err("Step owner-grouped combine is stale or its geometry changed".into());
4623        }
4624        combine.output_generation = None;
4625        combine.broadcast_generation = None;
4626        for owner in &plan.owners {
4627            if owner.rank == 0 || owner.global_pairs.is_empty() {
4628                continue;
4629            }
4630            let engine = &self.ranks[owner.rank];
4631            let _main = engine.gpu.enter_main()?;
4632            engine.stream().synchronize()?;
4633        }
4634        let root = self
4635            .ranks
4636            .first()
4637            .ok_or("Step owner-grouped combine has no root rank")?;
4638        let _main = root.gpu.enter_main()?;
4639        if root.ctx().ordinal() != combine.root_device {
4640            return Err("Step owner-grouped combine is not resident on the root device".into());
4641        }
4642        for (index, owner) in plan.owners.iter().enumerate() {
4643            let metadata = &combine.owners[index];
4644            if owner.global_pairs.len() != metadata.active_pairs {
4645                return Err(format!(
4646                    "Step owner-grouped combine owner {index} rows {} != metadata {}",
4647                    owner.global_pairs.len(),
4648                    metadata.active_pairs
4649                )
4650                .into());
4651            }
4652            if metadata.active_pairs == 0 {
4653                continue;
4654            }
4655            let values = metadata
4656                .active_pairs
4657                .checked_mul(combine.width)
4658                .ok_or("Step owner-grouped combine peer value count overflow")?;
4659            if owner.rank == 0 {
4660                root.scatter_slot(
4661                    owner.down_workspace.output(),
4662                    &metadata.token_rows,
4663                    &metadata.slots,
4664                    &metadata.weights,
4665                    &mut combine.slots,
4666                    &mut combine.weights,
4667                    combine.width,
4668                    combine.experts_per_token,
4669                    metadata.active_pairs,
4670                )?;
4671            } else {
4672                let source = owner.down_workspace.output().slice(0..values);
4673                let mut destination = combine.peer_staging.slice_mut(0..values);
4674                root.stream().memcpy_dtod(&source, &mut destination)?;
4675                root.scatter_slot(
4676                    &combine.peer_staging,
4677                    &metadata.token_rows,
4678                    &metadata.slots,
4679                    &metadata.weights,
4680                    &mut combine.slots,
4681                    &mut combine.weights,
4682                    combine.width,
4683                    combine.experts_per_token,
4684                    metadata.active_pairs,
4685                )?;
4686            }
4687        }
4688        root.reduce_slots_host(
4689            &combine.slots,
4690            &combine.weights,
4691            &mut combine.output,
4692            combine.width,
4693            combine.experts_per_token,
4694            combine.tokens,
4695        )?;
4696        combine.output_generation = Some(plan.generation);
4697        Ok(())
4698    }
4699
4700    pub fn collect_step_grouped_expert_parallel_combine(
4701        &self,
4702        plan: &PreparedStepGroupedExpertParallelGate,
4703        combine: &PreparedPeerWeightedRouteCombine,
4704    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4705        if !plan.ready
4706            || combine.output_generation != Some(plan.generation)
4707            || combine.projection_generation != plan.generation
4708        {
4709            return Err("Step owner-grouped combine output is stale or has not executed".into());
4710        }
4711        let root = self
4712            .ranks
4713            .first()
4714            .ok_or("Step owner-grouped combine has no root rank")?;
4715        let _main = root.gpu.enter_main()?;
4716        if root.ctx().ordinal() != combine.root_device {
4717            return Err("Step owner-grouped combine is not resident on the root device".into());
4718        }
4719        root.dtoh_view(&combine.output.slice(0..combine.tokens * combine.width))
4720    }
4721
4722    /// Copy the active root combine result into a caller-owned engine on the same CUDA device.
4723    ///
4724    /// The persistent combine buffer remains reusable by the next route generation; the returned
4725    /// allocation follows the serving runtime's ordinary transient-output ownership.
4726    pub fn copy_step_grouped_expert_parallel_combine_root(
4727        &self,
4728        plan: &PreparedStepGroupedExpertParallelGate,
4729        combine: &PreparedPeerWeightedRouteCombine,
4730        destination: &Engine,
4731    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4732        if !plan.ready
4733            || combine.output_generation != Some(plan.generation)
4734            || combine.projection_generation != plan.generation
4735        {
4736            return Err("Step owner-grouped combine output is stale or has not executed".into());
4737        }
4738        let root = self
4739            .ranks
4740            .first()
4741            .ok_or("Step owner-grouped combine has no root rank")?;
4742        if root.ctx().ordinal() != combine.root_device
4743            || destination.ctx().ordinal() != combine.root_device
4744        {
4745            return Err(format!(
4746                "Step owner-grouped combine root/destination devices {}/{} != {}",
4747                root.ctx().ordinal(),
4748                destination.ctx().ordinal(),
4749                combine.root_device,
4750            )
4751            .into());
4752        }
4753        let values = combine
4754            .tokens
4755            .checked_mul(combine.width)
4756            .ok_or("Step owner-grouped combine copy size overflow")?;
4757        {
4758            let _main = root.gpu.enter_main()?;
4759            root.stream().synchronize()?;
4760        }
4761        let _main = destination.gpu.enter_main()?;
4762        let mut output = destination.uninit(values)?;
4763        destination
4764            .stream()
4765            .memcpy_dtod(&combine.output.slice(0..values), &mut output)?;
4766        Ok(output)
4767    }
4768
4769    pub fn broadcast_step_grouped_expert_parallel_combine(
4770        &self,
4771        plan: &PreparedStepGroupedExpertParallelGate,
4772        combine: &mut PreparedPeerWeightedRouteCombine,
4773    ) -> Result<(), Box<dyn std::error::Error>> {
4774        if !plan.ready
4775            || combine.output_generation != Some(plan.generation)
4776            || combine.projection_generation != plan.generation
4777            || combine.peer_devices.len() + 1 != self.ranks.len()
4778            || combine.peer_outputs.len() + 1 != self.ranks.len()
4779        {
4780            return Err("Step owner-grouped combine output cannot be broadcast".into());
4781        }
4782        combine.broadcast_generation = None;
4783        let values = combine
4784            .tokens
4785            .checked_mul(combine.width)
4786            .ok_or("Step owner-grouped combine broadcast size overflow")?;
4787        {
4788            let root = self
4789                .ranks
4790                .first()
4791                .ok_or("Step owner-grouped combine has no root rank")?;
4792            let _main = root.gpu.enter_main()?;
4793            if root.ctx().ordinal() != combine.root_device {
4794                return Err("Step owner-grouped combine root device changed".into());
4795            }
4796            root.stream().synchronize()?;
4797        }
4798        let source = &combine.output;
4799        for (index, destination_buffer) in combine.peer_outputs.iter_mut().enumerate() {
4800            let engine = &self.ranks[index + 1];
4801            let _main = engine.gpu.enter_main()?;
4802            if engine.ctx().ordinal() != combine.peer_devices[index] {
4803                return Err(format!(
4804                    "Step owner-grouped combine peer {} device changed",
4805                    index + 1
4806                )
4807                .into());
4808            }
4809            let mut destination = destination_buffer.slice_mut(0..values);
4810            engine
4811                .stream()
4812                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
4813        }
4814        combine.broadcast_generation = Some(plan.generation);
4815        Ok(())
4816    }
4817
4818    pub fn collect_step_grouped_expert_parallel_broadcast(
4819        &self,
4820        plan: &PreparedStepGroupedExpertParallelGate,
4821        combine: &PreparedPeerWeightedRouteCombine,
4822    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
4823        if !plan.ready
4824            || combine.output_generation != Some(plan.generation)
4825            || combine.broadcast_generation != Some(plan.generation)
4826            || combine.peer_outputs.len() + 1 != self.ranks.len()
4827        {
4828            return Err("Step owner-grouped combine broadcast is stale or incomplete".into());
4829        }
4830        let values = combine
4831            .tokens
4832            .checked_mul(combine.width)
4833            .ok_or("Step owner-grouped combine collection size overflow")?;
4834        let mut outputs = Vec::with_capacity(self.ranks.len());
4835        {
4836            let root = &self.ranks[0];
4837            let _main = root.gpu.enter_main()?;
4838            outputs.push(root.dtoh_view(&combine.output.slice(0..values))?);
4839        }
4840        for (index, output) in combine.peer_outputs.iter().enumerate() {
4841            let engine = &self.ranks[index + 1];
4842            let _main = engine.gpu.enter_main()?;
4843            outputs.push(engine.dtoh_view(&output.slice(0..values))?);
4844        }
4845        Ok(outputs)
4846    }
4847
4848    /// Add routed and replicated shared-expert outputs, then add the attention residual.
4849    pub fn finish_step_grouped_expert_parallel_layer(
4850        &self,
4851        plan: &PreparedStepGroupedExpertParallelGate,
4852        combine: &PreparedPeerWeightedRouteCombine,
4853        shared: &ResidentReplicatedDeviceRows,
4854        residual: &ResidentReplicatedDeviceRows,
4855    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
4856        validate_replicated_device_rows(&self.ranks, shared)?;
4857        validate_replicated_device_rows(&self.ranks, residual)?;
4858        if !plan.ready
4859            || plan.executed_generation != Some(plan.generation)
4860            || combine.output_generation != Some(plan.generation)
4861            || combine.broadcast_generation != Some(plan.generation)
4862            || combine.projection_generation != plan.generation
4863            || combine.peer_outputs.len() + 1 != self.ranks.len()
4864            || shared.tokens != combine.tokens
4865            || residual.tokens != combine.tokens
4866            || shared.width != combine.width
4867            || residual.width != combine.width
4868        {
4869            return Err("Step full-layer finish inputs are stale or their geometry changed".into());
4870        }
4871        let values = combine
4872            .tokens
4873            .checked_mul(combine.width)
4874            .ok_or("Step full-layer output size overflow")?;
4875        let mut ranks = Vec::with_capacity(self.ranks.len());
4876        for rank in 0..self.ranks.len() {
4877            let engine = &self.ranks[rank];
4878            let _main = engine.gpu.enter_main()?;
4879            let routed = if rank == 0 {
4880                &combine.output
4881            } else {
4882                &combine.peer_outputs[rank - 1]
4883            };
4884            let mut ffn = engine.uninit(values)?;
4885            engine.add(routed, &shared.ranks[rank], &mut ffn, values)?;
4886            let mut output = engine.uninit(values)?;
4887            engine.add(&residual.ranks[rank], &ffn, &mut output, values)?;
4888            ranks.push(output);
4889        }
4890        Ok(ResidentReplicatedDeviceRows {
4891            ranks,
4892            tokens: combine.tokens,
4893            width: combine.width,
4894        })
4895    }
4896
4897    pub fn run_step_grouped_expert_parallel_combine(
4898        &self,
4899        plan: &PreparedStepGroupedExpertParallelGate,
4900        combine: &mut PreparedPeerWeightedRouteCombine,
4901    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4902        self.execute_step_grouped_expert_parallel_combine(plan, combine)?;
4903        self.collect_step_grouped_expert_parallel_combine(plan, combine)
4904    }
4905
4906    pub fn upload_tensor_parallel(
4907        &self,
4908        gate: E4m3ExpertBank<'_>,
4909        up: E4m3ExpertBank<'_>,
4910        down: E4m3ExpertBank<'_>,
4911    ) -> Result<ResidentTensorParallel, Box<dyn std::error::Error>> {
4912        gate.validate()?;
4913        up.validate()?;
4914        down.validate()?;
4915        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
4916            return Err("TP gate/up/down expert counts differ".into());
4917        }
4918        if gate.in_features != up.in_features || gate.out_features != up.out_features {
4919            return Err("TP gate/up dimensions differ".into());
4920        }
4921        if down.in_features != gate.out_features || down.out_features != gate.in_features {
4922            return Err(format!(
4923                "TP down {}x{} does not invert gate/up {}x{}",
4924                down.out_features, down.in_features, gate.out_features, gate.in_features
4925            )
4926            .into());
4927        }
4928        let tp = self.ranks.len();
4929        validate_column_bank_shape(gate, tp)?;
4930        validate_column_bank_shape(up, tp)?;
4931        validate_row_bank_shape(down, tp)?;
4932
4933        let mut gate_ranks = Vec::with_capacity(tp);
4934        let mut up_ranks = Vec::with_capacity(tp);
4935        let mut down_ranks = Vec::with_capacity(tp);
4936        for (rank, engine) in self.ranks.iter().enumerate() {
4937            gate_ranks.push(upload_column_bank_rank(engine, gate, tp, rank)?);
4938            up_ranks.push(upload_column_bank_rank(engine, up, tp, rank)?);
4939            down_ranks.push(upload_row_bank_rank(engine, down, tp, rank)?);
4940        }
4941        Ok(ResidentTensorParallel {
4942            bank: ResidentTpExpertBank {
4943                gate: gate_ranks,
4944                up: up_ranks,
4945                down: down_ranks,
4946                expert_count: gate.expert_count,
4947                input_width: gate.in_features,
4948                expert_width: gate.out_features,
4949            },
4950        })
4951    }
4952
4953    pub fn run_tensor_parallel_routes(
4954        &self,
4955        experts: &ResidentTensorParallel,
4956        input: &[f32],
4957        tokens: usize,
4958        selected: &[usize],
4959        route_weights: &[f32],
4960        experts_per_token: usize,
4961    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4962        validate_tp_bank_residency(&self.ranks, &experts.bank)?;
4963        validate_activations(input, tokens, experts.bank.input_width)?;
4964        let pairs = tokens
4965            .checked_mul(experts_per_token)
4966            .ok_or("TP route count overflow")?;
4967        if selected.len() != pairs || route_weights.len() != pairs {
4968            return Err(format!(
4969                "TP routes selected={} weights={} != tokens {tokens} x experts/token \
4970                 {experts_per_token} ({pairs})",
4971                selected.len(),
4972                route_weights.len(),
4973            )
4974            .into());
4975        }
4976        if !route_weights.iter().all(|weight| weight.is_finite()) {
4977            return Err("TP route weights contain a non-finite value".into());
4978        }
4979
4980        let mut output = vec![0.0f32; tokens * experts.bank.input_width];
4981        for token in 0..tokens {
4982            let input_row =
4983                &input[token * experts.bank.input_width..(token + 1) * experts.bank.input_width];
4984            for slot in 0..experts_per_token {
4985                let pair = token * experts_per_token + slot;
4986                let expert = selected[pair];
4987                if expert >= experts.bank.expert_count {
4988                    return Err(format!(
4989                        "TP selected expert {expert} outside 0..{}",
4990                        experts.bank.expert_count
4991                    )
4992                    .into());
4993                }
4994                let down = if self.native_p2p {
4995                    self.run_tensor_parallel_expert_native(&experts.bank, expert, input_row)?
4996                } else {
4997                    let gate =
4998                        self.run_column_bank_expert(&experts.bank.gate, expert, input_row)?;
4999                    let up = self.run_column_bank_expert(&experts.bank.up, expert, input_row)?;
5000                    let activated: Vec<f32> = gate
5001                        .iter()
5002                        .zip(&up)
5003                        .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
5004                        .collect();
5005                    debug_assert_eq!(activated.len(), experts.bank.expert_width);
5006                    self.run_row_bank_expert(&experts.bank.down, expert, &activated)?
5007                };
5008                let weight = route_weights[pair];
5009                for (sum, value) in output
5010                    [token * experts.bank.input_width..(token + 1) * experts.bank.input_width]
5011                    .iter_mut()
5012                    .zip(down)
5013                {
5014                    *sum += weight * value;
5015                }
5016            }
5017        }
5018        Ok(output)
5019    }
5020
5021    fn run_column_bank_expert(
5022        &self,
5023        ranks: &[ResidentE4m3ExpertBankRank],
5024        expert: usize,
5025        input: &[f32],
5026    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5027        let local_out = ranks
5028            .first()
5029            .ok_or("TP column bank has no ranks")?
5030            .out_features;
5031        let mut gathered = vec![0.0f32; local_out * ranks.len()];
5032        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
5033            let shard = run_resident_bank_expert(engine, bank, expert, input, 1)?;
5034            gathered[rank * local_out..(rank + 1) * local_out].copy_from_slice(&shard);
5035        }
5036        Ok(gathered)
5037    }
5038
5039    fn run_row_bank_expert(
5040        &self,
5041        ranks: &[ResidentE4m3ExpertBankRank],
5042        expert: usize,
5043        input: &[f32],
5044    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5045        let local_in = ranks.first().ok_or("TP row bank has no ranks")?.in_features;
5046        if input.len() != local_in * ranks.len() {
5047            return Err(format!(
5048                "TP row input {} != {} ranks x {local_in}",
5049                input.len(),
5050                ranks.len()
5051            )
5052            .into());
5053        }
5054        let out_features = ranks[0].out_features;
5055        let mut reduced = vec![0.0f32; out_features];
5056        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
5057            let blocks = bank
5058                .k_blocks
5059                .ok_or("TP row bank is not packed in native K-block order")?;
5060            if blocks * FP8_BLOCK != local_in {
5061                return Err(format!(
5062                    "TP row bank has {blocks} blocks but local input width is {local_in}"
5063                )
5064                .into());
5065            }
5066            for block in 0..blocks {
5067                let global_start = rank * local_in + block * FP8_BLOCK;
5068                let partial = run_resident_bank_expert_block(
5069                    engine,
5070                    bank,
5071                    expert,
5072                    block,
5073                    &input[global_start..global_start + FP8_BLOCK],
5074                )?;
5075                for (sum, value) in reduced.iter_mut().zip(partial) {
5076                    *sum += value;
5077                }
5078            }
5079        }
5080        Ok(reduced)
5081    }
5082
5083    fn run_tensor_parallel_expert_native(
5084        &self,
5085        bank: &ResidentTpExpertBank,
5086        expert: usize,
5087        input: &[f32],
5088    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5089        if !self.native_p2p || self.ranks.len() < 2 {
5090            return Err("native TP expert execution requires at least two P2P ranks".into());
5091        }
5092        let local_out = bank
5093            .gate
5094            .first()
5095            .ok_or("native TP gate bank has no ranks")?
5096            .out_features;
5097        if local_out * self.ranks.len() != bank.expert_width {
5098            return Err(format!(
5099                "native TP gate shards {}x{local_out} != expert width {}",
5100                self.ranks.len(),
5101                bank.expert_width
5102            )
5103            .into());
5104        }
5105
5106        // The caller's routed input is already host-canonical. Upload once on rank zero, then
5107        // broadcast over peer copies so no other rank receives a host-staged duplicate.
5108        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
5109        let root_input = {
5110            let root = &self.ranks[0];
5111            let _main = root.gpu.enter_main()?;
5112            root.htod(input)?
5113        };
5114        rank_inputs.push(root_input);
5115        for engine in &self.ranks[1..] {
5116            let peer_input = {
5117                let _main = engine.gpu.enter_main()?;
5118                let mut peer_input = engine.uninit(input.len())?;
5119                engine
5120                    .stream()
5121                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
5122                peer_input
5123            };
5124            rank_inputs.push(peer_input);
5125        }
5126
5127        let mut gate_shards = Vec::with_capacity(self.ranks.len());
5128        let mut up_shards = Vec::with_capacity(self.ranks.len());
5129        for rank in 0..self.ranks.len() {
5130            gate_shards.push(run_resident_bank_expert_device(
5131                &self.ranks[rank],
5132                &bank.gate[rank],
5133                expert,
5134                &rank_inputs[rank],
5135                1,
5136            )?);
5137            up_shards.push(run_resident_bank_expert_device(
5138                &self.ranks[rank],
5139                &bank.up[rank],
5140                expert,
5141                &rank_inputs[rank],
5142                1,
5143            )?);
5144        }
5145
5146        // Preserve the established canonical activation program for the first native transport
5147        // milestone. The shards move to rank zero over P2P; only the scalar activation expression
5148        // executes on host. A later device-activation increment must earn its own exactness gate.
5149        let gate = self.gather_native_column_shards(&gate_shards, 1, local_out)?;
5150        let up = self.gather_native_column_shards(&up_shards, 1, local_out)?;
5151        let activated = gate
5152            .iter()
5153            .zip(&up)
5154            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
5155            .collect::<Vec<_>>();
5156        debug_assert_eq!(activated.len(), bank.expert_width);
5157
5158        let root_activated = {
5159            let root = &self.ranks[0];
5160            let _main = root.gpu.enter_main()?;
5161            root.htod(&activated)?
5162        };
5163        let mut rank_activated = Vec::with_capacity(self.ranks.len());
5164        for (rank, engine) in self.ranks.iter().enumerate() {
5165            let start = rank * local_out;
5166            let source = root_activated.slice(start..start + local_out);
5167            let local = {
5168                let _main = engine.gpu.enter_main()?;
5169                let mut local = engine.uninit(local_out)?;
5170                engine.stream().memcpy_dtod(&source, &mut local)?;
5171                local
5172            };
5173            rank_activated.push(local);
5174        }
5175
5176        let out_features = bank
5177            .down
5178            .first()
5179            .ok_or("native TP down bank has no ranks")?
5180            .out_features;
5181        let mut reduced = {
5182            let root = &self.ranks[0];
5183            let _main = root.gpu.enter_main()?;
5184            root.htod(&vec![0.0f32; out_features])?
5185        };
5186        let mut remote_partial_keepalive = Vec::new();
5187        for rank in 0..self.ranks.len() {
5188            let down = &bank.down[rank];
5189            let blocks = down
5190                .k_blocks
5191                .ok_or("native TP row bank is not packed in checkpoint-block order")?;
5192            if blocks * FP8_BLOCK != local_out {
5193                return Err(format!(
5194                    "native TP rank {rank} has {blocks} blocks but local activation width is \
5195                     {local_out}"
5196                )
5197                .into());
5198            }
5199            for block in 0..blocks {
5200                let start = block * FP8_BLOCK;
5201                let input_block = rank_activated[rank].slice(start..start + FP8_BLOCK);
5202                let partial = run_resident_bank_expert_block_device(
5203                    &self.ranks[rank],
5204                    down,
5205                    expert,
5206                    block,
5207                    &input_block,
5208                )?;
5209                let root_partial = if rank == 0 {
5210                    partial
5211                } else {
5212                    let root = &self.ranks[0];
5213                    let _main = root.gpu.enter_main()?;
5214                    let mut peer_partial = root.uninit(out_features)?;
5215                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
5216                    remote_partial_keepalive.push(partial);
5217                    peer_partial
5218                };
5219                let next = {
5220                    let root = &self.ranks[0];
5221                    let _main = root.gpu.enter_main()?;
5222                    let mut next = root.uninit(out_features)?;
5223                    root.add(&reduced, &root_partial, &mut next, out_features)?;
5224                    next
5225                };
5226                reduced = next;
5227            }
5228        }
5229        let output = {
5230            let root = &self.ranks[0];
5231            let _main = root.gpu.enter_main()?;
5232            root.dtoh(&reduced)?
5233        };
5234        drop(remote_partial_keepalive);
5235        Ok(output)
5236    }
5237
5238    /// Gather token-major rank-local columns into one canonical root-device matrix.
5239    pub fn gather_native_column_shards_device(
5240        &self,
5241        shards: &[CudaSlice<f32>],
5242        tokens: usize,
5243        local_out: usize,
5244    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5245        let shard_len = tokens
5246            .checked_mul(local_out)
5247            .ok_or("native TP gather shard size overflow")?;
5248        if shards.len() != self.ranks.len() || shards.iter().any(|shard| shard.len() != shard_len) {
5249            return Err("native TP gather shard geometry mismatch".into());
5250        }
5251        // PRODUCER FENCE (2026-08-20 flake fix): the root stream peer-reads shards produced on
5252        // the other ranks' streams; without fencing those producers the copy can read a partial
5253        // kernel output.
5254        for engine in &self.ranks[1..] {
5255            let _main = engine.gpu.enter_main()?;
5256            engine.stream().synchronize()?;
5257        }
5258        let root = &self.ranks[0];
5259        let _main = root.gpu.enter_main()?;
5260        let global_out = shards
5261            .len()
5262            .checked_mul(local_out)
5263            .ok_or("native TP gather output width overflow")?;
5264        let gathered_len = tokens
5265            .checked_mul(global_out)
5266            .ok_or("native TP gather output size overflow")?;
5267        let mut gathered = root.uninit(gathered_len)?;
5268        if self.bulk_p2p {
5269            root.place_rows_strided(&shards[0], &mut gathered, local_out, tokens, global_out, 0)?;
5270            if shards.len() > 1 {
5271                let mut staging = root.uninit(shard_len)?;
5272                for (rank, shard) in shards.iter().enumerate().skip(1) {
5273                    root.stream().memcpy_dtod(shard, &mut staging)?;
5274                    root.place_rows_strided(
5275                        &staging,
5276                        &mut gathered,
5277                        local_out,
5278                        tokens,
5279                        global_out,
5280                        rank * local_out,
5281                    )?;
5282                }
5283            }
5284        } else {
5285            for token in 0..tokens {
5286                for (rank, shard) in shards.iter().enumerate() {
5287                    let source = shard.slice(token * local_out..(token + 1) * local_out);
5288                    let start = token * global_out + rank * local_out;
5289                    let mut destination = gathered.slice_mut(start..start + local_out);
5290                    root.stream().memcpy_dtod(&source, &mut destination)?;
5291                }
5292            }
5293        }
5294        Ok(gathered)
5295    }
5296
5297    pub fn gather_native_column_shards(
5298        &self,
5299        shards: &[CudaSlice<f32>],
5300        tokens: usize,
5301        local_out: usize,
5302    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5303        let gathered = self.gather_native_column_shards_device(shards, tokens, local_out)?;
5304        let root = &self.ranks[0];
5305        let _main = root.gpu.enter_main()?;
5306        root.dtoh(&gathered)
5307    }
5308
5309    pub(crate) fn decode_v2_workspace(&self) -> &std::sync::Mutex<Vec<StepTpDecodeV2Ws>> {
5310        &self.decode_v2
5311    }
5312
5313    /// Build the v2 decode-attention workspace for this layer's geometry on first use, or
5314    /// return the index of the matching one. Attention geometry varies across the trunk
5315    /// (per-layer query-head counts), so workspaces are keyed by their geometry pins — a
5316    /// handful exist per model, never one per layer.
5317    ///
5318    /// Refuses non-F32-resident projections: the v2 driver's bit-exactness claim against v1
5319    /// holds per residency class, and only the mirror class has no per-call weight expansion
5320    /// to hide allocation churn behind.
5321    pub(crate) fn decode_v2_ensure(
5322        &self,
5323        e: &Engine,
5324        q_m: &ResidentBf16ColumnParallel,
5325        k_m: &ResidentBf16ColumnParallel,
5326        v_m: &ResidentBf16ColumnParallel,
5327        o_m: &ResidentStepBf16RowParallel,
5328        heads: usize,
5329    ) -> Result<usize, Box<dyn std::error::Error>> {
5330        if self.ranks.len() > 1 && !self.native_p2p {
5331            return Err("step TP decode v2 requires native P2P ranks".into());
5332        }
5333        let ranks = self.ranks.len();
5334        // Residency contract: the canonical-chunk (non-fused) program needs the F32 mirror;
5335        // the fused-kernel door also reads raw checkpoint bf16 directly (halving the weight
5336        // traffic), so bf16 residency is accepted when that door is on.
5337        let fused_door = step_tp_qkv_fused_enabled()?;
5338        let arm_ok = |weight: &ResidentBf16Weight| match weight {
5339            ResidentBf16Weight::F32(_) => true,
5340            ResidentBf16Weight::Bf16(_) => fused_door,
5341        };
5342        for matrix in [q_m, k_m, v_m] {
5343            validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
5344            if matrix.out_features % ranks != 0 || matrix.in_features != q_m.in_features {
5345                return Err("step TP decode v2 QKV geometry mismatch".into());
5346            }
5347            for rank in &matrix.ranks {
5348                if !arm_ok(&rank.weight) {
5349                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5350                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5351                        .into());
5352                }
5353            }
5354        }
5355        validate_step_bf16_row_residency(&self.ranks, o_m)?;
5356        for blocks in &o_m.ranks {
5357            for block in blocks {
5358                if !arm_ok(&block.weight) {
5359                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5360                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5361                        .into());
5362                }
5363            }
5364        }
5365        if v_m.out_features != k_m.out_features
5366            || o_m.in_features != q_m.out_features
5367            || heads == 0
5368            || heads % ranks != 0
5369        {
5370            return Err("step TP decode v2 K/V/O geometry mismatch".into());
5371        }
5372        let local_q_dim = q_m.out_features / ranks;
5373        let local_kv_dim = k_m.out_features / ranks;
5374        let o_out = o_m.out_features;
5375        let o_block_cols = o_m.canonical_chunk_cols;
5376        let blocks_per_rank = o_m.ranks.first().map(Vec::len).unwrap_or(0);
5377        if blocks_per_rank == 0
5378            || o_m
5379                .ranks
5380                .iter()
5381                .any(|blocks| blocks.len() != blocks_per_rank)
5382            || blocks_per_rank * o_block_cols * ranks != o_m.in_features
5383        {
5384            return Err("step TP decode v2 O canonical block grid mismatch".into());
5385        }
5386
5387        let mut guard = self
5388            .decode_v2
5389            .lock()
5390            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5391        if let Some(index) = guard.iter().position(|ws| {
5392            ws.local_q_dim == local_q_dim
5393                && ws.local_kv_dim == local_kv_dim
5394                && ws.heads == heads
5395                && ws.o_out == o_out
5396                && ws.o_block_cols == o_block_cols
5397                && ws.blocks_per_rank == blocks_per_rank
5398                && ws.e_device == e.ctx().ordinal()
5399                && ws.q.len() == ranks
5400        }) {
5401            return Ok(index);
5402        }
5403
5404        let mut q_raw = Vec::with_capacity(ranks);
5405        let mut k_raw = Vec::with_capacity(ranks);
5406        let mut v_raw = Vec::with_capacity(ranks);
5407        let mut q = Vec::with_capacity(ranks);
5408        let mut k = Vec::with_capacity(ranks);
5409        let mut pos = Vec::with_capacity(ranks);
5410        let mut gate = Vec::with_capacity(ranks);
5411        let mut attn_out = Vec::with_capacity(ranks);
5412        let mut gated = Vec::with_capacity(ranks);
5413        let mut fuse_ctr = Vec::with_capacity(ranks);
5414        let mut o_partials = Vec::with_capacity(ranks);
5415        let mut ev_rank = Vec::with_capacity(ranks);
5416        let direct_join = oproj_direct_on();
5417        for (rank, engine) in self.ranks.iter().enumerate() {
5418            let _main = engine.gpu.enter_main()?;
5419            q_raw.push(engine.uninit(local_q_dim)?);
5420            k_raw.push(engine.uninit(local_kv_dim)?);
5421            v_raw.push(engine.uninit(local_kv_dim)?);
5422            q.push(engine.uninit(local_q_dim)?);
5423            k.push(engine.uninit(local_kv_dim)?);
5424            pos.push(engine.htod_i32(&[0])?);
5425            fuse_ctr.push(engine.stream().clone_htod(&[0u32])?);
5426            gate.push(engine.uninit(heads / ranks)?);
5427            attn_out.push(engine.uninit(local_q_dim)?);
5428            gated.push(engine.uninit(local_q_dim)?);
5429            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
5430            for _ in 0..blocks_per_rank {
5431                // Direct join: peer ranks' partials live on ROOT so the b4 kernel's
5432                // stores land there over P2P (UVA) and no pull copy is needed.
5433                if direct_join && rank != 0 {
5434                    let root = &self.ranks[0];
5435                    let _root_main = root.gpu.enter_main()?;
5436                    rank_partials.push(root.uninit(o_out)?);
5437                } else {
5438                    rank_partials.push(engine.uninit(o_out)?);
5439                }
5440            }
5441            o_partials.push(rank_partials);
5442            ev_rank.push(engine.ctx().new_event(None)?);
5443        }
5444        let root = &self.ranks[0];
5445        let (peer_partial, reduce_a, reduce_b, zeros, k_shadow, v_shadow, ev_refresh, ev_oproj) = {
5446            let _main = root.gpu.enter_main()?;
5447            (
5448                root.uninit(o_out)?,
5449                root.uninit(o_out)?,
5450                root.uninit(o_out)?,
5451                root.htod(&vec![0.0f32; o_out])?,
5452                root.uninit(ranks * local_kv_dim)?,
5453                root.uninit(ranks * local_kv_dim)?,
5454                root.ctx().new_event(None)?,
5455                root.ctx().new_event(None)?,
5456            )
5457        };
5458        let (gate_e, ev_entry) = {
5459            let _main = e.gpu.enter_main()?;
5460            (e.uninit(heads)?, e.ctx().new_event(None)?)
5461        };
5462        let raw_attn_in = Vec::new();
5463        let raw_pos = Vec::new();
5464        guard.push(StepTpDecodeV2Ws {
5465            tcol_q: Vec::new(),
5466            tcol_k: Vec::new(),
5467            tcol_v: Vec::new(),
5468            tcol_g: Vec::new(),
5469            tcol_in: Vec::new(),
5470            tcol_cap: 0,
5471            w8_aq: Vec::new(),
5472            w8_ad: Vec::new(),
5473            w8_in: 0,
5474            w8o_aq: Vec::new(),
5475            w8o_ad: Vec::new(),
5476            w8o_in: 0,
5477            fa2_q: Vec::new(),
5478            fa2_gate: Vec::new(),
5479            fa2_gated: Vec::new(),
5480            fa2_cap: 0,
5481            rope_k_t: Vec::new(),
5482            rope_ctr_t: Vec::new(),
5483            rope_pos_t: Vec::new(),
5484            rows_tabs: Vec::new(),
5485            tcol_gated: Vec::new(),
5486            tcol_opart: Vec::new(),
5487            tcol_opeer: None,
5488            tcol_omix: None,
5489            tcol_ocap: 0,
5490            q_raw,
5491            k_raw,
5492            v_raw,
5493            q,
5494            k,
5495            pos,
5496            fuse_ctr,
5497            gate,
5498            attn_out,
5499            gated,
5500            o_partials,
5501            ev_rank,
5502            peer_partial,
5503            reduce_a,
5504            reduce_b,
5505            zeros,
5506            k_shadow,
5507            v_shadow,
5508            ev_refresh,
5509            ev_oproj,
5510            gate_e,
5511            attn_in: Vec::new(),
5512            h_stage: None,
5513            pos_stage: None,
5514            raw_h_stage: 0,
5515            raw_pos_stage: 0,
5516            raw_attn_in,
5517            raw_pos,
5518            raw_o_partial1: 0,
5519            raw_peer_partial: 0,
5520            raw_k1: 0,
5521            raw_v1: 0,
5522            raw_k_shadow: 0,
5523            raw_v_shadow: 0,
5524            raw_mixed_stage_e: 0,
5525            raw_reduce_a: 0,
5526            raw_shadow_stage_e: (0, 0),
5527            ev_entry,
5528            e_device: e.ctx().ordinal(),
5529            local_q_dim,
5530            local_kv_dim,
5531            heads,
5532            o_out,
5533            o_block_cols,
5534            blocks_per_rank,
5535        });
5536        eprintln!(
5537            "[step-tp-decode-v2] workspace ranks={ranks} local_q={local_q_dim} \
5538             local_kv={local_kv_dim} heads={heads} o_blocks={blocks_per_rank}x{o_block_cols} \
5539             residency=persistent ordering=evented performance_claim=false"
5540        );
5541        Ok(guard.len() - 1)
5542    }
5543
5544    /// v2 phase 1: replicate the layer input, project QKV, norm, rope, and stage the gate —
5545    /// all into the persistent workspace, ordered by events instead of host syncs.
5546    ///
5547    /// The caller must have queued every producer of `h`, `pos_d`, and `gate_raw` on `e`'s
5548    /// stream BEFORE this call: `ev_entry` is recorded once here and every rank stream waits
5549    /// on it (the entry fence also guards workspace reuse across layers — any consumer of the
5550    /// previous layer's outputs was queued on `e`'s stream before this record).
5551    #[allow(clippy::too_many_arguments)]
5552    /// T-COLUMN verify precompute (spec MTP): stage T input rows to every rank and run the
5553    /// weight-amortized qkvg_tcol per rank into the ws slabs. Rope/norm/append stay per
5554    /// column in the unmodified t=1 program (defer_norm_rope contract). Bit-exact per
5555    /// column vs the t=1 kernel by construction.
5556    #[allow(clippy::too_many_arguments)]
5557    pub fn decode_v2_input_qkv_tcol(
5558        &self,
5559        ws_index: usize,
5560        e: &Engine,
5561        h_t: &CudaSlice<f32>,
5562        t: usize,
5563        q_m: &ResidentBf16ColumnParallel,
5564        k_m: &ResidentBf16ColumnParallel,
5565        v_m: &ResidentBf16ColumnParallel,
5566        gate_shards: Option<StepTpGateShards<'_>>,
5567    ) -> Result<(), Box<dyn std::error::Error>> {
5568        let ranks = self.ranks.len();
5569        let mut guard = self
5570            .decode_v2
5571            .lock()
5572            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5573        let ws = guard
5574            .get_mut(ws_index)
5575            .ok_or("step TP decode v2 workspace index out of range")?;
5576        let in_f = q_m.in_features;
5577        if h_t.len() < t * in_f || t == 0 || t > 32 {
5578            return Err("decode_v2_input_qkv_tcol geometry".into());
5579        }
5580        // Lazily arm the slabs to capacity.
5581        if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
5582            ws.tcol_q.clear();
5583            ws.tcol_k.clear();
5584            ws.tcol_v.clear();
5585            ws.tcol_g.clear();
5586            ws.tcol_in.clear();
5587            for engine in &self.ranks {
5588                let _m = engine.gpu.enter_main()?;
5589                ws.tcol_q.push(engine.uninit(32 * ws.local_q_dim)?);
5590                ws.tcol_k.push(engine.uninit(32 * ws.local_kv_dim)?);
5591                ws.tcol_v.push(engine.uninit(32 * ws.local_kv_dim)?);
5592                ws.tcol_g
5593                    .push(engine.uninit(32 * (ws.heads / ranks).max(1))?);
5594                ws.tcol_in.push(engine.uninit(32 * in_f)?);
5595            }
5596            ws.tcol_cap = 32;
5597        }
5598        // Stage the T input rows on e, fence, per-rank pull + tcol launch.
5599        use cudarc::driver::DevicePtr;
5600        let raw_src = {
5601            let _main = e.gpu.enter_main()?;
5602            let stream = e.stream();
5603            let (p, _g) = h_t.device_ptr(&stream);
5604            ws.ev_entry.record(&stream)?;
5605            p as u64
5606        };
5607        for rank in 0..ranks {
5608            let engine = &self.ranks[rank];
5609            let _main = engine.gpu.enter_main()?;
5610            engine.stream().wait(&ws.ev_entry)?;
5611            let raw_dst = {
5612                let stream = engine.stream();
5613                let (p, _g) = ws.tcol_in[rank].device_ptr(&stream);
5614                p as u64
5615            };
5616            raw_copy_bytes(raw_dst, raw_src, t * in_f * 4, engine)?;
5617            let out_g = match &gate_shards {
5618                Some(_) => ws.heads / ranks,
5619                None => 0,
5620            };
5621            match (
5622                &q_m.ranks[rank].weight,
5623                &k_m.ranks[rank].weight,
5624                &v_m.ranks[rank].weight,
5625            ) {
5626                (
5627                    ResidentBf16Weight::Bf16(wq),
5628                    ResidentBf16Weight::Bf16(wk),
5629                    ResidentBf16Weight::Bf16(wv),
5630                ) => {
5631                    let wg = match &gate_shards {
5632                        Some(StepTpGateShards::Bf16(shards)) => &shards[rank],
5633                        Some(StepTpGateShards::F32(_)) => {
5634                            return Err(
5635                                "tcol verify: gate shard class does not match bf16 QKV".into()
5636                            );
5637                        }
5638                        None => wq,
5639                    };
5640                    let StepTpDecodeV2Ws {
5641                        tcol_q,
5642                        tcol_k,
5643                        tcol_v,
5644                        tcol_g,
5645                        tcol_in,
5646                        local_q_dim,
5647                        local_kv_dim,
5648                        ..
5649                    } = &mut *ws;
5650                    // MEMRA_TCOL_REFKERN=1 (bisect): fill the slabs via the t=1 kernel per
5651                    // column — separates driver bugs from tcol-kernel bugs.
5652                    static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5653                    let refk = *REFK
5654                        .get_or_init(|| std::env::var("MEMRA_TCOL_REFKERN").as_deref() == Ok("1"));
5655                    if refk {
5656                        let lq = *local_q_dim;
5657                        let lkv = *local_kv_dim;
5658                        let mut hrow = engine.uninit(in_f)?;
5659                        let mut qr = engine.uninit(lq)?;
5660                        let mut kr = engine.uninit(lkv)?;
5661                        let mut vr = engine.uninit(lkv)?;
5662                        let mut gr = engine.uninit(out_g.max(1))?;
5663                        for c in 0..t {
5664                            {
5665                                let mut dst = hrow.slice_mut(0..in_f);
5666                                engine.stream().memcpy_dtod(
5667                                    &tcol_in[rank].slice(c * in_f..(c + 1) * in_f),
5668                                    &mut dst,
5669                                )?;
5670                            }
5671                            engine.matvec_bf16_qkvg_into(
5672                                wq, wk, wv, wg, &hrow, &mut qr, &mut kr, &mut vr, &mut gr, in_f,
5673                                lq, lkv, out_g,
5674                            )?;
5675                            let stream = engine.stream();
5676                            {
5677                                let mut dst = tcol_q[rank].slice_mut(c * lq..(c + 1) * lq);
5678                                stream.memcpy_dtod(&qr.slice(0..lq), &mut dst)?;
5679                            }
5680                            {
5681                                let mut dst = tcol_k[rank].slice_mut(c * lkv..(c + 1) * lkv);
5682                                stream.memcpy_dtod(&kr.slice(0..lkv), &mut dst)?;
5683                            }
5684                            {
5685                                let mut dst = tcol_v[rank].slice_mut(c * lkv..(c + 1) * lkv);
5686                                stream.memcpy_dtod(&vr.slice(0..lkv), &mut dst)?;
5687                            }
5688                            if out_g > 0 {
5689                                let mut dst = tcol_g[rank].slice_mut(c * out_g..(c + 1) * out_g);
5690                                stream.memcpy_dtod(&gr.slice(0..out_g), &mut dst)?;
5691                            }
5692                        }
5693                    } else {
5694                        engine.matvec_bf16_qkvg_tcol_into(
5695                            wq,
5696                            wk,
5697                            wv,
5698                            wg,
5699                            &tcol_in[rank],
5700                            &mut tcol_q[rank],
5701                            &mut tcol_k[rank],
5702                            &mut tcol_v[rank],
5703                            &mut tcol_g[rank],
5704                            in_f,
5705                            *local_q_dim,
5706                            *local_kv_dim,
5707                            out_g,
5708                            t,
5709                        )?;
5710                    }
5711                }
5712                _ => return Err("tcol verify requires bf16-resident fused QKV".into()),
5713            }
5714        }
5715        Ok(())
5716    }
5717
5718    /// MEMRA_TCOL_OPROJ eligibility: the defer replaces exactly the o_fused direct-join
5719    /// finish (bf16 b4 kernel, 2 ranks, 4 canonical blocks) with the shadow gathers
5720    /// skipped — so it requires the same doors that arm dictate that finish shape.
5721    pub(crate) fn decode_v2_oproj_tcol_eligible(
5722        &self,
5723        ws: &StepTpDecodeV2Ws,
5724        o_m: &ResidentStepBf16RowParallel,
5725    ) -> bool {
5726        self.ranks.len() == 2
5727            && ws.blocks_per_rank == 4
5728            && step_tp_qkv_fused_enabled().unwrap_or(false)
5729            && no_local_shadow_on()
5730            && std::env::var("MEMRA_B4_X2").as_deref() != Ok("1")
5731            && o_m
5732                .ranks
5733                .iter()
5734                .flatten()
5735                .all(|block| matches!(block.weight, ResidentBf16Weight::Bf16(_)))
5736    }
5737
5738    /// MEMRA_SPEC_FA2 stash: copy this column's per-rank post-rope q and gate rows into
5739    /// the fa2 slabs (rank-stream ordered behind the rope/append that produced them), and
5740    /// give `e` the same anti-dependency wait the skipped finish provided (next column's
5741    /// h/pos re-staging must not overtake this column's rank pulls).
5742    pub(crate) fn decode_v2_stash_fa2(
5743        &self,
5744        ws: &mut StepTpDecodeV2Ws,
5745        e: &Engine,
5746        col: usize,
5747    ) -> Result<(), Box<dyn std::error::Error>> {
5748        let ranks = self.ranks.len();
5749        if col >= 32 {
5750            return Err("decode_v2_stash_fa2 column out of range".into());
5751        }
5752        let lq = ws.local_q_dim;
5753        let lg = (ws.heads / ranks).max(1);
5754        if ws.fa2_cap < 32 || ws.fa2_q.len() != ranks {
5755            ws.fa2_q.clear();
5756            ws.fa2_gate.clear();
5757            ws.fa2_gated.clear();
5758            ws.rope_k_t.clear();
5759            ws.rope_ctr_t.clear();
5760            ws.rope_pos_t.clear();
5761            for engine in &self.ranks {
5762                let _m = engine.gpu.enter_main()?;
5763                ws.fa2_q.push(engine.uninit(32 * lq)?);
5764                ws.fa2_gate.push(engine.uninit(32 * lg)?);
5765                ws.fa2_gated.push(engine.uninit(32 * lq)?);
5766                ws.rope_k_t.push(engine.uninit(32 * ws.local_kv_dim)?);
5767                ws.rope_ctr_t.push(engine.stream().clone_htod(&[0u32; 32])?);
5768                ws.rope_pos_t.push(engine.htod_i32(&[0i32; 32])?);
5769            }
5770            ws.rows_tabs = (0..ranks).map(|_| Default::default()).collect();
5771            ws.fa2_cap = 32;
5772        }
5773        for rank in 0..ranks {
5774            let engine = &self.ranks[rank];
5775            let _main = engine.gpu.enter_main()?;
5776            {
5777                let mut dst = ws.fa2_q[rank].slice_mut(col * lq..(col + 1) * lq);
5778                engine
5779                    .stream()
5780                    .memcpy_dtod(&ws.q[rank].slice(0..lq), &mut dst)?;
5781            }
5782            {
5783                let mut dst = ws.fa2_gate[rank].slice_mut(col * lg..(col + 1) * lg);
5784                engine
5785                    .stream()
5786                    .memcpy_dtod(&ws.gate[rank].slice(0..lg), &mut dst)?;
5787            }
5788            ws.ev_rank[rank].record(&engine.stream())?;
5789        }
5790        {
5791            let _main = e.gpu.enter_main()?;
5792            for ev in ws.ev_rank.iter() {
5793                e.stream().wait(ev)?;
5794            }
5795        }
5796        Ok(())
5797    }
5798
5799    /// MEMRA_SPEC_FA2 join: after BOTH verify columns stashed (their appends landed in
5800    /// rank-stream order), run ONE fa_decode_dcw2 per rank over the shared KV stream —
5801    /// two query rows, per-row causal bounds, per-row combine+gate — then land the two
5802    /// gated rows in the o-tcol slabs and reuse the weight-amortized o_proj join.
5803    /// Returns the [2, o_out] `mixed` slab on `e`. The caller's precheck enforced the
5804    /// equal-partition guard (boundary rounds never arm the defer).
5805    #[allow(clippy::too_many_arguments)]
5806    pub(crate) fn decode_v2_spec_fa2_join(
5807        &self,
5808        ws_index: usize,
5809        e: &Engine,
5810        o_m: &ResidentStepBf16RowParallel,
5811        kv: &ResidentTpKvCache,
5812        head_dim: usize,
5813        window: usize,
5814        bucket_max: usize,
5815        scale: f32,
5816    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5817        let ranks = self.ranks.len();
5818        // Engagement receipt: a vacuous gate (precheck never passing) must be visible.
5819        static ONCE: std::sync::Once = std::sync::Once::new();
5820        ONCE.call_once(|| eprintln!("[spec-fa2] joined T=2 attention ENGAGED"));
5821        {
5822            let mut guard = self
5823                .decode_v2
5824                .lock()
5825                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5826            let ws = guard
5827                .get_mut(ws_index)
5828                .ok_or("step TP decode v2 workspace index out of range")?;
5829            if ws.fa2_cap < 2 || ws.fa2_q.len() != ranks {
5830                return Err("spec fa2 join without stashed columns".into());
5831            }
5832            let lq = ws.local_q_dim;
5833            let local_heads = (ws.heads / ranks).max(1);
5834            let local_kv_heads = (ws.local_kv_dim / head_dim).max(1);
5835            let capacity = kv.physical_capacity();
5836            let (k_tok_bytes, v_tok_bytes) = (kv.k_tok_bytes(), kv.v_tok_bytes());
5837            // Arm the o-tcol slabs if the oproj door never ran this boot (same shapes).
5838            if ws.tcol_ocap < 2 || ws.tcol_gated.len() != ranks {
5839                ws.tcol_gated.clear();
5840                ws.tcol_opart.clear();
5841                for engine in &self.ranks {
5842                    let _m = engine.gpu.enter_main()?;
5843                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
5844                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
5845                }
5846                let root = &self.ranks[0];
5847                let _m = root.gpu.enter_main()?;
5848                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
5849                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
5850                ws.tcol_ocap = 32;
5851            }
5852            for rank in 0..ranks {
5853                let engine = &self.ranks[rank];
5854                let _main = engine.gpu.enter_main()?;
5855                let rank_cache = kv
5856                    .rank(rank)
5857                    .ok_or("spec fa2 join lost its KV cache rank")?;
5858                let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes);
5859                let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes);
5860                {
5861                    let StepTpDecodeV2Ws {
5862                        fa2_q,
5863                        fa2_gate,
5864                        fa2_gated,
5865                        ..
5866                    } = &mut *ws;
5867                    engine.fa_decode_dcw2(
5868                        &fa2_q[rank],
5869                        &k_ring,
5870                        &v_ring,
5871                        &mut fa2_gated[rank],
5872                        head_dim,
5873                        local_heads,
5874                        local_kv_heads,
5875                        rank_cache.len_d(),
5876                        rank_cache.base_d(),
5877                        window,
5878                        bucket_max,
5879                        scale,
5880                        k_tok_bytes,
5881                        v_tok_bytes,
5882                        &fa2_gate[rank],
5883                    )?;
5884                }
5885                // Both gated rows are contiguous [2, lq] — exactly columns 0..2 of the
5886                // o-tcol slab layout. One dtod, in rank-stream order behind the fa.
5887                let StepTpDecodeV2Ws {
5888                    fa2_gated,
5889                    tcol_gated,
5890                    ..
5891                } = &mut *ws;
5892                let mut dst = tcol_gated[rank].slice_mut(0..2 * lq);
5893                engine
5894                    .stream()
5895                    .memcpy_dtod(&fa2_gated[rank].slice(0..2 * lq), &mut dst)?;
5896            }
5897        }
5898        self.decode_v2_oproj_tcol(ws_index, e, o_m, 2)
5899    }
5900
5901    /// FULL T-ROW ATTENTION PASS over per-row session tables (batched serving): reads
5902    /// the tcol raw-projection slabs, runs ONE rope/append rows launch + ONE fa rows
5903    /// launch + ONE combine per rank (gate straight from the tcol gate slab), then the
5904    /// o_proj tcol join — the whole per-row attention loop in 3 launches/rank/layer.
5905    /// Per-(row, head) programs are the t=1 kernels verbatim; each row appends to and
5906    /// attends its OWN session. `session_parts[rank][row]` = {k_plane, v_plane, len_ptr,
5907    /// base_ptr}; `tab_keys[rank]` keys the per-rank combined-table cache (caller folds
5908    /// layer + session-set + base-arming into it); `stage_pos` stages the position slab
5909    /// (positions are constant across layers within a tick — stage on the first layer).
5910    #[allow(clippy::too_many_arguments)]
5911    pub(crate) fn decode_v2_rope_fa_rows(
5912        &self,
5913        ws_index: usize,
5914        e: &Engine,
5915        o_m: &ResidentStepBf16RowParallel,
5916        session_parts: &[Vec<[u64; 4]>],
5917        tab_keys: &[u64],
5918        positions: &[i32],
5919        stage_pos: bool,
5920        same_session: bool,
5921        q_norms: &[CudaSlice<f32>],
5922        k_norms: &[CudaSlice<f32>],
5923        rope_freqs: &[Option<&crate::CudaSlice<f32>>],
5924        t: usize,
5925        head_dim: usize,
5926        n_rot: usize,
5927        window: usize,
5928        max_ns: usize,
5929        scale: f32,
5930        k_tok_bytes: usize,
5931        v_tok_bytes: usize,
5932        eps: f32,
5933        rope_base: f32,
5934    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
5935        use cudarc::driver::DevicePtr;
5936        let ranks = self.ranks.len();
5937        if session_parts.len() != ranks || tab_keys.len() != ranks || positions.len() < t {
5938            return Err("rope fa rows geometry".into());
5939        }
5940        {
5941            let mut guard = self
5942                .decode_v2
5943                .lock()
5944                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5945            let ws = guard
5946                .get_mut(ws_index)
5947                .ok_or("step TP decode v2 workspace index out of range")?;
5948            if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
5949                return Err("rope fa rows without tcol slabs".into());
5950            }
5951            let lq = ws.local_q_dim;
5952            let lkv = ws.local_kv_dim;
5953            let lg = (ws.heads / ranks).max(1);
5954            let local_heads = (ws.heads / ranks).max(1);
5955            let local_kv_heads = (lkv / head_dim).max(1);
5956            // Arm the fa2/rope slabs (shared with the stash path).
5957            if ws.fa2_cap < 32 || ws.fa2_q.len() != ranks {
5958                ws.fa2_q.clear();
5959                ws.fa2_gate.clear();
5960                ws.fa2_gated.clear();
5961                ws.rope_k_t.clear();
5962                ws.rope_ctr_t.clear();
5963                ws.rope_pos_t.clear();
5964                for engine in &self.ranks {
5965                    let _m = engine.gpu.enter_main()?;
5966                    ws.fa2_q.push(engine.uninit(32 * lq)?);
5967                    ws.fa2_gate.push(engine.uninit(32 * lg)?);
5968                    ws.fa2_gated.push(engine.uninit(32 * lq)?);
5969                    ws.rope_k_t.push(engine.uninit(32 * lkv)?);
5970                    ws.rope_ctr_t.push(engine.stream().clone_htod(&[0u32; 32])?);
5971                    ws.rope_pos_t.push(engine.htod_i32(&[0i32; 32])?);
5972                }
5973                ws.rows_tabs = (0..ranks).map(|_| Default::default()).collect();
5974                ws.fa2_cap = 32;
5975            }
5976            if ws.tcol_ocap < t || ws.tcol_gated.len() != ranks {
5977                ws.tcol_gated.clear();
5978                ws.tcol_opart.clear();
5979                for engine in &self.ranks {
5980                    let _m = engine.gpu.enter_main()?;
5981                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
5982                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
5983                }
5984                let root = &self.ranks[0];
5985                let _m = root.gpu.enter_main()?;
5986                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
5987                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
5988                ws.tcol_ocap = 32;
5989            }
5990            for rank in 0..ranks {
5991                let engine = &self.ranks[rank];
5992                let _main = engine.gpu.enter_main()?;
5993                if stage_pos {
5994                    let host: Vec<i32> = positions[..t].to_vec();
5995                    let mut view = ws.rope_pos_t[rank].slice_mut(0..t);
5996                    engine.stream().memcpy_htod(&host, &mut view)?;
5997                }
5998                // Combined 6-word table {k, v, len, base, ctr, back=0}; ctr = this
5999                // rank's per-row counter slab.
6000                if !ws.rows_tabs[rank].contains_key(&tab_keys[rank]) {
6001                    let ctr_base = {
6002                        let s = engine.stream();
6003                        let (p, _g) = ws.rope_ctr_t[rank].device_ptr(&s);
6004                        p as u64
6005                    };
6006                    let mut host = Vec::with_capacity(t * 6);
6007                    for (r, parts) in session_parts[rank].iter().enumerate().take(t) {
6008                        host.extend_from_slice(&[
6009                            parts[0],
6010                            parts[1],
6011                            parts[2],
6012                            parts[3],
6013                            if same_session {
6014                                ctr_base
6015                            } else {
6016                                ctr_base + (r as u64) * 4
6017                            },
6018                            if same_session {
6019                                (t - 1 - r) as u64
6020                            } else {
6021                                0u64
6022                            },
6023                        ]);
6024                    }
6025                    let tab = engine.stream().clone_htod(&host)?;
6026                    ws.rows_tabs[rank].insert(tab_keys[rank], tab);
6027                }
6028                let StepTpDecodeV2Ws {
6029                    tcol_q,
6030                    tcol_k,
6031                    tcol_v,
6032                    tcol_g,
6033                    fa2_q,
6034                    fa2_gated,
6035                    rope_k_t,
6036                    rope_pos_t,
6037                    rows_tabs,
6038                    ..
6039                } = &mut *ws;
6040                let tab = rows_tabs[rank]
6041                    .get(&tab_keys[rank])
6042                    .expect("inserted above");
6043                engine.qk_norm_rope_append_inc_dcw_rows(
6044                    &tcol_q[rank],
6045                    &tcol_k[rank],
6046                    &tcol_v[rank],
6047                    &q_norms[rank],
6048                    &k_norms[rank],
6049                    &mut fa2_q[rank],
6050                    &mut rope_k_t[rank],
6051                    tab,
6052                    &rope_pos_t[rank],
6053                    same_session,
6054                    t,
6055                    lkv,
6056                    lkv,
6057                    k_tok_bytes,
6058                    v_tok_bytes,
6059                    head_dim,
6060                    n_rot,
6061                    local_heads,
6062                    local_kv_heads,
6063                    eps,
6064                    rope_base,
6065                    1.0,
6066                    rope_freqs[rank],
6067                )?;
6068                engine.fa_decode_dcw_rows(
6069                    &fa2_q[rank],
6070                    tab,
6071                    &mut fa2_gated[rank],
6072                    t,
6073                    head_dim,
6074                    local_heads,
6075                    local_kv_heads,
6076                    window,
6077                    max_ns,
6078                    scale,
6079                    k_tok_bytes,
6080                    v_tok_bytes,
6081                    &tcol_g[rank],
6082                )?;
6083                let StepTpDecodeV2Ws {
6084                    fa2_gated,
6085                    tcol_gated,
6086                    ..
6087                } = &mut *ws;
6088                let mut dst = tcol_gated[rank].slice_mut(0..t * lq);
6089                engine
6090                    .stream()
6091                    .memcpy_dtod(&fa2_gated[rank].slice(0..t * lq), &mut dst)?;
6092            }
6093        }
6094        self.decode_v2_oproj_tcol(ws_index, e, o_m, t)
6095    }
6096
6097    /// T-ROW fa join over per-row session tables (the per-session distributed-KV
6098    /// primitive): after all t rows stashed q+gate (their appends landed in rank-stream
6099    /// order), ONE fa_decode_dcw_rows per rank walks every row's own ring with its own
6100    /// geometry — bit-identical per row to its per-row launch — then the o_proj tcol
6101    /// join lands the [t, o_out] `mixed` slab on `e`. `tabs[rank]` is the pre-staged
6102    /// device table on that rank.
6103    #[allow(clippy::too_many_arguments)]
6104    pub(crate) fn decode_v2_fa_rows_join(
6105        &self,
6106        ws_index: usize,
6107        e: &Engine,
6108        o_m: &ResidentStepBf16RowParallel,
6109        tabs: &[&crate::CudaSlice<u64>],
6110        t: usize,
6111        head_dim: usize,
6112        window: usize,
6113        max_ns: usize,
6114        scale: f32,
6115        k_tok_bytes: usize,
6116        v_tok_bytes: usize,
6117    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
6118        let ranks = self.ranks.len();
6119        if tabs.len() != ranks {
6120            return Err("fa rows join needs one table per rank".into());
6121        }
6122        {
6123            let mut guard = self
6124                .decode_v2
6125                .lock()
6126                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6127            let ws = guard
6128                .get_mut(ws_index)
6129                .ok_or("step TP decode v2 workspace index out of range")?;
6130            if ws.fa2_cap < t || ws.fa2_q.len() != ranks {
6131                return Err("fa rows join without stashed rows".into());
6132            }
6133            let lq = ws.local_q_dim;
6134            let local_heads = (ws.heads / ranks).max(1);
6135            let local_kv_heads = (ws.local_kv_dim / head_dim).max(1);
6136            if ws.tcol_ocap < t || ws.tcol_gated.len() != ranks {
6137                ws.tcol_gated.clear();
6138                ws.tcol_opart.clear();
6139                for engine in &self.ranks {
6140                    let _m = engine.gpu.enter_main()?;
6141                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
6142                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6143                }
6144                let root = &self.ranks[0];
6145                let _m = root.gpu.enter_main()?;
6146                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6147                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6148                ws.tcol_ocap = 32;
6149            }
6150            for rank in 0..ranks {
6151                let engine = &self.ranks[rank];
6152                let _main = engine.gpu.enter_main()?;
6153                {
6154                    let StepTpDecodeV2Ws {
6155                        fa2_q,
6156                        fa2_gate,
6157                        fa2_gated,
6158                        ..
6159                    } = &mut *ws;
6160                    engine.fa_decode_dcw_rows(
6161                        &fa2_q[rank],
6162                        tabs[rank],
6163                        &mut fa2_gated[rank],
6164                        t,
6165                        head_dim,
6166                        local_heads,
6167                        local_kv_heads,
6168                        window,
6169                        max_ns,
6170                        scale,
6171                        k_tok_bytes,
6172                        v_tok_bytes,
6173                        &fa2_gate[rank],
6174                    )?;
6175                }
6176                let StepTpDecodeV2Ws {
6177                    fa2_gated,
6178                    tcol_gated,
6179                    ..
6180                } = &mut *ws;
6181                let mut dst = tcol_gated[rank].slice_mut(0..t * lq);
6182                engine
6183                    .stream()
6184                    .memcpy_dtod(&fa2_gated[rank].slice(0..t * lq), &mut dst)?;
6185            }
6186        }
6187        self.decode_v2_oproj_tcol(ws_index, e, o_m, t)
6188    }
6189
6190    /// MEMRA_TCOL_OPROJ stash: copy this column's per-rank `gated` rows into the o-tcol
6191    /// slabs (rank-stream ordered behind the attention kernels that produced them). The
6192    /// per-column finish choreography is skipped entirely; `decode_v2_oproj_tcol` joins
6193    /// every column afterwards.
6194    pub(crate) fn decode_v2_stash_gated(
6195        &self,
6196        ws: &mut StepTpDecodeV2Ws,
6197        e: &Engine,
6198        col: usize,
6199    ) -> Result<(), Box<dyn std::error::Error>> {
6200        let ranks = self.ranks.len();
6201        if col >= 8 {
6202            return Err("decode_v2_stash_gated column out of range".into());
6203        }
6204        let lq = ws.local_q_dim;
6205        if ws.tcol_ocap == 0 || ws.tcol_gated.len() != ranks {
6206            ws.tcol_gated.clear();
6207            ws.tcol_opart.clear();
6208            for engine in &self.ranks {
6209                let _m = engine.gpu.enter_main()?;
6210                ws.tcol_gated.push(engine.uninit(32 * lq)?);
6211                ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6212            }
6213            let root = &self.ranks[0];
6214            let _m = root.gpu.enter_main()?;
6215            ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6216            ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6217            ws.tcol_ocap = 32;
6218        }
6219        for rank in 0..ranks {
6220            let engine = &self.ranks[rank];
6221            let _main = engine.gpu.enter_main()?;
6222            let mut dst = ws.tcol_gated[rank].slice_mut(col * lq..(col + 1) * lq);
6223            engine
6224                .stream()
6225                .memcpy_dtod(&ws.gated[rank].slice(0..lq), &mut dst)?;
6226            // The skipped finish's e-wait was ALSO the anti-dependency guard: it ordered
6227            // e's NEXT column's h/pos re-staging behind this column's rank-side raw pulls.
6228            // Record each rank here and make e wait — same protection, no o_proj work.
6229            ws.ev_rank[rank].record(&engine.stream())?;
6230        }
6231        {
6232            let _main = e.gpu.enter_main()?;
6233            for ev in ws.ev_rank.iter() {
6234                e.stream().wait(ev)?;
6235            }
6236        }
6237        Ok(())
6238    }
6239
6240    /// MEMRA_TCOL_OPROJ join: one weight-amortized b4_tcol per rank over the stashed
6241    /// `gated` slabs (per-column FP order == the t=1 b4 kernel), one peer pull of rank1's
6242    /// partial slab, one elementwise slab add on the root (independent elements — each
6243    /// column's add is the exact direct-join `add(p0, p1)`), then the joined `mixed` slab
6244    /// lands on `e`. Returns [t, o_out] on the model engine.
6245    pub(crate) fn decode_v2_oproj_tcol(
6246        &self,
6247        ws_index: usize,
6248        e: &Engine,
6249        o_m: &ResidentStepBf16RowParallel,
6250        t: usize,
6251    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6252        let ranks = self.ranks.len();
6253        let mut guard = self
6254            .decode_v2
6255            .lock()
6256            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6257        let ws = guard
6258            .get_mut(ws_index)
6259            .ok_or("step TP decode v2 workspace index out of range")?;
6260        if ranks != 2 || ws.blocks_per_rank != 4 || t == 0 || t > 32 || ws.tcol_ocap < t {
6261            return Err("decode_v2_oproj_tcol geometry".into());
6262        }
6263        for rank in 0..ranks {
6264            let engine = &self.ranks[rank];
6265            let _main = engine.gpu.enter_main()?;
6266            let mut weights = Vec::with_capacity(4);
6267            for block in 0..4 {
6268                let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
6269                    return Err("tcol o_proj requires bf16-resident O blocks".into());
6270                };
6271                weights.push(weight);
6272            }
6273            {
6274                let StepTpDecodeV2Ws {
6275                    tcol_gated,
6276                    tcol_opart,
6277                    local_q_dim,
6278                    o_block_cols,
6279                    o_out,
6280                    ..
6281                } = &mut *ws;
6282                // MEMRA_TCOL_OPROJ_REF=1 (bisect): fill the partial slab via the t=1 b4
6283                // kernel per column — separates choreography bugs from tcol-kernel bugs.
6284                static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
6285                let refk = *REFK
6286                    .get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ_REF").as_deref() == Ok("1"));
6287                if refk {
6288                    let lq = *local_q_dim;
6289                    let mut xr = engine.uninit(lq)?;
6290                    let mut yr = engine.uninit(*o_out)?;
6291                    for c in 0..t {
6292                        {
6293                            let mut dst = xr.slice_mut(0..lq);
6294                            engine.stream().memcpy_dtod(
6295                                &tcol_gated[rank].slice(c * lq..(c + 1) * lq),
6296                                &mut dst,
6297                            )?;
6298                        }
6299                        engine.matvec_bf16_b4_into(
6300                            [weights[0], weights[1], weights[2], weights[3]],
6301                            &xr,
6302                            &mut yr,
6303                            *o_block_cols,
6304                            *o_out,
6305                        )?;
6306                        let mut dst = tcol_opart[rank].slice_mut(c * *o_out..(c + 1) * *o_out);
6307                        engine
6308                            .stream()
6309                            .memcpy_dtod(&yr.slice(0..*o_out), &mut dst)?;
6310                    }
6311                } else {
6312                    engine.matvec_bf16_b4_tcol_into(
6313                        [weights[0], weights[1], weights[2], weights[3]],
6314                        &tcol_gated[rank],
6315                        &mut tcol_opart[rank],
6316                        *o_block_cols,
6317                        *o_out,
6318                        t,
6319                    )?;
6320                }
6321            }
6322            if rank != 0 {
6323                ws.ev_rank[rank].record(&engine.stream())?;
6324            }
6325        }
6326        let root = &self.ranks[0];
6327        {
6328            let _main = root.gpu.enter_main()?;
6329            for ev in ws.ev_rank.iter().skip(1) {
6330                root.stream().wait(ev)?;
6331            }
6332            {
6333                let StepTpDecodeV2Ws {
6334                    tcol_opart,
6335                    tcol_opeer,
6336                    tcol_omix,
6337                    o_out,
6338                    ..
6339                } = &mut *ws;
6340                let opeer = tcol_opeer.as_mut().ok_or("tcol o_proj slabs not armed")?;
6341                let omix = tcol_omix.as_mut().ok_or("tcol o_proj slabs not armed")?;
6342                {
6343                    let mut dst = opeer.slice_mut(0..t * *o_out);
6344                    root.stream()
6345                        .memcpy_dtod(&tcol_opart[1].slice(0..t * *o_out), &mut dst)?;
6346                }
6347                // Elementwise over the whole slab: per element identical to the per-column
6348                // direct-join add (independent lanes, same operand values).
6349                root.add(&tcol_opart[0], opeer, omix, t * *o_out)?;
6350            }
6351            ws.ev_oproj.record(&root.stream())?;
6352        }
6353        let _main = e.gpu.enter_main()?;
6354        e.stream().wait(&ws.ev_oproj)?;
6355        let mut out = e.uninit(t * ws.o_out)?;
6356        let omix = ws.tcol_omix.as_ref().ok_or("tcol o_proj slabs not armed")?;
6357        e.stream().memcpy_dtod(
6358            &omix.slice(0..t * ws.o_out),
6359            &mut out.slice_mut(0..t * ws.o_out),
6360        )?;
6361        Ok(out)
6362    }
6363
6364    pub(crate) fn decode_v2_input_qkv(
6365        &self,
6366        ws: &mut StepTpDecodeV2Ws,
6367        e: &Engine,
6368        h: &CudaSlice<f32>,
6369        pos_d: &CudaSlice<i32>,
6370        gate_raw: Option<&CudaSlice<f32>>,
6371        gate_shards: Option<StepTpGateShards<'_>>,
6372        decode_input: &mut ResidentReplicatedDeviceRows,
6373        q_m: &ResidentBf16ColumnParallel,
6374        k_m: &ResidentBf16ColumnParallel,
6375        v_m: &ResidentBf16ColumnParallel,
6376        q_norm: &[CudaSlice<f32>],
6377        k_norm: &[CudaSlice<f32>],
6378        head_dim: usize,
6379        n_rot: usize,
6380        rope_base: f32,
6381        rope_freqs: &[Option<&CudaSlice<f32>>],
6382        rms_eps: f32,
6383        defer_norm_rope: bool,
6384        tcol_col: Option<usize>,
6385    ) -> Result<(), Box<dyn std::error::Error>> {
6386        let ranks = self.ranks.len();
6387        validate_replicated_device_rows(&self.ranks, decode_input)?;
6388        if decode_input.tokens != 1
6389            || decode_input.width != q_m.in_features
6390            || pos_d.len() != 1
6391            || gate_raw.is_some_and(|gate| gate.len() != ws.heads)
6392            || gate_raw.is_none() != gate_shards.is_some()
6393            || gate_shards.as_ref().is_some_and(|shards| match shards {
6394                StepTpGateShards::F32(shards) => shards.len() != ranks,
6395                StepTpGateShards::Bf16(shards) => shards.len() != ranks,
6396            })
6397            || q_norm.len() != ranks
6398            || k_norm.len() != ranks
6399            || rope_freqs.len() != ranks
6400            || e.ctx().ordinal() != ws.e_device
6401        {
6402            return Err("step TP decode v2 input geometry mismatch".into());
6403        }
6404
6405        let qkv_fused = step_tp_qkv_fused_enabled()?;
6406        if gate_shards.is_some() && !qkv_fused {
6407            return Err("step TP decode v2 gate shards require MEMRA_STEP_TP_QKV_FUSED=1".into());
6408        }
6409        let values = decode_input.width;
6410        if h.len() != values {
6411            return Err(format!(
6412                "step TP decode v2 hidden width {} != replicated width {values}",
6413                h.len()
6414            )
6415            .into());
6416        }
6417
6418        if qkv_fused {
6419            // STAGE-BASED flow (graph increment A): h and pos land in fixed e-context stages
6420            // (one e-stream copy each), the entry event covers them, and every rank raw-copies
6421            // from the stages on its own stream — exactly the shape graph capture wraps.
6422            if ws.h_stage.is_none() {
6423                use cudarc::driver::DevicePtr;
6424                let _main = e.gpu.enter_main()?;
6425                let h_stage = e.uninit(values)?;
6426                let pos_stage = e.htod_i32(&[0])?;
6427                {
6428                    let stream = e.stream();
6429                    let (hp, _g0) = h_stage.device_ptr(&stream);
6430                    let (pp, _g1) = pos_stage.device_ptr(&stream);
6431                    ws.raw_h_stage = hp as u64;
6432                    ws.raw_pos_stage = pp as u64;
6433                }
6434                ws.h_stage = Some(h_stage);
6435                ws.pos_stage = Some(pos_stage);
6436                for rank in 0..ranks {
6437                    use cudarc::driver::DevicePtr;
6438                    let engine = &self.ranks[rank];
6439                    let _rmain = engine.gpu.enter_main()?;
6440                    let attn_in = engine.uninit(values)?;
6441                    let (dp, pp) = {
6442                        let stream = engine.stream();
6443                        let (dp, _g2) = attn_in.device_ptr(&stream);
6444                        let (pp, _g3) = ws.pos[rank].device_ptr(&stream);
6445                        (dp as u64, pp as u64)
6446                    };
6447                    ws.raw_attn_in.push(dp);
6448                    ws.raw_pos.push(pp);
6449                    ws.attn_in.push(attn_in);
6450                }
6451                {
6452                    use cudarc::driver::DevicePtr;
6453                    let root = &self.ranks[0];
6454                    let _rmain = root.gpu.enter_main()?;
6455                    let stream = root.stream();
6456                    let (a, _g) = ws.peer_partial.device_ptr(&stream);
6457                    let (b, _g) = ws.k_shadow.device_ptr(&stream);
6458                    let (c, _g) = ws.v_shadow.device_ptr(&stream);
6459                    ws.raw_peer_partial = a as u64;
6460                    ws.raw_k_shadow = b as u64;
6461                    ws.raw_v_shadow = c as u64;
6462                }
6463                {
6464                    use cudarc::driver::DevicePtr;
6465                    let rank1 = &self.ranks[1];
6466                    let _rmain = rank1.gpu.enter_main()?;
6467                    let stream = rank1.stream();
6468                    let (a, _g) = ws.o_partials[1][0].device_ptr(&stream);
6469                    let (b, _g) = ws.k[1].device_ptr(&stream);
6470                    let (c, _g) = ws.v_raw[1].device_ptr(&stream);
6471                    ws.raw_o_partial1 = a as u64;
6472                    ws.raw_k1 = b as u64;
6473                    ws.raw_v1 = c as u64;
6474                }
6475            }
6476            {
6477                let _main = e.gpu.enter_main()?;
6478                {
6479                    // (Always staged: a tcol column below the dcw floor falls back to the
6480                    // normal fused arm, which reads h through this stage.)
6481                    let h_stage = ws.h_stage.as_mut().expect("stage armed above");
6482                    let mut dst = h_stage.slice_mut(0..values);
6483                    e.stream().memcpy_dtod(&h.slice(0..values), &mut dst)?;
6484                }
6485                {
6486                    let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
6487                    let mut dst = pos_stage.slice_mut(0..1);
6488                    e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
6489                }
6490                ws.ev_entry.record(&e.stream())?;
6491            }
6492            for rank in 0..ranks {
6493                let engine = &self.ranks[rank];
6494                let _main = engine.gpu.enter_main()?;
6495                engine.stream().wait(&ws.ev_entry)?;
6496            }
6497        } else {
6498            // Evented replicate flow (the pre-stage shape, kept for the non-fused class).
6499            {
6500                let _main = e.gpu.enter_main()?;
6501                if let Some(gate_raw) = gate_raw {
6502                    let mut gate_dst = ws.gate_e.slice_mut(0..ws.heads);
6503                    e.stream()
6504                        .memcpy_dtod(&gate_raw.slice(0..ws.heads), &mut gate_dst)?;
6505                }
6506                ws.ev_entry.record(&e.stream())?;
6507            }
6508            {
6509                let root = &self.ranks[0];
6510                let _main = root.gpu.enter_main()?;
6511                root.stream().wait(&ws.ev_entry)?;
6512                let mut destination = decode_input.ranks[0].slice_mut(0..values);
6513                root.stream()
6514                    .memcpy_dtod(&h.slice(0..values), &mut destination)?;
6515                ws.ev_refresh.record(&root.stream())?;
6516            }
6517            for rank in 1..ranks {
6518                let engine = &self.ranks[rank];
6519                let _main = engine.gpu.enter_main()?;
6520                engine.stream().wait(&ws.ev_refresh)?;
6521                let (root_rows, peer_rows) = decode_input.ranks.split_at_mut(rank);
6522                let mut destination = peer_rows[0].slice_mut(0..values);
6523                engine
6524                    .stream()
6525                    .memcpy_dtod(&root_rows[0].slice(0..values), &mut destination)?;
6526            }
6527        }
6528        for rank in 0..ranks {
6529            self.decode_v2_input_qkv_rank(
6530                ws,
6531                pos_d,
6532                decode_input,
6533                q_m,
6534                k_m,
6535                v_m,
6536                q_norm,
6537                k_norm,
6538                head_dim,
6539                n_rot,
6540                rope_base,
6541                rope_freqs,
6542                rms_eps,
6543                gate_shards.as_ref(),
6544                qkv_fused,
6545                defer_norm_rope,
6546                rank,
6547                tcol_col,
6548            )?;
6549        }
6550        Ok(())
6551    }
6552
6553    /// One rank's slice of `decode_v2_input_qkv` (projection, norm+rope, gate staging) — the
6554    /// per-device issue unit the whole-token graph captures on that rank's stream.
6555    #[allow(clippy::too_many_arguments)]
6556    pub(crate) fn decode_v2_input_qkv_rank(
6557        &self,
6558        ws: &mut StepTpDecodeV2Ws,
6559        pos_d: &CudaSlice<i32>,
6560        decode_input: &mut ResidentReplicatedDeviceRows,
6561        q_m: &ResidentBf16ColumnParallel,
6562        k_m: &ResidentBf16ColumnParallel,
6563        v_m: &ResidentBf16ColumnParallel,
6564        q_norm: &[CudaSlice<f32>],
6565        k_norm: &[CudaSlice<f32>],
6566        head_dim: usize,
6567        n_rot: usize,
6568        rope_base: f32,
6569        rope_freqs: &[Option<&CudaSlice<f32>>],
6570        rms_eps: f32,
6571        gate_shards: Option<&StepTpGateShards<'_>>,
6572        qkv_fused: bool,
6573        defer_norm_rope: bool,
6574        rank: usize,
6575        tcol_col: Option<usize>,
6576    ) -> Result<(), Box<dyn std::error::Error>> {
6577        let ranks = self.ranks.len();
6578        let local_heads = ws.local_q_dim / head_dim;
6579        let local_kv_heads = ws.local_kv_dim / head_dim;
6580        let engine = &self.ranks[rank];
6581        let _main = engine.gpu.enter_main()?;
6582        let ws_e_device = ws.e_device;
6583        // T-COLUMN SELECT (spec verify): the projections for this column were precomputed
6584        // by the weight-amortized tcol kernel — copy the column into the single-row buffers
6585        // (pure f32 moves, bit-exact) and skip the per-column matvec. Rope/norm/append run
6586        // below exactly as in the t=1 program.
6587        if qkv_fused && tcol_col.is_some() {
6588            let c = tcol_col.expect("checked");
6589            if ws.tcol_cap == 0 || ws.tcol_q.len() != ranks {
6590                return Err("tcol select without precompute".into());
6591            }
6592            // The select skips the matvec but NOT the position: rope/append below still
6593            // read this rank's pos buffer, which only the (skipped) stage path fills for
6594            // peer-device ranks. Stage it here or rank1 ropes at the previous position.
6595            if engine.ctx().ordinal() != ws_e_device {
6596                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
6597            }
6598            let StepTpDecodeV2Ws {
6599                tcol_q,
6600                tcol_k,
6601                tcol_v,
6602                tcol_g,
6603                q_raw,
6604                k_raw,
6605                v_raw,
6606                gate,
6607                local_q_dim,
6608                local_kv_dim,
6609                heads,
6610                ..
6611            } = &mut *ws;
6612            let lg = *heads / ranks;
6613            let stream = engine.stream();
6614            {
6615                let mut dst = q_raw[rank].slice_mut(0..*local_q_dim);
6616                stream.memcpy_dtod(
6617                    &tcol_q[rank].slice(c * *local_q_dim..(c + 1) * *local_q_dim),
6618                    &mut dst,
6619                )?;
6620            }
6621            {
6622                let mut dst = k_raw[rank].slice_mut(0..*local_kv_dim);
6623                stream.memcpy_dtod(
6624                    &tcol_k[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
6625                    &mut dst,
6626                )?;
6627            }
6628            {
6629                let mut dst = v_raw[rank].slice_mut(0..*local_kv_dim);
6630                stream.memcpy_dtod(
6631                    &tcol_v[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
6632                    &mut dst,
6633                )?;
6634            }
6635            if lg > 0 {
6636                let mut dst = gate[rank].slice_mut(0..lg);
6637                stream.memcpy_dtod(&tcol_g[rank].slice(c * lg..(c + 1) * lg), &mut dst)?;
6638            }
6639            if !defer_norm_rope {
6640                // Below the dcw floor (or a non-defer shape) the col-select cannot apply:
6641                // fall through and recompute this column's QKV from the REAL h row — the
6642                // caller always passes it. The slab copies above are dead stores.
6643            } else {
6644                return Ok(());
6645            }
6646        }
6647        if qkv_fused {
6648            // Stage-based input: raw copies from the fixed e-context stages (capture-safe;
6649            // eager ordering comes from the caller's ev_entry wait on this stream). The rank
6650            // SHARING e's device reads the stages directly — same context (probed), ordering
6651            // identical (ev_entry / graph edge), bytes identical: the copies are pure waste.
6652            let same_dev = engine.ctx().ordinal() == ws.e_device;
6653            if !same_dev {
6654                raw_copy_bytes(
6655                    ws.raw_attn_in[rank],
6656                    ws.raw_h_stage,
6657                    q_m.in_features * 4,
6658                    engine,
6659                )?;
6660                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
6661            }
6662            let StepTpDecodeV2Ws {
6663                q_raw,
6664                k_raw,
6665                v_raw,
6666                gate,
6667                gate_e,
6668                attn_in,
6669                h_stage,
6670                heads,
6671                local_q_dim,
6672                local_kv_dim,
6673                w8_aq,
6674                w8_ad,
6675                w8_in,
6676                ..
6677            } = &mut *ws;
6678            let input_ref: &CudaSlice<f32> = if same_dev {
6679                h_stage
6680                    .as_ref()
6681                    .ok_or("step TP decode v2 stage not armed")?
6682            } else {
6683                &attn_in[rank]
6684            };
6685            match (
6686                &q_m.ranks[rank].weight,
6687                &k_m.ranks[rank].weight,
6688                &v_m.ranks[rank].weight,
6689            ) {
6690                (
6691                    ResidentBf16Weight::F32(wq),
6692                    ResidentBf16Weight::F32(wk),
6693                    ResidentBf16Weight::F32(wv),
6694                ) => {
6695                    let (wg, out_g) = match &gate_shards {
6696                        Some(StepTpGateShards::F32(shards)) => (&shards[rank], *heads / ranks),
6697                        Some(StepTpGateShards::Bf16(_)) => {
6698                            return Err("step TP decode v2 gate shard class does not \
6699                                            match the F32 projections"
6700                                .into());
6701                        }
6702                        // out_g = 0: the kernel never reads wg; any resident buffer works.
6703                        None => (&*gate_e, 0),
6704                    };
6705                    engine.matvec_f32_qkv_into(
6706                        wq,
6707                        wk,
6708                        wv,
6709                        wg,
6710                        input_ref,
6711                        &mut q_raw[rank],
6712                        &mut k_raw[rank],
6713                        &mut v_raw[rank],
6714                        &mut gate[rank],
6715                        q_m.in_features,
6716                        *local_q_dim,
6717                        *local_kv_dim,
6718                        out_g,
6719                    )?;
6720                }
6721                (
6722                    ResidentBf16Weight::Bf16(wq),
6723                    ResidentBf16Weight::Bf16(wk),
6724                    ResidentBf16Weight::Bf16(wv),
6725                ) => {
6726                    let (wg, out_g) = match &gate_shards {
6727                        Some(StepTpGateShards::Bf16(shards)) => (&shards[rank], *heads / ranks),
6728                        Some(StepTpGateShards::F32(_)) => {
6729                            return Err("step TP decode v2 gate shard class does not \
6730                                            match the bf16 projections"
6731                                .into());
6732                        }
6733                        None => (wq, 0),
6734                    };
6735                    // MEMRA_STEP_TP_W8: q8_0 weights + q8_1 activation through mmvq instead of
6736                    // the fused bf16 qkvg. NUMERIC CLASS (int8 dp4a with per-32 scales, not a
6737                    // bf16 fma chain) — argmax-gated, never a bit-tape flip. Q, K and V each
6738                    // get their own launch because the fused kernel has no q8 twin; the gate
6739                    // rows stay bf16 (32 rows, ~0.3 MB, nothing to win and one less class to
6740                    // qualify). Measured motive: 23.0 us bf16 -> 14.0 us q8 at this shape.
6741                    let in_f = q_m.in_features;
6742                    let q8_ready = crate::step_tp_w8_on()
6743                        && q_m.ranks[rank].q8.is_some()
6744                        && k_m.ranks[rank].q8.is_some()
6745                        && v_m.ranks[rank].q8.is_some();
6746                    if q8_ready {
6747                        if *w8_in != in_f || w8_aq.len() != ranks {
6748                            w8_aq.clear();
6749                            w8_ad.clear();
6750                            for e_rank in &self.ranks {
6751                                let _m = e_rank.gpu.enter_main()?;
6752                                w8_aq.push(e_rank.alloc_uninit::<i8>(in_f)?);
6753                                w8_ad.push(e_rank.alloc_uninit::<f32>(in_f / 32)?);
6754                            }
6755                            *w8_in = in_f;
6756                        }
6757                        engine.quantize_q8_1_into(
6758                            input_ref,
6759                            1,
6760                            in_f,
6761                            &mut w8_aq[rank],
6762                            &mut w8_ad[rank],
6763                        )?;
6764                        // ONE launch over the stacked q/k/v rows. The three-call version
6765                        // measured 79.52 vs 80.72 tok/s — SLOWER than the bf16 fused kernel —
6766                        // because three launches plus the activation quantize cost more than
6767                        // the halved weight bytes save. Bit-identical to those three calls.
6768                        engine.qmatvec_q8_0_qkv_rp_into(
6769                            q_m.ranks[rank].q8.as_ref().unwrap(),
6770                            k_m.ranks[rank].q8.as_ref().unwrap(),
6771                            v_m.ranks[rank].q8.as_ref().unwrap(),
6772                            &w8_aq[rank],
6773                            &w8_ad[rank],
6774                            &mut q_raw[rank],
6775                            &mut k_raw[rank],
6776                            &mut v_raw[rank],
6777                            in_f,
6778                            *local_q_dim,
6779                            *local_kv_dim,
6780                        )?;
6781                        if out_g > 0 {
6782                            engine.matvec_bf16_into(wg, input_ref, &mut gate[rank], in_f, out_g)?;
6783                        }
6784                    } else {
6785                        engine.matvec_bf16_qkvg_into(
6786                            wq,
6787                            wk,
6788                            wv,
6789                            wg,
6790                            input_ref,
6791                            &mut q_raw[rank],
6792                            &mut k_raw[rank],
6793                            &mut v_raw[rank],
6794                            &mut gate[rank],
6795                            q_m.in_features,
6796                            *local_q_dim,
6797                            *local_kv_dim,
6798                            out_g,
6799                        )?;
6800                    }
6801                }
6802                _ => {
6803                    return Err("step TP decode v2 QKV projections mix residency classes".into());
6804                }
6805            }
6806        } else {
6807            for (matrix, local_out, raw) in [
6808                (q_m, ws.local_q_dim, &mut ws.q_raw),
6809                (k_m, ws.local_kv_dim, &mut ws.k_raw),
6810                (v_m, ws.local_kv_dim, &mut ws.v_raw),
6811            ] {
6812                let ResidentBf16Weight::F32(values_w) = &matrix.ranks[rank].weight else {
6813                    return Err("step TP decode v2 lost its F32 projection residency".into());
6814                };
6815                let chunk_rows = matrix.canonical_chunk_rows.unwrap_or(local_out);
6816                engine.linear_f32_resident_canonical_rows_t1_into(
6817                    &decode_input.ranks[rank],
6818                    values_w,
6819                    &mut raw[rank],
6820                    matrix.in_features,
6821                    local_out,
6822                    chunk_rows,
6823                )?;
6824            }
6825        }
6826        if qkv_fused && defer_norm_rope {
6827            // FUSION #1 defers norm+rope to the caller's fused rope+append+inc launch.
6828        } else if qkv_fused {
6829            // Fused norm+rope: one launch; the position comes from the rank-local staged
6830            // copy (raw-copied above from the fixed e-context pos stage — capture-safe).
6831            let StepTpDecodeV2Ws {
6832                q_raw,
6833                k_raw,
6834                q,
6835                k,
6836                pos,
6837                pos_stage,
6838                ..
6839            } = &mut *ws;
6840            let same_dev = engine.ctx().ordinal() == ws_e_device;
6841            let pos_ref: &CudaSlice<i32> = if same_dev {
6842                pos_stage
6843                    .as_ref()
6844                    .ok_or("step TP decode v2 pos stage not armed")?
6845            } else {
6846                &pos[rank]
6847            };
6848            engine.qk_norm_rope_into(
6849                &q_raw[rank],
6850                &k_raw[rank],
6851                &q_norm[rank],
6852                &k_norm[rank],
6853                &mut q[rank],
6854                &mut k[rank],
6855                pos_ref,
6856                head_dim,
6857                n_rot,
6858                local_heads,
6859                local_kv_heads,
6860                rms_eps,
6861                rope_base,
6862                1.0,
6863                rope_freqs[rank],
6864            )?;
6865        } else {
6866            engine.rms_norm(
6867                &ws.q_raw[rank],
6868                &q_norm[rank],
6869                &mut ws.q[rank],
6870                head_dim,
6871                local_heads,
6872                rms_eps,
6873            )?;
6874            engine.rms_norm(
6875                &ws.k_raw[rank],
6876                &k_norm[rank],
6877                &mut ws.k[rank],
6878                head_dim,
6879                local_kv_heads,
6880                rms_eps,
6881            )?;
6882            {
6883                let mut pos_dst = ws.pos[rank].slice_mut(0..1);
6884                engine
6885                    .stream()
6886                    .memcpy_dtod(&pos_d.slice(0..1), &mut pos_dst)?;
6887            }
6888            engine.rope_neox2(
6889                &mut ws.q[rank],
6890                &mut ws.k[rank],
6891                &ws.pos[rank],
6892                head_dim,
6893                n_rot,
6894                local_heads,
6895                local_kv_heads,
6896                1,
6897                rope_base,
6898                1.0,
6899                rope_freqs[rank],
6900            )?;
6901        }
6902        if gate_shards.is_none() {
6903            let gate_start = rank * (ws.heads / ranks);
6904            let mut gate_dst = ws.gate[rank].slice_mut(0..ws.heads / ranks);
6905            engine.stream().memcpy_dtod(
6906                &ws.gate_e.slice(gate_start..gate_start + ws.heads / ranks),
6907                &mut gate_dst,
6908            )?;
6909        }
6910        Ok(())
6911    }
6912
6913    /// One rank's O-partial slice of `decode_v2_finish` — the per-device issue unit the
6914    /// whole-token graph captures on that rank's stream (the rank-done event stays with the
6915    /// eager caller; graphs order via parent edges instead).
6916    pub(crate) fn decode_v2_finish_rank_partial(
6917        &self,
6918        ws: &mut StepTpDecodeV2Ws,
6919        o_m: &ResidentStepBf16RowParallel,
6920        o_fused: bool,
6921        rank: usize,
6922    ) -> Result<(), Box<dyn std::error::Error>> {
6923        let engine = &self.ranks[rank];
6924        let _main = engine.gpu.enter_main()?;
6925        if o_fused {
6926            let StepTpDecodeV2Ws {
6927                gated,
6928                o_partials,
6929                o_block_cols,
6930                o_out,
6931                w8o_aq,
6932                w8o_ad,
6933                w8o_in,
6934                ..
6935            } = &mut *ws;
6936            let all_f32 = o_m.ranks[rank]
6937                .iter()
6938                .all(|block| matches!(block.weight, ResidentBf16Weight::F32(_)));
6939            if all_f32 {
6940                let mut weights = Vec::with_capacity(4);
6941                for block in 0..4 {
6942                    let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
6943                        unreachable!("all_f32 checked above");
6944                    };
6945                    weights.push(weight);
6946                }
6947                engine.matvec_f32_b4_into(
6948                    [weights[0], weights[1], weights[2], weights[3]],
6949                    &gated[rank],
6950                    &mut o_partials[rank][0],
6951                    *o_block_cols,
6952                    *o_out,
6953                )?;
6954            } else if crate::step_tp_w8_on() && (0..4).all(|b| o_m.ranks[rank][b].q8.is_some()) {
6955                // MEMRA_STEP_TP_W8, o_proj half: quantize the gated attention output once and
6956                // run all four HEAD_SPLIT blocks in one q8 launch. Measured motive: bf16 b4 is
6957                // 24.2 us/layer against 11.7 for the q8 shape — the largest decode line left
6958                // after the QKV arm banked +2.9%.
6959                let in_f = 4 * *o_block_cols;
6960                if *w8o_in != in_f || w8o_aq.len() != self.ranks.len() {
6961                    w8o_aq.clear();
6962                    w8o_ad.clear();
6963                    for e_rank in &self.ranks {
6964                        let _m = e_rank.gpu.enter_main()?;
6965                        w8o_aq.push(e_rank.alloc_uninit::<i8>(in_f)?);
6966                        w8o_ad.push(e_rank.alloc_uninit::<f32>(in_f / 32)?);
6967                    }
6968                    *w8o_in = in_f;
6969                }
6970                engine.quantize_q8_1_into(
6971                    &gated[rank],
6972                    1,
6973                    in_f,
6974                    &mut w8o_aq[rank],
6975                    &mut w8o_ad[rank],
6976                )?;
6977                engine.qmatvec_q8_0_b4_rp_into(
6978                    [
6979                        o_m.ranks[rank][0].q8.as_ref().unwrap(),
6980                        o_m.ranks[rank][1].q8.as_ref().unwrap(),
6981                        o_m.ranks[rank][2].q8.as_ref().unwrap(),
6982                        o_m.ranks[rank][3].q8.as_ref().unwrap(),
6983                    ],
6984                    &w8o_aq[rank],
6985                    &w8o_ad[rank],
6986                    &mut o_partials[rank][0],
6987                    *o_block_cols,
6988                    *o_out,
6989                )?;
6990            } else {
6991                let mut weights = Vec::with_capacity(4);
6992                for block in 0..4 {
6993                    let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
6994                        return Err("step TP decode v2 O projections mix residency classes".into());
6995                    };
6996                    weights.push(weight);
6997                }
6998                engine.matvec_bf16_b4_into(
6999                    [weights[0], weights[1], weights[2], weights[3]],
7000                    &gated[rank],
7001                    &mut o_partials[rank][0],
7002                    *o_block_cols,
7003                    *o_out,
7004                )?;
7005            }
7006        } else {
7007            for block in 0..ws.blocks_per_rank {
7008                let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
7009                    return Err("step TP decode v2 lost its F32 O residency".into());
7010                };
7011                let x =
7012                    ws.gated[rank].slice(block * ws.o_block_cols..(block + 1) * ws.o_block_cols);
7013                let w = weight.slice(0..weight.len());
7014                let mut y = ws.o_partials[rank][block].slice_mut(0..ws.o_out);
7015                engine.linear_t1_into(&x, &w, &mut y, ws.o_block_cols, ws.o_out)?;
7016            }
7017        }
7018        Ok(())
7019    }
7020
7021    /// v2 phase 2: canonical-block O reduction on the root device plus the K/V shadow gathers,
7022    /// returning a fresh model-engine output ordered behind `ev_oproj` on `e`'s stream.
7023    ///
7024    /// The caller must have queued every rank's attention work (reading `ws.gated`, `ws.k`,
7025    /// `ws.v_raw`) on the rank streams before this call. Reduction order is identical to
7026    /// `step_bf16_row_parallel_resident_native`: zeros, then rank 0's blocks, then each peer
7027    /// rank's blocks, one `add` per block.
7028    pub(crate) fn decode_v2_finish(
7029        &self,
7030        ws: &mut StepTpDecodeV2Ws,
7031        e: &Engine,
7032        o_m: &ResidentStepBf16RowParallel,
7033    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7034        let ranks = self.ranks.len();
7035        if e.ctx().ordinal() != ws.e_device {
7036            return Err("step TP decode v2 finish engine changed".into());
7037        }
7038        // MEMRA_STEP_TP_QKV_FUSED extends to the O path: one matvec_f32_b4 launch per rank
7039        // (in-order canonical block accumulation per element) and a single peer-copy + add on
7040        // the root, replacing 4 cuBLASLt launches per rank + the 4-copy/8-add chain. Same
7041        // numeric-class door and gate as the fused QKV projection.
7042        let o_fused = step_tp_qkv_fused_enabled()? && ws.blocks_per_rank == 4 && ranks == 2;
7043
7044        // Per-rank O block partials on the owning rank's stream (serial after the attention
7045        // kernels the driver queued there), then the rank-done event for root's peer reads.
7046        for rank in 0..ranks {
7047            self.decode_v2_finish_rank_partial(ws, o_m, o_fused, rank)?;
7048            if rank == 0 {
7049                // root == rank0: its own stream order covers the partial; only peers need
7050                // the record/wait pair (host-op diet, matches the routes-arm skip).
7051                continue;
7052            }
7053            let engine = &self.ranks[rank];
7054            let _main = engine.gpu.enter_main()?;
7055            ws.ev_rank[rank].record(&engine.stream())?;
7056        }
7057
7058        // Root reduce in canonical order + shadow gathers, all on the root stream.
7059        let root = &self.ranks[0];
7060        #[allow(unused_assignments)]
7061        let mut final_in_a = false;
7062        {
7063            let _main = root.gpu.enter_main()?;
7064            for ev in ws.ev_rank.iter().skip(1) {
7065                root.stream().wait(ev)?;
7066            }
7067            if o_fused && oproj_direct_on() && ranks == 2 && no_local_shadow_on() {
7068                // DIRECT JOIN: rank1's partial already sits in root memory (P2P kernel
7069                // stores; visibility guaranteed by the ev_rank[1] wait above), rank0's
7070                // partial is root-stream-ordered — record ONE event and let the model
7071                // engine do the single add itself, straight into its own output row.
7072                // Same operands, same add order as finish_root_fused: BIT-IDENTICAL.
7073                ws.ev_oproj.record(&root.stream())?;
7074                let _main = e.gpu.enter_main()?;
7075                e.stream().wait(&ws.ev_oproj)?;
7076                let mut output = e.uninit(ws.o_out)?;
7077                if oproj_tail_on() && oproj_tail_eligible() {
7078                    // M2: defer the add into the residual+norm consumer (waits stay HERE;
7079                    // only the arithmetic moves). `output` is returned unwritten.
7080                    use cudarc::driver::DevicePtr;
7081                    let stream = e.stream();
7082                    let (p0, _g0) = ws.o_partials[0][0].device_ptr(&stream);
7083                    let (p1, _g1) = ws.o_partials[1][0].device_ptr(&stream);
7084                    set_oproj_tail((p0 as u64, p1 as u64));
7085                    return Ok(output);
7086                }
7087                e.add(
7088                    &ws.o_partials[0][0],
7089                    &ws.o_partials[1][0],
7090                    &mut output,
7091                    ws.o_out,
7092                )?;
7093                return Ok(output);
7094            }
7095            if o_fused {
7096                self.decode_v2_finish_root_fused(ws)?;
7097                ws.ev_oproj.record(&root.stream())?;
7098                let _main = e.gpu.enter_main()?;
7099                e.stream().wait(&ws.ev_oproj)?;
7100                let mut output = e.uninit(ws.o_out)?;
7101                e.stream().memcpy_dtod(
7102                    &ws.reduce_a.slice(0..ws.o_out),
7103                    &mut output.slice_mut(0..ws.o_out),
7104                )?;
7105                return Ok(output);
7106            }
7107            let mut first = true;
7108            let mut current_is_a = false;
7109            for rank in 0..ranks {
7110                for block in 0..ws.blocks_per_rank {
7111                    let use_peer = rank != 0;
7112                    if use_peer {
7113                        root.stream()
7114                            .memcpy_dtod(&ws.o_partials[rank][block], &mut ws.peer_partial)?;
7115                    }
7116                    // add(prev, partial) -> the other reduce buffer, exactly one add per block
7117                    match (first, current_is_a, use_peer) {
7118                        (true, _, true) => {
7119                            root.add(&ws.zeros, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
7120                        }
7121                        (true, _, false) => root.add(
7122                            &ws.zeros,
7123                            &ws.o_partials[0][block],
7124                            &mut ws.reduce_a,
7125                            ws.o_out,
7126                        )?,
7127                        (false, true, true) => {
7128                            root.add(&ws.reduce_a, &ws.peer_partial, &mut ws.reduce_b, ws.o_out)?
7129                        }
7130                        (false, true, false) => root.add(
7131                            &ws.reduce_a,
7132                            &ws.o_partials[0][block],
7133                            &mut ws.reduce_b,
7134                            ws.o_out,
7135                        )?,
7136                        (false, false, true) => {
7137                            root.add(&ws.reduce_b, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
7138                        }
7139                        (false, false, false) => root.add(
7140                            &ws.reduce_b,
7141                            &ws.o_partials[0][block],
7142                            &mut ws.reduce_a,
7143                            ws.o_out,
7144                        )?,
7145                    }
7146                    current_is_a = first || !current_is_a;
7147                    first = false;
7148                }
7149            }
7150            final_in_a = current_is_a;
7151
7152            for rank in 0..ranks {
7153                let start = rank * ws.local_kv_dim;
7154                let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
7155                root.stream().memcpy_dtod(&ws.k[rank], &mut k_dst)?;
7156                let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
7157                root.stream().memcpy_dtod(&ws.v_raw[rank], &mut v_dst)?;
7158            }
7159            ws.ev_oproj.record(&root.stream())?;
7160        }
7161
7162        // Model-engine output: e waits the root event, then copies the reduced row into a
7163        // fresh e-context buffer (same ownership contract as v1's `e.htod`). The same wait
7164        // orders the driver's shadow append (it reads ws.k_shadow/ws.v_shadow on e's stream).
7165        let _main = e.gpu.enter_main()?;
7166        e.stream().wait(&ws.ev_oproj)?;
7167        let mut output = e.uninit(ws.o_out)?;
7168        let source = if final_in_a {
7169            &ws.reduce_a
7170        } else {
7171            &ws.reduce_b
7172        };
7173        e.stream().memcpy_dtod(
7174            &source.slice(0..ws.o_out),
7175            &mut output.slice_mut(0..ws.o_out),
7176        )?;
7177        Ok(output)
7178    }
7179
7180    pub fn run_routed_experts(
7181        &self,
7182        experts: &ResidentExpertParallel,
7183        input: &[f32],
7184        tokens: usize,
7185        selected: &[usize],
7186        route_weights: &[f32],
7187        experts_per_token: usize,
7188        activation_limit: Option<f32>,
7189    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7190        validate_step_expert_activation_limit(activation_limit)?;
7191        validate_ep_residency(&self.ranks, experts)?;
7192        validate_activations(input, tokens, experts.input_width)?;
7193        let pairs = tokens
7194            .checked_mul(experts_per_token)
7195            .ok_or("EP route count overflow")?;
7196        if selected.len() != pairs || route_weights.len() != pairs {
7197            return Err(format!(
7198                "EP routes selected={} weights={} != tokens {tokens} x experts/token \
7199                 {experts_per_token} ({pairs})",
7200                selected.len(),
7201                route_weights.len(),
7202            )
7203            .into());
7204        }
7205        if !route_weights.iter().all(|weight| weight.is_finite()) {
7206            return Err("EP route weights contain a non-finite value".into());
7207        }
7208        if self.native_p2p {
7209            return self.run_routed_experts_native(
7210                experts,
7211                input,
7212                tokens,
7213                selected,
7214                route_weights,
7215                experts_per_token,
7216                activation_limit,
7217            );
7218        }
7219
7220        let mut output = vec![0.0f32; tokens * experts.input_width];
7221        let per_rank = experts.expert_count / experts.ranks.len();
7222        for token in 0..tokens {
7223            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7224            for slot in 0..experts_per_token {
7225                let pair = token * experts_per_token + slot;
7226                let expert = selected[pair];
7227                if expert >= experts.expert_count {
7228                    return Err(format!(
7229                        "EP selected expert {expert} outside 0..{}",
7230                        experts.expert_count
7231                    )
7232                    .into());
7233                }
7234                let owner = expert / per_rank;
7235                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7236                let rank = &experts.ranks[owner];
7237                let engine = &self.ranks[owner];
7238                let gate =
7239                    run_resident_bank_expert(engine, &rank.gate, local_expert, input_row, 1)?;
7240                let up = run_resident_bank_expert(engine, &rank.up, local_expert, input_row, 1)?;
7241                let activated: Vec<f32> = gate
7242                    .iter()
7243                    .zip(&up)
7244                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
7245                    .collect();
7246                debug_assert_eq!(activated.len(), experts.expert_width);
7247                let down =
7248                    run_resident_bank_expert(engine, &rank.down, local_expert, &activated, 1)?;
7249                let weight = route_weights[pair];
7250                for (sum, value) in output
7251                    [token * experts.input_width..(token + 1) * experts.input_width]
7252                    .iter_mut()
7253                    .zip(down)
7254                {
7255                    *sum += weight * value;
7256                }
7257            }
7258        }
7259        Ok(output)
7260    }
7261
7262    fn run_routed_experts_native(
7263        &self,
7264        experts: &ResidentExpertParallel,
7265        input: &[f32],
7266        tokens: usize,
7267        selected: &[usize],
7268        route_weights: &[f32],
7269        experts_per_token: usize,
7270        activation_limit: Option<f32>,
7271    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7272        if !self.native_p2p || self.ranks.len() < 2 {
7273            return Err("native EP execution requires at least two P2P ranks".into());
7274        }
7275        if self.ep_device_arithmetic {
7276            return self.run_routed_experts_native_device(
7277                experts,
7278                input,
7279                tokens,
7280                selected,
7281                route_weights,
7282                experts_per_token,
7283                activation_limit,
7284            );
7285        }
7286        let mut output = vec![0.0f32; tokens * experts.input_width];
7287        let per_rank = experts.expert_count / experts.ranks.len();
7288        for token in 0..tokens {
7289            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7290            let mut rank_inputs = (0..self.ranks.len())
7291                .map(|_| None)
7292                .collect::<Vec<Option<CudaSlice<f32>>>>();
7293            rank_inputs[0] = Some({
7294                let root = &self.ranks[0];
7295                let _main = root.gpu.enter_main()?;
7296                root.htod(input_row)?
7297            });
7298
7299            for slot in 0..experts_per_token {
7300                let pair = token * experts_per_token + slot;
7301                let expert = selected[pair];
7302                if expert >= experts.expert_count {
7303                    return Err(format!(
7304                        "EP selected expert {expert} outside 0..{}",
7305                        experts.expert_count
7306                    )
7307                    .into());
7308                }
7309                let owner = expert / per_rank;
7310                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7311                if rank_inputs[owner].is_none() {
7312                    let peer_input = {
7313                        let root_input = rank_inputs[0]
7314                            .as_ref()
7315                            .ok_or("native EP lost its root input")?;
7316                        let engine = &self.ranks[owner];
7317                        let _main = engine.gpu.enter_main()?;
7318                        let mut peer_input = engine.uninit(experts.input_width)?;
7319                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
7320                        peer_input
7321                    };
7322                    rank_inputs[owner] = Some(peer_input);
7323                }
7324
7325                let rank = &experts.ranks[owner];
7326                let engine = &self.ranks[owner];
7327                let owner_input = rank_inputs[owner]
7328                    .as_ref()
7329                    .ok_or("native EP owner input is absent after dispatch")?;
7330                let gate = run_resident_bank_expert_device(
7331                    engine,
7332                    &rank.gate,
7333                    local_expert,
7334                    owner_input,
7335                    1,
7336                )?;
7337                let up = run_resident_bank_expert_device(
7338                    engine,
7339                    &rank.up,
7340                    local_expert,
7341                    owner_input,
7342                    1,
7343                )?;
7344                let (gate, up) = {
7345                    let _main = engine.gpu.enter_main()?;
7346                    (engine.dtoh(&gate)?, engine.dtoh(&up)?)
7347                };
7348                let activated = gate
7349                    .iter()
7350                    .zip(&up)
7351                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
7352                    .collect::<Vec<_>>();
7353                debug_assert_eq!(activated.len(), experts.expert_width);
7354                let activated = {
7355                    let _main = engine.gpu.enter_main()?;
7356                    engine.htod(&activated)?
7357                };
7358                let down = run_resident_bank_expert_device(
7359                    engine,
7360                    &rank.down,
7361                    local_expert,
7362                    &activated,
7363                    1,
7364                )?;
7365                let down = if owner == 0 {
7366                    let _main = engine.gpu.enter_main()?;
7367                    engine.dtoh(&down)?
7368                } else {
7369                    let root = &self.ranks[0];
7370                    let _main = root.gpu.enter_main()?;
7371                    let mut root_down = root.uninit(experts.input_width)?;
7372                    root.stream().memcpy_dtod(&down, &mut root_down)?;
7373                    root.dtoh(&root_down)?
7374                };
7375                let weight = route_weights[pair];
7376                for (sum, value) in output
7377                    [token * experts.input_width..(token + 1) * experts.input_width]
7378                    .iter_mut()
7379                    .zip(down)
7380                {
7381                    *sum += weight * value;
7382                }
7383            }
7384        }
7385        Ok(output)
7386    }
7387
7388    fn run_routed_experts_native_device(
7389        &self,
7390        experts: &ResidentExpertParallel,
7391        input: &[f32],
7392        tokens: usize,
7393        selected: &[usize],
7394        route_weights: &[f32],
7395        experts_per_token: usize,
7396        activation_limit: Option<f32>,
7397    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7398        if !self.native_p2p || !self.ep_device_arithmetic || self.ranks.len() < 2 {
7399            return Err(
7400                "device-resident EP arithmetic requires at least two native P2P ranks".into(),
7401            );
7402        }
7403        let mut output = Vec::with_capacity(tokens * experts.input_width);
7404        let per_rank = experts.expert_count / experts.ranks.len();
7405        let root = &self.ranks[0];
7406        for token in 0..tokens {
7407            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7408            let mut rank_inputs = (0..self.ranks.len())
7409                .map(|_| None)
7410                .collect::<Vec<Option<CudaSlice<f32>>>>();
7411            rank_inputs[0] = Some({
7412                let _main = root.gpu.enter_main()?;
7413                root.htod(input_row)?
7414            });
7415            let mut root_output = {
7416                let _main = root.gpu.enter_main()?;
7417                root.zeros(experts.input_width)?
7418            };
7419            let mut remote_down_keepalive = Vec::new();
7420
7421            for slot in 0..experts_per_token {
7422                let pair = token * experts_per_token + slot;
7423                let expert = selected[pair];
7424                if expert >= experts.expert_count {
7425                    return Err(format!(
7426                        "EP selected expert {expert} outside 0..{}",
7427                        experts.expert_count
7428                    )
7429                    .into());
7430                }
7431                let owner = expert / per_rank;
7432                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7433                if rank_inputs[owner].is_none() {
7434                    let peer_input = {
7435                        let root_input = rank_inputs[0]
7436                            .as_ref()
7437                            .ok_or("native EP lost its root input")?;
7438                        let engine = &self.ranks[owner];
7439                        let _main = engine.gpu.enter_main()?;
7440                        let mut peer_input = engine.uninit(experts.input_width)?;
7441                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
7442                        peer_input
7443                    };
7444                    rank_inputs[owner] = Some(peer_input);
7445                }
7446
7447                let rank = &experts.ranks[owner];
7448                let engine = &self.ranks[owner];
7449                let owner_input = rank_inputs[owner]
7450                    .as_ref()
7451                    .ok_or("native EP owner input is absent after dispatch")?;
7452                let gate = run_resident_bank_expert_device(
7453                    engine,
7454                    &rank.gate,
7455                    local_expert,
7456                    owner_input,
7457                    1,
7458                )?;
7459                let up = run_resident_bank_expert_device(
7460                    engine,
7461                    &rank.up,
7462                    local_expert,
7463                    owner_input,
7464                    1,
7465                )?;
7466                let activated = {
7467                    let _main = engine.gpu.enter_main()?;
7468                    let mut activated = engine.uninit(experts.expert_width)?;
7469                    if let Some(limit) = activation_limit {
7470                        engine.silu_clamped_mul_host_expf(
7471                            &gate,
7472                            &up,
7473                            limit,
7474                            &mut activated,
7475                            experts.expert_width,
7476                        )?;
7477                    } else {
7478                        engine.silu_mul_host_expf(
7479                            &gate,
7480                            &up,
7481                            &mut activated,
7482                            experts.expert_width,
7483                        )?;
7484                    }
7485                    activated
7486                };
7487                let down = run_resident_bank_expert_device(
7488                    engine,
7489                    &rank.down,
7490                    local_expert,
7491                    &activated,
7492                    1,
7493                )?;
7494                let root_down = if owner == 0 {
7495                    down
7496                } else {
7497                    let _main = root.gpu.enter_main()?;
7498                    let mut root_down = root.uninit(experts.input_width)?;
7499                    root.stream().memcpy_dtod(&down, &mut root_down)?;
7500                    // The peer copy runs on the root stream. Keep its remote source alive until
7501                    // the final root readback synchronizes that stream; otherwise async free can
7502                    // recycle the owner's allocation while cuMemcpyPeerAsync is still reading it.
7503                    remote_down_keepalive.push(down);
7504                    root_down
7505                };
7506                let _main = root.gpu.enter_main()?;
7507                let mut destination = root_output.slice_mut(0..experts.input_width);
7508                root.axpy_host_into(
7509                    &root_down.slice(0..root_down.len()),
7510                    route_weights[pair],
7511                    &mut destination,
7512                    experts.input_width,
7513                )?;
7514            }
7515
7516            let _main = root.gpu.enter_main()?;
7517            let root_output = root.dtoh(&root_output)?;
7518            drop(remote_down_keepalive);
7519            output.extend(root_output);
7520        }
7521        Ok(output)
7522    }
7523}
7524
7525fn validate_column_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
7526    if matrix.out_features % tp != 0 {
7527        return Err(format!(
7528            "column-parallel out_features {} is not divisible by TP={tp}",
7529            matrix.out_features
7530        ));
7531    }
7532    let local_out = matrix.out_features / tp;
7533    if local_out % FP8_BLOCK != 0 {
7534        return Err(format!(
7535            "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
7536             E4M3 scale block"
7537        ));
7538    }
7539    Ok(())
7540}
7541
7542fn step_bf16_canonical_chunk_rows(out_features: usize, tp: usize) -> Result<usize, String> {
7543    if !matches!(tp, 1 | 2 | 4 | 8) {
7544        return Err(format!(
7545            "Step BF16 canonical projection requires TP1/TP2/TP4/TP8, got TP={tp}"
7546        ));
7547    }
7548    if out_features == 0 || out_features % PRODUCT_MAX_CARDS != 0 {
7549        return Err(format!(
7550            "Step BF16 output width {out_features} is not divisible by the TP8 product envelope"
7551        ));
7552    }
7553    let canonical_rows = out_features / PRODUCT_MAX_CARDS;
7554    let local_out = out_features / tp;
7555    if local_out % canonical_rows != 0 {
7556        return Err(format!(
7557            "Step BF16 TP={tp} output shard {local_out} is not divisible by canonical \
7558             {canonical_rows}-row chunks"
7559        ));
7560    }
7561    Ok(canonical_rows)
7562}
7563
7564fn step_bf16_canonical_chunk_cols(in_features: usize, tp: usize) -> Result<usize, String> {
7565    if !matches!(tp, 1 | 2 | 4 | 8) {
7566        return Err(format!(
7567            "Step BF16 canonical row projection requires TP1/TP2/TP4/TP8, got TP={tp}"
7568        ));
7569    }
7570    if in_features == 0 || in_features % PRODUCT_MAX_CARDS != 0 {
7571        return Err(format!(
7572            "Step BF16 input width {in_features} is not divisible by the TP8 product envelope"
7573        ));
7574    }
7575    let canonical_cols = in_features / PRODUCT_MAX_CARDS;
7576    let local_in = in_features / tp;
7577    if local_in % canonical_cols != 0 {
7578        return Err(format!(
7579            "Step BF16 TP={tp} input shard {local_in} is not divisible by canonical \
7580             {canonical_cols}-column chunks"
7581        ));
7582    }
7583    Ok(canonical_cols)
7584}
7585
7586fn validate_row_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
7587    if matrix.in_features % tp != 0 {
7588        return Err(format!(
7589            "row-parallel in_features {} is not divisible by TP={tp}",
7590            matrix.in_features
7591        ));
7592    }
7593    let local_in = matrix.in_features / tp;
7594    if local_in % FP8_BLOCK != 0 {
7595        return Err(format!(
7596            "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
7597             E4M3 scale block"
7598        ));
7599    }
7600    Ok(())
7601}
7602
7603fn upload_rank(
7604    engine: &Engine,
7605    matrix: E4m3BlockMatrix<'_>,
7606) -> Result<ResidentE4m3Rank, Box<dyn std::error::Error>> {
7607    let _main = engine.gpu.enter_main()?;
7608    matrix.validate()?;
7609    Ok(ResidentE4m3Rank {
7610        codes: engine.htod_bytes(matrix.codes)?,
7611        scales: engine.htod(matrix.scales)?,
7612        out_features: matrix.out_features,
7613        in_features: matrix.in_features,
7614    })
7615}
7616
7617fn upload_bf16_rank(
7618    engine: &Engine,
7619    matrix: Bf16Matrix<'_>,
7620    f32_mirror: bool,
7621) -> Result<ResidentBf16Rank, Box<dyn std::error::Error>> {
7622    let _main = engine.gpu.enter_main()?;
7623    matrix.validate()?;
7624    let bytes = engine.htod_bytes(matrix.bytes)?;
7625    let weight = if f32_mirror {
7626        let values = matrix
7627            .out_features
7628            .checked_mul(matrix.in_features)
7629            .ok_or("resident BF16 mirror element count overflow")?;
7630        ResidentBf16Weight::F32(engine.bf16_to_f32(&bytes.slice(0..bytes.len()), values)?)
7631    } else {
7632        ResidentBf16Weight::Bf16(bytes)
7633    };
7634    // MEMRA_STEP_TP_W8: encode the q8_0 decode mirror once, here, while the bf16 bytes are
7635    // already resident. Rows whose in_features is not a multiple of 32 have no q8_0 form and
7636    // simply keep the bf16 program (the decode arm checks for the mirror, never assumes it).
7637    let q8 = if crate::step_tp_w8_on() && matrix.in_features % 32 == 0 {
7638        if let ResidentBf16Weight::Bf16(bytes) = &weight {
7639            // Two steps, because the mmvq rp kernel does NOT read ggml-interleaved 34-byte
7640            // blocks: it reads a PLANAR mirror (all quants, then all half scales — the
7641            // q4_0/NVFP4 rp convention). The encoder writes the interleaved form and
7642            // `build_q8_rp4_raw` — the same kernel the GGUF loader uses — splits it into
7643            // planes. Skipping the split is what made the first W8 gate return zeros
7644            // (verify-prefill argmax=0, maxdiff=0.000e0).
7645            let row_bytes = Engine::q8_0_row_bytes(matrix.in_features);
7646            let mut interleaved = engine.alloc_u8_uninit(matrix.out_features * row_bytes)?;
7647            engine.encode_q8_0_from_bf16(
7648                bytes,
7649                &mut interleaved,
7650                matrix.in_features,
7651                matrix.out_features,
7652            )?;
7653            let mirror =
7654                engine.build_q8_rp4_raw(&interleaved, matrix.in_features, matrix.out_features)?;
7655            Some(mirror)
7656        } else {
7657            None
7658        }
7659    } else {
7660        None
7661    };
7662    Ok(ResidentBf16Rank {
7663        weight,
7664        out_features: matrix.out_features,
7665        in_features: matrix.in_features,
7666        q8,
7667    })
7668}
7669
7670fn upload_expert_bank_rank(
7671    engine: &Engine,
7672    bank: E4m3ExpertBank<'_>,
7673    expert_range: Range<usize>,
7674) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
7675    let _main = engine.gpu.enter_main()?;
7676    bank.validate()?;
7677    if expert_range.start >= expert_range.end || expert_range.end > bank.expert_count {
7678        return Err(format!(
7679            "invalid EP expert range {expert_range:?} for {} experts",
7680            bank.expert_count
7681        )
7682        .into());
7683    }
7684    let code_stride = bank.out_features * bank.in_features;
7685    let scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * bank.in_features.div_ceil(FP8_BLOCK);
7686    Ok(ResidentE4m3ExpertBankRank {
7687        codes: engine.htod_bytes(
7688            &bank.codes[expert_range.start * code_stride..expert_range.end * code_stride],
7689        )?,
7690        scales: engine.htod(
7691            &bank.scales[expert_range.start * scale_stride..expert_range.end * scale_stride],
7692        )?,
7693        expert_range,
7694        out_features: bank.out_features,
7695        in_features: bank.in_features,
7696        code_stride,
7697        scale_stride,
7698        k_blocks: None,
7699    })
7700}
7701
7702fn validate_column_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
7703    if bank.out_features % tp != 0 {
7704        return Err(format!(
7705            "TP expert output width {} is not divisible by TP={tp}",
7706            bank.out_features
7707        ));
7708    }
7709    let local_out = bank.out_features / tp;
7710    if local_out % FP8_BLOCK != 0 {
7711        return Err(format!(
7712            "TP expert output shard {local_out} cuts through a {FP8_BLOCK}-row E4M3 scale block"
7713        ));
7714    }
7715    Ok(())
7716}
7717
7718fn validate_row_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
7719    if bank.in_features % tp != 0 {
7720        return Err(format!(
7721            "TP expert input width {} is not divisible by TP={tp}",
7722            bank.in_features
7723        ));
7724    }
7725    let local_in = bank.in_features / tp;
7726    if local_in % FP8_BLOCK != 0 {
7727        return Err(format!(
7728            "TP expert input shard {local_in} cuts through a {FP8_BLOCK}-column E4M3 scale block"
7729        ));
7730    }
7731    Ok(())
7732}
7733
7734fn upload_column_bank_rank(
7735    engine: &Engine,
7736    bank: E4m3ExpertBank<'_>,
7737    tp: usize,
7738    rank: usize,
7739) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
7740    let _main = engine.gpu.enter_main()?;
7741    let packed = pack_column_bank_rank(bank, tp, rank)?;
7742    Ok(ResidentE4m3ExpertBankRank {
7743        codes: engine.htod_bytes(&packed.codes)?,
7744        scales: engine.htod(&packed.scales)?,
7745        expert_range: packed.expert_range,
7746        out_features: packed.out_features,
7747        in_features: packed.in_features,
7748        code_stride: packed.code_stride,
7749        scale_stride: packed.scale_stride,
7750        k_blocks: packed.k_blocks,
7751    })
7752}
7753
7754fn pack_column_bank_rank(
7755    bank: E4m3ExpertBank<'_>,
7756    tp: usize,
7757    rank: usize,
7758) -> Result<PackedE4m3ExpertBankRank, String> {
7759    bank.validate()?;
7760    validate_column_bank_shape(bank, tp)?;
7761    if rank >= tp {
7762        return Err(format!("TP rank {rank} outside 0..{tp}"));
7763    }
7764    let local_out = bank.out_features / tp;
7765    let full_code_stride = bank.out_features * bank.in_features;
7766    let local_code_stride = local_out * bank.in_features;
7767    let scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
7768    let full_scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * scale_cols;
7769    let local_scale_rows = local_out / FP8_BLOCK;
7770    let local_scale_stride = local_scale_rows * scale_cols;
7771    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
7772    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
7773    let row_start = rank * local_out;
7774    let scale_row_start = rank * local_scale_rows;
7775    for expert in 0..bank.expert_count {
7776        let code_start = expert * full_code_stride + row_start * bank.in_features;
7777        codes.extend_from_slice(&bank.codes[code_start..code_start + local_code_stride]);
7778        let scale_start = expert * full_scale_stride + scale_row_start * scale_cols;
7779        scales.extend_from_slice(&bank.scales[scale_start..scale_start + local_scale_stride]);
7780    }
7781    Ok(PackedE4m3ExpertBankRank {
7782        codes,
7783        scales,
7784        expert_range: 0..bank.expert_count,
7785        out_features: local_out,
7786        in_features: bank.in_features,
7787        code_stride: local_code_stride,
7788        scale_stride: local_scale_stride,
7789        k_blocks: None,
7790    })
7791}
7792
7793fn upload_row_bank_rank(
7794    engine: &Engine,
7795    bank: E4m3ExpertBank<'_>,
7796    tp: usize,
7797    rank: usize,
7798) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
7799    let _main = engine.gpu.enter_main()?;
7800    let packed = pack_row_bank_rank(bank, tp, rank)?;
7801    Ok(ResidentE4m3ExpertBankRank {
7802        codes: engine.htod_bytes(&packed.codes)?,
7803        scales: engine.htod(&packed.scales)?,
7804        expert_range: packed.expert_range,
7805        out_features: packed.out_features,
7806        in_features: packed.in_features,
7807        code_stride: packed.code_stride,
7808        scale_stride: packed.scale_stride,
7809        k_blocks: packed.k_blocks,
7810    })
7811}
7812
7813fn pack_row_bank_rank(
7814    bank: E4m3ExpertBank<'_>,
7815    tp: usize,
7816    rank: usize,
7817) -> Result<PackedE4m3ExpertBankRank, String> {
7818    bank.validate()?;
7819    validate_row_bank_shape(bank, tp)?;
7820    if rank >= tp {
7821        return Err(format!("TP rank {rank} outside 0..{tp}"));
7822    }
7823    let local_in = bank.in_features / tp;
7824    let full_code_stride = bank.out_features * bank.in_features;
7825    let local_code_stride = bank.out_features * local_in;
7826    let full_scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
7827    let local_scale_cols = local_in / FP8_BLOCK;
7828    let scale_rows = bank.out_features.div_ceil(FP8_BLOCK);
7829    let full_scale_stride = scale_rows * full_scale_cols;
7830    let local_scale_stride = scale_rows * local_scale_cols;
7831    let global_block_start = rank * local_scale_cols;
7832    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
7833    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
7834    for expert in 0..bank.expert_count {
7835        let expert_code_start = expert * full_code_stride;
7836        let expert_scale_start = expert * full_scale_stride;
7837        for local_block in 0..local_scale_cols {
7838            let global_block = global_block_start + local_block;
7839            let column_start = global_block * FP8_BLOCK;
7840            for row in 0..bank.out_features {
7841                let start = expert_code_start + row * bank.in_features + column_start;
7842                codes.extend_from_slice(&bank.codes[start..start + FP8_BLOCK]);
7843            }
7844            for row in 0..scale_rows {
7845                scales.push(bank.scales[expert_scale_start + row * full_scale_cols + global_block]);
7846            }
7847        }
7848    }
7849    Ok(PackedE4m3ExpertBankRank {
7850        codes,
7851        scales,
7852        expert_range: 0..bank.expert_count,
7853        out_features: bank.out_features,
7854        in_features: local_in,
7855        code_stride: local_code_stride,
7856        scale_stride: local_scale_stride,
7857        k_blocks: Some(local_scale_cols),
7858    })
7859}
7860
7861fn validate_resident_ranks(engines: &[Engine], ranks: &[ResidentE4m3Rank]) -> Result<(), String> {
7862    if engines.len() != ranks.len() {
7863        return Err(format!(
7864            "resident TP rank count {} != runtime rank count {}",
7865            ranks.len(),
7866            engines.len()
7867        ));
7868    }
7869    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
7870        let device = engine.ctx().ordinal();
7871        if matrix.codes.ordinal() != device || matrix.scales.ordinal() != device {
7872            return Err(format!(
7873                "resident TP rank {rank} is not owned by runtime device {device}"
7874            ));
7875        }
7876    }
7877    Ok(())
7878}
7879
7880fn validate_tp_bank_residency(
7881    engines: &[Engine],
7882    experts: &ResidentTpExpertBank,
7883) -> Result<(), String> {
7884    if engines.len() != experts.gate.len()
7885        || engines.len() != experts.up.len()
7886        || engines.len() != experts.down.len()
7887    {
7888        return Err(format!(
7889            "resident TP expert-bank rank counts gate={} up={} down={} != runtime {}",
7890            experts.gate.len(),
7891            experts.up.len(),
7892            experts.down.len(),
7893            engines.len()
7894        ));
7895    }
7896    for (rank, engine) in engines.iter().enumerate() {
7897        let device = engine.ctx().ordinal();
7898        for (projection, bank) in [
7899            ("gate", &experts.gate[rank]),
7900            ("up", &experts.up[rank]),
7901            ("down", &experts.down[rank]),
7902        ] {
7903            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
7904                return Err(format!(
7905                    "resident TP rank {rank} {projection} bank is not owned by runtime device \
7906                     {device}"
7907                ));
7908            }
7909        }
7910    }
7911    Ok(())
7912}
7913
7914fn validate_ep_residency(
7915    engines: &[Engine],
7916    experts: &ResidentExpertParallel,
7917) -> Result<(), String> {
7918    if engines.len() != experts.ranks.len() {
7919        return Err(format!(
7920            "resident EP rank count {} != runtime rank count {}",
7921            experts.ranks.len(),
7922            engines.len()
7923        ));
7924    }
7925    for (rank, (engine, resident)) in engines.iter().zip(&experts.ranks).enumerate() {
7926        let device = engine.ctx().ordinal();
7927        for (projection, bank) in [
7928            ("gate", &resident.gate),
7929            ("up", &resident.up),
7930            ("down", &resident.down),
7931        ] {
7932            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
7933                return Err(format!(
7934                    "resident EP rank {rank} {projection} bank is not owned by runtime device \
7935                     {device}"
7936                ));
7937            }
7938        }
7939    }
7940    Ok(())
7941}
7942
7943fn run_rank(
7944    engine: &Engine,
7945    matrix: E4m3BlockMatrix<'_>,
7946    activations: &[f32],
7947    tokens: usize,
7948) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7949    let _main = engine.gpu.enter_main()?;
7950    let codes = engine.htod_bytes(matrix.codes)?;
7951    let scales = engine.htod(matrix.scales)?;
7952    let activations = engine.htod(activations)?;
7953    let output = engine.qmatvec_mmq_fp8_blk(
7954        &codes,
7955        &scales,
7956        &activations,
7957        tokens,
7958        matrix.in_features,
7959        matrix.out_features,
7960    )?;
7961    engine.dtoh(&output)
7962}
7963
7964fn run_resident_rank(
7965    engine: &Engine,
7966    matrix: &ResidentE4m3Rank,
7967    activations: &[f32],
7968    tokens: usize,
7969) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7970    let _main = engine.gpu.enter_main()?;
7971    let activations = engine.htod(activations)?;
7972    let output = engine.qmatvec_mmq_fp8_blk(
7973        &matrix.codes,
7974        &matrix.scales,
7975        &activations,
7976        tokens,
7977        matrix.in_features,
7978        matrix.out_features,
7979    )?;
7980    engine.dtoh(&output)
7981}
7982
7983fn run_resident_bf16_rank(
7984    engine: &Engine,
7985    matrix: &ResidentBf16Rank,
7986    activations: &[f32],
7987    tokens: usize,
7988    canonical_chunk_rows: Option<usize>,
7989) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7990    let _main = engine.gpu.enter_main()?;
7991    let activations = engine.htod(activations)?;
7992    let output = run_resident_bf16_rank_device(
7993        engine,
7994        matrix,
7995        &activations,
7996        tokens,
7997        canonical_chunk_rows,
7998        false,
7999    )?;
8000    engine.dtoh(&output)
8001}
8002
8003fn run_resident_bf16_rank_device(
8004    engine: &Engine,
8005    matrix: &ResidentBf16Rank,
8006    activations: &CudaSlice<f32>,
8007    tokens: usize,
8008    canonical_chunk_rows: Option<usize>,
8009    strided_chunk_output: bool,
8010) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8011    let _main = engine.gpu.enter_main()?;
8012    if activations.ordinal() != engine.ctx().ordinal() {
8013        return Err(format!(
8014            "resident BF16 activation device {} != rank device {}",
8015            activations.ordinal(),
8016            engine.ctx().ordinal()
8017        )
8018        .into());
8019    }
8020    if activations.len() != tokens * matrix.in_features {
8021        return Err(format!(
8022            "resident BF16 activation count {} != {tokens}x{}",
8023            activations.len(),
8024            matrix.in_features
8025        )
8026        .into());
8027    }
8028    match (&matrix.weight, canonical_chunk_rows) {
8029        (ResidentBf16Weight::Bf16(bytes), Some(rows)) => engine
8030            .linear_bf16_resident_canonical_rows(
8031                activations,
8032                bytes,
8033                tokens,
8034                matrix.in_features,
8035                matrix.out_features,
8036                rows,
8037            ),
8038        (ResidentBf16Weight::Bf16(bytes), None) => engine.linear_bf16_resident(
8039            activations,
8040            bytes,
8041            tokens,
8042            matrix.in_features,
8043            matrix.out_features,
8044        ),
8045        (ResidentBf16Weight::F32(values), Some(rows)) if strided_chunk_output => engine
8046            .linear_f32_resident_canonical_rows_strided(
8047                activations,
8048                values,
8049                tokens,
8050                matrix.in_features,
8051                matrix.out_features,
8052                rows,
8053            ),
8054        (ResidentBf16Weight::F32(values), Some(rows)) => engine.linear_f32_resident_canonical_rows(
8055            activations,
8056            values,
8057            tokens,
8058            matrix.in_features,
8059            matrix.out_features,
8060            rows,
8061        ),
8062        (ResidentBf16Weight::F32(values), None) => engine.linear(
8063            activations,
8064            values,
8065            tokens,
8066            matrix.in_features,
8067            matrix.out_features,
8068        ),
8069    }
8070}
8071
8072fn validate_resident_bf16_ranks(
8073    engines: &[Engine],
8074    ranks: &[ResidentBf16Rank],
8075) -> Result<(), String> {
8076    if engines.len() != ranks.len() {
8077        return Err(format!(
8078            "resident BF16 TP rank count {} != runtime rank count {}",
8079            ranks.len(),
8080            engines.len(),
8081        ));
8082    }
8083    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
8084        let device = engine.ctx().ordinal();
8085        if matrix.weight.ordinal() != device {
8086            return Err(format!(
8087                "resident BF16 TP rank {rank} is not owned by runtime device {device}"
8088            ));
8089        }
8090    }
8091    Ok(())
8092}
8093
8094fn validate_step_bf16_row_residency(
8095    engines: &[Engine],
8096    matrix: &ResidentStepBf16RowParallel,
8097) -> Result<(), String> {
8098    if engines.len() != matrix.ranks.len() {
8099        return Err(format!(
8100            "resident Step BF16 row rank count {} != runtime rank count {}",
8101            matrix.ranks.len(),
8102            engines.len(),
8103        ));
8104    }
8105    let canonical_cols = step_bf16_canonical_chunk_cols(matrix.in_features, engines.len())?;
8106    if matrix.canonical_chunk_cols != canonical_cols {
8107        return Err(format!(
8108            "resident Step BF16 row canonical columns {} != registered {canonical_cols}",
8109            matrix.canonical_chunk_cols
8110        ));
8111    }
8112    let blocks_per_rank = PRODUCT_MAX_CARDS / engines.len();
8113    for (rank, (engine, blocks)) in engines.iter().zip(&matrix.ranks).enumerate() {
8114        if blocks.len() != blocks_per_rank {
8115            return Err(format!(
8116                "resident Step BF16 row rank {rank} has {} blocks, expected {blocks_per_rank}",
8117                blocks.len()
8118            ));
8119        }
8120        let device = engine.ctx().ordinal();
8121        for (block, resident) in blocks.iter().enumerate() {
8122            if resident.weight.ordinal() != device
8123                || resident.in_features != canonical_cols
8124                || resident.out_features != matrix.out_features
8125            {
8126                return Err(format!(
8127                    "resident Step BF16 row rank {rank} block {block} has inconsistent \
8128                     device or geometry"
8129                ));
8130            }
8131        }
8132    }
8133    Ok(())
8134}
8135
8136fn validate_replicated_device_rows(
8137    engines: &[Engine],
8138    rows: &ResidentReplicatedDeviceRows,
8139) -> Result<(), String> {
8140    let rank_lengths = rows
8141        .ranks
8142        .iter()
8143        .map(|rank_rows| rank_rows.len())
8144        .collect::<Vec<_>>();
8145    replicated_device_row_values(rows.tokens, rows.width, engines.len(), &rank_lengths)?;
8146    if rows
8147        .ranks
8148        .iter()
8149        .zip(engines)
8150        .any(|(rank_rows, engine)| rank_rows.ordinal() != engine.ctx().ordinal())
8151    {
8152        return Err("replicated device rows are owned by the wrong CUDA contexts".into());
8153    }
8154    Ok(())
8155}
8156
8157fn replicated_device_row_values(
8158    tokens: usize,
8159    width: usize,
8160    expected_ranks: usize,
8161    rank_lengths: &[usize],
8162) -> Result<usize, String> {
8163    let values = tokens
8164        .checked_mul(width)
8165        .ok_or("replicated device row size overflow")?;
8166    if tokens == 0
8167        || width == 0
8168        || expected_ranks == 0
8169        || rank_lengths.len() != expected_ranks
8170        || rank_lengths.iter().any(|&rank_len| rank_len != values)
8171    {
8172        return Err(format!(
8173            "replicated device rows have inconsistent geometry tokens={} width={} ranks={}/{}",
8174            tokens,
8175            width,
8176            rank_lengths.len(),
8177            expected_ranks
8178        ));
8179    }
8180    Ok(values)
8181}
8182
8183fn replicated_device_row_source_values(
8184    tokens: usize,
8185    width: usize,
8186    source_len: usize,
8187    source_device: usize,
8188    root_device: usize,
8189) -> Result<usize, String> {
8190    let values = tokens
8191        .checked_mul(width)
8192        .ok_or("replicated device row size overflow")?;
8193    if tokens == 0 || width == 0 || source_len != values || source_device != root_device {
8194        return Err(format!(
8195            "replicated device row source has inconsistent geometry/device \
8196             tokens={tokens} width={width} source={source_len}@{source_device} root={root_device}"
8197        ));
8198    }
8199    Ok(values)
8200}
8201
8202fn bf16_column_shard(
8203    matrix: Bf16Matrix<'_>,
8204    tp: usize,
8205    rank: usize,
8206) -> Result<Bf16Matrix<'_>, String> {
8207    matrix.validate()?;
8208    if tp == 0 || rank >= tp || matrix.out_features % tp != 0 {
8209        return Err(format!(
8210            "invalid BF16 column shard out={} TP={tp} rank={rank}",
8211            matrix.out_features
8212        ));
8213    }
8214    let local_out = matrix.out_features / tp;
8215    let row_bytes = matrix.in_features * 2;
8216    let start = rank * local_out * row_bytes;
8217    Ok(Bf16Matrix {
8218        bytes: &matrix.bytes[start..start + local_out * row_bytes],
8219        out_features: local_out,
8220        in_features: matrix.in_features,
8221    })
8222}
8223
8224fn bf16_row_shard(matrix: Bf16Matrix<'_>, tp: usize, rank: usize) -> Result<Vec<u8>, String> {
8225    matrix.validate()?;
8226    if tp == 0 || rank >= tp || matrix.in_features % tp != 0 {
8227        return Err(format!(
8228            "invalid BF16 row shard in={} TP={tp} rank={rank}",
8229            matrix.in_features
8230        ));
8231    }
8232    let local_in = matrix.in_features / tp;
8233    let mut bytes = Vec::with_capacity(matrix.out_features * local_in * 2);
8234    for row in 0..matrix.out_features {
8235        let start = (row * matrix.in_features + rank * local_in) * 2;
8236        bytes.extend_from_slice(&matrix.bytes[start..start + local_in * 2]);
8237    }
8238    Ok(bytes)
8239}
8240
8241fn bf16_row_block(
8242    matrix: Bf16Matrix<'_>,
8243    col_start: usize,
8244    block_cols: usize,
8245) -> Result<Vec<u8>, String> {
8246    matrix.validate()?;
8247    let col_end = col_start
8248        .checked_add(block_cols)
8249        .ok_or("BF16 row block column overflow")?;
8250    if block_cols == 0 || col_end > matrix.in_features {
8251        return Err(format!(
8252            "invalid BF16 row block columns {col_start}..{col_end} for input width {}",
8253            matrix.in_features
8254        ));
8255    }
8256    let mut bytes = Vec::with_capacity(matrix.out_features * block_cols * 2);
8257    for row in 0..matrix.out_features {
8258        let start = (row * matrix.in_features + col_start) * 2;
8259        bytes.extend_from_slice(&matrix.bytes[start..start + block_cols * 2]);
8260    }
8261    Ok(bytes)
8262}
8263
8264fn run_resident_bank_expert(
8265    engine: &Engine,
8266    bank: &ResidentE4m3ExpertBankRank,
8267    local_expert: usize,
8268    activations: &[f32],
8269    tokens: usize,
8270) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8271    let _main = engine.gpu.enter_main()?;
8272    if bank.k_blocks.is_some() {
8273        return Err("block-major TP row bank requires canonical block execution".into());
8274    }
8275    let local_count = bank.expert_range.end - bank.expert_range.start;
8276    if local_expert >= local_count {
8277        return Err(format!(
8278            "local EP expert {local_expert} outside 0..{local_count} for range {:?}",
8279            bank.expert_range
8280        )
8281        .into());
8282    }
8283    validate_activations(activations, tokens, bank.in_features)?;
8284    let activations = engine.htod(activations)?;
8285    let weight = bank
8286        .codes
8287        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
8288    let scales = bank
8289        .scales
8290        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
8291    let input = activations.slice(0..activations.len());
8292    let output = engine.qmatvec_mmq_fp8_blk_view(
8293        &weight,
8294        &scales,
8295        &input,
8296        tokens,
8297        bank.in_features,
8298        bank.out_features,
8299    )?;
8300    engine.dtoh(&output)
8301}
8302
8303fn run_resident_bank_expert_block(
8304    engine: &Engine,
8305    bank: &ResidentE4m3ExpertBankRank,
8306    local_expert: usize,
8307    block: usize,
8308    activations: &[f32],
8309) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8310    let _main = engine.gpu.enter_main()?;
8311    let local_count = bank.expert_range.end - bank.expert_range.start;
8312    if local_expert >= local_count {
8313        return Err(format!(
8314            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8315            bank.expert_range
8316        )
8317        .into());
8318    }
8319    let blocks = bank
8320        .k_blocks
8321        .ok_or("TP row bank is not packed in native K-block order")?;
8322    if block >= blocks {
8323        return Err(format!("TP row block {block} outside 0..{blocks}").into());
8324    }
8325    validate_activations(activations, 1, FP8_BLOCK)?;
8326    let block_code_stride = bank.out_features * FP8_BLOCK;
8327    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
8328    if bank.in_features != blocks * FP8_BLOCK
8329        || bank.code_stride != blocks * block_code_stride
8330        || bank.scale_stride != blocks * block_scale_stride
8331    {
8332        return Err("TP row bank block-major geometry is inconsistent".into());
8333    }
8334
8335    let expert_code_start = local_expert * bank.code_stride;
8336    let expert_scale_start = local_expert * bank.scale_stride;
8337    let weight = bank.codes.slice(
8338        expert_code_start + block * block_code_stride
8339            ..expert_code_start + (block + 1) * block_code_stride,
8340    );
8341    let scales = bank.scales.slice(
8342        expert_scale_start + block * block_scale_stride
8343            ..expert_scale_start + (block + 1) * block_scale_stride,
8344    );
8345    let activations = engine.htod(activations)?;
8346    let input = activations.slice(0..activations.len());
8347    let output = engine.qmatvec_mmq_fp8_blk_view(
8348        &weight,
8349        &scales,
8350        &input,
8351        1,
8352        FP8_BLOCK,
8353        bank.out_features,
8354    )?;
8355    engine.dtoh(&output)
8356}
8357
8358fn run_resident_bank_expert_device(
8359    engine: &Engine,
8360    bank: &ResidentE4m3ExpertBankRank,
8361    local_expert: usize,
8362    activations: &CudaSlice<f32>,
8363    tokens: usize,
8364) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8365    let _main = engine.gpu.enter_main()?;
8366    if bank.k_blocks.is_some() {
8367        return Err("block-major TP row bank requires canonical block execution".into());
8368    }
8369    let local_count = bank.expert_range.end - bank.expert_range.start;
8370    if local_expert >= local_count {
8371        return Err(format!(
8372            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8373            bank.expert_range
8374        )
8375        .into());
8376    }
8377    let expected = tokens
8378        .checked_mul(bank.in_features)
8379        .ok_or("native TP activation size overflow")?;
8380    if activations.len() != expected || activations.ordinal() != engine.ctx().ordinal() {
8381        return Err(format!(
8382            "native TP activation len/device {}/{} != expected {expected}/{}",
8383            activations.len(),
8384            activations.ordinal(),
8385            engine.ctx().ordinal()
8386        )
8387        .into());
8388    }
8389    let weight = bank
8390        .codes
8391        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
8392    let scales = bank
8393        .scales
8394        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
8395    let input = activations.slice(0..activations.len());
8396    engine.qmatvec_mmq_fp8_blk_view(
8397        &weight,
8398        &scales,
8399        &input,
8400        tokens,
8401        bank.in_features,
8402        bank.out_features,
8403    )
8404}
8405
8406fn run_resident_bank_expert_block_device(
8407    engine: &Engine,
8408    bank: &ResidentE4m3ExpertBankRank,
8409    local_expert: usize,
8410    block: usize,
8411    activations: &cudarc::driver::CudaView<'_, f32>,
8412) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8413    let _main = engine.gpu.enter_main()?;
8414    let local_count = bank.expert_range.end - bank.expert_range.start;
8415    if local_expert >= local_count {
8416        return Err(format!(
8417            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8418            bank.expert_range
8419        )
8420        .into());
8421    }
8422    let blocks = bank
8423        .k_blocks
8424        .ok_or("native TP row bank is not packed in checkpoint-block order")?;
8425    if block >= blocks {
8426        return Err(format!("native TP row block {block} outside 0..{blocks}").into());
8427    }
8428    let activation_device = activations.stream().context().ordinal();
8429    if activations.len() != FP8_BLOCK || activation_device != engine.ctx().ordinal() {
8430        return Err(format!(
8431            "native TP block activation len/device {}/{} != expected {FP8_BLOCK}/{}",
8432            activations.len(),
8433            activation_device,
8434            engine.ctx().ordinal()
8435        )
8436        .into());
8437    }
8438    let block_code_stride = bank.out_features * FP8_BLOCK;
8439    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
8440    if bank.in_features != blocks * FP8_BLOCK
8441        || bank.code_stride != blocks * block_code_stride
8442        || bank.scale_stride != blocks * block_scale_stride
8443    {
8444        return Err("native TP row bank block-major geometry is inconsistent".into());
8445    }
8446    let expert_code_start = local_expert * bank.code_stride;
8447    let expert_scale_start = local_expert * bank.scale_stride;
8448    let weight = bank.codes.slice(
8449        expert_code_start + block * block_code_stride
8450            ..expert_code_start + (block + 1) * block_code_stride,
8451    );
8452    let scales = bank.scales.slice(
8453        expert_scale_start + block * block_scale_stride
8454            ..expert_scale_start + (block + 1) * block_scale_stride,
8455    );
8456    engine.qmatvec_mmq_fp8_blk_view(
8457        &weight,
8458        &scales,
8459        activations,
8460        1,
8461        FP8_BLOCK,
8462        bank.out_features,
8463    )
8464}
8465
8466fn configure_native_p2p(
8467    ranks: &[Engine],
8468    devices: &[usize],
8469) -> Result<(), Box<dyn std::error::Error>> {
8470    if ranks.len() != devices.len() || ranks.len() < 2 {
8471        return Err("native TP P2P setup requires matching multi-rank devices".into());
8472    }
8473    for (rank, (&device, engine)) in devices.iter().zip(ranks).enumerate() {
8474        if engine.ctx().ordinal() != device {
8475            return Err(format!(
8476                "native TP rank {rank} context device {} != requested device {device}",
8477                engine.ctx().ordinal()
8478            )
8479            .into());
8480        }
8481    }
8482
8483    for src in 0..ranks.len() {
8484        for dst in 0..ranks.len() {
8485            if src == dst {
8486                continue;
8487            }
8488            let mut can_access = 0;
8489            unsafe {
8490                cudarc::driver::sys::cuDeviceCanAccessPeer(
8491                    &mut can_access,
8492                    ranks[src].ctx().cu_device(),
8493                    ranks[dst].ctx().cu_device(),
8494                )
8495                .result()?;
8496            }
8497            if can_access == 0 {
8498                return Err(format!(
8499                    "native TP requires P2P, but dev{} cannot access dev{}",
8500                    devices[src], devices[dst]
8501                )
8502                .into());
8503            }
8504            ranks[src].ctx().bind_to_thread()?;
8505            let rc =
8506                unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(ranks[dst].ctx().cu_ctx(), 0) };
8507            use cudarc::driver::sys::cudaError_enum as E;
8508            if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
8509                return Err(format!(
8510                    "native TP cuCtxEnablePeerAccess(dev{} -> dev{}) failed: {rc:?}",
8511                    devices[src], devices[dst]
8512                )
8513                .into());
8514            }
8515        }
8516    }
8517
8518    for &owner in devices {
8519        for &accessor in devices {
8520            if owner == accessor {
8521                continue;
8522            }
8523            let device = cudarc::driver::result::device::get(owner as i32)?;
8524            let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
8525            unsafe {
8526                cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, device).result()?;
8527            }
8528            let desc = cudarc::driver::sys::CUmemAccessDesc {
8529                location: cudarc::driver::sys::CUmemLocation {
8530                    type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
8531                    id: accessor as i32,
8532                },
8533                flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
8534            };
8535            let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
8536            if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
8537                return Err(format!(
8538                    "native TP cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: \
8539                     {rc:?}"
8540                )
8541                .into());
8542            }
8543        }
8544    }
8545
8546    for src in 0..ranks.len() {
8547        for dst in 0..ranks.len() {
8548            if src == dst {
8549                continue;
8550            }
8551            for &words in NATIVE_P2P_PROBE_WORDS {
8552                let expected = (0..words)
8553                    .map(|index| {
8554                        (index as u32)
8555                            .wrapping_mul(0x9e37_79b9)
8556                            .wrapping_add(((src as u32) << 16) | dst as u32)
8557                    })
8558                    .collect::<Vec<_>>();
8559                let poison = expected.iter().map(|value| !value).collect::<Vec<_>>();
8560                let source = ranks[src].htod_u32_v(&expected)?;
8561                let mut destination = ranks[dst].htod_u32_v(&poison)?;
8562                ranks[dst].stream().memcpy_dtod(&source, &mut destination)?;
8563                let actual = ranks[dst].dtoh_u32(&destination)?;
8564                if actual != expected {
8565                    let mismatches = actual
8566                        .iter()
8567                        .zip(&expected)
8568                        .filter(|(actual, expected)| actual != expected)
8569                        .count();
8570                    return Err(format!(
8571                        "native TP peer probe dev{}->dev{} failed at {} bytes: \
8572                         {mismatches}/{} words differ",
8573                        devices[src],
8574                        devices[dst],
8575                        words * std::mem::size_of::<u32>(),
8576                        expected.len()
8577                    )
8578                    .into());
8579                }
8580            }
8581        }
8582    }
8583    ranks[0].ctx().bind_to_thread()?;
8584    eprintln!(
8585        "[tp] native peer byte-integrity probe PASS: devices={devices:?} \
8586         directions={} byte_ladder={:?} mismatches=0",
8587        ranks.len() * (ranks.len() - 1),
8588        NATIVE_P2P_PROBE_WORDS
8589            .iter()
8590            .map(|words| words * std::mem::size_of::<u32>())
8591            .collect::<Vec<_>>(),
8592    );
8593    Ok(())
8594}
8595
8596fn validate_activations(
8597    activations: &[f32],
8598    tokens: usize,
8599    in_features: usize,
8600) -> Result<(), String> {
8601    let expected = tokens
8602        .checked_mul(in_features)
8603        .ok_or_else(|| "activation size overflow".to_string())?;
8604    if activations.len() != expected {
8605        return Err(format!(
8606            "activation count {} != {tokens}x{in_features} ({expected})",
8607            activations.len()
8608        ));
8609    }
8610    if !activations.iter().all(|value| value.is_finite()) {
8611        return Err("activations contain a non-finite value".to_string());
8612    }
8613    Ok(())
8614}
8615
8616fn column_shard(
8617    matrix: E4m3BlockMatrix<'_>,
8618    tp: usize,
8619    rank: usize,
8620) -> Result<E4m3BlockMatrix<'_>, String> {
8621    let local_out = matrix.out_features / tp;
8622    let row_start = rank * local_out;
8623    let code_start = row_start * matrix.in_features;
8624    let code_end = code_start + local_out * matrix.in_features;
8625    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
8626    let local_scale_rows = local_out / FP8_BLOCK;
8627    let scale_start = rank * local_scale_rows * scale_cols;
8628    let scale_end = scale_start + local_scale_rows * scale_cols;
8629    Ok(E4m3BlockMatrix {
8630        codes: &matrix.codes[code_start..code_end],
8631        scales: &matrix.scales[scale_start..scale_end],
8632        out_features: local_out,
8633        in_features: matrix.in_features,
8634    })
8635}
8636
8637fn row_shard(
8638    matrix: E4m3BlockMatrix<'_>,
8639    tp: usize,
8640    rank: usize,
8641) -> Result<(Vec<u8>, Vec<f32>), String> {
8642    let local_in = matrix.in_features / tp;
8643    let col_start = rank * local_in;
8644    let mut codes = Vec::with_capacity(matrix.out_features * local_in);
8645    for row in 0..matrix.out_features {
8646        let start = row * matrix.in_features + col_start;
8647        codes.extend_from_slice(&matrix.codes[start..start + local_in]);
8648    }
8649
8650    let scale_rows = matrix.out_features.div_ceil(FP8_BLOCK);
8651    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
8652    let local_scale_cols = local_in / FP8_BLOCK;
8653    let scale_col_start = rank * local_scale_cols;
8654    let mut scales = Vec::with_capacity(scale_rows * local_scale_cols);
8655    for row in 0..scale_rows {
8656        let start = row * scale_cols + scale_col_start;
8657        scales.extend_from_slice(&matrix.scales[start..start + local_scale_cols]);
8658    }
8659    Ok((codes, scales))
8660}
8661
8662fn activation_shard(
8663    activations: &[f32],
8664    tokens: usize,
8665    in_features: usize,
8666    tp: usize,
8667    rank: usize,
8668) -> Vec<f32> {
8669    let local_in = in_features / tp;
8670    let col_start = rank * local_in;
8671    let mut shard = Vec::with_capacity(tokens * local_in);
8672    for token in 0..tokens {
8673        let start = token * in_features + col_start;
8674        shard.extend_from_slice(&activations[start..start + local_in]);
8675    }
8676    shard
8677}
8678
8679// ─── Step NVFP4 expert TP program (official Step-3.7-Flash-NVFP4 checkpoint class) ─────────────
8680//
8681// The routed experts of the NVFP4 checkpoint are modelopt-packed: e2m1 codes (2/byte), per-16
8682// UE4M3 sub-scales, and a per-EXPERT `weight_scale_2` f32 macro (~1e-5..1e-4, LOAD-BEARING).
8683// Rank compute repacks each shard host-side into memra block_nvfp4 rows (nibble reorder only —
8684// value-exact, see nvfp4_repack.rs) and runs the proven `qmatvec_nvfp4_fast` dp4a kernel; the
8685// activation q8_1 quantization uses per-32 blocks, and every shard cut here is 64-aligned, so a
8686// rank-local partial is bit-identical to the corresponding slice of the unsharded kernel.
8687//
8688// MACRO CANONICAL ORDER: the macro multiplies each assembled f32 output exactly ONCE — after the
8689// column gather (gate/up) and after the FULL row-parallel reduce (down), never per-partial.
8690// `(a + b) * m` and `a * m + b * m` differ in f32, so applying it per-rank would break the
8691// TP1-vs-TP2 bit gate. Every entry point below follows this order.
8692//
8693// TP2 shard legality is NVFP4-native: column parallelism splits whole output rows (scale rows
8694// ride along, nothing cuts), row parallelism splits input columns at 64-element superblock
8695// boundaries (16-element scale groups nest inside). The 128-block E4M3 constraint does not apply.
8696
8697/// One expert's modelopt NVFP4 projection: packed codes + per-16 UE4M3 scale bytes + macro.
8698#[derive(Clone, Copy)]
8699pub struct Nvfp4BlockMatrix<'a> {
8700    pub codes: &'a [u8],  // [out_features, in_features/2] packed e2m1, row-major
8701    pub scales: &'a [u8], // [out_features, in_features/16] UE4M3 bytes, row-major
8702    pub macro_scale: f32, // per-expert weight_scale_2 dequant multiplier
8703    pub out_features: usize,
8704    pub in_features: usize,
8705}
8706
8707impl Nvfp4BlockMatrix<'_> {
8708    pub fn validate(&self) -> Result<(), String> {
8709        if self.in_features == 0 || self.out_features == 0 {
8710            return Err("NVFP4 matrix has a zero dimension".to_string());
8711        }
8712        if self.in_features % 64 != 0 {
8713            return Err(format!(
8714                "NVFP4 in_features {} is not 64-aligned (memra block_nvfp4 superblock)",
8715                self.in_features
8716            ));
8717        }
8718        if self.codes.len() != self.out_features * self.in_features / 2 {
8719            return Err(format!(
8720                "NVFP4 code bytes {} != {}x{}/2",
8721                self.codes.len(),
8722                self.out_features,
8723                self.in_features
8724            ));
8725        }
8726        if self.scales.len() != self.out_features * self.in_features / 16 {
8727            return Err(format!(
8728                "NVFP4 scale bytes {} != {}x{}/16",
8729                self.scales.len(),
8730                self.out_features,
8731                self.in_features
8732            ));
8733        }
8734        if !self.macro_scale.is_finite() || self.macro_scale <= 0.0 {
8735            return Err(format!(
8736                "NVFP4 macro scale {} is not finite-positive",
8737                self.macro_scale
8738            ));
8739        }
8740        Ok(())
8741    }
8742}
8743
8744/// Stacked modelopt NVFP4 expert bank (host view over the checkpoint bytes).
8745#[derive(Clone, Copy)]
8746pub struct Nvfp4ExpertBank<'a> {
8747    pub codes: &'a [u8],   // [expert_count, out_features, in_features/2]
8748    pub scales: &'a [u8],  // [expert_count, out_features, in_features/16]
8749    pub macros: &'a [f32], // [expert_count] weight_scale_2
8750    pub expert_count: usize,
8751    pub out_features: usize,
8752    pub in_features: usize,
8753}
8754
8755impl Nvfp4ExpertBank<'_> {
8756    pub fn validate(&self) -> Result<(), String> {
8757        if self.expert_count == 0 {
8758            return Err("NVFP4 expert bank is empty".to_string());
8759        }
8760        if self.macros.len() != self.expert_count {
8761            return Err(format!(
8762                "NVFP4 bank macros {} != expert count {}",
8763                self.macros.len(),
8764                self.expert_count
8765            ));
8766        }
8767        self.expert(0).map(|_| ())
8768    }
8769
8770    pub fn expert(&self, expert: usize) -> Result<Nvfp4BlockMatrix<'_>, String> {
8771        if expert >= self.expert_count {
8772            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
8773        }
8774        let code_stride = self.out_features * self.in_features / 2;
8775        let scale_stride = self.out_features * self.in_features / 16;
8776        if self.codes.len() != self.expert_count * code_stride
8777            || self.scales.len() != self.expert_count * scale_stride
8778        {
8779            return Err("NVFP4 bank byte extents do not match the declared geometry".to_string());
8780        }
8781        let matrix = Nvfp4BlockMatrix {
8782            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
8783            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
8784            macro_scale: self.macros[expert],
8785            out_features: self.out_features,
8786            in_features: self.in_features,
8787        };
8788        matrix.validate()?;
8789        Ok(matrix)
8790    }
8791}
8792
8793/// One rank's resident repacked NVFP4 shard: memra block_nvfp4 rows on device.
8794pub struct ResidentNvfp4Rank {
8795    blocks: crate::CudaSlice<u8>,
8796    macro_scale: f32,
8797    out_features: usize,
8798    in_features: usize,
8799    row_bytes: usize,
8800}
8801
8802pub struct ResidentNvfp4ColumnParallel {
8803    ranks: Vec<ResidentNvfp4Rank>,
8804    pub out_features: usize,
8805    pub in_features: usize,
8806}
8807
8808pub struct ResidentNvfp4RowParallel {
8809    ranks: Vec<ResidentNvfp4Rank>,
8810    pub out_features: usize,
8811    pub in_features: usize,
8812}
8813
8814pub struct ResidentTpNvfp4Expert {
8815    gate: ResidentNvfp4ColumnParallel,
8816    up: ResidentNvfp4ColumnParallel,
8817    down: ResidentNvfp4RowParallel,
8818    pub input_width: usize,
8819    pub expert_width: usize,
8820}
8821
8822/// One rank's resident NVFP4 expert bank shard: one repacked block buffer PER expert (per-expert
8823/// device allocations keep this increment off any new strided-kernel API; the strided twin is a
8824/// later perf rung, mirroring the FP8 bank's history).
8825pub struct ResidentNvfp4ColumnBankRank {
8826    /// Contiguous per-rank expert bank: `expert_count` repacked shards of `expert_bytes` each.
8827    /// Contiguity is what lets the device-routes program cover every selected expert with ONE
8828    /// launch (`qmatvec_nvfp4_dp4a_sel` indexes `sel[t] * expert_bytes`).
8829    bank: crate::CudaSlice<u8>,
8830    expert_bytes: usize,
8831    local_out: usize,
8832    in_features: usize,
8833    row_bytes: usize,
8834}
8835
8836impl ResidentNvfp4ColumnBankRank {
8837    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
8838        self.bank
8839            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
8840    }
8841}
8842
8843/// Canonical row-shard count for the NVFP4 down projection. The down reduction ALWAYS executes
8844/// as exactly this many input-column windows summed in shard order, at every world size: a
8845/// single full-width dot and a two-half-dots-plus-add differ in f32 parenthesization, so pinning
8846/// the shard grid (not the world size) is what makes the TP1-oracle-vs-TP2 bit gate meaningful.
8847/// This is the NVFP4 twin of the FP8 bank's canonical checkpoint-block reduction.
8848pub const NVFP4_CANONICAL_ROW_SHARDS: usize = 2;
8849
8850pub struct ResidentNvfp4RowBankRank {
8851    /// Contiguous per-shard expert bank (see `ResidentNvfp4ColumnBankRank::bank`).
8852    bank: crate::CudaSlice<u8>,
8853    expert_bytes: usize,
8854    device_rank: usize, // index into the runtime's rank engines this canonical shard lives on
8855    out_features: usize,
8856    local_in: usize,
8857    row_bytes: usize,
8858}
8859
8860impl ResidentNvfp4RowBankRank {
8861    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
8862        self.bank
8863            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
8864    }
8865}
8866
8867impl ResidentNvfp4TensorParallel {
8868    pub(crate) fn device_workspace_handle(
8869        &self,
8870    ) -> &std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>> {
8871        &self.device_workspace
8872    }
8873}
8874
8875pub struct ResidentNvfp4TensorParallel {
8876    gate: Vec<ResidentNvfp4ColumnBankRank>,
8877    up: Vec<ResidentNvfp4ColumnBankRank>,
8878    down: Vec<ResidentNvfp4RowBankRank>,
8879    macros_gate: Vec<f32>,
8880    macros_up: Vec<f32>,
8881    macros_down: Vec<f32>,
8882    /// Per-rank device copies of the gate/up macro-scales (E f32 each), indexed by the
8883    /// batched SwiGLU kernel via the selection array. Down macros stay host-side — they fold
8884    /// into the route-weight axpy scalar.
8885    macros_gate_dev: Vec<crate::CudaSlice<f32>>,
8886    macros_up_dev: Vec<crate::CudaSlice<f32>>,
8887    macros_down_dev: Vec<crate::CudaSlice<f32>>,
8888    pub expert_count: usize,
8889    pub input_width: usize,
8890    pub expert_width: usize,
8891    /// Lazily-built persistent decode workspace (device routes program). Interior mutability
8892    /// mirrors StepEpGroupedDecode: the forward holds the bank behind a shared reference.
8893    device_workspace: std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>>,
8894    /// Lazily-built spec-verify t=2 workspace (MEMRA_TCOL_FFN): the two-column routed
8895    /// sweep's slabs and events, kept apart from the serving workspace so the verify walk
8896    /// never perturbs serving state.
8897    t2_workspace: std::sync::Mutex<Option<Nvfp4T2Workspace>>,
8898    /// MEMRA_STEP_NVFP4_EP2: the rank banks above hold WHOLE experts (owner = id & 1,
8899    /// slot = id >> 1) at full width instead of TP shards. Consumers must branch on this;
8900    /// shard-semantics paths refuse loudly.
8901    pub(crate) ep2: bool,
8902}
8903
8904/// Persistent buffers for the two-column (spec verify) NVFP4 device-routed program: every
8905/// slab is the t=1 workspace shape doubled along the pair axis, plus per-column
8906/// accumulators. One per expert bank, reused every (round, layer) call.
8907pub struct Nvfp4T2Workspace {
8908    input2: Vec<crate::CudaSlice<f32>>,
8909    in_q2: Vec<crate::CudaSlice<i8>>,
8910    in_d2: Vec<crate::CudaSlice<f32>>,
8911    sel2: Vec<crate::CudaSlice<i32>>,
8912    route_w2: Vec<crate::CudaSlice<f32>>,
8913    gate_out2: Vec<crate::CudaSlice<f32>>,
8914    up_out2: Vec<crate::CudaSlice<f32>>,
8915    act_q2: Vec<crate::CudaSlice<i8>>,
8916    act_d2: Vec<crate::CudaSlice<f32>>,
8917    partial2: Vec<crate::CudaSlice<f32>>,
8918    /// Per-rank per-column combine accumulators ([width] each).
8919    acc_a: Vec<crate::CudaSlice<f32>>,
8920    acc_b: Vec<crate::CudaSlice<f32>>,
8921    /// down8_t2 arm: per-rank [2, width] combined slab, root peer pull and joined slab —
8922    /// the fused kernel writes both columns, so the join is ONE pull + ONE add.
8923    acc2: Vec<crate::CudaSlice<f32>>,
8924    peer2: crate::CudaSlice<f32>,
8925    omix2: crate::CudaSlice<f32>,
8926    /// Root-side pulls of rank1's accumulators and the joined columns.
8927    peer_a: crate::CudaSlice<f32>,
8928    peer_b: crate::CudaSlice<f32>,
8929    omix_a: crate::CudaSlice<f32>,
8930    omix_b: crate::CudaSlice<f32>,
8931    ev_entry: CudaEvent,
8932    ev_rank: Vec<CudaEvent>,
8933    ev_root: CudaEvent,
8934    t_cap: usize,
8935    n_sel: usize,
8936    e_device: usize,
8937}
8938
8939fn nvfp4_trow_workspace_needs_grow(
8940    current: Option<(usize, usize)>,
8941    t: usize,
8942    n_sel: usize,
8943) -> bool {
8944    current.is_none_or(|(t_cap, n_sel_cap)| t_cap < t || n_sel_cap < n_sel)
8945}
8946
8947#[cfg(test)]
8948mod nvfp4_trow_workspace_tests {
8949    use super::nvfp4_trow_workspace_needs_grow;
8950
8951    #[test]
8952    fn workspace_grows_but_never_shrinks_between_spec_and_batch() {
8953        assert!(nvfp4_trow_workspace_needs_grow(None, 2, 16));
8954        assert!(nvfp4_trow_workspace_needs_grow(Some((2, 16)), 8, 64));
8955        assert!(!nvfp4_trow_workspace_needs_grow(Some((8, 64)), 2, 16));
8956        assert!(!nvfp4_trow_workspace_needs_grow(Some((32, 256)), 8, 64));
8957    }
8958}
8959
8960/// Persistent per-call device buffers for the NVFP4 device routes program: one gate/up output,
8961/// one down partial, and one shard accumulator per rank, plus root combine staging. Reused every
8962/// (token, layer) call so the decode loop performs zero output allocations.
8963/// A stitched multi-device parent graph for one layer's device-routed expert program, plus
8964/// the children it was built from (retained: AddChildGraphNode clones, but the probe retains
8965/// conservatively) and the persistent e-context input staging its copies read.
8966struct RoutesGraph {
8967    exec: cudarc::driver::sys::CUgraphExec,
8968    parent: cudarc::driver::sys::CUgraph,
8969    _children: Vec<cudarc::driver::CudaGraph>,
8970}
8971// SAFETY: the raw handles are only used from the single decode thread; CUDA graph handles are
8972// context-agnostic process handles.
8973unsafe impl Send for RoutesGraph {}
8974
8975impl Drop for RoutesGraph {
8976    fn drop(&mut self) {
8977        unsafe {
8978            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
8979            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
8980        }
8981    }
8982}
8983
8984impl Nvfp4DeviceRoutesWorkspace {
8985    pub(crate) fn in_stage_handle(&self) -> Option<&crate::CudaSlice<f32>> {
8986        self.in_stage_e.as_ref()
8987    }
8988    pub(crate) fn in_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
8989        self.in_stage_e.as_mut()
8990    }
8991    pub(crate) fn out_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
8992        self.out_stage_e.as_mut()
8993    }
8994    /// Arm the e-context stages + router staging pair when absent (token-graph entry).
8995    pub(crate) fn arm_stages(
8996        &mut self,
8997        e: &Engine,
8998        width: usize,
8999        n_sel: usize,
9000    ) -> Result<(), Box<dyn std::error::Error>> {
9001        let _main = e.gpu.enter_main()?;
9002        if self.in_stage_e.is_none() {
9003            self.in_stage_e = Some(e.htod(&vec![0.0f32; width])?);
9004            self.out_stage_e = Some(e.htod(&vec![0.0f32; width])?);
9005        }
9006        if self.dev_route_e.is_none() {
9007            self.dev_route_e = Some((
9008                e.htod_i32(&vec![0i32; n_sel])?,
9009                e.htod(&vec![0.0f32; n_sel])?,
9010            ));
9011        }
9012        Ok(())
9013    }
9014
9015    /// Split-borrow: the routes input (shared) + output (mut) stages together.
9016    pub(crate) fn in_and_out_stages_mut(
9017        &mut self,
9018    ) -> Option<(&crate::CudaSlice<f32>, &mut crate::CudaSlice<f32>)> {
9019        match (self.in_stage_e.as_ref(), self.out_stage_e.as_mut()) {
9020            (Some(input), Some(output)) => Some((input, output)),
9021            _ => None,
9022        }
9023    }
9024    pub(crate) fn dev_route_e_mut(
9025        &mut self,
9026    ) -> Option<(&mut crate::CudaSlice<i32>, &mut crate::CudaSlice<f32>)> {
9027        self.dev_route_e.as_mut().map(|(a, b)| (a, b))
9028    }
9029}
9030
9031pub struct Nvfp4DeviceRoutesWorkspace {
9032    /// [n_sel, local_out] batched gate/up outputs and the SwiGLU q8_1 pair; [n_sel, width]
9033    /// down partials. Sized for `n_sel` selected experts per token (pinned at first call).
9034    gate_out: Vec<crate::CudaSlice<f32>>,
9035    up_out: Vec<crate::CudaSlice<f32>>,
9036    act_q: Vec<crate::CudaSlice<i8>>,
9037    act_d: Vec<crate::CudaSlice<f32>>,
9038    sel: Vec<crate::CudaSlice<i32>>,
9039    partial: Vec<crate::CudaSlice<f32>>,
9040    accumulator: Vec<crate::CudaSlice<f32>>,
9041    /// Per-rank folded combine weights (route_weight x down macro), one htod per call.
9042    combine_w: Vec<crate::CudaSlice<f32>>,
9043    /// Device-routed extension: per-rank raw route weights (the down-macro fold happens
9044    /// in-kernel via sel + macros_down_dev).
9045    route_w: Vec<crate::CudaSlice<f32>>,
9046    /// Persistent q8_1 pair of the shared layer input (one quantize per rank per call, no
9047    /// per-call allocation).
9048    in_q: Vec<crate::CudaSlice<i8>>,
9049    in_d: Vec<crate::CudaSlice<f32>>,
9050    /// e-context staging for the device router outputs (persistent — rank streams peer-read
9051    /// them, so the router's fresh outputs are copied here on e's stream first; the pp.rs
9052    /// never-free discipline).
9053    dev_route_e: Option<(crate::CudaSlice<i32>, crate::CudaSlice<f32>)>,
9054    /// Prestage door state: input pull + quantize already issued for this layer's call
9055    /// (nvfp4_routes_prestage), so the routed run skips them. Reset per call.
9056    prestaged: bool,
9057    /// Peer-router door state: rank1's sel/route_w were computed locally in prestage;
9058    /// the routed run skips rank1's sel pull. Reset per call.
9059    rank1_routed: bool,
9060    /// Doorbell fences (MEMRA_FENCE_MEMOPS): raw cuMemAlloc'd [rank1_flag, root_flag]
9061    /// u32 pair in ROOT memory (async-pool memory is memop-INELIGIBLE — receipted
9062    /// CUDA_ERROR_INVALID_VALUE) + the host-side monotonic ticket. 0 = unarmed.
9063    fence_flags_raw: u64,
9064    fence_ticket: u32,
9065    /// Prestage input fence, recorded on e after the input's producer.
9066    ev_input: Option<(CudaEvent, usize)>,
9067    /// Graph-door staging: persistent e-context input row + output row (fixed addresses the
9068    /// captured copies read/write), and the per-layer stitched parent.
9069    in_stage_e: Option<crate::CudaSlice<f32>>,
9070    out_stage_e: Option<crate::CudaSlice<f32>>,
9071    routes_graph: Option<RoutesGraph>,
9072    /// Token-graph raw pointer sets (armed once by routes_arm_raw).
9073    raw_dev_route_e: Option<(u64, u64)>,
9074    raw_combine: Option<(u64, u64, u64, u64)>,
9075    raw_input: Vec<u64>,
9076    raw_sel: Vec<u64>,
9077    raw_route_w: Vec<u64>,
9078    remote: crate::CudaSlice<f32>,
9079    combined: crate::CudaSlice<f32>,
9080    n_sel: usize,
9081    /// Device-IO extension (lazily built by `run_tensor_parallel_routes_nvfp4_device_io`):
9082    /// persistent per-rank input rows plus the evented ordering pair — the pp.rs
9083    /// BoundarySlot discipline, same as the v2 attention workspace.
9084    input: Vec<crate::CudaSlice<f32>>,
9085    ev_rank: Vec<CudaEvent>,
9086    ev_done: Option<CudaEvent>,
9087    ev_entry: Option<(CudaEvent, usize)>,
9088}
9089
9090/// One rank's whole-expert NVFP4 residency (expert-parallel ownership).
9091struct ResidentNvfp4EpRank {
9092    gate: Vec<crate::CudaSlice<u8>>,
9093    up: Vec<crate::CudaSlice<u8>>,
9094    down: Vec<crate::CudaSlice<u8>>,
9095    #[allow(dead_code)]
9096    expert_range: Range<usize>,
9097}
9098
9099pub struct ResidentNvfp4ExpertParallel {
9100    ranks: Vec<ResidentNvfp4EpRank>,
9101    macros_gate: Vec<f32>,
9102    macros_up: Vec<f32>,
9103    macros_down: Vec<f32>,
9104    pub expert_count: usize,
9105    pub input_width: usize,
9106    pub expert_width: usize,
9107    gate_row_bytes: usize,
9108    down_row_bytes: usize,
9109}
9110
9111fn nvfp4_repack_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
9112    memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
9113        matrix.codes,
9114        matrix.scales,
9115        matrix.out_features,
9116        matrix.in_features,
9117    )
9118}
9119
9120fn nvfp4_row_bytes(in_features: usize) -> usize {
9121    in_features / 64 * 36 // memra block_nvfp4: 64 elems -> 36 bytes (4 UE4M3 + 32 packed e2m1)
9122}
9123
9124/// MEMRA_NVFP4_BANK_V2=1: store the contiguous expert banks in the slot-major layout the
9125/// coalesced `*_v2` kernels read (see qmatvec.cu). Pure byte permutation — value-exact.
9126/// MEMRA_NO_LOCAL_SHADOW=1: skip the per-layer local-KV shadow gathers and appends in the
9127/// eager v2 decode (lengths still advance) — the graph door proved contents-stale local KV
9128/// is decode-identical (12/12). The local contents feed spec/MTP scratch only.
9129/// MEMRA_FUSE_ROPE_APPEND=1: fuse qk norms + rope + dcw KV append + len inc into one
9130/// launch per rank per layer (bit-identical; identity-gated). dcw path only.
9131pub(crate) fn fuse_rope_append_on() -> bool {
9132    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9133    *ON.get_or_init(|| std::env::var("MEMRA_FUSE_ROPE_APPEND").as_deref() == Ok("1"))
9134}
9135
9136pub(crate) fn no_local_shadow_on() -> bool {
9137    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9138    *ON.get_or_init(|| std::env::var("MEMRA_NO_LOCAL_SHADOW").as_deref() == Ok("1"))
9139}
9140
9141pub(crate) fn nvfp4_bank_v2_on() -> bool {
9142    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9143    *ON.get_or_init(|| std::env::var("MEMRA_NVFP4_BANK_V2").as_deref() == Ok("1"))
9144}
9145
9146/// Permute one repacked block_nvfp4 matrix (out_features rows of `nvfp4_row_bytes(in_f)`)
9147/// into the slot-major v2 row layout: per row, slot g's 16 qs bytes at g*16, then the two
9148/// UE4M3 scale bytes per slot at nslots*16 + g*2. Row byte count unchanged.
9149fn nvfp4_matrix_v2_permute(v1: &[u8], out_features: usize, in_features: usize) -> Vec<u8> {
9150    let row_bytes = nvfp4_row_bytes(in_features);
9151    assert_eq!(v1.len(), out_features * row_bytes, "v2 permute geometry");
9152    let n_slots = in_features / 32;
9153    let mut out = Vec::with_capacity(v1.len());
9154    for row in 0..out_features {
9155        let r = &v1[row * row_bytes..(row + 1) * row_bytes];
9156        for g in 0..n_slots {
9157            let (sblk, h) = (g / 2, g % 2);
9158            let b = &r[sblk * 36..sblk * 36 + 36];
9159            out.extend_from_slice(&b[4 + 16 * h..4 + 16 * h + 16]);
9160        }
9161        for g in 0..n_slots {
9162            let (sblk, h) = (g / 2, g % 2);
9163            let b = &r[sblk * 36..sblk * 36 + 36];
9164            out.push(b[2 * h]);
9165            out.push(b[2 * h + 1]);
9166        }
9167    }
9168    out
9169}
9170
9171/// Repack + (optionally) v2-permute one expert shard for the contiguous banks.
9172fn nvfp4_repack_bank_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
9173    let (out_features, in_features) = (matrix.out_features, matrix.in_features);
9174    let v1 = nvfp4_repack_matrix(matrix);
9175    if nvfp4_bank_v2_on() {
9176        nvfp4_matrix_v2_permute(&v1, out_features, in_features)
9177    } else {
9178        v1
9179    }
9180}
9181
9182/// Column shard: whole output rows per rank (codes and scales are row-major, so both slices are
9183/// contiguous borrows). The macro rides unchanged — it is applied post-gather by the caller.
9184fn nvfp4_column_shard<'a>(
9185    matrix: Nvfp4BlockMatrix<'a>,
9186    tp: usize,
9187    rank: usize,
9188) -> Result<Nvfp4BlockMatrix<'a>, String> {
9189    if matrix.out_features % tp != 0 {
9190        return Err(format!(
9191            "NVFP4 column-parallel out_features {} is not divisible by TP={tp}",
9192            matrix.out_features
9193        ));
9194    }
9195    let local_out = matrix.out_features / tp;
9196    let code_row = matrix.in_features / 2;
9197    let scale_row = matrix.in_features / 16;
9198    Ok(Nvfp4BlockMatrix {
9199        codes: &matrix.codes[rank * local_out * code_row..(rank + 1) * local_out * code_row],
9200        scales: &matrix.scales[rank * local_out * scale_row..(rank + 1) * local_out * scale_row],
9201        macro_scale: matrix.macro_scale,
9202        out_features: local_out,
9203        in_features: matrix.in_features,
9204    })
9205}
9206
9207/// Row shard: input-column windows per rank, 64-superblock aligned. Owned buffers: each output
9208/// row contributes one contiguous byte window, gathered across rows.
9209fn nvfp4_row_shard(
9210    matrix: Nvfp4BlockMatrix<'_>,
9211    tp: usize,
9212    rank: usize,
9213) -> Result<(Vec<u8>, Vec<u8>, usize), String> {
9214    if matrix.in_features % tp != 0 {
9215        return Err(format!(
9216            "NVFP4 row-parallel in_features {} is not divisible by TP={tp}",
9217            matrix.in_features
9218        ));
9219    }
9220    let local_in = matrix.in_features / tp;
9221    if local_in % 64 != 0 {
9222        return Err(format!(
9223            "NVFP4 row-parallel input shard {local_in} cuts through a 64-element superblock"
9224        ));
9225    }
9226    let code_row = matrix.in_features / 2;
9227    let scale_row = matrix.in_features / 16;
9228    let local_code = local_in / 2;
9229    let local_scale = local_in / 16;
9230    let mut codes = Vec::with_capacity(matrix.out_features * local_code);
9231    let mut scales = Vec::with_capacity(matrix.out_features * local_scale);
9232    for row in 0..matrix.out_features {
9233        let code_start = row * code_row + rank * local_code;
9234        codes.extend_from_slice(&matrix.codes[code_start..code_start + local_code]);
9235        let scale_start = row * scale_row + rank * local_scale;
9236        scales.extend_from_slice(&matrix.scales[scale_start..scale_start + local_scale]);
9237    }
9238    Ok((codes, scales, local_in))
9239}
9240
9241/// Rank compute leaf: repack modelopt -> block_nvfp4, upload, run the proven dp4a kernel. The
9242/// macro is NOT applied here — callers apply it once at the canonical post-gather/post-reduce
9243/// point (see the section header).
9244fn run_rank_nvfp4(
9245    engine: &Engine,
9246    matrix: Nvfp4BlockMatrix<'_>,
9247    activations: &[f32],
9248    tokens: usize,
9249) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9250    matrix.validate()?;
9251    validate_activations(activations, tokens, matrix.in_features)?;
9252    let _main = engine.gpu.enter_main()?;
9253    let blocks = engine.htod_bytes(&nvfp4_repack_matrix(matrix))?;
9254    let activations = engine.htod(activations)?;
9255    let output = engine.qmatvec_nvfp4_fast(
9256        &blocks.slice(0..blocks.len()),
9257        &activations,
9258        tokens,
9259        matrix.in_features,
9260        matrix.out_features,
9261        nvfp4_row_bytes(matrix.in_features),
9262    )?;
9263    engine.dtoh(&output)
9264}
9265
9266fn upload_rank_nvfp4(
9267    engine: &Engine,
9268    matrix: Nvfp4BlockMatrix<'_>,
9269) -> Result<ResidentNvfp4Rank, Box<dyn std::error::Error>> {
9270    matrix.validate()?;
9271    let _main = engine.gpu.enter_main()?;
9272    Ok(ResidentNvfp4Rank {
9273        blocks: engine.htod_bytes(&nvfp4_repack_matrix(matrix))?,
9274        macro_scale: matrix.macro_scale,
9275        out_features: matrix.out_features,
9276        in_features: matrix.in_features,
9277        row_bytes: nvfp4_row_bytes(matrix.in_features),
9278    })
9279}
9280
9281fn run_resident_rank_nvfp4(
9282    engine: &Engine,
9283    rank: &ResidentNvfp4Rank,
9284    activations: &[f32],
9285    tokens: usize,
9286) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9287    validate_activations(activations, tokens, rank.in_features)?;
9288    let _main = engine.gpu.enter_main()?;
9289    let activations = engine.htod(activations)?;
9290    let output = engine.qmatvec_nvfp4_fast(
9291        &rank.blocks.slice(0..rank.blocks.len()),
9292        &activations,
9293        tokens,
9294        rank.in_features,
9295        rank.out_features,
9296        rank.row_bytes,
9297    )?;
9298    engine.dtoh(&output)
9299}
9300
9301fn apply_macro(values: &mut [f32], macro_scale: f32) {
9302    for value in values.iter_mut() {
9303        *value *= macro_scale;
9304    }
9305}
9306
9307impl TpE4m3HostBounce {
9308    /// Unsharded NVFP4 projection on rank 0 (compatibility oracle). Macro applied post-kernel.
9309    pub fn full_nvfp4(
9310        &self,
9311        matrix: Nvfp4BlockMatrix<'_>,
9312        activations: &[f32],
9313        tokens: usize,
9314    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9315        let mut output = run_rank_nvfp4(&self.ranks[0], matrix, activations, tokens)?;
9316        apply_macro(&mut output, matrix.macro_scale);
9317        Ok(output)
9318    }
9319
9320    /// Column-parallel NVFP4 projection: output rows partition across ranks, host gather in rank
9321    /// order, macro applied ONCE post-gather.
9322    pub fn column_parallel_nvfp4(
9323        &self,
9324        matrix: Nvfp4BlockMatrix<'_>,
9325        activations: &[f32],
9326        tokens: usize,
9327    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
9328        matrix.validate()?;
9329        validate_activations(activations, tokens, matrix.in_features)?;
9330        let tp = self.ranks.len();
9331        let local_out = matrix.out_features / tp;
9332        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
9333        let mut rank_outputs = Vec::with_capacity(tp);
9334        for (rank_index, rank) in self.ranks.iter().enumerate() {
9335            let shard = nvfp4_column_shard(matrix, tp, rank_index)?;
9336            let output = run_rank_nvfp4(rank, shard, activations, tokens)?;
9337            let row_start = rank_index * local_out;
9338            for token in 0..tokens {
9339                gathered[token * matrix.out_features + row_start
9340                    ..token * matrix.out_features + row_start + local_out]
9341                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
9342            }
9343            rank_outputs.push(output);
9344        }
9345        apply_macro(&mut gathered, matrix.macro_scale);
9346        Ok(ColumnParallelResult {
9347            gathered,
9348            rank_outputs,
9349        })
9350    }
9351
9352    /// Row-parallel NVFP4 projection: input columns partition at 64-superblock boundaries,
9353    /// rank-local partials reduce in stable rank order, macro applied ONCE post-reduce.
9354    pub fn row_parallel_nvfp4(
9355        &self,
9356        matrix: Nvfp4BlockMatrix<'_>,
9357        activations: &[f32],
9358        tokens: usize,
9359    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
9360        matrix.validate()?;
9361        validate_activations(activations, tokens, matrix.in_features)?;
9362        let tp = self.ranks.len();
9363        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
9364        let mut rank_partials = Vec::with_capacity(tp);
9365        for (rank_index, rank) in self.ranks.iter().enumerate() {
9366            let (codes, scales, local_in) = nvfp4_row_shard(matrix, tp, rank_index)?;
9367            let local_activations =
9368                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
9369            let shard = Nvfp4BlockMatrix {
9370                codes: &codes,
9371                scales: &scales,
9372                macro_scale: matrix.macro_scale,
9373                out_features: matrix.out_features,
9374                in_features: local_in,
9375            };
9376            let partial = run_rank_nvfp4(rank, shard, &local_activations, tokens)?;
9377            for (sum, value) in reduced.iter_mut().zip(&partial) {
9378                *sum += *value;
9379            }
9380            rank_partials.push(partial);
9381        }
9382        apply_macro(&mut reduced, matrix.macro_scale);
9383        Ok(RowParallelResult {
9384            reduced,
9385            rank_partials,
9386        })
9387    }
9388
9389    pub fn upload_expert_nvfp4(
9390        &self,
9391        gate: Nvfp4BlockMatrix<'_>,
9392        up: Nvfp4BlockMatrix<'_>,
9393        down: Nvfp4BlockMatrix<'_>,
9394    ) -> Result<ResidentTpNvfp4Expert, Box<dyn std::error::Error>> {
9395        if gate.in_features != up.in_features || gate.out_features != up.out_features {
9396            return Err("NVFP4 TP expert gate/up dimensions differ".into());
9397        }
9398        if down.in_features != gate.out_features || down.out_features != gate.in_features {
9399            return Err(format!(
9400                "NVFP4 TP expert down {}x{} does not invert gate/up {}x{}",
9401                down.out_features, down.in_features, gate.out_features, gate.in_features
9402            )
9403            .into());
9404        }
9405        let tp = self.ranks.len();
9406        let mut gate_ranks = Vec::with_capacity(tp);
9407        let mut up_ranks = Vec::with_capacity(tp);
9408        let mut down_ranks = Vec::with_capacity(tp);
9409        for (rank_index, engine) in self.ranks.iter().enumerate() {
9410            gate_ranks.push(upload_rank_nvfp4(
9411                engine,
9412                nvfp4_column_shard(gate, tp, rank_index)?,
9413            )?);
9414            up_ranks.push(upload_rank_nvfp4(
9415                engine,
9416                nvfp4_column_shard(up, tp, rank_index)?,
9417            )?);
9418            let (codes, scales, local_in) = nvfp4_row_shard(down, tp, rank_index)?;
9419            down_ranks.push(upload_rank_nvfp4(
9420                engine,
9421                Nvfp4BlockMatrix {
9422                    codes: &codes,
9423                    scales: &scales,
9424                    macro_scale: down.macro_scale,
9425                    out_features: down.out_features,
9426                    in_features: local_in,
9427                },
9428            )?);
9429        }
9430        Ok(ResidentTpNvfp4Expert {
9431            gate: ResidentNvfp4ColumnParallel {
9432                ranks: gate_ranks,
9433                out_features: gate.out_features,
9434                in_features: gate.in_features,
9435            },
9436            up: ResidentNvfp4ColumnParallel {
9437                ranks: up_ranks,
9438                out_features: up.out_features,
9439                in_features: up.in_features,
9440            },
9441            down: ResidentNvfp4RowParallel {
9442                ranks: down_ranks,
9443                out_features: down.out_features,
9444                in_features: down.in_features,
9445            },
9446            input_width: gate.in_features,
9447            expert_width: gate.out_features,
9448        })
9449    }
9450
9451    fn column_parallel_resident_nvfp4(
9452        &self,
9453        matrix: &ResidentNvfp4ColumnParallel,
9454        activations: &[f32],
9455        tokens: usize,
9456    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9457        validate_activations(activations, tokens, matrix.in_features)?;
9458        let local_out = matrix.out_features / self.ranks.len();
9459        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
9460        let mut macro_scale = None;
9461        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
9462            let output = run_resident_rank_nvfp4(engine, shard, activations, tokens)?;
9463            let row_start = rank_index * local_out;
9464            for token in 0..tokens {
9465                gathered[token * matrix.out_features + row_start
9466                    ..token * matrix.out_features + row_start + local_out]
9467                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
9468            }
9469            macro_scale = Some(shard.macro_scale);
9470        }
9471        apply_macro(
9472            &mut gathered,
9473            macro_scale.ok_or("NVFP4 column-parallel matrix has no ranks")?,
9474        );
9475        Ok(gathered)
9476    }
9477
9478    fn row_parallel_resident_nvfp4(
9479        &self,
9480        matrix: &ResidentNvfp4RowParallel,
9481        activations: &[f32],
9482        tokens: usize,
9483    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9484        validate_activations(activations, tokens, matrix.in_features)?;
9485        let tp = self.ranks.len();
9486        let local_in = matrix.in_features / tp;
9487        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
9488        let mut macro_scale = None;
9489        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
9490            if shard.in_features != local_in {
9491                return Err(format!(
9492                    "NVFP4 resident row shard in_features {} != expected {local_in}",
9493                    shard.in_features
9494                )
9495                .into());
9496            }
9497            let local_activations =
9498                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
9499            let partial = run_resident_rank_nvfp4(engine, shard, &local_activations, tokens)?;
9500            for (sum, value) in reduced.iter_mut().zip(&partial) {
9501                *sum += *value;
9502            }
9503            macro_scale = Some(shard.macro_scale);
9504        }
9505        apply_macro(
9506            &mut reduced,
9507            macro_scale.ok_or("NVFP4 row-parallel matrix has no ranks")?,
9508        );
9509        Ok(reduced)
9510    }
9511
9512    pub fn run_expert_nvfp4(
9513        &self,
9514        expert: &ResidentTpNvfp4Expert,
9515        input: &[f32],
9516        tokens: usize,
9517    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9518        validate_activations(input, tokens, expert.input_width)?;
9519        let gate = self.column_parallel_resident_nvfp4(&expert.gate, input, tokens)?;
9520        let up = self.column_parallel_resident_nvfp4(&expert.up, input, tokens)?;
9521        let activated: Vec<f32> = gate
9522            .iter()
9523            .zip(&up)
9524            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
9525            .collect();
9526        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
9527        self.row_parallel_resident_nvfp4(&expert.down, &activated, tokens)
9528    }
9529
9530    /// Upload every expert's TP shards resident (one repacked block buffer per expert per rank).
9531    pub fn upload_tensor_parallel_nvfp4(
9532        &self,
9533        gate: Nvfp4ExpertBank<'_>,
9534        up: Nvfp4ExpertBank<'_>,
9535        down: Nvfp4ExpertBank<'_>,
9536    ) -> Result<ResidentNvfp4TensorParallel, Box<dyn std::error::Error>> {
9537        gate.validate()?;
9538        up.validate()?;
9539        down.validate()?;
9540        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
9541            return Err("NVFP4 TP gate/up/down expert counts differ".into());
9542        }
9543        if gate.in_features != up.in_features || gate.out_features != up.out_features {
9544            return Err("NVFP4 TP gate/up dimensions differ".into());
9545        }
9546        if down.in_features != gate.out_features || down.out_features != gate.in_features {
9547            return Err(format!(
9548                "NVFP4 TP down {}x{} does not invert gate/up {}x{}",
9549                down.out_features, down.in_features, gate.out_features, gate.in_features
9550            )
9551            .into());
9552        }
9553        let tp = self.ranks.len();
9554        if gate.out_features % tp != 0 {
9555            return Err(format!(
9556                "NVFP4 TP expert output width {} is not divisible by TP={tp}",
9557                gate.out_features
9558            )
9559            .into());
9560        }
9561        if down.in_features % NVFP4_CANONICAL_ROW_SHARDS != 0
9562            || (down.in_features / NVFP4_CANONICAL_ROW_SHARDS) % 64 != 0
9563        {
9564            return Err(format!(
9565                "NVFP4 TP expert input width {} does not split into 64-aligned canonical \
9566                 shards ({NVFP4_CANONICAL_ROW_SHARDS})",
9567                down.in_features
9568            )
9569            .into());
9570        }
9571        if tp > NVFP4_CANONICAL_ROW_SHARDS {
9572            return Err(format!(
9573                "NVFP4 TP world {tp} exceeds the canonical row-shard grid \
9574                 ({NVFP4_CANONICAL_ROW_SHARDS})"
9575            )
9576            .into());
9577        }
9578
9579        let ep2 = step_nvfp4_ep2_on() && tp == 2;
9580        let mut gate_ranks = Vec::with_capacity(tp);
9581        let mut up_ranks = Vec::with_capacity(tp);
9582        let mut macros_gate_dev = Vec::with_capacity(tp);
9583        let mut macros_up_dev = Vec::with_capacity(tp);
9584        let mut macros_down_dev = Vec::with_capacity(tp);
9585        for (rank_index, engine) in self.ranks.iter().enumerate() {
9586            let _main = engine.gpu.enter_main()?;
9587            // Contiguous per-rank banks: repack every expert shard into one host buffer, one
9588            // upload. Contiguity feeds the batched selected-experts launch; per-expert bytes
9589            // are unchanged (same repack).
9590            // EP2: this rank holds the FULL matrices of the experts it owns (id & 1 ==
9591            // rank_index), stacked at slot id >> 1 — same total bytes as the shard bank.
9592            let mut gate_host: Vec<u8> = Vec::new();
9593            let mut up_host: Vec<u8> = Vec::new();
9594            let mut owned = 0usize;
9595            for expert in 0..gate.expert_count {
9596                if ep2 {
9597                    if expert % 2 != rank_index {
9598                        continue;
9599                    }
9600                    owned += 1;
9601                    gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate.expert(expert)?));
9602                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up.expert(expert)?));
9603                } else {
9604                    let gate_shard = nvfp4_column_shard(gate.expert(expert)?, tp, rank_index)?;
9605                    gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate_shard));
9606                    let up_shard = nvfp4_column_shard(up.expert(expert)?, tp, rank_index)?;
9607                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up_shard));
9608                }
9609            }
9610            let bank_experts = if ep2 { owned } else { gate.expert_count };
9611            let gate_expert_bytes = gate_host.len() / bank_experts.max(1);
9612            let up_expert_bytes = up_host.len() / bank_experts.max(1);
9613            let local_out = if ep2 {
9614                gate.out_features
9615            } else {
9616                gate.out_features / tp
9617            };
9618            gate_ranks.push(ResidentNvfp4ColumnBankRank {
9619                bank: engine.htod_bytes(&gate_host)?,
9620                expert_bytes: gate_expert_bytes,
9621                local_out,
9622                in_features: gate.in_features,
9623                row_bytes: nvfp4_row_bytes(gate.in_features),
9624            });
9625            up_ranks.push(ResidentNvfp4ColumnBankRank {
9626                bank: engine.htod_bytes(&up_host)?,
9627                expert_bytes: up_expert_bytes,
9628                local_out,
9629                in_features: up.in_features,
9630                row_bytes: nvfp4_row_bytes(up.in_features),
9631            });
9632            macros_gate_dev.push(engine.htod(gate.macros)?);
9633            macros_up_dev.push(engine.htod(up.macros)?);
9634            macros_down_dev.push(engine.htod(down.macros)?);
9635        }
9636        // Down: canonical shard grid, NOT the world size (see NVFP4_CANONICAL_ROW_SHARDS).
9637        // Shard s lives on rank s % world, so TP1 holds both shards and TP2 one each, while the
9638        // execution and reduction order stay identical.
9639        let mut down_ranks = Vec::with_capacity(NVFP4_CANONICAL_ROW_SHARDS);
9640        for shard_index in 0..NVFP4_CANONICAL_ROW_SHARDS {
9641            let device_rank = shard_index % tp;
9642            let engine = &self.ranks[device_rank];
9643            let _main = engine.gpu.enter_main()?;
9644            let mut down_host: Vec<u8> = Vec::new();
9645            let mut owned = 0usize;
9646            for expert in 0..down.expert_count {
9647                let down_matrix = down.expert(expert)?;
9648                if ep2 {
9649                    // EP2: shard_index doubles as the owner rank; full-width down matrices
9650                    // of the owned experts, stacked at slot id >> 1.
9651                    if expert % 2 != device_rank {
9652                        continue;
9653                    }
9654                    owned += 1;
9655                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(down_matrix));
9656                } else {
9657                    let (codes, scales, local_in) =
9658                        nvfp4_row_shard(down_matrix, NVFP4_CANONICAL_ROW_SHARDS, shard_index)?;
9659                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(Nvfp4BlockMatrix {
9660                        codes: &codes,
9661                        scales: &scales,
9662                        macro_scale: down_matrix.macro_scale,
9663                        out_features: down_matrix.out_features,
9664                        in_features: local_in,
9665                    }));
9666                }
9667            }
9668            let bank_experts = if ep2 { owned } else { down.expert_count };
9669            let down_expert_bytes = down_host.len() / bank_experts.max(1);
9670            let local_in = if ep2 {
9671                down.in_features
9672            } else {
9673                down.in_features / NVFP4_CANONICAL_ROW_SHARDS
9674            };
9675            down_ranks.push(ResidentNvfp4RowBankRank {
9676                bank: engine.htod_bytes(&down_host)?,
9677                expert_bytes: down_expert_bytes,
9678                device_rank,
9679                out_features: down.out_features,
9680                local_in,
9681                row_bytes: nvfp4_row_bytes(local_in),
9682            });
9683        }
9684        Ok(ResidentNvfp4TensorParallel {
9685            gate: gate_ranks,
9686            up: up_ranks,
9687            down: down_ranks,
9688            macros_gate: gate.macros.to_vec(),
9689            macros_up: up.macros.to_vec(),
9690            macros_down: down.macros.to_vec(),
9691            macros_gate_dev,
9692            macros_up_dev,
9693            macros_down_dev,
9694            expert_count: gate.expert_count,
9695            input_width: gate.in_features,
9696            expert_width: gate.out_features,
9697            device_workspace: std::sync::Mutex::new(None),
9698            t2_workspace: std::sync::Mutex::new(None),
9699            ep2,
9700        })
9701    }
9702
9703    /// EP2 host-canonical: the whole expert executes on its owning rank at full width
9704    /// (owner = expert & 1, bank slot = expert >> 1). Per-row program == the column-bank
9705    /// path's kernel, so gate/up are bit-equal to the TP layout.
9706    fn run_full_bank_expert_nvfp4(
9707        &self,
9708        ranks: &[ResidentNvfp4ColumnBankRank],
9709        macros: &[f32],
9710        expert: usize,
9711        input: &[f32],
9712    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9713        let owner = expert & 1;
9714        let slot = expert >> 1;
9715        let bank = ranks
9716            .get(owner)
9717            .ok_or("NVFP4 EP2 column bank missing owner rank")?;
9718        let engine = &self.ranks[owner];
9719        let _main = engine.gpu.enter_main()?;
9720        let activations = engine.htod(input)?;
9721        let output = if nvfp4_bank_v2_on() {
9722            engine.qmatvec_nvfp4_fast_v2(
9723                &bank.expert(slot),
9724                &activations,
9725                1,
9726                bank.in_features,
9727                bank.local_out,
9728                bank.row_bytes,
9729            )?
9730        } else {
9731            engine.qmatvec_nvfp4_fast(
9732                &bank.expert(slot),
9733                &activations,
9734                1,
9735                bank.in_features,
9736                bank.local_out,
9737                bank.row_bytes,
9738            )?
9739        };
9740        let mut out = engine.dtoh(&output)?;
9741        apply_macro(&mut out, macros[expert]);
9742        Ok(out)
9743    }
9744
9745    /// EP2 host-canonical down: one full-width dot on the owner (NUMERIC-CLASS vs the
9746    /// canonical 2-shard sum — the parenthesization this door declares).
9747    fn run_full_down_expert_nvfp4(
9748        &self,
9749        shards: &[ResidentNvfp4RowBankRank],
9750        macros: &[f32],
9751        expert: usize,
9752        input: &[f32],
9753    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9754        let owner = expert & 1;
9755        let slot = expert >> 1;
9756        let shard = shards
9757            .get(owner)
9758            .ok_or("NVFP4 EP2 down bank missing owner rank")?;
9759        let engine = &self.ranks[owner];
9760        let _main = engine.gpu.enter_main()?;
9761        let activations = engine.htod(input)?;
9762        let output = if nvfp4_bank_v2_on() {
9763            engine.qmatvec_nvfp4_fast_v2(
9764                &shard.expert(slot),
9765                &activations,
9766                1,
9767                shard.local_in,
9768                shard.out_features,
9769                shard.row_bytes,
9770            )?
9771        } else {
9772            engine.qmatvec_nvfp4_fast(
9773                &shard.expert(slot),
9774                &activations,
9775                1,
9776                shard.local_in,
9777                shard.out_features,
9778                shard.row_bytes,
9779            )?
9780        };
9781        let mut out = engine.dtoh(&output)?;
9782        apply_macro(&mut out, macros[expert]);
9783        Ok(out)
9784    }
9785
9786    fn run_column_bank_expert_nvfp4(
9787        &self,
9788        ranks: &[ResidentNvfp4ColumnBankRank],
9789        macros: &[f32],
9790        expert: usize,
9791        input: &[f32],
9792    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9793        let local_out = ranks
9794            .first()
9795            .ok_or("NVFP4 TP column bank has no ranks")?
9796            .local_out;
9797        let mut gathered = vec![0.0f32; local_out * ranks.len()];
9798        for (rank_index, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
9799            let _main = engine.gpu.enter_main()?;
9800            let activations = engine.htod(input)?;
9801            let output = if nvfp4_bank_v2_on() {
9802                engine.qmatvec_nvfp4_fast_v2(
9803                    &bank.expert(expert),
9804                    &activations,
9805                    1,
9806                    bank.in_features,
9807                    bank.local_out,
9808                    bank.row_bytes,
9809                )?
9810            } else {
9811                engine.qmatvec_nvfp4_fast(
9812                    &bank.expert(expert),
9813                    &activations,
9814                    1,
9815                    bank.in_features,
9816                    bank.local_out,
9817                    bank.row_bytes,
9818                )?
9819            };
9820            let output = engine.dtoh(&output)?;
9821            gathered[rank_index * local_out..(rank_index + 1) * local_out].copy_from_slice(&output);
9822        }
9823        apply_macro(&mut gathered, macros[expert]);
9824        Ok(gathered)
9825    }
9826
9827    /// Canonical-shard row reduction: iterate the FIXED shard grid in shard order (each shard
9828    /// executes on its owning rank engine), so the reduction parenthesization is identical at
9829    /// every world size — that identity is what the TP1-oracle-vs-TP2 bit gate proves.
9830    fn run_row_bank_expert_nvfp4(
9831        &self,
9832        shards: &[ResidentNvfp4RowBankRank],
9833        macros: &[f32],
9834        expert: usize,
9835        input: &[f32],
9836    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9837        let out_features = shards
9838            .first()
9839            .ok_or("NVFP4 TP row bank has no canonical shards")?
9840            .out_features;
9841        let in_features = shards.iter().map(|shard| shard.local_in).sum::<usize>();
9842        let mut reduced = vec![0.0f32; out_features];
9843        for (shard_index, shard) in shards.iter().enumerate() {
9844            let engine = self
9845                .ranks
9846                .get(shard.device_rank)
9847                .ok_or("NVFP4 canonical shard names a rank outside this runtime")?;
9848            let _main = engine.gpu.enter_main()?;
9849            let local_activations =
9850                activation_shard(input, 1, in_features, shards.len(), shard_index);
9851            let activations = engine.htod(&local_activations)?;
9852            let output = if nvfp4_bank_v2_on() {
9853                engine.qmatvec_nvfp4_fast_v2(
9854                    &shard.expert(expert),
9855                    &activations,
9856                    1,
9857                    shard.local_in,
9858                    shard.out_features,
9859                    shard.row_bytes,
9860                )?
9861            } else {
9862                engine.qmatvec_nvfp4_fast(
9863                    &shard.expert(expert),
9864                    &activations,
9865                    1,
9866                    shard.local_in,
9867                    shard.out_features,
9868                    shard.row_bytes,
9869                )?
9870            };
9871            let partial = engine.dtoh(&output)?;
9872            for (sum, value) in reduced.iter_mut().zip(&partial) {
9873                *sum += *value;
9874            }
9875        }
9876        apply_macro(&mut reduced, macros[expert]);
9877        Ok(reduced)
9878    }
9879
9880    /// Upload whole experts per owning rank (NVFP4 expert-parallel: the layout the clamped tail
9881    /// layers require — clamp semantics do not distribute across a tensor shard). Each owned
9882    /// expert keeps its full gate/up/down as one repacked block buffer on its owner.
9883    pub fn upload_expert_parallel_nvfp4(
9884        &self,
9885        gate: Nvfp4ExpertBank<'_>,
9886        up: Nvfp4ExpertBank<'_>,
9887        down: Nvfp4ExpertBank<'_>,
9888    ) -> Result<ResidentNvfp4ExpertParallel, Box<dyn std::error::Error>> {
9889        gate.validate()?;
9890        up.validate()?;
9891        down.validate()?;
9892        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
9893            return Err("NVFP4 EP gate/up/down expert counts differ".into());
9894        }
9895        if gate.in_features != up.in_features || gate.out_features != up.out_features {
9896            return Err("NVFP4 EP gate/up dimensions differ".into());
9897        }
9898        if down.in_features != gate.out_features || down.out_features != gate.in_features {
9899            return Err(format!(
9900                "NVFP4 EP down {}x{} does not invert gate/up {}x{}",
9901                down.out_features, down.in_features, gate.out_features, gate.in_features
9902            )
9903            .into());
9904        }
9905        let world = self.ranks.len();
9906        if gate.expert_count % world != 0 {
9907            return Err(format!(
9908                "NVFP4 EP expert count {} is not divisible by {world} ranks",
9909                gate.expert_count
9910            )
9911            .into());
9912        }
9913        let experts_per_rank = gate.expert_count / world;
9914        let mut ranks = Vec::with_capacity(world);
9915        for (rank_index, engine) in self.ranks.iter().enumerate() {
9916            let _main = engine.gpu.enter_main()?;
9917            let expert_range = rank_index * experts_per_rank..(rank_index + 1) * experts_per_rank;
9918            let mut gate_experts = Vec::with_capacity(experts_per_rank);
9919            let mut up_experts = Vec::with_capacity(experts_per_rank);
9920            let mut down_experts = Vec::with_capacity(experts_per_rank);
9921            for expert in expert_range.clone() {
9922                gate_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(gate.expert(expert)?))?);
9923                up_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(up.expert(expert)?))?);
9924                down_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(down.expert(expert)?))?);
9925            }
9926            ranks.push(ResidentNvfp4EpRank {
9927                gate: gate_experts,
9928                up: up_experts,
9929                down: down_experts,
9930                expert_range,
9931            });
9932        }
9933        Ok(ResidentNvfp4ExpertParallel {
9934            ranks,
9935            macros_gate: gate.macros.to_vec(),
9936            macros_up: up.macros.to_vec(),
9937            macros_down: down.macros.to_vec(),
9938            expert_count: gate.expert_count,
9939            input_width: gate.in_features,
9940            expert_width: gate.out_features,
9941            gate_row_bytes: nvfp4_row_bytes(gate.in_features),
9942            down_row_bytes: nvfp4_row_bytes(down.in_features),
9943        })
9944    }
9945
9946    /// Routed NVFP4 expert-parallel program, host-canonical: every selected expert executes WHOLE
9947    /// on its owning rank (gate -> up -> clamped-or-plain SwiGLU on host -> down), each projection
9948    /// macro applied once post-kernel, route-weighted accumulate on the host in slot order. The
9949    /// activation uses `step_expert_activation_host`, so the clamped tail layers keep the official
9950    /// contract. Exactness-first; no throughput claim.
9951    #[allow(clippy::too_many_arguments)]
9952    pub fn run_routed_experts_nvfp4(
9953        &self,
9954        experts: &ResidentNvfp4ExpertParallel,
9955        input: &[f32],
9956        tokens: usize,
9957        selected: &[usize],
9958        route_weights: &[f32],
9959        experts_per_token: usize,
9960        activation_limit: Option<f32>,
9961    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9962        validate_activations(input, tokens, experts.input_width)?;
9963        let pairs = tokens
9964            .checked_mul(experts_per_token)
9965            .ok_or("NVFP4 EP route count overflow")?;
9966        if selected.len() != pairs || route_weights.len() != pairs {
9967            return Err(format!(
9968                "NVFP4 EP routes selected={} weights={} != tokens {tokens} x experts/token \
9969                 {experts_per_token} ({pairs})",
9970                selected.len(),
9971                route_weights.len(),
9972            )
9973            .into());
9974        }
9975        if !route_weights.iter().all(|weight| weight.is_finite()) {
9976            return Err("NVFP4 EP route weights contain a non-finite value".into());
9977        }
9978        let experts_per_rank = experts.expert_count / experts.ranks.len();
9979        let mut output = vec![0.0f32; tokens * experts.input_width];
9980        for token in 0..tokens {
9981            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
9982            for slot in 0..experts_per_token {
9983                let pair = token * experts_per_token + slot;
9984                let expert = selected[pair];
9985                if expert >= experts.expert_count {
9986                    return Err(format!(
9987                        "NVFP4 EP selected expert {expert} outside 0..{}",
9988                        experts.expert_count
9989                    )
9990                    .into());
9991                }
9992                let owner = expert / experts_per_rank;
9993                let local = expert - owner * experts_per_rank;
9994                let rank = &experts.ranks[owner];
9995                let engine = &self.ranks[owner];
9996                let _main = engine.gpu.enter_main()?;
9997                let device_input = engine.htod(input_row)?;
9998                let gate_out = engine.qmatvec_nvfp4_fast(
9999                    &rank.gate[local].slice(0..rank.gate[local].len()),
10000                    &device_input,
10001                    1,
10002                    experts.input_width,
10003                    experts.expert_width,
10004                    experts.gate_row_bytes,
10005                )?;
10006                let up_out = engine.qmatvec_nvfp4_fast(
10007                    &rank.up[local].slice(0..rank.up[local].len()),
10008                    &device_input,
10009                    1,
10010                    experts.input_width,
10011                    experts.expert_width,
10012                    experts.gate_row_bytes,
10013                )?;
10014                let mut gate_host = engine.dtoh(&gate_out)?;
10015                let mut up_host = engine.dtoh(&up_out)?;
10016                apply_macro(&mut gate_host, experts.macros_gate[expert]);
10017                apply_macro(&mut up_host, experts.macros_up[expert]);
10018                let activated: Vec<f32> = gate_host
10019                    .iter()
10020                    .zip(&up_host)
10021                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
10022                    .collect();
10023                let device_activated = engine.htod(&activated)?;
10024                let down_out = engine.qmatvec_nvfp4_fast(
10025                    &rank.down[local].slice(0..rank.down[local].len()),
10026                    &device_activated,
10027                    1,
10028                    experts.expert_width,
10029                    experts.input_width,
10030                    experts.down_row_bytes,
10031                )?;
10032                let mut down_host = engine.dtoh(&down_out)?;
10033                apply_macro(&mut down_host, experts.macros_down[expert]);
10034                let weight = route_weights[pair];
10035                for (sum, value) in output
10036                    [token * experts.input_width..(token + 1) * experts.input_width]
10037                    .iter_mut()
10038                    .zip(down_host)
10039                {
10040                    *sum += weight * value;
10041                }
10042            }
10043        }
10044        Ok(output)
10045    }
10046
10047    /// Device-resident routed NVFP4 expert program (decode shape, t=1 rows). The geometry gift
10048    /// this exploits: gate/up column halves land on the SAME rank that owns the matching down
10049    /// canonical shard (act[rank r] is exactly down-shard r's input-column window), so the whole
10050    /// expert interior — gate, up, macro-scaled SwiGLU, down partial, route-weighted accumulate —
10051    /// runs rank-local with ZERO cross-rank transfer. Per (token, layer): one input upload per
10052    /// rank, one fenced peer copy of the remote accumulator, one root add, one readback.
10053    ///
10054    /// Numeric class: device silu (silu_mul_scaled) with gate/up macros folded as gs/us and the
10055    /// down macro folded into the accumulate scalar (weight * macro_down — exact, both are
10056    /// per-expert constants). This matches the owning-stage MoE dev-path semantics, NOT the
10057    /// host-canonical program bit-for-bit; gate it with argmax + relative bounds against the
10058    /// host-canonical oracle, and with repeat determinism against itself.
10059    /// Clamped layers refuse (they stay on the EP program).
10060    pub fn run_tensor_parallel_routes_nvfp4_device(
10061        &self,
10062        experts: &ResidentNvfp4TensorParallel,
10063        input: &[f32],
10064        selected: &[usize],
10065        route_weights: &[f32],
10066        experts_per_token: usize,
10067        activation_limit: Option<f32>,
10068    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10069        validate_activations(input, 1, experts.input_width)?;
10070        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
10071            return Err(format!(
10072                "NVFP4 device routes selected={} weights={} != experts/token {experts_per_token}",
10073                selected.len(),
10074                route_weights.len(),
10075            )
10076            .into());
10077        }
10078        if !route_weights.iter().all(|weight| weight.is_finite()) {
10079            return Err("NVFP4 device route weights contain a non-finite value".into());
10080        }
10081        let world = self.ranks.len();
10082        if world != NVFP4_CANONICAL_ROW_SHARDS {
10083            return Err(format!(
10084                "NVFP4 device routes require world == canonical shard grid \
10085                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
10086            )
10087            .into());
10088        }
10089        let local_out = if experts.ep2 {
10090            experts.expert_width
10091        } else {
10092            experts.expert_width / world
10093        };
10094
10095        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of this program, printed every 430 calls
10096        // (~one 43-layer decode step's worth) so a bench run decomposes expert-program time vs
10097        // everything else without Nsight.
10098        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10099        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10100        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10101        let started = timing.then(std::time::Instant::now);
10102
10103        let n_sel = experts_per_token;
10104        let mut workspace_guard = experts
10105            .device_workspace
10106            .lock()
10107            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10108        if workspace_guard.is_none() {
10109            let mut gate_out = Vec::with_capacity(world);
10110            let mut up_out = Vec::with_capacity(world);
10111            let mut act_q = Vec::with_capacity(world);
10112            let mut act_d = Vec::with_capacity(world);
10113            let mut sel = Vec::with_capacity(world);
10114            let mut partial = Vec::with_capacity(world);
10115            let mut accumulator = Vec::with_capacity(world);
10116            let mut combine_w = Vec::with_capacity(world);
10117            let mut route_w = Vec::with_capacity(world);
10118            let mut in_q = Vec::with_capacity(world);
10119            let mut in_d = Vec::with_capacity(world);
10120            let mut input = Vec::with_capacity(world);
10121            let mut ev_rank = Vec::with_capacity(world);
10122            let moe_direct = moe_direct_on();
10123            for (rank, engine) in self.ranks.iter().enumerate() {
10124                let _main = engine.gpu.enter_main()?;
10125                gate_out.push(engine.uninit(n_sel * local_out)?);
10126                up_out.push(engine.uninit(n_sel * local_out)?);
10127                act_q.push(engine.uninit_i8(n_sel * local_out)?);
10128                act_d.push(engine.uninit(n_sel * local_out / 32)?);
10129                sel.push(engine.htod_i32(&vec![0i32; n_sel])?);
10130                partial.push(engine.uninit(n_sel * experts.input_width)?);
10131                // Direct join: peer accumulators live on ROOT (single P2P store pass).
10132                if moe_direct && rank != 0 {
10133                    let root = &self.ranks[0];
10134                    let _root_main = root.gpu.enter_main()?;
10135                    accumulator.push(root.zeros(experts.input_width)?);
10136                } else {
10137                    accumulator.push(engine.zeros(experts.input_width)?);
10138                }
10139                combine_w.push(engine.htod(&vec![0.0f32; n_sel])?);
10140                route_w.push(engine.htod(&vec![0.0f32; n_sel])?);
10141                in_q.push(engine.uninit_i8(experts.input_width)?);
10142                in_d.push(engine.uninit(experts.input_width / 32)?);
10143                input.push(engine.uninit(experts.input_width)?);
10144                ev_rank.push(engine.ctx().new_event(None)?);
10145            }
10146            let root = &self.ranks[0];
10147            let _main = root.gpu.enter_main()?;
10148            *workspace_guard = Some(Nvfp4DeviceRoutesWorkspace {
10149                prestaged: false,
10150                rank1_routed: false,
10151                ev_input: None,
10152                fence_flags_raw: 0,
10153                fence_ticket: 0,
10154                gate_out,
10155                up_out,
10156                act_q,
10157                act_d,
10158                sel,
10159                partial,
10160                accumulator,
10161                combine_w,
10162                route_w,
10163                in_q,
10164                in_d,
10165                dev_route_e: None,
10166                in_stage_e: None,
10167                out_stage_e: None,
10168                routes_graph: None,
10169                raw_dev_route_e: None,
10170                raw_combine: None,
10171                raw_input: Vec::new(),
10172                raw_sel: Vec::new(),
10173                raw_route_w: Vec::new(),
10174                remote: root.uninit(experts.input_width)?,
10175                combined: root.uninit(experts.input_width)?,
10176                n_sel,
10177                input,
10178                ev_rank,
10179                ev_done: Some(root.ctx().new_event(None)?),
10180                ev_entry: None,
10181            });
10182        }
10183        let workspace = workspace_guard
10184            .as_mut()
10185            .expect("NVFP4 device routes workspace initialized above");
10186        // EP2 uses this call only as the workspace-arming warmup (the prejoin path drives
10187        // decode); its host-routed sweep semantics do not apply to whole-expert banks.
10188        if experts.ep2 {
10189            return Ok(vec![0.0f32; experts.input_width]);
10190        }
10191        if workspace.n_sel != n_sel {
10192            return Err(format!(
10193                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10194                workspace.n_sel
10195            )
10196            .into());
10197        }
10198        for &expert in selected {
10199            if expert >= experts.expert_count {
10200                return Err(format!(
10201                    "NVFP4 device selected expert {expert} outside 0..{}",
10202                    experts.expert_count
10203                )
10204                .into());
10205            }
10206        }
10207        let sel_i32 = selected
10208            .iter()
10209            .map(|&expert| expert as i32)
10210            .collect::<Vec<_>>();
10211
10212        // BATCHED program (2026-08-20): per rank, ONE launch per sweep (gate, up, SwiGLU,
10213        // down) covers every selected expert via the selection array and the contiguous bank —
10214        // the per-expert launch loop was pure host latency (~100 sequential launches/layer,
10215        // 291us wall for ~35us of arithmetic). Per (expert, row) the kernels are bit-identical
10216        // to the per-expert forms, and the route-weight axpy chain keeps its exact sequential
10217        // accumulation order — the program's values are unchanged.
10218        for (rank_index, engine) in self.ranks.iter().enumerate() {
10219            let _main = engine.gpu.enter_main()?;
10220            let device_input = engine.htod(input)?;
10221            let Nvfp4DeviceRoutesWorkspace { in_q, in_d, .. } = &mut *workspace;
10222            engine.quantize_q8_1_into(
10223                &device_input,
10224                1,
10225                experts.input_width,
10226                &mut in_q[rank_index],
10227                &mut in_d[rank_index],
10228            )?;
10229            // device_input frees on this rank's stream after the quantize — same-stream order.
10230        }
10231        self.nvfp4_routes_batched_sweeps(
10232            experts,
10233            workspace,
10234            selected,
10235            route_weights,
10236            &sel_i32,
10237            local_out,
10238            n_sel,
10239            activation_limit,
10240            false,
10241        )?;
10242
10243        // Combine: fence the remote shard's producer stream, peer-copy its accumulator to root,
10244        // reduce in canonical shard order, read back once.
10245        let root = &self.ranks[0];
10246        for engine in &self.ranks[1..] {
10247            let _main = engine.gpu.enter_main()?;
10248            engine.stream().synchronize()?;
10249        }
10250        let _main = root.gpu.enter_main()?;
10251        root.stream()
10252            .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
10253        root.add(
10254            &workspace.accumulator[0],
10255            &workspace.remote,
10256            &mut workspace.combined,
10257            experts.input_width,
10258        )?;
10259        let output = root.dtoh(&workspace.combined)?;
10260        if let Some(started) = started {
10261            use std::sync::atomic::Ordering;
10262            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10263                + started.elapsed().as_nanos() as u64;
10264            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10265            if calls % 430 == 0 {
10266                eprintln!(
10267                    "[nvfp4-dev-routes-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10268                    ns as f64 / 1.0e6,
10269                    ns as f64 / calls as f64 / 1.0e3,
10270                );
10271            }
10272        }
10273        Ok(output)
10274    }
10275
10276    /// The shared batched sweeps of the device routes program: per rank, upload the selection,
10277    /// reset the accumulator, run the gate/up/SwiGLU/down batched launches, then the
10278    /// route-weight axpy chain in exact sequential per-pair order. Every op queues on the
10279    /// owning rank's stream; callers own input acquisition and the combine.
10280    #[allow(clippy::too_many_arguments)]
10281    fn nvfp4_routes_batched_sweeps(
10282        &self,
10283        experts: &ResidentNvfp4TensorParallel,
10284        workspace: &mut Nvfp4DeviceRoutesWorkspace,
10285        selected: &[usize],
10286        route_weights: &[f32],
10287        sel_i32: &[i32],
10288        local_out: usize,
10289        n_sel: usize,
10290        activation_limit: Option<f32>,
10291        device_routed: bool,
10292    ) -> Result<(), Box<dyn std::error::Error>> {
10293        for rank_index in 0..self.ranks.len() {
10294            self.nvfp4_routes_batched_sweeps_rank(
10295                experts,
10296                workspace,
10297                selected,
10298                route_weights,
10299                sel_i32,
10300                local_out,
10301                n_sel,
10302                activation_limit,
10303                device_routed,
10304                rank_index,
10305            )?;
10306        }
10307        Ok(())
10308    }
10309
10310    /// One rank's sweeps (the per-rank body of `nvfp4_routes_batched_sweeps`) — separated so
10311    /// the graph door can capture each rank's segment on its own stream.
10312    #[allow(clippy::too_many_arguments)]
10313    fn nvfp4_routes_batched_sweeps_rank(
10314        &self,
10315        experts: &ResidentNvfp4TensorParallel,
10316        workspace: &mut Nvfp4DeviceRoutesWorkspace,
10317        selected: &[usize],
10318        route_weights: &[f32],
10319        sel_i32: &[i32],
10320        local_out: usize,
10321        n_sel: usize,
10322        activation_limit: Option<f32>,
10323        device_routed: bool,
10324        rank_index: usize,
10325    ) -> Result<(), Box<dyn std::error::Error>> {
10326        {
10327            let engine = &self.ranks[rank_index];
10328            let _main = engine.gpu.enter_main()?;
10329            // EP2: whole-expert full-width sweep, owner-guarded; down+combine fused writes
10330            // this rank's slot-ordered partial straight into its accumulator (the join is
10331            // unchanged). Device-routed only — the host-routed arm and the graph door refuse
10332            // at the caller.
10333            if experts.ep2 {
10334                if !device_routed {
10335                    return Err("NVFP4 EP2 banks support the device-routed decode arm only".into());
10336                }
10337                let gate_bank = &experts.gate[rank_index];
10338                let up_bank = &experts.up[rank_index];
10339                if gate_bank.local_out != experts.expert_width
10340                    || gate_bank.expert_bytes != up_bank.expert_bytes
10341                {
10342                    return Err("NVFP4 EP2 bank geometry drifted".into());
10343                }
10344                {
10345                    let Nvfp4DeviceRoutesWorkspace {
10346                        sel,
10347                        gate_out,
10348                        up_out,
10349                        in_q,
10350                        in_d,
10351                        ..
10352                    } = &mut *workspace;
10353                    engine.qmatvec_nvfp4_sel_gu_ep_into(
10354                        &gate_bank.bank,
10355                        &up_bank.bank,
10356                        &sel[rank_index],
10357                        &in_q[rank_index],
10358                        &in_d[rank_index],
10359                        &mut gate_out[rank_index],
10360                        &mut up_out[rank_index],
10361                        n_sel,
10362                        gate_bank.in_features,
10363                        gate_bank.local_out,
10364                        gate_bank.row_bytes,
10365                        gate_bank.expert_bytes,
10366                        rank_index,
10367                    )?;
10368                }
10369                {
10370                    let Nvfp4DeviceRoutesWorkspace {
10371                        gate_out,
10372                        up_out,
10373                        sel,
10374                        act_q,
10375                        act_d,
10376                        ..
10377                    } = &mut *workspace;
10378                    engine.silu_mul_scaled_q8_1_sel_ep_into(
10379                        &gate_out[rank_index],
10380                        &up_out[rank_index],
10381                        &experts.macros_gate_dev[rank_index],
10382                        &experts.macros_up_dev[rank_index],
10383                        &sel[rank_index],
10384                        activation_limit,
10385                        &mut act_q[rank_index],
10386                        &mut act_d[rank_index],
10387                        local_out,
10388                        n_sel,
10389                        rank_index,
10390                    )?;
10391                }
10392                let shard = &experts.down[rank_index];
10393                if shard.device_rank != rank_index || shard.local_in != local_out {
10394                    return Err("NVFP4 EP2 down bank placement drifted".into());
10395                }
10396                {
10397                    let Nvfp4DeviceRoutesWorkspace {
10398                        sel,
10399                        act_q,
10400                        act_d,
10401                        route_w,
10402                        accumulator,
10403                        ..
10404                    } = &mut *workspace;
10405                    engine.qmatvec_nvfp4_sel_down8_ep_into(
10406                        &shard.bank,
10407                        &sel[rank_index],
10408                        &act_q[rank_index],
10409                        &act_d[rank_index],
10410                        &route_w[rank_index],
10411                        &experts.macros_down_dev[rank_index],
10412                        &mut accumulator[rank_index],
10413                        n_sel,
10414                        shard.local_in,
10415                        shard.out_features,
10416                        shard.row_bytes,
10417                        shard.expert_bytes,
10418                        local_out,
10419                        local_out / 32,
10420                        rank_index,
10421                    )?;
10422                }
10423                return Ok(());
10424            }
10425            if !device_routed {
10426                engine.htod_i32_into(&mut workspace.sel[rank_index], sel_i32)?;
10427                // Folded combine weights (route_weight x down macro) — one 40-byte upload
10428                // replaces the accumulator reset + n_sel sequential axpy launches below.
10429                let folded = (0..n_sel)
10430                    .map(|pair| route_weights[pair] * experts.macros_down[selected[pair]])
10431                    .collect::<Vec<_>>();
10432                let mut view = workspace.combine_w[rank_index].slice_mut(0..n_sel);
10433                engine.stream().memcpy_htod(&folded, &mut view)?;
10434            }
10435            let gate_bank = &experts.gate[rank_index];
10436            let up_bank = &experts.up[rank_index];
10437            let (aq, ad) = (&workspace.in_q[rank_index], &workspace.in_d[rank_index]);
10438            // FUSION #2a (v2 banks): the two sweeps share sel/aq/ad and identical geometry
10439            // — one launch, per-row bit-identical, double the grid fill.
10440            let gu_fused = nvfp4_bank_v2_on()
10441                && gate_bank.in_features == up_bank.in_features
10442                && gate_bank.local_out == up_bank.local_out
10443                && gate_bank.row_bytes == up_bank.row_bytes
10444                && gate_bank.expert_bytes == up_bank.expert_bytes;
10445            if gu_fused {
10446                let Nvfp4DeviceRoutesWorkspace {
10447                    sel,
10448                    gate_out,
10449                    up_out,
10450                    in_q,
10451                    in_d,
10452                    ..
10453                } = &mut *workspace;
10454                engine.qmatvec_nvfp4_sel_gu_into(
10455                    &gate_bank.bank,
10456                    &up_bank.bank,
10457                    &sel[rank_index],
10458                    &in_q[rank_index],
10459                    &in_d[rank_index],
10460                    &mut gate_out[rank_index],
10461                    &mut up_out[rank_index],
10462                    n_sel,
10463                    gate_bank.in_features,
10464                    gate_bank.local_out,
10465                    gate_bank.row_bytes,
10466                    gate_bank.expert_bytes,
10467                )?;
10468            } else {
10469                engine.qmatvec_nvfp4_sel_into(
10470                    &gate_bank.bank,
10471                    &workspace.sel[rank_index],
10472                    aq,
10473                    ad,
10474                    &mut workspace.gate_out[rank_index],
10475                    n_sel,
10476                    gate_bank.in_features,
10477                    gate_bank.local_out,
10478                    gate_bank.row_bytes,
10479                    gate_bank.expert_bytes,
10480                    0,
10481                    0,
10482                )?;
10483                engine.qmatvec_nvfp4_sel_into(
10484                    &up_bank.bank,
10485                    &workspace.sel[rank_index],
10486                    aq,
10487                    ad,
10488                    &mut workspace.up_out[rank_index],
10489                    n_sel,
10490                    up_bank.in_features,
10491                    up_bank.local_out,
10492                    up_bank.row_bytes,
10493                    up_bank.expert_bytes,
10494                    0,
10495                    0,
10496                )?;
10497            }
10498            // Fused macro-scaled SwiGLU that EMITS q8_1 directly — down consumes it with no
10499            // separate quantize launch. act[rank] IS down canonical shard `rank_index`'s
10500            // input-column window (the geometry gift; see the method doc).
10501            {
10502                let Nvfp4DeviceRoutesWorkspace {
10503                    gate_out,
10504                    up_out,
10505                    sel,
10506                    act_q,
10507                    act_d,
10508                    ..
10509                } = &mut *workspace;
10510                engine.silu_mul_scaled_q8_1_sel_into(
10511                    &gate_out[rank_index],
10512                    &up_out[rank_index],
10513                    &experts.macros_gate_dev[rank_index],
10514                    &experts.macros_up_dev[rank_index],
10515                    &sel[rank_index],
10516                    activation_limit,
10517                    &mut act_q[rank_index],
10518                    &mut act_d[rank_index],
10519                    local_out,
10520                    n_sel,
10521                )?;
10522            }
10523            let shard = &experts.down[rank_index];
10524            if shard.device_rank != rank_index || shard.local_in != local_out {
10525                return Err(
10526                    "NVFP4 device routes: down canonical shard placement drifted from \
10527                     the gate/up column split"
10528                        .into(),
10529                );
10530            }
10531            // MEMRA_SEL_DOWN8=1: down sweep + route-weight combine in ONE launch, one warp
10532            // per SLOT instead of one warp per (row, slot) — the q8 `down8 w8` occupancy arm
10533            // (cx-downkernel: waves/SM 0.91 -> 4.36) ported to the NVFP4 banks. Bit-identical
10534            // (same dot program, same reduce tree, same slot-ordered chain), and the
10535            // n_sel x out_f partial buffer round trip disappears. Device-routed only: the
10536            // host-routed arm folds the macro into combine_w instead of reading md on device.
10537            let down8 = device_routed && sel_down8_on() && (shard.local_in >> 5) <= 32;
10538            {
10539                // MEMRA_SWEEP_TRACE=1: one receipt PER DISTINCT decision combo — a
10540                // silently-dead fusion reads as roofline physics without it (and the
10541                // prime's host-routed call must not swallow the decode receipt).
10542                static SEEN: std::sync::Mutex<Vec<(bool, bool)>> =
10543                    std::sync::Mutex::new(Vec::new());
10544                if std::env::var("MEMRA_SWEEP_TRACE").as_deref() == Ok("1") {
10545                    let mut seen = SEEN.lock().unwrap();
10546                    if !seen.contains(&(down8, device_routed)) {
10547                        seen.push((down8, device_routed));
10548                        eprintln!(
10549                            "[sweep-trace] down8={down8} device_routed={device_routed} \
10550                             sel_down8_on={} local_in={} n_sel={n_sel}",
10551                            sel_down8_on(),
10552                            shard.local_in
10553                        );
10554                    }
10555                }
10556            }
10557            if down8 {
10558                let Nvfp4DeviceRoutesWorkspace {
10559                    sel,
10560                    act_q,
10561                    act_d,
10562                    route_w,
10563                    accumulator,
10564                    ..
10565                } = &mut *workspace;
10566                engine.qmatvec_nvfp4_sel_down8_into(
10567                    &shard.bank,
10568                    &sel[rank_index],
10569                    &act_q[rank_index],
10570                    &act_d[rank_index],
10571                    &route_w[rank_index],
10572                    &experts.macros_down_dev[rank_index],
10573                    &mut accumulator[rank_index],
10574                    n_sel,
10575                    shard.local_in,
10576                    shard.out_features,
10577                    shard.row_bytes,
10578                    shard.expert_bytes,
10579                    local_out,
10580                    local_out / 32,
10581                )?;
10582            } else {
10583                let Nvfp4DeviceRoutesWorkspace {
10584                    sel,
10585                    act_q,
10586                    act_d,
10587                    partial,
10588                    ..
10589                } = &mut *workspace;
10590                engine.qmatvec_nvfp4_sel_into(
10591                    &shard.bank,
10592                    &sel[rank_index],
10593                    &act_q[rank_index],
10594                    &act_d[rank_index],
10595                    &mut partial[rank_index],
10596                    n_sel,
10597                    shard.local_in,
10598                    shard.out_features,
10599                    shard.row_bytes,
10600                    shard.expert_bytes,
10601                    local_out,
10602                    local_out / 32,
10603                )?;
10604            }
10605            // Route-weight accumulation: axpy_rows_seq keeps the exact sequential per-pair
10606            // FP chain of the reset + n_sel axpy launches in ONE launch. Device-routed calls
10607            // fold the down macro in-kernel from the device selection. (down8 already
10608            // produced the accumulator inside the sweep.)
10609            if !down8 {
10610                let Nvfp4DeviceRoutesWorkspace {
10611                    partial,
10612                    combine_w,
10613                    route_w,
10614                    sel,
10615                    accumulator,
10616                    ..
10617                } = &mut *workspace;
10618                if device_routed {
10619                    engine.axpy_rows_seq_md_into(
10620                        &partial[rank_index],
10621                        &route_w[rank_index],
10622                        &experts.macros_down_dev[rank_index],
10623                        &sel[rank_index],
10624                        &mut accumulator[rank_index],
10625                        experts.input_width,
10626                        n_sel,
10627                    )?;
10628                } else {
10629                    engine.axpy_rows_seq_into(
10630                        &partial[rank_index],
10631                        &combine_w[rank_index],
10632                        &mut accumulator[rank_index],
10633                        experts.input_width,
10634                        n_sel,
10635                    )?;
10636                }
10637            }
10638        }
10639        Ok(())
10640    }
10641
10642    /// Device-IO twin of `run_tensor_parallel_routes_nvfp4_device`: the layer input arrives as
10643    /// a device row on the model engine `e` and the combined output returns as a fresh
10644    /// `e`-context row — no host round-trip, no host stream sync. Ordering is evented (the v2
10645    /// attention discipline): `ev_entry` is recorded on `e`'s stream AFTER the caller queued
10646    /// the input's producer; each rank waits it before its peer read; the root reduce waits
10647    /// every rank's done event; `e` waits the root's done event before copying out. The
10648    /// program bytes are identical to the host-IO twin — dtoh/htod and dtod preserve f32 bits.
10649    pub fn run_tensor_parallel_routes_nvfp4_device_io(
10650        &self,
10651        experts: &ResidentNvfp4TensorParallel,
10652        e: &Engine,
10653        input_dev: &crate::CudaSlice<f32>,
10654        selected: &[usize],
10655        route_weights: &[f32],
10656        experts_per_token: usize,
10657        activation_limit: Option<f32>,
10658    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10659        if input_dev.len() != experts.input_width {
10660            return Err(format!(
10661                "NVFP4 device-io routes input {} != width {}",
10662                input_dev.len(),
10663                experts.input_width
10664            )
10665            .into());
10666        }
10667        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
10668            return Err(format!(
10669                "NVFP4 device-io routes selected={} weights={} != experts/token {experts_per_token}",
10670                selected.len(),
10671                route_weights.len(),
10672            )
10673            .into());
10674        }
10675        if !route_weights.iter().all(|weight| weight.is_finite()) {
10676            return Err("NVFP4 device route weights contain a non-finite value".into());
10677        }
10678        let world = self.ranks.len();
10679        if world != NVFP4_CANONICAL_ROW_SHARDS {
10680            return Err(format!(
10681                "NVFP4 device routes require world == canonical shard grid \
10682                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
10683            )
10684            .into());
10685        }
10686        let local_out = experts.expert_width / world;
10687        let n_sel = experts_per_token;
10688
10689        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10690        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10691        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10692        let started = timing.then(std::time::Instant::now);
10693
10694        let mut workspace_guard = experts
10695            .device_workspace
10696            .lock()
10697            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10698        if workspace_guard.is_none() {
10699            drop(workspace_guard);
10700            // Build through the host-IO ensure path exactly once: run it with a zero input.
10701            // Cheaper than duplicating the init; the first real call overwrites everything.
10702            let zero = vec![0.0f32; experts.input_width];
10703            let zero_sel = vec![0usize; n_sel];
10704            let zero_w = vec![0.0f32; n_sel];
10705            let _ = self.run_tensor_parallel_routes_nvfp4_device(
10706                experts,
10707                &zero,
10708                &zero_sel,
10709                &zero_w,
10710                n_sel,
10711                activation_limit,
10712            )?;
10713            workspace_guard = experts
10714                .device_workspace
10715                .lock()
10716                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10717        }
10718        let workspace = workspace_guard
10719            .as_mut()
10720            .expect("NVFP4 device routes workspace initialized above");
10721        if workspace.n_sel != n_sel {
10722            return Err(format!(
10723                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10724                workspace.n_sel
10725            )
10726            .into());
10727        }
10728        for &expert in selected {
10729            if expert >= experts.expert_count {
10730                return Err(format!(
10731                    "NVFP4 device selected expert {expert} outside 0..{}",
10732                    experts.expert_count
10733                )
10734                .into());
10735            }
10736        }
10737        let sel_i32 = selected
10738            .iter()
10739            .map(|&expert| expert as i32)
10740            .collect::<Vec<_>>();
10741
10742        // Entry fence: e's stream position covers the input's producer AND every consumer of
10743        // the previous layer's output (queued on e's stream before this call), guarding the
10744        // workspace reuse exactly like the v2 attention driver.
10745        if let Some((_, device)) = workspace.ev_entry.as_ref() {
10746            if *device != e.ctx().ordinal() {
10747                return Err("NVFP4 device-io routes engine changed".into());
10748            }
10749        } else {
10750            let _main = e.gpu.enter_main()?;
10751            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
10752        }
10753        {
10754            let _main = e.gpu.enter_main()?;
10755            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10756            ev_entry.record(&e.stream())?;
10757        }
10758        for (rank_index, engine) in self.ranks.iter().enumerate() {
10759            let _main = engine.gpu.enter_main()?;
10760            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10761            engine.stream().wait(ev_entry)?;
10762            {
10763                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
10764                engine
10765                    .stream()
10766                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
10767            }
10768            {
10769                let Nvfp4DeviceRoutesWorkspace {
10770                    input, in_q, in_d, ..
10771                } = &mut *workspace;
10772                engine.quantize_q8_1_into(
10773                    &input[rank_index],
10774                    1,
10775                    experts.input_width,
10776                    &mut in_q[rank_index],
10777                    &mut in_d[rank_index],
10778                )?;
10779            }
10780        }
10781        self.nvfp4_routes_batched_sweeps(
10782            experts,
10783            workspace,
10784            selected,
10785            route_weights,
10786            &sel_i32,
10787            local_out,
10788            n_sel,
10789            activation_limit,
10790            false,
10791        )?;
10792
10793        // Evented combine: rank done events replace the host stream syncs, the reduce runs on
10794        // the root stream in canonical shard order, and e copies the combined row out behind
10795        // the root's done event.
10796        // rank0 == root: its own stream order already covers its sweep; only the PEER
10797        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
10798        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
10799            let _main = engine.gpu.enter_main()?;
10800            workspace.ev_rank[rank_index].record(&engine.stream())?;
10801        }
10802        if moe_direct_on() && self.ranks.len() == 2 {
10803            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
10804            // rank0's is root-stream-ordered. One root event + rank1's own event order
10805            // the model engine's single add — same operand order as root's add
10806            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
10807            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
10808            // hazard class does not apply).
10809            {
10810                let root = &self.ranks[0];
10811                let _main = root.gpu.enter_main()?;
10812                workspace
10813                    .ev_done
10814                    .as_ref()
10815                    .expect("device routes done event")
10816                    .record(&root.stream())?;
10817            }
10818            let _main = e.gpu.enter_main()?;
10819            e.stream().wait(
10820                workspace
10821                    .ev_done
10822                    .as_ref()
10823                    .expect("device routes done event"),
10824            )?;
10825            for ev in workspace.ev_rank.iter().skip(1) {
10826                e.stream().wait(ev)?;
10827            }
10828            let mut output = e.uninit(experts.input_width)?;
10829            e.add(
10830                &workspace.accumulator[0],
10831                &workspace.accumulator[1],
10832                &mut output,
10833                experts.input_width,
10834            )?;
10835            let output = output;
10836            if let Some(started) = started {
10837                use std::sync::atomic::Ordering;
10838                let ns = TIMING_NS
10839                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10840                    + started.elapsed().as_nanos() as u64;
10841                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10842                if calls % 430 == 0 {
10843                    eprintln!(
10844                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10845                        ns as f64 / 1.0e6,
10846                        ns as f64 / calls as f64 / 1.0e3,
10847                    );
10848                }
10849            }
10850            return Ok(output);
10851        }
10852        {
10853            let root = &self.ranks[0];
10854            let _main = root.gpu.enter_main()?;
10855            for ev in workspace.ev_rank.iter().skip(1) {
10856                root.stream().wait(ev)?;
10857            }
10858            root.stream()
10859                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
10860            {
10861                let Nvfp4DeviceRoutesWorkspace {
10862                    accumulator,
10863                    remote,
10864                    combined,
10865                    ..
10866                } = &mut *workspace;
10867                root.add(&accumulator[0], remote, combined, experts.input_width)?;
10868            }
10869            workspace
10870                .ev_done
10871                .as_ref()
10872                .expect("device routes done event")
10873                .record(&root.stream())?;
10874        }
10875        let output = {
10876            let _main = e.gpu.enter_main()?;
10877            e.stream().wait(
10878                workspace
10879                    .ev_done
10880                    .as_ref()
10881                    .expect("device routes done event"),
10882            )?;
10883            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
10884            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
10885            let mut output = e.uninit(experts.input_width)?;
10886            e.stream().memcpy_dtod(
10887                &workspace.combined.slice(0..experts.input_width),
10888                &mut output.slice_mut(0..experts.input_width),
10889            )?;
10890            output
10891        };
10892        if let Some(started) = started {
10893            use std::sync::atomic::Ordering;
10894            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10895                + started.elapsed().as_nanos() as u64;
10896            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10897            if calls % 430 == 0 {
10898                eprintln!(
10899                    "[nvfp4-dev-routes-io-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10900                    ns as f64 / 1.0e6,
10901                    ns as f64 / calls as f64 / 1.0e3,
10902                );
10903            }
10904        }
10905        Ok(output)
10906    }
10907
10908    /// Device-routed twin of `run_tensor_parallel_routes_nvfp4_device_io`: the selection and
10909    /// route weights arrive as the device router's e-context outputs — the per-layer host
10910    /// logits readback disappears. The fresh router outputs are staged into persistent
10911    /// e-context buffers on e's stream (never-free discipline) before the entry event; each
10912    /// rank peer-reads them behind it. The down-macro fold happens in-kernel.
10913    #[allow(clippy::too_many_arguments)]
10914    /// Prestage the routed-expert input: pull the shared row to every rank and quantize it
10915    /// there, WITHOUT the selection — callable before the router so the rank chains overlap
10916    /// it. No-op (returns false) when the workspace is not built yet or the door is off;
10917    /// the routed run then does its own staging as before.
10918    pub fn nvfp4_routes_prestage(
10919        &self,
10920        experts: &ResidentNvfp4TensorParallel,
10921        e: &Engine,
10922        input_dev: &crate::CudaSlice<f32>,
10923    ) -> Result<bool, Box<dyn std::error::Error>> {
10924        self.nvfp4_routes_prestage_with(experts, e, input_dev, |_, _, _, _| Ok(false))
10925    }
10926
10927    /// `nvfp4_routes_prestage` with a PEER-ROUTER hook: after rank1's input pull +
10928    /// quantize, the hook may compute rank1's route selection LOCALLY (replicated router —
10929    /// deterministic kernels on identical input bits produce identical sel/w, so the
10930    /// selection is bit-equal to the root's). Returns true when it wrote sel/route_w; the
10931    /// routed run then skips rank1's sel pull.
10932    pub fn nvfp4_routes_prestage_with(
10933        &self,
10934        experts: &ResidentNvfp4TensorParallel,
10935        e: &Engine,
10936        input_dev: &crate::CudaSlice<f32>,
10937        rank1_router: impl FnOnce(
10938            &Engine,
10939            &crate::CudaSlice<f32>,
10940            &mut crate::CudaSlice<i32>,
10941            &mut crate::CudaSlice<f32>,
10942        ) -> Result<bool, Box<dyn std::error::Error>>,
10943    ) -> Result<bool, Box<dyn std::error::Error>> {
10944        if !routes_prestage_on() || step_tp_graph_enabled()? {
10945            return Ok(false);
10946        }
10947        if input_dev.len() != experts.input_width {
10948            return Err("NVFP4 prestage input width mismatch".into());
10949        }
10950        let mut workspace_guard = experts
10951            .device_workspace
10952            .lock()
10953            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10954        let Some(workspace) = workspace_guard.as_mut() else {
10955            return Ok(false);
10956        };
10957        if workspace.ev_input.is_none() {
10958            let _main = e.gpu.enter_main()?;
10959            workspace.ev_input = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
10960        } else if workspace.ev_input.as_ref().map(|(_, d)| *d) != Some(e.ctx().ordinal()) {
10961            return Err("NVFP4 prestage engine changed".into());
10962        }
10963        {
10964            let _main = e.gpu.enter_main()?;
10965            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
10966            ev.record(&e.stream())?;
10967        }
10968        for (rank_index, engine) in self.ranks.iter().enumerate() {
10969            let _main = engine.gpu.enter_main()?;
10970            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
10971            engine.stream().wait(ev)?;
10972            {
10973                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
10974                engine
10975                    .stream()
10976                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
10977            }
10978            {
10979                let Nvfp4DeviceRoutesWorkspace {
10980                    input, in_q, in_d, ..
10981                } = &mut *workspace;
10982                engine.quantize_q8_1_into(
10983                    &input[rank_index],
10984                    1,
10985                    experts.input_width,
10986                    &mut in_q[rank_index],
10987                    &mut in_d[rank_index],
10988                )?;
10989            }
10990        }
10991        if self.ranks.len() == 2 {
10992            let rank1 = &self.ranks[1];
10993            let _r1 = rank1.gpu.enter_main()?;
10994            let Nvfp4DeviceRoutesWorkspace {
10995                input,
10996                sel,
10997                route_w,
10998                ..
10999            } = &mut *workspace;
11000            let (in1, rest_sel) = (&input[1], &mut sel[1]);
11001            if rank1_router(rank1, in1, rest_sel, &mut route_w[1])? {
11002                workspace.rank1_routed = true;
11003            }
11004        }
11005        workspace.prestaged = true;
11006        Ok(true)
11007    }
11008
11009    /// TWO-COLUMN device-routed expert program (spec verify, MEMRA_TCOL_FFN): one gu_tcol
11010    /// sweep over 2*n_sel_col pairs (pair t reads activation row t/n_sel_col — weights the
11011    /// two columns share dedup through L2), the UNCHANGED silu/down kernels at n_sel=16
11012    /// (both already index per pair), and one offset-axpy combine per column (the exact
11013    /// t=1 sequential chain over that column's 8 pairs). No serving doors: no graph, no
11014    /// prestage, no shexp folding — plain evented ordering. Returns [2, input_width] on e.
11015    ///
11016    /// EXACTNESS: every kernel body is the t=1 program per (pair,row) or per element; the
11017    /// per-column combine order equals the t=1 combine; the cross-rank join adds the same
11018    /// operand values elementwise. Gated by the greedy tape like every verify arm.
11019    #[allow(clippy::too_many_arguments)]
11020    pub fn run_tensor_parallel_routes_nvfp4_device_routed_tn(
11021        &self,
11022        experts: &ResidentNvfp4TensorParallel,
11023        e: &Engine,
11024        z_t: &crate::CudaSlice<f32>,
11025        sel_d: &crate::CudaSlice<i32>,
11026        w_d: &crate::CudaSlice<f32>,
11027        t: usize,
11028        n_sel_col: usize,
11029        activation_limit: Option<f32>,
11030    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11031        let world = self.ranks.len();
11032        if world != NVFP4_CANONICAL_ROW_SHARDS {
11033            return Err("NVFP4 t-row routes require the canonical 2-shard grid".into());
11034        }
11035        let width = experts.input_width;
11036        let n_sel = t * n_sel_col;
11037        if t == 0 || t > 32 || z_t.len() < t * width || sel_d.len() < n_sel || w_d.len() < n_sel {
11038            return Err("NVFP4 t-row routes geometry".into());
11039        }
11040        if !nvfp4_bank_v2_on() {
11041            return Err("NVFP4 t-row routes require the v2 banks (MEMRA_NVFP4_BANK_V2=1)".into());
11042        }
11043        let local_out = experts.expert_width / world;
11044        let mut guard = experts
11045            .t2_workspace
11046            .lock()
11047            .map_err(|_| "NVFP4 t2 workspace lock is poisoned")?;
11048        if nvfp4_trow_workspace_needs_grow(guard.as_ref().map(|ws| (ws.t_cap, ws.n_sel)), t, n_sel)
11049        {
11050            let t_cap = guard.as_ref().map_or(t, |ws| ws.t_cap.max(t));
11051            let n_sel_cap = guard.as_ref().map_or(n_sel, |ws| ws.n_sel.max(n_sel));
11052            let mut input2 = Vec::new();
11053            let mut in_q2 = Vec::new();
11054            let mut in_d2 = Vec::new();
11055            let mut sel2 = Vec::new();
11056            let mut route_w2 = Vec::new();
11057            let mut gate_out2 = Vec::new();
11058            let mut up_out2 = Vec::new();
11059            let mut act_q2 = Vec::new();
11060            let mut act_d2 = Vec::new();
11061            let mut partial2 = Vec::new();
11062            let mut acc_a = Vec::new();
11063            let mut acc_b = Vec::new();
11064            let mut acc2 = Vec::new();
11065            let mut ev_rank = Vec::new();
11066            for engine in &self.ranks {
11067                let _m = engine.gpu.enter_main()?;
11068                input2.push(engine.uninit(t_cap * width)?);
11069                in_q2.push(engine.alloc_i8_uninit(t_cap * width)?);
11070                in_d2.push(engine.uninit(t_cap * (width / 32))?);
11071                sel2.push(engine.htod_i32(&vec![0i32; n_sel_cap])?);
11072                route_w2.push(engine.uninit(n_sel_cap)?);
11073                gate_out2.push(engine.uninit(n_sel_cap * local_out)?);
11074                up_out2.push(engine.uninit(n_sel_cap * local_out)?);
11075                act_q2.push(engine.alloc_i8_uninit(n_sel_cap * local_out)?);
11076                act_d2.push(engine.uninit(n_sel_cap * (local_out / 32))?);
11077                partial2.push(engine.uninit(n_sel_cap * width)?);
11078                acc_a.push(engine.uninit(width)?);
11079                acc_b.push(engine.uninit(width)?);
11080                acc2.push(engine.uninit(t_cap * width)?);
11081                ev_rank.push(engine.ctx().new_event(None)?);
11082            }
11083            let root = &self.ranks[0];
11084            let (peer_a, peer_b, omix_a, omix_b, peer2, omix2, ev_root) = {
11085                let _m = root.gpu.enter_main()?;
11086                (
11087                    root.uninit(width)?,
11088                    root.uninit(width)?,
11089                    root.uninit(width)?,
11090                    root.uninit(width)?,
11091                    root.uninit(t_cap * width)?,
11092                    root.uninit(t_cap * width)?,
11093                    root.ctx().new_event(None)?,
11094                )
11095            };
11096            let ev_entry = {
11097                let _m = e.gpu.enter_main()?;
11098                e.ctx().new_event(None)?
11099            };
11100            *guard = Some(Nvfp4T2Workspace {
11101                input2,
11102                in_q2,
11103                in_d2,
11104                sel2,
11105                route_w2,
11106                gate_out2,
11107                up_out2,
11108                act_q2,
11109                act_d2,
11110                partial2,
11111                acc_a,
11112                acc_b,
11113                acc2,
11114                peer2,
11115                omix2,
11116                peer_a,
11117                peer_b,
11118                omix_a,
11119                omix_b,
11120                ev_entry,
11121                ev_rank,
11122                ev_root,
11123                t_cap,
11124                n_sel: n_sel_cap,
11125                e_device: e.ctx().ordinal(),
11126            });
11127        }
11128        let ws = guard.as_mut().expect("armed above");
11129        if ws.e_device != e.ctx().ordinal() {
11130            return Err("NVFP4 t2 routes engine changed".into());
11131        }
11132        {
11133            let _main = e.gpu.enter_main()?;
11134            ws.ev_entry.record(&e.stream())?;
11135        }
11136        // One decision for the sweep AND the join (an acc2 the sweep never wrote must
11137        // never be joined). t > 2 has no split-accumulator fallback: it requires the
11138        // fused rows kernel.
11139        let down8 = sel_down8_on() && (local_out >> 5) <= 32 && n_sel_col <= 8;
11140        if !down8 && t != 2 {
11141            return Err(
11142                "NVFP4 t-row routes at t != 2 require MEMRA_SEL_DOWN8=1 (fused rows kernel)".into(),
11143            );
11144        }
11145        for rank in 0..world {
11146            let engine = &self.ranks[rank];
11147            let _main = engine.gpu.enter_main()?;
11148            engine.stream().wait(&ws.ev_entry)?;
11149            {
11150                let mut dst = ws.input2[rank].slice_mut(0..t * width);
11151                engine
11152                    .stream()
11153                    .memcpy_dtod(&z_t.slice(0..t * width), &mut dst)?;
11154            }
11155            {
11156                let mut dst = ws.sel2[rank].slice_mut(0..n_sel);
11157                engine
11158                    .stream()
11159                    .memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
11160            }
11161            {
11162                let mut dst = ws.route_w2[rank].slice_mut(0..n_sel);
11163                engine
11164                    .stream()
11165                    .memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
11166            }
11167            {
11168                let Nvfp4T2Workspace {
11169                    input2,
11170                    in_q2,
11171                    in_d2,
11172                    ..
11173                } = &mut *ws;
11174                engine.quantize_q8_1_into(
11175                    &input2[rank],
11176                    t,
11177                    width,
11178                    &mut in_q2[rank],
11179                    &mut in_d2[rank],
11180                )?;
11181            }
11182            let gate_bank = &experts.gate[rank];
11183            let up_bank = &experts.up[rank];
11184            if gate_bank.in_features != up_bank.in_features
11185                || gate_bank.local_out != up_bank.local_out
11186                || gate_bank.row_bytes != up_bank.row_bytes
11187                || gate_bank.expert_bytes != up_bank.expert_bytes
11188            {
11189                return Err("NVFP4 t-row routes need matched gate/up bank geometry".into());
11190            }
11191            {
11192                let Nvfp4T2Workspace {
11193                    sel2,
11194                    in_q2,
11195                    in_d2,
11196                    gate_out2,
11197                    up_out2,
11198                    ..
11199                } = &mut *ws;
11200                engine.qmatvec_nvfp4_sel_gu_tcol_into(
11201                    &gate_bank.bank,
11202                    &up_bank.bank,
11203                    &sel2[rank],
11204                    &in_q2[rank],
11205                    &in_d2[rank],
11206                    &mut gate_out2[rank],
11207                    &mut up_out2[rank],
11208                    n_sel,
11209                    n_sel_col,
11210                    gate_bank.in_features,
11211                    gate_bank.local_out,
11212                    gate_bank.row_bytes,
11213                    gate_bank.expert_bytes,
11214                    width,
11215                    width / 32,
11216                )?;
11217            }
11218            {
11219                let Nvfp4T2Workspace {
11220                    gate_out2,
11221                    up_out2,
11222                    sel2,
11223                    act_q2,
11224                    act_d2,
11225                    ..
11226                } = &mut *ws;
11227                engine.silu_mul_scaled_q8_1_sel_into(
11228                    &gate_out2[rank],
11229                    &up_out2[rank],
11230                    &experts.macros_gate_dev[rank],
11231                    &experts.macros_up_dev[rank],
11232                    &sel2[rank],
11233                    activation_limit,
11234                    &mut act_q2[rank],
11235                    &mut act_d2[rank],
11236                    local_out,
11237                    n_sel,
11238                )?;
11239            }
11240            let shard = &experts.down[rank];
11241            if shard.device_rank != rank || shard.local_in != local_out {
11242                return Err("NVFP4 t-row routes: down shard placement drifted".into());
11243            }
11244            // MEMRA_SEL_DOWN8=1: down sweep + per-row combine in ONE launch (t2 twin of
11245            // the t=1 fusion) — kills the n_sel x width partial round-trip and both axpy
11246            // passes. Each row's FP chain == its own down8/axpy pair (bit-identical).
11247            if down8 {
11248                let Nvfp4T2Workspace {
11249                    sel2,
11250                    act_q2,
11251                    act_d2,
11252                    route_w2,
11253                    acc2,
11254                    ..
11255                } = &mut *ws;
11256                engine.qmatvec_nvfp4_sel_down8_rows_into(
11257                    &shard.bank,
11258                    &sel2[rank],
11259                    &act_q2[rank],
11260                    &act_d2[rank],
11261                    &route_w2[rank],
11262                    &experts.macros_down_dev[rank],
11263                    &mut acc2[rank],
11264                    t,
11265                    n_sel_col,
11266                    shard.local_in,
11267                    shard.out_features,
11268                    shard.row_bytes,
11269                    shard.expert_bytes,
11270                    local_out,
11271                    local_out / 32,
11272                )?;
11273            } else {
11274                {
11275                    let Nvfp4T2Workspace {
11276                        sel2,
11277                        act_q2,
11278                        act_d2,
11279                        partial2,
11280                        ..
11281                    } = &mut *ws;
11282                    engine.qmatvec_nvfp4_sel_into(
11283                        &shard.bank,
11284                        &sel2[rank],
11285                        &act_q2[rank],
11286                        &act_d2[rank],
11287                        &mut partial2[rank],
11288                        n_sel,
11289                        shard.local_in,
11290                        shard.out_features,
11291                        shard.row_bytes,
11292                        shard.expert_bytes,
11293                        local_out,
11294                        local_out / 32,
11295                    )?;
11296                }
11297                let Nvfp4T2Workspace {
11298                    partial2,
11299                    route_w2,
11300                    sel2,
11301                    acc_a,
11302                    acc_b,
11303                    ..
11304                } = &mut *ws;
11305                engine.axpy_rows_seq_md_off_into(
11306                    &partial2[rank],
11307                    &route_w2[rank],
11308                    &experts.macros_down_dev[rank],
11309                    &sel2[rank],
11310                    &mut acc_a[rank],
11311                    width,
11312                    n_sel_col,
11313                    0,
11314                )?;
11315                engine.axpy_rows_seq_md_off_into(
11316                    &partial2[rank],
11317                    &route_w2[rank],
11318                    &experts.macros_down_dev[rank],
11319                    &sel2[rank],
11320                    &mut acc_b[rank],
11321                    width,
11322                    n_sel_col,
11323                    n_sel_col,
11324                )?;
11325            }
11326            if rank != 0 {
11327                ws.ev_rank[rank].record(&engine.stream())?;
11328            }
11329        }
11330        let root = &self.ranks[0];
11331        {
11332            let _main = root.gpu.enter_main()?;
11333            for ev in ws.ev_rank.iter().skip(1) {
11334                root.stream().wait(ev)?;
11335            }
11336            if down8 {
11337                // Fused-slab join: ONE peer pull + ONE elementwise add cover every
11338                // row (independent elements; per-element op == the split join).
11339                let Nvfp4T2Workspace {
11340                    acc2, peer2, omix2, ..
11341                } = &mut *ws;
11342                {
11343                    let mut dst = peer2.slice_mut(0..t * width);
11344                    root.stream()
11345                        .memcpy_dtod(&acc2[1].slice(0..t * width), &mut dst)?;
11346                }
11347                root.add(&acc2[0], peer2, omix2, t * width)?;
11348            } else {
11349                let Nvfp4T2Workspace {
11350                    acc_a,
11351                    acc_b,
11352                    peer_a,
11353                    peer_b,
11354                    omix_a,
11355                    omix_b,
11356                    ..
11357                } = &mut *ws;
11358                {
11359                    let mut dst = peer_a.slice_mut(0..width);
11360                    root.stream()
11361                        .memcpy_dtod(&acc_a[1].slice(0..width), &mut dst)?;
11362                }
11363                {
11364                    let mut dst = peer_b.slice_mut(0..width);
11365                    root.stream()
11366                        .memcpy_dtod(&acc_b[1].slice(0..width), &mut dst)?;
11367                }
11368                root.add(&acc_a[0], peer_a, omix_a, width)?;
11369                root.add(&acc_b[0], peer_b, omix_b, width)?;
11370            }
11371            ws.ev_root.record(&root.stream())?;
11372        }
11373        let _main = e.gpu.enter_main()?;
11374        e.stream().wait(&ws.ev_root)?;
11375        let mut out = e.uninit(t * width)?;
11376        if down8 {
11377            e.stream().memcpy_dtod(
11378                &ws.omix2.slice(0..t * width),
11379                &mut out.slice_mut(0..t * width),
11380            )?;
11381        } else {
11382            e.stream()
11383                .memcpy_dtod(&ws.omix_a.slice(0..width), &mut out.slice_mut(0..width))?;
11384            e.stream().memcpy_dtod(
11385                &ws.omix_b.slice(0..width),
11386                &mut out.slice_mut(width..2 * width),
11387            )?;
11388        }
11389        Ok(out)
11390    }
11391
11392    pub fn run_tensor_parallel_routes_nvfp4_device_routed(
11393        &self,
11394        experts: &ResidentNvfp4TensorParallel,
11395        e: &Engine,
11396        input_dev: &crate::CudaSlice<f32>,
11397        sel_d: &crate::CudaSlice<i32>,
11398        w_d: &crate::CudaSlice<f32>,
11399        experts_per_token: usize,
11400        activation_limit: Option<f32>,
11401    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11402        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
11403            experts,
11404            e,
11405            input_dev,
11406            sel_d,
11407            w_d,
11408            experts_per_token,
11409            activation_limit,
11410            || Ok(()),
11411        )
11412    }
11413
11414    /// `run_tensor_parallel_routes_nvfp4_device_routed` with a PREJOIN hook: `pre_join`
11415    /// runs on the host right before the join wait is enqueued on e's stream — work it
11416    /// issues there (e.g. the shexp overlap) executes WHILE the peer rank finishes its
11417    /// sweep, instead of after the join. Value-neutral by construction (the hook only
11418    /// reorders independent host issue).
11419    #[allow(clippy::too_many_arguments)]
11420    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
11421        &self,
11422        experts: &ResidentNvfp4TensorParallel,
11423        e: &Engine,
11424        input_dev: &crate::CudaSlice<f32>,
11425        sel_d: &crate::CudaSlice<i32>,
11426        w_d: &crate::CudaSlice<f32>,
11427        experts_per_token: usize,
11428        activation_limit: Option<f32>,
11429        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
11430    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11431        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
11432            experts,
11433            e,
11434            input_dev,
11435            sel_d,
11436            w_d,
11437            experts_per_token,
11438            activation_limit,
11439            pre_join,
11440            None,
11441        )
11442    }
11443
11444    /// The prejoin variant with MOE TAIL FUSION M1: when `post_add = Some((sh_raw,
11445    /// scale_raw))`, the direct-join arm folds the shexp apply into the join add
11446    /// (`dst = (acc0+acc1) + sh*scale[0]`, exact split-pair sequence) — the caller skips
11447    /// its apply launch. Raw UVA pointers so no lock is held across the call.
11448    #[allow(clippy::too_many_arguments)]
11449    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
11450        &self,
11451        experts: &ResidentNvfp4TensorParallel,
11452        e: &Engine,
11453        input_dev: &crate::CudaSlice<f32>,
11454        sel_d: &crate::CudaSlice<i32>,
11455        w_d: &crate::CudaSlice<f32>,
11456        experts_per_token: usize,
11457        activation_limit: Option<f32>,
11458        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
11459        post_add: Option<(u64, u64)>,
11460    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11461        if input_dev.len() != experts.input_width {
11462            return Err(format!(
11463                "NVFP4 device-routed input {} != width {}",
11464                input_dev.len(),
11465                experts.input_width
11466            )
11467            .into());
11468        }
11469        let n_sel = experts_per_token;
11470        if sel_d.len() < n_sel || w_d.len() < n_sel {
11471            return Err(format!(
11472                "NVFP4 device-routed routes sel={} w={} < experts/token {n_sel}",
11473                sel_d.len(),
11474                w_d.len()
11475            )
11476            .into());
11477        }
11478        let world = self.ranks.len();
11479        if world != NVFP4_CANONICAL_ROW_SHARDS {
11480            return Err(format!(
11481                "NVFP4 device routes require world == canonical shard grid \
11482                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
11483            )
11484            .into());
11485        }
11486        let local_out = if experts.ep2 {
11487            experts.expert_width
11488        } else {
11489            experts.expert_width / world
11490        };
11491
11492        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11493        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11494        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
11495        let started = timing.then(std::time::Instant::now);
11496
11497        let mut workspace_guard = experts
11498            .device_workspace
11499            .lock()
11500            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
11501        if workspace_guard.is_none() {
11502            drop(workspace_guard);
11503            let zero = vec![0.0f32; experts.input_width];
11504            let zero_sel = vec![0usize; n_sel];
11505            let zero_w = vec![0.0f32; n_sel];
11506            let _ = self.run_tensor_parallel_routes_nvfp4_device(
11507                experts,
11508                &zero,
11509                &zero_sel,
11510                &zero_w,
11511                n_sel,
11512                activation_limit,
11513            )?;
11514            workspace_guard = experts
11515                .device_workspace
11516                .lock()
11517                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
11518        }
11519        let workspace = workspace_guard
11520            .as_mut()
11521            .expect("NVFP4 device routes workspace initialized above");
11522        if workspace.n_sel != n_sel {
11523            return Err(format!(
11524                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
11525                workspace.n_sel
11526            )
11527            .into());
11528        }
11529
11530        // GRAPH DOOR (MEMRA_STEP_TP_GRAPH=1): the whole rank+root segment replays as one
11531        // stitched multi-device parent launched on e's stream — no events, no per-token node
11532        // updates (every address is persistent staging). VALUE-IDENTICAL to the eager path:
11533        // the children replay exactly the same kernel/copy sequence.
11534        if step_tp_graph_enabled()? {
11535            if experts.ep2 {
11536                return Err(
11537                    "MEMRA_STEP_TP_GRAPH=1 with MEMRA_STEP_NVFP4_EP2=1 has never been \
11538                     co-gated; unset one"
11539                        .into(),
11540                );
11541            }
11542            if workspace.dev_route_e.is_none() {
11543                let _main = e.gpu.enter_main()?;
11544                workspace.dev_route_e = Some((
11545                    e.htod_i32(&vec![0i32; n_sel])?,
11546                    e.htod(&vec![0.0f32; n_sel])?,
11547                ));
11548            }
11549            if workspace.in_stage_e.is_none() {
11550                let _main = e.gpu.enter_main()?;
11551                workspace.in_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
11552                workspace.out_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
11553            }
11554            if workspace.routes_graph.is_none() {
11555                let graph = self.nvfp4_routes_build_graph(
11556                    experts,
11557                    workspace,
11558                    local_out,
11559                    n_sel,
11560                    activation_limit,
11561                )?;
11562                workspace.routes_graph = Some(graph);
11563                eprintln!(
11564                    "[step-tp-graph] routes segment captured: ranks={world} n_sel={n_sel} \
11565                     children=3 updates=none performance_claim=false"
11566                );
11567            }
11568            let output = {
11569                let _main = e.gpu.enter_main()?;
11570                {
11571                    let (sel_e, w_e) = workspace
11572                        .dev_route_e
11573                        .as_mut()
11574                        .expect("device route staging set above");
11575                    {
11576                        let mut dst = sel_e.slice_mut(0..n_sel);
11577                        e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
11578                    }
11579                    {
11580                        let mut dst = w_e.slice_mut(0..n_sel);
11581                        e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
11582                    }
11583                }
11584                {
11585                    let in_stage = workspace
11586                        .in_stage_e
11587                        .as_mut()
11588                        .expect("graph staging set above");
11589                    let mut dst = in_stage.slice_mut(0..experts.input_width);
11590                    e.stream()
11591                        .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut dst)?;
11592                }
11593                unsafe {
11594                    let r = cudarc::driver::sys::cuGraphLaunch(
11595                        workspace
11596                            .routes_graph
11597                            .as_ref()
11598                            .expect("routes graph built above")
11599                            .exec,
11600                        e.stream().cu_stream() as cudarc::driver::sys::CUstream,
11601                    );
11602                    if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
11603                        return Err(format!("routes graph launch: {r:?}").into());
11604                    }
11605                }
11606                let mut output = e.uninit(experts.input_width)?;
11607                {
11608                    let out_stage = workspace
11609                        .out_stage_e
11610                        .as_ref()
11611                        .expect("graph staging set above");
11612                    e.stream().memcpy_dtod(
11613                        &out_stage.slice(0..experts.input_width),
11614                        &mut output.slice_mut(0..experts.input_width),
11615                    )?;
11616                }
11617                output
11618            };
11619            if let Some(started) = started {
11620                use std::sync::atomic::Ordering;
11621                let ns = TIMING_NS
11622                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
11623                    + started.elapsed().as_nanos() as u64;
11624                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11625                if calls % 430 == 0 {
11626                    eprintln!(
11627                        "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11628                        ns as f64 / 1.0e6,
11629                        ns as f64 / calls as f64 / 1.0e3,
11630                    );
11631                }
11632            }
11633            return Ok(output);
11634        }
11635
11636        // Entry fence + router-output staging, all on e's stream: the fresh sel/w slices are
11637        // copied into the persistent e-context pair, then the event is recorded — the caller's
11638        // sel_d/w_d can free on e's stream with no cross-stream reader.
11639        if let Some((_, device)) = workspace.ev_entry.as_ref() {
11640            if *device != e.ctx().ordinal() {
11641                return Err("NVFP4 device-routed routes engine changed".into());
11642            }
11643        } else {
11644            let _main = e.gpu.enter_main()?;
11645            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
11646        }
11647        if workspace.dev_route_e.is_none() {
11648            let _main = e.gpu.enter_main()?;
11649            workspace.dev_route_e = Some((
11650                e.htod_i32(&vec![0i32; n_sel])?,
11651                e.htod(&vec![0.0f32; n_sel])?,
11652            ));
11653        }
11654        // MEMRA_SEL_MIRROR: the staging pair exists so the rank streams read a persistent
11655        // e-context address. The caller's sel_d/w_d ARE persistent (the process-static
11656        // selection rows), so when every consuming rank shares e's device the ranks can read
11657        // them directly and this hop disappears. The graph door keeps the staging (its
11658        // captured copies read the fixed addresses).
11659        let mirror = sel_mirror_on() && !step_tp_graph_enabled()?;
11660        let e_device = e.ctx().ordinal();
11661        // rank1_routed is consumed (taken) below; peek it here for the staging decision.
11662        let rank1_routed_peek = workspace.rank1_routed;
11663        let stage_needed = !mirror
11664            || self.ranks.iter().enumerate().any(|(rank_index, engine)| {
11665                !(rank1_routed_peek && rank_index == 1) && engine.ctx().ordinal() != e_device
11666            });
11667        {
11668            let _main = e.gpu.enter_main()?;
11669            if stage_needed {
11670                let (sel_e, w_e) = workspace
11671                    .dev_route_e
11672                    .as_mut()
11673                    .expect("device route staging set above");
11674                {
11675                    let mut dst = sel_e.slice_mut(0..n_sel);
11676                    e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
11677                }
11678                {
11679                    let mut dst = w_e.slice_mut(0..n_sel);
11680                    e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
11681                }
11682            }
11683            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
11684            ev_entry.record(&e.stream())?;
11685        }
11686        // Prestage door: input pull + quantize were already issued on the rank streams
11687        // (before the router) — the rank stream order suffices, skip them here.
11688        let prestaged = std::mem::take(&mut workspace.prestaged);
11689        let rank1_routed = std::mem::take(&mut workspace.rank1_routed);
11690        for (rank_index, engine) in self.ranks.iter().enumerate() {
11691            let _main = engine.gpu.enter_main()?;
11692            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
11693            engine.stream().wait(ev_entry)?;
11694            if !prestaged {
11695                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
11696                engine
11697                    .stream()
11698                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
11699            }
11700            if !(rank1_routed && rank_index == 1) {
11701                // ONE mirror launch instead of two 32-byte copy-engine dispatches; source is
11702                // the caller's persistent rows when this rank shares e's device (UVA, ordered
11703                // by ev_entry), else the staged e-context pair.
11704                let same_dev = engine.ctx().ordinal() == e_device;
11705                if mirror {
11706                    // Split the workspace borrow so the source (the staged pair, when this
11707                    // rank is off-device) and the destination rows coexist.
11708                    let Nvfp4DeviceRoutesWorkspace {
11709                        sel,
11710                        route_w,
11711                        dev_route_e,
11712                        ..
11713                    } = &mut *workspace;
11714                    let (src_sel, src_w): (&crate::CudaSlice<i32>, &crate::CudaSlice<f32>) =
11715                        if same_dev {
11716                            (sel_d, w_d)
11717                        } else {
11718                            let (sel_e, w_e) = dev_route_e
11719                                .as_ref()
11720                                .expect("device route staging set above");
11721                            (sel_e, w_e)
11722                        };
11723                    engine.moe_sel_w_mirror(
11724                        src_sel,
11725                        src_w,
11726                        &mut sel[rank_index],
11727                        &mut route_w[rank_index],
11728                        n_sel,
11729                    )?;
11730                } else {
11731                    let (sel_e, w_e) = workspace
11732                        .dev_route_e
11733                        .as_ref()
11734                        .expect("device route staging set above");
11735                    {
11736                        let mut dst = workspace.sel[rank_index].slice_mut(0..n_sel);
11737                        engine
11738                            .stream()
11739                            .memcpy_dtod(&sel_e.slice(0..n_sel), &mut dst)?;
11740                    }
11741                    {
11742                        let mut dst = workspace.route_w[rank_index].slice_mut(0..n_sel);
11743                        engine
11744                            .stream()
11745                            .memcpy_dtod(&w_e.slice(0..n_sel), &mut dst)?;
11746                    }
11747                }
11748            }
11749            if !prestaged {
11750                let Nvfp4DeviceRoutesWorkspace {
11751                    input, in_q, in_d, ..
11752                } = &mut *workspace;
11753                engine.quantize_q8_1_into(
11754                    &input[rank_index],
11755                    1,
11756                    experts.input_width,
11757                    &mut in_q[rank_index],
11758                    &mut in_d[rank_index],
11759                )?;
11760            }
11761        }
11762        self.nvfp4_routes_batched_sweeps(
11763            experts,
11764            workspace,
11765            &[],
11766            &[],
11767            &[],
11768            local_out,
11769            n_sel,
11770            activation_limit,
11771            true,
11772        )?;
11773
11774        // rank0 == root: its own stream order already covers its sweep; only the PEER
11775        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
11776        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
11777            let _main = engine.gpu.enter_main()?;
11778            workspace.ev_rank[rank_index].record(&engine.stream())?;
11779        }
11780        // Doorbell fences (MEMRA_FENCE_MEMOPS=1): rank1 + root ring their flags; e waits
11781        // the tickets instead of the two events. Arm lazily; 0-len = unsupported.
11782        let memops = fence_memops_on() && moe_direct_on() && self.ranks.len() == 2;
11783        let mut ticket = 0u32;
11784        if memops {
11785            use cudarc::driver::sys;
11786            if workspace.fence_flags_raw == 0 {
11787                let root = &self.ranks[0];
11788                let _main = root.gpu.enter_main()?;
11789                let mut ptr: sys::CUdeviceptr = 0;
11790                let r = unsafe { sys::cuMemAlloc_v2(&mut ptr, 8) };
11791                if r != sys::CUresult::CUDA_SUCCESS {
11792                    return Err(format!("fence flag alloc: {r:?}").into());
11793                }
11794                let r = unsafe { sys::cuMemsetD8_v2(ptr, 0, 8) };
11795                if r != sys::CUresult::CUDA_SUCCESS {
11796                    return Err(format!("fence flag memset: {r:?}").into());
11797                }
11798                workspace.fence_flags_raw = ptr as u64;
11799            }
11800            workspace.fence_ticket = workspace.fence_ticket.wrapping_add(1).max(1);
11801            ticket = workspace.fence_ticket;
11802            let base = workspace.fence_flags_raw;
11803            // rank1's fence: a peer stream MEMOP is rejected over PCIe P2P
11804            // (CUDA_ERROR_INVALID_VALUE, receipted 2026-08-23), but a peer KERNEL STORE into
11805            // root memory is legal — the direct join already relies on it. Under
11806            // MEMRA_FENCE_RANK1 rank1 rings flag[0] that way and e waits it same-device,
11807            // replacing the cross-device event wait below.
11808            if fence_rank1_on() {
11809                let peer = &self.ranks[1];
11810                let _pmain = peer.gpu.enter_main()?;
11811                peer.ring_flag_raw(base, ticket)?;
11812            }
11813            {
11814                let root = &self.ranks[0];
11815                let _main = root.gpu.enter_main()?;
11816                let r = unsafe {
11817                    sys::cuStreamWriteValue32_v2(
11818                        root.stream().cu_stream() as sys::CUstream,
11819                        (base + 4) as sys::CUdeviceptr,
11820                        ticket,
11821                        0,
11822                    )
11823                };
11824                if r != sys::CUresult::CUDA_SUCCESS {
11825                    return Err(format!("fence write root: {r:?}").into());
11826                }
11827            }
11828        }
11829        // PREJOIN hook: rank work is fully issued (dev1 running); independent e-stream
11830        // kernels queued here execute while the peer rank drains its sweep.
11831        pre_join()?;
11832
11833        if moe_direct_on() && self.ranks.len() == 2 {
11834            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
11835            // rank0's is root-stream-ordered. One root event + rank1's own event order
11836            // the model engine's single add — same operand order as root's add
11837            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
11838            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
11839            // hazard class does not apply).
11840            let _main = e.gpu.enter_main()?;
11841            if memops {
11842                use cudarc::driver::sys;
11843                let base = workspace.fence_flags_raw;
11844                let r = unsafe {
11845                    sys::cuStreamWaitValue32_v2(
11846                        e.stream().cu_stream() as sys::CUstream,
11847                        (base + 4) as sys::CUdeviceptr,
11848                        ticket,
11849                        sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
11850                    )
11851                };
11852                if r != sys::CUresult::CUDA_SUCCESS {
11853                    return Err(format!("fence wait: {r:?}").into());
11854                }
11855                if fence_rank1_on() {
11856                    // Same-device wait on the flag rank1 rang over P2P.
11857                    let r = unsafe {
11858                        sys::cuStreamWaitValue32_v2(
11859                            e.stream().cu_stream() as sys::CUstream,
11860                            base as sys::CUdeviceptr,
11861                            ticket,
11862                            sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
11863                        )
11864                    };
11865                    if r != sys::CUresult::CUDA_SUCCESS {
11866                        return Err(format!("fence wait rank1: {r:?}").into());
11867                    }
11868                } else {
11869                    for ev in workspace.ev_rank.iter().skip(1) {
11870                        e.stream().wait(ev)?;
11871                    }
11872                }
11873            } else {
11874                {
11875                    let root = &self.ranks[0];
11876                    let _rmain = root.gpu.enter_main()?;
11877                    workspace
11878                        .ev_done
11879                        .as_ref()
11880                        .expect("device routes done event")
11881                        .record(&root.stream())?;
11882                }
11883                e.stream().wait(
11884                    workspace
11885                        .ev_done
11886                        .as_ref()
11887                        .expect("device routes done event"),
11888                )?;
11889                for ev in workspace.ev_rank.iter().skip(1) {
11890                    e.stream().wait(ev)?;
11891                }
11892            }
11893            let mut output = e.uninit(experts.input_width)?;
11894            if let Some((sh_raw, scale_raw)) = post_add {
11895                // MOE TAIL FUSION M1: fold the shexp apply into the join add —
11896                // dst = (acc0 + acc1) + sh*scale[0], the exact split-pair sequence.
11897                e.add3_raw(
11898                    &workspace.accumulator[0],
11899                    &workspace.accumulator[1],
11900                    sh_raw,
11901                    scale_raw,
11902                    &mut output,
11903                    experts.input_width,
11904                )?;
11905            } else {
11906                e.add(
11907                    &workspace.accumulator[0],
11908                    &workspace.accumulator[1],
11909                    &mut output,
11910                    experts.input_width,
11911                )?;
11912            }
11913            let output = output;
11914            if let Some(started) = started {
11915                use std::sync::atomic::Ordering;
11916                let ns = TIMING_NS
11917                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
11918                    + started.elapsed().as_nanos() as u64;
11919                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11920                if calls % 430 == 0 {
11921                    eprintln!(
11922                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11923                        ns as f64 / 1.0e6,
11924                        ns as f64 / calls as f64 / 1.0e3,
11925                    );
11926                }
11927            }
11928            return Ok(output);
11929        }
11930        {
11931            let root = &self.ranks[0];
11932            let _main = root.gpu.enter_main()?;
11933            for ev in workspace.ev_rank.iter().skip(1) {
11934                root.stream().wait(ev)?;
11935            }
11936            root.stream()
11937                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
11938            {
11939                let Nvfp4DeviceRoutesWorkspace {
11940                    accumulator,
11941                    remote,
11942                    combined,
11943                    ..
11944                } = &mut *workspace;
11945                root.add(&accumulator[0], remote, combined, experts.input_width)?;
11946            }
11947            workspace
11948                .ev_done
11949                .as_ref()
11950                .expect("device routes done event")
11951                .record(&root.stream())?;
11952        }
11953        let output = {
11954            let _main = e.gpu.enter_main()?;
11955            e.stream().wait(
11956                workspace
11957                    .ev_done
11958                    .as_ref()
11959                    .expect("device routes done event"),
11960            )?;
11961            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
11962            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
11963            let mut output = e.uninit(experts.input_width)?;
11964            e.stream().memcpy_dtod(
11965                &workspace.combined.slice(0..experts.input_width),
11966                &mut output.slice_mut(0..experts.input_width),
11967            )?;
11968            output
11969        };
11970        if let Some(started) = started {
11971            use std::sync::atomic::Ordering;
11972            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
11973                + started.elapsed().as_nanos() as u64;
11974            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11975            if calls % 430 == 0 {
11976                eprintln!(
11977                    "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11978                    ns as f64 / 1.0e6,
11979                    ns as f64 / calls as f64 / 1.0e3,
11980                );
11981            }
11982        }
11983        Ok(output)
11984    }
11985
11986    /// The fused finish's ROOT section (combine + shadow gathers), event-free: the eager
11987    /// caller wraps it with rank-event waits + the done record; the token graph captures it
11988    /// verbatim (parent edges provide the ordering).
11989    pub(crate) fn decode_v2_finish_root_fused(
11990        &self,
11991        ws: &mut StepTpDecodeV2Ws,
11992    ) -> Result<(), Box<dyn std::error::Error>> {
11993        let root = &self.ranks[0];
11994        let _main = root.gpu.enter_main()?;
11995        if ws.raw_peer_partial != 0 {
11996            // Capture-safe raw seams (arming happened in the stage flow).
11997            raw_copy_bytes(ws.raw_peer_partial, ws.raw_o_partial1, ws.o_out * 4, root)?;
11998        } else {
11999            root.stream()
12000                .memcpy_dtod(&ws.o_partials[1][0], &mut ws.peer_partial)?;
12001        }
12002        {
12003            let StepTpDecodeV2Ws {
12004                o_partials,
12005                peer_partial,
12006                reduce_a,
12007                o_out,
12008                ..
12009            } = &mut *ws;
12010            root.add(&o_partials[0][0], peer_partial, reduce_a, *o_out)?;
12011        }
12012        let shadows = !no_local_shadow_on() || ws.raw_mixed_stage_e != 0;
12013        if shadows {
12014            // rank0's shadows are same-context (root) copies; rank1's cross-context reads go
12015            // raw when armed.
12016            let mut k_dst = ws.k_shadow.slice_mut(0..ws.local_kv_dim);
12017            root.stream().memcpy_dtod(&ws.k[0], &mut k_dst)?;
12018            let mut v_dst = ws.v_shadow.slice_mut(0..ws.local_kv_dim);
12019            root.stream().memcpy_dtod(&ws.v_raw[0], &mut v_dst)?;
12020        }
12021        if shadows && ws.raw_peer_partial != 0 {
12022            raw_copy_bytes(
12023                ws.raw_k_shadow + (ws.local_kv_dim * 4) as u64,
12024                ws.raw_k1,
12025                ws.local_kv_dim * 4,
12026                root,
12027            )?;
12028            raw_copy_bytes(
12029                ws.raw_v_shadow + (ws.local_kv_dim * 4) as u64,
12030                ws.raw_v1,
12031                ws.local_kv_dim * 4,
12032                root,
12033            )?;
12034        } else if shadows {
12035            let start = ws.local_kv_dim;
12036            let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
12037            root.stream().memcpy_dtod(&ws.k[1], &mut k_dst)?;
12038            let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
12039            root.stream().memcpy_dtod(&ws.v_raw[1], &mut v_dst)?;
12040        }
12041        if ws.raw_mixed_stage_e != 0 {
12042            // Token-graph mirrors: the e-glue children read same-context copies of the
12043            // root-produced rows.
12044            raw_copy_bytes(ws.raw_mixed_stage_e, ws.raw_reduce_a, ws.o_out * 4, root)?;
12045            let (k_stage, v_stage) = ws.raw_shadow_stage_e;
12046            raw_copy_bytes(k_stage, ws.raw_k_shadow, 2 * ws.local_kv_dim * 4, root)?;
12047            raw_copy_bytes(v_stage, ws.raw_v_shadow, 2 * ws.local_kv_dim * 4, root)?;
12048        }
12049        Ok(())
12050    }
12051
12052    /// Arm the token-graph e-context mirrors (orchestrator-supplied fixed addresses) plus
12053    /// reduce_a's own pointer.
12054    pub(crate) fn decode_v2_arm_token_mirrors(
12055        &self,
12056        ws: &mut StepTpDecodeV2Ws,
12057        mixed_stage_e: u64,
12058        shadow_stage_e: (u64, u64),
12059    ) -> Result<(), Box<dyn std::error::Error>> {
12060        use cudarc::driver::DevicePtr;
12061        let root = &self.ranks[0];
12062        let _main = root.gpu.enter_main()?;
12063        let stream = root.stream();
12064        let (a, _g) = ws.reduce_a.device_ptr(&stream);
12065        ws.raw_reduce_a = a as u64;
12066        ws.raw_mixed_stage_e = mixed_stage_e;
12067        ws.raw_shadow_stage_e = shadow_stage_e;
12068        Ok(())
12069    }
12070
12071    /// Build one layer's stitched routes graph: per-rank children captured on their own
12072    /// streams (raw cuMemcpyAsync at every cross-context seam — cudarc's slice tracking is
12073    /// capture-illegal there), a root combine child, and a multi-device parent with
12074    /// {rank0, rank1} -> root dependency edges. Zero per-token updates: every address the
12075    /// nodes touch is persistent workspace/staging.
12076    fn nvfp4_routes_build_graph(
12077        &self,
12078        experts: &ResidentNvfp4TensorParallel,
12079        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12080        local_out: usize,
12081        n_sel: usize,
12082        activation_limit: Option<f32>,
12083    ) -> Result<RoutesGraph, Box<dyn std::error::Error>> {
12084        use cudarc::driver::DevicePtr;
12085        use cudarc::driver::sys;
12086        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
12087            if r == sys::CUresult::CUDA_SUCCESS {
12088                Ok(())
12089            } else {
12090                Err(format!("{what}: {r:?}").into())
12091            }
12092        }
12093        let world = self.ranks.len();
12094        if world != 2 {
12095            return Err("routes graph door is built for the TP2 pair".into());
12096        }
12097        let width = experts.input_width;
12098
12099        // Raw pointers cached before capture (each read with its owner's stream).
12100        let ptr_f32 = |buf: &crate::CudaSlice<f32>, engine: &Engine| -> u64 {
12101            let stream = engine.stream();
12102            let (ptr, _g) = buf.device_ptr(&stream);
12103            ptr as u64
12104        };
12105        let ptr_i32 = |buf: &crate::CudaSlice<i32>, engine: &Engine| -> u64 {
12106            let stream = engine.stream();
12107            let (ptr, _g) = buf.device_ptr(&stream);
12108            ptr as u64
12109        };
12110        let (sel_e, w_e) = workspace
12111            .dev_route_e
12112            .as_ref()
12113            .expect("device route staging set before graph build");
12114        let root_engine = &self.ranks[0];
12115        let p_in_stage = ptr_f32(
12116            workspace.in_stage_e.as_ref().expect("graph staging"),
12117            root_engine,
12118        );
12119        let p_out_stage = ptr_f32(
12120            workspace.out_stage_e.as_ref().expect("graph staging"),
12121            root_engine,
12122        );
12123        let p_sel_e = ptr_i32(sel_e, root_engine);
12124        let p_w_e = ptr_f32(w_e, root_engine);
12125        let p_input: Vec<u64> = (0..world)
12126            .map(|r| ptr_f32(&workspace.input[r], &self.ranks[r]))
12127            .collect();
12128        let p_sel: Vec<u64> = (0..world)
12129            .map(|r| ptr_i32(&workspace.sel[r], &self.ranks[r]))
12130            .collect();
12131        let p_route_w: Vec<u64> = (0..world)
12132            .map(|r| ptr_f32(&workspace.route_w[r], &self.ranks[r]))
12133            .collect();
12134        let p_acc1 = ptr_f32(&workspace.accumulator[1], &self.ranks[1]);
12135        let p_remote = ptr_f32(&workspace.remote, root_engine);
12136        let p_combined = ptr_f32(&workspace.combined, root_engine);
12137
12138        let raw_copy = |dst: u64,
12139                        src: u64,
12140                        bytes: usize,
12141                        engine: &Engine|
12142         -> Result<(), Box<dyn std::error::Error>> {
12143            unsafe {
12144                cu_try(
12145                    sys::cuMemcpyAsync(
12146                        dst as sys::CUdeviceptr,
12147                        src as sys::CUdeviceptr,
12148                        bytes,
12149                        engine.stream().cu_stream() as sys::CUstream,
12150                    ),
12151                    "routes graph cuMemcpyAsync",
12152                )
12153            }
12154        };
12155
12156        let mut children = Vec::with_capacity(3);
12157        for rank in 0..world {
12158            let engine = &self.ranks[rank];
12159            let _main = engine.gpu.enter_main()?;
12160            let (child, _retained) = engine.capture_graph_retained(|_| {
12161                raw_copy(p_input[rank], p_in_stage, width * 4, engine)?;
12162                raw_copy(p_sel[rank], p_sel_e, n_sel * 4, engine)?;
12163                raw_copy(p_route_w[rank], p_w_e, n_sel * 4, engine)?;
12164                {
12165                    let Nvfp4DeviceRoutesWorkspace {
12166                        input, in_q, in_d, ..
12167                    } = &mut *workspace;
12168                    engine.quantize_q8_1_into(
12169                        &input[rank],
12170                        1,
12171                        width,
12172                        &mut in_q[rank],
12173                        &mut in_d[rank],
12174                    )?;
12175                }
12176                self.nvfp4_routes_batched_sweeps_rank(
12177                    experts,
12178                    workspace,
12179                    &[],
12180                    &[],
12181                    &[],
12182                    local_out,
12183                    n_sel,
12184                    activation_limit,
12185                    true,
12186                    rank,
12187                )?;
12188                Ok(())
12189            })?;
12190            children.push(child);
12191        }
12192        {
12193            let root = &self.ranks[0];
12194            let _main = root.gpu.enter_main()?;
12195            let (child, _retained) = root.capture_graph_retained(|_| {
12196                raw_copy(p_remote, p_acc1, width * 4, root)?;
12197                {
12198                    let Nvfp4DeviceRoutesWorkspace {
12199                        accumulator,
12200                        remote,
12201                        combined,
12202                        ..
12203                    } = &mut *workspace;
12204                    root.add(&accumulator[0], remote, combined, width)?;
12205                }
12206                raw_copy(p_out_stage, p_combined, width * 4, root)?;
12207                Ok(())
12208            })?;
12209            children.push(child);
12210        }
12211
12212        let mut parent: sys::CUgraph = std::ptr::null_mut();
12213        unsafe {
12214            cu_try(sys::cuGraphCreate(&mut parent, 0), "routes cuGraphCreate")?;
12215        }
12216        let mut n0: sys::CUgraphNode = std::ptr::null_mut();
12217        let mut n1: sys::CUgraphNode = std::ptr::null_mut();
12218        let mut n2: sys::CUgraphNode = std::ptr::null_mut();
12219        unsafe {
12220            cu_try(
12221                sys::cuGraphAddChildGraphNode(
12222                    &mut n0,
12223                    parent,
12224                    std::ptr::null(),
12225                    0,
12226                    children[0].cu_graph(),
12227                ),
12228                "routes child r0",
12229            )?;
12230            cu_try(
12231                sys::cuGraphAddChildGraphNode(
12232                    &mut n1,
12233                    parent,
12234                    std::ptr::null(),
12235                    0,
12236                    children[1].cu_graph(),
12237                ),
12238                "routes child r1",
12239            )?;
12240            let deps = [n0, n1];
12241            cu_try(
12242                sys::cuGraphAddChildGraphNode(
12243                    &mut n2,
12244                    parent,
12245                    deps.as_ptr(),
12246                    2,
12247                    children[2].cu_graph(),
12248                ),
12249                "routes child root",
12250            )?;
12251        }
12252        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
12253        unsafe {
12254            cu_try(
12255                sys::cuGraphInstantiateWithFlags(&mut exec, parent, 0),
12256                "routes instantiate",
12257            )?;
12258        }
12259        Ok(RoutesGraph {
12260            exec,
12261            parent,
12262            _children: children,
12263        })
12264    }
12265
12266    /// One rank's routes section for the token graph (event-free): staged input copy (raw
12267    /// when the caller supplies the source pointer), quantize, and the batched sweeps.
12268    /// Eager device_routed wraps it with the entry-event wait.
12269    #[allow(clippy::too_many_arguments)]
12270    pub(crate) fn routes_rank_section(
12271        &self,
12272        experts: &ResidentNvfp4TensorParallel,
12273        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12274        raw_input_src: u64,
12275        local_out: usize,
12276        n_sel: usize,
12277        activation_limit: Option<f32>,
12278        rank_index: usize,
12279    ) -> Result<(), Box<dyn std::error::Error>> {
12280        let engine = &self.ranks[rank_index];
12281        {
12282            let _main = engine.gpu.enter_main()?;
12283            // sel/route_w land via raw copies from the e staging (fixed addresses).
12284            let (sel_e_ptr, w_e_ptr) = workspace
12285                .raw_dev_route_e
12286                .ok_or("routes rank section requires armed staging pointers")?;
12287            raw_copy_bytes(
12288                workspace.raw_input[rank_index],
12289                raw_input_src,
12290                experts.input_width * 4,
12291                engine,
12292            )?;
12293            raw_copy_bytes(workspace.raw_sel[rank_index], sel_e_ptr, n_sel * 4, engine)?;
12294            raw_copy_bytes(
12295                workspace.raw_route_w[rank_index],
12296                w_e_ptr,
12297                n_sel * 4,
12298                engine,
12299            )?;
12300            {
12301                let Nvfp4DeviceRoutesWorkspace {
12302                    input, in_q, in_d, ..
12303                } = &mut *workspace;
12304                engine.quantize_q8_1_into(
12305                    &input[rank_index],
12306                    1,
12307                    experts.input_width,
12308                    &mut in_q[rank_index],
12309                    &mut in_d[rank_index],
12310                )?;
12311            }
12312        }
12313        self.nvfp4_routes_batched_sweeps_rank(
12314            experts,
12315            workspace,
12316            &[],
12317            &[],
12318            &[],
12319            local_out,
12320            n_sel,
12321            activation_limit,
12322            true,
12323            rank_index,
12324        )
12325    }
12326
12327    /// The routes ROOT combine section (event-free): peer accumulator read (raw), canonical
12328    /// add, combined row raw-copied into the fixed e-context out stage.
12329    pub(crate) fn routes_root_section(
12330        &self,
12331        experts: &ResidentNvfp4TensorParallel,
12332        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12333    ) -> Result<(), Box<dyn std::error::Error>> {
12334        let root = &self.ranks[0];
12335        let _main = root.gpu.enter_main()?;
12336        let (acc1_ptr, remote_ptr, combined_ptr, out_stage_ptr) = workspace
12337            .raw_combine
12338            .ok_or("routes root section requires armed combine pointers")?;
12339        raw_copy_bytes(remote_ptr, acc1_ptr, experts.input_width * 4, root)?;
12340        {
12341            let Nvfp4DeviceRoutesWorkspace {
12342                accumulator,
12343                remote,
12344                combined,
12345                ..
12346            } = &mut *workspace;
12347            root.add(&accumulator[0], remote, combined, experts.input_width)?;
12348        }
12349        raw_copy_bytes(out_stage_ptr, combined_ptr, experts.input_width * 4, root)?;
12350        Ok(())
12351    }
12352
12353    /// Arm the routes raw pointers (once): staging pair, per-rank input/sel/route_w, and the
12354    /// combine set. Requires dev_route_e + in/out stages already allocated.
12355    pub(crate) fn routes_arm_raw(
12356        &self,
12357        experts: &ResidentNvfp4TensorParallel,
12358        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12359    ) -> Result<(), Box<dyn std::error::Error>> {
12360        use cudarc::driver::DevicePtr;
12361        if workspace.raw_dev_route_e.is_some() {
12362            return Ok(());
12363        }
12364        let _ = experts;
12365        let (sel_e, w_e) = workspace
12366            .dev_route_e
12367            .as_ref()
12368            .ok_or("routes staging not armed")?;
12369        let root = &self.ranks[0];
12370        {
12371            let _main = root.gpu.enter_main()?;
12372            let stream = root.stream();
12373            let (a, _g) = sel_e.device_ptr(&stream);
12374            let (b, _g) = w_e.device_ptr(&stream);
12375            workspace.raw_dev_route_e = Some((a as u64, b as u64));
12376            let (c, _g) = workspace.accumulator[1].device_ptr(&stream);
12377            let (d, _g) = workspace.remote.device_ptr(&stream);
12378            let (f, _g) = workspace.combined.device_ptr(&stream);
12379            let out_stage = workspace
12380                .out_stage_e
12381                .as_ref()
12382                .ok_or("routes out stage not armed")?;
12383            let (g_, _g) = out_stage.device_ptr(&stream);
12384            workspace.raw_combine = Some((c as u64, d as u64, f as u64, g_ as u64));
12385        }
12386        for rank in 0..self.ranks.len() {
12387            let engine = &self.ranks[rank];
12388            let _main = engine.gpu.enter_main()?;
12389            let stream = engine.stream();
12390            let (a, _g) = workspace.input[rank].device_ptr(&stream);
12391            let (b, _g) = workspace.sel[rank].device_ptr(&stream);
12392            let (c, _g) = workspace.route_w[rank].device_ptr(&stream);
12393            workspace.raw_input.push(a as u64);
12394            workspace.raw_sel.push(b as u64);
12395            workspace.raw_route_w.push(c as u64);
12396        }
12397        Ok(())
12398    }
12399
12400    /// Routed NVFP4 expert program, host-canonical transport. Native/bulk P2P transport for the
12401    /// NVFP4 bank is a separate increment; this entry point is exactness-first and reports no
12402    /// throughput claim.
12403    pub fn run_tensor_parallel_routes_nvfp4(
12404        &self,
12405        experts: &ResidentNvfp4TensorParallel,
12406        input: &[f32],
12407        tokens: usize,
12408        selected: &[usize],
12409        route_weights: &[f32],
12410        experts_per_token: usize,
12411        activation_limit: Option<f32>,
12412    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
12413        validate_activations(input, tokens, experts.input_width)?;
12414        let pairs = tokens
12415            .checked_mul(experts_per_token)
12416            .ok_or("NVFP4 TP route count overflow")?;
12417        if selected.len() != pairs || route_weights.len() != pairs {
12418            return Err(format!(
12419                "NVFP4 TP routes selected={} weights={} != tokens {tokens} x experts/token \
12420                 {experts_per_token} ({pairs})",
12421                selected.len(),
12422                route_weights.len(),
12423            )
12424            .into());
12425        }
12426        if !route_weights.iter().all(|weight| weight.is_finite()) {
12427            return Err("NVFP4 TP route weights contain a non-finite value".into());
12428        }
12429
12430        let mut output = vec![0.0f32; tokens * experts.input_width];
12431        for token in 0..tokens {
12432            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
12433            for slot in 0..experts_per_token {
12434                let pair = token * experts_per_token + slot;
12435                let expert = selected[pair];
12436                if expert >= experts.expert_count {
12437                    return Err(format!(
12438                        "NVFP4 TP selected expert {expert} outside 0..{}",
12439                        experts.expert_count
12440                    )
12441                    .into());
12442                }
12443                // EP2 banks hold the WHOLE expert on rank (expert & 1) at slot (expert >> 1);
12444                // per-row dots are the same full-width program either way (a column shard
12445                // splits ROWS, not the dot), so gate/up are bit-equal across layouts. Only
12446                // down's parenthesization moves (full-width dot vs canonical 2-shard sum) —
12447                // the numeric-class this door declares.
12448                let gate = if experts.ep2 {
12449                    self.run_full_bank_expert_nvfp4(
12450                        &experts.gate,
12451                        &experts.macros_gate,
12452                        expert,
12453                        input_row,
12454                    )?
12455                } else {
12456                    self.run_column_bank_expert_nvfp4(
12457                        &experts.gate,
12458                        &experts.macros_gate,
12459                        expert,
12460                        input_row,
12461                    )?
12462                };
12463                let up = if experts.ep2 {
12464                    self.run_full_bank_expert_nvfp4(
12465                        &experts.up,
12466                        &experts.macros_up,
12467                        expert,
12468                        input_row,
12469                    )?
12470                } else {
12471                    self.run_column_bank_expert_nvfp4(
12472                        &experts.up,
12473                        &experts.macros_up,
12474                        expert,
12475                        input_row,
12476                    )?
12477                };
12478                let activated: Vec<f32> = gate
12479                    .iter()
12480                    .zip(&up)
12481                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
12482                    .collect();
12483                debug_assert_eq!(activated.len(), experts.expert_width);
12484                let down = if experts.ep2 {
12485                    self.run_full_down_expert_nvfp4(
12486                        &experts.down,
12487                        &experts.macros_down,
12488                        expert,
12489                        &activated,
12490                    )?
12491                } else {
12492                    self.run_row_bank_expert_nvfp4(
12493                        &experts.down,
12494                        &experts.macros_down,
12495                        expert,
12496                        &activated,
12497                    )?
12498                };
12499                let weight = route_weights[pair];
12500                for (sum, value) in output
12501                    [token * experts.input_width..(token + 1) * experts.input_width]
12502                    .iter_mut()
12503                    .zip(down)
12504                {
12505                    *sum += weight * value;
12506                }
12507            }
12508        }
12509        Ok(output)
12510    }
12511}
12512
12513#[cfg(test)]
12514mod tests {
12515    use super::*;
12516
12517    #[test]
12518    fn step_expert_activation_clamps_each_arm_by_the_official_contract() {
12519        let limit = Some(7.0);
12520        assert_eq!(step_expert_activation_host(20.0, 9.0, limit), 49.0);
12521        assert_eq!(step_expert_activation_host(20.0, -9.0, limit), -49.0);
12522        assert!(
12523            step_expert_activation_host(-20.0, 9.0, limit).abs()
12524                < step_expert_activation_host(-20.0, 9.0, None).abs()
12525        );
12526        assert!(validate_step_expert_activation_limit(Some(f32::NAN)).is_err());
12527        assert!(validate_step_expert_activation_limit(Some(0.0)).is_err());
12528        assert!(validate_step_expert_activation_limit(limit).is_ok());
12529    }
12530
12531    #[test]
12532    fn moe_residual_host_preserves_official_add_order() {
12533        let output = moe_residual_host(&[1.0e20], &[-1.0e20], &[1.0]).unwrap();
12534        assert_eq!(output, [0.0]);
12535        assert_eq!(
12536            moe_residual_host(&[0.0], &[0.0, 1.0], &[0.0]).unwrap_err(),
12537            "MoE residual lengths residual=1 routed=2 shared=1"
12538        );
12539    }
12540
12541    #[test]
12542    fn expert_owner_routes_preserve_global_pair_order_with_local_expert_ids() {
12543        let selected = [0, 36, 72, 108, 144, 180, 216, 252];
12544        let owners = partition_expert_owner_routes(288, 4, 1, 8, &selected).unwrap();
12545        assert_eq!(owners.len(), 4);
12546        for (rank, owner) in owners.iter().enumerate() {
12547            assert_eq!(owner.rank, rank);
12548            assert_eq!(owner.selected, vec![0, 36]);
12549            assert_eq!(owner.token_rows, vec![0, 0]);
12550            assert_eq!(owner.global_pairs, vec![rank * 2, rank * 2 + 1]);
12551        }
12552    }
12553
12554    #[test]
12555    fn expert_owner_routes_validate_geometry_and_selected_experts() {
12556        assert!(partition_expert_owner_routes(288, 5, 1, 8, &[0; 8]).is_err());
12557        assert!(partition_expert_owner_routes(288, 4, 2, 8, &[0; 8]).is_err());
12558        let error = partition_expert_owner_routes(288, 4, 1, 8, &[288; 8]).unwrap_err();
12559        assert!(error.contains("outside 0..288"));
12560    }
12561
12562    #[test]
12563    fn step_grouped_owner_routes_validate_dynamic_top8_shapes() {
12564        let selected = [
12565            1, 73, 80, 145, 152, 159, 217, 224, 12, 84, 91, 156, 163, 170, 228, 235,
12566        ];
12567        assert_eq!(
12568            validate_step_grouped_owner_routes(288, 2, &selected).unwrap(),
12569            16
12570        );
12571        let owners = partition_expert_owner_routes(288, 4, 2, 8, &selected).unwrap();
12572        assert_eq!(
12573            owners
12574                .iter()
12575                .map(|owner| owner.selected.len())
12576                .collect::<Vec<_>>(),
12577            vec![2, 4, 6, 4]
12578        );
12579        assert!(validate_step_grouped_owner_routes(288, 2, &selected[..8]).is_err());
12580        assert!(validate_step_grouped_owner_routes(288, 1, &[0; 8]).is_err());
12581        assert!(validate_step_grouped_owner_routes(287, 2, &selected).is_err());
12582    }
12583
12584    #[test]
12585    fn weighted_route_combine_requires_a_canonical_pair_permutation() {
12586        let owner0 = [0usize, 3];
12587        let owner1 = [1usize, 2];
12588        let owners = [owner0.as_slice(), owner1.as_slice()];
12589        assert_eq!(
12590            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, 0.2, 0.3, 0.4],)
12591                .unwrap(),
12592            WeightedRouteCombineShape {
12593                pairs: 4,
12594                max_pairs: 12,
12595            }
12596        );
12597        let duplicate = [owner0.as_slice(), &[1usize, 1][..]];
12598        assert!(
12599            validate_weighted_route_combine(4096, 4, 3, 1, &duplicate, &[0.1, 0.2, 0.3, 0.4],)
12600                .is_err()
12601        );
12602        assert!(
12603            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, f32::NAN, 0.3, 0.4],)
12604                .is_err()
12605        );
12606        assert!(
12607            validate_weighted_route_combine(4096, 4, 1, 2, &owners, &[0.1, 0.2, 0.3, 0.4],)
12608                .is_err()
12609        );
12610    }
12611
12612    #[test]
12613    fn native_p2p_door_is_strict_and_default_off() {
12614        assert!(!parse_step_tp_native_p2p(None).unwrap());
12615        assert!(!parse_step_tp_native_p2p(Some("")).unwrap());
12616        assert!(!parse_step_tp_native_p2p(Some("0")).unwrap());
12617        assert!(parse_step_tp_native_p2p(Some("1")).unwrap());
12618        assert!(parse_step_tp_native_p2p(Some("true")).is_err());
12619        assert!(parse_step_tp_native_p2p(Some("2")).is_err());
12620    }
12621
12622    #[test]
12623    fn bulk_p2p_door_is_strict_and_default_off() {
12624        assert!(!parse_step_tp_bulk_p2p(None).unwrap());
12625        assert!(!parse_step_tp_bulk_p2p(Some("")).unwrap());
12626        assert!(!parse_step_tp_bulk_p2p(Some("0")).unwrap());
12627        assert!(parse_step_tp_bulk_p2p(Some("1")).unwrap());
12628        assert!(parse_step_tp_bulk_p2p(Some("true")).is_err());
12629        assert!(parse_step_tp_bulk_p2p(Some("2")).is_err());
12630    }
12631
12632    #[test]
12633    fn ep_device_arithmetic_door_is_strict_and_default_off() {
12634        assert!(!parse_step_ep_device_arithmetic(None).unwrap());
12635        assert!(!parse_step_ep_device_arithmetic(Some("")).unwrap());
12636        assert!(!parse_step_ep_device_arithmetic(Some("0")).unwrap());
12637        assert!(parse_step_ep_device_arithmetic(Some("1")).unwrap());
12638        assert!(parse_step_ep_device_arithmetic(Some("true")).is_err());
12639        assert!(parse_step_ep_device_arithmetic(Some("2")).is_err());
12640    }
12641
12642    #[test]
12643    fn f32_mirror_door_is_strict_and_default_off() {
12644        assert!(!parse_step_tp_f32_mirror(None).unwrap());
12645        assert!(!parse_step_tp_f32_mirror(Some("")).unwrap());
12646        assert!(!parse_step_tp_f32_mirror(Some("0")).unwrap());
12647        assert!(parse_step_tp_f32_mirror(Some("1")).unwrap());
12648        assert!(parse_step_tp_f32_mirror(Some("true")).is_err());
12649        assert!(parse_step_tp_f32_mirror(Some("2")).is_err());
12650    }
12651
12652    fn matrix(out_features: usize, in_features: usize) -> (Vec<u8>, Vec<f32>) {
12653        let codes = (0..out_features * in_features)
12654            .map(|index| (index % 251) as u8)
12655            .collect();
12656        let scales = (0..out_features.div_ceil(FP8_BLOCK) * in_features.div_ceil(FP8_BLOCK))
12657            .map(|index| index as f32 + 1.0)
12658            .collect();
12659        (codes, scales)
12660    }
12661
12662    fn bf16_matrix_bytes(out_features: usize, in_features: usize) -> Vec<u8> {
12663        (0..out_features * in_features)
12664            .flat_map(|value| (value as u16).to_le_bytes())
12665            .collect()
12666    }
12667
12668    fn decode_u16(bytes: &[u8]) -> Vec<u16> {
12669        bytes
12670            .chunks_exact(2)
12671            .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
12672            .collect()
12673    }
12674
12675    #[test]
12676    fn bf16_matrix_rejects_wrong_byte_count() {
12677        let bytes = vec![0u8; 4 * 4 * 2 - 1];
12678        let matrix = Bf16Matrix {
12679            bytes: &bytes,
12680            out_features: 4,
12681            in_features: 4,
12682        };
12683        assert!(matrix.validate().unwrap_err().contains("4x4x2"));
12684    }
12685
12686    #[test]
12687    fn replicated_device_rows_require_exact_rank_local_shapes() {
12688        assert_eq!(
12689            replicated_device_row_values(3, 4096, 4, &[12_288; 4]).unwrap(),
12690            12_288
12691        );
12692        assert!(replicated_device_row_values(0, 4096, 4, &[0; 4]).is_err());
12693        assert!(replicated_device_row_values(3, 0, 4, &[0; 4]).is_err());
12694        assert!(replicated_device_row_values(3, 4096, 4, &[12_288; 3]).is_err());
12695        assert!(
12696            replicated_device_row_values(3, 4096, 4, &[12_288, 12_288, 12_287, 12_288]).is_err()
12697        );
12698        assert!(replicated_device_row_values(usize::MAX, 2, 1, &[0]).is_err());
12699    }
12700
12701    #[test]
12702    fn replicated_device_row_refresh_requires_exact_root_source() {
12703        assert_eq!(
12704            replicated_device_row_source_values(1, 12_288, 12_288, 3, 3).unwrap(),
12705            12_288
12706        );
12707        assert!(replicated_device_row_source_values(0, 12_288, 0, 3, 3).is_err());
12708        assert!(replicated_device_row_source_values(1, 0, 0, 3, 3).is_err());
12709        assert!(replicated_device_row_source_values(1, 12_288, 12_287, 3, 3).is_err());
12710        assert!(replicated_device_row_source_values(1, 12_288, 12_288, 2, 3).is_err());
12711        assert!(replicated_device_row_source_values(usize::MAX, 2, 0, 3, 3).is_err());
12712    }
12713
12714    #[test]
12715    fn step_bf16_canonical_rows_are_topology_invariant_through_tp8() {
12716        for tp in [1, 2, 4, 8] {
12717            assert_eq!(step_bf16_canonical_chunk_rows(8_192, tp).unwrap(), 1_024);
12718            assert_eq!(step_bf16_canonical_chunk_rows(12_288, tp).unwrap(), 1_536);
12719            assert_eq!(step_bf16_canonical_chunk_rows(1_024, tp).unwrap(), 128);
12720            assert_eq!(step_bf16_canonical_chunk_cols(8_192, tp).unwrap(), 1_024);
12721            assert_eq!(step_bf16_canonical_chunk_cols(12_288, tp).unwrap(), 1_536);
12722        }
12723        assert!(step_bf16_canonical_chunk_rows(12_288, 3).is_err());
12724        assert!(step_bf16_canonical_chunk_rows(1_001, 2).is_err());
12725        assert!(step_bf16_canonical_chunk_cols(12_288, 3).is_err());
12726        assert!(step_bf16_canonical_chunk_cols(1_001, 2).is_err());
12727    }
12728
12729    #[test]
12730    fn cache_rows_split_by_token_then_rank() {
12731        let rows = (0u8..24).collect::<Vec<_>>();
12732        assert_eq!(
12733            cache_rank_rows(&rows, 3, 4, 2, 0).unwrap(),
12734            vec![0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19]
12735        );
12736        assert_eq!(
12737            cache_rank_rows(&rows, 3, 4, 2, 1).unwrap(),
12738            vec![4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23]
12739        );
12740        assert!(cache_rank_rows(&rows[..23], 3, 4, 2, 0).is_err());
12741        assert!(cache_rank_rows(&rows, 3, 4, 2, 2).is_err());
12742    }
12743
12744    #[test]
12745    fn bf16_column_shard_preserves_contiguous_output_rows() {
12746        let bytes = bf16_matrix_bytes(4, 4);
12747        let matrix = Bf16Matrix {
12748            bytes: &bytes,
12749            out_features: 4,
12750            in_features: 4,
12751        };
12752        let shard = bf16_column_shard(matrix, 2, 1).unwrap();
12753        assert_eq!(shard.out_features, 2);
12754        assert_eq!(shard.in_features, 4);
12755        assert_eq!(decode_u16(shard.bytes), (8..16).collect::<Vec<_>>());
12756    }
12757
12758    #[test]
12759    fn bf16_row_shard_preserves_each_input_column_window() {
12760        let bytes = bf16_matrix_bytes(3, 4);
12761        let matrix = Bf16Matrix {
12762            bytes: &bytes,
12763            out_features: 3,
12764            in_features: 4,
12765        };
12766        let shard = bf16_row_shard(matrix, 2, 1).unwrap();
12767        assert_eq!(decode_u16(&shard), vec![2, 3, 6, 7, 10, 11]);
12768    }
12769
12770    #[test]
12771    fn bf16_row_block_preserves_global_column_order() {
12772        let bytes = bf16_matrix_bytes(3, 8);
12773        let matrix = Bf16Matrix {
12774            bytes: &bytes,
12775            out_features: 3,
12776            in_features: 8,
12777        };
12778        let block = bf16_row_block(matrix, 2, 3).unwrap();
12779        assert_eq!(decode_u16(&block), vec![2, 3, 4, 10, 11, 12, 18, 19, 20]);
12780    }
12781
12782    #[test]
12783    fn column_shard_preserves_contiguous_weight_and_scale_rows() {
12784        let (codes, scales) = matrix(1280, 4096);
12785        let matrix = E4m3BlockMatrix {
12786            codes: &codes,
12787            scales: &scales,
12788            out_features: 1280,
12789            in_features: 4096,
12790        };
12791        let shard = column_shard(matrix, 2, 1).unwrap();
12792        assert_eq!(shard.out_features, 640);
12793        assert_eq!(shard.codes, &codes[640 * 4096..]);
12794        assert_eq!(shard.scales, &scales[5 * 32..]);
12795    }
12796
12797    #[test]
12798    fn row_shard_preserves_each_weight_and_scale_column_window() {
12799        let (codes, scales) = matrix(4096, 1280);
12800        let matrix = E4m3BlockMatrix {
12801            codes: &codes,
12802            scales: &scales,
12803            out_features: 4096,
12804            in_features: 1280,
12805        };
12806        let (shard_codes, shard_scales) = row_shard(matrix, 2, 1).unwrap();
12807        assert_eq!(shard_codes.len(), 4096 * 640);
12808        assert_eq!(&shard_codes[..640], &codes[640..1280]);
12809        assert_eq!(&shard_codes[640..1280], &codes[1280 + 640..2560]);
12810        assert_eq!(shard_scales.len(), 32 * 5);
12811        assert_eq!(&shard_scales[..5], &scales[5..10]);
12812        assert_eq!(&shard_scales[5..10], &scales[15..20]);
12813    }
12814
12815    #[test]
12816    fn activation_shards_keep_token_rows_separate() {
12817        let activations: Vec<f32> = (0..2 * 8).map(|value| value as f32).collect();
12818        assert_eq!(
12819            activation_shard(&activations, 2, 8, 2, 1),
12820            vec![4.0, 5.0, 6.0, 7.0, 12.0, 13.0, 14.0, 15.0],
12821        );
12822    }
12823
12824    #[test]
12825    fn expert_bank_selects_expert_major_code_and_scale_planes() {
12826        let expert_count = 2;
12827        let out_features = 128;
12828        let in_features = 128;
12829        let code_stride = out_features * in_features;
12830        let codes: Vec<u8> = (0..expert_count * code_stride)
12831            .map(|index| (index % 251) as u8)
12832            .collect();
12833        let scales = vec![1.0f32, 2.0];
12834        let bank = E4m3ExpertBank {
12835            codes: &codes,
12836            scales: &scales,
12837            expert_count,
12838            out_features,
12839            in_features,
12840        };
12841        bank.validate().unwrap();
12842        let expert = bank.expert(1).unwrap();
12843        assert_eq!(expert.codes, &codes[code_stride..]);
12844        assert_eq!(expert.scales, &[2.0]);
12845    }
12846
12847    #[test]
12848    fn expert_bank_rejects_non_positive_scale() {
12849        let codes = vec![0u8; 128 * 128];
12850        let scales = vec![0.0f32];
12851        let bank = E4m3ExpertBank {
12852            codes: &codes,
12853            scales: &scales,
12854            expert_count: 1,
12855            out_features: 128,
12856            in_features: 128,
12857        };
12858        assert!(bank.validate().unwrap_err().contains("non-positive"));
12859    }
12860
12861    #[test]
12862    fn tensor_parallel_column_bank_keeps_each_expert_scale_plane_separate() {
12863        let expert_count = 2;
12864        let out_features = 256;
12865        let in_features = 128;
12866        let code_stride = out_features * in_features;
12867        let scale_stride = 2;
12868        let codes = (0..expert_count * code_stride)
12869            .map(|index| (index % 251) as u8)
12870            .collect::<Vec<_>>();
12871        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
12872        let bank = E4m3ExpertBank {
12873            codes: &codes,
12874            scales: &scales,
12875            expert_count,
12876            out_features,
12877            in_features,
12878        };
12879
12880        let rank = pack_column_bank_rank(bank, 2, 1).unwrap();
12881        assert_eq!(rank.out_features, 128);
12882        assert_eq!(rank.in_features, 128);
12883        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
12884        assert_eq!(rank.scales, vec![11.0, 21.0]);
12885        assert_eq!(&rank.codes[..128 * 128], &codes[128 * 128..256 * 128]);
12886        assert_eq!(
12887            &rank.codes[128 * 128..],
12888            &codes[code_stride + 128 * 128..2 * code_stride]
12889        );
12890        assert_eq!(scale_stride, scales.len() / expert_count);
12891    }
12892
12893    #[test]
12894    fn tensor_parallel_row_bank_keeps_each_expert_scale_plane_separate() {
12895        let expert_count = 2;
12896        let out_features = 128;
12897        let in_features = 256;
12898        let code_stride = out_features * in_features;
12899        let codes = (0..expert_count * code_stride)
12900            .map(|index| (index % 251) as u8)
12901            .collect::<Vec<_>>();
12902        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
12903        let bank = E4m3ExpertBank {
12904            codes: &codes,
12905            scales: &scales,
12906            expert_count,
12907            out_features,
12908            in_features,
12909        };
12910
12911        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
12912        assert_eq!(rank.out_features, 128);
12913        assert_eq!(rank.in_features, 128);
12914        assert_eq!(rank.k_blocks, Some(1));
12915        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
12916        assert_eq!(rank.scales, vec![11.0, 21.0]);
12917        assert_eq!(&rank.codes[..128], &codes[128..256]);
12918        assert_eq!(
12919            &rank.codes[128 * 128..128 * 128 + 128],
12920            &codes[code_stride + 128..code_stride + 256]
12921        );
12922    }
12923
12924    #[test]
12925    fn tensor_parallel_row_bank_preserves_global_k_block_order() {
12926        let expert_count = 2;
12927        let out_features = 256;
12928        let in_features = 512;
12929        let code_stride = out_features * in_features;
12930        let mut codes = vec![0u8; expert_count * code_stride];
12931        for expert in 0..expert_count {
12932            for row in 0..out_features {
12933                for block in 0..4 {
12934                    let value = (expert * 80 + block * 16 + row % 16) as u8;
12935                    let start = expert * code_stride + row * in_features + block * FP8_BLOCK;
12936                    codes[start..start + FP8_BLOCK].fill(value);
12937                }
12938            }
12939        }
12940        let scales = vec![
12941            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,
12942            112.0, 113.0, 114.0,
12943        ];
12944        let bank = E4m3ExpertBank {
12945            codes: &codes,
12946            scales: &scales,
12947            expert_count,
12948            out_features,
12949            in_features,
12950        };
12951
12952        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
12953        assert_eq!(rank.out_features, out_features);
12954        assert_eq!(rank.in_features, 256);
12955        assert_eq!(rank.k_blocks, Some(2));
12956        assert_eq!(rank.code_stride, out_features * 256);
12957        assert_eq!(rank.scale_stride, 4);
12958        assert_eq!(&rank.scales[..4], &[3.0, 13.0, 4.0, 14.0]);
12959        assert_eq!(&rank.scales[4..], &[103.0, 113.0, 104.0, 114.0]);
12960
12961        let block_stride = out_features * FP8_BLOCK;
12962        assert!(rank.codes[..FP8_BLOCK].iter().all(|&code| code == 32));
12963        assert!(
12964            rank.codes[block_stride..block_stride + FP8_BLOCK]
12965                .iter()
12966                .all(|&code| code == 48)
12967        );
12968        assert!(
12969            rank.codes[rank.code_stride..rank.code_stride + FP8_BLOCK]
12970                .iter()
12971                .all(|&code| code == 112)
12972        );
12973        assert!(
12974            rank.codes
12975                [rank.code_stride + block_stride..rank.code_stride + block_stride + FP8_BLOCK]
12976                .iter()
12977                .all(|&code| code == 128)
12978        );
12979    }
12980
12981    #[test]
12982    fn step_ep_layer_specs_are_literal_and_fail_closed() {
12983        assert!(parse_step_ep_layer_specs(None).unwrap().is_empty());
12984        assert!(parse_step_ep_layer_specs(Some("0")).unwrap().is_empty());
12985        assert_eq!(
12986            parse_step_ep_layer_specs(Some("24@1,2")).unwrap(),
12987            vec![StepEpLayerSpec {
12988                layer: 24,
12989                devices: vec![1, 2],
12990            }]
12991        );
12992        assert_eq!(
12993            parse_step_ep_layer_specs(Some("24-25@1,2;31@0,2")).unwrap(),
12994            vec![
12995                StepEpLayerSpec {
12996                    layer: 24,
12997                    devices: vec![1, 2],
12998                },
12999                StepEpLayerSpec {
13000                    layer: 25,
13001                    devices: vec![1, 2],
13002                },
13003                StepEpLayerSpec {
13004                    layer: 31,
13005                    devices: vec![0, 2],
13006                },
13007            ]
13008        );
13009        assert!(parse_step_ep_layer_specs(Some("24@1")).is_err());
13010        assert!(parse_step_ep_layer_specs(Some("24@1,1")).is_err());
13011        assert!(parse_step_ep_layer_specs(Some("layer@1,2")).is_err());
13012        assert!(parse_step_ep_layer_specs(Some("25-24@1,2")).is_err());
13013        assert!(parse_step_ep_layer_specs(Some("0-128@1,2")).is_err());
13014        assert!(parse_step_ep_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
13015        assert!(parse_step_ep_layer_specs(Some("all@0,1")).is_err());
13016    }
13017
13018    #[test]
13019    fn step_tp_layer_specs_share_the_fail_closed_layer_contract() {
13020        assert!(parse_step_tp_layer_specs(None).unwrap().is_empty());
13021        assert!(parse_step_tp_layer_specs(Some("0")).unwrap().is_empty());
13022        assert_eq!(
13023            parse_step_tp_layer_specs(Some("24-25@1,2")).unwrap(),
13024            vec![
13025                StepTpLayerSpec {
13026                    layer: 24,
13027                    devices: vec![1, 2],
13028                },
13029                StepTpLayerSpec {
13030                    layer: 25,
13031                    devices: vec![1, 2],
13032                },
13033            ]
13034        );
13035        let error = parse_step_tp_layer_specs(Some("24@1")).unwrap_err();
13036        assert!(error.contains("MEMRA_STEP_TP"));
13037        assert!(parse_step_tp_layer_specs(Some("24@1,1")).is_err());
13038        assert!(parse_step_tp_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
13039
13040        let all = parse_step_tp_layer_specs(Some("all@0,1,2,3,4,5,6,7")).unwrap();
13041        assert_eq!(all.len(), STEP37_TRUNK_LAYERS);
13042        assert_eq!(all.first().unwrap().layer, 0);
13043        assert_eq!(all.last().unwrap().layer, STEP37_TRUNK_LAYERS - 1);
13044        let devices = (0..8).collect::<Vec<_>>();
13045        assert!(all.iter().all(|spec| spec.devices == devices));
13046        assert!(parse_step_tp_layer_specs(Some("all@0,1;44@0,1")).is_err());
13047    }
13048}
13049
13050// ===== Whole-token graph builder (increment B) ==================================================
13051//
13052// The decode fns are already sectioned at every e/rank/root seam (the stage flow, sweeps_rank,
13053// finish splits, the dcw arm). `graph_section` is the one annotation those seams call: eager
13054// mode runs the closure verbatim; build mode wraps it in a stream capture on the section's
13055// device and records a child + its dependency edges. A token then assembles as ONE multi-device
13056// parent (children per section per layer), launched once per token — the launch-collapse the
13057// per-layer minis could not reach (routes-mini negative, 2026-08-21).
13058
13059/// One captured section: the child graph plus which parent node it became, and the CUDA
13060/// context it was captured under (exec memset updates need it).
13061struct TokenGraphChild {
13062    graph: cudarc::driver::CudaGraph,
13063    node: cudarc::driver::sys::CUgraphNode,
13064    ctx: cudarc::driver::sys::CUcontext,
13065}
13066
13067/// Exec-updatable fa geometry discovered in one attention rank child: the three partial-pool
13068/// memsets, the dcw fa kernel, and its combine — everything a bucket change touches. Node
13069/// handles address the parent's CLONED child graphs (the M1-probed update path).
13070struct TokenGraphFaSite {
13071    ctx: cudarc::driver::sys::CUcontext,
13072    memset_o: cudarc::driver::sys::CUgraphNode,
13073    memset_m: [cudarc::driver::sys::CUgraphNode; 2],
13074    fa: cudarc::driver::sys::CUgraphNode,
13075    combine: cudarc::driver::sys::CUgraphNode,
13076    window: usize,
13077    n_head: usize,
13078    n_head_kv: usize,
13079    head_dim: usize,
13080}
13081
13082pub struct TokenGraphBuilder {
13083    parent: cudarc::driver::sys::CUgraph,
13084    children: Vec<TokenGraphChild>,
13085    /// Nodes every NEXT section must depend on (the frontier): one node for serial flow,
13086    /// several while a parallel group is open.
13087    frontier: Vec<cudarc::driver::sys::CUgraphNode>,
13088    /// Detached sections: forked from the frontier at issue time, joined ONLY by the next
13089    /// non-group section (they never gate a parallel group merge — the SH1 shape).
13090    pending_detached: Vec<cudarc::driver::sys::CUgraphNode>,
13091    /// Open parallel group: sections issued under the same group id fork from the SAME
13092    /// predecessor set and merge into the frontier together when the group closes.
13093    group: Option<(
13094        u32,
13095        Vec<cudarc::driver::sys::CUgraphNode>,
13096        Vec<cudarc::driver::sys::CUgraphNode>,
13097    )>,
13098}
13099
13100// SAFETY: single decode thread; graph handles are process handles.
13101unsafe impl Send for TokenGraphBuilder {}
13102
13103impl TokenGraphBuilder {
13104    pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
13105        use cudarc::driver::sys;
13106        let mut parent: sys::CUgraph = std::ptr::null_mut();
13107        let r = unsafe { sys::cuGraphCreate(&mut parent, 0) };
13108        if r != sys::CUresult::CUDA_SUCCESS {
13109            return Err(format!("token graph create: {r:?}").into());
13110        }
13111        Ok(Self {
13112            parent,
13113            children: Vec::new(),
13114            frontier: Vec::new(),
13115            pending_detached: Vec::new(),
13116            group: None,
13117        })
13118    }
13119
13120    fn push_child(
13121        &mut self,
13122        graph: cudarc::driver::CudaGraph,
13123        parallel_group: Option<u32>,
13124        detached: bool,
13125        absorb: bool,
13126        ctx: cudarc::driver::sys::CUcontext,
13127    ) -> Result<(), Box<dyn std::error::Error>> {
13128        use cudarc::driver::sys;
13129        // Resolve the dependency set: serial sections depend on the current frontier; a
13130        // parallel-group section depends on the frontier AS OF the group opening; a
13131        // DETACHED section forks like a group member but joins only the next serial section.
13132        let deps: Vec<sys::CUgraphNode> = match (&mut self.group, parallel_group) {
13133            (Some((open, base, _)), Some(group)) if *open == group => base.clone(),
13134            (state, Some(group)) => {
13135                // opening a new group (closing any previous one first)
13136                if let Some((_, _, members)) = state.take() {
13137                    self.frontier = members;
13138                }
13139                let base = self.frontier.clone();
13140                *state = Some((group, base.clone(), Vec::new()));
13141                base
13142            }
13143            (state, None) if detached => match state.as_ref() {
13144                Some((_, base, _)) => base.clone(),
13145                None => self.frontier.clone(),
13146            },
13147            (state, None) => {
13148                if let Some((_, _, members)) = state.take() {
13149                    self.frontier = members;
13150                }
13151                let mut deps = self.frontier.clone();
13152                if absorb {
13153                    deps.append(&mut self.pending_detached);
13154                }
13155                deps
13156            }
13157        };
13158        let mut node: sys::CUgraphNode = std::ptr::null_mut();
13159        let r = unsafe {
13160            sys::cuGraphAddChildGraphNode(
13161                &mut node,
13162                self.parent,
13163                if deps.is_empty() {
13164                    std::ptr::null()
13165                } else {
13166                    deps.as_ptr()
13167                },
13168                deps.len(),
13169                graph.cu_graph(),
13170            )
13171        };
13172        if r != sys::CUresult::CUDA_SUCCESS {
13173            return Err(format!("token graph child: {r:?}").into());
13174        }
13175        match (&mut self.group, parallel_group, detached) {
13176            (_, None, true) => self.pending_detached.push(node),
13177            (Some((_, _, members)), Some(_), _) => members.push(node),
13178            _ => self.frontier = vec![node],
13179        }
13180        self.children.push(TokenGraphChild { graph, node, ctx });
13181        Ok(())
13182    }
13183
13184    pub fn finish(mut self) -> Result<TokenGraph, Box<dyn std::error::Error>> {
13185        use cudarc::driver::sys;
13186        if let Some((_, _, members)) = self.group.take() {
13187            self.frontier = members;
13188        }
13189        // Discover the fa sites BEFORE instantiate: the parent's cloned child graphs hold
13190        // the node handles the exec update path (M1) addresses.
13191        let mut fa_sites = Vec::new();
13192        for child in &self.children {
13193            if let Some(site) = discover_fa_site(child.node, child.ctx)? {
13194                fa_sites.push(site);
13195            }
13196        }
13197        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
13198        let r = unsafe { sys::cuGraphInstantiateWithFlags(&mut exec, self.parent, 0) };
13199        if r != sys::CUresult::CUDA_SUCCESS {
13200            return Err(format!("token graph instantiate: {r:?}").into());
13201        }
13202        Ok(TokenGraph {
13203            exec,
13204            parent: self.parent,
13205            _children: self.children,
13206            fa_sites,
13207        })
13208    }
13209}
13210
13211/// Walk one child graph; if it carries the attention-section signature (exactly three MEMSET
13212/// nodes chained memset->memset->memset->fa_kernel->combine_kernel), return its update site.
13213fn discover_fa_site(
13214    child_node: cudarc::driver::sys::CUgraphNode,
13215    ctx: cudarc::driver::sys::CUcontext,
13216) -> Result<Option<TokenGraphFaSite>, Box<dyn std::error::Error>> {
13217    use cudarc::driver::sys;
13218    fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
13219        if r == sys::CUresult::CUDA_SUCCESS {
13220            Ok(())
13221        } else {
13222            Err(format!("{what}: {r:?}").into())
13223        }
13224    }
13225    let mut graph: sys::CUgraph = std::ptr::null_mut();
13226    unsafe {
13227        cu_try(
13228            sys::cuGraphChildGraphNodeGetGraph(child_node, &mut graph),
13229            "fa-site child GetGraph",
13230        )?;
13231    }
13232    let mut count: usize = 0;
13233    unsafe {
13234        cu_try(
13235            sys::cuGraphGetNodes(graph, std::ptr::null_mut(), &mut count),
13236            "fa-site GetNodes(count)",
13237        )?;
13238    }
13239    let mut nodes: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); count];
13240    unsafe {
13241        cu_try(
13242            sys::cuGraphGetNodes(graph, nodes.as_mut_ptr(), &mut count),
13243            "fa-site GetNodes",
13244        )?;
13245    }
13246    nodes.truncate(count);
13247    let node_type =
13248        |node: sys::CUgraphNode| -> Result<sys::CUgraphNodeType, Box<dyn std::error::Error>> {
13249            let mut ty = sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_EMPTY;
13250            unsafe {
13251                cu_try(
13252                    sys::cuGraphNodeGetType(node, &mut ty),
13253                    "fa-site NodeGetType",
13254                )?;
13255            }
13256            Ok(ty)
13257        };
13258    let memsets: Vec<sys::CUgraphNode> = {
13259        let mut v = Vec::new();
13260        for &node in &nodes {
13261            if node_type(node)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_MEMSET {
13262                v.push(node);
13263            }
13264        }
13265        v
13266    };
13267    if memsets.len() != 3 {
13268        return Ok(None);
13269    }
13270    // Single-stream capture makes the chain linear: follow dependent edges from each memset.
13271    let dependents =
13272        |node: sys::CUgraphNode| -> Result<Vec<sys::CUgraphNode>, Box<dyn std::error::Error>> {
13273            let mut n: usize = 0;
13274            unsafe {
13275                cu_try(
13276                    sys::cuGraphNodeGetDependentNodes_v2(
13277                        node,
13278                        std::ptr::null_mut(),
13279                        std::ptr::null_mut(),
13280                        &mut n,
13281                    ),
13282                    "fa-site GetDependentNodes(count)",
13283                )?;
13284            }
13285            let mut v: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); n];
13286            unsafe {
13287                cu_try(
13288                    sys::cuGraphNodeGetDependentNodes_v2(
13289                        node,
13290                        v.as_mut_ptr(),
13291                        std::ptr::null_mut(),
13292                        &mut n,
13293                    ),
13294                    "fa-site GetDependentNodes",
13295                )?;
13296            }
13297            v.truncate(n);
13298            Ok(v)
13299        };
13300    // The LAST memset is the one whose direct dependent is a kernel (fa); the other two are
13301    // ordered among themselves but interchangeable for width updates.
13302    let mut fa: Option<sys::CUgraphNode> = None;
13303    let mut last_memset: Option<sys::CUgraphNode> = None;
13304    for &ms in &memsets {
13305        for dep in dependents(ms)? {
13306            if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
13307                fa = Some(dep);
13308                last_memset = Some(ms);
13309            }
13310        }
13311    }
13312    let (Some(fa), Some(_last)) = (fa, last_memset) else {
13313        return Ok(None);
13314    };
13315    let mut combine: Option<sys::CUgraphNode> = None;
13316    for dep in dependents(fa)? {
13317        if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
13318            combine = Some(dep);
13319        }
13320    }
13321    let Some(combine) = combine else {
13322        return Ok(None);
13323    };
13324    // Read the fa launch geometry from its baked args (arg order pinned by fa_decode_dcw):
13325    // 6=hd 7=nh 8=nhkv 11=win 13=nsp 14=ski.
13326    let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
13327    unsafe {
13328        cu_try(
13329            sys::cuGraphKernelNodeGetParams_v2(fa, &mut params),
13330            "fa-site KernelNodeGetParams",
13331        )?;
13332    }
13333    let arg_i32 =
13334        |slot: usize| -> i32 { unsafe { *(*params.kernelParams.add(slot) as *const i32) } };
13335    let (hd, nh, nhkv, win) = (arg_i32(6), arg_i32(7), arg_i32(8), arg_i32(11));
13336    // Identify the o-partial memset (hd x wider than the m/l pair).
13337    let width_of = |node: sys::CUgraphNode| -> Result<usize, Box<dyn std::error::Error>> {
13338        let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
13339        unsafe {
13340            cu_try(
13341                sys::cuGraphMemsetNodeGetParams(node, &mut mp),
13342                "fa-site MemsetNodeGetParams",
13343            )?;
13344        }
13345        Ok(mp.width)
13346    };
13347    let mut widest = memsets[0];
13348    for &ms in &memsets[1..] {
13349        if width_of(ms)? > width_of(widest)? {
13350            widest = ms;
13351        }
13352    }
13353    let memset_m: Vec<sys::CUgraphNode> =
13354        memsets.iter().copied().filter(|&m| m != widest).collect();
13355    Ok(Some(TokenGraphFaSite {
13356        ctx,
13357        memset_o: widest,
13358        memset_m: [memset_m[0], memset_m[1]],
13359        fa,
13360        combine,
13361        window: win as usize,
13362        n_head: nh as usize,
13363        n_head_kv: nhkv as usize,
13364        head_dim: hd as usize,
13365    }))
13366}
13367
13368pub struct TokenGraph {
13369    exec: cudarc::driver::sys::CUgraphExec,
13370    parent: cudarc::driver::sys::CUgraph,
13371    _children: Vec<TokenGraphChild>,
13372    fa_sites: Vec<TokenGraphFaSite>,
13373}
13374
13375unsafe impl Send for TokenGraph {}
13376
13377impl TokenGraph {
13378    /// Retarget every fa site to a new bucket via exec param updates (M1 path) — replaces the
13379    /// per-bucket whole-graph rebuild (~55ms) with ~450 node updates (~1ms). Per site the
13380    /// bucket caps at the layer window; nsp/ski/gridDimY and the partial-pool memset widths
13381    /// move together so the exec always matches what a fresh build at `bucket` would bake.
13382    pub fn retarget_bucket(&mut self, bucket: usize) -> Result<(), Box<dyn std::error::Error>> {
13383        use cudarc::driver::sys;
13384        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
13385            if r == sys::CUresult::CUDA_SUCCESS {
13386                Ok(())
13387            } else {
13388                Err(format!("{what}: {r:?}").into())
13389            }
13390        }
13391        for site in &self.fa_sites {
13392            let layer_bucket = if site.window > 0 {
13393                bucket.min(site.window)
13394            } else {
13395                bucket
13396            };
13397            let sp = crate::fa_split_keys(layer_bucket, site.n_head_kv);
13398            let nsp = layer_bucket.div_ceil(sp).max(1);
13399            // fa kernel: nsp (slot 13), ski (slot 14), gridDimY = nsp.
13400            let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
13401            unsafe {
13402                cu_try(
13403                    sys::cuGraphKernelNodeGetParams_v2(site.fa, &mut params),
13404                    "retarget fa GetParams",
13405                )?;
13406                *(*params.kernelParams.add(13) as *mut i32) = nsp as i32;
13407                *(*params.kernelParams.add(14) as *mut i32) = sp as i32;
13408                params.gridDimY = nsp as u32;
13409                cu_try(
13410                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.fa, &params),
13411                    "retarget fa SetParams",
13412                )?;
13413            }
13414            // combine: nsp (slot 6).
13415            let mut cparams: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
13416            unsafe {
13417                cu_try(
13418                    sys::cuGraphKernelNodeGetParams_v2(site.combine, &mut cparams),
13419                    "retarget combine GetParams",
13420                )?;
13421                *(*cparams.kernelParams.add(6) as *mut i32) = nsp as i32;
13422                cu_try(
13423                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.combine, &cparams),
13424                    "retarget combine SetParams",
13425                )?;
13426            }
13427            // partial-pool memsets: o = nh*nsp*hd elements, m/l = nh*nsp.
13428            let set_width =
13429                |node: sys::CUgraphNode, width: usize| -> Result<(), Box<dyn std::error::Error>> {
13430                    let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
13431                    unsafe {
13432                        cu_try(
13433                            sys::cuGraphMemsetNodeGetParams(node, &mut mp),
13434                            "retarget memset GetParams",
13435                        )?;
13436                    }
13437                    mp.width = width;
13438                    unsafe {
13439                        cu_try(
13440                            sys::cuGraphExecMemsetNodeSetParams(self.exec, node, &mp, site.ctx),
13441                            "retarget memset SetParams",
13442                        )?;
13443                    }
13444                    Ok(())
13445                };
13446            set_width(site.memset_o, site.n_head * nsp * site.head_dim)?;
13447            set_width(site.memset_m[0], site.n_head * nsp)?;
13448            set_width(site.memset_m[1], site.n_head * nsp)?;
13449        }
13450        Ok(())
13451    }
13452
13453    pub fn launch(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
13454        use cudarc::driver::sys;
13455        let _main = e.gpu.enter_main()?;
13456        let r = unsafe { sys::cuGraphLaunch(self.exec, e.stream().cu_stream() as sys::CUstream) };
13457        if r != sys::CUresult::CUDA_SUCCESS {
13458            return Err(format!("token graph launch: {r:?}").into());
13459        }
13460        Ok(())
13461    }
13462}
13463
13464impl Drop for TokenGraph {
13465    fn drop(&mut self) {
13466        unsafe {
13467            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
13468            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
13469        }
13470    }
13471}
13472
13473std::thread_local! {
13474    static TOKEN_GRAPH_BUILDER: std::cell::RefCell<Option<TokenGraphBuilder>> =
13475        const { std::cell::RefCell::new(None) };
13476}
13477
13478/// Arm the thread-local builder (build mode) — the next `graph_section` calls capture.
13479pub fn token_graph_build_begin() -> Result<(), Box<dyn std::error::Error>> {
13480    let builder = TokenGraphBuilder::new()?;
13481    TOKEN_GRAPH_BUILDER.with(|cell| *cell.borrow_mut() = Some(builder));
13482    Ok(())
13483}
13484
13485/// Take the finished parent (ends build mode).
13486pub fn token_graph_build_finish() -> Result<TokenGraph, Box<dyn std::error::Error>> {
13487    let builder = TOKEN_GRAPH_BUILDER
13488        .with(|cell| cell.borrow_mut().take())
13489        .ok_or("token graph build was not begun")?;
13490    builder.finish()
13491}
13492
13493/// True while the thread-local builder is armed.
13494pub fn token_graph_building() -> bool {
13495    TOKEN_GRAPH_BUILDER.with(|cell| cell.borrow().is_some())
13496}
13497
13498/// The section annotation: eager mode runs the closure verbatim; build mode wraps it in a
13499/// stream capture on `engine`'s stream and records the child. Sections sharing a
13500/// `parallel_group` id fork from the same predecessor set and merge together. The closure
13501/// must be capture-safe (raw copies at cross-context seams, no host syncs, no events).
13502pub fn graph_section<F>(
13503    engine: &Engine,
13504    parallel_group: Option<u32>,
13505    f: F,
13506) -> Result<(), Box<dyn std::error::Error>>
13507where
13508    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13509{
13510    graph_section_opts(engine, parallel_group, false, false, f)
13511}
13512
13513/// Serial section that ALSO joins every pending detached section (the SH1 consumer shape).
13514pub fn graph_section_absorbing<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
13515where
13516    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13517{
13518    graph_section_opts(engine, None, false, true, f)
13519}
13520
13521/// `graph_section` with the DETACHED shape: forks from the current frontier (or the open
13522/// group base) and is joined only by the next serial section — never gates a group merge.
13523pub fn graph_section_detached<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
13524where
13525    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13526{
13527    graph_section_opts(engine, None, true, false, f)
13528}
13529
13530pub fn graph_section_opts<F>(
13531    engine: &Engine,
13532    parallel_group: Option<u32>,
13533    detached: bool,
13534    absorb: bool,
13535    f: F,
13536) -> Result<(), Box<dyn std::error::Error>>
13537where
13538    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13539{
13540    let building = token_graph_building();
13541    if !building {
13542        let mut f = f;
13543        return f();
13544    }
13545    let (child, ctx) = {
13546        let _main = engine.gpu.enter_main()?;
13547        let mut ctx: cudarc::driver::sys::CUcontext = std::ptr::null_mut();
13548        let r = unsafe { cudarc::driver::sys::cuCtxGetCurrent(&mut ctx) };
13549        if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
13550            return Err(format!("graph section ctx query: {r:?}").into());
13551        }
13552        let mut f = f;
13553        // NO WARMUP RUNS: section bodies carry device side effects (dcw appends, counter
13554        // incs) that a warmup would really execute — the len_d-drift crash of 2026-08-21.
13555        let (child, _retained) = engine.capture_graph_retained_nowarm(|_| f())?;
13556        (child, ctx)
13557    };
13558    TOKEN_GRAPH_BUILDER.with(|cell| {
13559        cell.borrow_mut()
13560            .as_mut()
13561            .expect("builder checked above")
13562            .push_child(child, parallel_group, detached, absorb, ctx)
13563    })
13564}