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, DevicePtr, DeviceSlice, LaunchConfig, PushKernelArg};
12use std::ops::Range;
13
14/// Previous gate output per (rank, t), so the determ probe can report the SHAPE of a divergence
15/// (dense-ULP vs sparse-huge) and not merely that a checksum moved. Probe-only state.
16#[allow(clippy::type_complexity)] // allow: one-shot composite type; naming it would hide the shape that matters at the call site
17static DETERM_PREV: std::sync::OnceLock<
18    std::sync::Mutex<std::collections::HashMap<(usize, usize), Vec<f32>>>,
19> = std::sync::OnceLock::new();
20
21const FP8_BLOCK: usize = 128;
22const NATIVE_P2P_PROBE_WORDS: &[usize] = &[4096, 16_384, 262_144, 16_777_216];
23const STEP_GROUPED_FP8_EXPERTS: usize = 288;
24const STEP_GROUPED_FP8_TOP_K: usize = 8;
25const STEP_GROUPED_FP8_WIDTH: usize = 1280;
26
27fn validate_step_expert_activation_limit(limit: Option<f32>) -> Result<(), String> {
28    if let Some(limit) = limit
29        && (!limit.is_finite() || limit <= 0.0)
30    {
31        return Err(format!(
32            "Step routed-expert activation limit must be positive and finite, got {limit}"
33        ));
34    }
35    Ok(())
36}
37
38/// Host-canonical Step routed-expert SwiGLU operation.
39///
40/// Step's final routed layers clamp the linear arm symmetrically and the SiLU arm only above.
41/// Keeping this scalar order explicit also defines the device-host-exact CUDA gate.
42/// Raw stream-ordered device copy for capture-safe cross-context seams (cudarc's slice-use
43/// tracking creates capture-illegal dependencies there). Pointers must be pre-cached with
44/// their owners' streams; bytes flow identically to the tracked copy.
45/// MEMRA_OPROJ_DIRECT=1 (o-proj direct join, default OFF until gated): peer ranks write
46/// their fused O partial OVER P2P into a root-resident buffer (UVA kernel stores), and the
47/// model engine adds the two partials itself — the root stream leaves the join entirely
48/// (no peer pull copy, no root add, no second event hop, no final 16KB ownership copy).
49/// Reduction order and kernel programs are unchanged, so the row is BIT-IDENTICAL.
50/// MEMRA_MOE_DIRECT=1 (moe direct join, default OFF until gated): the o-proj direct-join
51/// recipe on the expert combine — peer ranks' accumulators live root-side (the axpy twin
52/// register-accumulates and stores ONCE, so the P2P cost is a single 16KB store pass), and
53/// the model engine adds the two shard rows itself. Operand order matches root's add:
54/// BIT-IDENTICAL.
55/// MEMRA_ROUTES_PRESTAGE=1 (default OFF until gated): stage the shared layer input to
56/// every rank and quantize it BEFORE the router runs — neither depends on the selection,
57/// so the rank streams' pull+quantize overlaps dev0's router gemv+topk instead of chaining
58/// behind it (the router->quantize and axpy->add gap edges). Same copies, same quantize
59/// kernel, same operands: BIT-IDENTICAL.
60pub(crate) fn routes_prestage_on() -> bool {
61    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
62    *ON.get_or_init(|| std::env::var("MEMRA_ROUTES_PRESTAGE").as_deref() == Ok("1"))
63}
64
65/// MEMRA_FENCE_MEMOPS=1 (default OFF until gated): the moe direct join's two event
66/// fences become cuStreamWriteValue32/cuStreamWaitValue32 doorbells — hardware stream
67/// memops with lower signal->wake latency than cross-device cuStreamWaitEvent. Ordering:
68/// PCIe posted writes from one device arrive in order, so rank1's accumulator stores are
69/// visible before its flag write lands; e's GEQ wait then covers them. Falls back to
70/// events when the device rejects stream memops. Scheduling-only: BIT-IDENTICAL values.
71/// MEMRA_LEN_MIRROR_LAZY=1 (default OFF until gated): skip redundant per-layer 4B len
72/// htods — the local device mirror is unread in TP decode, and under FUSE_ROPE_APPEND the
73/// fused append's atomicInc owns the rank counters. Every one of those tiny copies is a
74/// compute->copy engine turnaround in the middle of the layer stream.
75/// MEMRA_RANK0_MERGE=1 (default OFF until gated): same-device rank0 rides e's stream via
76/// the runtime redirect — see decode_step_h.
77/// MEMRA_OPROJ_TAIL=1 (default OFF until gated): the o-proj direct-join add is DEFERRED —
78/// the finish arm keeps its waits, stores the two partial pointers here, and the residual
79/// add_rms_norm consumer composes mixed = a0+a1 in-register (join_add_rms_norm, verbatim
80/// program: BIT-IDENTICAL). The returned `mixed` buffer is UNWRITTEN in this mode; its
81/// only live consumer is the residual_norm_ffn seam, which takes the handoff.
82pub(crate) fn oproj_tail_on() -> bool {
83    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
84    *ON.get_or_init(|| std::env::var("MEMRA_OPROJ_TAIL").as_deref() == Ok("1"))
85}
86thread_local! {
87    static OPROJ_TAIL_PENDING: std::cell::Cell<Option<(u64, u64)>> =
88        const { std::cell::Cell::new(None) };
89}
90thread_local! {
91    /// The deferral is legal ONLY under callers whose walk flows into
92    /// residual_norm_ffn (decode_step_h / decode_step_chain arm this) — the verify
93    /// prefill reaches the same finish and would consume unwritten `mixed` otherwise
94    /// (M2-MISMATCH receipt: prefill argmax corrupted while decode stayed exact).
95    static OPROJ_TAIL_ELIGIBLE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
96}
97/// RAII eligibility scope for the o-proj tail deferral.
98pub(crate) struct OprojTailScope(());
99pub(crate) fn oproj_tail_scope() -> OprojTailScope {
100    OPROJ_TAIL_ELIGIBLE.with(|c| c.set(true));
101    OprojTailScope(())
102}
103impl Drop for OprojTailScope {
104    fn drop(&mut self) {
105        OPROJ_TAIL_ELIGIBLE.with(|c| c.set(false));
106        // A leftover un-consumed handoff must never leak across calls.
107        OPROJ_TAIL_PENDING.with(|c| c.set(None));
108    }
109}
110thread_local! {
111    /// T-COLUMN verify select: the verify driver sets the column before each per-column
112    /// attention call; decode_v2_input_qkv takes it (once) and selects from the slabs.
113    static VERIFY_TCOL: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
114}
115pub(crate) fn set_verify_tcol(c: Option<usize>) {
116    VERIFY_TCOL.with(|x| x.set(c));
117}
118pub(crate) fn take_verify_tcol() -> Option<usize> {
119    VERIFY_TCOL.with(|x| x.take())
120}
121
122/// MEMRA_TCOL_OPROJ=1 (spec verify): defer each column's o_proj out of the per-column
123/// walk — the finish seam stashes the column's `gated` rows instead of running the
124/// per-column finish choreography (rank events, P2P join, engine handoff), and one
125/// weight-amortized b4_tcol per rank + one elementwise join produce every column's
126/// `mixed` afterwards. Bit-exact per column: the tcol kernel is the t=1 b4 program per
127/// column, and the slab join adds the same operand values elementwise.
128pub(crate) fn tcol_oproj_on() -> bool {
129    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
130    *ON.get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ").as_deref() == Ok("1"))
131}
132thread_local! {
133    /// The verify driver arms the column before each per-column attention call; the
134    /// finish seam takes it (once). Stashed=true reports the defer actually happened
135    /// (the seam falls back to the normal finish when the config is ineligible).
136    static TCOL_OPROJ_DEFER: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
137    static TCOL_OPROJ_STASHED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
138}
139pub(crate) fn set_tcol_oproj_defer(c: Option<usize>) {
140    TCOL_OPROJ_DEFER.with(|x| x.set(c));
141}
142pub(crate) fn take_tcol_oproj_defer() -> Option<usize> {
143    TCOL_OPROJ_DEFER.with(|x| x.take())
144}
145pub(crate) fn set_tcol_oproj_stashed() {
146    TCOL_OPROJ_STASHED.with(|x| x.set(true));
147}
148pub(crate) fn take_tcol_oproj_stashed() -> bool {
149    TCOL_OPROJ_STASHED.with(|x| x.replace(false))
150}
151
152pub(crate) fn oproj_tail_eligible() -> bool {
153    OPROJ_TAIL_ELIGIBLE.with(|c| c.get())
154}
155pub(crate) fn take_oproj_tail() -> Option<(u64, u64)> {
156    OPROJ_TAIL_PENDING.with(|c| c.take())
157}
158pub(crate) fn set_oproj_tail(v: (u64, u64)) {
159    OPROJ_TAIL_PENDING.with(|c| c.set(Some(v)));
160}
161
162pub(crate) fn rank0_merge_on() -> bool {
163    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
164    *ON.get_or_init(|| std::env::var("MEMRA_RANK0_MERGE").as_deref() == Ok("1"))
165}
166
167pub(crate) fn len_mirror_lazy_on() -> bool {
168    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
169    *ON.get_or_init(|| std::env::var("MEMRA_LEN_MIRROR_LAZY").as_deref() == Ok("1"))
170}
171
172pub(crate) fn fence_memops_on() -> bool {
173    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
174    *ON.get_or_init(|| std::env::var("MEMRA_FENCE_MEMOPS").as_deref() == Ok("1"))
175}
176
177pub(crate) fn moe_direct_on() -> bool {
178    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
179    *ON.get_or_init(|| std::env::var("MEMRA_MOE_DIRECT").as_deref() == Ok("1"))
180}
181
182/// MEMRA_SEL_MIRROR=1: the per-rank routed-selection pull runs as ONE `moe_sel_w_mirror`
183/// launch instead of two 32-byte D2D copies, and when every consuming rank shares e's device
184/// the intermediate e-context staging pair is skipped entirely (the caller's sel/route_w rows
185/// are process-persistent, so the ranks read them directly). Bit-identical: same bytes, one
186/// fewer hop. Refused under the graph door, whose captured copies need the fixed staging
187/// addresses. Default OFF until receipted.
188/// MEMRA_FENCE_RANK1=1: the peer rank rings a doorbell in ROOT memory with a kernel store
189/// (`memra_ring_flag`) and the model engine waits it with a SAME-DEVICE stream memop, instead
190/// of waiting a cross-device event. Completes the half the memops receipt left open (peer
191/// memops are rejected; peer kernel stores are the direct-join mechanism). Ordering only —
192/// values are untouched. Requires MEMRA_FENCE_MEMOPS=1 (it owns the flag allocation).
193pub(crate) fn fence_rank1_on() -> bool {
194    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
195    *ON.get_or_init(|| std::env::var("MEMRA_FENCE_RANK1").as_deref() == Ok("1"))
196}
197
198/// MEMRA_SPEC_FA2=1 (the DSpark verify lesson): the T=2 verify walk defers each column's
199/// ATTENTION CORE — the dcw arm appends the column's K/V and stashes its post-rope q and
200/// gate rows, then ONE fa_decode_dcw2 per rank walks the KV stream once for both columns
201/// (per-row causal bounds; bit-identical per row under the equal-partition guard), the
202/// per-row combine writes both gated rows, and the o_proj join runs on the TCOL slabs.
203/// ROW-TABLE RESTAGE (`MEMRA_ROWS_TAB_RESTAGE`, DEFAULT ON since this lane).
204///
205/// ON: `decode_v2_rope_fa_rows` builds the 6-word-per-row pointer table from the caller's
206/// freshly-read live cache pointers and stages it into a persistent per-rank slab before
207/// every launch. OFF (`=0`): the retired process-lifetime `rows_tabs` memo, keyed by a hash
208/// of (k pointer, base pointer, layer, t) that could not see the V or LEN pointers the
209/// entry also carried, and that nothing invalidated when a session's KV cache was dropped.
210///
211/// Default ON because the OFF arm is a proven use-after-free, not a slower correct path:
212/// on step37-flash with MEMRA_FUSE_ROPE_APPEND=1 it made speculative decoding unservable
213/// (whole non-finite verify rows, then CUDA_ERROR_ILLEGAL_ADDRESS). ON is value-neutral on
214/// every fresh lookup by construction: identical bytes reach the same kernels. Rollback
215/// seam: `MEMRA_ROWS_TAB_RESTAGE=0`.
216/// The 6-word-per-row launch table `{k, v, len, base, ctr, back}` the fused rope/append/fa
217/// kernels dereference. Pure so it can be tested: the words come from the caller's live
218/// per-row `[k, v, len, base]` pointers, `ctr` is this rank's counter slab (one shared cell
219/// for same-session rows, one cell per row otherwise) and `back` is the same-session causal
220/// step-back `t-1-r` (0 across sessions, where each row owns its own len).
221pub(crate) fn rows_tab_host(
222    parts_rank: &[[u64; 4]],
223    ctr_base: u64,
224    same_session: bool,
225    t: usize,
226) -> Vec<u64> {
227    let mut host = Vec::with_capacity(t * 6);
228    for (r, parts) in parts_rank.iter().enumerate().take(t) {
229        host.extend_from_slice(&[
230            parts[0],
231            parts[1],
232            parts[2],
233            parts[3],
234            if same_session {
235                ctr_base
236            } else {
237                ctr_base + (r as u64) * 4
238            },
239            if same_session {
240                (t - 1 - r) as u64
241            } else {
242                0u64
243            },
244        ]);
245    }
246    host
247}
248
249/// The RETIRED memo key, kept ONLY so a test can assert what it cannot see. Both historical
250/// call sites hashed a SUBSET of the pointers the table carries; this reproduces the verify
251/// site's formula verbatim.
252#[cfg(test)]
253pub(crate) fn retired_rows_tab_key(kp: u64, bp: u64, il: usize, t: usize) -> u64 {
254    kp.rotate_left(17)
255        .wrapping_add(bp)
256        .wrapping_add((il as u64) << 32)
257        .wrapping_add(t as u64)
258        .wrapping_add(1 << 63)
259}
260
261pub(crate) fn rows_tab_restage_on() -> bool {
262    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
263    *ON.get_or_init(|| std::env::var("MEMRA_ROWS_TAB_RESTAGE").as_deref() != Ok("0"))
264}
265
266/// STALE-HIT RECEIPT (`MEMRA_ROWS_TAB_STALE_SCAN`, DEFAULT OFF, diagnostic only).
267///
268/// Keeps a HOST shadow of the last table staged under each retired memo key and prints one
269/// line whenever the key repeats with different contents, naming the words that moved. It
270/// costs a host hash lookup and a small clone per rank per layer per verify round, so it is
271/// off in serving. `[rows-tab] engaged=` on the counter proves the path executes at all,
272/// which is what separates "the memo was innocent" from "the memo never ran".
273/// Rollback seam: unset it (or `=0`).
274pub(crate) fn rows_tab_stale_scan() -> bool {
275    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
276    *ON.get_or_init(|| std::env::var("MEMRA_ROWS_TAB_STALE_SCAN").as_deref() == Ok("1"))
277}
278
279pub(crate) static ROWS_TAB_ENGAGED: std::sync::atomic::AtomicU64 =
280    std::sync::atomic::AtomicU64::new(0);
281pub(crate) static ROWS_TAB_STALE: std::sync::atomic::AtomicU64 =
282    std::sync::atomic::AtomicU64::new(0);
283
284pub(crate) fn spec_fa2_on() -> bool {
285    static ENV: std::sync::OnceLock<Option<bool>> = std::sync::OnceLock::new();
286    crate::step37_door(&ENV, "MEMRA_SPEC_FA2")
287}
288thread_local! {
289    /// The verify driver arms the column before each per-column attention call; the dcw
290    /// arm takes it (once) and stashes q/gate instead of running fa+finish.
291    static SPEC_FA2_DEFER: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
292    static SPEC_FA2_STASHED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
293}
294pub(crate) fn set_spec_fa2_defer(c: Option<usize>) {
295    SPEC_FA2_DEFER.with(|x| x.set(c));
296}
297pub(crate) fn take_spec_fa2_defer() -> Option<usize> {
298    SPEC_FA2_DEFER.with(|x| x.take())
299}
300pub(crate) fn set_spec_fa2_stashed() {
301    SPEC_FA2_STASHED.with(|x| x.set(true));
302}
303pub(crate) fn take_spec_fa2_stashed() -> bool {
304    SPEC_FA2_STASHED.with(|x| x.replace(false))
305}
306
307pub(crate) fn sel_mirror_on() -> bool {
308    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
309    *ON.get_or_init(|| std::env::var("MEMRA_SEL_MIRROR").as_deref() == Ok("1"))
310}
311
312/// MEMRA_STEP_NVFP4_EP2=1: whole-expert (expert-parallel) NVFP4 banks at 2 ranks — expert e
313/// lives ENTIRE on rank (e & 1) at bank slot (e >> 1), replacing the TP column/row shards
314/// (same total VRAM; both sets cannot coexist). Decode rides owner-guarded full-width
315/// sweeps with per-rank slot-ordered partial sums; the cross-rank join is unchanged.
316/// NUMERIC-CLASS door (the slot chain regroups per rank): run-gen argmax gate + battery +
317/// fresh tape, the DEV_ROUTES acceptance class.
318pub(crate) fn step_nvfp4_ep2_on() -> bool {
319    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
320    *ON.get_or_init(|| std::env::var("MEMRA_STEP_NVFP4_EP2").as_deref() == Ok("1"))
321}
322
323pub(crate) fn oproj_direct_on() -> bool {
324    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
325    *ON.get_or_init(|| std::env::var("MEMRA_OPROJ_DIRECT").as_deref() == Ok("1"))
326}
327
328// ─── Slot-major NVFP4 expert-bank programs: THREE independent doors ───────────────────────────
329//
330// These restore, under separate flags, the three programs that the 2026-08-29 removal
331// (`fd0a175ab`) deleted behind ONE env var (`MEMRA_NVFP4_BANK_V2`). That coupling is why the
332// incident's bisect could not name a mechanism: toggling one var moved the bank layout, the
333// gate+up fusion (which auto-armed on the same predicate with no door of its own) and the fused
334// down+combine (which hard-refused without the layout) all at once, so the priced -21.5% wall /
335// -23.7% decode (research/perf-chain-20260831 cell 1) was an unattributable bundle.
336//
337// The corruption they were removed for was NOT any of them: it was a defaulted `in_f = 0`
338// argument at two `kq_fetch` call sites in the PREFILL grouped-GEMM tail
339// (research/step37-bankv3-20260901/DIAGNOSIS.md), fixed compiler-enforced at `1b18a61e8` and
340// gated device-side by the `nvfp4-bank-oracle` bin. Each door below is strict `0`/`1` and
341// admitted separately so its contribution is a number; BANK_SM and SEL_DOWN8 default ON since
342// 2026-09-01 (one coupled decision, PR #76 battery), SEL_GU and the sub-doors default OFF.
343//
344// LAYOUT IS A PROPERTY OF THE BANK. `bank_slot_major_on()` is read ONCE, at bank BUILD, and
345// recorded on the resident bank (`ResidentNvfp4{Column,Row}BankRank::slot_major`). Every reader
346// branches on that stored field, never on the env door. The removed implementation read
347// `nvfp4_bank_v2_on()` at each reader site instead, which is the same class of hole as the
348// defaulted `in_f`: a piece of layout geometry that a caller can fail to supply or can supply
349// inconsistently with the bytes actually resident.
350
351/// Read a DEFAULT-ON door strictly, and report the SOURCE of the answer rather than only the
352/// answer. `0` disables (the rollback seam), `1` re-states the default, unset takes the default.
353///
354/// Two properties this buys, both learned the hard way in this lane's own archaeology:
355///
356/// * **A typo cannot silently disarm a rollback seam.** The default-OFF doors here parse as
357///   `== Ok("1")`, which is safe when the default is OFF (a typo reads as the default) and
358///   DANGEROUS when the default is ON: `MEMRA_NVFP4_BANK_SM=false` under a `!= Ok("0")` rule
359///   would keep the program armed while the operator believed it was rolled back. So an
360///   unrecognized value is reported as such and the default is kept, loudly.
361/// * **The engagement receipt can name the source.** `default-on` and `MEMRA_..=1` are
362///   different facts about the same boot: one says the flip is doing the work, the other says
363///   a recipe is. A pricing or post-deploy receipt that cannot tell them apart cannot prove a
364///   DEFAULT was measured (TRAP:corrupt-arm-inflates-its-own-perf-price's sibling: an arm that
365///   cannot name what armed it is not an arm).
366fn door_default_on(name: &'static str) -> (bool, &'static str) {
367    let raw = std::env::var(name).ok();
368    door_default_on_value(name, raw.as_deref())
369}
370
371/// The parse, separated from the environment so it can be TESTED. `std::env` is process-global
372/// state and these doors are `OnceLock`-cached, so an env-var test would be both racy under
373/// `cargo test`'s thread pool and unrepeatable within one process — i.e. exactly the kind of
374/// gate that passes because it never really ran.
375fn door_default_on_value(name: &str, value: Option<&str>) -> (bool, &'static str) {
376    match value {
377        Some("0") => (false, "env=0 (rollback seam)"),
378        Some("1") => (true, "env=1"),
379        None => (true, "default-on"),
380        Some(_) => {
381            eprintln!(
382                "[nvfp4-door] WARN {name} has an unrecognized value; only `0` and `1` are \
383                 accepted and the DEFAULT-ON answer is kept. To roll back, set {name}=0."
384            );
385            (true, "default-on (unrecognized value ignored)")
386        }
387    }
388}
389
390/// MEMRA_NVFP4_BANK_SM (PROGRAM 1, **default ON since 2026-09-01**): build the step TP
391/// contiguous NVFP4 expert banks (gate/up/down) in the SLOT-MAJOR row layout — slot g's 16 qs
392/// bytes contiguous at `g*16` (one coalesced 512B warp wave) and the two UE4M3 scale bytes at
393/// `nslots*16 + g*2` — and dispatch the `_sel_v2` decode readers over them. Pure byte
394/// permutation, so BIT-IDENTICAL per row; the claim is gated by `nvfp4-bank-oracle`
395/// (device-side, prefill GEMM included) and by end-to-end greedy byte identity, never by a
396/// comment.
397///
398/// **WHY A BIT-IDENTICAL, MEASURABLY-FREE PROGRAM DEFAULTS ON.** On its own this layout earns
399/// nothing: x5 interleaved, 105.35 vs 106.78 decode tok/s, per-boot range `[104.66, 107.95]`
400/// overlapping the OFF arm's `[105.11, 107.09]`. It defaults ON for exactly one reason —
401/// `MEMRA_NVFP4_SEL_DOWN8` (PROGRAM 3), the one program that DOES separate (+5.48% decode), is
402/// gated at its call site on `shard.slot_major`, so with this door off PROGRAM 3's default-ON
403/// is a SILENT NO-OP: `down8=false door=true`, no refusal, no warning, and the win simply does
404/// not happen. The deployable unit is the two together, which makes this one coupled default
405/// decision and not two independent ones. Receipts:
406/// `research/step37-bankv3-20260901/RESULTS.md` (the down8 default-ON qualification battery).
407///
408/// ROLLBACK SEAM: `MEMRA_NVFP4_BANK_SM=0`, which also disarms PROGRAM 3 by construction.
409pub(crate) fn bank_slot_major_on() -> bool {
410    bank_slot_major_source().0
411}
412
413/// `bank_slot_major_on()` plus the SOURCE of the answer, for the engagement receipt.
414pub(crate) fn bank_slot_major_source() -> (bool, &'static str) {
415    static ON: std::sync::OnceLock<(bool, &'static str)> = std::sync::OnceLock::new();
416    *ON.get_or_init(|| door_default_on("MEMRA_NVFP4_BANK_SM"))
417}
418
419/// MEMRA_NVFP4_SEL_GU=1 (PROGRAM 2, default OFF): run the routed gate and up sweeps as ONE
420/// launch (`qmatvec_nvfp4_dp4a_sel_v2_gu`) instead of two — the two sweeps share sel/aq/ad and
421/// have identical geometry, so blocks `[0,out_f)` take the gate bank and `[out_f,2*out_f)` the
422/// up bank. Per-row bit-identical; halves the sweep launch count and doubles grid fill.
423/// Subordinate to PROGRAM 1 by construction: it reads slot-major rows, so the caller arms it
424/// only when both banks report `slot_major`. In the removed implementation this fusion had NO
425/// door of its own and auto-armed on the bank predicate, which is one third of why the bundle
426/// was unattributable.
427pub(crate) fn sel_gu_fused_on() -> bool {
428    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
429    *ON.get_or_init(|| std::env::var("MEMRA_NVFP4_SEL_GU").as_deref() == Ok("1"))
430}
431
432/// MEMRA_NVFP4_SEL_DOWN8 (PROGRAM 3, default ON since 2026-09-01; `=0` is the rollback seam): fuse the routed DOWN sweep with the
433/// route-weight combine into one launch (`qmatvec_nvfp4_dp4a_sel_v2_down8`, the q8 `down8 w8`
434/// occupancy arm ported to the NVFP4 banks) — one warp per routed slot instead of one warp per
435/// (row, slot), and the `n_sel x out_f` partial-buffer round trip disappears. Bit-identical
436/// (same dot program, same reduce tree, same slot-ordered combine chain). Also subordinate to
437/// PROGRAM 1: the caller arms it only when the down shard reports `slot_major`, and only on the
438/// device-routed arm at `nsb <= 32` (the fit-block class the reduce identity is argued at).
439/// Rides LAST per the lane mandate: it is priced only on green gates for the layers beneath it.
440///
441/// **DEFAULT ON since 2026-09-01**, and it is the reason PROGRAM 1 defaults ON too. This is the
442/// only one of the three restored programs that separates from noise: +5.48% decode / +5.09%
443/// wall, x5 interleaved vendor-default sampled, per-boot range `[112.59, 114.82]` with NO
444/// overlap against either the OFF arm or the arm directly beneath it, re-qualified at deploy
445/// grade in `research/step37-bankv3-20260901/RESULTS.md`.
446///
447/// ELIGIBILITY IS NARROWER THAN THE DEFAULT, and the engagement receipt below prints every
448/// condition: the arm needs `device_routed`, `shard.slot_major` (i.e. PROGRAM 1) and
449/// `nsb <= 32`. On any other geometry or route the default is INERT, which is correct-by-
450/// refusal and NOT a regression — but it does mean "default ON" and "engaged" are two facts,
451/// and only the `[nvfp4-sweep]` line settles the second.
452///
453/// ROLLBACK SEAM: `MEMRA_NVFP4_SEL_DOWN8=0` (or `MEMRA_NVFP4_BANK_SM=0`, which disarms it by
454/// construction).
455pub(crate) fn sel_down8_on() -> bool {
456    sel_down8_source().0
457}
458
459/// `sel_down8_on()` plus the SOURCE of the answer, for the engagement receipt.
460pub(crate) fn sel_down8_source() -> (bool, &'static str) {
461    static ON: std::sync::OnceLock<(bool, &'static str)> = std::sync::OnceLock::new();
462    *ON.get_or_init(|| door_default_on("MEMRA_NVFP4_SEL_DOWN8"))
463}
464
465pub(crate) fn raw_copy_bytes(
466    dst: u64,
467    src: u64,
468    bytes: usize,
469    engine: &Engine,
470) -> Result<(), Box<dyn std::error::Error>> {
471    use cudarc::driver::sys;
472    let r = unsafe {
473        sys::cuMemcpyAsync(
474            dst as sys::CUdeviceptr,
475            src as sys::CUdeviceptr,
476            bytes,
477            engine.stream().cu_stream() as sys::CUstream,
478        )
479    };
480    if r == sys::CUresult::CUDA_SUCCESS {
481        Ok(())
482    } else {
483        // MEMRA_RAW_COPY_TRACE=1: a raw D2D failure carries no call site by itself, and
484        // every slab-width bug in the t-row family surfaces here. Operands + backtrace.
485        if std::env::var("MEMRA_RAW_COPY_TRACE").as_deref() == Ok("1") {
486            eprintln!(
487                "[raw-copy-fail] dst={dst:#x} src={src:#x} bytes={bytes} {r:?}\n{}",
488                std::backtrace::Backtrace::force_capture()
489            );
490        }
491        Err(format!("raw_copy_bytes: {r:?} bytes={bytes} dst={dst:#x} src={src:#x}").into())
492    }
493}
494
495pub fn step_expert_activation_host(gate: f32, up: f32, limit: Option<f32>) -> f32 {
496    let silu = gate / (1.0 + (-gate).exp());
497    match limit {
498        Some(limit) => silu.min(limit) * up.clamp(-limit, limit),
499        None => silu * up,
500    }
501}
502
503#[derive(Debug, Clone, PartialEq, Eq)]
504struct ExpertOwnerRoutes {
505    rank: usize,
506    selected: Vec<usize>,
507    token_rows: Vec<usize>,
508    global_pairs: Vec<usize>,
509}
510
511#[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
512fn partition_expert_owner_routes(
513    expert_count: usize,
514    ranks: usize,
515    tokens: usize,
516    experts_per_token: usize,
517    selected: &[usize],
518) -> Result<Vec<ExpertOwnerRoutes>, String> {
519    if expert_count == 0
520        || ranks == 0
521        || tokens == 0
522        || experts_per_token == 0
523        || expert_count % ranks != 0
524    {
525        return Err(format!(
526            "invalid expert-owner route geometry experts={expert_count} ranks={ranks} \
527             tokens={tokens} experts_per_token={experts_per_token}"
528        ));
529    }
530    let pairs = tokens
531        .checked_mul(experts_per_token)
532        .ok_or("expert-owner route count overflow")?;
533    if selected.len() != pairs {
534        return Err(format!(
535            "expert-owner routes {} != {tokens}x{experts_per_token} ({pairs})",
536            selected.len()
537        ));
538    }
539    let per_rank = expert_count / ranks;
540    let mut owners = (0..ranks)
541        .map(|rank| ExpertOwnerRoutes {
542            rank,
543            selected: Vec::new(),
544            token_rows: Vec::new(),
545            global_pairs: Vec::new(),
546        })
547        .collect::<Vec<_>>();
548    for (pair, &expert) in selected.iter().enumerate() {
549        if expert >= expert_count {
550            return Err(format!(
551                "expert-owner route {pair} selects expert {expert} outside 0..{expert_count}"
552            ));
553        }
554        let rank = expert / per_rank;
555        owners[rank].selected.push(expert - rank * per_rank);
556        owners[rank].token_rows.push(pair / experts_per_token);
557        owners[rank].global_pairs.push(pair);
558    }
559    Ok(owners)
560}
561
562fn validate_step_grouped_owner_routes(
563    expert_count: usize,
564    tokens: usize,
565    selected: &[usize],
566) -> Result<usize, String> {
567    if expert_count != STEP_GROUPED_FP8_EXPERTS || tokens == 0 {
568        return Err(format!(
569            "official Step owner-grouped FP8 requires {} experts and nonzero tokens, got \
570             experts={expert_count} tokens={tokens}",
571            STEP_GROUPED_FP8_EXPERTS
572        ));
573    }
574    let pairs = tokens
575        .checked_mul(STEP_GROUPED_FP8_TOP_K)
576        .ok_or("official Step owner-grouped FP8 route count overflow")?;
577    if selected.len() != pairs {
578        return Err(format!(
579            "official Step owner-grouped FP8 routes {} != {tokens}x{} ({pairs})",
580            selected.len(),
581            STEP_GROUPED_FP8_TOP_K,
582        ));
583    }
584    for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
585        let mut unique = routes.to_vec();
586        unique.sort_unstable();
587        unique.dedup();
588        if unique.len() != STEP_GROUPED_FP8_TOP_K {
589            return Err(format!(
590                "official Step owner-grouped FP8 token {token} routes are not top-8 unique: \
591                 {routes:?}"
592            ));
593        }
594    }
595    Ok(pairs)
596}
597
598#[derive(Debug, Clone, Copy, PartialEq, Eq)]
599struct WeightedRouteCombineShape {
600    pairs: usize,
601    max_pairs: usize,
602}
603
604fn validate_weighted_route_combine(
605    width: usize,
606    experts_per_token: usize,
607    max_tokens: usize,
608    tokens: usize,
609    owner_global_pairs: &[&[usize]],
610    route_weights: &[f32],
611) -> Result<WeightedRouteCombineShape, String> {
612    if width == 0
613        || experts_per_token == 0
614        || max_tokens == 0
615        || tokens == 0
616        || tokens > max_tokens
617        || width > i32::MAX as usize
618        || experts_per_token > i32::MAX as usize
619        || tokens > i32::MAX as usize
620    {
621        return Err(format!(
622            "invalid weighted route combine geometry width={width} experts_per_token=\
623             {experts_per_token} tokens={tokens}/{max_tokens}"
624        ));
625    }
626    let pairs = tokens
627        .checked_mul(experts_per_token)
628        .ok_or("weighted route combine pair count overflow")?;
629    let max_pairs = max_tokens
630        .checked_mul(experts_per_token)
631        .ok_or("weighted route combine capacity overflow")?;
632    if route_weights.len() != pairs || !route_weights.iter().all(|weight| weight.is_finite()) {
633        return Err(format!(
634            "weighted route combine weights {} != pairs {pairs} or contain a non-finite value",
635            route_weights.len()
636        ));
637    }
638    let mut seen = vec![false; pairs];
639    let mut observed = 0usize;
640    for pairs_for_owner in owner_global_pairs {
641        observed = observed
642            .checked_add(pairs_for_owner.len())
643            .ok_or("weighted route combine observed pair count overflow")?;
644        for &pair in *pairs_for_owner {
645            if pair >= pairs || std::mem::replace(&mut seen[pair], true) {
646                return Err(format!(
647                    "weighted route combine pair {pair} is outside 0..{pairs} or duplicated"
648                ));
649            }
650        }
651    }
652    if observed != pairs || seen.iter().any(|present| !present) {
653        return Err(format!(
654            "weighted route combine owner schedules cover {observed} of {pairs} canonical pairs"
655        ));
656    }
657    Ok(WeightedRouteCombineShape { pairs, max_pairs })
658}
659
660fn cache_rank_rows(
661    rows: &[u8],
662    tokens: usize,
663    local_token_bytes: usize,
664    ranks: usize,
665    rank: usize,
666) -> Result<Vec<u8>, String> {
667    if ranks == 0 || rank >= ranks {
668        return Err(format!(
669            "TP cache rank {rank} is outside a {ranks}-rank layout"
670        ));
671    }
672    let global_token_bytes = local_token_bytes
673        .checked_mul(ranks)
674        .ok_or("TP cache global token-byte overflow")?;
675    let expected = tokens
676        .checked_mul(global_token_bytes)
677        .ok_or("TP cache row-byte overflow")?;
678    if rows.len() != expected {
679        return Err(format!(
680            "TP cache rows contain {} bytes, expected {tokens}x{global_token_bytes}={expected}",
681            rows.len()
682        ));
683    }
684    let mut shard = Vec::with_capacity(tokens * local_token_bytes);
685    for token in 0..tokens {
686        let start = token * global_token_bytes + rank * local_token_bytes;
687        shard.extend_from_slice(&rows[start..start + local_token_bytes]);
688    }
689    Ok(shard)
690}
691
692fn parse_step_tp_native_p2p(value: Option<&str>) -> Result<bool, String> {
693    match value {
694        None | Some("") | Some("0") => Ok(false),
695        Some("1") => Ok(true),
696        Some(value) => Err(format!(
697            "MEMRA_STEP_TP_NATIVE_P2P={value:?} is invalid; expected 0 or 1"
698        )),
699    }
700}
701
702pub fn step_tp_native_p2p_enabled() -> Result<bool, String> {
703    parse_step_tp_native_p2p(std::env::var("MEMRA_STEP_TP_NATIVE_P2P").ok().as_deref())
704}
705
706fn parse_step_tp_bulk_p2p(value: Option<&str>) -> Result<bool, String> {
707    match value {
708        None | Some("") | Some("0") => Ok(false),
709        Some("1") => Ok(true),
710        Some(value) => Err(format!(
711            "MEMRA_STEP_TP_BULK_P2P={value:?} is invalid; expected 0 or 1"
712        )),
713    }
714}
715
716pub fn step_tp_bulk_p2p_enabled() -> Result<bool, String> {
717    parse_step_tp_bulk_p2p(std::env::var("MEMRA_STEP_TP_BULK_P2P").ok().as_deref())
718}
719
720fn parse_step_ep_device_arithmetic(value: Option<&str>) -> Result<bool, String> {
721    match value {
722        None | Some("") | Some("0") => Ok(false),
723        Some("1") => Ok(true),
724        Some(value) => Err(format!(
725            "MEMRA_STEP_EP_DEVICE_ARITHMETIC={value:?} is invalid; expected 0 or 1"
726        )),
727    }
728}
729
730fn parse_step_nvfp4_dev_routes(value: Option<&str>) -> Result<bool, String> {
731    match value {
732        None | Some("") | Some("0") => Ok(false),
733        Some("1") => Ok(true),
734        Some(value) => Err(format!(
735            "MEMRA_STEP_NVFP4_DEV_ROUTES={value:?} is invalid; expected 0 or 1"
736        )),
737    }
738}
739
740/// Opt-in door for the device-resident NVFP4 TP routed-expert decode program. Default OFF; the
741/// host-canonical program remains the oracle until the device path carries its own gates.
742pub fn step_nvfp4_dev_routes_enabled() -> Result<bool, String> {
743    parse_step_nvfp4_dev_routes(std::env::var("MEMRA_STEP_NVFP4_DEV_ROUTES").ok().as_deref())
744}
745
746pub fn step_ep_device_arithmetic_enabled() -> Result<bool, String> {
747    parse_step_ep_device_arithmetic(
748        std::env::var("MEMRA_STEP_EP_DEVICE_ARITHMETIC")
749            .ok()
750            .as_deref(),
751    )
752}
753
754fn parse_step_tp_f32_mirror(value: Option<&str>) -> Result<bool, String> {
755    match value {
756        None | Some("") | Some("0") => Ok(false),
757        Some("1") => Ok(true),
758        Some(value) => Err(format!(
759            "MEMRA_STEP_TP_F32_MIRROR={value:?} is invalid; expected 0 or 1"
760        )),
761    }
762}
763
764pub fn step_tp_f32_mirror_enabled() -> Result<bool, String> {
765    parse_step_tp_f32_mirror(std::env::var("MEMRA_STEP_TP_F32_MIRROR").ok().as_deref())
766}
767
768fn parse_step_tp_decode_v2(value: Option<&str>) -> Result<bool, String> {
769    match value {
770        None | Some("") | Some("0") => Ok(false),
771        Some("1") => Ok(true),
772        Some(value) => Err(format!(
773            "MEMRA_STEP_TP_DECODE_V2={value:?} is invalid; expected 0 or 1"
774        )),
775    }
776}
777
778/// The v2 rank-local Step decode-attention driver: persistent workspaces, evented cross-stream
779/// ordering, and a root-device O reduction — same kernels, values, and canonical reduction order
780/// as the v1 driver (it requires the F32 mirror so no per-call weight expansion exists on either
781/// side of the comparison).
782pub fn step_tp_decode_v2_enabled() -> Result<bool, String> {
783    parse_step_tp_decode_v2(std::env::var("MEMRA_STEP_TP_DECODE_V2").ok().as_deref())
784}
785
786fn parse_step_tp_qkv_fused(value: Option<&str>) -> Result<bool, String> {
787    match value {
788        None | Some("") | Some("0") => Ok(false),
789        Some("1") => Ok(true),
790        Some(value) => Err(format!(
791            "MEMRA_STEP_TP_QKV_FUSED={value:?} is invalid; expected 0 or 1"
792        )),
793    }
794}
795
796fn parse_step_tp_dev_router(value: Option<&str>) -> Result<bool, String> {
797    match value {
798        None | Some("") | Some("0") => Ok(false),
799        Some("1") => Ok(true),
800        Some(value) => Err(format!(
801            "MEMRA_STEP_TP_DEV_ROUTER={value:?} is invalid; expected 0 or 1"
802        )),
803    }
804}
805
806/// Device-side sigmoid top-k routing for the TP device-IO expert program: the per-layer host
807/// logits readback (the last per-layer host sync) disappears. Selection tie-breaking may
808/// differ from the host router — NUMERIC-CLASS door, run-gen argmax gate + boot battery.
809pub fn step_tp_dev_router_enabled() -> Result<bool, String> {
810    parse_step_tp_dev_router(std::env::var("MEMRA_STEP_TP_DEV_ROUTER").ok().as_deref())
811}
812
813fn parse_step_tp_graph(value: Option<&str>) -> Result<bool, String> {
814    match value {
815        None | Some("") | Some("0") => Ok(false),
816        Some("1") => Ok(true),
817        Some(value) => Err(format!(
818            "MEMRA_STEP_TP_GRAPH={value:?} is invalid; expected 0 or 1"
819        )),
820    }
821}
822
823fn parse_step_tp_dcw(value: Option<&str>) -> Result<bool, String> {
824    match value {
825        None | Some("") | Some("0") => Ok(false),
826        Some("1") => Ok(true),
827        Some(value) => Err(format!(
828            "MEMRA_STEP_TP_DCW={value:?} is invalid; expected 0 or 1"
829        )),
830    }
831}
832
833/// Device-counter attention path (graph increment A run EAGERLY): append at len_d - base_d,
834/// inc_i32, fa over the counter-derived window — with bucket = the effective t_kv this is
835/// bit-identical to the host-row + kvmod path (the one-partition law), and it is the exact
836/// child content the capture wraps. Rebase tokens and sub-vec-floor contexts fall back.
837pub fn step_tp_dcw_enabled() -> Result<bool, String> {
838    parse_step_tp_dcw(std::env::var("MEMRA_STEP_TP_DCW").ok().as_deref())
839}
840
841/// CUDA-graph door for the shape-stable TP segments (first increment: the device-routed
842/// expert program — per-layer multi-device parents built from per-rank children, launched on
843/// the model engine's stream; zero per-token node updates). Mechanism proven by
844/// tp_graph_probe. VALUE-IDENTICAL: the graphs replay exactly the eager kernel/copy sequence.
845pub fn step_tp_graph_enabled() -> Result<bool, String> {
846    parse_step_tp_graph(std::env::var("MEMRA_STEP_TP_GRAPH").ok().as_deref())
847}
848
849/// GRAPH-LAUNCH HEADROOM GUARD for the routed-prejoin graph door (see
850/// `spec::GRAPH_LAUNCH_MIN_FREE`): checked on the launching engine only when the door
851/// is armed (short-circuit after `step_tp_graph_enabled`), noting once per process with
852/// the sweep's grep-stable `graph replay suspended:` key.
853fn step_tp_graph_headroom_ok(e: &Engine) -> bool {
854    let ok = crate::spec::graph_launch_headroom_ok(e);
855    if !ok {
856        static NOTED: std::sync::Once = std::sync::Once::new();
857        NOTED.call_once(|| crate::spec::graph_replay_suspended_note("step-tp-routes"));
858    }
859    ok
860}
861
862/// Fused single-launch QKV projection inside the v2 decode driver — a NUMERIC-CLASS door
863/// (per-row deterministic tree reduce instead of the chunked cuBLASLt program), default OFF,
864/// gated by the run-gen argmax gate + boot battery like MEMRA_STEP_NVFP4_DEV_ROUTES.
865pub fn step_tp_qkv_fused_enabled() -> Result<bool, String> {
866    parse_step_tp_qkv_fused(std::env::var("MEMRA_STEP_TP_QKV_FUSED").ok().as_deref())
867}
868
869#[derive(Debug, Clone, PartialEq, Eq)]
870pub struct StepEpLayerSpec {
871    pub layer: usize,
872    pub devices: Vec<usize>,
873}
874
875pub type StepTpLayerSpec = StepEpLayerSpec;
876
877/// ModelPlan-driven whole-model parallel policy. `auto` removes per-layer family recipes; the
878/// loader derives its scope from dense/MoE operations and selects a registered numeric backend
879/// from the artifact tensor/activation contract.
880fn parse_auto_parallel_devices(
881    mode: Option<&str>,
882    raw_devices: Option<&str>,
883) -> Result<Option<Vec<usize>>, String> {
884    let mode = match mode {
885        None | Some("") | Some("0") | Some("off") => return Ok(None),
886        Some("auto") => "auto",
887        Some(value) => {
888            return Err(format!(
889                "MEMRA_PARALLEL={value:?} is invalid; expected off or auto"
890            ));
891        }
892    };
893    let raw = raw_devices.ok_or_else(|| {
894        format!("{mode} parallel placement requires MEMRA_PARALLEL_DEVICES=DEVICE,DEVICE[...]")
895    })?;
896    let devices =
897        raw.split(',')
898            .map(|device| {
899                device.trim().parse::<usize>().map_err(|_| {
900                    format!("MEMRA_PARALLEL_DEVICES entry {device:?} is not an integer")
901                })
902            })
903            .collect::<Result<Vec<_>, _>>()?;
904    if !(2..=crate::parallel::AUTO_PARALLEL_MAX_CARDS).contains(&devices.len()) {
905        return Err(format!(
906            "MEMRA_PARALLEL=auto requires 2..={} devices, got {}",
907            crate::parallel::AUTO_PARALLEL_MAX_CARDS,
908            devices.len()
909        ));
910    }
911    let mut unique = devices.clone();
912    unique.sort_unstable();
913    unique.dedup();
914    if unique.len() != devices.len() {
915        return Err(format!(
916            "MEMRA_PARALLEL_DEVICES must be distinct, got {devices:?}"
917        ));
918    }
919    Ok(Some(devices))
920}
921
922pub fn auto_parallel_devices() -> Result<Option<Vec<usize>>, String> {
923    parse_auto_parallel_devices(
924        std::env::var("MEMRA_PARALLEL").ok().as_deref(),
925        std::env::var("MEMRA_PARALLEL_DEVICES").ok().as_deref(),
926    )
927}
928
929fn parse_parallel_ep_device_router(value: Option<&str>) -> Result<bool, String> {
930    match value {
931        None | Some("") | Some("0") => Ok(false),
932        Some("1") => Ok(true),
933        Some(value) => Err(format!(
934            "MEMRA_PARALLEL_EP_DEVICE_ROUTER={value:?} is invalid; expected 0 or 1"
935        )),
936    }
937}
938
939pub fn parallel_ep_device_router_enabled() -> Result<bool, String> {
940    parse_parallel_ep_device_router(
941        std::env::var("MEMRA_PARALLEL_EP_DEVICE_ROUTER")
942            .ok()
943            .as_deref(),
944    )
945}
946
947fn parse_parallel_ep_graph(value: Option<&str>) -> Result<bool, String> {
948    match value {
949        None | Some("") | Some("0") => Ok(false),
950        Some("1") => Ok(true),
951        Some(value) => Err(format!(
952            "MEMRA_PARALLEL_EP_GRAPH={value:?} is invalid; expected 0 or 1"
953        )),
954    }
955}
956
957pub fn parallel_ep_graph_enabled() -> Result<bool, String> {
958    parse_parallel_ep_graph(std::env::var("MEMRA_PARALLEL_EP_GRAPH").ok().as_deref())
959}
960
961fn parse_parallel_ep_pair_down(value: Option<&str>) -> Result<bool, String> {
962    match value {
963        None | Some("") | Some("0") => Ok(false),
964        Some("1") => Ok(true),
965        Some(value) => Err(format!(
966            "MEMRA_PARALLEL_EP_PAIR_DOWN={value:?} is invalid; expected 0 or 1"
967        )),
968    }
969}
970
971pub fn parallel_ep_pair_down_enabled() -> Result<bool, String> {
972    parse_parallel_ep_pair_down(std::env::var("MEMRA_PARALLEL_EP_PAIR_DOWN").ok().as_deref())
973}
974
975fn parse_parallel_ep_q8_act(value: Option<&str>) -> Result<bool, String> {
976    match value {
977        None | Some("") | Some("0") => Ok(false),
978        Some("1") => Ok(true),
979        Some(value) => Err(format!(
980            "MEMRA_PARALLEL_EP_Q8_ACT={value:?} is invalid; expected 0 or 1"
981        )),
982    }
983}
984
985pub fn parallel_ep_q8_act_enabled() -> Result<bool, String> {
986    parse_parallel_ep_q8_act(std::env::var("MEMRA_PARALLEL_EP_Q8_ACT").ok().as_deref())
987}
988
989#[derive(Clone, Copy, Debug, PartialEq, Eq)]
990pub(crate) enum ParallelEpQ8Scope {
991    All,
992    GateUp,
993    Down,
994}
995
996impl ParallelEpQ8Scope {
997    fn label(self) -> &'static str {
998        match self {
999            Self::All => "all",
1000            Self::GateUp => "gate-up",
1001            Self::Down => "down",
1002        }
1003    }
1004}
1005
1006fn parse_parallel_ep_q8_scope(value: Option<&str>) -> Result<Option<ParallelEpQ8Scope>, String> {
1007    match value {
1008        None | Some("") => Ok(None),
1009        Some("all") => Ok(Some(ParallelEpQ8Scope::All)),
1010        Some("gate-up") => Ok(Some(ParallelEpQ8Scope::GateUp)),
1011        Some("down") => Ok(Some(ParallelEpQ8Scope::Down)),
1012        Some(value) => Err(format!(
1013            "MEMRA_PARALLEL_EP_Q8_SCOPE={value:?} is invalid; expected all, gate-up, or down"
1014        )),
1015    }
1016}
1017
1018pub(crate) fn parallel_ep_q8_scope() -> Result<Option<ParallelEpQ8Scope>, String> {
1019    parse_parallel_ep_q8_scope(std::env::var("MEMRA_PARALLEL_EP_Q8_SCOPE").ok().as_deref())
1020}
1021
1022fn parse_parallel_ep_q8_gu_paired(value: Option<&str>) -> Result<Option<bool>, String> {
1023    match value {
1024        None | Some("") => Ok(None),
1025        Some("0") => Ok(Some(false)),
1026        Some("1") => Ok(Some(true)),
1027        Some(value) => Err(format!(
1028            "MEMRA_PARALLEL_EP_Q8_GU_PAIRED={value:?} is invalid; expected 0 or 1"
1029        )),
1030    }
1031}
1032
1033fn resolve_parallel_ep_q8_gu_paired(
1034    value: Option<&str>,
1035    q8_active: bool,
1036    scope: Option<ParallelEpQ8Scope>,
1037) -> Result<bool, String> {
1038    let configured = parse_parallel_ep_q8_gu_paired(value)?;
1039    if configured == Some(true) && !q8_active {
1040        return Err("MEMRA_PARALLEL_EP_Q8_GU_PAIRED=1 requires MEMRA_PARALLEL_EP_Q8_ACT=1".into());
1041    }
1042    if configured == Some(true) && scope == Some(ParallelEpQ8Scope::Down) {
1043        return Err(
1044            "MEMRA_PARALLEL_EP_Q8_GU_PAIRED=1 requires Q8 gate/up arithmetic; \
1045             MEMRA_PARALLEL_EP_Q8_SCOPE=down keeps gate/up BF16"
1046                .into(),
1047        );
1048    }
1049    Ok(q8_active && scope != Some(ParallelEpQ8Scope::Down) && configured.unwrap_or(true))
1050}
1051
1052pub(crate) fn parallel_ep_q8_gu_paired_enabled(
1053    q8_active: bool,
1054    scope: Option<ParallelEpQ8Scope>,
1055) -> Result<bool, String> {
1056    resolve_parallel_ep_q8_gu_paired(
1057        std::env::var("MEMRA_PARALLEL_EP_Q8_GU_PAIRED")
1058            .ok()
1059            .as_deref(),
1060        q8_active,
1061        scope,
1062    )
1063}
1064
1065fn parse_step_layer_specs(
1066    flag: &str,
1067    value: Option<&str>,
1068    allow_full_model: bool,
1069) -> Result<Vec<StepEpLayerSpec>, String> {
1070    let trunk = allow_full_model.then_some(STEP37_TRUNK_LAYERS);
1071    parse_layer_specs_for_trunk(flag, value, trunk)
1072}
1073
1074/// The pure composition-refusal law behind every parallel door's UNPROVEN-pair matrix
1075/// (hoisted from the glm5 TP door, lane/glm5-extract-general): the first armed flag in
1076/// `table` refuses by name, BEFORE any parallel CUDA state exists. Each family owns its
1077/// own TABLE of `(flag, why)` rows — the reasons are gate receipts, part of the law; a
1078/// pair unlocks only with its own composition gate (the primary flag's FLAGS.md row
1079/// carries the matrix). `armed` reports whether a flag is set to `"1"` (env in
1080/// production; a plain set in unit tests — the pattern keeps tests env-mutation-free).
1081pub(crate) fn refuse_door_composition(
1082    primary: &str,
1083    table: &[(&str, &str)],
1084    armed: impl Fn(&str) -> bool,
1085) -> Result<(), String> {
1086    for (flag, why) in table {
1087        if armed(flag) {
1088            return Err(format!(
1089                "{primary} + {flag}: unproven composition, refused ({why})"
1090            ));
1091        }
1092    }
1093    Ok(())
1094}
1095
1096/// The shared `LAYER[-LAYER]@DEVICE,DEVICE[;...]` grammar behind every per-layer parallel
1097/// door. `full_model_trunk` enables the `all` shorthand and names the trunk it expands to —
1098/// the caller's model contract owns that constant, never this parser (the step door passes
1099/// `STEP37_TRUNK_LAYERS`; the glm5 door passes its own trunk length at load time).
1100pub(crate) fn parse_layer_specs_for_trunk(
1101    flag: &str,
1102    value: Option<&str>,
1103    full_model_trunk: Option<usize>,
1104) -> Result<Vec<StepEpLayerSpec>, String> {
1105    let Some(value) = value else {
1106        return Ok(Vec::new());
1107    };
1108    if value.is_empty() || value == "0" {
1109        return Ok(Vec::new());
1110    }
1111
1112    let mut specs = Vec::new();
1113    for item in value.split(';') {
1114        let (layers, devices) = item.split_once('@').ok_or_else(|| {
1115            let layers = if full_model_trunk.is_some() {
1116                "LAYER[-LAYER] or all"
1117            } else {
1118                "LAYER[-LAYER]"
1119            };
1120            format!("{flag} must be {layers}@DEVICE,DEVICE[;...]")
1121        })?;
1122        let (first, last) = if layers == "all" {
1123            let Some(trunk) = full_model_trunk else {
1124                return Err(format!(
1125                    "{flag} does not support the full-model shorthand; assign routed layers \
1126                     explicitly"
1127                ));
1128            };
1129            (0, trunk - 1)
1130        } else {
1131            match layers.split_once('-') {
1132                Some((first, last)) => {
1133                    let first = first
1134                        .parse::<usize>()
1135                        .map_err(|_| format!("{flag} layer {first:?} is not an integer"))?;
1136                    let last = last
1137                        .parse::<usize>()
1138                        .map_err(|_| format!("{flag} layer {last:?} is not an integer"))?;
1139                    if first > last {
1140                        return Err(format!("{flag} layer range {first}-{last} is reversed"));
1141                    }
1142                    if last - first + 1 > 128 {
1143                        return Err(format!(
1144                            "{flag} layer range {first}-{last} exceeds the 128-layer parser cap"
1145                        ));
1146                    }
1147                    (first, last)
1148                }
1149                None => {
1150                    let layer = layers
1151                        .parse::<usize>()
1152                        .map_err(|_| format!("{flag} layer {layers:?} is not an integer"))?;
1153                    (layer, layer)
1154                }
1155            }
1156        };
1157        let devices = devices
1158            .split(',')
1159            .map(|device| {
1160                device
1161                    .parse::<usize>()
1162                    .map_err(|_| format!("{flag} device {device:?} is not an integer"))
1163            })
1164            .collect::<Result<Vec<_>, _>>()?;
1165        if !(2..=8).contains(&devices.len()) {
1166            return Err(format!(
1167                "{flag} requires 2..=8 devices, got {}",
1168                devices.len()
1169            ));
1170        }
1171        let mut unique = devices.clone();
1172        unique.sort_unstable();
1173        unique.dedup();
1174        if unique.len() != devices.len() {
1175            return Err(format!("{flag} devices must be distinct, got {devices:?}"));
1176        }
1177        for layer in first..=last {
1178            if specs
1179                .iter()
1180                .any(|existing: &StepEpLayerSpec| existing.layer == layer)
1181            {
1182                return Err(format!("{flag} assigns layer {layer} more than once"));
1183            }
1184            specs.push(StepEpLayerSpec {
1185                layer,
1186                devices: devices.clone(),
1187            });
1188        }
1189    }
1190    Ok(specs)
1191}
1192
1193pub fn parse_step_ep_layer_specs(value: Option<&str>) -> Result<Vec<StepEpLayerSpec>, String> {
1194    parse_step_layer_specs("MEMRA_STEP_EP", value, false)
1195}
1196
1197pub fn step_ep_layer_specs() -> Result<Vec<StepEpLayerSpec>, String> {
1198    parse_step_ep_layer_specs(std::env::var("MEMRA_STEP_EP").ok().as_deref())
1199}
1200
1201pub fn parse_step_tp_layer_specs(value: Option<&str>) -> Result<Vec<StepTpLayerSpec>, String> {
1202    parse_step_layer_specs("MEMRA_STEP_TP", value, true)
1203}
1204
1205pub fn step_tp_layer_specs() -> Result<Vec<StepTpLayerSpec>, String> {
1206    parse_step_tp_layer_specs(std::env::var("MEMRA_STEP_TP").ok().as_deref())
1207}
1208
1209#[derive(Clone, Copy)]
1210pub struct E4m3BlockMatrix<'a> {
1211    pub codes: &'a [u8],
1212    pub scales: &'a [f32],
1213    pub out_features: usize,
1214    pub in_features: usize,
1215}
1216
1217impl E4m3BlockMatrix<'_> {
1218    fn validate(&self) -> Result<(), String> {
1219        let code_count = self
1220            .out_features
1221            .checked_mul(self.in_features)
1222            .ok_or_else(|| "E4M3 matrix size overflow".to_string())?;
1223        if self.codes.len() != code_count {
1224            return Err(format!(
1225                "E4M3 code count {} != {}x{} ({code_count})",
1226                self.codes.len(),
1227                self.out_features,
1228                self.in_features,
1229            ));
1230        }
1231        let scale_count =
1232            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
1233        if self.scales.len() != scale_count {
1234            return Err(format!(
1235                "E4M3 scale count {} != {scale_count} for {}x{}",
1236                self.scales.len(),
1237                self.out_features,
1238                self.in_features,
1239            ));
1240        }
1241        if !self
1242            .scales
1243            .iter()
1244            .all(|scale| scale.is_finite() && *scale > 0.0)
1245        {
1246            return Err("E4M3 scale grid contains a non-finite or non-positive value".to_string());
1247        }
1248        Ok(())
1249    }
1250}
1251
1252#[derive(Clone, Copy)]
1253pub struct E4m3ExpertBank<'a> {
1254    pub codes: &'a [u8],
1255    pub scales: &'a [f32],
1256    pub expert_count: usize,
1257    pub out_features: usize,
1258    pub in_features: usize,
1259}
1260
1261impl E4m3ExpertBank<'_> {
1262    fn validate(&self) -> Result<(), String> {
1263        if self.expert_count == 0 {
1264            return Err("E4M3 expert bank is empty".to_string());
1265        }
1266        let code_stride = self
1267            .out_features
1268            .checked_mul(self.in_features)
1269            .ok_or_else(|| "E4M3 expert code stride overflow".to_string())?;
1270        let code_count = self
1271            .expert_count
1272            .checked_mul(code_stride)
1273            .ok_or_else(|| "E4M3 expert code count overflow".to_string())?;
1274        if self.codes.len() != code_count {
1275            return Err(format!(
1276                "E4M3 expert code count {} != {}x{} ({code_count})",
1277                self.codes.len(),
1278                self.expert_count,
1279                code_stride,
1280            ));
1281        }
1282        let scale_stride =
1283            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
1284        let scale_count = self
1285            .expert_count
1286            .checked_mul(scale_stride)
1287            .ok_or_else(|| "E4M3 expert scale count overflow".to_string())?;
1288        if self.scales.len() != scale_count {
1289            return Err(format!(
1290                "E4M3 expert scale count {} != {}x{} ({scale_count})",
1291                self.scales.len(),
1292                self.expert_count,
1293                scale_stride,
1294            ));
1295        }
1296        if !self
1297            .scales
1298            .iter()
1299            .all(|scale| scale.is_finite() && *scale > 0.0)
1300        {
1301            return Err(
1302                "E4M3 expert scale grid contains a non-finite or non-positive value".to_string(),
1303            );
1304        }
1305        Ok(())
1306    }
1307
1308    pub fn expert(&self, expert: usize) -> Result<E4m3BlockMatrix<'_>, String> {
1309        if expert >= self.expert_count {
1310            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
1311        }
1312        let code_stride = self.out_features * self.in_features;
1313        let scale_stride =
1314            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
1315        Ok(E4m3BlockMatrix {
1316            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
1317            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
1318            out_features: self.out_features,
1319            in_features: self.in_features,
1320        })
1321    }
1322}
1323
1324pub struct ColumnParallelResult {
1325    pub gathered: Vec<f32>,
1326    pub rank_outputs: Vec<Vec<f32>>,
1327}
1328
1329pub struct RowParallelResult {
1330    pub reduced: Vec<f32>,
1331    pub rank_partials: Vec<Vec<f32>>,
1332}
1333
1334#[derive(Clone, Copy)]
1335pub struct Bf16Matrix<'a> {
1336    pub bytes: &'a [u8],
1337    pub out_features: usize,
1338    pub in_features: usize,
1339}
1340
1341impl Bf16Matrix<'_> {
1342    pub fn validate(&self) -> Result<(), String> {
1343        if self.out_features == 0 || self.in_features == 0 {
1344            return Err("BF16 matrix dimensions must be nonzero".into());
1345        }
1346        let expected = self
1347            .out_features
1348            .checked_mul(self.in_features)
1349            .and_then(|values| values.checked_mul(2))
1350            .ok_or("BF16 matrix byte count overflow")?;
1351        if self.bytes.len() != expected {
1352            return Err(format!(
1353                "BF16 matrix bytes {} != {}x{}x2 ({expected})",
1354                self.bytes.len(),
1355                self.out_features,
1356                self.in_features,
1357            ));
1358        }
1359        Ok(())
1360    }
1361}
1362
1363struct ResidentE4m3Rank {
1364    codes: CudaSlice<u8>,
1365    scales: CudaSlice<f32>,
1366    out_features: usize,
1367    in_features: usize,
1368}
1369
1370enum ResidentBf16Weight {
1371    Bf16(CudaSlice<u8>),
1372    F32(CudaSlice<f32>),
1373}
1374
1375impl ResidentBf16Weight {
1376    fn ordinal(&self) -> usize {
1377        match self {
1378            Self::Bf16(bytes) => bytes.ordinal(),
1379            Self::F32(values) => values.ordinal(),
1380        }
1381    }
1382}
1383
1384struct ResidentBf16Rank {
1385    weight: ResidentBf16Weight,
1386    out_features: usize,
1387    in_features: usize,
1388    /// q8_0 mirror built at load under MEMRA_STEP_TP_W8 (numeric-class door; the bf16 slab
1389    /// stays resident because every prefill/verify path is qualified against it).
1390    q8: Option<CudaSlice<u8>>,
1391}
1392
1393pub struct ResidentColumnParallel {
1394    ranks: Vec<ResidentE4m3Rank>,
1395    out_features: usize,
1396    in_features: usize,
1397}
1398
1399pub struct ResidentRowParallel {
1400    ranks: Vec<ResidentE4m3Rank>,
1401    out_features: usize,
1402    in_features: usize,
1403}
1404
1405pub struct ResidentBf16ColumnParallel {
1406    ranks: Vec<ResidentBf16Rank>,
1407    out_features: usize,
1408    in_features: usize,
1409    canonical_chunk_rows: Option<usize>,
1410}
1411
1412pub struct ResidentBf16RowParallel {
1413    ranks: Vec<ResidentBf16Rank>,
1414    out_features: usize,
1415    in_features: usize,
1416}
1417
1418pub struct ResidentStepBf16RowParallel {
1419    ranks: Vec<Vec<ResidentBf16Rank>>,
1420    out_features: usize,
1421    in_features: usize,
1422    canonical_chunk_cols: usize,
1423}
1424
1425/// Root-owned BF16 sigmoid router with persistent F32 weight, bias, and active mask.
1426pub struct ResidentSigmoidTopKRouter {
1427    weight: CudaSlice<f32>,
1428    correction_bias: CudaSlice<f32>,
1429    active: CudaSlice<u8>,
1430    root_device: usize,
1431    input_width: usize,
1432    expert_count: usize,
1433    experts_per_token: usize,
1434    active_count: usize,
1435    scaling_factor: f32,
1436    route_norm: bool,
1437}
1438
1439pub struct SigmoidTopKHostOutput {
1440    pub logits: Vec<f32>,
1441    pub selected: Vec<u32>,
1442    pub weights: Vec<f32>,
1443}
1444
1445/// Full BF16 SwiGLU weights replicated independently on every runtime rank.
1446pub struct ResidentReplicatedBf16SwiGlu {
1447    gate: Vec<ResidentBf16Rank>,
1448    up: Vec<ResidentBf16Rank>,
1449    down: Vec<ResidentBf16Rank>,
1450    input_width: usize,
1451    intermediate_width: usize,
1452}
1453
1454/// One token-major F32 batch replicated across a native-P2P rank group.
1455///
1456/// Every allocation is owned by its matching rank CUDA context. This is the generic handoff
1457/// substrate between independently sharded operators; it carries no model or topology claim.
1458pub struct ResidentReplicatedDeviceRows {
1459    ranks: Vec<CudaSlice<f32>>,
1460    tokens: usize,
1461    width: usize,
1462}
1463
1464impl ResidentReplicatedDeviceRows {
1465    pub fn tokens(&self) -> usize {
1466        self.tokens
1467    }
1468
1469    pub fn width(&self) -> usize {
1470        self.width
1471    }
1472
1473    pub fn ranks(&self) -> usize {
1474        self.ranks.len()
1475    }
1476}
1477
1478/// Canonical MoE output order: routed plus shared, then add the layer residual.
1479pub fn moe_residual_host(
1480    residual: &[f32],
1481    routed: &[f32],
1482    shared: &[f32],
1483) -> Result<Vec<f32>, String> {
1484    if residual.len() != routed.len() || residual.len() != shared.len() {
1485        return Err(format!(
1486            "MoE residual lengths residual={} routed={} shared={}",
1487            residual.len(),
1488            routed.len(),
1489            shared.len()
1490        ));
1491    }
1492    let ffn = routed
1493        .iter()
1494        .zip(shared)
1495        .map(|(&routed, &shared)| routed + shared)
1496        .collect::<Vec<_>>();
1497    Ok(residual
1498        .iter()
1499        .zip(ffn)
1500        .map(|(&residual, ffn)| residual + ffn)
1501        .collect())
1502}
1503
1504pub use memra_kv::{
1505    KvRingAppend, ResidentTpKvCache, ResidentTpKvCacheRank, TpKvAppendPlan, TpKvTransaction,
1506};
1507
1508/// Persistent TP2/TP4/TP8 routed-expert reference.
1509///
1510/// Rank-local checkpoint shards are uploaded once and remain tied to their owning CUDA context.
1511/// Activations and deterministic host-staged collectives remain per invocation. This is the
1512/// correctness substrate for serving TP/EP, not product-throughput evidence.
1513pub struct ResidentTpExpert {
1514    gate: ResidentColumnParallel,
1515    up: ResidentColumnParallel,
1516    down: ResidentRowParallel,
1517    input_width: usize,
1518    expert_width: usize,
1519}
1520
1521struct ResidentE4m3ExpertBankRank {
1522    codes: CudaSlice<u8>,
1523    scales: CudaSlice<f32>,
1524    expert_range: Range<usize>,
1525    out_features: usize,
1526    in_features: usize,
1527    code_stride: usize,
1528    scale_stride: usize,
1529    /// TP row banks are packed by native 128-wide K block so reduction can replay the
1530    /// checkpoint's global block order exactly. Other banks remain row-major.
1531    k_blocks: Option<usize>,
1532}
1533
1534struct PackedE4m3ExpertBankRank {
1535    codes: Vec<u8>,
1536    scales: Vec<f32>,
1537    expert_range: Range<usize>,
1538    out_features: usize,
1539    in_features: usize,
1540    code_stride: usize,
1541    scale_stride: usize,
1542    k_blocks: Option<usize>,
1543}
1544
1545struct ResidentEpRank {
1546    gate: ResidentE4m3ExpertBankRank,
1547    up: ResidentE4m3ExpertBankRank,
1548    down: ResidentE4m3ExpertBankRank,
1549}
1550
1551/// Persistent expert-parallel reference.
1552///
1553/// Every routed expert has exactly one owner rank. Shared experts are deliberately absent from
1554/// this object because Step replicates them per rank. Routes execute on the owner CUDA context.
1555/// The default oracle stages through host memory; the native path peer-dispatches inputs and
1556/// peer-returns owner outputs while preserving host-canonical activation and accumulation.
1557pub struct ResidentExpertParallel {
1558    ranks: Vec<ResidentEpRank>,
1559    expert_count: usize,
1560    input_width: usize,
1561    expert_width: usize,
1562}
1563
1564/// Projection-level output from the opt-in official Step grouped-FP8 gate.
1565///
1566/// Rows remain pair-major. Routing, weighted combine, and production integration are deliberately
1567/// outside this gate-only adapter.
1568pub struct StepGroupedFp8ProjectionOutput {
1569    pub gate: Vec<f32>,
1570    pub up: Vec<f32>,
1571    pub down: Vec<f32>,
1572}
1573
1574/// Prepared official Step grouped-FP8 projection gate.
1575///
1576/// The complete tensor banks, both CSR schedules, input, activation buffer, and three projection
1577/// workspaces are uploaded or allocated once. Repeated execution performs no device allocation.
1578pub struct PreparedStepGroupedFp8Gate {
1579    device: usize,
1580    gate: ResidentE4m3ExpertBankRank,
1581    up: ResidentE4m3ExpertBankRank,
1582    down: ResidentE4m3ExpertBankRank,
1583    input: CudaSlice<f32>,
1584    route_csr: DeviceExpertCsr,
1585    down_csr: DeviceExpertCsr,
1586    gate_workspace: Fp8GroupedWorkspace,
1587    up_workspace: Fp8GroupedWorkspace,
1588    down_workspace: Fp8GroupedWorkspace,
1589    activation: CudaSlice<f32>,
1590    activation_limit: Option<f32>,
1591    tokens: usize,
1592    pairs: usize,
1593}
1594
1595impl PreparedStepGroupedFp8Gate {
1596    pub fn tokens(&self) -> usize {
1597        self.tokens
1598    }
1599
1600    pub fn pairs(&self) -> usize {
1601        self.pairs
1602    }
1603}
1604
1605struct PreparedStepGroupedExpertOwner {
1606    rank: usize,
1607    global_pairs: Vec<usize>,
1608    route_csr: DeviceExpertCsr,
1609    down_csr: DeviceExpertCsr,
1610    gate_workspace: Fp8GroupedWorkspace,
1611    up_workspace: Fp8GroupedWorkspace,
1612    down_workspace: Fp8GroupedWorkspace,
1613    activation: CudaSlice<f32>,
1614}
1615
1616struct StepGroupedExpertOwnerSchedule {
1617    global_pairs: Vec<usize>,
1618    route_csr: ExpertCsr,
1619    down_csr: ExpertCsr,
1620}
1621
1622/// Prepared official Step expert-owner grouped-FP8 projection gate.
1623///
1624/// Route partitioning, owner-local CSR uploads, input dispatch, activation buffers, and grouped
1625/// workspaces are persistent. Projection rows are scattered back to canonical pair order only
1626/// after every owner has completed its rank-local program.
1627pub struct PreparedStepGroupedExpertParallelGate {
1628    rank_inputs: Vec<CudaSlice<f32>>,
1629    owners: Vec<PreparedStepGroupedExpertOwner>,
1630    activation_limit: Option<f32>,
1631    tokens: usize,
1632    pairs: usize,
1633    max_tokens: usize,
1634    max_pairs: usize,
1635    input_width: usize,
1636    expert_width: usize,
1637    generation: u64,
1638    executed_generation: Option<u64>,
1639    ready: bool,
1640}
1641
1642impl PreparedStepGroupedExpertParallelGate {
1643    pub fn tokens(&self) -> usize {
1644        self.tokens
1645    }
1646
1647    pub fn pairs(&self) -> usize {
1648        self.pairs
1649    }
1650
1651    pub fn max_tokens(&self) -> usize {
1652        self.max_tokens
1653    }
1654
1655    pub fn input_width(&self) -> usize {
1656        self.input_width
1657    }
1658
1659    pub fn expert_width(&self) -> usize {
1660        self.expert_width
1661    }
1662
1663    pub fn set_activation_limit(&mut self, limit: Option<f32>) -> Result<(), String> {
1664        validate_step_expert_activation_limit(limit)?;
1665        self.activation_limit = limit;
1666        self.executed_generation = None;
1667        Ok(())
1668    }
1669
1670    pub fn active_owners(&self) -> usize {
1671        self.owners
1672            .iter()
1673            .filter(|owner| !owner.global_pairs.is_empty())
1674            .count()
1675    }
1676
1677    pub fn owner_pair_counts(&self) -> Vec<usize> {
1678        self.owners
1679            .iter()
1680            .map(|owner| owner.global_pairs.len())
1681            .collect()
1682    }
1683
1684    pub fn generation(&self) -> u64 {
1685        self.generation
1686    }
1687}
1688
1689struct PreparedPeerWeightedRouteOwner {
1690    token_rows: CudaSlice<i32>,
1691    slots: CudaSlice<i32>,
1692    weights: CudaSlice<f32>,
1693    active_pairs: usize,
1694}
1695
1696/// Persistent root-side weighted combine for peer-owned canonical route rows.
1697///
1698/// Owner metadata, one reusable peer staging buffer, the canonical slot bank, weight bank, and
1699/// output are allocated once. Refreshes update metadata prefixes; execution peer-copies active
1700/// rows, scatters them by canonical token/slot, and reduces in the requested numeric order.
1701pub struct PreparedPeerWeightedRouteCombine {
1702    root_device: usize,
1703    owners: Vec<PreparedPeerWeightedRouteOwner>,
1704    peer_staging: CudaSlice<f32>,
1705    slots: CudaSlice<f32>,
1706    weights: CudaSlice<f32>,
1707    output: CudaSlice<f32>,
1708    peer_devices: Vec<usize>,
1709    peer_outputs: Vec<CudaSlice<f32>>,
1710    width: usize,
1711    experts_per_token: usize,
1712    max_tokens: usize,
1713    max_pairs: usize,
1714    tokens: usize,
1715    pairs: usize,
1716    projection_generation: u64,
1717    output_generation: Option<u64>,
1718    broadcast_generation: Option<u64>,
1719    ready: bool,
1720}
1721
1722impl PreparedPeerWeightedRouteCombine {
1723    pub fn tokens(&self) -> usize {
1724        self.tokens
1725    }
1726
1727    pub fn pairs(&self) -> usize {
1728        self.pairs
1729    }
1730
1731    pub fn owner_pair_counts(&self) -> Vec<usize> {
1732        self.owners.iter().map(|owner| owner.active_pairs).collect()
1733    }
1734
1735    pub fn distributed_ranks(&self) -> usize {
1736        1 + self.peer_outputs.len()
1737    }
1738}
1739
1740struct ResidentTpExpertBank {
1741    gate: Vec<ResidentE4m3ExpertBankRank>,
1742    up: Vec<ResidentE4m3ExpertBankRank>,
1743    down: Vec<ResidentE4m3ExpertBankRank>,
1744    expert_count: usize,
1745    input_width: usize,
1746    expert_width: usize,
1747}
1748
1749/// Persistent tensor-parallel expert bank.
1750///
1751/// Every rank owns a checkpoint-aligned output-row shard of every gate/up projection and an
1752/// input-column shard of every down projection. Activations cross deterministic host-staged
1753/// collectives on hosts where native peer copies are unavailable or corrupt.
1754pub struct ResidentTensorParallel {
1755    bank: ResidentTpExpertBank,
1756}
1757
1758/// Multi-context TP correctness runtime. Each rank owns an independent `Engine` and CUDA context.
1759///
1760/// Host bounce is the default oracle. Native P2P is opt-in and preserves the oracle's global
1761/// checkpoint-block reduction order; it remains a correctness path until serving gates and
1762/// repeated performance evidence qualify it.
1763pub struct TpE4m3HostBounce {
1764    devices: Vec<usize>,
1765    ranks: Vec<Engine>,
1766    native_p2p: bool,
1767    ep_device_arithmetic: bool,
1768    bulk_p2p: bool,
1769    /// v2 decode-attention workspace (MEMRA_STEP_TP_DECODE_V2). One per runtime, shared by
1770    /// every TP attention layer — the buffer shapes are geometry-constant across the trunk.
1771    decode_v2: std::sync::Mutex<Vec<StepTpDecodeV2Ws>>,
1772}
1773
1774pub struct TpKvVerifiedLayer<'a> {
1775    pub cache: &'a mut ResidentTpKvCache,
1776    pub start: usize,
1777    pub logical_len: usize,
1778    pub source_k_raw: u64,
1779    pub source_v_raw: u64,
1780    pub source_k_tok_bytes: usize,
1781    pub source_v_tok_bytes: usize,
1782}
1783
1784/// Persistent two-rank all-reduce for one replicated f32 row.
1785///
1786/// Each rank pushes its local partial directly into a peer-resident staging row, records one
1787/// reusable event, waits for the peer's corresponding event, then adds `(rank0, rank1)` in that
1788/// same operand order on both devices. Two staging rows per direction are enough because join
1789/// `j + 1` is ordered after each rank's add at join `j`, so overwriting parity `j` cannot race its
1790/// peer consumer. The collective allocates nothing and performs no host synchronization per call.
1791pub struct Tp2ReplicatedRowJoin {
1792    width: usize,
1793    parity: usize,
1794    stage0: [CudaSlice<f32>; 2],
1795    stage1: [CudaSlice<f32>; 2],
1796    stage0_raw: [u64; 2],
1797    stage1_raw: [u64; 2],
1798    event0: [CudaEvent; 2],
1799    event1: [CudaEvent; 2],
1800}
1801
1802fn validate_tp2_replicated_row_join(
1803    ranks: usize,
1804    native_p2p: bool,
1805    width: usize,
1806) -> Result<(), String> {
1807    if ranks != 2 {
1808        return Err(format!(
1809            "replicated-row join requires exactly two ranks, got {ranks}"
1810        ));
1811    }
1812    if !native_p2p {
1813        return Err("replicated-row join requires native P2P".into());
1814    }
1815    if width == 0 || width > i32::MAX as usize {
1816        return Err(format!(
1817            "replicated-row join width must be in 1..={}, got {width}",
1818            i32::MAX
1819        ));
1820    }
1821    Ok(())
1822}
1823
1824fn launch_tp2_peer_push(
1825    engine: &Engine,
1826    source: &CudaSlice<f32>,
1827    destination: u64,
1828    width: usize,
1829) -> Result<(), Box<dyn std::error::Error>> {
1830    let function = engine.func("q4e_push_f32");
1831    let config = LaunchConfig::for_num_elems(width as u32);
1832    let width = width as i64;
1833    let stream = engine.gpu.stream();
1834    let mut launch = stream.launch_builder(&function);
1835    launch.arg(source).arg(&destination).arg(&width);
1836    unsafe {
1837        launch.launch(config)?;
1838    }
1839    Ok(())
1840}
1841
1842/// Persistent workspace of the v2 rank-local decode-attention driver.
1843///
1844/// Buffers live in their producing rank's CUDA context, are never freed, and events are
1845/// re-recorded per call — the pp.rs `BoundarySlot` discipline — so the per-token path has no
1846/// cuMemAlloc, no cross-stream free, and no host round-trip. Every buffer is fully overwritten
1847/// before its consumers run in the same call; nothing carries state between tokens.
1848/// Per-rank attn_gate row shards for the fused QKV+gate kernel, in the weight class the
1849/// fused kernels read (F32 mirror or raw checkpoint bf16).
1850pub enum StepTpGateShards<'a> {
1851    F32(&'a [crate::CudaSlice<f32>]),
1852    Bf16(&'a [crate::CudaSlice<u8>]),
1853}
1854
1855pub struct StepTpDecodeV2Ws {
1856    /// T-COLUMN verify slabs (spec MTP): per-rank [t, local_dim] projections computed by
1857    /// the weight-amortized qkvg_tcol kernel; the col-select door copies one column into
1858    /// the single-row buffers and everything downstream runs the unmodified t=1 program.
1859    pub(crate) tcol_q: Vec<CudaSlice<f32>>,
1860    pub(crate) tcol_k: Vec<CudaSlice<f32>>,
1861    pub(crate) tcol_v: Vec<CudaSlice<f32>>,
1862    pub(crate) tcol_g: Vec<CudaSlice<f32>>,
1863    pub(crate) tcol_in: Vec<CudaSlice<f32>>,
1864    pub(crate) tcol_cap: usize,
1865    /// MEMRA_STEP_TP_W8 activation scratch: per-rank q8_1 quantized attention input
1866    /// ([in_f] i8 + one f32 scale pair per 32). Persistent because the alternative is an
1867    /// allocation per rank per layer per token.
1868    w8_aq: Vec<CudaSlice<i8>>,
1869    w8_ad: Vec<CudaSlice<f32>>,
1870    w8_in: usize,
1871    /// o_proj-side twin of the same scratch (its activation is the gated attention output,
1872    /// a different vector from the QKV input, so it needs its own buffers).
1873    w8o_aq: Vec<CudaSlice<i8>>,
1874    w8o_ad: Vec<CudaSlice<f32>>,
1875    w8o_in: usize,
1876    /// VERIFY-WALK q8_1 activation scratch, t columns wide (the decode scratch above is one
1877    /// row). Two sets because the QKV input and the gated attention output are different
1878    /// vectors of different widths.
1879    w8t_aq: Vec<CudaSlice<i8>>,
1880    w8t_ad: Vec<CudaSlice<f32>>,
1881    w8t_in: usize,
1882    w8t_oaq: Vec<CudaSlice<i8>>,
1883    w8t_oad: Vec<CudaSlice<f32>>,
1884    w8t_oin: usize,
1885    w8t_cap: usize,
1886    /// MEMRA_TCOL_OPROJ slabs: per-rank stashed `gated` rows ([8, local_q_dim]), per-rank
1887    /// b4_tcol partials ([8, o_out]), a root-side peer pull of rank1's partial slab, and
1888    /// the root-side joined `mixed` slab. Armed lazily by the first stash.
1889    /// MEMRA_SPEC_FA2 slabs: per-rank stashed post-rope q rows ([2, local_q_dim]), gate
1890    /// rows ([2, heads/ranks]) and the two gated outputs the per-row combine writes
1891    /// ([2, local_q_dim]). Armed lazily by the first stash.
1892    pub(crate) fa2_q: Vec<CudaSlice<f32>>,
1893    pub(crate) fa2_gate: Vec<CudaSlice<f32>>,
1894    pub(crate) fa2_gated: Vec<CudaSlice<f32>>,
1895    pub(crate) fa2_cap: usize,
1896    /// T-ROW rope/append twin scratch: per-rank roped-k rows ([8, local_kv]), per-row
1897    /// last-block counters ([8]) and the per-tick position slab ([8]). Armed with the
1898    /// fa2 slabs.
1899    rope_k_t: Vec<CudaSlice<f32>>,
1900    rope_ctr_t: Vec<CudaSlice<u32>>,
1901    rope_pos_t: Vec<CudaSlice<i32>>,
1902    /// Per-rank combined 6-word row tables, keyed by the caller's (layer, session-set,
1903    /// base-arming) signature. LEGACY: only the `MEMRA_ROWS_TAB_RESTAGE=0` rollback arm
1904    /// reads this. See `rows_tab_t` for why the key cannot be made safe.
1905    rows_tabs: Vec<std::collections::HashMap<u64, CudaSlice<u64>>>,
1906    /// Per-rank PERSISTENT 6-word row-table slab ([32, 6] u64), RESTAGED from the live
1907    /// distributed cache before every launch. Replaces the `rows_tabs` memo, whose key was
1908    /// a hash of (k pointer, base pointer, layer, t) while the table it returned also
1909    /// carried the V and LEN pointers: a session whose K buffer address was recycled hit
1910    /// another session's table and the append kernel wrote its K/V through the FREED
1911    /// pointers the entry still held. Same defect and same cure as the row-table twin in
1912    /// `step35_verify_fa_rows_join` (8c8397e0b2, Hermes `11339f5cd3c132a3`), which this
1913    /// path was left out of. One 32-word htod per rank per layer replaces the map lookup;
1914    /// no allocation, and the staging is stream-ordered exactly like `rope_pos_t`.
1915    rows_tab_t: Vec<CudaSlice<u64>>,
1916    /// HOST shadow of the last table staged under each retired memo key, used ONLY by
1917    /// `MEMRA_ROWS_TAB_STALE_SCAN=1` to prove that the retired key would have handed a live
1918    /// launch another allocation's pointers. Never read by a kernel.
1919    rows_tab_shadow: Vec<std::collections::HashMap<u64, Vec<u64>>>,
1920    tcol_gated: Vec<CudaSlice<f32>>,
1921    tcol_opart: Vec<CudaSlice<f32>>,
1922    tcol_opeer: Option<CudaSlice<f32>>,
1923    tcol_omix: Option<CudaSlice<f32>>,
1924    tcol_ocap: usize,
1925    // rank-context buffers, indexed by rank (pub(crate): the v2 driver in hybrid_forward
1926    // feeds them to the KV transaction and attention kernels between the two v2 phases)
1927    pub(crate) q_raw: Vec<CudaSlice<f32>>,
1928    pub(crate) k_raw: Vec<CudaSlice<f32>>,
1929    pub(crate) v_raw: Vec<CudaSlice<f32>>,
1930    pub(crate) q: Vec<CudaSlice<f32>>,
1931    pub(crate) k: Vec<CudaSlice<f32>>,
1932    pub(crate) pos: Vec<CudaSlice<i32>>,
1933    /// FUSION #1 last-block counters (one per rank; atomicInc auto-resets per launch).
1934    pub(crate) fuse_ctr: Vec<CudaSlice<u32>>,
1935    pub(crate) gate: Vec<CudaSlice<f32>>,
1936    pub(crate) attn_out: Vec<CudaSlice<f32>>,
1937    pub(crate) gated: Vec<CudaSlice<f32>>,
1938    /// [rank][block] O partials, each `o_out` wide, in the owning rank's context.
1939    o_partials: Vec<Vec<CudaSlice<f32>>>,
1940    /// Stable workspace pointers for the rank-done-fenced raw P2P gather. Safe
1941    /// `memcpy_dtod` creates a fresh source event for every cross-context copy; the v2
1942    /// driver already records one persistent `ev_rank` after all three source families.
1943    raw_o_partials: Vec<Vec<u64>>,
1944    raw_k: Vec<u64>,
1945    raw_v_raw: Vec<u64>,
1946    /// Recorded on each rank's stream after its per-call work; root waits before peer reads.
1947    ev_rank: Vec<CudaEvent>,
1948    // root-context buffers
1949    peer_partial: CudaSlice<f32>,
1950    reduce_a: CudaSlice<f32>,
1951    reduce_b: CudaSlice<f32>,
1952    /// Never written; the canonical zero start of the v1 add chain.
1953    zeros: CudaSlice<f32>,
1954    pub(crate) k_shadow: CudaSlice<f32>,
1955    pub(crate) v_shadow: CudaSlice<f32>,
1956    ev_refresh: CudaEvent,
1957    ev_oproj: CudaEvent,
1958    // model-engine (e) context
1959    gate_e: CudaSlice<f32>,
1960    /// Per-token stages (e-ctx, fixed addresses): one eager e-stream copy each per layer; the
1961    /// rank flows raw-copy FROM them, which is exactly the shape graph capture needs.
1962    pub(crate) h_stage: Option<CudaSlice<f32>>,
1963    pub(crate) pos_stage: Option<CudaSlice<i32>>,
1964    /// Workspace-owned per-rank attention input rows (the stage flow copies into THESE, not
1965    /// the per-layer decode_input buffers — the workspace is shared across layers, so every
1966    /// captured/raw address it uses must be layer-invariant).
1967    attn_in: Vec<CudaSlice<f32>>,
1968    /// Cached raw pointers of the stage-flow operands (set when the stages arm).
1969    raw_h_stage: u64,
1970    raw_pos_stage: u64,
1971    raw_attn_in: Vec<u64>,
1972    raw_pos: Vec<u64>,
1973    raw_o_partial1: u64,
1974    raw_peer_partial: u64,
1975    raw_k1: u64,
1976    raw_v1: u64,
1977    raw_k_shadow: u64,
1978    raw_v_shadow: u64,
1979    /// Token-graph e-context mirrors (armed by the orchestrator): the root section
1980    /// raw-copies the reduced attention output and the shadow rows here so the e-glue
1981    /// children read same-context memory (cross-context kernel args are capture-illegal).
1982    raw_mixed_stage_e: u64,
1983    raw_reduce_a: u64,
1984    raw_shadow_stage_e: (u64, u64),
1985    ev_entry: CudaEvent,
1986    e_device: usize,
1987    // geometry pins
1988    local_q_dim: usize,
1989    local_kv_dim: usize,
1990    heads: usize,
1991    pub(crate) o_out: usize,
1992    o_block_cols: usize,
1993    blocks_per_rank: usize,
1994}
1995
1996impl TpE4m3HostBounce {
1997    pub fn new(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1998        Self::new_inner(devices, false, false, false, false)
1999    }
2000
2001    pub fn new_native_p2p(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
2002        Self::new_inner(devices, false, true, false, false)
2003    }
2004
2005    pub fn new_native_p2p_device_arithmetic(
2006        devices: &[usize],
2007    ) -> Result<Self, Box<dyn std::error::Error>> {
2008        Self::new_inner(devices, false, true, true, false)
2009    }
2010
2011    pub(crate) fn new_configured(
2012        devices: &[usize],
2013        native_p2p: bool,
2014        ep_device_arithmetic: bool,
2015        bulk_p2p: bool,
2016    ) -> Result<Self, Box<dyn std::error::Error>> {
2017        Self::new_inner(devices, false, native_p2p, ep_device_arithmetic, bulk_p2p)
2018    }
2019
2020    /// Single-rank execution of the canonical checkpoint-block TP program.
2021    ///
2022    /// This is an oracle for distributed exactness, not a serving topology. It lets gates compare
2023    /// TP=1 and TP>1 with the same packing, kernel launches, and deterministic reduction order.
2024    pub fn new_single_rank_oracle(device: usize) -> Result<Self, Box<dyn std::error::Error>> {
2025        Self::new_inner(&[device], true, false, false, false)
2026    }
2027
2028    fn new_inner(
2029        devices: &[usize],
2030        allow_single_rank: bool,
2031        native_p2p: bool,
2032        ep_device_arithmetic: bool,
2033        bulk_p2p: bool,
2034    ) -> Result<Self, Box<dyn std::error::Error>> {
2035        if ep_device_arithmetic && !native_p2p {
2036            return Err("device-resident EP arithmetic requires native P2P".into());
2037        }
2038        if bulk_p2p && !native_p2p {
2039            return Err("bulk TP transport requires native P2P".into());
2040        }
2041        let minimum = if allow_single_rank { 1 } else { 2 };
2042        if !(minimum..=8).contains(&devices.len()) {
2043            return Err(format!(
2044                "TP reference requires {minimum}..=8 devices, got {}",
2045                devices.len()
2046            )
2047            .into());
2048        }
2049        let mut unique = devices.to_vec();
2050        unique.sort_unstable();
2051        unique.dedup();
2052        if unique.len() != devices.len() {
2053            return Err(format!("TP devices must be distinct, got {devices:?}").into());
2054        }
2055        let ranks = devices
2056            .iter()
2057            .map(|&device| Engine::new(device))
2058            .collect::<Result<Vec<_>, _>>()?;
2059        if native_p2p {
2060            configure_native_p2p(&ranks, devices)?;
2061        }
2062        if allow_single_rank {
2063            eprintln!(
2064                "[tp] canonical oracle transport=local device={} performance_claim=false",
2065                devices[0]
2066            );
2067        } else if native_p2p {
2068            if ep_device_arithmetic {
2069                eprintln!(
2070                    "[tp] correctness transport=native-p2p devices={devices:?} \
2071                     native_p2p=true activation=device-host-exact \
2072                     accumulation=device-host-exact output=root-readback \
2073                     bulk_p2p={bulk_p2p} performance_claim=false"
2074                );
2075            } else {
2076                eprintln!(
2077                    "[tp] correctness transport=native-p2p devices={devices:?} \
2078                     native_p2p=true activation=host-canonical bulk_p2p={bulk_p2p} \
2079                     performance_claim=false"
2080                );
2081            }
2082        } else {
2083            eprintln!(
2084                "[tp] correctness transport=host-bounce devices={devices:?} \
2085                 native_p2p=false performance_claim=false"
2086            );
2087        }
2088        Ok(Self {
2089            devices: devices.to_vec(),
2090            ranks,
2091            native_p2p,
2092            ep_device_arithmetic,
2093            bulk_p2p,
2094            decode_v2: std::sync::Mutex::new(Vec::new()),
2095        })
2096    }
2097
2098    pub fn devices(&self) -> &[usize] {
2099        &self.devices
2100    }
2101
2102    pub fn native_p2p(&self) -> bool {
2103        self.native_p2p
2104    }
2105
2106    pub fn bulk_p2p(&self) -> bool {
2107        self.bulk_p2p
2108    }
2109
2110    pub fn expert_activation_label(&self) -> &'static str {
2111        if self.ep_device_arithmetic {
2112            "device-host-exact"
2113        } else {
2114            "host-canonical"
2115        }
2116    }
2117
2118    pub fn expert_accumulation_label(&self) -> &'static str {
2119        self.expert_activation_label()
2120    }
2121
2122    pub fn expert_output_label(&self) -> &'static str {
2123        if self.ep_device_arithmetic {
2124            "root-readback"
2125        } else {
2126            "host-accumulated"
2127        }
2128    }
2129
2130    pub fn transport_label(&self) -> &'static str {
2131        if self.devices.len() == 1 {
2132            "local"
2133        } else if self.native_p2p {
2134            "native-p2p"
2135        } else {
2136            "host-bounce"
2137        }
2138    }
2139
2140    pub fn device_names(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
2141        self.ranks
2142            .iter()
2143            .map(|rank| rank.ctx().name().map_err(Into::into))
2144            .collect()
2145    }
2146
2147    /// Correctness-gate access to the engine that owns one TP rank.
2148    ///
2149    /// Model execution should prefer collective methods on this runtime. This accessor exists so
2150    /// focused gates can prove that the rank-local projection outputs remain device-resident
2151    /// through the next ownership boundary before that boundary is wired into serving.
2152    pub fn rank_engine(&self, rank: usize) -> Option<&Engine> {
2153        self.ranks.get(rank)
2154    }
2155
2156    /// Allocate the persistent ping-pong staging and event state for a two-rank replicated-row
2157    /// all-reduce. The caller owns one instance per concurrently live collective sequence.
2158    pub fn prepare_tp2_replicated_row_join(
2159        &self,
2160        width: usize,
2161    ) -> Result<Tp2ReplicatedRowJoin, Box<dyn std::error::Error>> {
2162        validate_tp2_replicated_row_join(self.ranks.len(), self.native_p2p, width)?;
2163        let rank0 = &self.ranks[0];
2164        let rank1 = &self.ranks[1];
2165
2166        let (stage0, stage0_raw, event0) = {
2167            let _main = rank0.gpu.enter_main()?;
2168            let stage = [rank0.zeros(width)?, rank0.zeros(width)?];
2169            let stream = rank0.gpu.stream();
2170            let raw = [
2171                stage[0].device_ptr(&stream).0,
2172                stage[1].device_ptr(&stream).0,
2173            ];
2174            let events = [rank0.ctx().new_event(None)?, rank0.ctx().new_event(None)?];
2175            (stage, raw, events)
2176        };
2177        let (stage1, stage1_raw, event1) = {
2178            let _main = rank1.gpu.enter_main()?;
2179            let stage = [rank1.zeros(width)?, rank1.zeros(width)?];
2180            let stream = rank1.gpu.stream();
2181            let raw = [
2182                stage[0].device_ptr(&stream).0,
2183                stage[1].device_ptr(&stream).0,
2184            ];
2185            let events = [rank1.ctx().new_event(None)?, rank1.ctx().new_event(None)?];
2186            (stage, raw, events)
2187        };
2188        Ok(Tp2ReplicatedRowJoin {
2189            width,
2190            parity: 0,
2191            stage0,
2192            stage1,
2193            stage0_raw,
2194            stage1_raw,
2195            event0,
2196            event1,
2197        })
2198    }
2199
2200    /// Sum one rank-local partial from each of two ranks and publish the same canonical
2201    /// `(rank0 + rank1)` row on both devices. The method only enqueues work; subsequent work on
2202    /// each rank's stream consumes its corresponding output without a host fence.
2203    pub fn tp2_replicated_row_join(
2204        &self,
2205        join: &mut Tp2ReplicatedRowJoin,
2206        partial0: &CudaSlice<f32>,
2207        partial1: &CudaSlice<f32>,
2208        output0: &mut CudaSlice<f32>,
2209        output1: &mut CudaSlice<f32>,
2210    ) -> Result<(), Box<dyn std::error::Error>> {
2211        validate_tp2_replicated_row_join(self.ranks.len(), self.native_p2p, join.width)?;
2212        let rank0 = &self.ranks[0];
2213        let rank1 = &self.ranks[1];
2214        let width = join.width;
2215        if partial0.len() < width
2216            || partial1.len() < width
2217            || output0.len() < width
2218            || output1.len() < width
2219            || partial0.ordinal() != rank0.ctx().ordinal()
2220            || output0.ordinal() != rank0.ctx().ordinal()
2221            || partial1.ordinal() != rank1.ctx().ordinal()
2222            || output1.ordinal() != rank1.ctx().ordinal()
2223        {
2224            return Err("replicated-row join buffer geometry or ownership mismatch".into());
2225        }
2226
2227        let parity = join.parity;
2228        {
2229            let _main = rank0.gpu.enter_main()?;
2230            launch_tp2_peer_push(rank0, partial0, join.stage1_raw[parity], width)?;
2231            join.event0[parity].record(&rank0.gpu.stream())?;
2232        }
2233        {
2234            let _main = rank1.gpu.enter_main()?;
2235            launch_tp2_peer_push(rank1, partial1, join.stage0_raw[parity], width)?;
2236            join.event1[parity].record(&rank1.gpu.stream())?;
2237        }
2238        {
2239            let _main = rank0.gpu.enter_main()?;
2240            rank0.gpu.stream().wait(&join.event1[parity])?;
2241            rank0.add(partial0, &join.stage0[parity], output0, width)?;
2242        }
2243        {
2244            let _main = rank1.gpu.enter_main()?;
2245            rank1.gpu.stream().wait(&join.event0[parity])?;
2246            rank1.add(&join.stage1[parity], partial1, output1, width)?;
2247        }
2248        join.parity ^= 1;
2249        Ok(())
2250    }
2251
2252    pub fn allocate_tp_kv_cache(
2253        &self,
2254        kv_dim_k: usize,
2255        kv_dim_v: usize,
2256        capacity: usize,
2257    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
2258        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, None)
2259    }
2260
2261    pub fn allocate_tp_swa_kv_cache(
2262        &self,
2263        kv_dim_k: usize,
2264        kv_dim_v: usize,
2265        capacity: usize,
2266        window: usize,
2267    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
2268        if window == 0 {
2269            return Err("TP SWA KV window must be nonzero".into());
2270        }
2271        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, Some(window))
2272    }
2273
2274    fn allocate_tp_kv_cache_inner(
2275        &self,
2276        kv_dim_k: usize,
2277        kv_dim_v: usize,
2278        capacity: usize,
2279        window: Option<usize>,
2280    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
2281        if capacity == 0 || capacity > i32::MAX as usize {
2282            return Err(
2283                format!("TP KV capacity must be in 1..={}, got {capacity}", i32::MAX).into(),
2284            );
2285        }
2286        let tp = self.ranks.len();
2287        let shape = crate::cache::tp_kv_rank_allocation_shape(kv_dim_k, kv_dim_v, tp)?;
2288        let physical_rows = window
2289            .map(|window| crate::cache::swa_ring_rows(window, capacity))
2290            .unwrap_or(capacity);
2291        let k_plane_bytes = physical_rows
2292            .checked_mul(shape.k_token_bytes)
2293            .and_then(|bytes| bytes.checked_add(8))
2294            .ok_or("TP KV K plane-byte overflow")?;
2295        let v_plane_bytes = physical_rows
2296            .checked_mul(shape.v_token_bytes)
2297            .and_then(|bytes| bytes.checked_add(8))
2298            .ok_or("TP KV V plane-byte overflow")?;
2299        let mut ranks = Vec::with_capacity(tp);
2300        for engine in &self.ranks {
2301            let _main = engine.gpu.enter_main()?;
2302            ranks.push(ResidentTpKvCacheRank::new(
2303                engine.alloc_u8(k_plane_bytes)?,
2304                engine.alloc_u8(v_plane_bytes)?,
2305                engine.htod_i32(&[0])?,
2306            ));
2307        }
2308        Ok(match window {
2309            Some(window) => ResidentTpKvCache::new_swa(
2310                ranks,
2311                shape.kv_dim_k,
2312                shape.kv_dim_v,
2313                shape.k_token_bytes,
2314                shape.v_token_bytes,
2315                capacity,
2316                window,
2317            ),
2318            None => ResidentTpKvCache::new(
2319                ranks,
2320                shape.kv_dim_k,
2321                shape.kv_dim_v,
2322                shape.k_token_bytes,
2323                shape.v_token_bytes,
2324                capacity,
2325            ),
2326        })
2327    }
2328
2329    pub fn grow_tp_kv_cache(
2330        &self,
2331        source: &ResidentTpKvCache,
2332        target_capacity: usize,
2333        rows: usize,
2334    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
2335        self.validate_tp_kv_cache(source)?;
2336        let plan = source.prepare_grow(target_capacity, rows)?;
2337        let ranks = self.ranks.len();
2338        let global_k = source
2339            .kv_dim_k()
2340            .checked_mul(ranks)
2341            .ok_or("TP KV grow global K dimension overflow")?;
2342        let global_v = source
2343            .kv_dim_v()
2344            .checked_mul(ranks)
2345            .ok_or("TP KV grow global V dimension overflow")?;
2346        let mut target = match source.ring_window() {
2347            Some(window) => {
2348                self.allocate_tp_swa_kv_cache(global_k, global_v, target_capacity, window)?
2349            }
2350            None => self.allocate_tp_kv_cache(global_k, global_v, target_capacity)?,
2351        };
2352        self.validate_tp_kv_cache(&target)?;
2353
2354        for (rank, engine) in self.ranks.iter().enumerate() {
2355            let _main = engine.gpu.enter_main()?;
2356            let src = source
2357                .rank(rank)
2358                .ok_or_else(|| format!("TP KV grow source has no rank {rank}"))?;
2359            let dst = target
2360                .rank_mut(rank)
2361                .ok_or_else(|| format!("TP KV grow target has no rank {rank}"))?;
2362            if plan.k_bytes() > 0 {
2363                engine.copy_u8_range_into(
2364                    dst.k_mut(),
2365                    0,
2366                    src.k(),
2367                    plan.source_row() * source.k_tok_bytes(),
2368                    plan.k_bytes(),
2369                )?;
2370            }
2371            if plan.v_bytes() > 0 {
2372                engine.copy_u8_range_into(
2373                    dst.v_mut(),
2374                    0,
2375                    src.v(),
2376                    plan.source_row() * source.v_tok_bytes(),
2377                    plan.v_bytes(),
2378                )?;
2379            }
2380        }
2381        self.set_tp_kv_len_mirrors(&mut target, plan.rows())?;
2382
2383        // The caller publishes `target` and immediately drops `source`. Drain every rank's
2384        // stream so an async-pool free cannot recycle a source plane under an in-flight D2D copy.
2385        for engine in &self.ranks {
2386            let _main = engine.gpu.enter_main()?;
2387            engine.stream().synchronize()?;
2388        }
2389        let physical_copy_rows = plan.copy_rows();
2390        target.publish_grow(plan)?;
2391        eprintln!(
2392            "[step-tp-kv-grow] rows={} source_capacity={} target_capacity={} ranks={} \
2393             physical_copy_rows={} ring_window={:?} copy=rank-local-dtod \
2394             rank_streams_synchronized=true generation_preserved=true",
2395            rows,
2396            source.capacity(),
2397            target_capacity,
2398            ranks,
2399            physical_copy_rows,
2400            source.ring_window(),
2401        );
2402        Ok(target)
2403    }
2404
2405    pub fn hydrate_tp_kv_cache(
2406        &self,
2407        cache: &mut ResidentTpKvCache,
2408        rows: usize,
2409        k_rows: &[u8],
2410        v_rows: &[u8],
2411    ) -> Result<(), Box<dyn std::error::Error>> {
2412        self.hydrate_tp_kv_cache_from(cache, rows, 0, k_rows, v_rows)
2413    }
2414
2415    pub fn hydrate_tp_kv_cache_from(
2416        &self,
2417        cache: &mut ResidentTpKvCache,
2418        logical_len: usize,
2419        resident_start: usize,
2420        k_rows: &[u8],
2421        v_rows: &[u8],
2422    ) -> Result<(), Box<dyn std::error::Error>> {
2423        self.validate_tp_kv_cache(cache)?;
2424        if cache.committed_len() != 0 || cache.staged_len() != 0 {
2425            return Err(format!(
2426                "TP KV hydration requires an empty cache, got committed/staged={}/{}",
2427                cache.committed_len(),
2428                cache.staged_len()
2429            )
2430            .into());
2431        }
2432        if resident_start > logical_len || logical_len > cache.capacity() {
2433            return Err(format!(
2434                "TP KV hydration range [{resident_start},{logical_len}) exceeds capacity {}",
2435                cache.capacity(),
2436            )
2437            .into());
2438        }
2439        let rows = logical_len - resident_start;
2440        if rows > cache.physical_capacity() {
2441            return Err(format!(
2442                "TP KV hydration rows {rows} exceed physical capacity {}",
2443                cache.physical_capacity()
2444            )
2445            .into());
2446        }
2447        for rank in 0..self.ranks.len() {
2448            let k_rank =
2449                cache_rank_rows(k_rows, rows, cache.k_tok_bytes(), self.ranks.len(), rank)?;
2450            let v_rank =
2451                cache_rank_rows(v_rows, rows, cache.v_tok_bytes(), self.ranks.len(), rank)?;
2452            let engine = &self.ranks[rank];
2453            let _main = engine.gpu.enter_main()?;
2454            let rank_cache = cache
2455                .rank_mut(rank)
2456                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
2457            engine.htod_u8_into(rank_cache.k_mut(), 0, &k_rank)?;
2458            engine.htod_u8_into(rank_cache.v_mut(), 0, &v_rank)?;
2459        }
2460        cache.publish_hydration(logical_len, resident_start)?;
2461        Ok(())
2462    }
2463
2464    /// Restore rows retained from a speculative verify walk into an already-live distributed
2465    /// cache. The verify oracle appends canonical full-width quantized K/V rows on the model
2466    /// device. Each destination rank pulls its own contiguous KV-head slice over native P2P; its
2467    /// length mirror is published on that same rank stream after every row copy.
2468    #[allow(clippy::too_many_arguments)]
2469    pub fn restore_tp_kv_rows_from_device(
2470        &self,
2471        cache: &mut ResidentTpKvCache,
2472        start: usize,
2473        logical_len: usize,
2474        source_k_raw: u64,
2475        source_v_raw: u64,
2476        source_k_tok_bytes: usize,
2477        source_v_tok_bytes: usize,
2478    ) -> Result<(), Box<dyn std::error::Error>> {
2479        self.validate_tp_kv_cache(cache)?;
2480        if cache.committed_len() != cache.staged_len() {
2481            return Err(format!(
2482                "TP KV verify restore requires quiescent state, got committed/staged={}/{}",
2483                cache.committed_len(),
2484                cache.staged_len()
2485            )
2486            .into());
2487        }
2488        if start > logical_len || logical_len > cache.capacity() {
2489            return Err(format!(
2490                "TP KV verify restore range [{start},{logical_len}) exceeds capacity {}",
2491                cache.capacity()
2492            )
2493            .into());
2494        }
2495        let rows = logical_len - start;
2496        let physical = cache.physical_range(start, logical_len)?;
2497        if physical.len() != rows {
2498            return Err(format!(
2499                "TP KV verify restore range [{start},{logical_len}) is not physically contiguous"
2500            )
2501            .into());
2502        }
2503        let ranks = self.ranks.len();
2504        let k_tok_bytes = cache.k_tok_bytes();
2505        let v_tok_bytes = cache.v_tok_bytes();
2506        if source_k_tok_bytes != k_tok_bytes * ranks || source_v_tok_bytes != v_tok_bytes * ranks {
2507            return Err(format!(
2508                "TP KV verify source token bytes k={source_k_tok_bytes} v={source_v_tok_bytes} \
2509                 do not match distributed k={}x{ranks} v={}x{ranks}",
2510                k_tok_bytes, v_tok_bytes
2511            )
2512            .into());
2513        }
2514        for rank in 0..self.ranks.len() {
2515            let engine = &self.ranks[rank];
2516            let _main = engine.gpu.enter_main()?;
2517            use cudarc::driver::DevicePtr;
2518            let stream = engine.stream();
2519            let k_offset = physical
2520                .start
2521                .checked_mul(k_tok_bytes)
2522                .ok_or("TP KV verify K offset overflow")?;
2523            let v_offset = physical
2524                .start
2525                .checked_mul(v_tok_bytes)
2526                .ok_or("TP KV verify V offset overflow")?;
2527            let rank_cache = cache
2528                .rank_mut(rank)
2529                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
2530            let k_dst = {
2531                let (pointer, _guard) = rank_cache.k_mut().device_ptr(&stream);
2532                pointer
2533            };
2534            let v_dst = {
2535                let (pointer, _guard) = rank_cache.v_mut().device_ptr(&stream);
2536                pointer
2537            };
2538            for row in 0..rows {
2539                let k_src = source_k_raw + (row * source_k_tok_bytes + rank * k_tok_bytes) as u64;
2540                let v_src = source_v_raw + (row * source_v_tok_bytes + rank * v_tok_bytes) as u64;
2541                let k_out = k_dst + (k_offset + row * k_tok_bytes) as u64;
2542                let v_out = v_dst + (v_offset + row * v_tok_bytes) as u64;
2543                raw_copy_bytes(k_out, k_src, k_tok_bytes, engine)?;
2544                raw_copy_bytes(v_out, v_src, v_tok_bytes, engine)?;
2545            }
2546        }
2547        cache.rewind_to(logical_len)?;
2548        Ok(())
2549    }
2550
2551    /// Batch the accepted verify rows of every uniform TP-attention layer into one kernel per
2552    /// rank. Returns `false` before enqueueing anything when the layer group is not uniform, so
2553    /// the caller can use the existing per-layer repair without splitting semantics.
2554    pub fn restore_tp_kv_layers_from_device(
2555        &self,
2556        layers: &mut [TpKvVerifiedLayer<'_>],
2557    ) -> Result<bool, Box<dyn std::error::Error>> {
2558        let Some(first) = layers.first() else {
2559            return Ok(false);
2560        };
2561        if !self.native_p2p || first.start >= first.logical_len {
2562            return Ok(false);
2563        }
2564        let ranks = self.ranks.len();
2565        let rows = first.logical_len - first.start;
2566        let logical_len = first.logical_len;
2567        let k_row_bytes = first.cache.k_tok_bytes();
2568        let v_row_bytes = first.cache.v_tok_bytes();
2569        let k_src_stride = first.source_k_tok_bytes;
2570        let v_src_stride = first.source_v_tok_bytes;
2571        let Some(expected_k_stride) = k_row_bytes.checked_mul(ranks) else {
2572            return Ok(false);
2573        };
2574        let Some(expected_v_stride) = v_row_bytes.checked_mul(ranks) else {
2575            return Ok(false);
2576        };
2577        if k_src_stride != expected_k_stride || v_src_stride != expected_v_stride {
2578            return Ok(false);
2579        }
2580
2581        for layer in layers.iter() {
2582            self.validate_tp_kv_cache(layer.cache)?;
2583            if layer.cache.committed_len() != layer.cache.staged_len()
2584                || layer.start > layer.logical_len
2585                || layer.logical_len != logical_len
2586                || layer.logical_len - layer.start != rows
2587                || layer.cache.k_tok_bytes() != k_row_bytes
2588                || layer.cache.v_tok_bytes() != v_row_bytes
2589                || layer.source_k_tok_bytes != k_src_stride
2590                || layer.source_v_tok_bytes != v_src_stride
2591            {
2592                return Ok(false);
2593            }
2594            let physical = layer.cache.physical_range(layer.start, layer.logical_len)?;
2595            if physical.len() != rows {
2596                return Ok(false);
2597            }
2598        }
2599
2600        for rank in 0..ranks {
2601            let engine = &self.ranks[rank];
2602            let _main = engine.gpu.enter_main()?;
2603            let stream = engine.stream();
2604            let n = layers.len();
2605            let mut table = vec![0u64; 5 * n];
2606            for (index, layer) in layers.iter_mut().enumerate() {
2607                let physical = layer.cache.physical_range(layer.start, layer.logical_len)?;
2608                let rank_cache = layer
2609                    .cache
2610                    .rank_mut(rank)
2611                    .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
2612                table[index] = layer.source_k_raw + (rank * k_row_bytes) as u64;
2613                table[n + index] = layer.source_v_raw + (rank * v_row_bytes) as u64;
2614                table[2 * n + index] = rank_cache.k_mut().device_ptr(&stream).0
2615                    + (physical.start * k_row_bytes) as u64;
2616                table[3 * n + index] = rank_cache.v_mut().device_ptr(&stream).0
2617                    + (physical.start * v_row_bytes) as u64;
2618                table[4 * n + index] = rank_cache.len_d_mut().device_ptr(&stream).0;
2619            }
2620            let table = engine.htod_u64(&table)?;
2621            engine.copy_batch_uniform_kv_u8_set_len(
2622                &table,
2623                n,
2624                rows,
2625                k_row_bytes,
2626                v_row_bytes,
2627                k_src_stride,
2628                v_src_stride,
2629                logical_len,
2630            )?;
2631        }
2632        let layer_count = layers.len();
2633        for layer in layers.iter_mut() {
2634            layer.cache.publish_device_rewind(logical_len)?;
2635        }
2636        static ANNOUNCED: std::sync::Once = std::sync::Once::new();
2637        ANNOUNCED.call_once(|| {
2638            eprintln!(
2639                "[tp-kv-verify-batch] engaged: layers={} ranks={ranks} rows={rows} \
2640                 k_row_bytes={k_row_bytes} v_row_bytes={v_row_bytes}",
2641                layer_count
2642            );
2643        });
2644        Ok(true)
2645    }
2646
2647    pub fn append_tp_kv_transaction(
2648        &self,
2649        cache: &mut ResidentTpKvCache,
2650        transaction: TpKvTransaction,
2651        k_shards: &[CudaSlice<f32>],
2652        v_shards: &[CudaSlice<f32>],
2653        rows: usize,
2654    ) -> Result<(), Box<dyn std::error::Error>> {
2655        self.append_tp_kv_transaction_inner(cache, transaction, k_shards, v_shards, rows, false)
2656    }
2657
2658    /// `external_rank_appends`: the dcw path already wrote the rank rows (device-counter
2659    /// append) — run everything EXCEPT the per-rank quantize/append loop (plan validation,
2660    /// rebase arm — unreachable when the caller peeked — and the absolute len-mirror sets,
2661    /// which land the same value the in-stream inc produced).
2662    #[allow(clippy::too_many_arguments)]
2663    pub fn append_tp_kv_transaction_inner(
2664        &self,
2665        cache: &mut ResidentTpKvCache,
2666        transaction: TpKvTransaction,
2667        k_shards: &[CudaSlice<f32>],
2668        v_shards: &[CudaSlice<f32>],
2669        rows: usize,
2670        external_rank_appends: bool,
2671    ) -> Result<(), Box<dyn std::error::Error>> {
2672        self.validate_tp_kv_cache(cache)?;
2673        let plan = cache.prepare_append(transaction, rows)?;
2674        let target = plan.target();
2675        let expected_k = rows
2676            .checked_mul(cache.kv_dim_k())
2677            .ok_or("TP KV K append size overflow")?;
2678        let expected_v = rows
2679            .checked_mul(cache.kv_dim_v())
2680            .ok_or("TP KV V append size overflow")?;
2681        // external_rank_appends passes no shards — the graph's dcw appends already wrote
2682        // the rank rows, so this call is bookkeeping-only and the shard slices are unused.
2683        if !external_rank_appends
2684            && (k_shards.len() != self.ranks.len() || v_shards.len() != self.ranks.len())
2685        {
2686            return Err(format!(
2687                "TP KV append shard counts k={} v={} != ranks {}",
2688                k_shards.len(),
2689                v_shards.len(),
2690                self.ranks.len()
2691            )
2692            .into());
2693        }
2694        let kv_dim_k = cache.kv_dim_k();
2695        let kv_dim_v = cache.kv_dim_v();
2696        let k_tok_bytes = cache.k_tok_bytes();
2697        let v_tok_bytes = cache.v_tok_bytes();
2698        if let Some(KvRingAppend::Rebase {
2699            src_row,
2700            keep_rows,
2701            new_base,
2702            ..
2703        }) = plan.ring_append()
2704        {
2705            for rank in 0..self.ranks.len() {
2706                let engine = &self.ranks[rank];
2707                let _main = engine.gpu.enter_main()?;
2708                let rank_cache = cache
2709                    .rank_mut(rank)
2710                    .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
2711                if keep_rows > 0 {
2712                    let k_len = keep_rows
2713                        .checked_mul(k_tok_bytes)
2714                        .ok_or("TP KV K rebase-byte overflow")?;
2715                    let v_len = keep_rows
2716                        .checked_mul(v_tok_bytes)
2717                        .ok_or("TP KV V rebase-byte overflow")?;
2718                    let mut k_tmp = engine.alloc_u8_uninit(k_len)?;
2719                    let mut v_tmp = engine.alloc_u8_uninit(v_len)?;
2720                    engine.copy_u8_range_into(
2721                        &mut k_tmp,
2722                        0,
2723                        rank_cache.k(),
2724                        src_row * k_tok_bytes,
2725                        k_len,
2726                    )?;
2727                    engine.copy_u8_range_into(
2728                        &mut v_tmp,
2729                        0,
2730                        rank_cache.v(),
2731                        src_row * v_tok_bytes,
2732                        v_len,
2733                    )?;
2734                    engine.copy_u8_into(rank_cache.k_mut(), 0, &k_tmp, k_len)?;
2735                    engine.copy_u8_into(rank_cache.v_mut(), 0, &v_tmp, v_len)?;
2736                }
2737                // dcw base mirror (graph increment A): physical row 0 now holds logical
2738                // row `new_base`; armed device mirrors track it (rebases are rare host
2739                // events, so a host set here is the whole maintenance cost).
2740                if rank_cache.base_d().is_some() {
2741                    let value = new_base as i32;
2742                    let rank_cache = cache
2743                        .rank_mut(rank)
2744                        .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
2745                    if let Some(base_d) = rank_cache.base_d_mut() {
2746                        engine.set_i32_one(base_d, value)?;
2747                    }
2748                }
2749            }
2750        }
2751        cache.publish_append_rebase(plan)?;
2752        let write_row = plan.write_row();
2753        for rank in 0..self.ranks.len() {
2754            if external_rank_appends {
2755                break;
2756            }
2757            let engine = &self.ranks[rank];
2758            let _main = engine.gpu.enter_main()?;
2759            if k_shards[rank].len() != expected_k
2760                || v_shards[rank].len() != expected_v
2761                || k_shards[rank].ordinal() != engine.ctx().ordinal()
2762                || v_shards[rank].ordinal() != engine.ctx().ordinal()
2763            {
2764                return Err(format!(
2765                    "TP KV rank {rank} shard geometry/device k={}/{} v={}/{} \
2766                     != expected {expected_k}/{expected_v} on device {}",
2767                    k_shards[rank].len(),
2768                    k_shards[rank].ordinal(),
2769                    v_shards[rank].len(),
2770                    v_shards[rank].ordinal(),
2771                    engine.ctx().ordinal(),
2772                )
2773                .into());
2774            }
2775            let rank_cache = cache
2776                .rank_mut(rank)
2777                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
2778            let (rank_k, rank_v) = rank_cache.planes_mut();
2779            engine.append_kv_quantized_rows(
2780                &k_shards[rank],
2781                &v_shards[rank],
2782                rank_k,
2783                rank_v,
2784                write_row,
2785                rows,
2786                kv_dim_k,
2787                kv_dim_v,
2788                k_tok_bytes,
2789                v_tok_bytes,
2790                Engine::kv_fp8_on(),
2791            )?;
2792        }
2793        if !external_rank_appends {
2794            // dcw appends advance the device counters with in-stream inc_i32; an absolute set
2795            // here would race the merged per-rank append (it reads len_d for its write row).
2796            self.set_tp_kv_len_mirrors(cache, target)?;
2797        }
2798        cache.publish_append_plan(plan)?;
2799        Ok(())
2800    }
2801
2802    pub fn commit_tp_kv_transaction(
2803        &self,
2804        cache: &mut ResidentTpKvCache,
2805        transaction: TpKvTransaction,
2806        accepted_rows: usize,
2807    ) -> Result<(), Box<dyn std::error::Error>> {
2808        self.validate_tp_kv_cache(cache)?;
2809        let target = cache.commit_target(transaction, accepted_rows)?;
2810        self.set_tp_kv_len_mirrors(cache, target)?;
2811        cache.publish_finalize(transaction, target)?;
2812        Ok(())
2813    }
2814
2815    /// Commit for the external-appends (token graph) path: host bookkeeping only, NO absolute
2816    /// len-mirror sets. The graph's in-stream inc_i32 owns the device counters; a rank-stream
2817    /// set here has no ordering edge against the NEXT token's graph launch (graph children do
2818    /// not wait on the rank streams), so it can land AFTER that graph's inc and drag the
2819    /// counter backward mid-token.
2820    pub fn commit_tp_kv_transaction_external(
2821        &self,
2822        cache: &mut ResidentTpKvCache,
2823        transaction: TpKvTransaction,
2824        accepted_rows: usize,
2825    ) -> Result<(), Box<dyn std::error::Error>> {
2826        self.validate_tp_kv_cache(cache)?;
2827        let target = cache.commit_target(transaction, accepted_rows)?;
2828        cache.publish_finalize(transaction, target)?;
2829        Ok(())
2830    }
2831
2832    pub fn rollback_tp_kv_transaction(
2833        &self,
2834        cache: &mut ResidentTpKvCache,
2835        transaction: TpKvTransaction,
2836    ) -> Result<(), Box<dyn std::error::Error>> {
2837        self.validate_tp_kv_cache(cache)?;
2838        cache.validate_transaction(transaction)?;
2839        let target = transaction.base_len();
2840        self.set_tp_kv_len_mirrors(cache, target)?;
2841        cache.publish_finalize(transaction, target)?;
2842        Ok(())
2843    }
2844
2845    pub fn tp_kv_device_lengths(
2846        &self,
2847        cache: &ResidentTpKvCache,
2848    ) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
2849        self.validate_tp_kv_cache(cache)?;
2850        let mut lengths = Vec::with_capacity(self.ranks.len());
2851        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks()) {
2852            let _main = engine.gpu.enter_main()?;
2853            lengths.push(engine.dtoh_i32_one(rank_cache.len_d())?);
2854        }
2855        Ok(lengths)
2856    }
2857
2858    fn set_tp_kv_len_mirrors(
2859        &self,
2860        cache: &mut ResidentTpKvCache,
2861        len: usize,
2862    ) -> Result<(), Box<dyn std::error::Error>> {
2863        let len = i32::try_from(len).map_err(|_| "TP KV length exceeds i32 device mirror")?;
2864        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks_mut()) {
2865            let _main = engine.gpu.enter_main()?;
2866            engine.set_i32_one(rank_cache.len_d_mut(), len)?;
2867        }
2868        Ok(())
2869    }
2870
2871    fn validate_tp_kv_cache(
2872        &self,
2873        cache: &ResidentTpKvCache,
2874    ) -> Result<(), Box<dyn std::error::Error>> {
2875        if cache.ranks_len() != self.ranks.len() {
2876            return Err(format!(
2877                "TP KV cache ranks {} != runtime ranks {}",
2878                cache.ranks_len(),
2879                self.ranks.len()
2880            )
2881            .into());
2882        }
2883        let expected_k = cache
2884            .physical_capacity()
2885            .checked_mul(cache.k_tok_bytes())
2886            .and_then(|bytes| bytes.checked_add(8))
2887            .ok_or("TP KV K plane validation overflow")?;
2888        let expected_v = cache
2889            .physical_capacity()
2890            .checked_mul(cache.v_tok_bytes())
2891            .and_then(|bytes| bytes.checked_add(8))
2892            .ok_or("TP KV V plane validation overflow")?;
2893        for (rank, (engine, rank_cache)) in self.ranks.iter().zip(cache.ranks()).enumerate() {
2894            let device = engine.ctx().ordinal();
2895            if rank_cache.k().len() != expected_k
2896                || rank_cache.v().len() != expected_v
2897                || rank_cache.len_d().len() != 1
2898                || rank_cache.k().ordinal() != device
2899                || rank_cache.v().ordinal() != device
2900                || rank_cache.len_d().ordinal() != device
2901            {
2902                return Err(format!(
2903                    "TP KV rank {rank} residency does not match device {device} or plane geometry"
2904                )
2905                .into());
2906            }
2907        }
2908        Ok(())
2909    }
2910
2911    pub fn full(
2912        &self,
2913        matrix: E4m3BlockMatrix<'_>,
2914        activations: &[f32],
2915        tokens: usize,
2916    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2917        matrix.validate()?;
2918        validate_activations(activations, tokens, matrix.in_features)?;
2919        run_rank(&self.ranks[0], matrix, activations, tokens)
2920    }
2921
2922    /// Column-parallel projection. Weight output rows and their scale rows are partitioned across
2923    /// ranks. The input is host-broadcast, rank-local projections execute independently, and the
2924    /// output is host-gathered in rank order.
2925    #[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
2926    pub fn column_parallel(
2927        &self,
2928        matrix: E4m3BlockMatrix<'_>,
2929        activations: &[f32],
2930        tokens: usize,
2931    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2932        matrix.validate()?;
2933        validate_activations(activations, tokens, matrix.in_features)?;
2934        let tp = self.ranks.len();
2935        if matrix.out_features % tp != 0 {
2936            return Err(format!(
2937                "column-parallel out_features {} is not divisible by TP={tp}",
2938                matrix.out_features
2939            )
2940            .into());
2941        }
2942        let local_out = matrix.out_features / tp;
2943        if !local_out.is_multiple_of(FP8_BLOCK) {
2944            return Err(format!(
2945                "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
2946                 E4M3 scale block"
2947            )
2948            .into());
2949        }
2950
2951        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2952        let mut rank_outputs = Vec::with_capacity(tp);
2953        for (rank_index, rank) in self.ranks.iter().enumerate() {
2954            let shard = column_shard(matrix, tp, rank_index)?;
2955            let output = run_rank(rank, shard, activations, tokens)?;
2956            let row_start = rank_index * local_out;
2957            for token in 0..tokens {
2958                gathered[token * matrix.out_features + row_start
2959                    ..token * matrix.out_features + row_start + local_out]
2960                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2961            }
2962            rank_outputs.push(output);
2963        }
2964        Ok(ColumnParallelResult {
2965            gathered,
2966            rank_outputs,
2967        })
2968    }
2969
2970    pub fn upload_column_parallel(
2971        &self,
2972        matrix: E4m3BlockMatrix<'_>,
2973    ) -> Result<ResidentColumnParallel, Box<dyn std::error::Error>> {
2974        matrix.validate()?;
2975        let tp = self.ranks.len();
2976        validate_column_shape(matrix, tp)?;
2977        let mut ranks = Vec::with_capacity(tp);
2978        for (rank_index, engine) in self.ranks.iter().enumerate() {
2979            ranks.push(upload_rank(engine, column_shard(matrix, tp, rank_index)?)?);
2980        }
2981        Ok(ResidentColumnParallel {
2982            ranks,
2983            out_features: matrix.out_features,
2984            in_features: matrix.in_features,
2985        })
2986    }
2987
2988    pub fn column_parallel_resident(
2989        &self,
2990        matrix: &ResidentColumnParallel,
2991        activations: &[f32],
2992        tokens: usize,
2993    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2994        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2995        validate_activations(activations, tokens, matrix.in_features)?;
2996        let local_out = matrix.out_features / self.ranks.len();
2997        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2998        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2999        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
3000            let output = run_resident_rank(engine, shard, activations, tokens)?;
3001            let row_start = rank_index * local_out;
3002            for token in 0..tokens {
3003                gathered[token * matrix.out_features + row_start
3004                    ..token * matrix.out_features + row_start + local_out]
3005                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
3006            }
3007            rank_outputs.push(output);
3008        }
3009        Ok(ColumnParallelResult {
3010            gathered,
3011            rank_outputs,
3012        })
3013    }
3014
3015    /// Row-parallel projection. Weight/input columns and their scale columns are partitioned
3016    /// across ranks. Rank-local partials return through host memory and are reduced in stable
3017    /// rank order.
3018    #[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
3019    pub fn row_parallel(
3020        &self,
3021        matrix: E4m3BlockMatrix<'_>,
3022        activations: &[f32],
3023        tokens: usize,
3024    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
3025        matrix.validate()?;
3026        validate_activations(activations, tokens, matrix.in_features)?;
3027        let tp = self.ranks.len();
3028        if matrix.in_features % tp != 0 {
3029            return Err(format!(
3030                "row-parallel in_features {} is not divisible by TP={tp}",
3031                matrix.in_features
3032            )
3033            .into());
3034        }
3035        let local_in = matrix.in_features / tp;
3036        if !local_in.is_multiple_of(FP8_BLOCK) {
3037            return Err(format!(
3038                "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
3039                 E4M3 scale block"
3040            )
3041            .into());
3042        }
3043
3044        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
3045        let mut rank_partials = Vec::with_capacity(tp);
3046        for (rank_index, rank) in self.ranks.iter().enumerate() {
3047            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
3048            let local_activations =
3049                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
3050            let shard = E4m3BlockMatrix {
3051                codes: &codes,
3052                scales: &scales,
3053                out_features: matrix.out_features,
3054                in_features: local_in,
3055            };
3056            let partial = run_rank(rank, shard, &local_activations, tokens)?;
3057            for (sum, value) in reduced.iter_mut().zip(&partial) {
3058                *sum += *value;
3059            }
3060            rank_partials.push(partial);
3061        }
3062        Ok(RowParallelResult {
3063            reduced,
3064            rank_partials,
3065        })
3066    }
3067
3068    pub fn upload_row_parallel(
3069        &self,
3070        matrix: E4m3BlockMatrix<'_>,
3071    ) -> Result<ResidentRowParallel, Box<dyn std::error::Error>> {
3072        matrix.validate()?;
3073        let tp = self.ranks.len();
3074        validate_row_shape(matrix, tp)?;
3075        let local_in = matrix.in_features / tp;
3076        let mut ranks = Vec::with_capacity(tp);
3077        for (rank_index, engine) in self.ranks.iter().enumerate() {
3078            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
3079            ranks.push(upload_rank(
3080                engine,
3081                E4m3BlockMatrix {
3082                    codes: &codes,
3083                    scales: &scales,
3084                    out_features: matrix.out_features,
3085                    in_features: local_in,
3086                },
3087            )?);
3088        }
3089        Ok(ResidentRowParallel {
3090            ranks,
3091            out_features: matrix.out_features,
3092            in_features: matrix.in_features,
3093        })
3094    }
3095
3096    pub fn row_parallel_resident(
3097        &self,
3098        matrix: &ResidentRowParallel,
3099        activations: &[f32],
3100        tokens: usize,
3101    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
3102        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
3103        validate_activations(activations, tokens, matrix.in_features)?;
3104        let tp = self.ranks.len();
3105        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
3106        let mut rank_partials = Vec::with_capacity(tp);
3107        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
3108            let local_activations =
3109                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
3110            let partial = run_resident_rank(engine, shard, &local_activations, tokens)?;
3111            for (sum, value) in reduced.iter_mut().zip(&partial) {
3112                *sum += *value;
3113            }
3114            rank_partials.push(partial);
3115        }
3116        Ok(RowParallelResult {
3117            reduced,
3118            rank_partials,
3119        })
3120    }
3121
3122    pub fn upload_bf16_column_parallel(
3123        &self,
3124        matrix: Bf16Matrix<'_>,
3125    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
3126        self.upload_bf16_column_parallel_inner(matrix, None, false)
3127    }
3128
3129    /// Step-3.7 column projection with one numerical program across TP1/TP2/TP4/TP8.
3130    pub fn upload_step_bf16_column_parallel(
3131        &self,
3132        matrix: Bf16Matrix<'_>,
3133    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
3134        self.upload_step_bf16_column_parallel_inner(matrix, false)
3135    }
3136
3137    /// Load-time exact F32 expansion of a Step BF16 shard.
3138    ///
3139    /// The original BF16 allocation is released after the stream-ordered conversion. Decode then
3140    /// reuses the resident F32 values with the same topology-invariant output-row chunks.
3141    pub fn upload_step_bf16_column_parallel_f32_mirror(
3142        &self,
3143        matrix: Bf16Matrix<'_>,
3144    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
3145        self.upload_step_bf16_column_parallel_inner(matrix, true)
3146    }
3147
3148    fn upload_step_bf16_column_parallel_inner(
3149        &self,
3150        matrix: Bf16Matrix<'_>,
3151        f32_mirror: bool,
3152    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
3153        let canonical_chunk_rows =
3154            step_bf16_canonical_chunk_rows(matrix.out_features, self.ranks.len())?;
3155        self.upload_bf16_column_parallel_inner(matrix, Some(canonical_chunk_rows), f32_mirror)
3156    }
3157
3158    #[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
3159    fn upload_bf16_column_parallel_inner(
3160        &self,
3161        matrix: Bf16Matrix<'_>,
3162        canonical_chunk_rows: Option<usize>,
3163        f32_mirror: bool,
3164    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
3165        matrix.validate()?;
3166        let tp = self.ranks.len();
3167        if matrix.out_features % tp != 0 {
3168            return Err(format!(
3169                "BF16 column-parallel out_features {} is not divisible by TP={tp}",
3170                matrix.out_features
3171            )
3172            .into());
3173        }
3174        let mut ranks = Vec::with_capacity(tp);
3175        for (rank, engine) in self.ranks.iter().enumerate() {
3176            ranks.push(upload_bf16_rank(
3177                engine,
3178                bf16_column_shard(matrix, tp, rank)?,
3179                f32_mirror,
3180            )?);
3181        }
3182        Ok(ResidentBf16ColumnParallel {
3183            ranks,
3184            out_features: matrix.out_features,
3185            in_features: matrix.in_features,
3186            canonical_chunk_rows,
3187        })
3188    }
3189
3190    pub fn bf16_column_parallel_resident(
3191        &self,
3192        matrix: &ResidentBf16ColumnParallel,
3193        activations: &[f32],
3194        tokens: usize,
3195    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
3196        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
3197        validate_activations(activations, tokens, matrix.in_features)?;
3198        let local_out = matrix.out_features / self.ranks.len();
3199        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
3200        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
3201        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
3202            let output = run_resident_bf16_rank(
3203                engine,
3204                shard,
3205                activations,
3206                tokens,
3207                matrix.canonical_chunk_rows,
3208            )?;
3209            for token in 0..tokens {
3210                let src = &output[token * local_out..(token + 1) * local_out];
3211                let dst_start = token * matrix.out_features + rank * local_out;
3212                gathered[dst_start..dst_start + local_out].copy_from_slice(src);
3213            }
3214            rank_outputs.push(output);
3215        }
3216        Ok(ColumnParallelResult {
3217            gathered,
3218            rank_outputs,
3219        })
3220    }
3221
3222    /// Native-P2P twin of [`Self::bf16_column_parallel_resident`].
3223    ///
3224    /// The host-canonical activation is uploaded once on rank zero and peer-broadcast to the
3225    /// remaining ranks. Rank-local outputs are peer-gathered in token-major order before one root
3226    /// readback. This removes per-rank host staging but deliberately still returns a host oracle;
3227    /// attention and KV ownership are separate milestones.
3228    pub fn bf16_column_parallel_resident_native(
3229        &self,
3230        matrix: &ResidentBf16ColumnParallel,
3231        activations: &[f32],
3232        tokens: usize,
3233    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3234        let rank_outputs =
3235            self.bf16_column_parallel_resident_device_shards(matrix, activations, tokens)?;
3236        let local_out = matrix.out_features / self.ranks.len();
3237        self.gather_native_column_shards(&rank_outputs, tokens, local_out)
3238    }
3239
3240    /// Does the serving engine live in the SAME CUDA context as this runtime's root rank?
3241    /// The device-resident input/output seams below hand raw device buffers across the
3242    /// Engine boundary, which is only addressable when both sides share the root device's
3243    /// primary context — the generic full-attention TP seam keys its residency dispatch on.
3244    pub fn root_shares_ctx(&self, e: &Engine) -> bool {
3245        self.ranks
3246            .first()
3247            .is_some_and(|root| root.ctx().cu_ctx() == e.ctx().cu_ctx())
3248    }
3249
3250    /// Device-input twin of [`Self::bf16_column_parallel_resident_native`] (lane/
3251    /// hermes-perf-fixes, 2026-08-23 — the step QKV TP host-bounce finding). The activation
3252    /// arrives as a ROOT-DEVICE buffer (first `tokens * in_features` values) instead of a
3253    /// host slice, and the gathered output stays root-resident: no DtoH of the hidden state,
3254    /// no host q/k/v staging, no re-upload. BYTE-IDENTICAL to the host-canonical native arm
3255    /// by construction — the root input bytes are dtod-copied where the host arm htod'd the
3256    /// same bytes, and every kernel, peer copy, and gather order is shared.
3257    ///
3258    /// FENCES: caller must have synchronized the producer stream that wrote
3259    /// `root_activation` (the serving engine's — a DIFFERENT stream in the same context);
3260    /// this method synchronizes the root stream before returning so the caller's stream can
3261    /// consume the gathered output immediately.
3262    pub fn bf16_column_parallel_resident_native_device(
3263        &self,
3264        matrix: &ResidentBf16ColumnParallel,
3265        root_activation: &CudaSlice<f32>,
3266        tokens: usize,
3267    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3268        let rank_outputs = self.bf16_column_parallel_resident_device_shards_from_root(
3269            matrix,
3270            root_activation,
3271            tokens,
3272        )?;
3273        let local_out = matrix.out_features / self.ranks.len();
3274        let gathered = self.gather_native_column_shards_device(&rank_outputs, tokens, local_out)?;
3275        let root = &self.ranks[0];
3276        let _main = root.gpu.enter_main()?;
3277        root.stream().synchronize()?;
3278        Ok(gathered)
3279    }
3280
3281    /// Root-device-input twin of [`Self::bf16_column_parallel_resident_device_shards`]:
3282    /// the canonical activation is already resident on the root device (len >=
3283    /// `tokens * in_features`; extra tail values beyond the active prefix are ignored,
3284    /// the reused-prime-slab contract of `active_matrix_values`).
3285    pub fn bf16_column_parallel_resident_device_shards_from_root(
3286        &self,
3287        matrix: &ResidentBf16ColumnParallel,
3288        root_activation: &CudaSlice<f32>,
3289        tokens: usize,
3290    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
3291        if self.ranks.len() > 1 && !self.native_p2p {
3292            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
3293        }
3294        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
3295        let values = tokens
3296            .checked_mul(matrix.in_features)
3297            .ok_or("device BF16 column activation size overflow")?;
3298        let root = &self.ranks[0];
3299        if tokens == 0
3300            || root_activation.len() < values
3301            || root_activation.ordinal() != root.ctx().ordinal()
3302        {
3303            return Err("device BF16 column root activation geometry mismatch".into());
3304        }
3305
3306        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
3307        let root_input = {
3308            let _main = root.gpu.enter_main()?;
3309            let mut root_input = root.uninit(values)?;
3310            root.stream()
3311                .memcpy_dtod(&root_activation.slice(0..values), &mut root_input)?;
3312            root_input
3313        };
3314        // PRODUCER FENCE (same discipline as the host-input twin): the peer broadcast
3315        // below reads this buffer from the OTHER ranks' streams while the root dtod may
3316        // still be in flight.
3317        {
3318            let _main = root.gpu.enter_main()?;
3319            root.stream().synchronize()?;
3320        }
3321        rank_inputs.push(root_input);
3322        for engine in &self.ranks[1..] {
3323            let peer_input = {
3324                let _main = engine.gpu.enter_main()?;
3325                let mut peer_input = engine.uninit(values)?;
3326                engine
3327                    .stream()
3328                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
3329                peer_input
3330            };
3331            rank_inputs.push(peer_input);
3332        }
3333
3334        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
3335        #[allow(clippy::needless_range_loop)]
3336        // allow: the explicit index loop keeps the offset arithmetic visible and aligned with the device-side indexing
3337        for rank in 0..self.ranks.len() {
3338            rank_outputs.push(run_resident_bf16_rank_device(
3339                &self.ranks[rank],
3340                &matrix.ranks[rank],
3341                &rank_inputs[rank],
3342                tokens,
3343                matrix.canonical_chunk_rows,
3344                self.bulk_p2p,
3345            )?);
3346        }
3347        Ok(rank_outputs)
3348    }
3349
3350    /// Keep Step BF16 column outputs resident on their owning TP ranks.
3351    ///
3352    /// Rank zero receives the host-canonical activation once and peer-broadcasts it when TP>1.
3353    /// Unlike [`Self::bf16_column_parallel_resident_native`], this method performs no output
3354    /// gather or readback. It is the correctness substrate for rank-local norm, RoPE, attention,
3355    /// and cache ownership; callers must not treat its existence as serving qualification.
3356    pub fn bf16_column_parallel_resident_device_shards(
3357        &self,
3358        matrix: &ResidentBf16ColumnParallel,
3359        activations: &[f32],
3360        tokens: usize,
3361    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
3362        if self.ranks.len() > 1 && !self.native_p2p {
3363            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
3364        }
3365        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
3366        validate_activations(activations, tokens, matrix.in_features)?;
3367
3368        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
3369        let root_input = {
3370            let root = &self.ranks[0];
3371            let _main = root.gpu.enter_main()?;
3372            root.htod(activations)?
3373        };
3374        // PRODUCER FENCE (2026-08-20 flake fix): the peer broadcast below reads this buffer from
3375        // the OTHER ranks' streams, and clone_htod is asynchronous on the root stream. Without
3376        // this fence a peer copy can overtake the in-flight H2D and replicate stale bytes — the
3377        // measured ~30%-of-boots prefill/decode argmax flake. Same discipline as
3378        // `upload_replicated_device_rows`.
3379        {
3380            let root = &self.ranks[0];
3381            let _main = root.gpu.enter_main()?;
3382            root.stream().synchronize()?;
3383        }
3384        rank_inputs.push(root_input);
3385        for engine in &self.ranks[1..] {
3386            let peer_input = {
3387                let _main = engine.gpu.enter_main()?;
3388                let mut peer_input = engine.uninit(activations.len())?;
3389                engine
3390                    .stream()
3391                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
3392                peer_input
3393            };
3394            rank_inputs.push(peer_input);
3395        }
3396
3397        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
3398        #[allow(clippy::needless_range_loop)]
3399        // allow: the explicit index loop keeps the offset arithmetic visible and aligned with the device-side indexing
3400        for rank in 0..self.ranks.len() {
3401            rank_outputs.push(run_resident_bf16_rank_device(
3402                &self.ranks[rank],
3403                &matrix.ranks[rank],
3404                &rank_inputs[rank],
3405                tokens,
3406                matrix.canonical_chunk_rows,
3407                self.bulk_p2p,
3408            )?);
3409        }
3410        Ok(rank_outputs)
3411    }
3412
3413    /// Allocate one fixed-shape replicated batch without initializing its contents.
3414    ///
3415    /// Callers must refresh every rank before passing the batch to an operator.
3416    pub fn allocate_replicated_device_rows(
3417        &self,
3418        tokens: usize,
3419        width: usize,
3420    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3421        if self.ranks.len() > 1 && !self.native_p2p {
3422            return Err("replicated device rows require native P2P ranks".into());
3423        }
3424        let values = tokens
3425            .checked_mul(width)
3426            .ok_or("replicated device row size overflow")?;
3427        let rank_lengths = vec![values; self.ranks.len()];
3428        replicated_device_row_values(tokens, width, self.ranks.len(), &rank_lengths)?;
3429        let mut ranks = Vec::with_capacity(self.ranks.len());
3430        for engine in &self.ranks {
3431            let _main = engine.gpu.enter_main()?;
3432            ranks.push(engine.uninit(values)?);
3433        }
3434        Ok(ResidentReplicatedDeviceRows {
3435            ranks,
3436            tokens,
3437            width,
3438        })
3439    }
3440
3441    /// Replace a fixed-shape replicated batch from a root-device source.
3442    pub fn refresh_replicated_device_rows_from_root(
3443        &self,
3444        rows: &mut ResidentReplicatedDeviceRows,
3445        source: &CudaSlice<f32>,
3446    ) -> Result<(), Box<dyn std::error::Error>> {
3447        if self.ranks.len() > 1 && !self.native_p2p {
3448            return Err("replicated device rows require native P2P ranks".into());
3449        }
3450        validate_replicated_device_rows(&self.ranks, rows)?;
3451        let root = self
3452            .ranks
3453            .first()
3454            .ok_or("replicated rows have no root rank")?;
3455        let values = replicated_device_row_source_values(
3456            rows.tokens,
3457            rows.width,
3458            source.len(),
3459            source.ordinal(),
3460            root.ctx().ordinal(),
3461        )?;
3462        let (root_rows, peer_rows) = rows
3463            .ranks
3464            .split_first_mut()
3465            .ok_or("replicated rows have no root allocation")?;
3466        {
3467            let _main = root.gpu.enter_main()?;
3468            let mut destination = root_rows.slice_mut(0..values);
3469            root.stream()
3470                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
3471            root.stream().synchronize()?;
3472        }
3473        for (engine, peer_rows) in self.ranks.iter().skip(1).zip(peer_rows) {
3474            let _main = engine.gpu.enter_main()?;
3475            let mut destination = peer_rows.slice_mut(0..values);
3476            engine
3477                .stream()
3478                .memcpy_dtod(&root_rows.slice(0..values), &mut destination)?;
3479        }
3480        Ok(())
3481    }
3482
3483    /// Upload one canonical batch on rank zero and replicate it over native P2P.
3484    pub fn upload_replicated_device_rows(
3485        &self,
3486        rows: &[f32],
3487        tokens: usize,
3488        width: usize,
3489    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3490        if self.ranks.len() > 1 && !self.native_p2p {
3491            return Err("replicated device rows require native P2P ranks".into());
3492        }
3493        validate_activations(rows, tokens, width)?;
3494        let root = self
3495            .ranks
3496            .first()
3497            .ok_or("replicated rows have no root rank")?;
3498        let root_rows = {
3499            let _main = root.gpu.enter_main()?;
3500            root.htod(rows)?
3501        };
3502        {
3503            let _main = root.gpu.enter_main()?;
3504            root.stream().synchronize()?;
3505        }
3506        let mut ranks = Vec::with_capacity(self.ranks.len());
3507        ranks.push(root_rows);
3508        for engine in self.ranks.iter().skip(1) {
3509            let _main = engine.gpu.enter_main()?;
3510            let mut peer_rows = engine.uninit(rows.len())?;
3511            engine.stream().memcpy_dtod(&ranks[0], &mut peer_rows)?;
3512            ranks.push(peer_rows);
3513        }
3514        Ok(ResidentReplicatedDeviceRows {
3515            ranks,
3516            tokens,
3517            width,
3518        })
3519    }
3520
3521    /// Execute a column-parallel BF16 matrix directly from rank-local replicated inputs.
3522    pub fn bf16_column_parallel_resident_replicated_device_shards(
3523        &self,
3524        matrix: &ResidentBf16ColumnParallel,
3525        activations: &ResidentReplicatedDeviceRows,
3526    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
3527        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
3528        validate_replicated_device_rows(&self.ranks, activations)?;
3529        if activations.width != matrix.in_features {
3530            return Err(format!(
3531                "replicated BF16 column input width {} != matrix width {}",
3532                activations.width, matrix.in_features
3533            )
3534            .into());
3535        }
3536        let mut outputs = Vec::with_capacity(self.ranks.len());
3537        for rank in 0..self.ranks.len() {
3538            outputs.push(run_resident_bf16_rank_device(
3539                &self.ranks[rank],
3540                &matrix.ranks[rank],
3541                &activations.ranks[rank],
3542                activations.tokens,
3543                matrix.canonical_chunk_rows,
3544                self.bulk_p2p,
3545            )?);
3546        }
3547        Ok(outputs)
3548    }
3549
3550    /// Upload a BF16 router once on rank zero and retain its exact F32 expansion.
3551    #[allow(clippy::too_many_arguments)]
3552    pub fn upload_sigmoid_topk_router(
3553        &self,
3554        weight: Bf16Matrix<'_>,
3555        correction_bias: &[f32],
3556        active: Option<&[bool]>,
3557        experts_per_token: usize,
3558        scaling_factor: f32,
3559        route_norm: bool,
3560    ) -> Result<ResidentSigmoidTopKRouter, Box<dyn std::error::Error>> {
3561        weight.validate()?;
3562        if correction_bias.len() != weight.out_features
3563            || experts_per_token == 0
3564            || experts_per_token > weight.out_features
3565            || !correction_bias.iter().all(|value| value.is_finite())
3566            || !scaling_factor.is_finite()
3567            || scaling_factor <= 0.0
3568        {
3569            return Err(format!(
3570                "sigmoid router geometry weight={}x{} bias={} top_k={} scale={scaling_factor}",
3571                weight.out_features,
3572                weight.in_features,
3573                correction_bias.len(),
3574                experts_per_token,
3575            )
3576            .into());
3577        }
3578        let active_row = active
3579            .map(|mask| {
3580                if mask.len() != weight.out_features {
3581                    return Err(format!(
3582                        "sigmoid router active mask {} != experts {}",
3583                        mask.len(),
3584                        weight.out_features
3585                    ));
3586                }
3587                Ok(mask
3588                    .iter()
3589                    .map(|&enabled| u8::from(enabled))
3590                    .collect::<Vec<_>>())
3591            })
3592            .transpose()?
3593            .unwrap_or_else(|| vec![1; weight.out_features]);
3594        let active_count = active_row.iter().filter(|&&enabled| enabled != 0).count();
3595        crate::sigrouter_contract::validate_active_count(experts_per_token, active_count)?;
3596
3597        let root = self
3598            .ranks
3599            .first()
3600            .ok_or("sigmoid router runtime has no root rank")?;
3601        let _main = root.gpu.enter_main()?;
3602        let bf16 = root.htod_bytes(weight.bytes)?;
3603        let weight_f32 = root.bf16_to_f32(
3604            &bf16.slice(0..bf16.len()),
3605            weight.out_features * weight.in_features,
3606        )?;
3607        Ok(ResidentSigmoidTopKRouter {
3608            weight: weight_f32,
3609            correction_bias: root.htod(correction_bias)?,
3610            active: root.htod_bytes(&active_row)?,
3611            root_device: root.ctx().ordinal(),
3612            input_width: weight.in_features,
3613            expert_count: weight.out_features,
3614            experts_per_token,
3615            active_count,
3616            scaling_factor,
3617            route_norm,
3618        })
3619    }
3620
3621    /// Route rank-zero replicated rows and return the narrow host control result plus logits.
3622    ///
3623    /// The logits readback exists for independent oracle comparison. This method is a correctness
3624    /// surface; a serving scheduler may retain logits and selected routes on device.
3625    pub fn sigmoid_topk_replicated_device_rows_host(
3626        &self,
3627        router: &ResidentSigmoidTopKRouter,
3628        input: &ResidentReplicatedDeviceRows,
3629    ) -> Result<SigmoidTopKHostOutput, Box<dyn std::error::Error>> {
3630        validate_replicated_device_rows(&self.ranks, input)?;
3631        if input.width != router.input_width {
3632            return Err(format!(
3633                "sigmoid router input width {} != resident width {}",
3634                input.width, router.input_width
3635            )
3636            .into());
3637        }
3638        let root = self
3639            .ranks
3640            .first()
3641            .ok_or("sigmoid router runtime has no root rank")?;
3642        let _main = root.gpu.enter_main()?;
3643        if root.ctx().ordinal() != router.root_device
3644            || router.weight.ordinal() != router.root_device
3645            || router.correction_bias.ordinal() != router.root_device
3646            || router.active.ordinal() != router.root_device
3647        {
3648            return Err("sigmoid router root residency changed".into());
3649        }
3650        let logits = root.router_gemv(
3651            &router.weight,
3652            &input.ranks[0],
3653            router.input_width,
3654            router.expert_count,
3655            input.tokens,
3656        )?;
3657        let (selected, weights) = root.moe_router_sigmoid_topk_host(
3658            &logits,
3659            input.tokens,
3660            router.expert_count,
3661            router.experts_per_token,
3662            router.active_count,
3663            &router.correction_bias,
3664            &router.active,
3665            router.scaling_factor,
3666            router.route_norm,
3667        )?;
3668        Ok(SigmoidTopKHostOutput {
3669            logits: root.dtoh(&logits)?,
3670            selected,
3671            weights,
3672        })
3673    }
3674
3675    /// Replicate a full BF16 SwiGLU bank on every rank.
3676    pub fn upload_replicated_bf16_swiglu(
3677        &self,
3678        gate: Bf16Matrix<'_>,
3679        up: Bf16Matrix<'_>,
3680        down: Bf16Matrix<'_>,
3681    ) -> Result<ResidentReplicatedBf16SwiGlu, Box<dyn std::error::Error>> {
3682        gate.validate()?;
3683        up.validate()?;
3684        down.validate()?;
3685        if gate.in_features != up.in_features
3686            || gate.out_features != up.out_features
3687            || down.in_features != gate.out_features
3688            || down.out_features != gate.in_features
3689        {
3690            return Err(format!(
3691                "replicated BF16 SwiGLU geometry gate={}x{} up={}x{} down={}x{}",
3692                gate.out_features,
3693                gate.in_features,
3694                up.out_features,
3695                up.in_features,
3696                down.out_features,
3697                down.in_features,
3698            )
3699            .into());
3700        }
3701        let mut gate_ranks = Vec::with_capacity(self.ranks.len());
3702        let mut up_ranks = Vec::with_capacity(self.ranks.len());
3703        let mut down_ranks = Vec::with_capacity(self.ranks.len());
3704        for engine in &self.ranks {
3705            gate_ranks.push(upload_bf16_rank(engine, gate, false)?);
3706            up_ranks.push(upload_bf16_rank(engine, up, false)?);
3707            down_ranks.push(upload_bf16_rank(engine, down, false)?);
3708        }
3709        Ok(ResidentReplicatedBf16SwiGlu {
3710            gate: gate_ranks,
3711            up: up_ranks,
3712            down: down_ranks,
3713            input_width: gate.in_features,
3714            intermediate_width: gate.out_features,
3715        })
3716    }
3717
3718    /// Execute a fully replicated BF16 SwiGLU directly from replicated device rows.
3719    pub fn replicated_bf16_swiglu_resident_device(
3720        &self,
3721        mlp: &ResidentReplicatedBf16SwiGlu,
3722        input: &ResidentReplicatedDeviceRows,
3723        activation_limit: Option<f32>,
3724    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3725        validate_step_expert_activation_limit(activation_limit)?;
3726        validate_replicated_device_rows(&self.ranks, input)?;
3727        validate_resident_bf16_ranks(&self.ranks, &mlp.gate)?;
3728        validate_resident_bf16_ranks(&self.ranks, &mlp.up)?;
3729        validate_resident_bf16_ranks(&self.ranks, &mlp.down)?;
3730        if input.width != mlp.input_width
3731            || mlp.gate.len() != self.ranks.len()
3732            || mlp.up.len() != self.ranks.len()
3733            || mlp.down.len() != self.ranks.len()
3734        {
3735            return Err("replicated BF16 SwiGLU residency or input width changed".into());
3736        }
3737
3738        let mut outputs = Vec::with_capacity(self.ranks.len());
3739        for rank in 0..self.ranks.len() {
3740            let engine = &self.ranks[rank];
3741            let gate = run_resident_bf16_rank_device(
3742                engine,
3743                &mlp.gate[rank],
3744                &input.ranks[rank],
3745                input.tokens,
3746                None,
3747                self.bulk_p2p,
3748            )?;
3749            let up = run_resident_bf16_rank_device(
3750                engine,
3751                &mlp.up[rank],
3752                &input.ranks[rank],
3753                input.tokens,
3754                None,
3755                self.bulk_p2p,
3756            )?;
3757            let _main = engine.gpu.enter_main()?;
3758            let values = input
3759                .tokens
3760                .checked_mul(mlp.intermediate_width)
3761                .ok_or("replicated BF16 SwiGLU activation size overflow")?;
3762            let mut activation = engine.uninit(values)?;
3763            if let Some(limit) = activation_limit {
3764                engine.silu_clamped_mul_host_expf(&gate, &up, limit, &mut activation, values)?;
3765            } else {
3766                engine.silu_mul_host_expf(&gate, &up, &mut activation, values)?;
3767            }
3768            outputs.push(run_resident_bf16_rank_device(
3769                engine,
3770                &mlp.down[rank],
3771                &activation,
3772                input.tokens,
3773                None,
3774                self.bulk_p2p,
3775            )?);
3776        }
3777        Ok(ResidentReplicatedDeviceRows {
3778            ranks: outputs,
3779            tokens: input.tokens,
3780            width: mlp.input_width,
3781        })
3782    }
3783
3784    /// Apply the same RMS-norm row program independently on every replicated rank.
3785    pub fn rms_norm_replicated_device_rows(
3786        &self,
3787        input: &ResidentReplicatedDeviceRows,
3788        weight: &[f32],
3789        eps: f32,
3790    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3791        validate_replicated_device_rows(&self.ranks, input)?;
3792        if weight.len() != input.width || !eps.is_finite() || eps <= 0.0 {
3793            return Err(format!(
3794                "replicated RMS norm weight/eps {}/{} != width {}",
3795                weight.len(),
3796                eps,
3797                input.width
3798            )
3799            .into());
3800        }
3801        let mut ranks = Vec::with_capacity(self.ranks.len());
3802        for (rank, engine) in self.ranks.iter().enumerate() {
3803            let _main = engine.gpu.enter_main()?;
3804            let weight = engine.htod(weight)?;
3805            let mut output = engine.uninit(input.tokens * input.width)?;
3806            engine.rms_norm(
3807                &input.ranks[rank],
3808                &weight,
3809                &mut output,
3810                input.width,
3811                input.tokens,
3812                eps,
3813            )?;
3814            ranks.push(output);
3815        }
3816        Ok(ResidentReplicatedDeviceRows {
3817            ranks,
3818            tokens: input.tokens,
3819            width: input.width,
3820        })
3821    }
3822
3823    /// Add two replicated batches and RMS-normalize the exact residual on every rank.
3824    pub fn add_rms_norm_replicated_device_rows(
3825        &self,
3826        input: &ResidentReplicatedDeviceRows,
3827        update: &ResidentReplicatedDeviceRows,
3828        weight: &[f32],
3829        eps: f32,
3830    ) -> Result<
3831        (ResidentReplicatedDeviceRows, ResidentReplicatedDeviceRows),
3832        Box<dyn std::error::Error>,
3833    > {
3834        validate_replicated_device_rows(&self.ranks, input)?;
3835        validate_replicated_device_rows(&self.ranks, update)?;
3836        if input.tokens != update.tokens
3837            || input.width != update.width
3838            || weight.len() != input.width
3839            || !eps.is_finite()
3840            || eps <= 0.0
3841        {
3842            return Err(format!(
3843                "replicated add/RMS geometry input={}x{} update={}x{} weight={} eps={eps}",
3844                input.tokens,
3845                input.width,
3846                update.tokens,
3847                update.width,
3848                weight.len(),
3849            )
3850            .into());
3851        }
3852        let values = input.tokens * input.width;
3853        let mut residual_ranks = Vec::with_capacity(self.ranks.len());
3854        let mut normalized_ranks = Vec::with_capacity(self.ranks.len());
3855        for (rank, engine) in self.ranks.iter().enumerate() {
3856            let _main = engine.gpu.enter_main()?;
3857            let weight = engine.htod(weight)?;
3858            let mut residual = engine.uninit(values)?;
3859            let mut normalized = engine.uninit(values)?;
3860            engine.add_rms_norm(
3861                &input.ranks[rank],
3862                &update.ranks[rank],
3863                &weight,
3864                &mut residual,
3865                &mut normalized,
3866                input.width,
3867                input.tokens,
3868                eps,
3869            )?;
3870            residual_ranks.push(residual);
3871            normalized_ranks.push(normalized);
3872        }
3873        Ok((
3874            ResidentReplicatedDeviceRows {
3875                ranks: residual_ranks,
3876                tokens: input.tokens,
3877                width: input.width,
3878            },
3879            ResidentReplicatedDeviceRows {
3880                ranks: normalized_ranks,
3881                tokens: input.tokens,
3882                width: input.width,
3883            },
3884        ))
3885    }
3886
3887    pub fn collect_replicated_device_rows(
3888        &self,
3889        rows: &ResidentReplicatedDeviceRows,
3890    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
3891        validate_replicated_device_rows(&self.ranks, rows)?;
3892        let mut outputs = Vec::with_capacity(self.ranks.len());
3893        for (rank, engine) in self.ranks.iter().enumerate() {
3894            let _main = engine.gpu.enter_main()?;
3895            outputs.push(engine.dtoh(&rows.ranks[rank])?);
3896        }
3897        Ok(outputs)
3898    }
3899
3900    #[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
3901    pub fn upload_bf16_row_parallel(
3902        &self,
3903        matrix: Bf16Matrix<'_>,
3904    ) -> Result<ResidentBf16RowParallel, Box<dyn std::error::Error>> {
3905        matrix.validate()?;
3906        let tp = self.ranks.len();
3907        if matrix.in_features % tp != 0 {
3908            return Err(format!(
3909                "BF16 row-parallel in_features {} is not divisible by TP={tp}",
3910                matrix.in_features
3911            )
3912            .into());
3913        }
3914        let mut ranks = Vec::with_capacity(tp);
3915        for (rank, engine) in self.ranks.iter().enumerate() {
3916            let shard = bf16_row_shard(matrix, tp, rank)?;
3917            ranks.push(upload_bf16_rank(
3918                engine,
3919                Bf16Matrix {
3920                    bytes: &shard,
3921                    out_features: matrix.out_features,
3922                    in_features: matrix.in_features / tp,
3923                },
3924                false,
3925            )?);
3926        }
3927        Ok(ResidentBf16RowParallel {
3928            ranks,
3929            out_features: matrix.out_features,
3930            in_features: matrix.in_features,
3931        })
3932    }
3933
3934    pub fn bf16_row_parallel_resident(
3935        &self,
3936        matrix: &ResidentBf16RowParallel,
3937        activations: &[f32],
3938        tokens: usize,
3939    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
3940        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
3941        validate_activations(activations, tokens, matrix.in_features)?;
3942        let tp = self.ranks.len();
3943        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
3944        let mut rank_partials = Vec::with_capacity(tp);
3945        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
3946            let local_activations =
3947                activation_shard(activations, tokens, matrix.in_features, tp, rank);
3948            let partial = run_resident_bf16_rank(engine, shard, &local_activations, tokens, None)?;
3949            for (sum, value) in reduced.iter_mut().zip(&partial) {
3950                *sum += value;
3951            }
3952            rank_partials.push(partial);
3953        }
3954        Ok(RowParallelResult {
3955            reduced,
3956            rank_partials,
3957        })
3958    }
3959
3960    /// Step-3.7 row projection split into the same eight global K blocks for TP1/TP2/TP4/TP8.
3961    pub fn upload_step_bf16_row_parallel(
3962        &self,
3963        matrix: Bf16Matrix<'_>,
3964    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3965        self.upload_step_bf16_row_parallel_inner(matrix, false)
3966    }
3967
3968    pub fn upload_step_bf16_row_parallel_f32_mirror(
3969        &self,
3970        matrix: Bf16Matrix<'_>,
3971    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3972        self.upload_step_bf16_row_parallel_inner(matrix, true)
3973    }
3974
3975    fn upload_step_bf16_row_parallel_inner(
3976        &self,
3977        matrix: Bf16Matrix<'_>,
3978        f32_mirror: bool,
3979    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3980        matrix.validate()?;
3981        let tp = self.ranks.len();
3982        let canonical_chunk_cols = step_bf16_canonical_chunk_cols(matrix.in_features, tp)?;
3983        let local_in = matrix.in_features / tp;
3984        let blocks_per_rank = local_in / canonical_chunk_cols;
3985        let mut ranks = Vec::with_capacity(tp);
3986        for (rank, engine) in self.ranks.iter().enumerate() {
3987            let mut blocks = Vec::with_capacity(blocks_per_rank);
3988            for block in 0..blocks_per_rank {
3989                let global_block = rank * blocks_per_rank + block;
3990                let col_start = global_block * canonical_chunk_cols;
3991                let bytes = bf16_row_block(matrix, col_start, canonical_chunk_cols)?;
3992                blocks.push(upload_bf16_rank(
3993                    engine,
3994                    Bf16Matrix {
3995                        bytes: &bytes,
3996                        out_features: matrix.out_features,
3997                        in_features: canonical_chunk_cols,
3998                    },
3999                    f32_mirror,
4000                )?);
4001            }
4002            ranks.push(blocks);
4003        }
4004        Ok(ResidentStepBf16RowParallel {
4005            ranks,
4006            out_features: matrix.out_features,
4007            in_features: matrix.in_features,
4008            canonical_chunk_cols,
4009        })
4010    }
4011
4012    /// Host-staged exactness twin of [`Self::step_bf16_row_parallel_resident_native`].
4013    ///
4014    /// Block inputs and partials cross host memory, but every partial is added on the root device
4015    /// in global checkpoint-column order. Native transport must reproduce this result bitwise.
4016    pub fn step_bf16_row_parallel_resident(
4017        &self,
4018        matrix: &ResidentStepBf16RowParallel,
4019        activations: &[f32],
4020        tokens: usize,
4021    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4022        validate_step_bf16_row_residency(&self.ranks, matrix)?;
4023        validate_activations(activations, tokens, matrix.in_features)?;
4024        let root = &self.ranks[0];
4025        let output_len = tokens
4026            .checked_mul(matrix.out_features)
4027            .ok_or("Step BF16 row output size overflow")?;
4028        let mut reduced = {
4029            let _main = root.gpu.enter_main()?;
4030            root.htod(&vec![0.0f32; output_len])?
4031        };
4032        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
4033        for (rank, blocks) in matrix.ranks.iter().enumerate() {
4034            for (block, resident) in blocks.iter().enumerate() {
4035                let global_block = rank * blocks_per_rank + block;
4036                let input = activation_shard(
4037                    activations,
4038                    tokens,
4039                    matrix.in_features,
4040                    PRODUCT_MAX_CARDS,
4041                    global_block,
4042                );
4043                let partial =
4044                    run_resident_bf16_rank(&self.ranks[rank], resident, &input, tokens, None)?;
4045                let next = {
4046                    let _main = root.gpu.enter_main()?;
4047                    let partial = root.htod(&partial)?;
4048                    let mut next = root.uninit(output_len)?;
4049                    root.add(&reduced, &partial, &mut next, output_len)?;
4050                    next
4051                };
4052                reduced = next;
4053            }
4054        }
4055        let _main = root.gpu.enter_main()?;
4056        root.dtoh(&reduced)
4057    }
4058
4059    /// Native-P2P Step row projection with canonical global K-block reduction.
4060    ///
4061    /// The full activation is uploaded once on the root. Each TP8-sized block is peer-scattered
4062    /// to its owning rank, its BF16 partial is peer-returned to the root, and root-device adds
4063    /// replay the same eight-block order as TP1 and the host-staged oracle.
4064    pub fn step_bf16_row_parallel_resident_native(
4065        &self,
4066        matrix: &ResidentStepBf16RowParallel,
4067        activations: &[f32],
4068        tokens: usize,
4069    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4070        if self.ranks.len() > 1 && !self.native_p2p {
4071            return Err("native Step BF16 row parallelism requires P2P ranks".into());
4072        }
4073        validate_step_bf16_row_residency(&self.ranks, matrix)?;
4074        validate_activations(activations, tokens, matrix.in_features)?;
4075        let root = &self.ranks[0];
4076        let root_input = {
4077            let _main = root.gpu.enter_main()?;
4078            root.htod(activations)?
4079        };
4080        // PRODUCER FENCE (2026-08-20 flake fix): the non-bulk arm below peer-reads root_input
4081        // from the other ranks' streams while root's clone_htod may still be in flight.
4082        {
4083            let _main = root.gpu.enter_main()?;
4084            root.stream().synchronize()?;
4085        }
4086        let reduced = self.step_bf16_row_native_reduce_from_root(matrix, &root_input, tokens)?;
4087        let _main = root.gpu.enter_main()?;
4088        root.dtoh(&reduced)
4089    }
4090
4091    /// Device-input twin of [`Self::step_bf16_row_parallel_resident_native`] (lane/
4092    /// hermes-perf-fixes, 2026-08-23): the full activation arrives as a ROOT-DEVICE buffer
4093    /// and the reduced output stays root-resident — no DtoH of the attention output, no
4094    /// host O staging, no re-upload. Byte-identical to the host-canonical arm by
4095    /// construction (same block scatter, kernels, and global TP8 reduction order; the root
4096    /// bytes are dtod-copied where the host arm htod'd the same bytes). Caller must have
4097    /// synchronized the producer stream; the root stream is synchronized before returning.
4098    pub fn step_bf16_row_parallel_resident_native_device(
4099        &self,
4100        matrix: &ResidentStepBf16RowParallel,
4101        root_activation: &CudaSlice<f32>,
4102        tokens: usize,
4103    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4104        if self.ranks.len() > 1 && !self.native_p2p {
4105            return Err("native Step BF16 row parallelism requires P2P ranks".into());
4106        }
4107        validate_step_bf16_row_residency(&self.ranks, matrix)?;
4108        let values = tokens
4109            .checked_mul(matrix.in_features)
4110            .ok_or("device Step BF16 row activation size overflow")?;
4111        let root = &self.ranks[0];
4112        if tokens == 0
4113            || root_activation.len() < values
4114            || root_activation.ordinal() != root.ctx().ordinal()
4115        {
4116            return Err("device Step BF16 row root activation geometry mismatch".into());
4117        }
4118        let root_input = {
4119            let _main = root.gpu.enter_main()?;
4120            let mut root_input = root.uninit(values)?;
4121            root.stream()
4122                .memcpy_dtod(&root_activation.slice(0..values), &mut root_input)?;
4123            root.stream().synchronize()?; // producer fence, as the host-input twin
4124            root_input
4125        };
4126        let reduced = self.step_bf16_row_native_reduce_from_root(matrix, &root_input, tokens)?;
4127        let _main = root.gpu.enter_main()?;
4128        root.stream().synchronize()?;
4129        Ok(reduced)
4130    }
4131
4132    /// Shared core of the two native Step row arms above: block scatter + rank GEMMs +
4133    /// canonical global TP8-order root reduction, from a root-resident input, returning the
4134    /// root-resident reduced output. Extracted verbatim so the host and device twins cannot
4135    /// drift numerically.
4136    fn step_bf16_row_native_reduce_from_root(
4137        &self,
4138        matrix: &ResidentStepBf16RowParallel,
4139        root_input: &CudaSlice<f32>,
4140        tokens: usize,
4141    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4142        let root = &self.ranks[0];
4143        let output_len = tokens
4144            .checked_mul(matrix.out_features)
4145            .ok_or("native Step BF16 row output size overflow")?;
4146        let mut reduced = {
4147            let _main = root.gpu.enter_main()?;
4148            root.htod(&vec![0.0f32; output_len])?
4149        };
4150        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
4151        let mut block_input_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
4152        let mut root_packed_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
4153        let mut remote_partial_keepalive = Vec::new();
4154        for (rank, blocks) in matrix.ranks.iter().enumerate() {
4155            for (block, resident) in blocks.iter().enumerate() {
4156                let global_block = rank * blocks_per_rank + block;
4157                let col_start = global_block * matrix.canonical_chunk_cols;
4158                let block_len = tokens
4159                    .checked_mul(matrix.canonical_chunk_cols)
4160                    .ok_or("native Step BF16 row block size overflow")?;
4161                let block_input = if self.bulk_p2p {
4162                    let root_packed = {
4163                        let _main = root.gpu.enter_main()?;
4164                        let mut root_packed = root.uninit(block_len)?;
4165                        root.copy_rows_strided(
4166                            root_input,
4167                            &mut root_packed,
4168                            matrix.canonical_chunk_cols,
4169                            tokens,
4170                            matrix.in_features,
4171                            col_start,
4172                        )?;
4173                        root_packed
4174                    };
4175                    if rank == 0 {
4176                        root_packed
4177                    } else {
4178                        // PRODUCER FENCE (2026-08-20 flake fix): the pack kernel runs on the
4179                        // root stream; this rank's peer read must not overtake it.
4180                        {
4181                            let _main = root.gpu.enter_main()?;
4182                            root.stream().synchronize()?;
4183                        }
4184                        let engine = &self.ranks[rank];
4185                        let _main = engine.gpu.enter_main()?;
4186                        let mut block_input = engine.uninit(block_len)?;
4187                        engine
4188                            .stream()
4189                            .memcpy_dtod(&root_packed, &mut block_input)?;
4190                        root_packed_keepalive.push(root_packed);
4191                        block_input
4192                    }
4193                } else {
4194                    let engine = &self.ranks[rank];
4195                    let _main = engine.gpu.enter_main()?;
4196                    let mut block_input = engine.uninit(block_len)?;
4197                    for token in 0..tokens {
4198                        let source_start = token * matrix.in_features + col_start;
4199                        let source = root_input
4200                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
4201                        let destination_start = token * matrix.canonical_chunk_cols;
4202                        let mut destination = block_input.slice_mut(
4203                            destination_start..destination_start + matrix.canonical_chunk_cols,
4204                        );
4205                        engine.stream().memcpy_dtod(&source, &mut destination)?;
4206                    }
4207                    block_input
4208                };
4209                let partial = run_resident_bf16_rank_device(
4210                    &self.ranks[rank],
4211                    resident,
4212                    &block_input,
4213                    tokens,
4214                    None,
4215                    self.bulk_p2p,
4216                )?;
4217                block_input_keepalive.push(block_input);
4218                let root_partial = if rank == 0 {
4219                    partial
4220                } else {
4221                    // PRODUCER FENCE (2026-08-20 flake fix): the partial was produced by this
4222                    // rank's kernel on its own stream; root's peer read must not overtake it.
4223                    {
4224                        let engine = &self.ranks[rank];
4225                        let _main = engine.gpu.enter_main()?;
4226                        engine.stream().synchronize()?;
4227                    }
4228                    let _main = root.gpu.enter_main()?;
4229                    let mut peer_partial = root.uninit(output_len)?;
4230                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
4231                    remote_partial_keepalive.push(partial);
4232                    peer_partial
4233                };
4234                let next = {
4235                    let _main = root.gpu.enter_main()?;
4236                    let mut next = root.uninit(output_len)?;
4237                    root.add(&reduced, &root_partial, &mut next, output_len)?;
4238                    next
4239                };
4240                reduced = next;
4241            }
4242        }
4243        {
4244            let _main = root.gpu.enter_main()?;
4245            root.stream().synchronize()?;
4246        }
4247        drop(remote_partial_keepalive);
4248        drop(root_packed_keepalive);
4249        drop(block_input_keepalive);
4250        Ok(reduced)
4251    }
4252
4253    /// Reduce rank-local Step attention shards in canonical TP8 K-block order and keep the result
4254    /// on the root device.
4255    pub fn step_bf16_row_parallel_resident_root_device(
4256        &self,
4257        matrix: &ResidentStepBf16RowParallel,
4258        rank_activations: &[CudaSlice<f32>],
4259        tokens: usize,
4260    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4261        if self.ranks.len() > 1 && !self.native_p2p {
4262            return Err(
4263                "device-resident Step BF16 row parallelism requires native P2P ranks".into(),
4264            );
4265        }
4266        validate_step_bf16_row_residency(&self.ranks, matrix)?;
4267        let local_width = matrix.in_features / self.ranks.len();
4268        let shard_len = tokens
4269            .checked_mul(local_width)
4270            .ok_or("device Step BF16 row shard size overflow")?;
4271        if tokens == 0
4272            || rank_activations.len() != self.ranks.len()
4273            || rank_activations
4274                .iter()
4275                .zip(&self.ranks)
4276                .any(|(rows, engine)| {
4277                    rows.len() != shard_len || rows.ordinal() != engine.ctx().ordinal()
4278                })
4279        {
4280            return Err("device Step BF16 row activation shard geometry changed".into());
4281        }
4282
4283        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
4284        let mut block_inputs = Vec::with_capacity(self.ranks.len());
4285        let mut partials = Vec::with_capacity(self.ranks.len());
4286        for (rank, blocks) in matrix.ranks.iter().enumerate() {
4287            if blocks.len() != blocks_per_rank {
4288                return Err(format!(
4289                    "device Step BF16 row rank {rank} blocks {} != {blocks_per_rank}",
4290                    blocks.len()
4291                )
4292                .into());
4293            }
4294            let engine = &self.ranks[rank];
4295            let _main = engine.gpu.enter_main()?;
4296            let mut rank_inputs = Vec::with_capacity(blocks_per_rank);
4297            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
4298            for (block, resident) in blocks.iter().enumerate() {
4299                let block_len = tokens
4300                    .checked_mul(matrix.canonical_chunk_cols)
4301                    .ok_or("device Step BF16 row block size overflow")?;
4302                let mut block_input = engine.uninit(block_len)?;
4303                let local_col_start = block * matrix.canonical_chunk_cols;
4304                if self.bulk_p2p {
4305                    engine.copy_rows_strided(
4306                        &rank_activations[rank],
4307                        &mut block_input,
4308                        matrix.canonical_chunk_cols,
4309                        tokens,
4310                        local_width,
4311                        local_col_start,
4312                    )?;
4313                } else {
4314                    for token in 0..tokens {
4315                        let source_start = token * local_width + local_col_start;
4316                        let source = rank_activations[rank]
4317                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
4318                        let destination_start = token * matrix.canonical_chunk_cols;
4319                        let mut destination = block_input.slice_mut(
4320                            destination_start..destination_start + matrix.canonical_chunk_cols,
4321                        );
4322                        engine.stream().memcpy_dtod(&source, &mut destination)?;
4323                    }
4324                }
4325                let partial = run_resident_bf16_rank_device(
4326                    engine,
4327                    resident,
4328                    &block_input,
4329                    tokens,
4330                    None,
4331                    self.bulk_p2p,
4332                )?;
4333                rank_inputs.push(block_input);
4334                rank_partials.push(partial);
4335            }
4336            block_inputs.push(rank_inputs);
4337            partials.push(rank_partials);
4338        }
4339        for engine in self.ranks.iter().skip(1) {
4340            let _main = engine.gpu.enter_main()?;
4341            engine.stream().synchronize()?;
4342        }
4343
4344        let output_len = tokens
4345            .checked_mul(matrix.out_features)
4346            .ok_or("device Step BF16 row output size overflow")?;
4347        let root = &self.ranks[0];
4348        let _main = root.gpu.enter_main()?;
4349        let mut reduced = root.htod(&vec![0.0f32; output_len])?;
4350        let mut remote_partials = Vec::new();
4351        for (rank, rank_partials) in partials.into_iter().enumerate() {
4352            for partial in rank_partials {
4353                let root_partial = if rank == 0 {
4354                    partial
4355                } else {
4356                    let mut peer_partial = root.uninit(output_len)?;
4357                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
4358                    remote_partials.push(partial);
4359                    peer_partial
4360                };
4361                let mut next = root.uninit(output_len)?;
4362                root.add(&reduced, &root_partial, &mut next, output_len)?;
4363                reduced = next;
4364            }
4365        }
4366        root.stream().synchronize()?;
4367        drop(remote_partials);
4368        drop(block_inputs);
4369        Ok(reduced)
4370    }
4371
4372    /// Reduce rank-local Step attention shards, then replicate the canonical root result.
4373    pub fn step_bf16_row_parallel_resident_replicated_device(
4374        &self,
4375        matrix: &ResidentStepBf16RowParallel,
4376        rank_activations: &[CudaSlice<f32>],
4377        tokens: usize,
4378    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
4379        let reduced =
4380            self.step_bf16_row_parallel_resident_root_device(matrix, rank_activations, tokens)?;
4381        let output_len = tokens
4382            .checked_mul(matrix.out_features)
4383            .ok_or("device Step BF16 row output size overflow")?;
4384        let mut ranks = Vec::with_capacity(self.ranks.len());
4385        ranks.push(reduced);
4386        for engine in self.ranks.iter().skip(1) {
4387            let _main = engine.gpu.enter_main()?;
4388            let mut peer_output = engine.uninit(output_len)?;
4389            engine.stream().memcpy_dtod(&ranks[0], &mut peer_output)?;
4390            ranks.push(peer_output);
4391        }
4392        Ok(ResidentReplicatedDeviceRows {
4393            ranks,
4394            tokens,
4395            width: matrix.out_features,
4396        })
4397    }
4398
4399    pub fn upload_expert(
4400        &self,
4401        gate: E4m3BlockMatrix<'_>,
4402        up: E4m3BlockMatrix<'_>,
4403        down: E4m3BlockMatrix<'_>,
4404    ) -> Result<ResidentTpExpert, Box<dyn std::error::Error>> {
4405        if gate.in_features != up.in_features || gate.out_features != up.out_features {
4406            return Err("TP expert gate/up dimensions differ".into());
4407        }
4408        if down.in_features != gate.out_features || down.out_features != gate.in_features {
4409            return Err(format!(
4410                "TP expert down {}x{} does not invert gate/up {}x{}",
4411                down.out_features, down.in_features, gate.out_features, gate.in_features
4412            )
4413            .into());
4414        }
4415        Ok(ResidentTpExpert {
4416            gate: self.upload_column_parallel(gate)?,
4417            up: self.upload_column_parallel(up)?,
4418            down: self.upload_row_parallel(down)?,
4419            input_width: gate.in_features,
4420            expert_width: gate.out_features,
4421        })
4422    }
4423
4424    pub fn run_expert(
4425        &self,
4426        expert: &ResidentTpExpert,
4427        input: &[f32],
4428        tokens: usize,
4429    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4430        validate_activations(input, tokens, expert.input_width)?;
4431        let gate = self.column_parallel_resident(&expert.gate, input, tokens)?;
4432        let up = self.column_parallel_resident(&expert.up, input, tokens)?;
4433        let activated: Vec<f32> = gate
4434            .gathered
4435            .iter()
4436            .zip(&up.gathered)
4437            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
4438            .collect();
4439        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
4440        Ok(self
4441            .row_parallel_resident(&expert.down, &activated, tokens)?
4442            .reduced)
4443    }
4444
4445    #[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
4446    pub fn upload_expert_parallel(
4447        &self,
4448        gate: E4m3ExpertBank<'_>,
4449        up: E4m3ExpertBank<'_>,
4450        down: E4m3ExpertBank<'_>,
4451    ) -> Result<ResidentExpertParallel, Box<dyn std::error::Error>> {
4452        gate.validate()?;
4453        up.validate()?;
4454        down.validate()?;
4455        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
4456            return Err("EP gate/up/down expert counts differ".into());
4457        }
4458        if gate.in_features != up.in_features || gate.out_features != up.out_features {
4459            return Err("EP gate/up dimensions differ".into());
4460        }
4461        if down.in_features != gate.out_features || down.out_features != gate.in_features {
4462            return Err(format!(
4463                "EP down {}x{} does not invert gate/up {}x{}",
4464                down.out_features, down.in_features, gate.out_features, gate.in_features
4465            )
4466            .into());
4467        }
4468        if gate.expert_count % self.ranks.len() != 0 {
4469            return Err(format!(
4470                "EP expert count {} is not divisible by {} ranks",
4471                gate.expert_count,
4472                self.ranks.len()
4473            )
4474            .into());
4475        }
4476
4477        let per_rank = gate.expert_count / self.ranks.len();
4478        let mut ranks = Vec::with_capacity(self.ranks.len());
4479        for (rank, engine) in self.ranks.iter().enumerate() {
4480            let expert_range = rank * per_rank..(rank + 1) * per_rank;
4481            ranks.push(ResidentEpRank {
4482                gate: upload_expert_bank_rank(engine, gate, expert_range.clone())?,
4483                up: upload_expert_bank_rank(engine, up, expert_range.clone())?,
4484                down: upload_expert_bank_rank(engine, down, expert_range)?,
4485            });
4486        }
4487        Ok(ResidentExpertParallel {
4488            ranks,
4489            expert_count: gate.expert_count,
4490            input_width: gate.in_features,
4491            expert_width: gate.out_features,
4492        })
4493    }
4494
4495    /// Prepare the official Step gate-only grouped-FP8 projection oracle on rank zero.
4496    ///
4497    /// This intentionally does not alter the resident EP path. It owns a full rank-local tensor
4498    /// bank solely so the grouped projection can be compared with the existing per-route oracle
4499    /// without routing, transport, or combine changing underneath it.
4500    #[allow(clippy::too_many_arguments)]
4501    pub fn prepare_step_grouped_fp8_gate(
4502        &self,
4503        gate: E4m3ExpertBank<'_>,
4504        up: E4m3ExpertBank<'_>,
4505        down: E4m3ExpertBank<'_>,
4506        input: &[f32],
4507        tokens: usize,
4508        selected: &[usize],
4509        activation_limit: Option<f32>,
4510    ) -> Result<PreparedStepGroupedFp8Gate, Box<dyn std::error::Error>> {
4511        gate.validate()?;
4512        up.validate()?;
4513        down.validate()?;
4514        validate_step_expert_activation_limit(activation_limit)?;
4515        if gate.expert_count != STEP_GROUPED_FP8_EXPERTS
4516            || up.expert_count != STEP_GROUPED_FP8_EXPERTS
4517            || down.expert_count != STEP_GROUPED_FP8_EXPERTS
4518        {
4519            return Err(format!(
4520                "official Step grouped FP8 gate requires {STEP_GROUPED_FP8_EXPERTS} experts, \
4521                 got gate/up/down={}/{}/{}",
4522                gate.expert_count, up.expert_count, down.expert_count,
4523            )
4524            .into());
4525        }
4526        if gate.in_features != up.in_features
4527            || gate.out_features != STEP_GROUPED_FP8_WIDTH
4528            || up.out_features != STEP_GROUPED_FP8_WIDTH
4529            || down.in_features != STEP_GROUPED_FP8_WIDTH
4530            || down.out_features != gate.in_features
4531        {
4532            return Err(format!(
4533                "official Step grouped FP8 geometry gate={}x{} up={}x{} down={}x{}",
4534                gate.out_features,
4535                gate.in_features,
4536                up.out_features,
4537                up.in_features,
4538                down.out_features,
4539                down.in_features,
4540            )
4541            .into());
4542        }
4543        validate_activations(input, tokens, gate.in_features)?;
4544        let pairs = tokens
4545            .checked_mul(STEP_GROUPED_FP8_TOP_K)
4546            .ok_or("official Step grouped FP8 route count overflow")?;
4547        if selected.len() != pairs {
4548            return Err(format!(
4549                "official Step grouped FP8 routes {} != {tokens}x{STEP_GROUPED_FP8_TOP_K} \
4550                 ({pairs})",
4551                selected.len()
4552            )
4553            .into());
4554        }
4555        for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
4556            let mut unique = routes.to_vec();
4557            unique.sort_unstable();
4558            unique.dedup();
4559            if unique.len() != STEP_GROUPED_FP8_TOP_K {
4560                return Err(format!(
4561                    "official Step grouped FP8 token {token} routes are not top-8 unique: \
4562                     {routes:?}"
4563                )
4564                .into());
4565            }
4566        }
4567
4568        let engine = self
4569            .ranks
4570            .first()
4571            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
4572        let _main = engine.gpu.enter_main()?;
4573        let expert_range = 0..STEP_GROUPED_FP8_EXPERTS;
4574        let gate = upload_expert_bank_rank(engine, gate, expert_range.clone())?;
4575        let up = upload_expert_bank_rank(engine, up, expert_range.clone())?;
4576        let down = upload_expert_bank_rank(engine, down, expert_range)?;
4577        let input = engine.htod(input)?;
4578        let route_csr = ExpertCsr::from_token_routes(
4579            STEP_GROUPED_FP8_EXPERTS,
4580            tokens,
4581            STEP_GROUPED_FP8_TOP_K,
4582            selected,
4583        )?
4584        .upload(engine)?;
4585        let pair_rows = (0..pairs).collect::<Vec<_>>();
4586        let down_csr =
4587            ExpertCsr::from_pair_rows(STEP_GROUPED_FP8_EXPERTS, pairs, selected, &pair_rows)?
4588                .upload(engine)?;
4589        let gate_workspace =
4590            Fp8GroupedWorkspace::new(engine, gate.in_features, gate.out_features, tokens, pairs)?;
4591        let up_workspace =
4592            Fp8GroupedWorkspace::new(engine, up.in_features, up.out_features, tokens, pairs)?;
4593        let down_workspace =
4594            Fp8GroupedWorkspace::new(engine, down.in_features, down.out_features, pairs, pairs)?;
4595        let activation = engine.uninit(pairs * STEP_GROUPED_FP8_WIDTH)?;
4596        Ok(PreparedStepGroupedFp8Gate {
4597            device: engine.ctx().ordinal(),
4598            gate,
4599            up,
4600            down,
4601            input,
4602            route_csr,
4603            down_csr,
4604            gate_workspace,
4605            up_workspace,
4606            down_workspace,
4607            activation,
4608            activation_limit,
4609            tokens,
4610            pairs,
4611        })
4612    }
4613
4614    /// Execute one prepared gate/up/activation/down projection sequence on rank zero.
4615    pub fn run_step_grouped_fp8_gate(
4616        &self,
4617        plan: &mut PreparedStepGroupedFp8Gate,
4618    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4619        let engine = self
4620            .ranks
4621            .first()
4622            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
4623        if engine.ctx().ordinal() != plan.device {
4624            return Err(format!(
4625                "official Step grouped FP8 plan device {} != rank-zero device {}",
4626                plan.device,
4627                engine.ctx().ordinal()
4628            )
4629            .into());
4630        }
4631        let _main = engine.gpu.enter_main()?;
4632
4633        plan.gate_workspace.quantize(engine, &plan.input)?;
4634        plan.gate_workspace.project(
4635            engine,
4636            &plan.gate.codes,
4637            &plan.gate.scales,
4638            &plan.route_csr,
4639            plan.gate.code_stride,
4640            plan.gate.scale_stride,
4641            1.0,
4642        )?;
4643        plan.up_workspace.quantize(engine, &plan.input)?;
4644        plan.up_workspace.project(
4645            engine,
4646            &plan.up.codes,
4647            &plan.up.scales,
4648            &plan.route_csr,
4649            plan.up.code_stride,
4650            plan.up.scale_stride,
4651            1.0,
4652        )?;
4653        if let Some(limit) = plan.activation_limit {
4654            engine.silu_clamped_mul_host_expf(
4655                plan.gate_workspace.output(),
4656                plan.up_workspace.output(),
4657                limit,
4658                &mut plan.activation,
4659                plan.pairs * STEP_GROUPED_FP8_WIDTH,
4660            )?;
4661        } else {
4662            engine.silu_mul_host_expf(
4663                plan.gate_workspace.output(),
4664                plan.up_workspace.output(),
4665                &mut plan.activation,
4666                plan.pairs * STEP_GROUPED_FP8_WIDTH,
4667            )?;
4668        }
4669        plan.down_workspace.quantize(engine, &plan.activation)?;
4670        plan.down_workspace.project(
4671            engine,
4672            &plan.down.codes,
4673            &plan.down.scales,
4674            &plan.down_csr,
4675            plan.down.code_stride,
4676            plan.down.scale_stride,
4677            1.0,
4678        )?;
4679
4680        Ok(StepGroupedFp8ProjectionOutput {
4681            gate: engine.dtoh(plan.gate_workspace.output())?,
4682            up: engine.dtoh(plan.up_workspace.output())?,
4683            down: engine.dtoh(plan.down_workspace.output())?,
4684        })
4685    }
4686
4687    pub fn prepare_step_grouped_expert_parallel_gate(
4688        &self,
4689        experts: &ResidentExpertParallel,
4690        input: &[f32],
4691        tokens: usize,
4692        selected: &[usize],
4693        activation_limit: Option<f32>,
4694    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
4695        self.prepare_step_grouped_expert_parallel_gate_with_capacity(
4696            experts,
4697            input,
4698            tokens,
4699            selected,
4700            activation_limit,
4701            tokens,
4702        )
4703    }
4704
4705    #[allow(clippy::too_many_arguments)]
4706    pub fn prepare_step_grouped_expert_parallel_gate_with_capacity(
4707        &self,
4708        experts: &ResidentExpertParallel,
4709        input: &[f32],
4710        tokens: usize,
4711        selected: &[usize],
4712        activation_limit: Option<f32>,
4713        max_tokens: usize,
4714    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
4715        if !self.native_p2p || !self.ep_device_arithmetic {
4716            return Err(
4717                "Step owner-grouped FP8 requires native P2P and device-resident arithmetic".into(),
4718            );
4719        }
4720        validate_step_expert_activation_limit(activation_limit)?;
4721        validate_ep_residency(&self.ranks, experts)?;
4722        validate_activations(input, tokens, experts.input_width)?;
4723        if max_tokens < tokens || max_tokens > i32::MAX as usize {
4724            return Err(format!(
4725                "official Step owner-grouped FP8 tokens {tokens} exceed capacity {max_tokens}"
4726            )
4727            .into());
4728        }
4729        if experts.expert_count != STEP_GROUPED_FP8_EXPERTS
4730            || experts.expert_width != STEP_GROUPED_FP8_WIDTH
4731        {
4732            return Err(format!(
4733                "official Step owner-grouped FP8 requires {} experts at width {}, got {} at {}",
4734                STEP_GROUPED_FP8_EXPERTS,
4735                STEP_GROUPED_FP8_WIDTH,
4736                experts.expert_count,
4737                experts.expert_width,
4738            )
4739            .into());
4740        }
4741        validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
4742        let max_pairs = max_tokens
4743            .checked_mul(STEP_GROUPED_FP8_TOP_K)
4744            .ok_or("official Step owner-grouped FP8 capacity route count overflow")?;
4745        let input_capacity = max_tokens
4746            .checked_mul(experts.input_width)
4747            .ok_or("official Step owner-grouped FP8 input capacity overflow")?;
4748
4749        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
4750        for engine in &self.ranks {
4751            let _main = engine.gpu.enter_main()?;
4752            rank_inputs.push(engine.uninit(input_capacity)?);
4753        }
4754
4755        let mut owners = Vec::with_capacity(self.ranks.len());
4756        for (owner_rank, rank) in experts.ranks.iter().enumerate() {
4757            if rank.gate.expert_range != rank.up.expert_range
4758                || rank.gate.expert_range != rank.down.expert_range
4759            {
4760                return Err(format!(
4761                    "owner-grouped FP8 rank {} gate/up/down expert ranges differ",
4762                    owner_rank
4763                )
4764                .into());
4765            }
4766            let local_experts = rank.gate.expert_range.len();
4767            let engine = &self.ranks[owner_rank];
4768            let _main = engine.gpu.enter_main()?;
4769            let route_csr =
4770                DeviceExpertCsr::with_capacity(engine, local_experts, max_tokens, max_pairs)?;
4771            let down_csr =
4772                DeviceExpertCsr::with_capacity(engine, local_experts, max_pairs, max_pairs)?;
4773            let gate_workspace = Fp8GroupedWorkspace::new(
4774                engine,
4775                experts.input_width,
4776                experts.expert_width,
4777                max_tokens,
4778                max_pairs,
4779            )?;
4780            let up_workspace = Fp8GroupedWorkspace::new(
4781                engine,
4782                experts.input_width,
4783                experts.expert_width,
4784                max_tokens,
4785                max_pairs,
4786            )?;
4787            let down_workspace = Fp8GroupedWorkspace::new(
4788                engine,
4789                experts.expert_width,
4790                experts.input_width,
4791                max_pairs,
4792                max_pairs,
4793            )?;
4794            let activation = engine.uninit(
4795                max_pairs
4796                    .checked_mul(experts.expert_width)
4797                    .ok_or("official Step owner-grouped FP8 activation capacity overflow")?,
4798            )?;
4799            owners.push(PreparedStepGroupedExpertOwner {
4800                rank: owner_rank,
4801                global_pairs: Vec::new(),
4802                route_csr,
4803                down_csr,
4804                gate_workspace,
4805                up_workspace,
4806                down_workspace,
4807                activation,
4808            });
4809        }
4810
4811        let mut plan = PreparedStepGroupedExpertParallelGate {
4812            rank_inputs,
4813            owners,
4814            activation_limit,
4815            tokens: 0,
4816            pairs: 0,
4817            max_tokens,
4818            max_pairs,
4819            input_width: experts.input_width,
4820            expert_width: experts.expert_width,
4821            generation: 0,
4822            executed_generation: None,
4823            ready: false,
4824        };
4825        self.refresh_step_grouped_expert_parallel_gate(
4826            experts, &mut plan, input, tokens, selected,
4827        )?;
4828        Ok(plan)
4829    }
4830
4831    #[allow(clippy::type_complexity)] // allow: one-shot composite type; naming it would hide the shape that matters at the call site
4832    fn prepare_step_grouped_expert_parallel_refresh(
4833        &self,
4834        experts: &ResidentExpertParallel,
4835        plan: &PreparedStepGroupedExpertParallelGate,
4836        tokens: usize,
4837        selected: &[usize],
4838    ) -> Result<(usize, u64, Vec<Option<StepGroupedExpertOwnerSchedule>>), Box<dyn std::error::Error>>
4839    {
4840        validate_ep_residency(&self.ranks, experts)?;
4841        if plan.rank_inputs.len() != self.ranks.len()
4842            || plan.owners.len() != self.ranks.len()
4843            || plan.input_width != experts.input_width
4844            || plan.expert_width != experts.expert_width
4845            || tokens > plan.max_tokens
4846        {
4847            return Err(format!(
4848                "Step owner-grouped FP8 refresh geometry changed ranks={}/{} owners={}/{} \
4849                 input={}/{} expert={}/{} tokens={}/{}",
4850                plan.rank_inputs.len(),
4851                self.ranks.len(),
4852                plan.owners.len(),
4853                self.ranks.len(),
4854                plan.input_width,
4855                experts.input_width,
4856                plan.expert_width,
4857                experts.expert_width,
4858                tokens,
4859                plan.max_tokens,
4860            )
4861            .into());
4862        }
4863        let pairs = validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
4864        if pairs > plan.max_pairs {
4865            return Err(format!(
4866                "Step owner-grouped FP8 route count {pairs} exceeds capacity {}",
4867                plan.max_pairs
4868            )
4869            .into());
4870        }
4871        let next_generation = plan
4872            .generation
4873            .checked_add(1)
4874            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
4875        let owner_routes = partition_expert_owner_routes(
4876            experts.expert_count,
4877            self.ranks.len(),
4878            tokens,
4879            STEP_GROUPED_FP8_TOP_K,
4880            selected,
4881        )?;
4882        let mut schedules = Vec::with_capacity(self.ranks.len());
4883        for routes in owner_routes {
4884            if routes.selected.is_empty() {
4885                schedules.push(None);
4886                continue;
4887            }
4888            let local_experts = experts.ranks[routes.rank].gate.expert_range.len();
4889            let local_pairs = routes.selected.len();
4890            let route_csr = ExpertCsr::from_pair_rows(
4891                local_experts,
4892                tokens,
4893                &routes.selected,
4894                &routes.token_rows,
4895            )?;
4896            let down_rows = (0..local_pairs).collect::<Vec<_>>();
4897            let down_csr = ExpertCsr::from_pair_rows(
4898                local_experts,
4899                local_pairs,
4900                &routes.selected,
4901                &down_rows,
4902            )?;
4903            schedules.push(Some(StepGroupedExpertOwnerSchedule {
4904                global_pairs: routes.global_pairs,
4905                route_csr,
4906                down_csr,
4907            }));
4908        }
4909        Ok((pairs, next_generation, schedules))
4910    }
4911
4912    fn commit_step_grouped_expert_parallel_refresh(
4913        &self,
4914        plan: &mut PreparedStepGroupedExpertParallelGate,
4915        tokens: usize,
4916        pairs: usize,
4917        next_generation: u64,
4918        schedules: Vec<Option<StepGroupedExpertOwnerSchedule>>,
4919    ) -> Result<(), Box<dyn std::error::Error>> {
4920        for (owner, schedule) in plan.owners.iter_mut().zip(schedules) {
4921            let engine = &self.ranks[owner.rank];
4922            let _main = engine.gpu.enter_main()?;
4923            if let Some(schedule) = schedule {
4924                owner.route_csr.refresh(engine, &schedule.route_csr)?;
4925                owner.down_csr.refresh(engine, &schedule.down_csr)?;
4926                owner.global_pairs = schedule.global_pairs;
4927            } else {
4928                owner.route_csr.clear();
4929                owner.down_csr.clear();
4930                owner.global_pairs.clear();
4931            }
4932        }
4933        plan.tokens = tokens;
4934        plan.pairs = pairs;
4935        plan.generation = next_generation;
4936        plan.ready = true;
4937        Ok(())
4938    }
4939
4940    pub fn refresh_step_grouped_expert_parallel_gate(
4941        &self,
4942        experts: &ResidentExpertParallel,
4943        plan: &mut PreparedStepGroupedExpertParallelGate,
4944        input: &[f32],
4945        tokens: usize,
4946        selected: &[usize],
4947    ) -> Result<(), Box<dyn std::error::Error>> {
4948        validate_activations(input, tokens, experts.input_width)?;
4949        let (pairs, next_generation, schedules) =
4950            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
4951
4952        plan.ready = false;
4953        plan.executed_generation = None;
4954        {
4955            let root = &self.ranks[0];
4956            let _main = root.gpu.enter_main()?;
4957            let mut destination = plan.rank_inputs[0].slice_mut(0..input.len());
4958            root.stream().memcpy_htod(input, &mut destination)?;
4959            root.stream().synchronize()?;
4960        }
4961        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
4962        let root_input = &root_inputs[0];
4963        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
4964            let engine = &self.ranks[rank + 1];
4965            let _main = engine.gpu.enter_main()?;
4966            let mut destination = peer_input.slice_mut(0..input.len());
4967            engine
4968                .stream()
4969                .memcpy_dtod(&root_input.slice(0..input.len()), &mut destination)?;
4970        }
4971        self.commit_step_grouped_expert_parallel_refresh(
4972            plan,
4973            tokens,
4974            pairs,
4975            next_generation,
4976            schedules,
4977        )
4978    }
4979
4980    /// Refresh routes and inputs from an already-resident rank-zero activation.
4981    ///
4982    /// The caller must order the source producer before this call. The root copy is completed
4983    /// before peer dispatch, while CSR and workspace allocations retain their stable addresses.
4984    pub fn refresh_step_grouped_expert_parallel_gate_from_root_device(
4985        &self,
4986        experts: &ResidentExpertParallel,
4987        plan: &mut PreparedStepGroupedExpertParallelGate,
4988        input: &CudaSlice<f32>,
4989        tokens: usize,
4990        selected: &[usize],
4991    ) -> Result<(), Box<dyn std::error::Error>> {
4992        let input_values = tokens
4993            .checked_mul(experts.input_width)
4994            .ok_or("Step owner-grouped FP8 input size overflow")?;
4995        let root = self
4996            .ranks
4997            .first()
4998            .ok_or("Step owner-grouped FP8 runtime has no root rank")?;
4999        if input.len() < input_values || input.ordinal() != root.ctx().ordinal() {
5000            return Err(format!(
5001                "Step owner-grouped FP8 root input len/device {}/{} does not cover {} values on \
5002                 device {}",
5003                input.len(),
5004                input.ordinal(),
5005                input_values,
5006                root.ctx().ordinal(),
5007            )
5008            .into());
5009        }
5010        let (pairs, next_generation, schedules) =
5011            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
5012
5013        plan.ready = false;
5014        plan.executed_generation = None;
5015        {
5016            let _main = root.gpu.enter_main()?;
5017            let mut destination = plan.rank_inputs[0].slice_mut(0..input_values);
5018            root.stream()
5019                .memcpy_dtod(&input.slice(0..input_values), &mut destination)?;
5020            root.stream().synchronize()?;
5021        }
5022        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
5023        let root_input = &root_inputs[0];
5024        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
5025            let engine = &self.ranks[rank + 1];
5026            let _main = engine.gpu.enter_main()?;
5027            let mut destination = peer_input.slice_mut(0..input_values);
5028            engine
5029                .stream()
5030                .memcpy_dtod(&root_input.slice(0..input_values), &mut destination)?;
5031        }
5032        self.commit_step_grouped_expert_parallel_refresh(
5033            plan,
5034            tokens,
5035            pairs,
5036            next_generation,
5037            schedules,
5038        )
5039    }
5040
5041    /// Replace a fixed route plan's rank inputs from an already replicated device batch.
5042    ///
5043    /// Route CSR remains unchanged. Advancing the generation invalidates every prior projection
5044    /// and combine result, so callers must refresh combine metadata before executing again.
5045    pub fn refresh_step_grouped_expert_parallel_inputs_from_replicated(
5046        &self,
5047        experts: &ResidentExpertParallel,
5048        plan: &mut PreparedStepGroupedExpertParallelGate,
5049        input: &ResidentReplicatedDeviceRows,
5050    ) -> Result<(), Box<dyn std::error::Error>> {
5051        validate_ep_residency(&self.ranks, experts)?;
5052        validate_replicated_device_rows(&self.ranks, input)?;
5053        if !plan.ready
5054            || input.tokens != plan.tokens
5055            || input.width != plan.input_width
5056            || input.tokens > plan.max_tokens
5057            || plan.rank_inputs.len() != self.ranks.len()
5058            || plan.owners.len() != self.ranks.len()
5059            || plan.input_width != experts.input_width
5060            || plan.expert_width != experts.expert_width
5061        {
5062            return Err("Step owner-grouped replicated input geometry changed".into());
5063        }
5064        let values = input
5065            .tokens
5066            .checked_mul(input.width)
5067            .ok_or("Step owner-grouped replicated input size overflow")?;
5068        let next_generation = plan
5069            .generation
5070            .checked_add(1)
5071            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
5072        plan.ready = false;
5073        plan.executed_generation = None;
5074        for (rank, engine) in self.ranks.iter().enumerate() {
5075            let _main = engine.gpu.enter_main()?;
5076            let mut destination = plan.rank_inputs[rank].slice_mut(0..values);
5077            engine
5078                .stream()
5079                .memcpy_dtod(&input.ranks[rank], &mut destination)?;
5080        }
5081        plan.generation = next_generation;
5082        plan.ready = true;
5083        Ok(())
5084    }
5085
5086    pub fn execute_step_grouped_expert_parallel_gate(
5087        &self,
5088        experts: &ResidentExpertParallel,
5089        plan: &mut PreparedStepGroupedExpertParallelGate,
5090    ) -> Result<(), Box<dyn std::error::Error>> {
5091        validate_ep_residency(&self.ranks, experts)?;
5092        if !plan.ready
5093            || plan.rank_inputs.len() != self.ranks.len()
5094            || plan.owners.len() != self.ranks.len()
5095            || plan.input_width != experts.input_width
5096            || plan.expert_width != experts.expert_width
5097        {
5098            return Err("Step owner-grouped FP8 plan is not ready or its geometry changed".into());
5099        }
5100        plan.executed_generation = None;
5101
5102        for owner in &mut plan.owners {
5103            if owner.global_pairs.is_empty() {
5104                continue;
5105            }
5106            let engine = &self.ranks[owner.rank];
5107            let bank = &experts.ranks[owner.rank];
5108            let _main = engine.gpu.enter_main()?;
5109            let local_pairs = owner.global_pairs.len();
5110            owner.gate_workspace.quantize_for_shape(
5111                engine,
5112                &plan.rank_inputs[owner.rank],
5113                plan.tokens,
5114                local_pairs,
5115            )?;
5116            owner.gate_workspace.project(
5117                engine,
5118                &bank.gate.codes,
5119                &bank.gate.scales,
5120                &owner.route_csr,
5121                bank.gate.code_stride,
5122                bank.gate.scale_stride,
5123                1.0,
5124            )?;
5125            owner.up_workspace.quantize_for_shape(
5126                engine,
5127                &plan.rank_inputs[owner.rank],
5128                plan.tokens,
5129                local_pairs,
5130            )?;
5131            owner.up_workspace.project(
5132                engine,
5133                &bank.up.codes,
5134                &bank.up.scales,
5135                &owner.route_csr,
5136                bank.up.code_stride,
5137                bank.up.scale_stride,
5138                1.0,
5139            )?;
5140        }
5141        for owner in &mut plan.owners {
5142            if owner.global_pairs.is_empty() {
5143                continue;
5144            }
5145            let engine = &self.ranks[owner.rank];
5146            let _main = engine.gpu.enter_main()?;
5147            let values = owner.global_pairs.len() * plan.expert_width;
5148            if let Some(limit) = plan.activation_limit {
5149                engine.silu_clamped_mul_host_expf(
5150                    owner.gate_workspace.output(),
5151                    owner.up_workspace.output(),
5152                    limit,
5153                    &mut owner.activation,
5154                    values,
5155                )?;
5156            } else {
5157                engine.silu_mul_host_expf(
5158                    owner.gate_workspace.output(),
5159                    owner.up_workspace.output(),
5160                    &mut owner.activation,
5161                    values,
5162                )?;
5163            }
5164        }
5165        for owner in &mut plan.owners {
5166            if owner.global_pairs.is_empty() {
5167                continue;
5168            }
5169            let engine = &self.ranks[owner.rank];
5170            let bank = &experts.ranks[owner.rank];
5171            let _main = engine.gpu.enter_main()?;
5172            let local_pairs = owner.global_pairs.len();
5173            owner.down_workspace.quantize_for_shape(
5174                engine,
5175                &owner.activation,
5176                local_pairs,
5177                local_pairs,
5178            )?;
5179            owner.down_workspace.project(
5180                engine,
5181                &bank.down.codes,
5182                &bank.down.scales,
5183                &owner.down_csr,
5184                bank.down.code_stride,
5185                bank.down.scale_stride,
5186                1.0,
5187            )?;
5188        }
5189        plan.executed_generation = Some(plan.generation);
5190        Ok(())
5191    }
5192
5193    pub fn collect_step_grouped_expert_parallel_gate(
5194        &self,
5195        plan: &PreparedStepGroupedExpertParallelGate,
5196    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
5197        if !plan.ready || plan.executed_generation != Some(plan.generation) {
5198            return Err("Step owner-grouped FP8 projection is stale or has not executed".into());
5199        }
5200        let mut gate = vec![0.0f32; plan.pairs * plan.expert_width];
5201        let mut up = vec![0.0f32; plan.pairs * plan.expert_width];
5202        let mut down = vec![0.0f32; plan.pairs * plan.input_width];
5203        for owner in &plan.owners {
5204            if owner.global_pairs.is_empty() {
5205                continue;
5206            }
5207            let engine = &self.ranks[owner.rank];
5208            let _main = engine.gpu.enter_main()?;
5209            let owner_gate = engine.dtoh_view(
5210                &owner
5211                    .gate_workspace
5212                    .output()
5213                    .slice(0..owner.gate_workspace.output_len()),
5214            )?;
5215            let owner_up = engine.dtoh_view(
5216                &owner
5217                    .up_workspace
5218                    .output()
5219                    .slice(0..owner.up_workspace.output_len()),
5220            )?;
5221            let owner_down = engine.dtoh_view(
5222                &owner
5223                    .down_workspace
5224                    .output()
5225                    .slice(0..owner.down_workspace.output_len()),
5226            )?;
5227            for (local_pair, &global_pair) in owner.global_pairs.iter().enumerate() {
5228                let local_expert = local_pair * plan.expert_width;
5229                let global_expert = global_pair * plan.expert_width;
5230                gate[global_expert..global_expert + plan.expert_width]
5231                    .copy_from_slice(&owner_gate[local_expert..local_expert + plan.expert_width]);
5232                up[global_expert..global_expert + plan.expert_width]
5233                    .copy_from_slice(&owner_up[local_expert..local_expert + plan.expert_width]);
5234
5235                let local_hidden = local_pair * plan.input_width;
5236                let global_hidden = global_pair * plan.input_width;
5237                down[global_hidden..global_hidden + plan.input_width]
5238                    .copy_from_slice(&owner_down[local_hidden..local_hidden + plan.input_width]);
5239            }
5240        }
5241        Ok(StepGroupedFp8ProjectionOutput { gate, up, down })
5242    }
5243
5244    pub fn run_step_grouped_expert_parallel_gate(
5245        &self,
5246        experts: &ResidentExpertParallel,
5247        plan: &mut PreparedStepGroupedExpertParallelGate,
5248    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
5249        self.execute_step_grouped_expert_parallel_gate(experts, plan)?;
5250        self.collect_step_grouped_expert_parallel_gate(plan)
5251    }
5252
5253    pub fn prepare_step_grouped_expert_parallel_combine(
5254        &self,
5255        plan: &PreparedStepGroupedExpertParallelGate,
5256        route_weights: &[f32],
5257    ) -> Result<PreparedPeerWeightedRouteCombine, Box<dyn std::error::Error>> {
5258        if !self.native_p2p || !self.ep_device_arithmetic || !plan.ready {
5259            return Err(
5260                "Step owner-grouped combine requires a ready native-P2P device plan".into(),
5261            );
5262        }
5263        let owner_pairs = plan
5264            .owners
5265            .iter()
5266            .map(|owner| owner.global_pairs.as_slice())
5267            .collect::<Vec<_>>();
5268        let shape = validate_weighted_route_combine(
5269            plan.input_width,
5270            STEP_GROUPED_FP8_TOP_K,
5271            plan.max_tokens,
5272            plan.tokens,
5273            &owner_pairs,
5274            route_weights,
5275        )?;
5276        if shape.max_pairs != plan.max_pairs {
5277            return Err(format!(
5278                "Step owner-grouped combine capacity {} != projection capacity {}",
5279                shape.max_pairs, plan.max_pairs
5280            )
5281            .into());
5282        }
5283        let root = self
5284            .ranks
5285            .first()
5286            .ok_or("Step owner-grouped combine has no root rank")?;
5287        let slot_values = shape
5288            .max_pairs
5289            .checked_mul(plan.input_width)
5290            .ok_or("Step owner-grouped combine slot capacity overflow")?;
5291        let output_values = plan
5292            .max_tokens
5293            .checked_mul(plan.input_width)
5294            .ok_or("Step owner-grouped combine output capacity overflow")?;
5295        let (root_device, owners, peer_staging, slots, weights, output) = {
5296            let _main = root.gpu.enter_main()?;
5297            let mut owners = Vec::with_capacity(plan.owners.len());
5298            for _ in &plan.owners {
5299                owners.push(PreparedPeerWeightedRouteOwner {
5300                    token_rows: root.htod_i32(&vec![0; shape.max_pairs])?,
5301                    slots: root.htod_i32(&vec![0; shape.max_pairs])?,
5302                    weights: root.htod(&vec![0.0; shape.max_pairs])?,
5303                    active_pairs: 0,
5304                });
5305            }
5306            (
5307                root.ctx().ordinal(),
5308                owners,
5309                root.uninit(slot_values)?,
5310                root.uninit(slot_values)?,
5311                root.uninit(shape.max_pairs)?,
5312                root.uninit(output_values)?,
5313            )
5314        };
5315        let mut peer_devices = Vec::with_capacity(self.ranks.len().saturating_sub(1));
5316        let mut peer_outputs = Vec::with_capacity(self.ranks.len().saturating_sub(1));
5317        for engine in self.ranks.iter().skip(1) {
5318            let _main = engine.gpu.enter_main()?;
5319            peer_devices.push(engine.ctx().ordinal());
5320            peer_outputs.push(engine.uninit(output_values)?);
5321        }
5322        let mut combine = PreparedPeerWeightedRouteCombine {
5323            root_device,
5324            owners,
5325            peer_staging,
5326            slots,
5327            weights,
5328            output,
5329            peer_devices,
5330            peer_outputs,
5331            width: plan.input_width,
5332            experts_per_token: STEP_GROUPED_FP8_TOP_K,
5333            max_tokens: plan.max_tokens,
5334            max_pairs: shape.max_pairs,
5335            tokens: 0,
5336            pairs: 0,
5337            projection_generation: 0,
5338            output_generation: None,
5339            broadcast_generation: None,
5340            ready: false,
5341        };
5342        self.refresh_step_grouped_expert_parallel_combine(plan, &mut combine, route_weights)?;
5343        Ok(combine)
5344    }
5345
5346    pub fn refresh_step_grouped_expert_parallel_combine(
5347        &self,
5348        plan: &PreparedStepGroupedExpertParallelGate,
5349        combine: &mut PreparedPeerWeightedRouteCombine,
5350        route_weights: &[f32],
5351    ) -> Result<(), Box<dyn std::error::Error>> {
5352        let output_capacity = combine
5353            .max_tokens
5354            .checked_mul(combine.width)
5355            .ok_or("Step owner-grouped combine output capacity overflow")?;
5356        if !plan.ready
5357            || combine.owners.len() != plan.owners.len()
5358            || combine.peer_devices.len() + 1 != self.ranks.len()
5359            || combine.peer_outputs.len() + 1 != self.ranks.len()
5360            || combine.width != plan.input_width
5361            || combine.experts_per_token != STEP_GROUPED_FP8_TOP_K
5362            || combine.max_tokens != plan.max_tokens
5363            || combine.max_pairs != plan.max_pairs
5364            || combine.output.len() < output_capacity
5365            || combine
5366                .peer_outputs
5367                .iter()
5368                .any(|output| output.len() < output_capacity)
5369        {
5370            return Err("Step owner-grouped combine/projection geometry changed".into());
5371        }
5372        if self
5373            .ranks
5374            .iter()
5375            .skip(1)
5376            .zip(&combine.peer_devices)
5377            .any(|(engine, &device)| engine.ctx().ordinal() != device)
5378        {
5379            return Err("Step owner-grouped combine peer devices changed".into());
5380        }
5381        let owner_pairs = plan
5382            .owners
5383            .iter()
5384            .map(|owner| owner.global_pairs.as_slice())
5385            .collect::<Vec<_>>();
5386        let shape = validate_weighted_route_combine(
5387            combine.width,
5388            combine.experts_per_token,
5389            combine.max_tokens,
5390            plan.tokens,
5391            &owner_pairs,
5392            route_weights,
5393        )?;
5394        if shape.max_pairs != combine.max_pairs {
5395            return Err("Step owner-grouped combine capacity changed during refresh".into());
5396        }
5397        let metadata = owner_pairs
5398            .iter()
5399            .map(|pairs| {
5400                let token_rows = pairs
5401                    .iter()
5402                    .map(|&pair| (pair / combine.experts_per_token) as i32)
5403                    .collect::<Vec<_>>();
5404                let slots = pairs
5405                    .iter()
5406                    .map(|&pair| (pair % combine.experts_per_token) as i32)
5407                    .collect::<Vec<_>>();
5408                let weights = pairs
5409                    .iter()
5410                    .map(|&pair| route_weights[pair])
5411                    .collect::<Vec<_>>();
5412                (token_rows, slots, weights)
5413            })
5414            .collect::<Vec<_>>();
5415
5416        combine.ready = false;
5417        combine.output_generation = None;
5418        combine.broadcast_generation = None;
5419        let root = self
5420            .ranks
5421            .first()
5422            .ok_or("Step owner-grouped combine has no root rank")?;
5423        let _main = root.gpu.enter_main()?;
5424        if root.ctx().ordinal() != combine.root_device {
5425            return Err(format!(
5426                "Step owner-grouped combine root device changed {} != {}",
5427                root.ctx().ordinal(),
5428                combine.root_device
5429            )
5430            .into());
5431        }
5432        for (owner, (token_rows, slots, weights)) in combine.owners.iter_mut().zip(metadata) {
5433            if token_rows.is_empty() {
5434                owner.active_pairs = 0;
5435                continue;
5436            }
5437            root.htod_i32_into(&mut owner.token_rows, &token_rows)?;
5438            root.htod_i32_into(&mut owner.slots, &slots)?;
5439            let mut weight_prefix = owner.weights.slice_mut(0..weights.len());
5440            root.stream().memcpy_htod(&weights, &mut weight_prefix)?;
5441            owner.active_pairs = token_rows.len();
5442        }
5443        combine.tokens = plan.tokens;
5444        combine.pairs = shape.pairs;
5445        combine.projection_generation = plan.generation;
5446        combine.ready = true;
5447        Ok(())
5448    }
5449
5450    pub fn execute_step_grouped_expert_parallel_combine(
5451        &self,
5452        plan: &PreparedStepGroupedExpertParallelGate,
5453        combine: &mut PreparedPeerWeightedRouteCombine,
5454    ) -> Result<(), Box<dyn std::error::Error>> {
5455        if !plan.ready
5456            || plan.executed_generation != Some(plan.generation)
5457            || !combine.ready
5458            || combine.tokens != plan.tokens
5459            || combine.pairs != plan.pairs
5460            || combine.width != plan.input_width
5461            || combine.owners.len() != plan.owners.len()
5462            || combine.projection_generation != plan.generation
5463        {
5464            return Err("Step owner-grouped combine is stale or its geometry changed".into());
5465        }
5466        combine.output_generation = None;
5467        combine.broadcast_generation = None;
5468        for owner in &plan.owners {
5469            if owner.rank == 0 || owner.global_pairs.is_empty() {
5470                continue;
5471            }
5472            let engine = &self.ranks[owner.rank];
5473            let _main = engine.gpu.enter_main()?;
5474            engine.stream().synchronize()?;
5475        }
5476        let root = self
5477            .ranks
5478            .first()
5479            .ok_or("Step owner-grouped combine has no root rank")?;
5480        let _main = root.gpu.enter_main()?;
5481        if root.ctx().ordinal() != combine.root_device {
5482            return Err("Step owner-grouped combine is not resident on the root device".into());
5483        }
5484        for (index, owner) in plan.owners.iter().enumerate() {
5485            let metadata = &combine.owners[index];
5486            if owner.global_pairs.len() != metadata.active_pairs {
5487                return Err(format!(
5488                    "Step owner-grouped combine owner {index} rows {} != metadata {}",
5489                    owner.global_pairs.len(),
5490                    metadata.active_pairs
5491                )
5492                .into());
5493            }
5494            if metadata.active_pairs == 0 {
5495                continue;
5496            }
5497            let values = metadata
5498                .active_pairs
5499                .checked_mul(combine.width)
5500                .ok_or("Step owner-grouped combine peer value count overflow")?;
5501            if owner.rank == 0 {
5502                root.scatter_slot(
5503                    owner.down_workspace.output(),
5504                    &metadata.token_rows,
5505                    &metadata.slots,
5506                    &metadata.weights,
5507                    &mut combine.slots,
5508                    &mut combine.weights,
5509                    combine.width,
5510                    combine.experts_per_token,
5511                    metadata.active_pairs,
5512                )?;
5513            } else {
5514                let source = owner.down_workspace.output().slice(0..values);
5515                let mut destination = combine.peer_staging.slice_mut(0..values);
5516                root.stream().memcpy_dtod(&source, &mut destination)?;
5517                root.scatter_slot(
5518                    &combine.peer_staging,
5519                    &metadata.token_rows,
5520                    &metadata.slots,
5521                    &metadata.weights,
5522                    &mut combine.slots,
5523                    &mut combine.weights,
5524                    combine.width,
5525                    combine.experts_per_token,
5526                    metadata.active_pairs,
5527                )?;
5528            }
5529        }
5530        root.reduce_slots_host(
5531            &combine.slots,
5532            &combine.weights,
5533            &mut combine.output,
5534            combine.width,
5535            combine.experts_per_token,
5536            combine.tokens,
5537        )?;
5538        combine.output_generation = Some(plan.generation);
5539        Ok(())
5540    }
5541
5542    pub fn collect_step_grouped_expert_parallel_combine(
5543        &self,
5544        plan: &PreparedStepGroupedExpertParallelGate,
5545        combine: &PreparedPeerWeightedRouteCombine,
5546    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5547        if !plan.ready
5548            || combine.output_generation != Some(plan.generation)
5549            || combine.projection_generation != plan.generation
5550        {
5551            return Err("Step owner-grouped combine output is stale or has not executed".into());
5552        }
5553        let root = self
5554            .ranks
5555            .first()
5556            .ok_or("Step owner-grouped combine has no root rank")?;
5557        let _main = root.gpu.enter_main()?;
5558        if root.ctx().ordinal() != combine.root_device {
5559            return Err("Step owner-grouped combine is not resident on the root device".into());
5560        }
5561        root.dtoh_view(&combine.output.slice(0..combine.tokens * combine.width))
5562    }
5563
5564    /// Copy the active root combine result into a caller-owned engine on the same CUDA device.
5565    ///
5566    /// The persistent combine buffer remains reusable by the next route generation; the returned
5567    /// allocation follows the serving runtime's ordinary transient-output ownership.
5568    pub fn copy_step_grouped_expert_parallel_combine_root(
5569        &self,
5570        plan: &PreparedStepGroupedExpertParallelGate,
5571        combine: &PreparedPeerWeightedRouteCombine,
5572        destination: &Engine,
5573    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5574        if !plan.ready
5575            || combine.output_generation != Some(plan.generation)
5576            || combine.projection_generation != plan.generation
5577        {
5578            return Err("Step owner-grouped combine output is stale or has not executed".into());
5579        }
5580        let root = self
5581            .ranks
5582            .first()
5583            .ok_or("Step owner-grouped combine has no root rank")?;
5584        if root.ctx().ordinal() != combine.root_device
5585            || destination.ctx().ordinal() != combine.root_device
5586        {
5587            return Err(format!(
5588                "Step owner-grouped combine root/destination devices {}/{} != {}",
5589                root.ctx().ordinal(),
5590                destination.ctx().ordinal(),
5591                combine.root_device,
5592            )
5593            .into());
5594        }
5595        let values = combine
5596            .tokens
5597            .checked_mul(combine.width)
5598            .ok_or("Step owner-grouped combine copy size overflow")?;
5599        {
5600            let _main = root.gpu.enter_main()?;
5601            root.stream().synchronize()?;
5602        }
5603        let _main = destination.gpu.enter_main()?;
5604        let mut output = destination.uninit(values)?;
5605        destination
5606            .stream()
5607            .memcpy_dtod(&combine.output.slice(0..values), &mut output)?;
5608        Ok(output)
5609    }
5610
5611    pub fn broadcast_step_grouped_expert_parallel_combine(
5612        &self,
5613        plan: &PreparedStepGroupedExpertParallelGate,
5614        combine: &mut PreparedPeerWeightedRouteCombine,
5615    ) -> Result<(), Box<dyn std::error::Error>> {
5616        if !plan.ready
5617            || combine.output_generation != Some(plan.generation)
5618            || combine.projection_generation != plan.generation
5619            || combine.peer_devices.len() + 1 != self.ranks.len()
5620            || combine.peer_outputs.len() + 1 != self.ranks.len()
5621        {
5622            return Err("Step owner-grouped combine output cannot be broadcast".into());
5623        }
5624        combine.broadcast_generation = None;
5625        let values = combine
5626            .tokens
5627            .checked_mul(combine.width)
5628            .ok_or("Step owner-grouped combine broadcast size overflow")?;
5629        {
5630            let root = self
5631                .ranks
5632                .first()
5633                .ok_or("Step owner-grouped combine has no root rank")?;
5634            let _main = root.gpu.enter_main()?;
5635            if root.ctx().ordinal() != combine.root_device {
5636                return Err("Step owner-grouped combine root device changed".into());
5637            }
5638            root.stream().synchronize()?;
5639        }
5640        let source = &combine.output;
5641        for (index, destination_buffer) in combine.peer_outputs.iter_mut().enumerate() {
5642            let engine = &self.ranks[index + 1];
5643            let _main = engine.gpu.enter_main()?;
5644            if engine.ctx().ordinal() != combine.peer_devices[index] {
5645                return Err(format!(
5646                    "Step owner-grouped combine peer {} device changed",
5647                    index + 1
5648                )
5649                .into());
5650            }
5651            let mut destination = destination_buffer.slice_mut(0..values);
5652            engine
5653                .stream()
5654                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
5655        }
5656        combine.broadcast_generation = Some(plan.generation);
5657        Ok(())
5658    }
5659
5660    pub fn collect_step_grouped_expert_parallel_broadcast(
5661        &self,
5662        plan: &PreparedStepGroupedExpertParallelGate,
5663        combine: &PreparedPeerWeightedRouteCombine,
5664    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
5665        if !plan.ready
5666            || combine.output_generation != Some(plan.generation)
5667            || combine.broadcast_generation != Some(plan.generation)
5668            || combine.peer_outputs.len() + 1 != self.ranks.len()
5669        {
5670            return Err("Step owner-grouped combine broadcast is stale or incomplete".into());
5671        }
5672        let values = combine
5673            .tokens
5674            .checked_mul(combine.width)
5675            .ok_or("Step owner-grouped combine collection size overflow")?;
5676        let mut outputs = Vec::with_capacity(self.ranks.len());
5677        {
5678            let root = &self.ranks[0];
5679            let _main = root.gpu.enter_main()?;
5680            outputs.push(root.dtoh_view(&combine.output.slice(0..values))?);
5681        }
5682        for (index, output) in combine.peer_outputs.iter().enumerate() {
5683            let engine = &self.ranks[index + 1];
5684            let _main = engine.gpu.enter_main()?;
5685            outputs.push(engine.dtoh_view(&output.slice(0..values))?);
5686        }
5687        Ok(outputs)
5688    }
5689
5690    /// Add routed and replicated shared-expert outputs, then add the attention residual.
5691    pub fn finish_step_grouped_expert_parallel_layer(
5692        &self,
5693        plan: &PreparedStepGroupedExpertParallelGate,
5694        combine: &PreparedPeerWeightedRouteCombine,
5695        shared: &ResidentReplicatedDeviceRows,
5696        residual: &ResidentReplicatedDeviceRows,
5697    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
5698        validate_replicated_device_rows(&self.ranks, shared)?;
5699        validate_replicated_device_rows(&self.ranks, residual)?;
5700        if !plan.ready
5701            || plan.executed_generation != Some(plan.generation)
5702            || combine.output_generation != Some(plan.generation)
5703            || combine.broadcast_generation != Some(plan.generation)
5704            || combine.projection_generation != plan.generation
5705            || combine.peer_outputs.len() + 1 != self.ranks.len()
5706            || shared.tokens != combine.tokens
5707            || residual.tokens != combine.tokens
5708            || shared.width != combine.width
5709            || residual.width != combine.width
5710        {
5711            return Err("Step full-layer finish inputs are stale or their geometry changed".into());
5712        }
5713        let values = combine
5714            .tokens
5715            .checked_mul(combine.width)
5716            .ok_or("Step full-layer output size overflow")?;
5717        let mut ranks = Vec::with_capacity(self.ranks.len());
5718        for rank in 0..self.ranks.len() {
5719            let engine = &self.ranks[rank];
5720            let _main = engine.gpu.enter_main()?;
5721            let routed = if rank == 0 {
5722                &combine.output
5723            } else {
5724                &combine.peer_outputs[rank - 1]
5725            };
5726            let mut ffn = engine.uninit(values)?;
5727            engine.add(routed, &shared.ranks[rank], &mut ffn, values)?;
5728            let mut output = engine.uninit(values)?;
5729            engine.add(&residual.ranks[rank], &ffn, &mut output, values)?;
5730            ranks.push(output);
5731        }
5732        Ok(ResidentReplicatedDeviceRows {
5733            ranks,
5734            tokens: combine.tokens,
5735            width: combine.width,
5736        })
5737    }
5738
5739    pub fn run_step_grouped_expert_parallel_combine(
5740        &self,
5741        plan: &PreparedStepGroupedExpertParallelGate,
5742        combine: &mut PreparedPeerWeightedRouteCombine,
5743    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5744        self.execute_step_grouped_expert_parallel_combine(plan, combine)?;
5745        self.collect_step_grouped_expert_parallel_combine(plan, combine)
5746    }
5747
5748    pub fn upload_tensor_parallel(
5749        &self,
5750        gate: E4m3ExpertBank<'_>,
5751        up: E4m3ExpertBank<'_>,
5752        down: E4m3ExpertBank<'_>,
5753    ) -> Result<ResidentTensorParallel, Box<dyn std::error::Error>> {
5754        gate.validate()?;
5755        up.validate()?;
5756        down.validate()?;
5757        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
5758            return Err("TP gate/up/down expert counts differ".into());
5759        }
5760        if gate.in_features != up.in_features || gate.out_features != up.out_features {
5761            return Err("TP gate/up dimensions differ".into());
5762        }
5763        if down.in_features != gate.out_features || down.out_features != gate.in_features {
5764            return Err(format!(
5765                "TP down {}x{} does not invert gate/up {}x{}",
5766                down.out_features, down.in_features, gate.out_features, gate.in_features
5767            )
5768            .into());
5769        }
5770        let tp = self.ranks.len();
5771        validate_column_bank_shape(gate, tp)?;
5772        validate_column_bank_shape(up, tp)?;
5773        validate_row_bank_shape(down, tp)?;
5774
5775        let mut gate_ranks = Vec::with_capacity(tp);
5776        let mut up_ranks = Vec::with_capacity(tp);
5777        let mut down_ranks = Vec::with_capacity(tp);
5778        for (rank, engine) in self.ranks.iter().enumerate() {
5779            gate_ranks.push(upload_column_bank_rank(engine, gate, tp, rank)?);
5780            up_ranks.push(upload_column_bank_rank(engine, up, tp, rank)?);
5781            down_ranks.push(upload_row_bank_rank(engine, down, tp, rank)?);
5782        }
5783        Ok(ResidentTensorParallel {
5784            bank: ResidentTpExpertBank {
5785                gate: gate_ranks,
5786                up: up_ranks,
5787                down: down_ranks,
5788                expert_count: gate.expert_count,
5789                input_width: gate.in_features,
5790                expert_width: gate.out_features,
5791            },
5792        })
5793    }
5794
5795    pub fn run_tensor_parallel_routes(
5796        &self,
5797        experts: &ResidentTensorParallel,
5798        input: &[f32],
5799        tokens: usize,
5800        selected: &[usize],
5801        route_weights: &[f32],
5802        experts_per_token: usize,
5803    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5804        validate_tp_bank_residency(&self.ranks, &experts.bank)?;
5805        validate_activations(input, tokens, experts.bank.input_width)?;
5806        let pairs = tokens
5807            .checked_mul(experts_per_token)
5808            .ok_or("TP route count overflow")?;
5809        if selected.len() != pairs || route_weights.len() != pairs {
5810            return Err(format!(
5811                "TP routes selected={} weights={} != tokens {tokens} x experts/token \
5812                 {experts_per_token} ({pairs})",
5813                selected.len(),
5814                route_weights.len(),
5815            )
5816            .into());
5817        }
5818        if !route_weights.iter().all(|weight| weight.is_finite()) {
5819            return Err("TP route weights contain a non-finite value".into());
5820        }
5821
5822        let mut output = vec![0.0f32; tokens * experts.bank.input_width];
5823        for token in 0..tokens {
5824            let input_row =
5825                &input[token * experts.bank.input_width..(token + 1) * experts.bank.input_width];
5826            for slot in 0..experts_per_token {
5827                let pair = token * experts_per_token + slot;
5828                let expert = selected[pair];
5829                if expert >= experts.bank.expert_count {
5830                    return Err(format!(
5831                        "TP selected expert {expert} outside 0..{}",
5832                        experts.bank.expert_count
5833                    )
5834                    .into());
5835                }
5836                let down = if self.native_p2p {
5837                    self.run_tensor_parallel_expert_native(&experts.bank, expert, input_row)?
5838                } else {
5839                    let gate =
5840                        self.run_column_bank_expert(&experts.bank.gate, expert, input_row)?;
5841                    let up = self.run_column_bank_expert(&experts.bank.up, expert, input_row)?;
5842                    let activated: Vec<f32> = gate
5843                        .iter()
5844                        .zip(&up)
5845                        .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
5846                        .collect();
5847                    debug_assert_eq!(activated.len(), experts.bank.expert_width);
5848                    self.run_row_bank_expert(&experts.bank.down, expert, &activated)?
5849                };
5850                let weight = route_weights[pair];
5851                for (sum, value) in output
5852                    [token * experts.bank.input_width..(token + 1) * experts.bank.input_width]
5853                    .iter_mut()
5854                    .zip(down)
5855                {
5856                    *sum += weight * value;
5857                }
5858            }
5859        }
5860        Ok(output)
5861    }
5862
5863    fn run_column_bank_expert(
5864        &self,
5865        ranks: &[ResidentE4m3ExpertBankRank],
5866        expert: usize,
5867        input: &[f32],
5868    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5869        let local_out = ranks
5870            .first()
5871            .ok_or("TP column bank has no ranks")?
5872            .out_features;
5873        let mut gathered = vec![0.0f32; local_out * ranks.len()];
5874        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
5875            let shard = run_resident_bank_expert(engine, bank, expert, input, 1)?;
5876            gathered[rank * local_out..(rank + 1) * local_out].copy_from_slice(&shard);
5877        }
5878        Ok(gathered)
5879    }
5880
5881    fn run_row_bank_expert(
5882        &self,
5883        ranks: &[ResidentE4m3ExpertBankRank],
5884        expert: usize,
5885        input: &[f32],
5886    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5887        let local_in = ranks.first().ok_or("TP row bank has no ranks")?.in_features;
5888        if input.len() != local_in * ranks.len() {
5889            return Err(format!(
5890                "TP row input {} != {} ranks x {local_in}",
5891                input.len(),
5892                ranks.len()
5893            )
5894            .into());
5895        }
5896        let out_features = ranks[0].out_features;
5897        let mut reduced = vec![0.0f32; out_features];
5898        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
5899            let blocks = bank
5900                .k_blocks
5901                .ok_or("TP row bank is not packed in native K-block order")?;
5902            if blocks * FP8_BLOCK != local_in {
5903                return Err(format!(
5904                    "TP row bank has {blocks} blocks but local input width is {local_in}"
5905                )
5906                .into());
5907            }
5908            for block in 0..blocks {
5909                let global_start = rank * local_in + block * FP8_BLOCK;
5910                let partial = run_resident_bank_expert_block(
5911                    engine,
5912                    bank,
5913                    expert,
5914                    block,
5915                    &input[global_start..global_start + FP8_BLOCK],
5916                )?;
5917                for (sum, value) in reduced.iter_mut().zip(partial) {
5918                    *sum += value;
5919                }
5920            }
5921        }
5922        Ok(reduced)
5923    }
5924
5925    fn run_tensor_parallel_expert_native(
5926        &self,
5927        bank: &ResidentTpExpertBank,
5928        expert: usize,
5929        input: &[f32],
5930    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5931        if !self.native_p2p || self.ranks.len() < 2 {
5932            return Err("native TP expert execution requires at least two P2P ranks".into());
5933        }
5934        let local_out = bank
5935            .gate
5936            .first()
5937            .ok_or("native TP gate bank has no ranks")?
5938            .out_features;
5939        if local_out * self.ranks.len() != bank.expert_width {
5940            return Err(format!(
5941                "native TP gate shards {}x{local_out} != expert width {}",
5942                self.ranks.len(),
5943                bank.expert_width
5944            )
5945            .into());
5946        }
5947
5948        // The caller's routed input is already host-canonical. Upload once on rank zero, then
5949        // broadcast over peer copies so no other rank receives a host-staged duplicate.
5950        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
5951        let root_input = {
5952            let root = &self.ranks[0];
5953            let _main = root.gpu.enter_main()?;
5954            root.htod(input)?
5955        };
5956        rank_inputs.push(root_input);
5957        for engine in &self.ranks[1..] {
5958            let peer_input = {
5959                let _main = engine.gpu.enter_main()?;
5960                let mut peer_input = engine.uninit(input.len())?;
5961                engine
5962                    .stream()
5963                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
5964                peer_input
5965            };
5966            rank_inputs.push(peer_input);
5967        }
5968
5969        let mut gate_shards = Vec::with_capacity(self.ranks.len());
5970        let mut up_shards = Vec::with_capacity(self.ranks.len());
5971        #[allow(clippy::needless_range_loop)]
5972        // allow: the explicit index loop keeps the offset arithmetic visible and aligned with the device-side indexing
5973        for rank in 0..self.ranks.len() {
5974            gate_shards.push(run_resident_bank_expert_device(
5975                &self.ranks[rank],
5976                &bank.gate[rank],
5977                expert,
5978                &rank_inputs[rank],
5979                1,
5980            )?);
5981            up_shards.push(run_resident_bank_expert_device(
5982                &self.ranks[rank],
5983                &bank.up[rank],
5984                expert,
5985                &rank_inputs[rank],
5986                1,
5987            )?);
5988        }
5989
5990        // Preserve the established canonical activation program for the first native transport
5991        // milestone. The shards move to rank zero over P2P; only the scalar activation expression
5992        // executes on host. A later device-activation increment must earn its own exactness gate.
5993        let gate = self.gather_native_column_shards(&gate_shards, 1, local_out)?;
5994        let up = self.gather_native_column_shards(&up_shards, 1, local_out)?;
5995        let activated = gate
5996            .iter()
5997            .zip(&up)
5998            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
5999            .collect::<Vec<_>>();
6000        debug_assert_eq!(activated.len(), bank.expert_width);
6001
6002        let root_activated = {
6003            let root = &self.ranks[0];
6004            let _main = root.gpu.enter_main()?;
6005            root.htod(&activated)?
6006        };
6007        let mut rank_activated = Vec::with_capacity(self.ranks.len());
6008        for (rank, engine) in self.ranks.iter().enumerate() {
6009            let start = rank * local_out;
6010            let source = root_activated.slice(start..start + local_out);
6011            let local = {
6012                let _main = engine.gpu.enter_main()?;
6013                let mut local = engine.uninit(local_out)?;
6014                engine.stream().memcpy_dtod(&source, &mut local)?;
6015                local
6016            };
6017            rank_activated.push(local);
6018        }
6019
6020        let out_features = bank
6021            .down
6022            .first()
6023            .ok_or("native TP down bank has no ranks")?
6024            .out_features;
6025        let mut reduced = {
6026            let root = &self.ranks[0];
6027            let _main = root.gpu.enter_main()?;
6028            root.htod(&vec![0.0f32; out_features])?
6029        };
6030        let mut remote_partial_keepalive = Vec::new();
6031        #[allow(clippy::needless_range_loop)]
6032        // allow: the explicit index loop keeps the offset arithmetic visible and aligned with the device-side indexing
6033        for rank in 0..self.ranks.len() {
6034            let down = &bank.down[rank];
6035            let blocks = down
6036                .k_blocks
6037                .ok_or("native TP row bank is not packed in checkpoint-block order")?;
6038            if blocks * FP8_BLOCK != local_out {
6039                return Err(format!(
6040                    "native TP rank {rank} has {blocks} blocks but local activation width is \
6041                     {local_out}"
6042                )
6043                .into());
6044            }
6045            for block in 0..blocks {
6046                let start = block * FP8_BLOCK;
6047                let input_block = rank_activated[rank].slice(start..start + FP8_BLOCK);
6048                let partial = run_resident_bank_expert_block_device(
6049                    &self.ranks[rank],
6050                    down,
6051                    expert,
6052                    block,
6053                    &input_block,
6054                )?;
6055                let root_partial = if rank == 0 {
6056                    partial
6057                } else {
6058                    let root = &self.ranks[0];
6059                    let _main = root.gpu.enter_main()?;
6060                    let mut peer_partial = root.uninit(out_features)?;
6061                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
6062                    remote_partial_keepalive.push(partial);
6063                    peer_partial
6064                };
6065                let next = {
6066                    let root = &self.ranks[0];
6067                    let _main = root.gpu.enter_main()?;
6068                    let mut next = root.uninit(out_features)?;
6069                    root.add(&reduced, &root_partial, &mut next, out_features)?;
6070                    next
6071                };
6072                reduced = next;
6073            }
6074        }
6075        let output = {
6076            let root = &self.ranks[0];
6077            let _main = root.gpu.enter_main()?;
6078            root.dtoh(&reduced)?
6079        };
6080        drop(remote_partial_keepalive);
6081        Ok(output)
6082    }
6083
6084    /// Gather token-major rank-local columns into one canonical root-device matrix.
6085    pub fn gather_native_column_shards_device(
6086        &self,
6087        shards: &[CudaSlice<f32>],
6088        tokens: usize,
6089        local_out: usize,
6090    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6091        let shard_len = tokens
6092            .checked_mul(local_out)
6093            .ok_or("native TP gather shard size overflow")?;
6094        if shards.len() != self.ranks.len() || shards.iter().any(|shard| shard.len() != shard_len) {
6095            return Err("native TP gather shard geometry mismatch".into());
6096        }
6097        // PRODUCER FENCE (2026-08-20 flake fix): the root stream peer-reads shards produced on
6098        // the other ranks' streams; without fencing those producers the copy can read a partial
6099        // kernel output.
6100        for engine in &self.ranks[1..] {
6101            let _main = engine.gpu.enter_main()?;
6102            engine.stream().synchronize()?;
6103        }
6104        let root = &self.ranks[0];
6105        let _main = root.gpu.enter_main()?;
6106        let global_out = shards
6107            .len()
6108            .checked_mul(local_out)
6109            .ok_or("native TP gather output width overflow")?;
6110        let gathered_len = tokens
6111            .checked_mul(global_out)
6112            .ok_or("native TP gather output size overflow")?;
6113        let mut gathered = root.uninit(gathered_len)?;
6114        if self.bulk_p2p {
6115            root.place_rows_strided(&shards[0], &mut gathered, local_out, tokens, global_out, 0)?;
6116            if shards.len() > 1 {
6117                let mut staging = root.uninit(shard_len)?;
6118                for (rank, shard) in shards.iter().enumerate().skip(1) {
6119                    root.stream().memcpy_dtod(shard, &mut staging)?;
6120                    root.place_rows_strided(
6121                        &staging,
6122                        &mut gathered,
6123                        local_out,
6124                        tokens,
6125                        global_out,
6126                        rank * local_out,
6127                    )?;
6128                }
6129            }
6130        } else {
6131            for token in 0..tokens {
6132                for (rank, shard) in shards.iter().enumerate() {
6133                    let source = shard.slice(token * local_out..(token + 1) * local_out);
6134                    let start = token * global_out + rank * local_out;
6135                    let mut destination = gathered.slice_mut(start..start + local_out);
6136                    root.stream().memcpy_dtod(&source, &mut destination)?;
6137                }
6138            }
6139        }
6140        Ok(gathered)
6141    }
6142
6143    pub fn gather_native_column_shards(
6144        &self,
6145        shards: &[CudaSlice<f32>],
6146        tokens: usize,
6147        local_out: usize,
6148    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6149        let gathered = self.gather_native_column_shards_device(shards, tokens, local_out)?;
6150        let root = &self.ranks[0];
6151        let _main = root.gpu.enter_main()?;
6152        root.dtoh(&gathered)
6153    }
6154
6155    pub(crate) fn decode_v2_workspace(&self) -> &std::sync::Mutex<Vec<StepTpDecodeV2Ws>> {
6156        &self.decode_v2
6157    }
6158
6159    /// Build the v2 decode-attention workspace for this layer's geometry on first use, or
6160    /// return the index of the matching one. Attention geometry varies across the trunk
6161    /// (per-layer query-head counts), so workspaces are keyed by their geometry pins — a
6162    /// handful exist per model, never one per layer.
6163    ///
6164    /// Refuses non-F32-resident projections: the v2 driver's bit-exactness claim against v1
6165    /// holds per residency class, and only the mirror class has no per-call weight expansion
6166    /// to hide allocation churn behind.
6167    #[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
6168    pub(crate) fn decode_v2_ensure(
6169        &self,
6170        e: &Engine,
6171        q_m: &ResidentBf16ColumnParallel,
6172        k_m: &ResidentBf16ColumnParallel,
6173        v_m: &ResidentBf16ColumnParallel,
6174        o_m: &ResidentStepBf16RowParallel,
6175        heads: usize,
6176    ) -> Result<usize, Box<dyn std::error::Error>> {
6177        if self.ranks.len() > 1 && !self.native_p2p {
6178            return Err("step TP decode v2 requires native P2P ranks".into());
6179        }
6180        let ranks = self.ranks.len();
6181        // Residency contract: the canonical-chunk (non-fused) program needs the F32 mirror;
6182        // the fused-kernel door also reads raw checkpoint bf16 directly (halving the weight
6183        // traffic), so bf16 residency is accepted when that door is on.
6184        let fused_door = step_tp_qkv_fused_enabled()?;
6185        let arm_ok = |weight: &ResidentBf16Weight| match weight {
6186            ResidentBf16Weight::F32(_) => true,
6187            ResidentBf16Weight::Bf16(_) => fused_door,
6188        };
6189        for matrix in [q_m, k_m, v_m] {
6190            validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
6191            if matrix.out_features % ranks != 0 || matrix.in_features != q_m.in_features {
6192                return Err("step TP decode v2 QKV geometry mismatch".into());
6193            }
6194            for rank in &matrix.ranks {
6195                if !arm_ok(&rank.weight) {
6196                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
6197                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
6198                        .into());
6199                }
6200            }
6201        }
6202        validate_step_bf16_row_residency(&self.ranks, o_m)?;
6203        for blocks in &o_m.ranks {
6204            for block in blocks {
6205                if !arm_ok(&block.weight) {
6206                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
6207                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
6208                        .into());
6209                }
6210            }
6211        }
6212        if v_m.out_features != k_m.out_features
6213            || o_m.in_features != q_m.out_features
6214            || heads == 0
6215            || heads % ranks != 0
6216        {
6217            return Err("step TP decode v2 K/V/O geometry mismatch".into());
6218        }
6219        let local_q_dim = q_m.out_features / ranks;
6220        let local_kv_dim = k_m.out_features / ranks;
6221        let o_out = o_m.out_features;
6222        let o_block_cols = o_m.canonical_chunk_cols;
6223        let blocks_per_rank = o_m.ranks.first().map(Vec::len).unwrap_or(0);
6224        if blocks_per_rank == 0
6225            || o_m
6226                .ranks
6227                .iter()
6228                .any(|blocks| blocks.len() != blocks_per_rank)
6229            || blocks_per_rank * o_block_cols * ranks != o_m.in_features
6230        {
6231            return Err("step TP decode v2 O canonical block grid mismatch".into());
6232        }
6233
6234        let mut guard = self
6235            .decode_v2
6236            .lock()
6237            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6238        if let Some(index) = guard.iter().position(|ws| {
6239            ws.local_q_dim == local_q_dim
6240                && ws.local_kv_dim == local_kv_dim
6241                && ws.heads == heads
6242                && ws.o_out == o_out
6243                && ws.o_block_cols == o_block_cols
6244                && ws.blocks_per_rank == blocks_per_rank
6245                && ws.e_device == e.ctx().ordinal()
6246                && ws.q.len() == ranks
6247        }) {
6248            return Ok(index);
6249        }
6250
6251        let mut q_raw = Vec::with_capacity(ranks);
6252        let mut k_raw = Vec::with_capacity(ranks);
6253        let mut v_raw = Vec::with_capacity(ranks);
6254        let mut q = Vec::with_capacity(ranks);
6255        let mut k = Vec::with_capacity(ranks);
6256        let mut pos = Vec::with_capacity(ranks);
6257        let mut gate = Vec::with_capacity(ranks);
6258        let mut attn_out = Vec::with_capacity(ranks);
6259        let mut gated = Vec::with_capacity(ranks);
6260        let mut fuse_ctr = Vec::with_capacity(ranks);
6261        let mut o_partials = Vec::with_capacity(ranks);
6262        let mut ev_rank = Vec::with_capacity(ranks);
6263        let direct_join = oproj_direct_on();
6264        for (rank, engine) in self.ranks.iter().enumerate() {
6265            let _main = engine.gpu.enter_main()?;
6266            q_raw.push(engine.uninit(local_q_dim)?);
6267            k_raw.push(engine.uninit(local_kv_dim)?);
6268            v_raw.push(engine.uninit(local_kv_dim)?);
6269            q.push(engine.uninit(local_q_dim)?);
6270            k.push(engine.uninit(local_kv_dim)?);
6271            pos.push(engine.htod_i32(&[0])?);
6272            fuse_ctr.push(engine.stream().clone_htod(&[0u32])?);
6273            gate.push(engine.uninit(heads / ranks)?);
6274            attn_out.push(engine.uninit(local_q_dim)?);
6275            gated.push(engine.uninit(local_q_dim)?);
6276            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
6277            for _ in 0..blocks_per_rank {
6278                // Direct join: peer ranks' partials live on ROOT so the b4 kernel's
6279                // stores land there over P2P (UVA) and no pull copy is needed.
6280                if direct_join && rank != 0 {
6281                    let root = &self.ranks[0];
6282                    let _root_main = root.gpu.enter_main()?;
6283                    rank_partials.push(root.uninit(o_out)?);
6284                } else {
6285                    rank_partials.push(engine.uninit(o_out)?);
6286                }
6287            }
6288            o_partials.push(rank_partials);
6289            ev_rank.push(engine.ctx().new_event(None)?);
6290        }
6291        use cudarc::driver::DevicePtr;
6292        let mut raw_o_partials = Vec::with_capacity(ranks);
6293        let mut raw_k = Vec::with_capacity(ranks);
6294        let mut raw_v_raw = Vec::with_capacity(ranks);
6295        for rank in 0..ranks {
6296            let engine = &self.ranks[rank];
6297            {
6298                let _main = engine.gpu.enter_main()?;
6299                let stream = engine.stream();
6300                let (k_ptr, _k_guard) = k[rank].device_ptr(&stream);
6301                let (v_ptr, _v_guard) = v_raw[rank].device_ptr(&stream);
6302                raw_k.push(k_ptr);
6303                raw_v_raw.push(v_ptr);
6304            }
6305            let partial_engine = if direct_join && rank != 0 {
6306                &self.ranks[0]
6307            } else {
6308                engine
6309            };
6310            let _main = partial_engine.gpu.enter_main()?;
6311            let stream = partial_engine.stream();
6312            let mut rank_raw = Vec::with_capacity(blocks_per_rank);
6313            for partial in &o_partials[rank] {
6314                let (ptr, _guard) = partial.device_ptr(&stream);
6315                rank_raw.push(ptr);
6316            }
6317            raw_o_partials.push(rank_raw);
6318        }
6319        let root = &self.ranks[0];
6320        let (peer_partial, reduce_a, reduce_b, zeros, k_shadow, v_shadow, ev_refresh, ev_oproj) = {
6321            let _main = root.gpu.enter_main()?;
6322            (
6323                root.uninit(o_out)?,
6324                root.uninit(o_out)?,
6325                root.uninit(o_out)?,
6326                root.htod(&vec![0.0f32; o_out])?,
6327                root.uninit(ranks * local_kv_dim)?,
6328                root.uninit(ranks * local_kv_dim)?,
6329                root.ctx().new_event(None)?,
6330                root.ctx().new_event(None)?,
6331            )
6332        };
6333        let (raw_peer_partial, raw_k_shadow, raw_v_shadow) = {
6334            let _main = root.gpu.enter_main()?;
6335            let stream = root.stream();
6336            let (peer, _peer_guard) = peer_partial.device_ptr(&stream);
6337            let (k, _k_guard) = k_shadow.device_ptr(&stream);
6338            let (v, _v_guard) = v_shadow.device_ptr(&stream);
6339            (peer, k, v)
6340        };
6341        let (gate_e, ev_entry) = {
6342            let _main = e.gpu.enter_main()?;
6343            (e.uninit(heads)?, e.ctx().new_event(None)?)
6344        };
6345        let raw_attn_in = Vec::new();
6346        let raw_pos = Vec::new();
6347        guard.push(StepTpDecodeV2Ws {
6348            tcol_q: Vec::new(),
6349            tcol_k: Vec::new(),
6350            tcol_v: Vec::new(),
6351            tcol_g: Vec::new(),
6352            tcol_in: Vec::new(),
6353            tcol_cap: 0,
6354            w8_aq: Vec::new(),
6355            w8_ad: Vec::new(),
6356            w8_in: 0,
6357            w8o_aq: Vec::new(),
6358            w8o_ad: Vec::new(),
6359            w8o_in: 0,
6360            w8t_aq: Vec::new(),
6361            w8t_ad: Vec::new(),
6362            w8t_in: 0,
6363            w8t_oaq: Vec::new(),
6364            w8t_oad: Vec::new(),
6365            w8t_oin: 0,
6366            w8t_cap: 0,
6367            fa2_q: Vec::new(),
6368            fa2_gate: Vec::new(),
6369            fa2_gated: Vec::new(),
6370            fa2_cap: 0,
6371            rope_k_t: Vec::new(),
6372            rope_ctr_t: Vec::new(),
6373            rope_pos_t: Vec::new(),
6374            rows_tabs: Vec::new(),
6375            rows_tab_t: Vec::new(),
6376            rows_tab_shadow: Vec::new(),
6377            tcol_gated: Vec::new(),
6378            tcol_opart: Vec::new(),
6379            tcol_opeer: None,
6380            tcol_omix: None,
6381            tcol_ocap: 0,
6382            q_raw,
6383            k_raw,
6384            v_raw,
6385            q,
6386            k,
6387            pos,
6388            fuse_ctr,
6389            gate,
6390            attn_out,
6391            gated,
6392            o_partials,
6393            raw_o_partials,
6394            raw_k,
6395            raw_v_raw,
6396            ev_rank,
6397            peer_partial,
6398            reduce_a,
6399            reduce_b,
6400            zeros,
6401            k_shadow,
6402            v_shadow,
6403            ev_refresh,
6404            ev_oproj,
6405            gate_e,
6406            attn_in: Vec::new(),
6407            h_stage: None,
6408            pos_stage: None,
6409            raw_h_stage: 0,
6410            raw_pos_stage: 0,
6411            raw_attn_in,
6412            raw_pos,
6413            raw_o_partial1: 0,
6414            raw_peer_partial,
6415            raw_k1: 0,
6416            raw_v1: 0,
6417            raw_k_shadow,
6418            raw_v_shadow,
6419            raw_mixed_stage_e: 0,
6420            raw_reduce_a: 0,
6421            raw_shadow_stage_e: (0, 0),
6422            ev_entry,
6423            e_device: e.ctx().ordinal(),
6424            local_q_dim,
6425            local_kv_dim,
6426            heads,
6427            o_out,
6428            o_block_cols,
6429            blocks_per_rank,
6430        });
6431        eprintln!(
6432            "[step-tp-decode-v2] workspace ranks={ranks} local_q={local_q_dim} \
6433             local_kv={local_kv_dim} heads={heads} o_blocks={blocks_per_rank}x{o_block_cols} \
6434             residency=persistent ordering=evented performance_claim=false"
6435        );
6436        Ok(guard.len() - 1)
6437    }
6438
6439    /// v2 phase 1: replicate the layer input, project QKV, norm, rope, and stage the gate —
6440    /// all into the persistent workspace, ordered by events instead of host syncs.
6441    ///
6442    /// The caller must have queued every producer of `h`, `pos_d`, and `gate_raw` on `e`'s
6443    /// stream BEFORE this call: `ev_entry` is recorded once here and every rank stream waits
6444    /// on it (the entry fence also guards workspace reuse across layers — any consumer of the
6445    /// previous layer's outputs was queued on `e`'s stream before this record).
6446    #[allow(clippy::too_many_arguments)]
6447    /// T-COLUMN verify precompute (spec MTP): stage T input rows to every rank and run the
6448    /// weight-amortized qkvg_tcol per rank into the ws slabs. Rope/norm/append stay per
6449    /// column in the unmodified t=1 program (defer_norm_rope contract). Bit-exact per
6450    /// column vs the t=1 kernel by construction.
6451    #[allow(clippy::too_many_arguments)]
6452    pub fn decode_v2_input_qkv_tcol(
6453        &self,
6454        ws_index: usize,
6455        e: &Engine,
6456        h_t: &CudaSlice<f32>,
6457        t: usize,
6458        q_m: &ResidentBf16ColumnParallel,
6459        k_m: &ResidentBf16ColumnParallel,
6460        v_m: &ResidentBf16ColumnParallel,
6461        gate_shards: Option<StepTpGateShards<'_>>,
6462    ) -> Result<(), Box<dyn std::error::Error>> {
6463        let ranks = self.ranks.len();
6464        let mut guard = self
6465            .decode_v2
6466            .lock()
6467            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6468        let ws = guard
6469            .get_mut(ws_index)
6470            .ok_or("step TP decode v2 workspace index out of range")?;
6471        let in_f = q_m.in_features;
6472        if h_t.len() < t * in_f || t == 0 || t > 32 {
6473            return Err("decode_v2_input_qkv_tcol geometry".into());
6474        }
6475        // Lazily arm the slabs to capacity.
6476        if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
6477            ws.tcol_q.clear();
6478            ws.tcol_k.clear();
6479            ws.tcol_v.clear();
6480            ws.tcol_g.clear();
6481            ws.tcol_in.clear();
6482            for engine in &self.ranks {
6483                let _m = engine.gpu.enter_main()?;
6484                ws.tcol_q.push(engine.uninit(32 * ws.local_q_dim)?);
6485                ws.tcol_k.push(engine.uninit(32 * ws.local_kv_dim)?);
6486                ws.tcol_v.push(engine.uninit(32 * ws.local_kv_dim)?);
6487                ws.tcol_g
6488                    .push(engine.uninit(32 * (ws.heads / ranks).max(1))?);
6489                ws.tcol_in.push(engine.uninit(32 * in_f)?);
6490            }
6491            ws.tcol_cap = 32;
6492        }
6493        // Stage the T input rows on e, fence, per-rank pull + tcol launch.
6494        use cudarc::driver::DevicePtr;
6495        let raw_src = {
6496            let _main = e.gpu.enter_main()?;
6497            let stream = e.stream();
6498            let (p, _g) = h_t.device_ptr(&stream);
6499            ws.ev_entry.record(&stream)?;
6500            p
6501        };
6502        for rank in 0..ranks {
6503            let engine = &self.ranks[rank];
6504            let _main = engine.gpu.enter_main()?;
6505            engine.stream().wait(&ws.ev_entry)?;
6506            let raw_dst = {
6507                let stream = engine.stream();
6508                let (p, _g) = ws.tcol_in[rank].device_ptr(&stream);
6509                p
6510            };
6511            raw_copy_bytes(raw_dst, raw_src, t * in_f * 4, engine)?;
6512            let out_g = match &gate_shards {
6513                Some(_) => ws.heads / ranks,
6514                None => 0,
6515            };
6516            match (
6517                &q_m.ranks[rank].weight,
6518                &k_m.ranks[rank].weight,
6519                &v_m.ranks[rank].weight,
6520            ) {
6521                (
6522                    ResidentBf16Weight::Bf16(wq),
6523                    ResidentBf16Weight::Bf16(wk),
6524                    ResidentBf16Weight::Bf16(wv),
6525                ) => {
6526                    let wg = match &gate_shards {
6527                        Some(StepTpGateShards::Bf16(shards)) => &shards[rank],
6528                        Some(StepTpGateShards::F32(_)) => {
6529                            return Err(
6530                                "tcol verify: gate shard class does not match bf16 QKV".into()
6531                            );
6532                        }
6533                        None => wq,
6534                    };
6535                    let StepTpDecodeV2Ws {
6536                        tcol_q,
6537                        tcol_k,
6538                        tcol_v,
6539                        tcol_g,
6540                        tcol_in,
6541                        local_q_dim,
6542                        local_kv_dim,
6543                        w8t_aq,
6544                        w8t_ad,
6545                        w8t_in,
6546                        w8t_cap,
6547                        ..
6548                    } = &mut *ws;
6549                    // MEMRA_TCOL_REFKERN=1 (bisect): fill the slabs via the t=1 kernel per
6550                    // column — separates driver bugs from tcol-kernel bugs.
6551                    static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
6552                    let refk = *REFK
6553                        .get_or_init(|| std::env::var("MEMRA_TCOL_REFKERN").as_deref() == Ok("1"));
6554                    if refk {
6555                        let lq = *local_q_dim;
6556                        let lkv = *local_kv_dim;
6557                        let mut hrow = engine.uninit(in_f)?;
6558                        let mut qr = engine.uninit(lq)?;
6559                        let mut kr = engine.uninit(lkv)?;
6560                        let mut vr = engine.uninit(lkv)?;
6561                        let mut gr = engine.uninit(out_g.max(1))?;
6562                        for c in 0..t {
6563                            {
6564                                let mut dst = hrow.slice_mut(0..in_f);
6565                                engine.stream().memcpy_dtod(
6566                                    &tcol_in[rank].slice(c * in_f..(c + 1) * in_f),
6567                                    &mut dst,
6568                                )?;
6569                            }
6570                            engine.matvec_bf16_qkvg_into(
6571                                wq, wk, wv, wg, &hrow, &mut qr, &mut kr, &mut vr, &mut gr, in_f,
6572                                lq, lkv, out_g,
6573                            )?;
6574                            let stream = engine.stream();
6575                            {
6576                                let mut dst = tcol_q[rank].slice_mut(c * lq..(c + 1) * lq);
6577                                stream.memcpy_dtod(&qr.slice(0..lq), &mut dst)?;
6578                            }
6579                            {
6580                                let mut dst = tcol_k[rank].slice_mut(c * lkv..(c + 1) * lkv);
6581                                stream.memcpy_dtod(&kr.slice(0..lkv), &mut dst)?;
6582                            }
6583                            {
6584                                let mut dst = tcol_v[rank].slice_mut(c * lkv..(c + 1) * lkv);
6585                                stream.memcpy_dtod(&vr.slice(0..lkv), &mut dst)?;
6586                            }
6587                            if out_g > 0 {
6588                                let mut dst = tcol_g[rank].slice_mut(c * out_g..(c + 1) * out_g);
6589                                stream.memcpy_dtod(&gr.slice(0..out_g), &mut dst)?;
6590                            }
6591                        }
6592                    } else if crate::step_tp_w8_on()
6593                        && q_m.ranks[rank].q8.is_some()
6594                        && k_m.ranks[rank].q8.is_some()
6595                        && v_m.ranks[rank].q8.is_some()
6596                        && in_f.is_multiple_of(32)
6597                    {
6598                        // MEMRA_STEP_TP_W8 on the VERIFY walk. nsys put the bf16 tcol QKV at
6599                        // 12.3% of spec GPU time and the bf16 tcol o_proj at 24.8% — the door
6600                        // had only ever replaced the DECODE kernels, so 37% of the verify still
6601                        // streamed bf16 weights. One q8 launch over all t columns; the gate rows
6602                        // stay bf16 as on the decode side.
6603                        if *w8t_in != in_f || *w8t_cap < t || w8t_aq.len() != ranks {
6604                            w8t_aq.clear();
6605                            w8t_ad.clear();
6606                            for e_rank in &self.ranks {
6607                                let _m = e_rank.gpu.enter_main()?;
6608                                w8t_aq.push(e_rank.alloc_i8_uninit(32 * in_f)?);
6609                                w8t_ad.push(e_rank.alloc_uninit::<f32>(32 * (in_f / 32))?);
6610                            }
6611                            *w8t_in = in_f;
6612                            *w8t_cap = 32;
6613                        }
6614                        engine.quantize_q8_1_into(
6615                            &tcol_in[rank],
6616                            t,
6617                            in_f,
6618                            &mut w8t_aq[rank],
6619                            &mut w8t_ad[rank],
6620                        )?;
6621                        engine.qmatvec_q8_0_qkv_rp_t_into(
6622                            q_m.ranks[rank].q8.as_ref().unwrap(),
6623                            k_m.ranks[rank].q8.as_ref().unwrap(),
6624                            v_m.ranks[rank].q8.as_ref().unwrap(),
6625                            &w8t_aq[rank],
6626                            &w8t_ad[rank],
6627                            &mut tcol_q[rank],
6628                            &mut tcol_k[rank],
6629                            &mut tcol_v[rank],
6630                            in_f,
6631                            *local_q_dim,
6632                            *local_kv_dim,
6633                            t,
6634                        )?;
6635                        if out_g > 0 {
6636                            engine.matvec_bf16_rows_into(
6637                                wg,
6638                                &tcol_in[rank],
6639                                &mut tcol_g[rank],
6640                                in_f,
6641                                out_g,
6642                                t,
6643                            )?;
6644                        }
6645                    } else {
6646                        engine.matvec_bf16_qkvg_tcol_into(
6647                            wq,
6648                            wk,
6649                            wv,
6650                            wg,
6651                            &tcol_in[rank],
6652                            &mut tcol_q[rank],
6653                            &mut tcol_k[rank],
6654                            &mut tcol_v[rank],
6655                            &mut tcol_g[rank],
6656                            in_f,
6657                            *local_q_dim,
6658                            *local_kv_dim,
6659                            out_g,
6660                            t,
6661                        )?;
6662                    }
6663                }
6664                _ => return Err("tcol verify requires bf16-resident fused QKV".into()),
6665            }
6666        }
6667        Ok(())
6668    }
6669
6670    /// MEMRA_TCOL_OPROJ eligibility: the defer replaces exactly the o_fused direct-join
6671    /// finish (bf16 b4 kernel, 2 ranks, 4 canonical blocks) with the shadow gathers
6672    /// skipped — so it requires the same doors that arm dictate that finish shape.
6673    pub(crate) fn decode_v2_oproj_tcol_eligible(
6674        &self,
6675        ws: &StepTpDecodeV2Ws,
6676        o_m: &ResidentStepBf16RowParallel,
6677    ) -> bool {
6678        self.ranks.len() == 2
6679            && ws.blocks_per_rank == 4
6680            && step_tp_qkv_fused_enabled().unwrap_or(false)
6681            && no_local_shadow_on()
6682            && std::env::var("MEMRA_B4_X2").as_deref() != Ok("1")
6683            && o_m
6684                .ranks
6685                .iter()
6686                .flatten()
6687                .all(|block| matches!(block.weight, ResidentBf16Weight::Bf16(_)))
6688    }
6689
6690    /// MEMRA_SPEC_FA2 stash: copy this column's per-rank post-rope q and gate rows into
6691    /// the fa2 slabs (rank-stream ordered behind the rope/append that produced them), and
6692    /// give `e` the same anti-dependency wait the skipped finish provided (next column's
6693    /// h/pos re-staging must not overtake this column's rank pulls).
6694    pub(crate) fn decode_v2_stash_fa2(
6695        &self,
6696        ws: &mut StepTpDecodeV2Ws,
6697        e: &Engine,
6698        col: usize,
6699    ) -> Result<(), Box<dyn std::error::Error>> {
6700        let ranks = self.ranks.len();
6701        if col >= 32 {
6702            return Err("decode_v2_stash_fa2 column out of range".into());
6703        }
6704        let lq = ws.local_q_dim;
6705        let lg = (ws.heads / ranks).max(1);
6706        if ws.fa2_cap < 32 || ws.fa2_q.len() != ranks || ws.rows_tab_t.len() != ranks {
6707            ws.fa2_q.clear();
6708            ws.fa2_gate.clear();
6709            ws.fa2_gated.clear();
6710            ws.rope_k_t.clear();
6711            ws.rope_ctr_t.clear();
6712            ws.rope_pos_t.clear();
6713            ws.rows_tab_t.clear();
6714            for engine in &self.ranks {
6715                let _m = engine.gpu.enter_main()?;
6716                ws.fa2_q.push(engine.uninit(32 * lq)?);
6717                ws.fa2_gate.push(engine.uninit(32 * lg)?);
6718                ws.fa2_gated.push(engine.uninit(32 * lq)?);
6719                ws.rope_k_t.push(engine.uninit(32 * ws.local_kv_dim)?);
6720                ws.rope_ctr_t.push(engine.stream().clone_htod(&[0u32; 32])?);
6721                ws.rope_pos_t.push(engine.htod_i32(&[0i32; 32])?);
6722                ws.rows_tab_t
6723                    .push(engine.stream().clone_htod(&[0u64; 32 * 6])?);
6724            }
6725            ws.rows_tabs = (0..ranks).map(|_| Default::default()).collect();
6726            ws.fa2_cap = 32;
6727        }
6728        for rank in 0..ranks {
6729            let engine = &self.ranks[rank];
6730            let _main = engine.gpu.enter_main()?;
6731            {
6732                let mut dst = ws.fa2_q[rank].slice_mut(col * lq..(col + 1) * lq);
6733                engine
6734                    .stream()
6735                    .memcpy_dtod(&ws.q[rank].slice(0..lq), &mut dst)?;
6736            }
6737            {
6738                let mut dst = ws.fa2_gate[rank].slice_mut(col * lg..(col + 1) * lg);
6739                engine
6740                    .stream()
6741                    .memcpy_dtod(&ws.gate[rank].slice(0..lg), &mut dst)?;
6742            }
6743            ws.ev_rank[rank].record(&engine.stream())?;
6744        }
6745        {
6746            let _main = e.gpu.enter_main()?;
6747            for ev in ws.ev_rank.iter() {
6748                e.stream().wait(ev)?;
6749            }
6750        }
6751        Ok(())
6752    }
6753
6754    /// MEMRA_SPEC_FA2 join: after BOTH verify columns stashed (their appends landed in
6755    /// rank-stream order), run ONE fa_decode_dcw2 per rank over the shared KV stream —
6756    /// two query rows, per-row causal bounds, per-row combine+gate — then land the two
6757    /// gated rows in the o-tcol slabs and reuse the weight-amortized o_proj join.
6758    /// Returns the [2, o_out] `mixed` slab on `e`. The caller's precheck enforced the
6759    /// equal-partition guard (boundary rounds never arm the defer).
6760    #[allow(clippy::too_many_arguments)]
6761    #[allow(dead_code)] // allow: banked MEMRA_SPEC_FA2 arm; kept as the named seam its precheck twin documents
6762    pub(crate) fn decode_v2_spec_fa2_join(
6763        &self,
6764        ws_index: usize,
6765        e: &Engine,
6766        o_m: &ResidentStepBf16RowParallel,
6767        kv: &ResidentTpKvCache,
6768        head_dim: usize,
6769        window: usize,
6770        bucket_max: usize,
6771        scale: f32,
6772    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6773        let ranks = self.ranks.len();
6774        // Engagement receipt: a vacuous gate (precheck never passing) must be visible.
6775        static ONCE: std::sync::Once = std::sync::Once::new();
6776        ONCE.call_once(|| eprintln!("[spec-fa2] joined T=2 attention ENGAGED"));
6777        {
6778            let mut guard = self
6779                .decode_v2
6780                .lock()
6781                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6782            let ws = guard
6783                .get_mut(ws_index)
6784                .ok_or("step TP decode v2 workspace index out of range")?;
6785            if ws.fa2_cap < 2 || ws.fa2_q.len() != ranks {
6786                return Err("spec fa2 join without stashed columns".into());
6787            }
6788            let lq = ws.local_q_dim;
6789            let local_heads = (ws.heads / ranks).max(1);
6790            let local_kv_heads = (ws.local_kv_dim / head_dim).max(1);
6791            let capacity = kv.physical_capacity();
6792            let (k_tok_bytes, v_tok_bytes) = (kv.k_tok_bytes(), kv.v_tok_bytes());
6793            // Arm the o-tcol slabs if the oproj door never ran this boot (same shapes).
6794            if ws.tcol_ocap < 2 || ws.tcol_gated.len() != ranks {
6795                ws.tcol_gated.clear();
6796                ws.tcol_opart.clear();
6797                for engine in &self.ranks {
6798                    let _m = engine.gpu.enter_main()?;
6799                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
6800                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6801                }
6802                let root = &self.ranks[0];
6803                let _m = root.gpu.enter_main()?;
6804                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6805                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6806                ws.tcol_ocap = 32;
6807            }
6808            for rank in 0..ranks {
6809                let engine = &self.ranks[rank];
6810                let _main = engine.gpu.enter_main()?;
6811                let rank_cache = kv
6812                    .rank(rank)
6813                    .ok_or("spec fa2 join lost its KV cache rank")?;
6814                let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes);
6815                let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes);
6816                {
6817                    let StepTpDecodeV2Ws {
6818                        fa2_q,
6819                        fa2_gate,
6820                        fa2_gated,
6821                        ..
6822                    } = &mut *ws;
6823                    engine.fa_decode_dcw2(
6824                        &fa2_q[rank],
6825                        &k_ring,
6826                        &v_ring,
6827                        &mut fa2_gated[rank],
6828                        head_dim,
6829                        local_heads,
6830                        local_kv_heads,
6831                        rank_cache.len_d(),
6832                        rank_cache.base_d(),
6833                        window,
6834                        bucket_max,
6835                        scale,
6836                        k_tok_bytes,
6837                        v_tok_bytes,
6838                        &fa2_gate[rank],
6839                    )?;
6840                }
6841                // Both gated rows are contiguous [2, lq] — exactly columns 0..2 of the
6842                // o-tcol slab layout. One dtod, in rank-stream order behind the fa.
6843                let StepTpDecodeV2Ws {
6844                    fa2_gated,
6845                    tcol_gated,
6846                    ..
6847                } = &mut *ws;
6848                let mut dst = tcol_gated[rank].slice_mut(0..2 * lq);
6849                engine
6850                    .stream()
6851                    .memcpy_dtod(&fa2_gated[rank].slice(0..2 * lq), &mut dst)?;
6852            }
6853        }
6854        self.decode_v2_oproj_tcol(ws_index, e, o_m, 2)
6855    }
6856
6857    /// FULL T-ROW ATTENTION PASS over per-row session tables (batched serving): reads
6858    /// the tcol raw-projection slabs, runs ONE rope/append rows launch + ONE fa rows
6859    /// launch + ONE combine per rank (gate straight from the tcol gate slab), then the
6860    /// o_proj tcol join — the whole per-row attention loop in 3 launches/rank/layer.
6861    /// Per-(row, head) programs are the t=1 kernels verbatim; each row appends to and
6862    /// attends its OWN session. `session_parts[rank][row]` = {k_plane, v_plane, len_ptr,
6863    /// base_ptr}; `tab_keys[rank]` keys the per-rank combined-table cache (caller folds
6864    /// layer + session-set + base-arming into it); `stage_pos` stages the position slab
6865    /// (positions are constant across layers within a tick — stage on the first layer).
6866    #[allow(clippy::too_many_arguments)]
6867    pub(crate) fn decode_v2_rope_fa_rows(
6868        &self,
6869        ws_index: usize,
6870        e: &Engine,
6871        o_m: &ResidentStepBf16RowParallel,
6872        session_parts: &[Vec<[u64; 4]>],
6873        tab_keys: &[u64],
6874        positions: &[i32],
6875        stage_pos: bool,
6876        same_session: bool,
6877        q_norms: &[CudaSlice<f32>],
6878        k_norms: &[CudaSlice<f32>],
6879        rope_freqs: &[Option<&crate::CudaSlice<f32>>],
6880        t: usize,
6881        head_dim: usize,
6882        n_rot: usize,
6883        window: usize,
6884        max_ns: usize,
6885        scale: f32,
6886        k_tok_bytes: usize,
6887        v_tok_bytes: usize,
6888        eps: f32,
6889        rope_base: f32,
6890    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
6891        use cudarc::driver::DevicePtr;
6892        let ranks = self.ranks.len();
6893        if session_parts.len() != ranks || tab_keys.len() != ranks || positions.len() < t {
6894            return Err("rope fa rows geometry".into());
6895        }
6896        {
6897            let mut guard = self
6898                .decode_v2
6899                .lock()
6900                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6901            let ws = guard
6902                .get_mut(ws_index)
6903                .ok_or("step TP decode v2 workspace index out of range")?;
6904            if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
6905                return Err("rope fa rows without tcol slabs".into());
6906            }
6907            let lq = ws.local_q_dim;
6908            let lkv = ws.local_kv_dim;
6909            let lg = (ws.heads / ranks).max(1);
6910            let local_heads = (ws.heads / ranks).max(1);
6911            let local_kv_heads = (lkv / head_dim).max(1);
6912            // Arm the fa2/rope slabs (shared with the stash path).
6913            if ws.fa2_cap < 32 || ws.fa2_q.len() != ranks || ws.rows_tab_t.len() != ranks {
6914                ws.fa2_q.clear();
6915                ws.fa2_gate.clear();
6916                ws.fa2_gated.clear();
6917                ws.rope_k_t.clear();
6918                ws.rope_ctr_t.clear();
6919                ws.rope_pos_t.clear();
6920                ws.rows_tab_t.clear();
6921                for engine in &self.ranks {
6922                    let _m = engine.gpu.enter_main()?;
6923                    ws.fa2_q.push(engine.uninit(32 * lq)?);
6924                    ws.fa2_gate.push(engine.uninit(32 * lg)?);
6925                    ws.fa2_gated.push(engine.uninit(32 * lq)?);
6926                    ws.rope_k_t.push(engine.uninit(32 * lkv)?);
6927                    ws.rope_ctr_t.push(engine.stream().clone_htod(&[0u32; 32])?);
6928                    ws.rope_pos_t.push(engine.htod_i32(&[0i32; 32])?);
6929                    ws.rows_tab_t
6930                        .push(engine.stream().clone_htod(&[0u64; 32 * 6])?);
6931                }
6932                ws.rows_tabs = (0..ranks).map(|_| Default::default()).collect();
6933                ws.fa2_cap = 32;
6934            }
6935            if ws.tcol_ocap < t || ws.tcol_gated.len() != ranks {
6936                ws.tcol_gated.clear();
6937                ws.tcol_opart.clear();
6938                for engine in &self.ranks {
6939                    let _m = engine.gpu.enter_main()?;
6940                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
6941                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6942                }
6943                let root = &self.ranks[0];
6944                let _m = root.gpu.enter_main()?;
6945                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6946                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6947                ws.tcol_ocap = 32;
6948            }
6949            for rank in 0..ranks {
6950                let engine = &self.ranks[rank];
6951                let _main = engine.gpu.enter_main()?;
6952                if stage_pos {
6953                    let host: Vec<i32> = positions[..t].to_vec();
6954                    let mut view = ws.rope_pos_t[rank].slice_mut(0..t);
6955                    engine.stream().memcpy_htod(&host, &mut view)?;
6956                }
6957                // Combined 6-word table {k, v, len, base, ctr, back}; ctr = this rank's
6958                // per-row counter slab. Built from the pointers the CALLER just read off
6959                // the live distributed cache, and RESTAGED into a persistent slab before
6960                // every launch (MEMRA_ROWS_TAB_RESTAGE, default ON).
6961                //
6962                // The `rows_tabs` memo this replaces was keyed by a hash of
6963                // (k pointer, base pointer, layer, t) but the table it handed back ALSO
6964                // carried the V and LEN pointers, and nothing invalidated it when a
6965                // session's KV cache was dropped. A later session whose K buffer landed on
6966                // a recycled address therefore hit a dead entry, and
6967                // `qk_norm_rope_append_inc_dcw_rows` WROTE this session's K/V rows through
6968                // the freed V/len pointers it still held while `fa_decode_dcw_rows` read
6969                // them back: a whole non-finite row when the freed pages were re-mapped,
6970                // CUDA_ERROR_ILLEGAL_ADDRESS when they were not. The row-table twin in
6971                // `step35_verify_fa_rows_join` was cured of exactly this in 8c8397e0b2
6972                // ("a process-lifetime map cannot prove allocation generation", Hermes
6973                // `11339f5cd3c132a3`); this fused rope+append+fa path was left out of it,
6974                // and MEMRA_FUSE_ROPE_APPEND=1 makes it the arm that actually runs.
6975                let ctr_base = {
6976                    let s = engine.stream();
6977                    let (p, _g) = ws.rope_ctr_t[rank].device_ptr(&s);
6978                    p
6979                };
6980                let host = rows_tab_host(&session_parts[rank], ctr_base, same_session, t);
6981                // STALE-HIT RECEIPT (MEMRA_ROWS_TAB_STALE_SCAN=1, default OFF): replay the
6982                // retired key against the contents we are about to stage. `engaged` proves
6983                // this path executes at all; `STALE` proves the retired memo would have
6984                // handed a live launch another allocation's pointers, and names which word
6985                // moved. Diagnostic only: it never feeds a kernel.
6986                if rows_tab_stale_scan() {
6987                    if ws.rows_tab_shadow.len() != ranks {
6988                        ws.rows_tab_shadow = (0..ranks).map(|_| Default::default()).collect();
6989                    }
6990                    let n = ROWS_TAB_ENGAGED.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
6991                    if let Some(prev) = ws.rows_tab_shadow[rank].get(&tab_keys[rank])
6992                        && prev != &host
6993                    {
6994                        let words = ["k", "v", "len", "base", "ctr", "back"];
6995                        let moved: Vec<String> = (0..host.len())
6996                            .filter(|&i| prev.get(i) != Some(&host[i]))
6997                            .map(|i| format!("{}[row{}]", words[i % 6], i / 6))
6998                            .collect();
6999                        let stale =
7000                            ROWS_TAB_STALE.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
7001                        eprintln!(
7002                            "[rows-tab] STALE #{stale} lookup #{n} rank={rank} t={t} key={:#018x} moved={}: the retired memo would have launched this row on another allocation's pointers",
7003                            tab_keys[rank],
7004                            moved.join(",")
7005                        );
7006                    }
7007                    ws.rows_tab_shadow[rank].insert(tab_keys[rank], host.clone());
7008                }
7009                let legacy_memo = !rows_tab_restage_on();
7010                if legacy_memo && !ws.rows_tabs[rank].contains_key(&tab_keys[rank]) {
7011                    let tab = engine.stream().clone_htod(&host)?;
7012                    ws.rows_tabs[rank].insert(tab_keys[rank], tab);
7013                }
7014                if !legacy_memo {
7015                    let mut view = ws.rows_tab_t[rank].slice_mut(0..t * 6);
7016                    engine.stream().memcpy_htod(&host, &mut view)?;
7017                }
7018                let StepTpDecodeV2Ws {
7019                    tcol_q,
7020                    tcol_k,
7021                    tcol_v,
7022                    tcol_g,
7023                    fa2_q,
7024                    fa2_gated,
7025                    rope_k_t,
7026                    rope_pos_t,
7027                    rows_tabs,
7028                    rows_tab_t,
7029                    ..
7030                } = &mut *ws;
7031                let tab = if legacy_memo {
7032                    rows_tabs[rank]
7033                        .get(&tab_keys[rank])
7034                        .ok_or("rows tab memo lost its entry")?
7035                } else {
7036                    &rows_tab_t[rank]
7037                };
7038                engine.qk_norm_rope_append_inc_dcw_rows(
7039                    &tcol_q[rank],
7040                    &tcol_k[rank],
7041                    &tcol_v[rank],
7042                    &q_norms[rank],
7043                    &k_norms[rank],
7044                    &mut fa2_q[rank],
7045                    &mut rope_k_t[rank],
7046                    tab,
7047                    &rope_pos_t[rank],
7048                    same_session,
7049                    t,
7050                    lkv,
7051                    lkv,
7052                    k_tok_bytes,
7053                    v_tok_bytes,
7054                    head_dim,
7055                    n_rot,
7056                    local_heads,
7057                    local_kv_heads,
7058                    eps,
7059                    rope_base,
7060                    1.0,
7061                    rope_freqs[rank],
7062                )?;
7063                engine.fa_decode_dcw_rows(
7064                    &fa2_q[rank],
7065                    tab,
7066                    &mut fa2_gated[rank],
7067                    t,
7068                    head_dim,
7069                    local_heads,
7070                    local_kv_heads,
7071                    window,
7072                    max_ns,
7073                    scale,
7074                    k_tok_bytes,
7075                    v_tok_bytes,
7076                    &tcol_g[rank],
7077                )?;
7078                let StepTpDecodeV2Ws {
7079                    fa2_gated,
7080                    tcol_gated,
7081                    ..
7082                } = &mut *ws;
7083                let mut dst = tcol_gated[rank].slice_mut(0..t * lq);
7084                engine
7085                    .stream()
7086                    .memcpy_dtod(&fa2_gated[rank].slice(0..t * lq), &mut dst)?;
7087            }
7088        }
7089        self.decode_v2_oproj_tcol(ws_index, e, o_m, t)
7090    }
7091
7092    /// T-ROW fa join over per-row session tables (the per-session distributed-KV
7093    /// primitive): after all t rows stashed q+gate (their appends landed in rank-stream
7094    /// order), ONE fa_decode_dcw_rows per rank walks every row's own ring with its own
7095    /// geometry — bit-identical per row to its per-row launch — then the o_proj tcol
7096    /// join lands the [t, o_out] `mixed` slab on `e`. `tabs[rank]` is the pre-staged
7097    /// device table on that rank.
7098    #[allow(clippy::too_many_arguments)]
7099    pub(crate) fn decode_v2_fa_rows_join(
7100        &self,
7101        ws_index: usize,
7102        e: &Engine,
7103        o_m: &ResidentStepBf16RowParallel,
7104        tabs: &[&crate::CudaSlice<u64>],
7105        t: usize,
7106        head_dim: usize,
7107        window: usize,
7108        max_ns: usize,
7109        scale: f32,
7110        k_tok_bytes: usize,
7111        v_tok_bytes: usize,
7112    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
7113        let ranks = self.ranks.len();
7114        if tabs.len() != ranks {
7115            return Err("fa rows join needs one table per rank".into());
7116        }
7117        {
7118            let mut guard = self
7119                .decode_v2
7120                .lock()
7121                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
7122            let ws = guard
7123                .get_mut(ws_index)
7124                .ok_or("step TP decode v2 workspace index out of range")?;
7125            if ws.fa2_cap < t || ws.fa2_q.len() != ranks {
7126                return Err("fa rows join without stashed rows".into());
7127            }
7128            let lq = ws.local_q_dim;
7129            let local_heads = (ws.heads / ranks).max(1);
7130            let local_kv_heads = (ws.local_kv_dim / head_dim).max(1);
7131            if ws.tcol_ocap < t || ws.tcol_gated.len() != ranks {
7132                ws.tcol_gated.clear();
7133                ws.tcol_opart.clear();
7134                for engine in &self.ranks {
7135                    let _m = engine.gpu.enter_main()?;
7136                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
7137                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
7138                }
7139                let root = &self.ranks[0];
7140                let _m = root.gpu.enter_main()?;
7141                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
7142                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
7143                ws.tcol_ocap = 32;
7144            }
7145            for rank in 0..ranks {
7146                let engine = &self.ranks[rank];
7147                let _main = engine.gpu.enter_main()?;
7148                {
7149                    let StepTpDecodeV2Ws {
7150                        fa2_q,
7151                        fa2_gate,
7152                        fa2_gated,
7153                        ..
7154                    } = &mut *ws;
7155                    engine.fa_decode_dcw_rows(
7156                        &fa2_q[rank],
7157                        tabs[rank],
7158                        &mut fa2_gated[rank],
7159                        t,
7160                        head_dim,
7161                        local_heads,
7162                        local_kv_heads,
7163                        window,
7164                        max_ns,
7165                        scale,
7166                        k_tok_bytes,
7167                        v_tok_bytes,
7168                        &fa2_gate[rank],
7169                    )?;
7170                }
7171                let StepTpDecodeV2Ws {
7172                    fa2_gated,
7173                    tcol_gated,
7174                    ..
7175                } = &mut *ws;
7176                let mut dst = tcol_gated[rank].slice_mut(0..t * lq);
7177                engine
7178                    .stream()
7179                    .memcpy_dtod(&fa2_gated[rank].slice(0..t * lq), &mut dst)?;
7180            }
7181        }
7182        self.decode_v2_oproj_tcol(ws_index, e, o_m, t)
7183    }
7184
7185    /// MEMRA_TCOL_OPROJ stash: copy this column's per-rank `gated` rows into the o-tcol
7186    /// slabs (rank-stream ordered behind the attention kernels that produced them). The
7187    /// per-column finish choreography is skipped entirely; `decode_v2_oproj_tcol` joins
7188    /// every column afterwards.
7189    pub(crate) fn decode_v2_stash_gated(
7190        &self,
7191        ws: &mut StepTpDecodeV2Ws,
7192        e: &Engine,
7193        col: usize,
7194    ) -> Result<(), Box<dyn std::error::Error>> {
7195        let ranks = self.ranks.len();
7196        // 32, not 8: the slabs below have been 32 rows since the slab-width fix, and the walk now
7197        // runs chunks up to t=32 (the w=16 arm died here on a guard three widths staler than its
7198        // own allocation, 2026-08-27).
7199        if col >= 32 {
7200            return Err("decode_v2_stash_gated column out of range".into());
7201        }
7202        let lq = ws.local_q_dim;
7203        if ws.tcol_ocap == 0 || ws.tcol_gated.len() != ranks {
7204            ws.tcol_gated.clear();
7205            ws.tcol_opart.clear();
7206            for engine in &self.ranks {
7207                let _m = engine.gpu.enter_main()?;
7208                ws.tcol_gated.push(engine.uninit(32 * lq)?);
7209                ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
7210            }
7211            let root = &self.ranks[0];
7212            let _m = root.gpu.enter_main()?;
7213            ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
7214            ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
7215            ws.tcol_ocap = 32;
7216        }
7217        for rank in 0..ranks {
7218            let engine = &self.ranks[rank];
7219            let _main = engine.gpu.enter_main()?;
7220            let mut dst = ws.tcol_gated[rank].slice_mut(col * lq..(col + 1) * lq);
7221            engine
7222                .stream()
7223                .memcpy_dtod(&ws.gated[rank].slice(0..lq), &mut dst)?;
7224            // The skipped finish's e-wait was ALSO the anti-dependency guard: it ordered
7225            // e's NEXT column's h/pos re-staging behind this column's rank-side raw pulls.
7226            // Record each rank here and make e wait — same protection, no o_proj work.
7227            ws.ev_rank[rank].record(&engine.stream())?;
7228        }
7229        {
7230            let _main = e.gpu.enter_main()?;
7231            for ev in ws.ev_rank.iter() {
7232                e.stream().wait(ev)?;
7233            }
7234        }
7235        Ok(())
7236    }
7237
7238    /// MEMRA_TCOL_OPROJ join: one weight-amortized b4_tcol per rank over the stashed
7239    /// `gated` slabs (per-column FP order == the t=1 b4 kernel), one peer pull of rank1's
7240    /// partial slab, one elementwise slab add on the root (independent elements — each
7241    /// column's add is the exact direct-join `add(p0, p1)`), then the joined `mixed` slab
7242    /// lands on `e`. Returns [t, o_out] on the model engine.
7243    pub(crate) fn decode_v2_oproj_tcol(
7244        &self,
7245        ws_index: usize,
7246        e: &Engine,
7247        o_m: &ResidentStepBf16RowParallel,
7248        t: usize,
7249    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7250        let ranks = self.ranks.len();
7251        let mut guard = self
7252            .decode_v2
7253            .lock()
7254            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
7255        let ws = guard
7256            .get_mut(ws_index)
7257            .ok_or("step TP decode v2 workspace index out of range")?;
7258        if ranks != 2 || ws.blocks_per_rank != 4 || t == 0 || t > 32 || ws.tcol_ocap < t {
7259            return Err("decode_v2_oproj_tcol geometry".into());
7260        }
7261        for rank in 0..ranks {
7262            let engine = &self.ranks[rank];
7263            let _main = engine.gpu.enter_main()?;
7264            let mut weights = Vec::with_capacity(4);
7265            for block in 0..4 {
7266                let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
7267                    return Err("tcol o_proj requires bf16-resident O blocks".into());
7268                };
7269                weights.push(weight);
7270            }
7271            {
7272                let StepTpDecodeV2Ws {
7273                    tcol_gated,
7274                    tcol_opart,
7275                    local_q_dim,
7276                    o_block_cols,
7277                    o_out,
7278                    w8t_oaq,
7279                    w8t_oad,
7280                    w8t_oin,
7281                    w8t_cap,
7282                    ..
7283                } = &mut *ws;
7284                // MEMRA_TCOL_OPROJ_REF=1 (bisect): fill the partial slab via the t=1 b4
7285                // kernel per column — separates choreography bugs from tcol-kernel bugs.
7286                static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7287                let refk = *REFK
7288                    .get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ_REF").as_deref() == Ok("1"));
7289                if refk {
7290                    let lq = *local_q_dim;
7291                    let mut xr = engine.uninit(lq)?;
7292                    let mut yr = engine.uninit(*o_out)?;
7293                    for c in 0..t {
7294                        {
7295                            let mut dst = xr.slice_mut(0..lq);
7296                            engine.stream().memcpy_dtod(
7297                                &tcol_gated[rank].slice(c * lq..(c + 1) * lq),
7298                                &mut dst,
7299                            )?;
7300                        }
7301                        engine.matvec_bf16_b4_into(
7302                            [weights[0], weights[1], weights[2], weights[3]],
7303                            &xr,
7304                            &mut yr,
7305                            *o_block_cols,
7306                            *o_out,
7307                        )?;
7308                        let mut dst = tcol_opart[rank].slice_mut(c * *o_out..(c + 1) * *o_out);
7309                        engine
7310                            .stream()
7311                            .memcpy_dtod(&yr.slice(0..*o_out), &mut dst)?;
7312                    }
7313                } else if crate::step_tp_w8_on()
7314                    && (0..4).all(|b| o_m.ranks[rank][b].q8.is_some())
7315                    && (4 * *o_block_cols) % 32 == 0
7316                {
7317                    // The verify walk's biggest single kernel: bf16 tcol o_proj was 24.8% of
7318                    // spec GPU time. Same planar q8_0 mirrors the decode arm uses, one launch
7319                    // over all t columns.
7320                    let in_f = 4 * *o_block_cols;
7321                    if *w8t_oin != in_f || *w8t_cap < t || w8t_oaq.len() != ranks {
7322                        w8t_oaq.clear();
7323                        w8t_oad.clear();
7324                        for e_rank in &self.ranks {
7325                            let _m = e_rank.gpu.enter_main()?;
7326                            w8t_oaq.push(e_rank.alloc_i8_uninit(32 * in_f)?);
7327                            w8t_oad.push(e_rank.alloc_uninit::<f32>(32 * (in_f / 32))?);
7328                        }
7329                        *w8t_oin = in_f;
7330                        *w8t_cap = (*w8t_cap).max(32);
7331                    }
7332                    engine.quantize_q8_1_into(
7333                        &tcol_gated[rank],
7334                        t,
7335                        in_f,
7336                        &mut w8t_oaq[rank],
7337                        &mut w8t_oad[rank],
7338                    )?;
7339                    engine.qmatvec_q8_0_b4_rp_t_into(
7340                        [
7341                            o_m.ranks[rank][0].q8.as_ref().unwrap(),
7342                            o_m.ranks[rank][1].q8.as_ref().unwrap(),
7343                            o_m.ranks[rank][2].q8.as_ref().unwrap(),
7344                            o_m.ranks[rank][3].q8.as_ref().unwrap(),
7345                        ],
7346                        &w8t_oaq[rank],
7347                        &w8t_oad[rank],
7348                        &mut tcol_opart[rank],
7349                        *o_block_cols,
7350                        *o_out,
7351                        t,
7352                    )?;
7353                } else {
7354                    engine.matvec_bf16_b4_tcol_into(
7355                        [weights[0], weights[1], weights[2], weights[3]],
7356                        &tcol_gated[rank],
7357                        &mut tcol_opart[rank],
7358                        *o_block_cols,
7359                        *o_out,
7360                        t,
7361                    )?;
7362                }
7363            }
7364            if rank != 0 {
7365                ws.ev_rank[rank].record(&engine.stream())?;
7366            }
7367        }
7368        let root = &self.ranks[0];
7369        {
7370            let _main = root.gpu.enter_main()?;
7371            for ev in ws.ev_rank.iter().skip(1) {
7372                root.stream().wait(ev)?;
7373            }
7374            {
7375                let StepTpDecodeV2Ws {
7376                    tcol_opart,
7377                    tcol_opeer,
7378                    tcol_omix,
7379                    o_out,
7380                    ..
7381                } = &mut *ws;
7382                let opeer = tcol_opeer.as_mut().ok_or("tcol o_proj slabs not armed")?;
7383                let omix = tcol_omix.as_mut().ok_or("tcol o_proj slabs not armed")?;
7384                {
7385                    let mut dst = opeer.slice_mut(0..t * *o_out);
7386                    root.stream()
7387                        .memcpy_dtod(&tcol_opart[1].slice(0..t * *o_out), &mut dst)?;
7388                }
7389                // Elementwise over the whole slab: per element identical to the per-column
7390                // direct-join add (independent lanes, same operand values).
7391                root.add(&tcol_opart[0], opeer, omix, t * *o_out)?;
7392            }
7393            ws.ev_oproj.record(&root.stream())?;
7394        }
7395        let _main = e.gpu.enter_main()?;
7396        e.stream().wait(&ws.ev_oproj)?;
7397        let mut out = e.uninit(t * ws.o_out)?;
7398        let omix = ws.tcol_omix.as_ref().ok_or("tcol o_proj slabs not armed")?;
7399        e.stream().memcpy_dtod(
7400            &omix.slice(0..t * ws.o_out),
7401            &mut out.slice_mut(0..t * ws.o_out),
7402        )?;
7403        Ok(out)
7404    }
7405
7406    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
7407    pub(crate) fn decode_v2_input_qkv(
7408        &self,
7409        ws: &mut StepTpDecodeV2Ws,
7410        e: &Engine,
7411        h: &CudaSlice<f32>,
7412        pos_d: &CudaSlice<i32>,
7413        gate_raw: Option<&CudaSlice<f32>>,
7414        gate_shards: Option<StepTpGateShards<'_>>,
7415        decode_input: &mut ResidentReplicatedDeviceRows,
7416        q_m: &ResidentBf16ColumnParallel,
7417        k_m: &ResidentBf16ColumnParallel,
7418        v_m: &ResidentBf16ColumnParallel,
7419        q_norm: &[CudaSlice<f32>],
7420        k_norm: &[CudaSlice<f32>],
7421        head_dim: usize,
7422        n_rot: usize,
7423        rope_base: f32,
7424        rope_freqs: &[Option<&CudaSlice<f32>>],
7425        rms_eps: f32,
7426        has_gate: bool,
7427        defer_norm_rope: bool,
7428        tcol_col: Option<usize>,
7429    ) -> Result<(), Box<dyn std::error::Error>> {
7430        let ranks = self.ranks.len();
7431        validate_replicated_device_rows(&self.ranks, decode_input)?;
7432        let gate_sources = usize::from(gate_raw.is_some()) + usize::from(gate_shards.is_some());
7433        if decode_input.tokens != 1
7434            || decode_input.width != q_m.in_features
7435            || pos_d.len() != 1
7436            || gate_raw.is_some_and(|gate| gate.len() != ws.heads)
7437            || (has_gate && gate_sources != 1)
7438            || (!has_gate && gate_sources != 0)
7439            || gate_shards.as_ref().is_some_and(|shards| match shards {
7440                StepTpGateShards::F32(shards) => shards.len() != ranks,
7441                StepTpGateShards::Bf16(shards) => shards.len() != ranks,
7442            })
7443            || q_norm.len() != ranks
7444            || k_norm.len() != ranks
7445            || rope_freqs.len() != ranks
7446            || e.ctx().ordinal() != ws.e_device
7447        {
7448            return Err("step TP decode v2 input geometry mismatch".into());
7449        }
7450
7451        let qkv_fused = step_tp_qkv_fused_enabled()?;
7452        if gate_shards.is_some() && !qkv_fused {
7453            return Err("step TP decode v2 gate shards require MEMRA_STEP_TP_QKV_FUSED=1".into());
7454        }
7455        let values = decode_input.width;
7456        if h.len() != values {
7457            return Err(format!(
7458                "step TP decode v2 hidden width {} != replicated width {values}",
7459                h.len()
7460            )
7461            .into());
7462        }
7463
7464        if qkv_fused {
7465            // STAGE-BASED flow (graph increment A): h and pos land in fixed e-context stages
7466            // (one e-stream copy each), the entry event covers them, and every rank raw-copies
7467            // from the stages on its own stream — exactly the shape graph capture wraps.
7468            if ws.h_stage.is_none() {
7469                use cudarc::driver::DevicePtr;
7470                let _main = e.gpu.enter_main()?;
7471                let h_stage = e.uninit(values)?;
7472                let pos_stage = e.htod_i32(&[0])?;
7473                {
7474                    let stream = e.stream();
7475                    let (hp, _g0) = h_stage.device_ptr(&stream);
7476                    let (pp, _g1) = pos_stage.device_ptr(&stream);
7477                    ws.raw_h_stage = hp;
7478                    ws.raw_pos_stage = pp;
7479                }
7480                ws.h_stage = Some(h_stage);
7481                ws.pos_stage = Some(pos_stage);
7482                for rank in 0..ranks {
7483                    use cudarc::driver::DevicePtr;
7484                    let engine = &self.ranks[rank];
7485                    let _rmain = engine.gpu.enter_main()?;
7486                    let attn_in = engine.uninit(values)?;
7487                    let (dp, pp) = {
7488                        let stream = engine.stream();
7489                        let (dp, _g2) = attn_in.device_ptr(&stream);
7490                        let (pp, _g3) = ws.pos[rank].device_ptr(&stream);
7491                        (dp, pp)
7492                    };
7493                    ws.raw_attn_in.push(dp);
7494                    ws.raw_pos.push(pp);
7495                    ws.attn_in.push(attn_in);
7496                }
7497                {
7498                    use cudarc::driver::DevicePtr;
7499                    let root = &self.ranks[0];
7500                    let _rmain = root.gpu.enter_main()?;
7501                    let stream = root.stream();
7502                    let (a, _g) = ws.peer_partial.device_ptr(&stream);
7503                    let (b, _g) = ws.k_shadow.device_ptr(&stream);
7504                    let (c, _g) = ws.v_shadow.device_ptr(&stream);
7505                    ws.raw_peer_partial = a;
7506                    ws.raw_k_shadow = b;
7507                    ws.raw_v_shadow = c;
7508                }
7509                {
7510                    use cudarc::driver::DevicePtr;
7511                    let rank1 = &self.ranks[1];
7512                    let _rmain = rank1.gpu.enter_main()?;
7513                    let stream = rank1.stream();
7514                    let (a, _g) = ws.o_partials[1][0].device_ptr(&stream);
7515                    let (b, _g) = ws.k[1].device_ptr(&stream);
7516                    let (c, _g) = ws.v_raw[1].device_ptr(&stream);
7517                    ws.raw_o_partial1 = a;
7518                    ws.raw_k1 = b;
7519                    ws.raw_v1 = c;
7520                }
7521            }
7522            {
7523                let _main = e.gpu.enter_main()?;
7524                {
7525                    // (Always staged: a tcol column below the dcw floor falls back to the
7526                    // normal fused arm, which reads h through this stage.)
7527                    let h_stage = ws.h_stage.as_mut().expect("stage armed above");
7528                    let mut dst = h_stage.slice_mut(0..values);
7529                    e.stream().memcpy_dtod(&h.slice(0..values), &mut dst)?;
7530                }
7531                {
7532                    let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
7533                    let mut dst = pos_stage.slice_mut(0..1);
7534                    e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
7535                }
7536                ws.ev_entry.record(&e.stream())?;
7537            }
7538            for rank in 0..ranks {
7539                let engine = &self.ranks[rank];
7540                let _main = engine.gpu.enter_main()?;
7541                engine.stream().wait(&ws.ev_entry)?;
7542            }
7543        } else {
7544            // Evented replicate flow (the pre-stage shape, kept for the non-fused class).
7545            {
7546                let _main = e.gpu.enter_main()?;
7547                if let Some(gate_raw) = gate_raw {
7548                    let mut gate_dst = ws.gate_e.slice_mut(0..ws.heads);
7549                    e.stream()
7550                        .memcpy_dtod(&gate_raw.slice(0..ws.heads), &mut gate_dst)?;
7551                }
7552                ws.ev_entry.record(&e.stream())?;
7553            }
7554            {
7555                let root = &self.ranks[0];
7556                let _main = root.gpu.enter_main()?;
7557                root.stream().wait(&ws.ev_entry)?;
7558                let mut destination = decode_input.ranks[0].slice_mut(0..values);
7559                root.stream()
7560                    .memcpy_dtod(&h.slice(0..values), &mut destination)?;
7561                ws.ev_refresh.record(&root.stream())?;
7562            }
7563            for rank in 1..ranks {
7564                let engine = &self.ranks[rank];
7565                let _main = engine.gpu.enter_main()?;
7566                engine.stream().wait(&ws.ev_refresh)?;
7567                let (root_rows, peer_rows) = decode_input.ranks.split_at_mut(rank);
7568                let mut destination = peer_rows[0].slice_mut(0..values);
7569                engine
7570                    .stream()
7571                    .memcpy_dtod(&root_rows[0].slice(0..values), &mut destination)?;
7572            }
7573        }
7574        for rank in 0..ranks {
7575            self.decode_v2_input_qkv_rank(
7576                ws,
7577                pos_d,
7578                decode_input,
7579                q_m,
7580                k_m,
7581                v_m,
7582                q_norm,
7583                k_norm,
7584                head_dim,
7585                n_rot,
7586                rope_base,
7587                rope_freqs,
7588                rms_eps,
7589                gate_shards.as_ref(),
7590                has_gate,
7591                qkv_fused,
7592                defer_norm_rope,
7593                rank,
7594                tcol_col,
7595            )?;
7596        }
7597        Ok(())
7598    }
7599
7600    /// One rank's slice of `decode_v2_input_qkv` (projection, norm+rope, gate staging) — the
7601    /// per-device issue unit the whole-token graph captures on that rank's stream.
7602    #[allow(clippy::too_many_arguments)]
7603    pub(crate) fn decode_v2_input_qkv_rank(
7604        &self,
7605        ws: &mut StepTpDecodeV2Ws,
7606        pos_d: &CudaSlice<i32>,
7607        decode_input: &mut ResidentReplicatedDeviceRows,
7608        q_m: &ResidentBf16ColumnParallel,
7609        k_m: &ResidentBf16ColumnParallel,
7610        v_m: &ResidentBf16ColumnParallel,
7611        q_norm: &[CudaSlice<f32>],
7612        k_norm: &[CudaSlice<f32>],
7613        head_dim: usize,
7614        n_rot: usize,
7615        rope_base: f32,
7616        rope_freqs: &[Option<&CudaSlice<f32>>],
7617        rms_eps: f32,
7618        gate_shards: Option<&StepTpGateShards<'_>>,
7619        has_gate: bool,
7620        qkv_fused: bool,
7621        defer_norm_rope: bool,
7622        rank: usize,
7623        tcol_col: Option<usize>,
7624    ) -> Result<(), Box<dyn std::error::Error>> {
7625        let ranks = self.ranks.len();
7626        let local_heads = ws.local_q_dim / head_dim;
7627        let local_kv_heads = ws.local_kv_dim / head_dim;
7628        let engine = &self.ranks[rank];
7629        let _main = engine.gpu.enter_main()?;
7630        let ws_e_device = ws.e_device;
7631        // T-COLUMN SELECT (spec verify): the projections for this column were precomputed
7632        // by the weight-amortized tcol kernel — copy the column into the single-row buffers
7633        // (pure f32 moves, bit-exact) and skip the per-column matvec. Rope/norm/append run
7634        // below exactly as in the t=1 program.
7635        if qkv_fused && tcol_col.is_some() {
7636            #[allow(clippy::unnecessary_unwrap)]
7637            // allow: the Some-guard sits in a multi-clause regime gate; if-let would reshape the arm structure
7638            let c = tcol_col.expect("checked");
7639            if ws.tcol_cap == 0 || ws.tcol_q.len() != ranks {
7640                return Err("tcol select without precompute".into());
7641            }
7642            // The select skips the matvec but NOT the position: rope/append below still
7643            // read this rank's pos buffer, which only the (skipped) stage path fills for
7644            // peer-device ranks. Stage it here or rank1 ropes at the previous position.
7645            if engine.ctx().ordinal() != ws_e_device {
7646                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
7647            }
7648            let StepTpDecodeV2Ws {
7649                tcol_q,
7650                tcol_k,
7651                tcol_v,
7652                tcol_g,
7653                q_raw,
7654                k_raw,
7655                v_raw,
7656                gate,
7657                local_q_dim,
7658                local_kv_dim,
7659                heads,
7660                ..
7661            } = &mut *ws;
7662            let lg = *heads / ranks;
7663            let stream = engine.stream();
7664            {
7665                let mut dst = q_raw[rank].slice_mut(0..*local_q_dim);
7666                stream.memcpy_dtod(
7667                    &tcol_q[rank].slice(c * *local_q_dim..(c + 1) * *local_q_dim),
7668                    &mut dst,
7669                )?;
7670            }
7671            {
7672                let mut dst = k_raw[rank].slice_mut(0..*local_kv_dim);
7673                stream.memcpy_dtod(
7674                    &tcol_k[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
7675                    &mut dst,
7676                )?;
7677            }
7678            {
7679                let mut dst = v_raw[rank].slice_mut(0..*local_kv_dim);
7680                stream.memcpy_dtod(
7681                    &tcol_v[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
7682                    &mut dst,
7683                )?;
7684            }
7685            if has_gate && lg > 0 {
7686                let mut dst = gate[rank].slice_mut(0..lg);
7687                stream.memcpy_dtod(&tcol_g[rank].slice(c * lg..(c + 1) * lg), &mut dst)?;
7688            }
7689            if !defer_norm_rope {
7690                // Below the dcw floor (or a non-defer shape) the col-select cannot apply:
7691                // fall through and recompute this column's QKV from the REAL h row — the
7692                // caller always passes it. The slab copies above are dead stores.
7693            } else {
7694                return Ok(());
7695            }
7696        }
7697        if qkv_fused {
7698            // Stage-based input: raw copies from the fixed e-context stages (capture-safe;
7699            // eager ordering comes from the caller's ev_entry wait on this stream). The rank
7700            // SHARING e's device reads the stages directly — same context (probed), ordering
7701            // identical (ev_entry / graph edge), bytes identical: the copies are pure waste.
7702            let same_dev = engine.ctx().ordinal() == ws.e_device;
7703            if !same_dev {
7704                raw_copy_bytes(
7705                    ws.raw_attn_in[rank],
7706                    ws.raw_h_stage,
7707                    q_m.in_features * 4,
7708                    engine,
7709                )?;
7710                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
7711            }
7712            let StepTpDecodeV2Ws {
7713                q_raw,
7714                k_raw,
7715                v_raw,
7716                gate,
7717                gate_e,
7718                attn_in,
7719                h_stage,
7720                heads,
7721                local_q_dim,
7722                local_kv_dim,
7723                w8_aq,
7724                w8_ad,
7725                w8_in,
7726                ..
7727            } = &mut *ws;
7728            let input_ref: &CudaSlice<f32> = if same_dev {
7729                h_stage
7730                    .as_ref()
7731                    .ok_or("step TP decode v2 stage not armed")?
7732            } else {
7733                &attn_in[rank]
7734            };
7735            match (
7736                &q_m.ranks[rank].weight,
7737                &k_m.ranks[rank].weight,
7738                &v_m.ranks[rank].weight,
7739            ) {
7740                (
7741                    ResidentBf16Weight::F32(wq),
7742                    ResidentBf16Weight::F32(wk),
7743                    ResidentBf16Weight::F32(wv),
7744                ) => {
7745                    let (wg, out_g) = match &gate_shards {
7746                        Some(StepTpGateShards::F32(shards)) => (&shards[rank], *heads / ranks),
7747                        Some(StepTpGateShards::Bf16(_)) => {
7748                            return Err("step TP decode v2 gate shard class does not \
7749                                            match the F32 projections"
7750                                .into());
7751                        }
7752                        // out_g = 0: the kernel never reads wg; any resident buffer works.
7753                        None => (&*gate_e, 0),
7754                    };
7755                    engine.matvec_f32_qkv_into(
7756                        wq,
7757                        wk,
7758                        wv,
7759                        wg,
7760                        input_ref,
7761                        &mut q_raw[rank],
7762                        &mut k_raw[rank],
7763                        &mut v_raw[rank],
7764                        &mut gate[rank],
7765                        q_m.in_features,
7766                        *local_q_dim,
7767                        *local_kv_dim,
7768                        out_g,
7769                    )?;
7770                }
7771                (
7772                    ResidentBf16Weight::Bf16(wq),
7773                    ResidentBf16Weight::Bf16(wk),
7774                    ResidentBf16Weight::Bf16(wv),
7775                ) => {
7776                    let (wg, out_g) = match &gate_shards {
7777                        Some(StepTpGateShards::Bf16(shards)) => (&shards[rank], *heads / ranks),
7778                        Some(StepTpGateShards::F32(_)) => {
7779                            return Err("step TP decode v2 gate shard class does not \
7780                                            match the bf16 projections"
7781                                .into());
7782                        }
7783                        None => (wq, 0),
7784                    };
7785                    // MEMRA_STEP_TP_W8: q8_0 weights + q8_1 activation through mmvq instead of
7786                    // the fused bf16 qkvg. NUMERIC CLASS (int8 dp4a with per-32 scales, not a
7787                    // bf16 fma chain) — argmax-gated, never a bit-tape flip. Q, K and V each
7788                    // get their own launch because the fused kernel has no q8 twin; the gate
7789                    // rows stay bf16 (32 rows, ~0.3 MB, nothing to win and one less class to
7790                    // qualify). Measured motive: 23.0 us bf16 -> 14.0 us q8 at this shape.
7791                    let in_f = q_m.in_features;
7792                    let q8_ready = crate::step_tp_w8_on()
7793                        && q_m.ranks[rank].q8.is_some()
7794                        && k_m.ranks[rank].q8.is_some()
7795                        && v_m.ranks[rank].q8.is_some();
7796                    if q8_ready {
7797                        if *w8_in != in_f || w8_aq.len() != ranks {
7798                            w8_aq.clear();
7799                            w8_ad.clear();
7800                            for e_rank in &self.ranks {
7801                                let _m = e_rank.gpu.enter_main()?;
7802                                w8_aq.push(e_rank.alloc_uninit::<i8>(in_f)?);
7803                                w8_ad.push(e_rank.alloc_uninit::<f32>(in_f / 32)?);
7804                            }
7805                            *w8_in = in_f;
7806                        }
7807                        engine.quantize_q8_1_into(
7808                            input_ref,
7809                            1,
7810                            in_f,
7811                            &mut w8_aq[rank],
7812                            &mut w8_ad[rank],
7813                        )?;
7814                        // ONE launch over the stacked q/k/v rows. The three-call version
7815                        // measured 79.52 vs 80.72 tok/s — SLOWER than the bf16 fused kernel —
7816                        // because three launches plus the activation quantize cost more than
7817                        // the halved weight bytes save. Bit-identical to those three calls.
7818                        engine.qmatvec_q8_0_qkv_rp_into(
7819                            q_m.ranks[rank].q8.as_ref().unwrap(),
7820                            k_m.ranks[rank].q8.as_ref().unwrap(),
7821                            v_m.ranks[rank].q8.as_ref().unwrap(),
7822                            &w8_aq[rank],
7823                            &w8_ad[rank],
7824                            &mut q_raw[rank],
7825                            &mut k_raw[rank],
7826                            &mut v_raw[rank],
7827                            in_f,
7828                            *local_q_dim,
7829                            *local_kv_dim,
7830                        )?;
7831                        if out_g > 0 {
7832                            engine.matvec_bf16_into(wg, input_ref, &mut gate[rank], in_f, out_g)?;
7833                        }
7834                    } else {
7835                        engine.matvec_bf16_qkvg_into(
7836                            wq,
7837                            wk,
7838                            wv,
7839                            wg,
7840                            input_ref,
7841                            &mut q_raw[rank],
7842                            &mut k_raw[rank],
7843                            &mut v_raw[rank],
7844                            &mut gate[rank],
7845                            q_m.in_features,
7846                            *local_q_dim,
7847                            *local_kv_dim,
7848                            out_g,
7849                        )?;
7850                    }
7851                }
7852                _ => {
7853                    return Err("step TP decode v2 QKV projections mix residency classes".into());
7854                }
7855            }
7856        } else {
7857            for (matrix, local_out, raw) in [
7858                (q_m, ws.local_q_dim, &mut ws.q_raw),
7859                (k_m, ws.local_kv_dim, &mut ws.k_raw),
7860                (v_m, ws.local_kv_dim, &mut ws.v_raw),
7861            ] {
7862                let ResidentBf16Weight::F32(values_w) = &matrix.ranks[rank].weight else {
7863                    return Err("step TP decode v2 lost its F32 projection residency".into());
7864                };
7865                let chunk_rows = matrix.canonical_chunk_rows.unwrap_or(local_out);
7866                engine.linear_f32_resident_canonical_rows_t1_into(
7867                    &decode_input.ranks[rank],
7868                    values_w,
7869                    &mut raw[rank],
7870                    matrix.in_features,
7871                    local_out,
7872                    chunk_rows,
7873                )?;
7874            }
7875        }
7876        if qkv_fused && defer_norm_rope {
7877            // FUSION #1 defers norm+rope to the caller's fused rope+append+inc launch.
7878        } else if qkv_fused {
7879            // Fused norm+rope: one launch; the position comes from the rank-local staged
7880            // copy (raw-copied above from the fixed e-context pos stage — capture-safe).
7881            let StepTpDecodeV2Ws {
7882                q_raw,
7883                k_raw,
7884                q,
7885                k,
7886                pos,
7887                pos_stage,
7888                ..
7889            } = &mut *ws;
7890            let same_dev = engine.ctx().ordinal() == ws_e_device;
7891            let pos_ref: &CudaSlice<i32> = if same_dev {
7892                pos_stage
7893                    .as_ref()
7894                    .ok_or("step TP decode v2 pos stage not armed")?
7895            } else {
7896                &pos[rank]
7897            };
7898            engine.qk_norm_rope_into(
7899                &q_raw[rank],
7900                &k_raw[rank],
7901                &q_norm[rank],
7902                &k_norm[rank],
7903                &mut q[rank],
7904                &mut k[rank],
7905                pos_ref,
7906                head_dim,
7907                n_rot,
7908                local_heads,
7909                local_kv_heads,
7910                rms_eps,
7911                rope_base,
7912                1.0,
7913                rope_freqs[rank],
7914            )?;
7915        } else {
7916            engine.rms_norm(
7917                &ws.q_raw[rank],
7918                &q_norm[rank],
7919                &mut ws.q[rank],
7920                head_dim,
7921                local_heads,
7922                rms_eps,
7923            )?;
7924            engine.rms_norm(
7925                &ws.k_raw[rank],
7926                &k_norm[rank],
7927                &mut ws.k[rank],
7928                head_dim,
7929                local_kv_heads,
7930                rms_eps,
7931            )?;
7932            {
7933                let mut pos_dst = ws.pos[rank].slice_mut(0..1);
7934                engine
7935                    .stream()
7936                    .memcpy_dtod(&pos_d.slice(0..1), &mut pos_dst)?;
7937            }
7938            engine.rope_neox2(
7939                &mut ws.q[rank],
7940                &mut ws.k[rank],
7941                &ws.pos[rank],
7942                head_dim,
7943                n_rot,
7944                local_heads,
7945                local_kv_heads,
7946                1,
7947                rope_base,
7948                1.0,
7949                rope_freqs[rank],
7950            )?;
7951        }
7952        if has_gate && gate_shards.is_none() {
7953            let gate_start = rank * (ws.heads / ranks);
7954            let mut gate_dst = ws.gate[rank].slice_mut(0..ws.heads / ranks);
7955            engine.stream().memcpy_dtod(
7956                &ws.gate_e.slice(gate_start..gate_start + ws.heads / ranks),
7957                &mut gate_dst,
7958            )?;
7959        }
7960        Ok(())
7961    }
7962
7963    /// One rank's O-partial slice of `decode_v2_finish` — the per-device issue unit the
7964    /// whole-token graph captures on that rank's stream (the rank-done event stays with the
7965    /// eager caller; graphs order via parent edges instead).
7966    pub(crate) fn decode_v2_finish_rank_partial(
7967        &self,
7968        ws: &mut StepTpDecodeV2Ws,
7969        o_m: &ResidentStepBf16RowParallel,
7970        o_fused: bool,
7971        rank: usize,
7972    ) -> Result<(), Box<dyn std::error::Error>> {
7973        let engine = &self.ranks[rank];
7974        let _main = engine.gpu.enter_main()?;
7975        if o_fused {
7976            let StepTpDecodeV2Ws {
7977                gated,
7978                o_partials,
7979                o_block_cols,
7980                o_out,
7981                w8o_aq,
7982                w8o_ad,
7983                w8o_in,
7984                ..
7985            } = &mut *ws;
7986            let all_f32 = o_m.ranks[rank]
7987                .iter()
7988                .all(|block| matches!(block.weight, ResidentBf16Weight::F32(_)));
7989            if all_f32 {
7990                let mut weights = Vec::with_capacity(4);
7991                for block in 0..4 {
7992                    let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
7993                        unreachable!("all_f32 checked above");
7994                    };
7995                    weights.push(weight);
7996                }
7997                engine.matvec_f32_b4_into(
7998                    [weights[0], weights[1], weights[2], weights[3]],
7999                    &gated[rank],
8000                    &mut o_partials[rank][0],
8001                    *o_block_cols,
8002                    *o_out,
8003                )?;
8004            } else if crate::step_tp_w8_on() && (0..4).all(|b| o_m.ranks[rank][b].q8.is_some()) {
8005                // MEMRA_STEP_TP_W8, o_proj half: quantize the gated attention output once and
8006                // run all four HEAD_SPLIT blocks in one q8 launch. Measured motive: bf16 b4 is
8007                // 24.2 us/layer against 11.7 for the q8 shape — the largest decode line left
8008                // after the QKV arm banked +2.9%.
8009                let in_f = 4 * *o_block_cols;
8010                if *w8o_in != in_f || w8o_aq.len() != self.ranks.len() {
8011                    w8o_aq.clear();
8012                    w8o_ad.clear();
8013                    for e_rank in &self.ranks {
8014                        let _m = e_rank.gpu.enter_main()?;
8015                        w8o_aq.push(e_rank.alloc_uninit::<i8>(in_f)?);
8016                        w8o_ad.push(e_rank.alloc_uninit::<f32>(in_f / 32)?);
8017                    }
8018                    *w8o_in = in_f;
8019                }
8020                engine.quantize_q8_1_into(
8021                    &gated[rank],
8022                    1,
8023                    in_f,
8024                    &mut w8o_aq[rank],
8025                    &mut w8o_ad[rank],
8026                )?;
8027                engine.qmatvec_q8_0_b4_rp_into(
8028                    [
8029                        o_m.ranks[rank][0].q8.as_ref().unwrap(),
8030                        o_m.ranks[rank][1].q8.as_ref().unwrap(),
8031                        o_m.ranks[rank][2].q8.as_ref().unwrap(),
8032                        o_m.ranks[rank][3].q8.as_ref().unwrap(),
8033                    ],
8034                    &w8o_aq[rank],
8035                    &w8o_ad[rank],
8036                    &mut o_partials[rank][0],
8037                    *o_block_cols,
8038                    *o_out,
8039                )?;
8040            } else {
8041                let mut weights = Vec::with_capacity(4);
8042                for block in 0..4 {
8043                    let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
8044                        return Err("step TP decode v2 O projections mix residency classes".into());
8045                    };
8046                    weights.push(weight);
8047                }
8048                engine.matvec_bf16_b4_into(
8049                    [weights[0], weights[1], weights[2], weights[3]],
8050                    &gated[rank],
8051                    &mut o_partials[rank][0],
8052                    *o_block_cols,
8053                    *o_out,
8054                )?;
8055            }
8056        } else {
8057            for block in 0..ws.blocks_per_rank {
8058                let x =
8059                    ws.gated[rank].slice(block * ws.o_block_cols..(block + 1) * ws.o_block_cols);
8060                let mut y = ws.o_partials[rank][block].slice_mut(0..ws.o_out);
8061                match &o_m.ranks[rank][block].weight {
8062                    ResidentBf16Weight::F32(weight) => {
8063                        let w = weight.slice(0..weight.len());
8064                        engine.linear_t1_into(&x, &w, &mut y, ws.o_block_cols, ws.o_out)?;
8065                    }
8066                    ResidentBf16Weight::Bf16(weight) => {
8067                        engine.matvec_bf16_views_into(
8068                            weight,
8069                            &x,
8070                            &mut y,
8071                            ws.o_block_cols,
8072                            ws.o_out,
8073                        )?;
8074                    }
8075                }
8076            }
8077        }
8078        Ok(())
8079    }
8080
8081    /// v2 phase 2: canonical-block O reduction on the root device plus the K/V shadow gathers,
8082    /// returning a fresh model-engine output ordered behind `ev_oproj` on `e`'s stream.
8083    ///
8084    /// The caller must have queued every rank's attention work (reading `ws.gated`, `ws.k`,
8085    /// `ws.v_raw`) on the rank streams before this call. Reduction order is identical to
8086    /// `step_bf16_row_parallel_resident_native`: zeros, then rank 0's blocks, then each peer
8087    /// rank's blocks, one `add` per block.
8088    pub(crate) fn decode_v2_finish(
8089        &self,
8090        ws: &mut StepTpDecodeV2Ws,
8091        e: &Engine,
8092        o_m: &ResidentStepBf16RowParallel,
8093    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8094        let ranks = self.ranks.len();
8095        if e.ctx().ordinal() != ws.e_device {
8096            return Err("step TP decode v2 finish engine changed".into());
8097        }
8098        // MEMRA_STEP_TP_QKV_FUSED extends to the O path: one matvec_f32_b4 launch per rank
8099        // (in-order canonical block accumulation per element) and a single peer-copy + add on
8100        // the root, replacing 4 cuBLASLt launches per rank + the 4-copy/8-add chain. Same
8101        // numeric-class door and gate as the fused QKV projection.
8102        let o_fused = step_tp_qkv_fused_enabled()? && ws.blocks_per_rank == 4 && ranks == 2;
8103
8104        // Per-rank O block partials on the owning rank's stream (serial after the attention
8105        // kernels the driver queued there), then the rank-done event for root's peer reads.
8106        for rank in 0..ranks {
8107            self.decode_v2_finish_rank_partial(ws, o_m, o_fused, rank)?;
8108            if rank == 0 {
8109                // root == rank0: its own stream order covers the partial; only peers need
8110                // the record/wait pair (host-op diet, matches the routes-arm skip).
8111                continue;
8112            }
8113            let engine = &self.ranks[rank];
8114            let _main = engine.gpu.enter_main()?;
8115            ws.ev_rank[rank].record(&engine.stream())?;
8116        }
8117
8118        // Root reduce in canonical order + shadow gathers, all on the root stream.
8119        let root = &self.ranks[0];
8120        #[allow(unused_assignments)]
8121        let mut final_in_a = false;
8122        {
8123            let _main = root.gpu.enter_main()?;
8124            for ev in ws.ev_rank.iter().skip(1) {
8125                root.stream().wait(ev)?;
8126            }
8127            if o_fused && oproj_direct_on() && ranks == 2 && no_local_shadow_on() {
8128                // DIRECT JOIN: rank1's partial already sits in root memory (P2P kernel
8129                // stores; visibility guaranteed by the ev_rank[1] wait above), rank0's
8130                // partial is root-stream-ordered — record ONE event and let the model
8131                // engine do the single add itself, straight into its own output row.
8132                // Same operands, same add order as finish_root_fused: BIT-IDENTICAL.
8133                ws.ev_oproj.record(&root.stream())?;
8134                let _main = e.gpu.enter_main()?;
8135                e.stream().wait(&ws.ev_oproj)?;
8136                let mut output = e.uninit(ws.o_out)?;
8137                if oproj_tail_on() && oproj_tail_eligible() {
8138                    // M2: defer the add into the residual+norm consumer (waits stay HERE;
8139                    // only the arithmetic moves). `output` is returned unwritten.
8140                    use cudarc::driver::DevicePtr;
8141                    let stream = e.stream();
8142                    let (p0, _g0) = ws.o_partials[0][0].device_ptr(&stream);
8143                    let (p1, _g1) = ws.o_partials[1][0].device_ptr(&stream);
8144                    set_oproj_tail((p0, p1));
8145                    return Ok(output);
8146                }
8147                e.add(
8148                    &ws.o_partials[0][0],
8149                    &ws.o_partials[1][0],
8150                    &mut output,
8151                    ws.o_out,
8152                )?;
8153                return Ok(output);
8154            }
8155            if o_fused {
8156                self.decode_v2_finish_root_fused(ws)?;
8157                ws.ev_oproj.record(&root.stream())?;
8158                let _main = e.gpu.enter_main()?;
8159                e.stream().wait(&ws.ev_oproj)?;
8160                let mut output = e.uninit(ws.o_out)?;
8161                e.stream().memcpy_dtod(
8162                    &ws.reduce_a.slice(0..ws.o_out),
8163                    &mut output.slice_mut(0..ws.o_out),
8164                )?;
8165                return Ok(output);
8166            }
8167            let mut first = true;
8168            let mut current_is_a = false;
8169            for rank in 0..ranks {
8170                for block in 0..ws.blocks_per_rank {
8171                    let use_peer = rank != 0;
8172                    if use_peer {
8173                        raw_copy_bytes(
8174                            ws.raw_peer_partial,
8175                            ws.raw_o_partials[rank][block],
8176                            ws.o_out * std::mem::size_of::<f32>(),
8177                            root,
8178                        )?;
8179                    }
8180                    // add(prev, partial) -> the other reduce buffer, exactly one add per block
8181                    match (first, current_is_a, use_peer) {
8182                        (true, _, true) => {
8183                            root.add(&ws.zeros, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
8184                        }
8185                        (true, _, false) => root.add(
8186                            &ws.zeros,
8187                            &ws.o_partials[0][block],
8188                            &mut ws.reduce_a,
8189                            ws.o_out,
8190                        )?,
8191                        (false, true, true) => {
8192                            root.add(&ws.reduce_a, &ws.peer_partial, &mut ws.reduce_b, ws.o_out)?
8193                        }
8194                        (false, true, false) => root.add(
8195                            &ws.reduce_a,
8196                            &ws.o_partials[0][block],
8197                            &mut ws.reduce_b,
8198                            ws.o_out,
8199                        )?,
8200                        (false, false, true) => {
8201                            root.add(&ws.reduce_b, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
8202                        }
8203                        (false, false, false) => root.add(
8204                            &ws.reduce_b,
8205                            &ws.o_partials[0][block],
8206                            &mut ws.reduce_a,
8207                            ws.o_out,
8208                        )?,
8209                    }
8210                    current_is_a = first || !current_is_a;
8211                    first = false;
8212                }
8213            }
8214            final_in_a = current_is_a;
8215
8216            if !no_local_shadow_on() {
8217                let bytes = ws.local_kv_dim * std::mem::size_of::<f32>();
8218                for rank in 0..ranks {
8219                    let offset = rank * bytes;
8220                    raw_copy_bytes(ws.raw_k_shadow + offset as u64, ws.raw_k[rank], bytes, root)?;
8221                    raw_copy_bytes(
8222                        ws.raw_v_shadow + offset as u64,
8223                        ws.raw_v_raw[rank],
8224                        bytes,
8225                        root,
8226                    )?;
8227                }
8228            }
8229            ws.ev_oproj.record(&root.stream())?;
8230        }
8231
8232        // Model-engine output: e waits the root event, then copies the reduced row into a
8233        // fresh e-context buffer (same ownership contract as v1's `e.htod`). The same wait
8234        // orders the driver's shadow append (it reads ws.k_shadow/ws.v_shadow on e's stream).
8235        let _main = e.gpu.enter_main()?;
8236        e.stream().wait(&ws.ev_oproj)?;
8237        let mut output = e.uninit(ws.o_out)?;
8238        let source = if final_in_a {
8239            &ws.reduce_a
8240        } else {
8241            &ws.reduce_b
8242        };
8243        e.stream().memcpy_dtod(
8244            &source.slice(0..ws.o_out),
8245            &mut output.slice_mut(0..ws.o_out),
8246        )?;
8247        Ok(output)
8248    }
8249
8250    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
8251    pub fn run_routed_experts(
8252        &self,
8253        experts: &ResidentExpertParallel,
8254        input: &[f32],
8255        tokens: usize,
8256        selected: &[usize],
8257        route_weights: &[f32],
8258        experts_per_token: usize,
8259        activation_limit: Option<f32>,
8260    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8261        validate_step_expert_activation_limit(activation_limit)?;
8262        validate_ep_residency(&self.ranks, experts)?;
8263        validate_activations(input, tokens, experts.input_width)?;
8264        let pairs = tokens
8265            .checked_mul(experts_per_token)
8266            .ok_or("EP route count overflow")?;
8267        if selected.len() != pairs || route_weights.len() != pairs {
8268            return Err(format!(
8269                "EP routes selected={} weights={} != tokens {tokens} x experts/token \
8270                 {experts_per_token} ({pairs})",
8271                selected.len(),
8272                route_weights.len(),
8273            )
8274            .into());
8275        }
8276        if !route_weights.iter().all(|weight| weight.is_finite()) {
8277            return Err("EP route weights contain a non-finite value".into());
8278        }
8279        if self.native_p2p {
8280            return self.run_routed_experts_native(
8281                experts,
8282                input,
8283                tokens,
8284                selected,
8285                route_weights,
8286                experts_per_token,
8287                activation_limit,
8288            );
8289        }
8290
8291        let mut output = vec![0.0f32; tokens * experts.input_width];
8292        let per_rank = experts.expert_count / experts.ranks.len();
8293        for token in 0..tokens {
8294            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
8295            for slot in 0..experts_per_token {
8296                let pair = token * experts_per_token + slot;
8297                let expert = selected[pair];
8298                if expert >= experts.expert_count {
8299                    return Err(format!(
8300                        "EP selected expert {expert} outside 0..{}",
8301                        experts.expert_count
8302                    )
8303                    .into());
8304                }
8305                let owner = expert / per_rank;
8306                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
8307                let rank = &experts.ranks[owner];
8308                let engine = &self.ranks[owner];
8309                let gate =
8310                    run_resident_bank_expert(engine, &rank.gate, local_expert, input_row, 1)?;
8311                let up = run_resident_bank_expert(engine, &rank.up, local_expert, input_row, 1)?;
8312                let activated: Vec<f32> = gate
8313                    .iter()
8314                    .zip(&up)
8315                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
8316                    .collect();
8317                debug_assert_eq!(activated.len(), experts.expert_width);
8318                let down =
8319                    run_resident_bank_expert(engine, &rank.down, local_expert, &activated, 1)?;
8320                let weight = route_weights[pair];
8321                for (sum, value) in output
8322                    [token * experts.input_width..(token + 1) * experts.input_width]
8323                    .iter_mut()
8324                    .zip(down)
8325                {
8326                    *sum += weight * value;
8327                }
8328            }
8329        }
8330        Ok(output)
8331    }
8332
8333    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
8334    fn run_routed_experts_native(
8335        &self,
8336        experts: &ResidentExpertParallel,
8337        input: &[f32],
8338        tokens: usize,
8339        selected: &[usize],
8340        route_weights: &[f32],
8341        experts_per_token: usize,
8342        activation_limit: Option<f32>,
8343    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8344        if !self.native_p2p || self.ranks.len() < 2 {
8345            return Err("native EP execution requires at least two P2P ranks".into());
8346        }
8347        if self.ep_device_arithmetic {
8348            return self.run_routed_experts_native_device(
8349                experts,
8350                input,
8351                tokens,
8352                selected,
8353                route_weights,
8354                experts_per_token,
8355                activation_limit,
8356            );
8357        }
8358        let mut output = vec![0.0f32; tokens * experts.input_width];
8359        let per_rank = experts.expert_count / experts.ranks.len();
8360        for token in 0..tokens {
8361            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
8362            let mut rank_inputs = (0..self.ranks.len())
8363                .map(|_| None)
8364                .collect::<Vec<Option<CudaSlice<f32>>>>();
8365            rank_inputs[0] = Some({
8366                let root = &self.ranks[0];
8367                let _main = root.gpu.enter_main()?;
8368                root.htod(input_row)?
8369            });
8370
8371            for slot in 0..experts_per_token {
8372                let pair = token * experts_per_token + slot;
8373                let expert = selected[pair];
8374                if expert >= experts.expert_count {
8375                    return Err(format!(
8376                        "EP selected expert {expert} outside 0..{}",
8377                        experts.expert_count
8378                    )
8379                    .into());
8380                }
8381                let owner = expert / per_rank;
8382                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
8383                if rank_inputs[owner].is_none() {
8384                    let peer_input = {
8385                        let root_input = rank_inputs[0]
8386                            .as_ref()
8387                            .ok_or("native EP lost its root input")?;
8388                        let engine = &self.ranks[owner];
8389                        let _main = engine.gpu.enter_main()?;
8390                        let mut peer_input = engine.uninit(experts.input_width)?;
8391                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
8392                        peer_input
8393                    };
8394                    rank_inputs[owner] = Some(peer_input);
8395                }
8396
8397                let rank = &experts.ranks[owner];
8398                let engine = &self.ranks[owner];
8399                let owner_input = rank_inputs[owner]
8400                    .as_ref()
8401                    .ok_or("native EP owner input is absent after dispatch")?;
8402                let gate = run_resident_bank_expert_device(
8403                    engine,
8404                    &rank.gate,
8405                    local_expert,
8406                    owner_input,
8407                    1,
8408                )?;
8409                let up = run_resident_bank_expert_device(
8410                    engine,
8411                    &rank.up,
8412                    local_expert,
8413                    owner_input,
8414                    1,
8415                )?;
8416                let (gate, up) = {
8417                    let _main = engine.gpu.enter_main()?;
8418                    (engine.dtoh(&gate)?, engine.dtoh(&up)?)
8419                };
8420                let activated = gate
8421                    .iter()
8422                    .zip(&up)
8423                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
8424                    .collect::<Vec<_>>();
8425                debug_assert_eq!(activated.len(), experts.expert_width);
8426                let activated = {
8427                    let _main = engine.gpu.enter_main()?;
8428                    engine.htod(&activated)?
8429                };
8430                let down = run_resident_bank_expert_device(
8431                    engine,
8432                    &rank.down,
8433                    local_expert,
8434                    &activated,
8435                    1,
8436                )?;
8437                let down = if owner == 0 {
8438                    let _main = engine.gpu.enter_main()?;
8439                    engine.dtoh(&down)?
8440                } else {
8441                    let root = &self.ranks[0];
8442                    let _main = root.gpu.enter_main()?;
8443                    let mut root_down = root.uninit(experts.input_width)?;
8444                    root.stream().memcpy_dtod(&down, &mut root_down)?;
8445                    root.dtoh(&root_down)?
8446                };
8447                let weight = route_weights[pair];
8448                for (sum, value) in output
8449                    [token * experts.input_width..(token + 1) * experts.input_width]
8450                    .iter_mut()
8451                    .zip(down)
8452                {
8453                    *sum += weight * value;
8454                }
8455            }
8456        }
8457        Ok(output)
8458    }
8459
8460    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
8461    fn run_routed_experts_native_device(
8462        &self,
8463        experts: &ResidentExpertParallel,
8464        input: &[f32],
8465        tokens: usize,
8466        selected: &[usize],
8467        route_weights: &[f32],
8468        experts_per_token: usize,
8469        activation_limit: Option<f32>,
8470    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8471        if !self.native_p2p || !self.ep_device_arithmetic || self.ranks.len() < 2 {
8472            return Err(
8473                "device-resident EP arithmetic requires at least two native P2P ranks".into(),
8474            );
8475        }
8476        let mut output = Vec::with_capacity(tokens * experts.input_width);
8477        let per_rank = experts.expert_count / experts.ranks.len();
8478        let root = &self.ranks[0];
8479        for token in 0..tokens {
8480            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
8481            let mut rank_inputs = (0..self.ranks.len())
8482                .map(|_| None)
8483                .collect::<Vec<Option<CudaSlice<f32>>>>();
8484            rank_inputs[0] = Some({
8485                let _main = root.gpu.enter_main()?;
8486                root.htod(input_row)?
8487            });
8488            let mut root_output = {
8489                let _main = root.gpu.enter_main()?;
8490                root.zeros(experts.input_width)?
8491            };
8492            let mut remote_down_keepalive = Vec::new();
8493
8494            for slot in 0..experts_per_token {
8495                let pair = token * experts_per_token + slot;
8496                let expert = selected[pair];
8497                if expert >= experts.expert_count {
8498                    return Err(format!(
8499                        "EP selected expert {expert} outside 0..{}",
8500                        experts.expert_count
8501                    )
8502                    .into());
8503                }
8504                let owner = expert / per_rank;
8505                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
8506                if rank_inputs[owner].is_none() {
8507                    let peer_input = {
8508                        let root_input = rank_inputs[0]
8509                            .as_ref()
8510                            .ok_or("native EP lost its root input")?;
8511                        let engine = &self.ranks[owner];
8512                        let _main = engine.gpu.enter_main()?;
8513                        let mut peer_input = engine.uninit(experts.input_width)?;
8514                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
8515                        peer_input
8516                    };
8517                    rank_inputs[owner] = Some(peer_input);
8518                }
8519
8520                let rank = &experts.ranks[owner];
8521                let engine = &self.ranks[owner];
8522                let owner_input = rank_inputs[owner]
8523                    .as_ref()
8524                    .ok_or("native EP owner input is absent after dispatch")?;
8525                let gate = run_resident_bank_expert_device(
8526                    engine,
8527                    &rank.gate,
8528                    local_expert,
8529                    owner_input,
8530                    1,
8531                )?;
8532                let up = run_resident_bank_expert_device(
8533                    engine,
8534                    &rank.up,
8535                    local_expert,
8536                    owner_input,
8537                    1,
8538                )?;
8539                let activated = {
8540                    let _main = engine.gpu.enter_main()?;
8541                    let mut activated = engine.uninit(experts.expert_width)?;
8542                    if let Some(limit) = activation_limit {
8543                        engine.silu_clamped_mul_host_expf(
8544                            &gate,
8545                            &up,
8546                            limit,
8547                            &mut activated,
8548                            experts.expert_width,
8549                        )?;
8550                    } else {
8551                        engine.silu_mul_host_expf(
8552                            &gate,
8553                            &up,
8554                            &mut activated,
8555                            experts.expert_width,
8556                        )?;
8557                    }
8558                    activated
8559                };
8560                let down = run_resident_bank_expert_device(
8561                    engine,
8562                    &rank.down,
8563                    local_expert,
8564                    &activated,
8565                    1,
8566                )?;
8567                let root_down = if owner == 0 {
8568                    down
8569                } else {
8570                    let _main = root.gpu.enter_main()?;
8571                    let mut root_down = root.uninit(experts.input_width)?;
8572                    root.stream().memcpy_dtod(&down, &mut root_down)?;
8573                    // The peer copy runs on the root stream. Keep its remote source alive until
8574                    // the final root readback synchronizes that stream; otherwise async free can
8575                    // recycle the owner's allocation while cuMemcpyPeerAsync is still reading it.
8576                    remote_down_keepalive.push(down);
8577                    root_down
8578                };
8579                let _main = root.gpu.enter_main()?;
8580                let mut destination = root_output.slice_mut(0..experts.input_width);
8581                root.axpy_host_into(
8582                    &root_down.slice(0..root_down.len()),
8583                    route_weights[pair],
8584                    &mut destination,
8585                    experts.input_width,
8586                )?;
8587            }
8588
8589            let _main = root.gpu.enter_main()?;
8590            let root_output = root.dtoh(&root_output)?;
8591            drop(remote_down_keepalive);
8592            output.extend(root_output);
8593        }
8594        Ok(output)
8595    }
8596}
8597
8598#[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
8599fn validate_column_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
8600    if matrix.out_features % tp != 0 {
8601        return Err(format!(
8602            "column-parallel out_features {} is not divisible by TP={tp}",
8603            matrix.out_features
8604        ));
8605    }
8606    let local_out = matrix.out_features / tp;
8607    if !local_out.is_multiple_of(FP8_BLOCK) {
8608        return Err(format!(
8609            "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
8610             E4M3 scale block"
8611        ));
8612    }
8613    Ok(())
8614}
8615
8616#[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
8617fn step_bf16_canonical_chunk_rows(out_features: usize, tp: usize) -> Result<usize, String> {
8618    if !matches!(tp, 1 | 2 | 4 | 8) {
8619        return Err(format!(
8620            "Step BF16 canonical projection requires TP1/TP2/TP4/TP8, got TP={tp}"
8621        ));
8622    }
8623    if out_features == 0 || !out_features.is_multiple_of(PRODUCT_MAX_CARDS) {
8624        return Err(format!(
8625            "Step BF16 output width {out_features} is not divisible by the TP8 product envelope"
8626        ));
8627    }
8628    let canonical_rows = out_features / PRODUCT_MAX_CARDS;
8629    let local_out = out_features / tp;
8630    if local_out % canonical_rows != 0 {
8631        return Err(format!(
8632            "Step BF16 TP={tp} output shard {local_out} is not divisible by canonical \
8633             {canonical_rows}-row chunks"
8634        ));
8635    }
8636    Ok(canonical_rows)
8637}
8638
8639#[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
8640fn step_bf16_canonical_chunk_cols(in_features: usize, tp: usize) -> Result<usize, String> {
8641    if !matches!(tp, 1 | 2 | 4 | 8) {
8642        return Err(format!(
8643            "Step BF16 canonical row projection requires TP1/TP2/TP4/TP8, got TP={tp}"
8644        ));
8645    }
8646    if in_features == 0 || !in_features.is_multiple_of(PRODUCT_MAX_CARDS) {
8647        return Err(format!(
8648            "Step BF16 input width {in_features} is not divisible by the TP8 product envelope"
8649        ));
8650    }
8651    let canonical_cols = in_features / PRODUCT_MAX_CARDS;
8652    let local_in = in_features / tp;
8653    if local_in % canonical_cols != 0 {
8654        return Err(format!(
8655            "Step BF16 TP={tp} input shard {local_in} is not divisible by canonical \
8656             {canonical_cols}-column chunks"
8657        ));
8658    }
8659    Ok(canonical_cols)
8660}
8661
8662#[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
8663fn validate_row_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
8664    if matrix.in_features % tp != 0 {
8665        return Err(format!(
8666            "row-parallel in_features {} is not divisible by TP={tp}",
8667            matrix.in_features
8668        ));
8669    }
8670    let local_in = matrix.in_features / tp;
8671    if !local_in.is_multiple_of(FP8_BLOCK) {
8672        return Err(format!(
8673            "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
8674             E4M3 scale block"
8675        ));
8676    }
8677    Ok(())
8678}
8679
8680fn upload_rank(
8681    engine: &Engine,
8682    matrix: E4m3BlockMatrix<'_>,
8683) -> Result<ResidentE4m3Rank, Box<dyn std::error::Error>> {
8684    let _main = engine.gpu.enter_main()?;
8685    matrix.validate()?;
8686    Ok(ResidentE4m3Rank {
8687        codes: engine.htod_bytes(matrix.codes)?,
8688        scales: engine.htod(matrix.scales)?,
8689        out_features: matrix.out_features,
8690        in_features: matrix.in_features,
8691    })
8692}
8693
8694fn upload_bf16_rank(
8695    engine: &Engine,
8696    matrix: Bf16Matrix<'_>,
8697    f32_mirror: bool,
8698) -> Result<ResidentBf16Rank, Box<dyn std::error::Error>> {
8699    let _main = engine.gpu.enter_main()?;
8700    matrix.validate()?;
8701    let bytes = engine.htod_bytes(matrix.bytes)?;
8702    let weight = if f32_mirror {
8703        let values = matrix
8704            .out_features
8705            .checked_mul(matrix.in_features)
8706            .ok_or("resident BF16 mirror element count overflow")?;
8707        ResidentBf16Weight::F32(engine.bf16_to_f32(&bytes.slice(0..bytes.len()), values)?)
8708    } else {
8709        ResidentBf16Weight::Bf16(bytes)
8710    };
8711    // MEMRA_STEP_TP_W8: encode the q8_0 decode mirror once, here, while the bf16 bytes are
8712    // already resident. Rows whose in_features is not a multiple of 32 have no q8_0 form and
8713    // simply keep the bf16 program (the decode arm checks for the mirror, never assumes it).
8714    let q8 = if crate::step_tp_w8_on() && matrix.in_features.is_multiple_of(32) {
8715        if let ResidentBf16Weight::Bf16(bytes) = &weight {
8716            // Two steps, because the mmvq rp kernel does NOT read ggml-interleaved 34-byte
8717            // blocks: it reads a PLANAR mirror (all quants, then all half scales — the
8718            // q4_0/NVFP4 rp convention). The encoder writes the interleaved form and
8719            // `build_q8_rp4_raw` — the same kernel the GGUF loader uses — splits it into
8720            // planes. Skipping the split is what made the first W8 gate return zeros
8721            // (verify-prefill argmax=0, maxdiff=0.000e0).
8722            let row_bytes = Engine::q8_0_row_bytes(matrix.in_features);
8723            let mut interleaved = engine.alloc_u8_uninit(matrix.out_features * row_bytes)?;
8724            engine.encode_q8_0_from_bf16(
8725                bytes,
8726                &mut interleaved,
8727                matrix.in_features,
8728                matrix.out_features,
8729            )?;
8730            let mirror =
8731                engine.build_q8_rp4_raw(&interleaved, matrix.in_features, matrix.out_features)?;
8732            Some(mirror)
8733        } else {
8734            None
8735        }
8736    } else {
8737        None
8738    };
8739    Ok(ResidentBf16Rank {
8740        weight,
8741        out_features: matrix.out_features,
8742        in_features: matrix.in_features,
8743        q8,
8744    })
8745}
8746
8747fn upload_expert_bank_rank(
8748    engine: &Engine,
8749    bank: E4m3ExpertBank<'_>,
8750    expert_range: Range<usize>,
8751) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
8752    let _main = engine.gpu.enter_main()?;
8753    bank.validate()?;
8754    if expert_range.start >= expert_range.end || expert_range.end > bank.expert_count {
8755        return Err(format!(
8756            "invalid EP expert range {expert_range:?} for {} experts",
8757            bank.expert_count
8758        )
8759        .into());
8760    }
8761    let code_stride = bank.out_features * bank.in_features;
8762    let scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * bank.in_features.div_ceil(FP8_BLOCK);
8763    Ok(ResidentE4m3ExpertBankRank {
8764        codes: engine.htod_bytes(
8765            &bank.codes[expert_range.start * code_stride..expert_range.end * code_stride],
8766        )?,
8767        scales: engine.htod(
8768            &bank.scales[expert_range.start * scale_stride..expert_range.end * scale_stride],
8769        )?,
8770        expert_range,
8771        out_features: bank.out_features,
8772        in_features: bank.in_features,
8773        code_stride,
8774        scale_stride,
8775        k_blocks: None,
8776    })
8777}
8778
8779#[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
8780fn validate_column_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
8781    if bank.out_features % tp != 0 {
8782        return Err(format!(
8783            "TP expert output width {} is not divisible by TP={tp}",
8784            bank.out_features
8785        ));
8786    }
8787    let local_out = bank.out_features / tp;
8788    if !local_out.is_multiple_of(FP8_BLOCK) {
8789        return Err(format!(
8790            "TP expert output shard {local_out} cuts through a {FP8_BLOCK}-row E4M3 scale block"
8791        ));
8792    }
8793    Ok(())
8794}
8795
8796#[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
8797fn validate_row_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
8798    if bank.in_features % tp != 0 {
8799        return Err(format!(
8800            "TP expert input width {} is not divisible by TP={tp}",
8801            bank.in_features
8802        ));
8803    }
8804    let local_in = bank.in_features / tp;
8805    if !local_in.is_multiple_of(FP8_BLOCK) {
8806        return Err(format!(
8807            "TP expert input shard {local_in} cuts through a {FP8_BLOCK}-column E4M3 scale block"
8808        ));
8809    }
8810    Ok(())
8811}
8812
8813fn upload_column_bank_rank(
8814    engine: &Engine,
8815    bank: E4m3ExpertBank<'_>,
8816    tp: usize,
8817    rank: usize,
8818) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
8819    let _main = engine.gpu.enter_main()?;
8820    let packed = pack_column_bank_rank(bank, tp, rank)?;
8821    Ok(ResidentE4m3ExpertBankRank {
8822        codes: engine.htod_bytes(&packed.codes)?,
8823        scales: engine.htod(&packed.scales)?,
8824        expert_range: packed.expert_range,
8825        out_features: packed.out_features,
8826        in_features: packed.in_features,
8827        code_stride: packed.code_stride,
8828        scale_stride: packed.scale_stride,
8829        k_blocks: packed.k_blocks,
8830    })
8831}
8832
8833fn pack_column_bank_rank(
8834    bank: E4m3ExpertBank<'_>,
8835    tp: usize,
8836    rank: usize,
8837) -> Result<PackedE4m3ExpertBankRank, String> {
8838    bank.validate()?;
8839    validate_column_bank_shape(bank, tp)?;
8840    if rank >= tp {
8841        return Err(format!("TP rank {rank} outside 0..{tp}"));
8842    }
8843    let local_out = bank.out_features / tp;
8844    let full_code_stride = bank.out_features * bank.in_features;
8845    let local_code_stride = local_out * bank.in_features;
8846    let scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
8847    let full_scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * scale_cols;
8848    let local_scale_rows = local_out / FP8_BLOCK;
8849    let local_scale_stride = local_scale_rows * scale_cols;
8850    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
8851    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
8852    let row_start = rank * local_out;
8853    let scale_row_start = rank * local_scale_rows;
8854    for expert in 0..bank.expert_count {
8855        let code_start = expert * full_code_stride + row_start * bank.in_features;
8856        codes.extend_from_slice(&bank.codes[code_start..code_start + local_code_stride]);
8857        let scale_start = expert * full_scale_stride + scale_row_start * scale_cols;
8858        scales.extend_from_slice(&bank.scales[scale_start..scale_start + local_scale_stride]);
8859    }
8860    Ok(PackedE4m3ExpertBankRank {
8861        codes,
8862        scales,
8863        expert_range: 0..bank.expert_count,
8864        out_features: local_out,
8865        in_features: bank.in_features,
8866        code_stride: local_code_stride,
8867        scale_stride: local_scale_stride,
8868        k_blocks: None,
8869    })
8870}
8871
8872fn upload_row_bank_rank(
8873    engine: &Engine,
8874    bank: E4m3ExpertBank<'_>,
8875    tp: usize,
8876    rank: usize,
8877) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
8878    let _main = engine.gpu.enter_main()?;
8879    let packed = pack_row_bank_rank(bank, tp, rank)?;
8880    Ok(ResidentE4m3ExpertBankRank {
8881        codes: engine.htod_bytes(&packed.codes)?,
8882        scales: engine.htod(&packed.scales)?,
8883        expert_range: packed.expert_range,
8884        out_features: packed.out_features,
8885        in_features: packed.in_features,
8886        code_stride: packed.code_stride,
8887        scale_stride: packed.scale_stride,
8888        k_blocks: packed.k_blocks,
8889    })
8890}
8891
8892fn pack_row_bank_rank(
8893    bank: E4m3ExpertBank<'_>,
8894    tp: usize,
8895    rank: usize,
8896) -> Result<PackedE4m3ExpertBankRank, String> {
8897    bank.validate()?;
8898    validate_row_bank_shape(bank, tp)?;
8899    if rank >= tp {
8900        return Err(format!("TP rank {rank} outside 0..{tp}"));
8901    }
8902    let local_in = bank.in_features / tp;
8903    let full_code_stride = bank.out_features * bank.in_features;
8904    let local_code_stride = bank.out_features * local_in;
8905    let full_scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
8906    let local_scale_cols = local_in / FP8_BLOCK;
8907    let scale_rows = bank.out_features.div_ceil(FP8_BLOCK);
8908    let full_scale_stride = scale_rows * full_scale_cols;
8909    let local_scale_stride = scale_rows * local_scale_cols;
8910    let global_block_start = rank * local_scale_cols;
8911    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
8912    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
8913    for expert in 0..bank.expert_count {
8914        let expert_code_start = expert * full_code_stride;
8915        let expert_scale_start = expert * full_scale_stride;
8916        for local_block in 0..local_scale_cols {
8917            let global_block = global_block_start + local_block;
8918            let column_start = global_block * FP8_BLOCK;
8919            for row in 0..bank.out_features {
8920                let start = expert_code_start + row * bank.in_features + column_start;
8921                codes.extend_from_slice(&bank.codes[start..start + FP8_BLOCK]);
8922            }
8923            for row in 0..scale_rows {
8924                scales.push(bank.scales[expert_scale_start + row * full_scale_cols + global_block]);
8925            }
8926        }
8927    }
8928    Ok(PackedE4m3ExpertBankRank {
8929        codes,
8930        scales,
8931        expert_range: 0..bank.expert_count,
8932        out_features: bank.out_features,
8933        in_features: local_in,
8934        code_stride: local_code_stride,
8935        scale_stride: local_scale_stride,
8936        k_blocks: Some(local_scale_cols),
8937    })
8938}
8939
8940fn validate_resident_ranks(engines: &[Engine], ranks: &[ResidentE4m3Rank]) -> Result<(), String> {
8941    if engines.len() != ranks.len() {
8942        return Err(format!(
8943            "resident TP rank count {} != runtime rank count {}",
8944            ranks.len(),
8945            engines.len()
8946        ));
8947    }
8948    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
8949        let device = engine.ctx().ordinal();
8950        if matrix.codes.ordinal() != device || matrix.scales.ordinal() != device {
8951            return Err(format!(
8952                "resident TP rank {rank} is not owned by runtime device {device}"
8953            ));
8954        }
8955    }
8956    Ok(())
8957}
8958
8959fn validate_tp_bank_residency(
8960    engines: &[Engine],
8961    experts: &ResidentTpExpertBank,
8962) -> Result<(), String> {
8963    if engines.len() != experts.gate.len()
8964        || engines.len() != experts.up.len()
8965        || engines.len() != experts.down.len()
8966    {
8967        return Err(format!(
8968            "resident TP expert-bank rank counts gate={} up={} down={} != runtime {}",
8969            experts.gate.len(),
8970            experts.up.len(),
8971            experts.down.len(),
8972            engines.len()
8973        ));
8974    }
8975    for (rank, engine) in engines.iter().enumerate() {
8976        let device = engine.ctx().ordinal();
8977        for (projection, bank) in [
8978            ("gate", &experts.gate[rank]),
8979            ("up", &experts.up[rank]),
8980            ("down", &experts.down[rank]),
8981        ] {
8982            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
8983                return Err(format!(
8984                    "resident TP rank {rank} {projection} bank is not owned by runtime device \
8985                     {device}"
8986                ));
8987            }
8988        }
8989    }
8990    Ok(())
8991}
8992
8993fn validate_ep_residency(
8994    engines: &[Engine],
8995    experts: &ResidentExpertParallel,
8996) -> Result<(), String> {
8997    if engines.len() != experts.ranks.len() {
8998        return Err(format!(
8999            "resident EP rank count {} != runtime rank count {}",
9000            experts.ranks.len(),
9001            engines.len()
9002        ));
9003    }
9004    for (rank, (engine, resident)) in engines.iter().zip(&experts.ranks).enumerate() {
9005        let device = engine.ctx().ordinal();
9006        for (projection, bank) in [
9007            ("gate", &resident.gate),
9008            ("up", &resident.up),
9009            ("down", &resident.down),
9010        ] {
9011            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
9012                return Err(format!(
9013                    "resident EP rank {rank} {projection} bank is not owned by runtime device \
9014                     {device}"
9015                ));
9016            }
9017        }
9018    }
9019    Ok(())
9020}
9021
9022fn run_rank(
9023    engine: &Engine,
9024    matrix: E4m3BlockMatrix<'_>,
9025    activations: &[f32],
9026    tokens: usize,
9027) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9028    let _main = engine.gpu.enter_main()?;
9029    let codes = engine.htod_bytes(matrix.codes)?;
9030    let scales = engine.htod(matrix.scales)?;
9031    let activations = engine.htod(activations)?;
9032    let output = engine.qmatvec_mmq_fp8_blk(
9033        &codes,
9034        &scales,
9035        &activations,
9036        tokens,
9037        matrix.in_features,
9038        matrix.out_features,
9039    )?;
9040    engine.dtoh(&output)
9041}
9042
9043fn run_resident_rank(
9044    engine: &Engine,
9045    matrix: &ResidentE4m3Rank,
9046    activations: &[f32],
9047    tokens: usize,
9048) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9049    let _main = engine.gpu.enter_main()?;
9050    let activations = engine.htod(activations)?;
9051    let output = engine.qmatvec_mmq_fp8_blk(
9052        &matrix.codes,
9053        &matrix.scales,
9054        &activations,
9055        tokens,
9056        matrix.in_features,
9057        matrix.out_features,
9058    )?;
9059    engine.dtoh(&output)
9060}
9061
9062fn run_resident_bf16_rank(
9063    engine: &Engine,
9064    matrix: &ResidentBf16Rank,
9065    activations: &[f32],
9066    tokens: usize,
9067    canonical_chunk_rows: Option<usize>,
9068) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9069    let _main = engine.gpu.enter_main()?;
9070    let activations = engine.htod(activations)?;
9071    let output = run_resident_bf16_rank_device(
9072        engine,
9073        matrix,
9074        &activations,
9075        tokens,
9076        canonical_chunk_rows,
9077        false,
9078    )?;
9079    engine.dtoh(&output)
9080}
9081
9082fn run_resident_bf16_rank_device(
9083    engine: &Engine,
9084    matrix: &ResidentBf16Rank,
9085    activations: &CudaSlice<f32>,
9086    tokens: usize,
9087    canonical_chunk_rows: Option<usize>,
9088    strided_chunk_output: bool,
9089) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9090    let _main = engine.gpu.enter_main()?;
9091    if activations.ordinal() != engine.ctx().ordinal() {
9092        return Err(format!(
9093            "resident BF16 activation device {} != rank device {}",
9094            activations.ordinal(),
9095            engine.ctx().ordinal()
9096        )
9097        .into());
9098    }
9099    if activations.len() != tokens * matrix.in_features {
9100        return Err(format!(
9101            "resident BF16 activation count {} != {tokens}x{}",
9102            activations.len(),
9103            matrix.in_features
9104        )
9105        .into());
9106    }
9107    match (&matrix.weight, canonical_chunk_rows) {
9108        (ResidentBf16Weight::Bf16(bytes), Some(rows)) => engine
9109            .linear_bf16_resident_canonical_rows(
9110                activations,
9111                bytes,
9112                tokens,
9113                matrix.in_features,
9114                matrix.out_features,
9115                rows,
9116            ),
9117        (ResidentBf16Weight::Bf16(bytes), None) => engine.linear_bf16_resident(
9118            activations,
9119            bytes,
9120            tokens,
9121            matrix.in_features,
9122            matrix.out_features,
9123        ),
9124        (ResidentBf16Weight::F32(values), Some(rows)) if strided_chunk_output => engine
9125            .linear_f32_resident_canonical_rows_strided(
9126                activations,
9127                values,
9128                tokens,
9129                matrix.in_features,
9130                matrix.out_features,
9131                rows,
9132            ),
9133        (ResidentBf16Weight::F32(values), Some(rows)) => engine.linear_f32_resident_canonical_rows(
9134            activations,
9135            values,
9136            tokens,
9137            matrix.in_features,
9138            matrix.out_features,
9139            rows,
9140        ),
9141        (ResidentBf16Weight::F32(values), None) => engine.linear(
9142            activations,
9143            values,
9144            tokens,
9145            matrix.in_features,
9146            matrix.out_features,
9147        ),
9148    }
9149}
9150
9151fn validate_resident_bf16_ranks(
9152    engines: &[Engine],
9153    ranks: &[ResidentBf16Rank],
9154) -> Result<(), String> {
9155    if engines.len() != ranks.len() {
9156        return Err(format!(
9157            "resident BF16 TP rank count {} != runtime rank count {}",
9158            ranks.len(),
9159            engines.len(),
9160        ));
9161    }
9162    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
9163        let device = engine.ctx().ordinal();
9164        if matrix.weight.ordinal() != device {
9165            return Err(format!(
9166                "resident BF16 TP rank {rank} is not owned by runtime device {device}"
9167            ));
9168        }
9169    }
9170    Ok(())
9171}
9172
9173fn validate_step_bf16_row_residency(
9174    engines: &[Engine],
9175    matrix: &ResidentStepBf16RowParallel,
9176) -> Result<(), String> {
9177    if engines.len() != matrix.ranks.len() {
9178        return Err(format!(
9179            "resident Step BF16 row rank count {} != runtime rank count {}",
9180            matrix.ranks.len(),
9181            engines.len(),
9182        ));
9183    }
9184    let canonical_cols = step_bf16_canonical_chunk_cols(matrix.in_features, engines.len())?;
9185    if matrix.canonical_chunk_cols != canonical_cols {
9186        return Err(format!(
9187            "resident Step BF16 row canonical columns {} != registered {canonical_cols}",
9188            matrix.canonical_chunk_cols
9189        ));
9190    }
9191    let blocks_per_rank = PRODUCT_MAX_CARDS / engines.len();
9192    for (rank, (engine, blocks)) in engines.iter().zip(&matrix.ranks).enumerate() {
9193        if blocks.len() != blocks_per_rank {
9194            return Err(format!(
9195                "resident Step BF16 row rank {rank} has {} blocks, expected {blocks_per_rank}",
9196                blocks.len()
9197            ));
9198        }
9199        let device = engine.ctx().ordinal();
9200        for (block, resident) in blocks.iter().enumerate() {
9201            if resident.weight.ordinal() != device
9202                || resident.in_features != canonical_cols
9203                || resident.out_features != matrix.out_features
9204            {
9205                return Err(format!(
9206                    "resident Step BF16 row rank {rank} block {block} has inconsistent \
9207                     device or geometry"
9208                ));
9209            }
9210        }
9211    }
9212    Ok(())
9213}
9214
9215fn validate_replicated_device_rows(
9216    engines: &[Engine],
9217    rows: &ResidentReplicatedDeviceRows,
9218) -> Result<(), String> {
9219    let rank_lengths = rows
9220        .ranks
9221        .iter()
9222        .map(|rank_rows| rank_rows.len())
9223        .collect::<Vec<_>>();
9224    replicated_device_row_values(rows.tokens, rows.width, engines.len(), &rank_lengths)?;
9225    if rows
9226        .ranks
9227        .iter()
9228        .zip(engines)
9229        .any(|(rank_rows, engine)| rank_rows.ordinal() != engine.ctx().ordinal())
9230    {
9231        return Err("replicated device rows are owned by the wrong CUDA contexts".into());
9232    }
9233    Ok(())
9234}
9235
9236fn replicated_device_row_values(
9237    tokens: usize,
9238    width: usize,
9239    expected_ranks: usize,
9240    rank_lengths: &[usize],
9241) -> Result<usize, String> {
9242    let values = tokens
9243        .checked_mul(width)
9244        .ok_or("replicated device row size overflow")?;
9245    if tokens == 0
9246        || width == 0
9247        || expected_ranks == 0
9248        || rank_lengths.len() != expected_ranks
9249        || rank_lengths.iter().any(|&rank_len| rank_len != values)
9250    {
9251        return Err(format!(
9252            "replicated device rows have inconsistent geometry tokens={} width={} ranks={}/{}",
9253            tokens,
9254            width,
9255            rank_lengths.len(),
9256            expected_ranks
9257        ));
9258    }
9259    Ok(values)
9260}
9261
9262fn replicated_device_row_source_values(
9263    tokens: usize,
9264    width: usize,
9265    source_len: usize,
9266    source_device: usize,
9267    root_device: usize,
9268) -> Result<usize, String> {
9269    let values = tokens
9270        .checked_mul(width)
9271        .ok_or("replicated device row size overflow")?;
9272    if tokens == 0 || width == 0 || source_len != values || source_device != root_device {
9273        return Err(format!(
9274            "replicated device row source has inconsistent geometry/device \
9275             tokens={tokens} width={width} source={source_len}@{source_device} root={root_device}"
9276        ));
9277    }
9278    Ok(values)
9279}
9280
9281#[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
9282fn bf16_column_shard(
9283    matrix: Bf16Matrix<'_>,
9284    tp: usize,
9285    rank: usize,
9286) -> Result<Bf16Matrix<'_>, String> {
9287    matrix.validate()?;
9288    if tp == 0 || rank >= tp || matrix.out_features % tp != 0 {
9289        return Err(format!(
9290            "invalid BF16 column shard out={} TP={tp} rank={rank}",
9291            matrix.out_features
9292        ));
9293    }
9294    let local_out = matrix.out_features / tp;
9295    let row_bytes = matrix.in_features * 2;
9296    let start = rank * local_out * row_bytes;
9297    Ok(Bf16Matrix {
9298        bytes: &matrix.bytes[start..start + local_out * row_bytes],
9299        out_features: local_out,
9300        in_features: matrix.in_features,
9301    })
9302}
9303
9304#[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
9305fn bf16_row_shard(matrix: Bf16Matrix<'_>, tp: usize, rank: usize) -> Result<Vec<u8>, String> {
9306    matrix.validate()?;
9307    if tp == 0 || rank >= tp || matrix.in_features % tp != 0 {
9308        return Err(format!(
9309            "invalid BF16 row shard in={} TP={tp} rank={rank}",
9310            matrix.in_features
9311        ));
9312    }
9313    let local_in = matrix.in_features / tp;
9314    let mut bytes = Vec::with_capacity(matrix.out_features * local_in * 2);
9315    for row in 0..matrix.out_features {
9316        let start = (row * matrix.in_features + rank * local_in) * 2;
9317        bytes.extend_from_slice(&matrix.bytes[start..start + local_in * 2]);
9318    }
9319    Ok(bytes)
9320}
9321
9322fn bf16_row_block(
9323    matrix: Bf16Matrix<'_>,
9324    col_start: usize,
9325    block_cols: usize,
9326) -> Result<Vec<u8>, String> {
9327    matrix.validate()?;
9328    let col_end = col_start
9329        .checked_add(block_cols)
9330        .ok_or("BF16 row block column overflow")?;
9331    if block_cols == 0 || col_end > matrix.in_features {
9332        return Err(format!(
9333            "invalid BF16 row block columns {col_start}..{col_end} for input width {}",
9334            matrix.in_features
9335        ));
9336    }
9337    let mut bytes = Vec::with_capacity(matrix.out_features * block_cols * 2);
9338    for row in 0..matrix.out_features {
9339        let start = (row * matrix.in_features + col_start) * 2;
9340        bytes.extend_from_slice(&matrix.bytes[start..start + block_cols * 2]);
9341    }
9342    Ok(bytes)
9343}
9344
9345fn run_resident_bank_expert(
9346    engine: &Engine,
9347    bank: &ResidentE4m3ExpertBankRank,
9348    local_expert: usize,
9349    activations: &[f32],
9350    tokens: usize,
9351) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9352    let _main = engine.gpu.enter_main()?;
9353    if bank.k_blocks.is_some() {
9354        return Err("block-major TP row bank requires canonical block execution".into());
9355    }
9356    let local_count = bank.expert_range.end - bank.expert_range.start;
9357    if local_expert >= local_count {
9358        return Err(format!(
9359            "local EP expert {local_expert} outside 0..{local_count} for range {:?}",
9360            bank.expert_range
9361        )
9362        .into());
9363    }
9364    validate_activations(activations, tokens, bank.in_features)?;
9365    let activations = engine.htod(activations)?;
9366    let weight = bank
9367        .codes
9368        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
9369    let scales = bank
9370        .scales
9371        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
9372    let input = activations.slice(0..activations.len());
9373    let output = engine.qmatvec_mmq_fp8_blk_view(
9374        &weight,
9375        &scales,
9376        &input,
9377        tokens,
9378        bank.in_features,
9379        bank.out_features,
9380    )?;
9381    engine.dtoh(&output)
9382}
9383
9384fn run_resident_bank_expert_block(
9385    engine: &Engine,
9386    bank: &ResidentE4m3ExpertBankRank,
9387    local_expert: usize,
9388    block: usize,
9389    activations: &[f32],
9390) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9391    let _main = engine.gpu.enter_main()?;
9392    let local_count = bank.expert_range.end - bank.expert_range.start;
9393    if local_expert >= local_count {
9394        return Err(format!(
9395            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
9396            bank.expert_range
9397        )
9398        .into());
9399    }
9400    let blocks = bank
9401        .k_blocks
9402        .ok_or("TP row bank is not packed in native K-block order")?;
9403    if block >= blocks {
9404        return Err(format!("TP row block {block} outside 0..{blocks}").into());
9405    }
9406    validate_activations(activations, 1, FP8_BLOCK)?;
9407    let block_code_stride = bank.out_features * FP8_BLOCK;
9408    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
9409    if bank.in_features != blocks * FP8_BLOCK
9410        || bank.code_stride != blocks * block_code_stride
9411        || bank.scale_stride != blocks * block_scale_stride
9412    {
9413        return Err("TP row bank block-major geometry is inconsistent".into());
9414    }
9415
9416    let expert_code_start = local_expert * bank.code_stride;
9417    let expert_scale_start = local_expert * bank.scale_stride;
9418    let weight = bank.codes.slice(
9419        expert_code_start + block * block_code_stride
9420            ..expert_code_start + (block + 1) * block_code_stride,
9421    );
9422    let scales = bank.scales.slice(
9423        expert_scale_start + block * block_scale_stride
9424            ..expert_scale_start + (block + 1) * block_scale_stride,
9425    );
9426    let activations = engine.htod(activations)?;
9427    let input = activations.slice(0..activations.len());
9428    let output = engine.qmatvec_mmq_fp8_blk_view(
9429        &weight,
9430        &scales,
9431        &input,
9432        1,
9433        FP8_BLOCK,
9434        bank.out_features,
9435    )?;
9436    engine.dtoh(&output)
9437}
9438
9439fn run_resident_bank_expert_device(
9440    engine: &Engine,
9441    bank: &ResidentE4m3ExpertBankRank,
9442    local_expert: usize,
9443    activations: &CudaSlice<f32>,
9444    tokens: usize,
9445) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9446    let _main = engine.gpu.enter_main()?;
9447    if bank.k_blocks.is_some() {
9448        return Err("block-major TP row bank requires canonical block execution".into());
9449    }
9450    let local_count = bank.expert_range.end - bank.expert_range.start;
9451    if local_expert >= local_count {
9452        return Err(format!(
9453            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
9454            bank.expert_range
9455        )
9456        .into());
9457    }
9458    let expected = tokens
9459        .checked_mul(bank.in_features)
9460        .ok_or("native TP activation size overflow")?;
9461    if activations.len() != expected || activations.ordinal() != engine.ctx().ordinal() {
9462        return Err(format!(
9463            "native TP activation len/device {}/{} != expected {expected}/{}",
9464            activations.len(),
9465            activations.ordinal(),
9466            engine.ctx().ordinal()
9467        )
9468        .into());
9469    }
9470    let weight = bank
9471        .codes
9472        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
9473    let scales = bank
9474        .scales
9475        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
9476    let input = activations.slice(0..activations.len());
9477    engine.qmatvec_mmq_fp8_blk_view(
9478        &weight,
9479        &scales,
9480        &input,
9481        tokens,
9482        bank.in_features,
9483        bank.out_features,
9484    )
9485}
9486
9487fn run_resident_bank_expert_block_device(
9488    engine: &Engine,
9489    bank: &ResidentE4m3ExpertBankRank,
9490    local_expert: usize,
9491    block: usize,
9492    activations: &cudarc::driver::CudaView<'_, f32>,
9493) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9494    let _main = engine.gpu.enter_main()?;
9495    let local_count = bank.expert_range.end - bank.expert_range.start;
9496    if local_expert >= local_count {
9497        return Err(format!(
9498            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
9499            bank.expert_range
9500        )
9501        .into());
9502    }
9503    let blocks = bank
9504        .k_blocks
9505        .ok_or("native TP row bank is not packed in checkpoint-block order")?;
9506    if block >= blocks {
9507        return Err(format!("native TP row block {block} outside 0..{blocks}").into());
9508    }
9509    let activation_device = activations.stream().context().ordinal();
9510    if activations.len() != FP8_BLOCK || activation_device != engine.ctx().ordinal() {
9511        return Err(format!(
9512            "native TP block activation len/device {}/{} != expected {FP8_BLOCK}/{}",
9513            activations.len(),
9514            activation_device,
9515            engine.ctx().ordinal()
9516        )
9517        .into());
9518    }
9519    let block_code_stride = bank.out_features * FP8_BLOCK;
9520    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
9521    if bank.in_features != blocks * FP8_BLOCK
9522        || bank.code_stride != blocks * block_code_stride
9523        || bank.scale_stride != blocks * block_scale_stride
9524    {
9525        return Err("native TP row bank block-major geometry is inconsistent".into());
9526    }
9527    let expert_code_start = local_expert * bank.code_stride;
9528    let expert_scale_start = local_expert * bank.scale_stride;
9529    let weight = bank.codes.slice(
9530        expert_code_start + block * block_code_stride
9531            ..expert_code_start + (block + 1) * block_code_stride,
9532    );
9533    let scales = bank.scales.slice(
9534        expert_scale_start + block * block_scale_stride
9535            ..expert_scale_start + (block + 1) * block_scale_stride,
9536    );
9537    engine.qmatvec_mmq_fp8_blk_view(
9538        &weight,
9539        &scales,
9540        activations,
9541        1,
9542        FP8_BLOCK,
9543        bank.out_features,
9544    )
9545}
9546
9547/// Grant `accessor` the right to reach `owner`'s memory — BOTH halves of the grant, which is
9548/// the part every caller gets wrong exactly once:
9549///
9550///   1. `cuCtxEnablePeerAccess`, which covers legacy `cuMemAlloc` allocations, and
9551///   2. `cuMemPoolSetAccess` on `owner`'s DEFAULT MEMORY POOL, because
9552///      `cuCtxEnablePeerAccess` does NOT map STREAM-ORDERED POOL allocations and every
9553///      normal memra buffer is one (the same note `pp.rs:1543`/`pp.rs:1578` carries).
9554///
9555/// Extracted from [`configure_native_p2p`] (which now calls it per ordered pair) so a seam
9556/// holding two `&Engine` rather than a `&[Engine]` — the glm5 TP-2 runtime — reuses the exact
9557/// grant sequence instead of growing a second, drifting copy of it. Directed: call it once
9558/// per direction. Refuses by name when `cuDeviceCanAccessPeer` says the pair has no path,
9559/// which is the only honest answer: this card class is NOT uniformly peer-connected. Some
9560/// 8-GPU host classes present PEER ISLANDS OF TWO — every cross-island cell of a peer-transfer
9561/// matrix reads `N/A` — so a TP group placed across an island boundary has no peer path at all
9562/// and must either stay inside one island or go through host memory. The per-host island map is
9563/// fleet data and lives in the private deployment repo, never here; the engine's job is to
9564/// refuse by name rather than to know which host it is on.
9565pub(crate) fn grant_peer_access(
9566    accessor: &Engine,
9567    owner: &Engine,
9568    label: &str,
9569) -> Result<(), Box<dyn std::error::Error>> {
9570    let (a_dev, o_dev) = (accessor.ctx().ordinal(), owner.ctx().ordinal());
9571    let mut can_access = 0;
9572    unsafe {
9573        cudarc::driver::sys::cuDeviceCanAccessPeer(
9574            &mut can_access,
9575            accessor.ctx().cu_device(),
9576            owner.ctx().cu_device(),
9577        )
9578        .result()?;
9579    }
9580    if can_access == 0 {
9581        return Err(
9582            format!("{label} requires P2P, but dev{a_dev} cannot access dev{o_dev}").into(),
9583        );
9584    }
9585    accessor.ctx().bind_to_thread()?;
9586    let rc = unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(owner.ctx().cu_ctx(), 0) };
9587    use cudarc::driver::sys::cudaError_enum as E;
9588    if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
9589        return Err(format!(
9590            "{label} cuCtxEnablePeerAccess(dev{a_dev} -> dev{o_dev}) failed: {rc:?}"
9591        )
9592        .into());
9593    }
9594    let device = cudarc::driver::result::device::get(o_dev as i32)?;
9595    let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
9596    unsafe {
9597        cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, device).result()?;
9598    }
9599    let desc = cudarc::driver::sys::CUmemAccessDesc {
9600        location: cudarc::driver::sys::CUmemLocation {
9601            type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
9602            id: a_dev as i32,
9603        },
9604        flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
9605    };
9606    let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
9607    if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
9608        return Err(format!(
9609            "{label} cuMemPoolSetAccess(dev{o_dev} pool -> dev{a_dev}) failed: {rc:?}"
9610        )
9611        .into());
9612    }
9613    Ok(())
9614}
9615
9616fn configure_native_p2p(
9617    ranks: &[Engine],
9618    devices: &[usize],
9619) -> Result<(), Box<dyn std::error::Error>> {
9620    if ranks.len() != devices.len() || ranks.len() < 2 {
9621        return Err("native TP P2P setup requires matching multi-rank devices".into());
9622    }
9623    for (rank, (&device, engine)) in devices.iter().zip(ranks).enumerate() {
9624        if engine.ctx().ordinal() != device {
9625            return Err(format!(
9626                "native TP rank {rank} context device {} != requested device {device}",
9627                engine.ctx().ordinal()
9628            )
9629            .into());
9630        }
9631    }
9632
9633    for src in 0..ranks.len() {
9634        for dst in 0..ranks.len() {
9635            if src == dst {
9636                continue;
9637            }
9638            grant_peer_access(&ranks[src], &ranks[dst], "native TP")?;
9639        }
9640    }
9641
9642    for src in 0..ranks.len() {
9643        for dst in 0..ranks.len() {
9644            if src == dst {
9645                continue;
9646            }
9647            for &words in NATIVE_P2P_PROBE_WORDS {
9648                let expected = (0..words)
9649                    .map(|index| {
9650                        (index as u32)
9651                            .wrapping_mul(0x9e37_79b9)
9652                            .wrapping_add(((src as u32) << 16) | dst as u32)
9653                    })
9654                    .collect::<Vec<_>>();
9655                let poison = expected.iter().map(|value| !value).collect::<Vec<_>>();
9656                let source = ranks[src].htod_u32_v(&expected)?;
9657                let mut destination = ranks[dst].htod_u32_v(&poison)?;
9658                ranks[dst].stream().memcpy_dtod(&source, &mut destination)?;
9659                let actual = ranks[dst].dtoh_u32(&destination)?;
9660                if actual != expected {
9661                    let mismatches = actual
9662                        .iter()
9663                        .zip(&expected)
9664                        .filter(|(actual, expected)| actual != expected)
9665                        .count();
9666                    return Err(format!(
9667                        "native TP peer probe dev{}->dev{} failed at {} bytes: \
9668                         {mismatches}/{} words differ",
9669                        devices[src],
9670                        devices[dst],
9671                        words * std::mem::size_of::<u32>(),
9672                        expected.len()
9673                    )
9674                    .into());
9675                }
9676            }
9677        }
9678    }
9679    ranks[0].ctx().bind_to_thread()?;
9680    eprintln!(
9681        "[tp] native peer byte-integrity probe PASS: devices={devices:?} \
9682         directions={} byte_ladder={:?} mismatches=0",
9683        ranks.len() * (ranks.len() - 1),
9684        NATIVE_P2P_PROBE_WORDS
9685            .iter()
9686            .map(|words| words * std::mem::size_of::<u32>())
9687            .collect::<Vec<_>>(),
9688    );
9689    Ok(())
9690}
9691
9692fn validate_activations(
9693    activations: &[f32],
9694    tokens: usize,
9695    in_features: usize,
9696) -> Result<(), String> {
9697    let expected = tokens
9698        .checked_mul(in_features)
9699        .ok_or_else(|| "activation size overflow".to_string())?;
9700    if activations.len() != expected {
9701        return Err(format!(
9702            "activation count {} != {tokens}x{in_features} ({expected})",
9703            activations.len()
9704        ));
9705    }
9706    if !activations.iter().all(|value| value.is_finite()) {
9707        return Err("activations contain a non-finite value".to_string());
9708    }
9709    Ok(())
9710}
9711
9712fn column_shard(
9713    matrix: E4m3BlockMatrix<'_>,
9714    tp: usize,
9715    rank: usize,
9716) -> Result<E4m3BlockMatrix<'_>, String> {
9717    let local_out = matrix.out_features / tp;
9718    let row_start = rank * local_out;
9719    let code_start = row_start * matrix.in_features;
9720    let code_end = code_start + local_out * matrix.in_features;
9721    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
9722    let local_scale_rows = local_out / FP8_BLOCK;
9723    let scale_start = rank * local_scale_rows * scale_cols;
9724    let scale_end = scale_start + local_scale_rows * scale_cols;
9725    Ok(E4m3BlockMatrix {
9726        codes: &matrix.codes[code_start..code_end],
9727        scales: &matrix.scales[scale_start..scale_end],
9728        out_features: local_out,
9729        in_features: matrix.in_features,
9730    })
9731}
9732
9733fn row_shard(
9734    matrix: E4m3BlockMatrix<'_>,
9735    tp: usize,
9736    rank: usize,
9737) -> Result<(Vec<u8>, Vec<f32>), String> {
9738    let local_in = matrix.in_features / tp;
9739    let col_start = rank * local_in;
9740    let mut codes = Vec::with_capacity(matrix.out_features * local_in);
9741    for row in 0..matrix.out_features {
9742        let start = row * matrix.in_features + col_start;
9743        codes.extend_from_slice(&matrix.codes[start..start + local_in]);
9744    }
9745
9746    let scale_rows = matrix.out_features.div_ceil(FP8_BLOCK);
9747    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
9748    let local_scale_cols = local_in / FP8_BLOCK;
9749    let scale_col_start = rank * local_scale_cols;
9750    let mut scales = Vec::with_capacity(scale_rows * local_scale_cols);
9751    for row in 0..scale_rows {
9752        let start = row * scale_cols + scale_col_start;
9753        scales.extend_from_slice(&matrix.scales[start..start + local_scale_cols]);
9754    }
9755    Ok((codes, scales))
9756}
9757
9758fn activation_shard(
9759    activations: &[f32],
9760    tokens: usize,
9761    in_features: usize,
9762    tp: usize,
9763    rank: usize,
9764) -> Vec<f32> {
9765    let local_in = in_features / tp;
9766    let col_start = rank * local_in;
9767    let mut shard = Vec::with_capacity(tokens * local_in);
9768    for token in 0..tokens {
9769        let start = token * in_features + col_start;
9770        shard.extend_from_slice(&activations[start..start + local_in]);
9771    }
9772    shard
9773}
9774
9775// ─── Step NVFP4 expert TP program (official Step-3.7-Flash-NVFP4 checkpoint class) ─────────────
9776//
9777// The routed experts of the NVFP4 checkpoint are modelopt-packed: e2m1 codes (2/byte), per-16
9778// UE4M3 sub-scales, and a per-EXPERT `weight_scale_2` f32 macro (~1e-5..1e-4, LOAD-BEARING).
9779// Rank compute repacks each shard host-side into memra block_nvfp4 rows (nibble reorder only —
9780// value-exact, see nvfp4_repack.rs) and runs the proven `qmatvec_nvfp4_fast` dp4a kernel; the
9781// activation q8_1 quantization uses per-32 blocks, and every shard cut here is 64-aligned, so a
9782// rank-local partial is bit-identical to the corresponding slice of the unsharded kernel.
9783//
9784// MACRO CANONICAL ORDER: the macro multiplies each assembled f32 output exactly ONCE — after the
9785// column gather (gate/up) and after the FULL row-parallel reduce (down), never per-partial.
9786// `(a + b) * m` and `a * m + b * m` differ in f32, so applying it per-rank would break the
9787// TP1-vs-TP2 bit gate. Every entry point below follows this order.
9788//
9789// TP2 shard legality is NVFP4-native: column parallelism splits whole output rows (scale rows
9790// ride along, nothing cuts), row parallelism splits input columns at 64-element superblock
9791// boundaries (16-element scale groups nest inside). The 128-block E4M3 constraint does not apply.
9792
9793/// One expert's modelopt NVFP4 projection: packed codes + per-16 UE4M3 scale bytes + macro.
9794#[derive(Clone, Copy)]
9795pub struct Nvfp4BlockMatrix<'a> {
9796    pub codes: &'a [u8],  // [out_features, in_features/2] packed e2m1, row-major
9797    pub scales: &'a [u8], // [out_features, in_features/16] UE4M3 bytes, row-major
9798    pub macro_scale: f32, // per-expert weight_scale_2 dequant multiplier
9799    pub out_features: usize,
9800    pub in_features: usize,
9801}
9802
9803impl Nvfp4BlockMatrix<'_> {
9804    pub fn validate(&self) -> Result<(), String> {
9805        if self.in_features == 0 || self.out_features == 0 {
9806            return Err("NVFP4 matrix has a zero dimension".to_string());
9807        }
9808        if !self.in_features.is_multiple_of(64) {
9809            return Err(format!(
9810                "NVFP4 in_features {} is not 64-aligned (memra block_nvfp4 superblock)",
9811                self.in_features
9812            ));
9813        }
9814        if self.codes.len() != self.out_features * self.in_features / 2 {
9815            return Err(format!(
9816                "NVFP4 code bytes {} != {}x{}/2",
9817                self.codes.len(),
9818                self.out_features,
9819                self.in_features
9820            ));
9821        }
9822        if self.scales.len() != self.out_features * self.in_features / 16 {
9823            return Err(format!(
9824                "NVFP4 scale bytes {} != {}x{}/16",
9825                self.scales.len(),
9826                self.out_features,
9827                self.in_features
9828            ));
9829        }
9830        if !self.macro_scale.is_finite() || self.macro_scale <= 0.0 {
9831            return Err(format!(
9832                "NVFP4 macro scale {} is not finite-positive",
9833                self.macro_scale
9834            ));
9835        }
9836        Ok(())
9837    }
9838}
9839
9840/// Stacked modelopt NVFP4 expert bank (host view over the checkpoint bytes).
9841#[derive(Clone, Copy)]
9842pub struct Nvfp4ExpertBank<'a> {
9843    pub codes: &'a [u8],   // [expert_count, out_features, in_features/2]
9844    pub scales: &'a [u8],  // [expert_count, out_features, in_features/16]
9845    pub macros: &'a [f32], // [expert_count] weight_scale_2
9846    pub expert_count: usize,
9847    pub out_features: usize,
9848    pub in_features: usize,
9849}
9850
9851impl Nvfp4ExpertBank<'_> {
9852    pub fn validate(&self) -> Result<(), String> {
9853        if self.expert_count == 0 {
9854            return Err("NVFP4 expert bank is empty".to_string());
9855        }
9856        if self.macros.len() != self.expert_count {
9857            return Err(format!(
9858                "NVFP4 bank macros {} != expert count {}",
9859                self.macros.len(),
9860                self.expert_count
9861            ));
9862        }
9863        self.expert(0).map(|_| ())
9864    }
9865
9866    pub fn expert(&self, expert: usize) -> Result<Nvfp4BlockMatrix<'_>, String> {
9867        if expert >= self.expert_count {
9868            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
9869        }
9870        let code_stride = self.out_features * self.in_features / 2;
9871        let scale_stride = self.out_features * self.in_features / 16;
9872        if self.codes.len() != self.expert_count * code_stride
9873            || self.scales.len() != self.expert_count * scale_stride
9874        {
9875            return Err("NVFP4 bank byte extents do not match the declared geometry".to_string());
9876        }
9877        let matrix = Nvfp4BlockMatrix {
9878            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
9879            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
9880            macro_scale: self.macros[expert],
9881            out_features: self.out_features,
9882            in_features: self.in_features,
9883        };
9884        matrix.validate()?;
9885        Ok(matrix)
9886    }
9887}
9888
9889/// One rank's resident repacked NVFP4 shard: memra block_nvfp4 rows on device.
9890pub struct ResidentNvfp4Rank {
9891    blocks: crate::CudaSlice<u8>,
9892    macro_scale: f32,
9893    out_features: usize,
9894    in_features: usize,
9895    row_bytes: usize,
9896}
9897
9898pub struct ResidentNvfp4ColumnParallel {
9899    ranks: Vec<ResidentNvfp4Rank>,
9900    pub out_features: usize,
9901    pub in_features: usize,
9902}
9903
9904pub struct ResidentNvfp4RowParallel {
9905    ranks: Vec<ResidentNvfp4Rank>,
9906    pub out_features: usize,
9907    pub in_features: usize,
9908}
9909
9910pub struct ResidentTpNvfp4Expert {
9911    gate: ResidentNvfp4ColumnParallel,
9912    up: ResidentNvfp4ColumnParallel,
9913    down: ResidentNvfp4RowParallel,
9914    pub input_width: usize,
9915    pub expert_width: usize,
9916}
9917
9918/// One rank's resident NVFP4 expert bank shard: one repacked block buffer PER expert (per-expert
9919/// device allocations keep this increment off any new strided-kernel API; the strided twin is a
9920/// later perf rung, mirroring the FP8 bank's history).
9921pub struct ResidentNvfp4ColumnBankRank {
9922    /// Contiguous per-rank expert bank: `expert_count` repacked shards of `expert_bytes` each.
9923    /// Contiguity is what lets the device-routes program cover every selected expert with ONE
9924    /// launch (`qmatvec_nvfp4_dp4a_sel` indexes `sel[t] * expert_bytes`).
9925    bank: crate::CudaSlice<u8>,
9926    expert_bytes: usize,
9927    local_out: usize,
9928    in_features: usize,
9929    row_bytes: usize,
9930    /// TRUE when these bytes are the slot-major permutation (`nvfp4_matrix_v2_permute`) and the
9931    /// `_v2` readers must be used; FALSE when they are block_nvfp4 v1. Recorded at BUILD from
9932    /// `ep2 || bank_slot_major_on()` and never re-derived: the layout travels with the pointer,
9933    /// so no reader can consult an env door that disagrees with the resident bytes. Feeding v1
9934    /// bytes to a `_v2` reader (or the reverse) is a garbage-output bug, and the 2026-08-29
9935    /// step37 incident was its neighbour — a piece of layout geometry a caller failed to supply.
9936    slot_major: bool,
9937}
9938
9939impl ResidentNvfp4ColumnBankRank {
9940    /// THE host-canonical reader for this bank, selected from the layout the bank RECORDS. One
9941    /// place maps layout -> reader for the column banks; every oracle goes through it, so a new
9942    /// producer cannot leave a reader behind (the failure mode that put v1 bytes under a `_v2`
9943    /// reader, called out in the `run_tensor_parallel_routes_nvfp4_prime_grouped` receipt).
9944    fn host_canonical_expert(
9945        &self,
9946        engine: &Engine,
9947        expert: usize,
9948        activations: &crate::CudaSlice<f32>,
9949    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
9950        let w = self.expert(expert);
9951        if self.slot_major {
9952            engine.qmatvec_nvfp4_fast_v2(
9953                &w,
9954                activations,
9955                1,
9956                self.in_features,
9957                self.local_out,
9958                self.row_bytes,
9959            )
9960        } else {
9961            engine.qmatvec_nvfp4_fast(
9962                &w,
9963                activations,
9964                1,
9965                self.in_features,
9966                self.local_out,
9967                self.row_bytes,
9968            )
9969        }
9970    }
9971
9972    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
9973        self.bank
9974            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
9975    }
9976}
9977
9978/// Canonical row-shard count for the NVFP4 down projection. The down reduction ALWAYS executes
9979/// as exactly this many input-column windows summed in shard order, at every world size: a
9980/// single full-width dot and a two-half-dots-plus-add differ in f32 parenthesization, so pinning
9981/// the shard grid (not the world size) is what makes the TP1-oracle-vs-TP2 bit gate meaningful.
9982/// This is the NVFP4 twin of the FP8 bank's canonical checkpoint-block reduction.
9983pub const NVFP4_CANONICAL_ROW_SHARDS: usize = 2;
9984
9985pub struct ResidentNvfp4RowBankRank {
9986    /// Contiguous per-shard expert bank (see `ResidentNvfp4ColumnBankRank::bank`).
9987    bank: crate::CudaSlice<u8>,
9988    expert_bytes: usize,
9989    device_rank: usize, // index into the runtime's rank engines this canonical shard lives on
9990    out_features: usize,
9991    local_in: usize,
9992    row_bytes: usize,
9993    /// Slot-major layout marker — see `ResidentNvfp4ColumnBankRank::slot_major`.
9994    slot_major: bool,
9995}
9996
9997impl ResidentNvfp4RowBankRank {
9998    /// THE host-canonical reader for this down shard — see
9999    /// `ResidentNvfp4ColumnBankRank::host_canonical_expert`.
10000    fn host_canonical_expert(
10001        &self,
10002        engine: &Engine,
10003        expert: usize,
10004        activations: &crate::CudaSlice<f32>,
10005    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10006        let w = self.expert(expert);
10007        if self.slot_major {
10008            engine.qmatvec_nvfp4_fast_v2(
10009                &w,
10010                activations,
10011                1,
10012                self.local_in,
10013                self.out_features,
10014                self.row_bytes,
10015            )
10016        } else {
10017            engine.qmatvec_nvfp4_fast(
10018                &w,
10019                activations,
10020                1,
10021                self.local_in,
10022                self.out_features,
10023                self.row_bytes,
10024            )
10025        }
10026    }
10027
10028    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
10029        self.bank
10030            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
10031    }
10032}
10033
10034impl ResidentNvfp4TensorParallel {
10035    pub(crate) fn device_workspace_handle(
10036        &self,
10037    ) -> &std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>> {
10038        &self.device_workspace
10039    }
10040}
10041
10042pub struct ResidentNvfp4TensorParallel {
10043    gate: Vec<ResidentNvfp4ColumnBankRank>,
10044    up: Vec<ResidentNvfp4ColumnBankRank>,
10045    down: Vec<ResidentNvfp4RowBankRank>,
10046    macros_gate: Vec<f32>,
10047    macros_up: Vec<f32>,
10048    macros_down: Vec<f32>,
10049    /// Per-rank device copies of the gate/up macro-scales (E f32 each), indexed by the
10050    /// batched SwiGLU kernel via the selection array. Down macros stay host-side — they fold
10051    /// into the route-weight axpy scalar.
10052    macros_gate_dev: Vec<crate::CudaSlice<f32>>,
10053    macros_up_dev: Vec<crate::CudaSlice<f32>>,
10054    macros_down_dev: Vec<crate::CudaSlice<f32>>,
10055    pub expert_count: usize,
10056    pub input_width: usize,
10057    pub expert_width: usize,
10058    /// Lazily-built persistent decode workspace (device routes program). Interior mutability
10059    /// mirrors StepEpGroupedDecode: the forward holds the bank behind a shared reference.
10060    device_workspace: std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>>,
10061    /// Grouped-prime per-rank slot-major pointer tables (gate/up/down x n_expert), built once.
10062    /// The banks are resident and never move, so rebuilding + re-uploading 3*n_expert u64s per
10063    /// rank per LAYER was pure per-call host churn on the prime path.
10064    prime_tables: std::sync::Mutex<Vec<crate::CudaSlice<u64>>>,
10065    /// MEMRA_STEP_NVFP4_EP2: the rank banks above hold WHOLE experts (owner = id & 1,
10066    /// slot = id >> 1) at full width instead of TP shards. Consumers must branch on this;
10067    /// shard-semantics paths refuse loudly.
10068    pub(crate) ep2: bool,
10069}
10070
10071/// Persistent per-call device buffers for the NVFP4 device routes program: one gate/up output,
10072/// one down partial, and one shard accumulator per rank, plus root combine staging. Reused every
10073/// (token, layer) call so the decode loop performs zero output allocations.
10074/// A stitched multi-device parent graph for one layer's device-routed expert program, plus
10075/// the children it was built from (retained: AddChildGraphNode clones, but the probe retains
10076/// conservatively) and the persistent e-context input staging its copies read.
10077struct RoutesGraph {
10078    exec: cudarc::driver::sys::CUgraphExec,
10079    parent: cudarc::driver::sys::CUgraph,
10080    _children: Vec<cudarc::driver::CudaGraph>,
10081}
10082// SAFETY: the raw handles are only used from the single decode thread; CUDA graph handles are
10083// context-agnostic process handles.
10084unsafe impl Send for RoutesGraph {}
10085
10086impl Drop for RoutesGraph {
10087    fn drop(&mut self) {
10088        unsafe {
10089            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
10090            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
10091        }
10092    }
10093}
10094
10095impl Nvfp4DeviceRoutesWorkspace {
10096    pub(crate) fn in_stage_handle(&self) -> Option<&crate::CudaSlice<f32>> {
10097        self.in_stage_e.as_ref()
10098    }
10099    pub(crate) fn in_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
10100        self.in_stage_e.as_mut()
10101    }
10102    #[allow(dead_code)] // allow: accessor twin of in_stage_mut; kept for the workspace API symmetry
10103    pub(crate) fn out_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
10104        self.out_stage_e.as_mut()
10105    }
10106    /// Arm the e-context stages + router staging pair when absent (token-graph entry).
10107    pub(crate) fn arm_stages(
10108        &mut self,
10109        e: &Engine,
10110        width: usize,
10111        n_sel: usize,
10112    ) -> Result<(), Box<dyn std::error::Error>> {
10113        let _main = e.gpu.enter_main()?;
10114        if self.in_stage_e.is_none() {
10115            self.in_stage_e = Some(e.htod(&vec![0.0f32; width])?);
10116            self.out_stage_e = Some(e.htod(&vec![0.0f32; width])?);
10117        }
10118        if self.dev_route_e.is_none() {
10119            self.dev_route_e = Some((
10120                e.htod_i32(&vec![0i32; n_sel])?,
10121                e.htod(&vec![0.0f32; n_sel])?,
10122            ));
10123        }
10124        Ok(())
10125    }
10126
10127    /// Split-borrow: the routes input (shared) + output (mut) stages together.
10128    pub(crate) fn in_and_out_stages_mut(
10129        &mut self,
10130    ) -> Option<(&crate::CudaSlice<f32>, &mut crate::CudaSlice<f32>)> {
10131        match (self.in_stage_e.as_ref(), self.out_stage_e.as_mut()) {
10132            (Some(input), Some(output)) => Some((input, output)),
10133            _ => None,
10134        }
10135    }
10136    pub(crate) fn dev_route_e_mut(
10137        &mut self,
10138    ) -> Option<(&mut crate::CudaSlice<i32>, &mut crate::CudaSlice<f32>)> {
10139        self.dev_route_e.as_mut().map(|(a, b)| (a, b))
10140    }
10141}
10142
10143pub struct Nvfp4DeviceRoutesWorkspace {
10144    /// [n_sel, local_out] batched gate/up outputs and the SwiGLU q8_1 pair; [n_sel, width]
10145    /// down partials. Sized for `n_sel` selected experts per token (pinned at first call).
10146    gate_out: Vec<crate::CudaSlice<f32>>,
10147    up_out: Vec<crate::CudaSlice<f32>>,
10148    act_q: Vec<crate::CudaSlice<i8>>,
10149    act_d: Vec<crate::CudaSlice<f32>>,
10150    sel: Vec<crate::CudaSlice<i32>>,
10151    partial: Vec<crate::CudaSlice<f32>>,
10152    accumulator: Vec<crate::CudaSlice<f32>>,
10153    /// Per-rank folded combine weights (route_weight x down macro), one htod per call.
10154    combine_w: Vec<crate::CudaSlice<f32>>,
10155    /// Device-routed extension: per-rank raw route weights (the down-macro fold happens
10156    /// in-kernel via sel + macros_down_dev).
10157    route_w: Vec<crate::CudaSlice<f32>>,
10158    /// Persistent q8_1 pair of the shared layer input (one quantize per rank per call, no
10159    /// per-call allocation).
10160    in_q: Vec<crate::CudaSlice<i8>>,
10161    in_d: Vec<crate::CudaSlice<f32>>,
10162    /// e-context staging for the device router outputs (persistent — rank streams peer-read
10163    /// them, so the router's fresh outputs are copied here on e's stream first; the pp.rs
10164    /// never-free discipline).
10165    dev_route_e: Option<(crate::CudaSlice<i32>, crate::CudaSlice<f32>)>,
10166    /// Prestage door state: input pull + quantize already issued for this layer's call
10167    /// (nvfp4_routes_prestage), so the routed run skips them. Reset per call.
10168    prestaged: bool,
10169    /// Peer-router door state: rank1's sel/route_w were computed locally in prestage;
10170    /// the routed run skips rank1's sel pull. Reset per call.
10171    rank1_routed: bool,
10172    /// Doorbell fences (MEMRA_FENCE_MEMOPS): raw cuMemAlloc'd [rank1_flag, root_flag]
10173    /// u32 pair in ROOT memory (async-pool memory is memop-INELIGIBLE — receipted
10174    /// CUDA_ERROR_INVALID_VALUE) + the host-side monotonic ticket. 0 = unarmed.
10175    fence_flags_raw: u64,
10176    fence_ticket: u32,
10177    /// Prestage input fence, recorded on e after the input's producer.
10178    ev_input: Option<(CudaEvent, usize)>,
10179    /// Graph-door staging: persistent e-context input row + output row (fixed addresses the
10180    /// captured copies read/write), and the per-layer stitched parent.
10181    in_stage_e: Option<crate::CudaSlice<f32>>,
10182    out_stage_e: Option<crate::CudaSlice<f32>>,
10183    routes_graph: Option<RoutesGraph>,
10184    /// Token-graph raw pointer sets (armed once by routes_arm_raw).
10185    raw_dev_route_e: Option<(u64, u64)>,
10186    raw_combine: Option<(u64, u64, u64, u64)>,
10187    raw_input: Vec<u64>,
10188    raw_sel: Vec<u64>,
10189    raw_route_w: Vec<u64>,
10190    remote: crate::CudaSlice<f32>,
10191    combined: crate::CudaSlice<f32>,
10192    n_sel: usize,
10193    /// Device-IO extension (lazily built by `run_tensor_parallel_routes_nvfp4_device_io`):
10194    /// persistent per-rank input rows plus the evented ordering pair — the pp.rs
10195    /// BoundarySlot discipline, same as the v2 attention workspace.
10196    input: Vec<crate::CudaSlice<f32>>,
10197    ev_rank: Vec<CudaEvent>,
10198    ev_done: Option<CudaEvent>,
10199    ev_entry: Option<(CudaEvent, usize)>,
10200}
10201
10202/// One rank's whole-expert NVFP4 residency (expert-parallel ownership).
10203struct ResidentNvfp4EpRank {
10204    gate: crate::CudaSlice<u8>,
10205    up: crate::CudaSlice<u8>,
10206    down: crate::CudaSlice<u8>,
10207    gate_expert_bytes: usize,
10208    down_expert_bytes: usize,
10209    macros_gate: crate::CudaSlice<f32>,
10210    macros_up: crate::CudaSlice<f32>,
10211    macros_down: crate::CudaSlice<f32>,
10212    expert_range: Range<usize>,
10213}
10214
10215struct Nvfp4EpDeviceWorkspace {
10216    input: Vec<crate::CudaSlice<f32>>,
10217    input_bf16: Vec<crate::CudaSlice<u8>>,
10218    input_q8: Vec<crate::CudaSlice<i8>>,
10219    input_q8_scales: Vec<crate::CudaSlice<f32>>,
10220    sel: Vec<crate::CudaSlice<i32>>,
10221    token_rows: Vec<crate::CudaSlice<i32>>,
10222    global_pairs: Vec<crate::CudaSlice<i32>>,
10223    route_w: Vec<crate::CudaSlice<f32>>,
10224    gate_out: Vec<crate::CudaSlice<f32>>,
10225    up_out: Vec<crate::CudaSlice<f32>>,
10226    activation_bf16: Vec<crate::CudaSlice<u8>>,
10227    activation_q8: Vec<crate::CudaSlice<i8>>,
10228    activation_q8_scales: Vec<crate::CudaSlice<f32>>,
10229    slot_rows: crate::CudaSlice<f32>,
10230    slot_rows_raw: u64,
10231    route_weights: crate::CudaSlice<f32>,
10232    graph_input: crate::CudaSlice<f32>,
10233    graph_output: crate::CudaSlice<f32>,
10234    graph_routes: Option<(u64, u64)>,
10235    graphs: Vec<Option<RoutesGraph>>,
10236    ev_entry: CudaEvent,
10237    ev_entry_device: usize,
10238    ev_rank: Vec<CudaEvent>,
10239    phase_events: Option<Nvfp4EpPhaseEvents>,
10240    capacity_tokens: usize,
10241    experts_per_token: usize,
10242}
10243
10244struct Nvfp4EpPhaseEvents {
10245    head: Vec<CudaEvent>,
10246    copy_done: Vec<CudaEvent>,
10247    gate_up_done: Vec<CudaEvent>,
10248    activation_done: Vec<CudaEvent>,
10249    down_done: Vec<CudaEvent>,
10250}
10251
10252pub(crate) const NVFP4_EP_DEVICE_BATCH_CAP: usize = 128;
10253pub(crate) const NVFP4_EP_DEVICE_ROUTER_BATCH_CAP: usize = 32;
10254pub(crate) const NVFP4_EP_Q8_BATCH_CAP: usize = 32;
10255const NVFP4_EP_GRAPH_BATCH_CAP: usize = 1;
10256
10257fn nvfp4_ep_active_input_values(
10258    input_values: usize,
10259    tokens: usize,
10260    input_width: usize,
10261) -> Result<usize, String> {
10262    if !(1..=NVFP4_EP_DEVICE_BATCH_CAP).contains(&tokens) {
10263        return Err(format!(
10264            "W4A16 NVFP4 device EP batch {tokens} is outside 1..={NVFP4_EP_DEVICE_BATCH_CAP}"
10265        ));
10266    }
10267    let active_values = tokens
10268        .checked_mul(input_width)
10269        .ok_or("W4A16 NVFP4 device EP active input size overflows usize")?;
10270    if input_values < active_values {
10271        return Err(format!(
10272            "W4A16 NVFP4 device EP input {input_values} is smaller than active \
10273             tokens {tokens} x width {input_width} ({active_values})"
10274        ));
10275    }
10276    Ok(active_values)
10277}
10278
10279pub struct ResidentNvfp4ExpertParallel {
10280    ranks: Vec<ResidentNvfp4EpRank>,
10281    macros_gate: Vec<f32>,
10282    macros_up: Vec<f32>,
10283    macros_down: Vec<f32>,
10284    pub expert_count: usize,
10285    pub input_width: usize,
10286    pub expert_width: usize,
10287    gate_row_bytes: usize,
10288    down_row_bytes: usize,
10289    device_workspace: std::sync::Mutex<Option<Nvfp4EpDeviceWorkspace>>,
10290}
10291
10292fn nvfp4_repack_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
10293    memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
10294        matrix.codes,
10295        matrix.scales,
10296        matrix.out_features,
10297        matrix.in_features,
10298    )
10299}
10300
10301fn nvfp4_row_bytes(in_features: usize) -> usize {
10302    in_features / 64 * 36 // memra block_nvfp4: 64 elems -> 36 bytes (4 UE4M3 + 32 packed e2m1)
10303}
10304
10305/// MEMRA_NO_LOCAL_SHADOW=1: skip the per-layer local-KV shadow gathers and appends in the
10306/// eager v2 decode (lengths still advance) — the graph door proved contents-stale local KV
10307/// is decode-identical (12/12). The local contents feed spec/MTP scratch only.
10308/// MEMRA_FUSE_ROPE_APPEND=1: fuse qk norms + rope + dcw KV append + len inc into one
10309/// launch per rank per layer (bit-identical; identity-gated). dcw path only.
10310pub(crate) fn fuse_rope_append_on() -> bool {
10311    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10312    *ON.get_or_init(|| std::env::var("MEMRA_FUSE_ROPE_APPEND").as_deref() == Ok("1"))
10313}
10314
10315pub(crate) fn no_local_shadow_on() -> bool {
10316    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10317    *ON.get_or_init(|| std::env::var("MEMRA_NO_LOCAL_SHADOW").as_deref() == Ok("1"))
10318}
10319
10320/// Permute one repacked block_nvfp4 matrix (out_features rows of `nvfp4_row_bytes(in_f)`)
10321/// into the slot-major row layout the EP2 kernels read: per row, slot g's 16 qs bytes at
10322/// g*16, then the two UE4M3 scale bytes per slot at nslots*16 + g*2. Row byte count
10323/// unchanged. This layout USED to be an env door (`MEMRA_NVFP4_BANK_V2`, removed 2026-08-29
10324/// after its ON arm changed generated text in serving, see
10325/// research/step37-bankv2-removal-20260829); it survives ONLY as the fixed layout of the
10326/// EP2 whole-expert banks, whose `*_ep` kernels read it unconditionally.
10327///
10328/// PUBLIC because it is the SINGLE SOURCE OF TRUTH for this byte map. Every reader — the
10329/// `*_ep` decode kernels, `kq_fetch<QT_NVFP4_V2>` in the grouped GEMM,
10330/// `dequant_nvfp4v2_f16_kernel` — is defined as "reads what this function writes", and the
10331/// `nvfp4-bank-oracle` bin is what proves it, on device, per kernel arm. Do not reimplement
10332/// the map anywhere: the two failures that appeared only on v2 readers were geometry-plumbing
10333/// bugs around a byte map that was itself correct in two separate places. The layout was
10334/// innocent; one live failure was the grouped-prefill sktail call site defaulting `in_f` to zero.
10335pub fn nvfp4_matrix_v2_permute(v1: &[u8], out_features: usize, in_features: usize) -> Vec<u8> {
10336    // The output row is n_slots*18 bytes; the stride every reader uses is
10337    // nvfp4_row_bytes(in_features) = (in_features/64)*36. Those are equal only when
10338    // in_features is a whole number of 64-element superblocks. At in_features % 64 == 32 the
10339    // permute would silently emit a LONGER row than the stride and every row after row 0
10340    // would be read at the wrong offset, so refuse instead of trusting the caller.
10341    assert_eq!(
10342        in_features % 64,
10343        0,
10344        "v2 permute needs whole 64-element superblocks, got in_features={in_features}"
10345    );
10346    let row_bytes = nvfp4_row_bytes(in_features);
10347    assert_eq!(v1.len(), out_features * row_bytes, "v2 permute geometry");
10348    let n_slots = in_features / 32;
10349    let mut out = Vec::with_capacity(v1.len());
10350    for row in 0..out_features {
10351        let r = &v1[row * row_bytes..(row + 1) * row_bytes];
10352        for g in 0..n_slots {
10353            let (sblk, h) = (g / 2, g % 2);
10354            let b = &r[sblk * 36..sblk * 36 + 36];
10355            out.extend_from_slice(&b[4 + 16 * h..4 + 16 * h + 16]);
10356        }
10357        for g in 0..n_slots {
10358            let (sblk, h) = (g / 2, g % 2);
10359            let b = &r[sblk * 36..sblk * 36 + 36];
10360            out.push(b[2 * h]);
10361            out.push(b[2 * h + 1]);
10362        }
10363    }
10364    out
10365}
10366
10367/// Repack one expert shard for the contiguous banks. `slot_major` is true ONLY for the EP2
10368/// whole-expert banks, whose `*_ep` kernels read the slot-major permutation; the TP
10369/// column/row shard banks stay in the block_nvfp4 v1 layout every other kernel reads.
10370fn nvfp4_repack_bank_matrix(matrix: Nvfp4BlockMatrix<'_>, slot_major: bool) -> Vec<u8> {
10371    let (out_features, in_features) = (matrix.out_features, matrix.in_features);
10372    let v1 = nvfp4_repack_matrix(matrix);
10373    if slot_major {
10374        nvfp4_matrix_v2_permute(&v1, out_features, in_features)
10375    } else {
10376        v1
10377    }
10378}
10379
10380/// Column shard: whole output rows per rank (codes and scales are row-major, so both slices are
10381/// contiguous borrows). The macro rides unchanged — it is applied post-gather by the caller.
10382#[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
10383fn nvfp4_column_shard<'a>(
10384    matrix: Nvfp4BlockMatrix<'a>,
10385    tp: usize,
10386    rank: usize,
10387) -> Result<Nvfp4BlockMatrix<'a>, String> {
10388    if matrix.out_features % tp != 0 {
10389        return Err(format!(
10390            "NVFP4 column-parallel out_features {} is not divisible by TP={tp}",
10391            matrix.out_features
10392        ));
10393    }
10394    let local_out = matrix.out_features / tp;
10395    let code_row = matrix.in_features / 2;
10396    let scale_row = matrix.in_features / 16;
10397    Ok(Nvfp4BlockMatrix {
10398        codes: &matrix.codes[rank * local_out * code_row..(rank + 1) * local_out * code_row],
10399        scales: &matrix.scales[rank * local_out * scale_row..(rank + 1) * local_out * scale_row],
10400        macro_scale: matrix.macro_scale,
10401        out_features: local_out,
10402        in_features: matrix.in_features,
10403    })
10404}
10405
10406/// Row shard: input-column windows per rank, 64-superblock aligned. Owned buffers: each output
10407/// row contributes one contiguous byte window, gathered across rows.
10408#[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
10409fn nvfp4_row_shard(
10410    matrix: Nvfp4BlockMatrix<'_>,
10411    tp: usize,
10412    rank: usize,
10413) -> Result<(Vec<u8>, Vec<u8>, usize), String> {
10414    if matrix.in_features % tp != 0 {
10415        return Err(format!(
10416            "NVFP4 row-parallel in_features {} is not divisible by TP={tp}",
10417            matrix.in_features
10418        ));
10419    }
10420    let local_in = matrix.in_features / tp;
10421    if !local_in.is_multiple_of(64) {
10422        return Err(format!(
10423            "NVFP4 row-parallel input shard {local_in} cuts through a 64-element superblock"
10424        ));
10425    }
10426    let code_row = matrix.in_features / 2;
10427    let scale_row = matrix.in_features / 16;
10428    let local_code = local_in / 2;
10429    let local_scale = local_in / 16;
10430    let mut codes = Vec::with_capacity(matrix.out_features * local_code);
10431    let mut scales = Vec::with_capacity(matrix.out_features * local_scale);
10432    for row in 0..matrix.out_features {
10433        let code_start = row * code_row + rank * local_code;
10434        codes.extend_from_slice(&matrix.codes[code_start..code_start + local_code]);
10435        let scale_start = row * scale_row + rank * local_scale;
10436        scales.extend_from_slice(&matrix.scales[scale_start..scale_start + local_scale]);
10437    }
10438    Ok((codes, scales, local_in))
10439}
10440
10441/// Rank compute leaf: repack modelopt -> block_nvfp4, upload, run the proven dp4a kernel. The
10442/// macro is NOT applied here — callers apply it once at the canonical post-gather/post-reduce
10443/// point (see the section header).
10444fn run_rank_nvfp4(
10445    engine: &Engine,
10446    matrix: Nvfp4BlockMatrix<'_>,
10447    activations: &[f32],
10448    tokens: usize,
10449) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10450    matrix.validate()?;
10451    validate_activations(activations, tokens, matrix.in_features)?;
10452    let _main = engine.gpu.enter_main()?;
10453    let blocks = engine.htod_bytes(&nvfp4_repack_matrix(matrix))?;
10454    let activations = engine.htod(activations)?;
10455    let output = engine.qmatvec_nvfp4_fast(
10456        &blocks.slice(0..blocks.len()),
10457        &activations,
10458        tokens,
10459        matrix.in_features,
10460        matrix.out_features,
10461        nvfp4_row_bytes(matrix.in_features),
10462    )?;
10463    engine.dtoh(&output)
10464}
10465
10466fn upload_rank_nvfp4(
10467    engine: &Engine,
10468    matrix: Nvfp4BlockMatrix<'_>,
10469) -> Result<ResidentNvfp4Rank, Box<dyn std::error::Error>> {
10470    matrix.validate()?;
10471    let _main = engine.gpu.enter_main()?;
10472    Ok(ResidentNvfp4Rank {
10473        blocks: engine.htod_bytes(&nvfp4_repack_matrix(matrix))?,
10474        macro_scale: matrix.macro_scale,
10475        out_features: matrix.out_features,
10476        in_features: matrix.in_features,
10477        row_bytes: nvfp4_row_bytes(matrix.in_features),
10478    })
10479}
10480
10481fn run_resident_rank_nvfp4(
10482    engine: &Engine,
10483    rank: &ResidentNvfp4Rank,
10484    activations: &[f32],
10485    tokens: usize,
10486) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10487    validate_activations(activations, tokens, rank.in_features)?;
10488    let _main = engine.gpu.enter_main()?;
10489    let activations = engine.htod(activations)?;
10490    let output = engine.qmatvec_nvfp4_fast(
10491        &rank.blocks.slice(0..rank.blocks.len()),
10492        &activations,
10493        tokens,
10494        rank.in_features,
10495        rank.out_features,
10496        rank.row_bytes,
10497    )?;
10498    engine.dtoh(&output)
10499}
10500
10501fn apply_macro(values: &mut [f32], macro_scale: f32) {
10502    for value in values.iter_mut() {
10503        *value *= macro_scale;
10504    }
10505}
10506
10507impl TpE4m3HostBounce {
10508    /// Unsharded NVFP4 projection on rank 0 (compatibility oracle). Macro applied post-kernel.
10509    pub fn full_nvfp4(
10510        &self,
10511        matrix: Nvfp4BlockMatrix<'_>,
10512        activations: &[f32],
10513        tokens: usize,
10514    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10515        let mut output = run_rank_nvfp4(&self.ranks[0], matrix, activations, tokens)?;
10516        apply_macro(&mut output, matrix.macro_scale);
10517        Ok(output)
10518    }
10519
10520    /// Column-parallel NVFP4 projection: output rows partition across ranks, host gather in rank
10521    /// order, macro applied ONCE post-gather.
10522    pub fn column_parallel_nvfp4(
10523        &self,
10524        matrix: Nvfp4BlockMatrix<'_>,
10525        activations: &[f32],
10526        tokens: usize,
10527    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
10528        matrix.validate()?;
10529        validate_activations(activations, tokens, matrix.in_features)?;
10530        let tp = self.ranks.len();
10531        let local_out = matrix.out_features / tp;
10532        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
10533        let mut rank_outputs = Vec::with_capacity(tp);
10534        for (rank_index, rank) in self.ranks.iter().enumerate() {
10535            let shard = nvfp4_column_shard(matrix, tp, rank_index)?;
10536            let output = run_rank_nvfp4(rank, shard, activations, tokens)?;
10537            let row_start = rank_index * local_out;
10538            for token in 0..tokens {
10539                gathered[token * matrix.out_features + row_start
10540                    ..token * matrix.out_features + row_start + local_out]
10541                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
10542            }
10543            rank_outputs.push(output);
10544        }
10545        apply_macro(&mut gathered, matrix.macro_scale);
10546        Ok(ColumnParallelResult {
10547            gathered,
10548            rank_outputs,
10549        })
10550    }
10551
10552    /// Row-parallel NVFP4 projection: input columns partition at 64-superblock boundaries,
10553    /// rank-local partials reduce in stable rank order, macro applied ONCE post-reduce.
10554    pub fn row_parallel_nvfp4(
10555        &self,
10556        matrix: Nvfp4BlockMatrix<'_>,
10557        activations: &[f32],
10558        tokens: usize,
10559    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
10560        matrix.validate()?;
10561        validate_activations(activations, tokens, matrix.in_features)?;
10562        let tp = self.ranks.len();
10563        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
10564        let mut rank_partials = Vec::with_capacity(tp);
10565        for (rank_index, rank) in self.ranks.iter().enumerate() {
10566            let (codes, scales, local_in) = nvfp4_row_shard(matrix, tp, rank_index)?;
10567            let local_activations =
10568                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
10569            let shard = Nvfp4BlockMatrix {
10570                codes: &codes,
10571                scales: &scales,
10572                macro_scale: matrix.macro_scale,
10573                out_features: matrix.out_features,
10574                in_features: local_in,
10575            };
10576            let partial = run_rank_nvfp4(rank, shard, &local_activations, tokens)?;
10577            for (sum, value) in reduced.iter_mut().zip(&partial) {
10578                *sum += *value;
10579            }
10580            rank_partials.push(partial);
10581        }
10582        apply_macro(&mut reduced, matrix.macro_scale);
10583        Ok(RowParallelResult {
10584            reduced,
10585            rank_partials,
10586        })
10587    }
10588
10589    pub fn upload_expert_nvfp4(
10590        &self,
10591        gate: Nvfp4BlockMatrix<'_>,
10592        up: Nvfp4BlockMatrix<'_>,
10593        down: Nvfp4BlockMatrix<'_>,
10594    ) -> Result<ResidentTpNvfp4Expert, Box<dyn std::error::Error>> {
10595        if gate.in_features != up.in_features || gate.out_features != up.out_features {
10596            return Err("NVFP4 TP expert gate/up dimensions differ".into());
10597        }
10598        if down.in_features != gate.out_features || down.out_features != gate.in_features {
10599            return Err(format!(
10600                "NVFP4 TP expert down {}x{} does not invert gate/up {}x{}",
10601                down.out_features, down.in_features, gate.out_features, gate.in_features
10602            )
10603            .into());
10604        }
10605        let tp = self.ranks.len();
10606        let mut gate_ranks = Vec::with_capacity(tp);
10607        let mut up_ranks = Vec::with_capacity(tp);
10608        let mut down_ranks = Vec::with_capacity(tp);
10609        for (rank_index, engine) in self.ranks.iter().enumerate() {
10610            gate_ranks.push(upload_rank_nvfp4(
10611                engine,
10612                nvfp4_column_shard(gate, tp, rank_index)?,
10613            )?);
10614            up_ranks.push(upload_rank_nvfp4(
10615                engine,
10616                nvfp4_column_shard(up, tp, rank_index)?,
10617            )?);
10618            let (codes, scales, local_in) = nvfp4_row_shard(down, tp, rank_index)?;
10619            down_ranks.push(upload_rank_nvfp4(
10620                engine,
10621                Nvfp4BlockMatrix {
10622                    codes: &codes,
10623                    scales: &scales,
10624                    macro_scale: down.macro_scale,
10625                    out_features: down.out_features,
10626                    in_features: local_in,
10627                },
10628            )?);
10629        }
10630        Ok(ResidentTpNvfp4Expert {
10631            gate: ResidentNvfp4ColumnParallel {
10632                ranks: gate_ranks,
10633                out_features: gate.out_features,
10634                in_features: gate.in_features,
10635            },
10636            up: ResidentNvfp4ColumnParallel {
10637                ranks: up_ranks,
10638                out_features: up.out_features,
10639                in_features: up.in_features,
10640            },
10641            down: ResidentNvfp4RowParallel {
10642                ranks: down_ranks,
10643                out_features: down.out_features,
10644                in_features: down.in_features,
10645            },
10646            input_width: gate.in_features,
10647            expert_width: gate.out_features,
10648        })
10649    }
10650
10651    fn column_parallel_resident_nvfp4(
10652        &self,
10653        matrix: &ResidentNvfp4ColumnParallel,
10654        activations: &[f32],
10655        tokens: usize,
10656    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10657        validate_activations(activations, tokens, matrix.in_features)?;
10658        let local_out = matrix.out_features / self.ranks.len();
10659        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
10660        let mut macro_scale = None;
10661        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
10662            let output = run_resident_rank_nvfp4(engine, shard, activations, tokens)?;
10663            let row_start = rank_index * local_out;
10664            for token in 0..tokens {
10665                gathered[token * matrix.out_features + row_start
10666                    ..token * matrix.out_features + row_start + local_out]
10667                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
10668            }
10669            macro_scale = Some(shard.macro_scale);
10670        }
10671        apply_macro(
10672            &mut gathered,
10673            macro_scale.ok_or("NVFP4 column-parallel matrix has no ranks")?,
10674        );
10675        Ok(gathered)
10676    }
10677
10678    fn row_parallel_resident_nvfp4(
10679        &self,
10680        matrix: &ResidentNvfp4RowParallel,
10681        activations: &[f32],
10682        tokens: usize,
10683    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10684        validate_activations(activations, tokens, matrix.in_features)?;
10685        let tp = self.ranks.len();
10686        let local_in = matrix.in_features / tp;
10687        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
10688        let mut macro_scale = None;
10689        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
10690            if shard.in_features != local_in {
10691                return Err(format!(
10692                    "NVFP4 resident row shard in_features {} != expected {local_in}",
10693                    shard.in_features
10694                )
10695                .into());
10696            }
10697            let local_activations =
10698                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
10699            let partial = run_resident_rank_nvfp4(engine, shard, &local_activations, tokens)?;
10700            for (sum, value) in reduced.iter_mut().zip(&partial) {
10701                *sum += *value;
10702            }
10703            macro_scale = Some(shard.macro_scale);
10704        }
10705        apply_macro(
10706            &mut reduced,
10707            macro_scale.ok_or("NVFP4 row-parallel matrix has no ranks")?,
10708        );
10709        Ok(reduced)
10710    }
10711
10712    pub fn run_expert_nvfp4(
10713        &self,
10714        expert: &ResidentTpNvfp4Expert,
10715        input: &[f32],
10716        tokens: usize,
10717    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10718        validate_activations(input, tokens, expert.input_width)?;
10719        let gate = self.column_parallel_resident_nvfp4(&expert.gate, input, tokens)?;
10720        let up = self.column_parallel_resident_nvfp4(&expert.up, input, tokens)?;
10721        let activated: Vec<f32> = gate
10722            .iter()
10723            .zip(&up)
10724            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
10725            .collect();
10726        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
10727        self.row_parallel_resident_nvfp4(&expert.down, &activated, tokens)
10728    }
10729
10730    /// Upload every expert's TP shards resident (one repacked block buffer per expert per rank).
10731    #[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
10732    pub fn upload_tensor_parallel_nvfp4(
10733        &self,
10734        gate: Nvfp4ExpertBank<'_>,
10735        up: Nvfp4ExpertBank<'_>,
10736        down: Nvfp4ExpertBank<'_>,
10737    ) -> Result<ResidentNvfp4TensorParallel, Box<dyn std::error::Error>> {
10738        gate.validate()?;
10739        up.validate()?;
10740        down.validate()?;
10741        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
10742            return Err("NVFP4 TP gate/up/down expert counts differ".into());
10743        }
10744        if gate.in_features != up.in_features || gate.out_features != up.out_features {
10745            return Err("NVFP4 TP gate/up dimensions differ".into());
10746        }
10747        if down.in_features != gate.out_features || down.out_features != gate.in_features {
10748            return Err(format!(
10749                "NVFP4 TP down {}x{} does not invert gate/up {}x{}",
10750                down.out_features, down.in_features, gate.out_features, gate.in_features
10751            )
10752            .into());
10753        }
10754        let tp = self.ranks.len();
10755        if gate.out_features % tp != 0 {
10756            return Err(format!(
10757                "NVFP4 TP expert output width {} is not divisible by TP={tp}",
10758                gate.out_features
10759            )
10760            .into());
10761        }
10762        if !down.in_features.is_multiple_of(NVFP4_CANONICAL_ROW_SHARDS)
10763            || !(down.in_features / NVFP4_CANONICAL_ROW_SHARDS).is_multiple_of(64)
10764        {
10765            return Err(format!(
10766                "NVFP4 TP expert input width {} does not split into 64-aligned canonical \
10767                 shards ({NVFP4_CANONICAL_ROW_SHARDS})",
10768                down.in_features
10769            )
10770            .into());
10771        }
10772        if tp > NVFP4_CANONICAL_ROW_SHARDS {
10773            return Err(format!(
10774                "NVFP4 TP world {tp} exceeds the canonical row-shard grid \
10775                 ({NVFP4_CANONICAL_ROW_SHARDS})"
10776            )
10777            .into());
10778        }
10779
10780        let ep2 = step_nvfp4_ep2_on() && tp == 2;
10781        // LAYOUT DECISION, MADE ONCE PER BANK BUILD. EP2 whole-expert banks are ALWAYS
10782        // slot-major (their `*_ep` kernels read that mapping unconditionally); TP shard banks
10783        // are slot-major only under PROGRAM 1's door. Every reader below takes this from the
10784        // bank it is reading, never from `bank_slot_major_on()` again.
10785        let slot_major = ep2 || bank_slot_major_on();
10786        // ENGAGEMENT RECEIPT, not a debug line. A pricing cell that proves only that the env var
10787        // is SET measures nothing: if the door fails to reach the code, the cell reports "the
10788        // program is worth 0%" when the truth is "the program never ran". That exact defect is
10789        // banked -- the MEMRA_BF16_MMV lane's first sweep grepped for engagement, got 0 in BOTH
10790        // arms, and the missing line was mistaken for a no-engagement result until an announce
10791        // was added. So the layout decision announces itself, WITH ITS SOURCE, so a receipt can
10792        // distinguish "armed by the door" from "armed because EP2" from "not armed".
10793        eprintln!(
10794            "[nvfp4-bank] layout={} source={} tp={tp} experts={} in_f={} out_f={}",
10795            if slot_major {
10796                "slot-major"
10797            } else {
10798                "block-nvfp4-v1"
10799            },
10800            // The source string distinguishes "armed by the 2026-09-01 DEFAULT" from "armed by
10801            // an explicit recipe" from "rolled back by the seam" from "armed because EP2". A
10802            // default flip whose receipt cannot say which of those happened cannot prove the
10803            // DEFAULT was what got measured.
10804            if ep2 {
10805                "ep2-always"
10806            } else {
10807                bank_slot_major_source().1
10808            },
10809            gate.expert_count,
10810            gate.in_features,
10811            gate.out_features
10812        );
10813        let mut gate_ranks = Vec::with_capacity(tp);
10814        let mut up_ranks = Vec::with_capacity(tp);
10815        let mut macros_gate_dev = Vec::with_capacity(tp);
10816        let mut macros_up_dev = Vec::with_capacity(tp);
10817        let mut macros_down_dev = Vec::with_capacity(tp);
10818        for (rank_index, engine) in self.ranks.iter().enumerate() {
10819            let _main = engine.gpu.enter_main()?;
10820            // Contiguous per-rank banks: repack every expert shard into one host buffer, one
10821            // upload. Contiguity feeds the batched selected-experts launch; per-expert bytes
10822            // are unchanged (same repack).
10823            // EP2: this rank holds the FULL matrices of the experts it owns (id & 1 ==
10824            // rank_index), stacked at slot id >> 1 — same total bytes as the shard bank.
10825            let mut gate_host: Vec<u8> = Vec::new();
10826            let mut up_host: Vec<u8> = Vec::new();
10827            let mut owned = 0usize;
10828            for expert in 0..gate.expert_count {
10829                if ep2 {
10830                    if expert % 2 != rank_index {
10831                        continue;
10832                    }
10833                    owned += 1;
10834                    gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(
10835                        gate.expert(expert)?,
10836                        slot_major,
10837                    ));
10838                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(
10839                        up.expert(expert)?,
10840                        slot_major,
10841                    ));
10842                } else {
10843                    let gate_shard = nvfp4_column_shard(gate.expert(expert)?, tp, rank_index)?;
10844                    gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate_shard, slot_major));
10845                    let up_shard = nvfp4_column_shard(up.expert(expert)?, tp, rank_index)?;
10846                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up_shard, slot_major));
10847                }
10848            }
10849            let bank_experts = if ep2 { owned } else { gate.expert_count };
10850            let gate_expert_bytes = gate_host.len() / bank_experts.max(1);
10851            let up_expert_bytes = up_host.len() / bank_experts.max(1);
10852            let local_out = if ep2 {
10853                gate.out_features
10854            } else {
10855                gate.out_features / tp
10856            };
10857            gate_ranks.push(ResidentNvfp4ColumnBankRank {
10858                bank: engine.htod_bytes(&gate_host)?,
10859                expert_bytes: gate_expert_bytes,
10860                local_out,
10861                in_features: gate.in_features,
10862                row_bytes: nvfp4_row_bytes(gate.in_features),
10863                slot_major,
10864            });
10865            up_ranks.push(ResidentNvfp4ColumnBankRank {
10866                bank: engine.htod_bytes(&up_host)?,
10867                expert_bytes: up_expert_bytes,
10868                local_out,
10869                in_features: up.in_features,
10870                row_bytes: nvfp4_row_bytes(up.in_features),
10871                slot_major,
10872            });
10873            macros_gate_dev.push(engine.htod(gate.macros)?);
10874            macros_up_dev.push(engine.htod(up.macros)?);
10875            macros_down_dev.push(engine.htod(down.macros)?);
10876        }
10877        // Down: canonical shard grid, NOT the world size (see NVFP4_CANONICAL_ROW_SHARDS).
10878        // Shard s lives on rank s % world, so TP1 holds both shards and TP2 one each, while the
10879        // execution and reduction order stay identical.
10880        let mut down_ranks = Vec::with_capacity(NVFP4_CANONICAL_ROW_SHARDS);
10881        for shard_index in 0..NVFP4_CANONICAL_ROW_SHARDS {
10882            let device_rank = shard_index % tp;
10883            let engine = &self.ranks[device_rank];
10884            let _main = engine.gpu.enter_main()?;
10885            let mut down_host: Vec<u8> = Vec::new();
10886            let mut owned = 0usize;
10887            for expert in 0..down.expert_count {
10888                let down_matrix = down.expert(expert)?;
10889                if ep2 {
10890                    // EP2: shard_index doubles as the owner rank; full-width down matrices
10891                    // of the owned experts, stacked at slot id >> 1.
10892                    if expert % 2 != device_rank {
10893                        continue;
10894                    }
10895                    owned += 1;
10896                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(down_matrix, slot_major));
10897                } else {
10898                    let (codes, scales, local_in) =
10899                        nvfp4_row_shard(down_matrix, NVFP4_CANONICAL_ROW_SHARDS, shard_index)?;
10900                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(
10901                        Nvfp4BlockMatrix {
10902                            codes: &codes,
10903                            scales: &scales,
10904                            macro_scale: down_matrix.macro_scale,
10905                            out_features: down_matrix.out_features,
10906                            in_features: local_in,
10907                        },
10908                        slot_major,
10909                    ));
10910                }
10911            }
10912            let bank_experts = if ep2 { owned } else { down.expert_count };
10913            let down_expert_bytes = down_host.len() / bank_experts.max(1);
10914            let local_in = if ep2 {
10915                down.in_features
10916            } else {
10917                down.in_features / NVFP4_CANONICAL_ROW_SHARDS
10918            };
10919            down_ranks.push(ResidentNvfp4RowBankRank {
10920                bank: engine.htod_bytes(&down_host)?,
10921                expert_bytes: down_expert_bytes,
10922                device_rank,
10923                out_features: down.out_features,
10924                local_in,
10925                row_bytes: nvfp4_row_bytes(local_in),
10926                slot_major,
10927            });
10928        }
10929        Ok(ResidentNvfp4TensorParallel {
10930            gate: gate_ranks,
10931            up: up_ranks,
10932            down: down_ranks,
10933            macros_gate: gate.macros.to_vec(),
10934            macros_up: up.macros.to_vec(),
10935            macros_down: down.macros.to_vec(),
10936            macros_gate_dev,
10937            macros_up_dev,
10938            macros_down_dev,
10939            expert_count: gate.expert_count,
10940            input_width: gate.in_features,
10941            expert_width: gate.out_features,
10942            device_workspace: std::sync::Mutex::new(None),
10943            prime_tables: std::sync::Mutex::new(Vec::new()),
10944            ep2,
10945        })
10946    }
10947
10948    /// EP2 host-canonical: the whole expert executes on its owning rank at full width
10949    /// (owner = expert & 1, bank slot = expert >> 1). Per-row program == the column-bank
10950    /// path's kernel, so gate/up are bit-equal to the TP layout.
10951    fn run_full_bank_expert_nvfp4(
10952        &self,
10953        ranks: &[ResidentNvfp4ColumnBankRank],
10954        macros: &[f32],
10955        expert: usize,
10956        input: &[f32],
10957    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10958        let owner = expert & 1;
10959        let slot = expert >> 1;
10960        let bank = ranks
10961            .get(owner)
10962            .ok_or("NVFP4 EP2 column bank missing owner rank")?;
10963        let engine = &self.ranks[owner];
10964        let _main = engine.gpu.enter_main()?;
10965        let activations = engine.htod(input)?;
10966        let output = bank.host_canonical_expert(engine, slot, &activations)?;
10967        let mut out = engine.dtoh(&output)?;
10968        apply_macro(&mut out, macros[expert]);
10969        Ok(out)
10970    }
10971
10972    /// EP2 host-canonical down: one full-width dot on the owner (NUMERIC-CLASS vs the
10973    /// canonical 2-shard sum — the parenthesization this door declares).
10974    fn run_full_down_expert_nvfp4(
10975        &self,
10976        shards: &[ResidentNvfp4RowBankRank],
10977        macros: &[f32],
10978        expert: usize,
10979        input: &[f32],
10980    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10981        let owner = expert & 1;
10982        let slot = expert >> 1;
10983        let shard = shards
10984            .get(owner)
10985            .ok_or("NVFP4 EP2 down bank missing owner rank")?;
10986        let engine = &self.ranks[owner];
10987        let _main = engine.gpu.enter_main()?;
10988        let activations = engine.htod(input)?;
10989        let output = shard.host_canonical_expert(engine, slot, &activations)?;
10990        let mut out = engine.dtoh(&output)?;
10991        apply_macro(&mut out, macros[expert]);
10992        Ok(out)
10993    }
10994
10995    fn run_column_bank_expert_nvfp4(
10996        &self,
10997        ranks: &[ResidentNvfp4ColumnBankRank],
10998        macros: &[f32],
10999        expert: usize,
11000        input: &[f32],
11001    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
11002        let local_out = ranks
11003            .first()
11004            .ok_or("NVFP4 TP column bank has no ranks")?
11005            .local_out;
11006        let mut gathered = vec![0.0f32; local_out * ranks.len()];
11007        for (rank_index, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
11008            let _main = engine.gpu.enter_main()?;
11009            let activations = engine.htod(input)?;
11010            let output = bank.host_canonical_expert(engine, expert, &activations)?;
11011            let output = engine.dtoh(&output)?;
11012            gathered[rank_index * local_out..(rank_index + 1) * local_out].copy_from_slice(&output);
11013        }
11014        apply_macro(&mut gathered, macros[expert]);
11015        Ok(gathered)
11016    }
11017
11018    /// Canonical-shard row reduction: iterate the FIXED shard grid in shard order (each shard
11019    /// executes on its owning rank engine), so the reduction parenthesization is identical at
11020    /// every world size — that identity is what the TP1-oracle-vs-TP2 bit gate proves.
11021    fn run_row_bank_expert_nvfp4(
11022        &self,
11023        shards: &[ResidentNvfp4RowBankRank],
11024        macros: &[f32],
11025        expert: usize,
11026        input: &[f32],
11027    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
11028        let out_features = shards
11029            .first()
11030            .ok_or("NVFP4 TP row bank has no canonical shards")?
11031            .out_features;
11032        let in_features = shards.iter().map(|shard| shard.local_in).sum::<usize>();
11033        let mut reduced = vec![0.0f32; out_features];
11034        for (shard_index, shard) in shards.iter().enumerate() {
11035            let engine = self
11036                .ranks
11037                .get(shard.device_rank)
11038                .ok_or("NVFP4 canonical shard names a rank outside this runtime")?;
11039            let _main = engine.gpu.enter_main()?;
11040            let local_activations =
11041                activation_shard(input, 1, in_features, shards.len(), shard_index);
11042            let activations = engine.htod(&local_activations)?;
11043            let output = shard.host_canonical_expert(engine, expert, &activations)?;
11044            let partial = engine.dtoh(&output)?;
11045            for (sum, value) in reduced.iter_mut().zip(&partial) {
11046                *sum += *value;
11047            }
11048        }
11049        apply_macro(&mut reduced, macros[expert]);
11050        Ok(reduced)
11051    }
11052
11053    /// Upload whole experts per owning rank (NVFP4 expert-parallel: the layout the clamped tail
11054    /// layers require — clamp semantics do not distribute across a tensor shard). Each owned
11055    /// expert keeps its full gate/up/down as one repacked block buffer on its owner.
11056    #[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
11057    pub fn upload_expert_parallel_nvfp4(
11058        &self,
11059        gate: Nvfp4ExpertBank<'_>,
11060        up: Nvfp4ExpertBank<'_>,
11061        down: Nvfp4ExpertBank<'_>,
11062    ) -> Result<ResidentNvfp4ExpertParallel, Box<dyn std::error::Error>> {
11063        gate.validate()?;
11064        up.validate()?;
11065        down.validate()?;
11066        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
11067            return Err("NVFP4 EP gate/up/down expert counts differ".into());
11068        }
11069        if gate.in_features != up.in_features || gate.out_features != up.out_features {
11070            return Err("NVFP4 EP gate/up dimensions differ".into());
11071        }
11072        if down.in_features != gate.out_features || down.out_features != gate.in_features {
11073            return Err(format!(
11074                "NVFP4 EP down {}x{} does not invert gate/up {}x{}",
11075                down.out_features, down.in_features, gate.out_features, gate.in_features
11076            )
11077            .into());
11078        }
11079        let world = self.ranks.len();
11080        if gate.expert_count % world != 0 {
11081            return Err(format!(
11082                "NVFP4 EP expert count {} is not divisible by {world} ranks",
11083                gate.expert_count
11084            )
11085            .into());
11086        }
11087        let experts_per_rank = gate.expert_count / world;
11088        let mut ranks = Vec::with_capacity(world);
11089        for (rank_index, engine) in self.ranks.iter().enumerate() {
11090            let _main = engine.gpu.enter_main()?;
11091            let expert_range = rank_index * experts_per_rank..(rank_index + 1) * experts_per_rank;
11092            let mut gate_host = Vec::new();
11093            let mut up_host = Vec::new();
11094            let mut down_host = Vec::new();
11095            for expert in expert_range.clone() {
11096                gate_host.extend_from_slice(&nvfp4_repack_matrix(gate.expert(expert)?));
11097                up_host.extend_from_slice(&nvfp4_repack_matrix(up.expert(expert)?));
11098                down_host.extend_from_slice(&nvfp4_repack_matrix(down.expert(expert)?));
11099            }
11100            let gate_expert_bytes = gate_host.len() / experts_per_rank;
11101            let up_expert_bytes = up_host.len() / experts_per_rank;
11102            if gate_expert_bytes != up_expert_bytes {
11103                return Err("NVFP4 EP gate/up packed expert bytes differ".into());
11104            }
11105            let down_expert_bytes = down_host.len() / experts_per_rank;
11106            ranks.push(ResidentNvfp4EpRank {
11107                gate: engine.htod_bytes(&gate_host)?,
11108                up: engine.htod_bytes(&up_host)?,
11109                down: engine.htod_bytes(&down_host)?,
11110                gate_expert_bytes,
11111                down_expert_bytes,
11112                macros_gate: engine.htod(&gate.macros[expert_range.clone()])?,
11113                macros_up: engine.htod(&up.macros[expert_range.clone()])?,
11114                macros_down: engine.htod(&down.macros[expert_range.clone()])?,
11115                expert_range,
11116            });
11117        }
11118        Ok(ResidentNvfp4ExpertParallel {
11119            ranks,
11120            macros_gate: gate.macros.to_vec(),
11121            macros_up: up.macros.to_vec(),
11122            macros_down: down.macros.to_vec(),
11123            expert_count: gate.expert_count,
11124            input_width: gate.in_features,
11125            expert_width: gate.out_features,
11126            gate_row_bytes: nvfp4_row_bytes(gate.in_features),
11127            down_row_bytes: nvfp4_row_bytes(down.in_features),
11128            device_workspace: std::sync::Mutex::new(None),
11129        })
11130    }
11131
11132    /// Upload an already-normalized NVFP4 expert bank.
11133    ///
11134    /// `HostExps` is the physical-format boundary: stacked checkpoint tensors, gathered
11135    /// per-expert tensors, and manifest-backed overlays all become the same contiguous
11136    /// block_nvfp4 expert representation before the parallel backend sees them.
11137    pub fn upload_expert_parallel_nvfp4_normalized(
11138        &self,
11139        gate: &crate::model::HostExps,
11140        up: &crate::model::HostExps,
11141        down: &crate::model::HostExps,
11142    ) -> Result<ResidentNvfp4ExpertParallel, Box<dyn std::error::Error>> {
11143        for (label, bank) in [("gate", gate), ("up", up), ("down", down)] {
11144            if bank.qtype != crate::QT_NVFP4 || !bank.is_uniform_layout() {
11145                return Err(format!(
11146                    "NVFP4 EP normalized {label} bank requires one uniform NVFP4 layout, \
11147                     got qtype={} uniform={}",
11148                    bank.qtype,
11149                    bank.is_uniform_layout()
11150                )
11151                .into());
11152            }
11153            if bank.n_expert == 0
11154                || bank.expert_stride != bank.out_f * bank.row_bytes
11155                || (0..bank.n_expert)
11156                    .any(|expert| bank.expert_bytes(expert).len() != bank.expert_stride)
11157            {
11158                return Err(format!("NVFP4 EP normalized {label} bank geometry is invalid").into());
11159            }
11160        }
11161        if gate.n_expert != up.n_expert || gate.n_expert != down.n_expert {
11162            return Err("NVFP4 EP normalized gate/up/down expert counts differ".into());
11163        }
11164        if gate.in_f != up.in_f || gate.out_f != up.out_f {
11165            return Err("NVFP4 EP normalized gate/up dimensions differ".into());
11166        }
11167        if down.in_f != gate.out_f || down.out_f != gate.in_f {
11168            return Err(format!(
11169                "NVFP4 EP normalized down {}x{} does not invert gate/up {}x{}",
11170                down.out_f, down.in_f, gate.out_f, gate.in_f
11171            )
11172            .into());
11173        }
11174        let macros = |bank: &crate::model::HostExps| -> Result<Vec<f32>, String> {
11175            let values = bank
11176                .macros
11177                .clone()
11178                .unwrap_or_else(|| vec![1.0; bank.n_expert]);
11179            if values.len() != bank.n_expert
11180                || !values.iter().all(|value| value.is_finite() && *value > 0.0)
11181            {
11182                return Err("NVFP4 EP normalized macro row is not finite-positive".to_string());
11183            }
11184            Ok(values)
11185        };
11186        let macros_gate = macros(gate)?;
11187        let macros_up = macros(up)?;
11188        let macros_down = macros(down)?;
11189        let world = self.ranks.len();
11190        if !gate.n_expert.is_multiple_of(world) {
11191            return Err(format!(
11192                "NVFP4 EP normalized expert count {} is not divisible by {world} ranks",
11193                gate.n_expert
11194            )
11195            .into());
11196        }
11197        let experts_per_rank = gate.n_expert / world;
11198        let mut ranks = Vec::with_capacity(world);
11199        for (rank_index, engine) in self.ranks.iter().enumerate() {
11200            let _main = engine.gpu.enter_main()?;
11201            let expert_range = rank_index * experts_per_rank..(rank_index + 1) * experts_per_rank;
11202            let mut gate_host = Vec::with_capacity(experts_per_rank * gate.expert_stride);
11203            let mut up_host = Vec::with_capacity(experts_per_rank * up.expert_stride);
11204            let mut down_host = Vec::with_capacity(experts_per_rank * down.expert_stride);
11205            for expert in expert_range.clone() {
11206                gate_host.extend_from_slice(gate.expert_bytes(expert));
11207                up_host.extend_from_slice(up.expert_bytes(expert));
11208                down_host.extend_from_slice(down.expert_bytes(expert));
11209            }
11210            ranks.push(ResidentNvfp4EpRank {
11211                gate: engine.htod_bytes(&gate_host)?,
11212                up: engine.htod_bytes(&up_host)?,
11213                down: engine.htod_bytes(&down_host)?,
11214                gate_expert_bytes: gate.expert_stride,
11215                down_expert_bytes: down.expert_stride,
11216                macros_gate: engine.htod(&macros_gate[expert_range.clone()])?,
11217                macros_up: engine.htod(&macros_up[expert_range.clone()])?,
11218                macros_down: engine.htod(&macros_down[expert_range.clone()])?,
11219                expert_range,
11220            });
11221        }
11222        Ok(ResidentNvfp4ExpertParallel {
11223            ranks,
11224            macros_gate,
11225            macros_up,
11226            macros_down,
11227            expert_count: gate.n_expert,
11228            input_width: gate.in_f,
11229            expert_width: gate.out_f,
11230            gate_row_bytes: gate.row_bytes,
11231            down_row_bytes: down.row_bytes,
11232            device_workspace: std::sync::Mutex::new(None),
11233        })
11234    }
11235
11236    /// Routed NVFP4 expert-parallel program, host-canonical: every selected expert executes WHOLE
11237    /// on its owning rank (gate -> up -> clamped-or-plain SwiGLU on host -> down), each projection
11238    /// macro applied once post-kernel, route-weighted accumulate on the host in slot order. The
11239    /// activation uses `step_expert_activation_host`, so the clamped tail layers keep the official
11240    /// contract. Exactness-first; no throughput claim.
11241    #[allow(clippy::too_many_arguments)]
11242    pub fn run_routed_experts_nvfp4(
11243        &self,
11244        experts: &ResidentNvfp4ExpertParallel,
11245        input: &[f32],
11246        tokens: usize,
11247        selected: &[usize],
11248        route_weights: &[f32],
11249        experts_per_token: usize,
11250        activation_limit: Option<f32>,
11251    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
11252        validate_activations(input, tokens, experts.input_width)?;
11253        let pairs = tokens
11254            .checked_mul(experts_per_token)
11255            .ok_or("NVFP4 EP route count overflow")?;
11256        if selected.len() != pairs || route_weights.len() != pairs {
11257            return Err(format!(
11258                "NVFP4 EP routes selected={} weights={} != tokens {tokens} x experts/token \
11259                 {experts_per_token} ({pairs})",
11260                selected.len(),
11261                route_weights.len(),
11262            )
11263            .into());
11264        }
11265        if !route_weights.iter().all(|weight| weight.is_finite()) {
11266            return Err("NVFP4 EP route weights contain a non-finite value".into());
11267        }
11268        let experts_per_rank = experts.expert_count / experts.ranks.len();
11269        let mut output = vec![0.0f32; tokens * experts.input_width];
11270        for token in 0..tokens {
11271            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
11272            for slot in 0..experts_per_token {
11273                let pair = token * experts_per_token + slot;
11274                let expert = selected[pair];
11275                if expert >= experts.expert_count {
11276                    return Err(format!(
11277                        "NVFP4 EP selected expert {expert} outside 0..{}",
11278                        experts.expert_count
11279                    )
11280                    .into());
11281                }
11282                let owner = expert / experts_per_rank;
11283                let local = expert - owner * experts_per_rank;
11284                let rank = &experts.ranks[owner];
11285                let engine = &self.ranks[owner];
11286                let _main = engine.gpu.enter_main()?;
11287                let device_input = engine.htod(input_row)?;
11288                let gate_out = engine.qmatvec_nvfp4_fast(
11289                    &rank.gate.slice(
11290                        local * rank.gate_expert_bytes..(local + 1) * rank.gate_expert_bytes,
11291                    ),
11292                    &device_input,
11293                    1,
11294                    experts.input_width,
11295                    experts.expert_width,
11296                    experts.gate_row_bytes,
11297                )?;
11298                let up_out = engine.qmatvec_nvfp4_fast(
11299                    &rank.up.slice(
11300                        local * rank.gate_expert_bytes..(local + 1) * rank.gate_expert_bytes,
11301                    ),
11302                    &device_input,
11303                    1,
11304                    experts.input_width,
11305                    experts.expert_width,
11306                    experts.gate_row_bytes,
11307                )?;
11308                let mut gate_host = engine.dtoh(&gate_out)?;
11309                let mut up_host = engine.dtoh(&up_out)?;
11310                apply_macro(&mut gate_host, experts.macros_gate[expert]);
11311                apply_macro(&mut up_host, experts.macros_up[expert]);
11312                let activated: Vec<f32> = gate_host
11313                    .iter()
11314                    .zip(&up_host)
11315                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
11316                    .collect();
11317                let device_activated = engine.htod(&activated)?;
11318                let down_out = engine.qmatvec_nvfp4_fast(
11319                    &rank.down.slice(
11320                        local * rank.down_expert_bytes..(local + 1) * rank.down_expert_bytes,
11321                    ),
11322                    &device_activated,
11323                    1,
11324                    experts.expert_width,
11325                    experts.input_width,
11326                    experts.down_row_bytes,
11327                )?;
11328                let mut down_host = engine.dtoh(&down_out)?;
11329                apply_macro(&mut down_host, experts.macros_down[expert]);
11330                let weight = route_weights[pair];
11331                for (sum, value) in output
11332                    [token * experts.input_width..(token + 1) * experts.input_width]
11333                    .iter_mut()
11334                    .zip(down_host)
11335                {
11336                    *sum += weight * value;
11337                }
11338            }
11339        }
11340        Ok(output)
11341    }
11342
11343    /// Device-resident W4A16 expert parallelism for one scheduler/prefill batch (1..=128 rows).
11344    ///
11345    /// The host router partitions token/slot pairs by contiguous expert owner. Each rank
11346    /// peer-reads the whole batch input once, rounds it to BF16, and executes its owner-local
11347    /// selected gate/up -> host-expf SwiGLU -> BF16 -> down program. Down rows scatter directly
11348    /// into canonical token-major pair positions in the model engine's peer-accessible pool at
11349    /// every batch width; the root reduces each token's slots in original order. Thus batching
11350    /// and owner assignment do not change route-reduction parenthesization.
11351    #[allow(clippy::too_many_arguments)]
11352    pub fn run_routed_experts_nvfp4_w4a16_device_io(
11353        &self,
11354        experts: &ResidentNvfp4ExpertParallel,
11355        e: &Engine,
11356        input_dev: &crate::CudaSlice<f32>,
11357        tokens: usize,
11358        selected: &[usize],
11359        route_weights: &[f32],
11360        experts_per_token: usize,
11361        activation_limit: Option<f32>,
11362    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11363        // Diagnostic attribution only: force the returned root event chain to completion so the
11364        // caller's shared-expert timer does not absorb routed-EP work. The normal path remains
11365        // fully asynchronous.
11366        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11367        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11368        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
11369        let started = timing.then(std::time::Instant::now);
11370        if !self.native_p2p {
11371            return Err("W4A16 NVFP4 device EP requires native P2P".into());
11372        }
11373        if self.devices.first().copied() != Some(e.ctx().ordinal()) {
11374            return Err(format!(
11375                "W4A16 NVFP4 device EP root device {:?} != model engine device {}",
11376                self.devices.first(),
11377                e.ctx().ordinal()
11378            )
11379            .into());
11380        }
11381        // Prime/cache scratch buffers are grow-only: a 160-token host-oracle chunk can be
11382        // followed by a 44-token device-EP tail using the same 160-row allocation. Consume the
11383        // active prefix rather than requiring allocation length == active length.
11384        let active_input_values =
11385            nvfp4_ep_active_input_values(input_dev.len(), tokens, experts.input_width)?;
11386        let pairs = tokens
11387            .checked_mul(experts_per_token)
11388            .ok_or("W4A16 NVFP4 device EP route count overflow")?;
11389        if selected.len() != pairs || route_weights.len() != pairs {
11390            return Err(format!(
11391                "W4A16 NVFP4 device EP routes selected={} weights={} != tokens {tokens} x \
11392                 experts/token {experts_per_token} ({pairs})",
11393                selected.len(),
11394                route_weights.len(),
11395            )
11396            .into());
11397        }
11398        if !route_weights.iter().all(|weight| weight.is_finite()) {
11399            return Err("W4A16 NVFP4 device EP route weights contain a non-finite value".into());
11400        }
11401        let world = self.ranks.len();
11402        if world != experts.ranks.len() || !(2..=PRODUCT_MAX_CARDS).contains(&world) {
11403            return Err(format!(
11404                "W4A16 NVFP4 device EP runtime ranks {world} != bank ranks {}",
11405                experts.ranks.len()
11406            )
11407            .into());
11408        }
11409        let owner_routes = partition_expert_owner_routes(
11410            experts.expert_count,
11411            world,
11412            tokens,
11413            experts_per_token,
11414            selected,
11415        )?;
11416
11417        let mut workspace_guard = experts
11418            .device_workspace
11419            .lock()
11420            .map_err(|_| "W4A16 NVFP4 device EP workspace lock is poisoned")?;
11421        if workspace_guard.is_none() {
11422            let capacity_tokens = NVFP4_EP_DEVICE_BATCH_CAP;
11423            let capacity_pairs = capacity_tokens * experts_per_token;
11424            let mut input = Vec::with_capacity(world);
11425            let mut input_bf16 = Vec::with_capacity(world);
11426            let mut input_q8 = Vec::with_capacity(world);
11427            let mut input_q8_scales = Vec::with_capacity(world);
11428            let mut sel = Vec::with_capacity(world);
11429            let mut token_rows = Vec::with_capacity(world);
11430            let mut global_pairs = Vec::with_capacity(world);
11431            let mut route_w = Vec::with_capacity(world);
11432            let mut gate_out = Vec::with_capacity(world);
11433            let mut up_out = Vec::with_capacity(world);
11434            let mut activation_bf16 = Vec::with_capacity(world);
11435            let mut activation_q8 = Vec::with_capacity(world);
11436            let mut activation_q8_scales = Vec::with_capacity(world);
11437            let mut ev_rank = Vec::with_capacity(world);
11438            for engine in &self.ranks {
11439                let _main = engine.gpu.enter_main()?;
11440                input.push(engine.uninit(capacity_tokens * experts.input_width)?);
11441                input_bf16.push(engine.alloc_u8_uninit(2 * capacity_tokens * experts.input_width)?);
11442                input_q8.push(engine.alloc_i8_uninit(capacity_tokens * experts.input_width)?);
11443                input_q8_scales
11444                    .push(engine.uninit(capacity_tokens * experts.input_width.div_ceil(32))?);
11445                sel.push(engine.htod_i32(&vec![0i32; capacity_pairs])?);
11446                token_rows.push(engine.htod_i32(&vec![0i32; capacity_pairs])?);
11447                global_pairs.push(engine.htod_i32(&vec![0i32; capacity_pairs])?);
11448                route_w.push(engine.htod(&vec![0.0f32; capacity_pairs])?);
11449                gate_out.push(engine.uninit(capacity_pairs * experts.expert_width)?);
11450                up_out.push(engine.uninit(capacity_pairs * experts.expert_width)?);
11451                activation_bf16
11452                    .push(engine.alloc_u8_uninit(2 * capacity_pairs * experts.expert_width)?);
11453                activation_q8.push(engine.alloc_i8_uninit(capacity_pairs * experts.expert_width)?);
11454                activation_q8_scales
11455                    .push(engine.uninit(capacity_pairs * experts.expert_width.div_ceil(32))?);
11456                ev_rank.push(engine.ctx().new_event(None)?);
11457            }
11458            let _main = e.gpu.enter_main()?;
11459            let slot_rows = e.uninit(capacity_pairs * experts.input_width)?;
11460            let slot_rows_raw = {
11461                use cudarc::driver::DevicePtr;
11462                let stream = e.stream();
11463                let (pointer, _guard) = slot_rows.device_ptr(&stream);
11464                pointer
11465            };
11466            *workspace_guard = Some(Nvfp4EpDeviceWorkspace {
11467                input,
11468                input_bf16,
11469                input_q8,
11470                input_q8_scales,
11471                sel,
11472                token_rows,
11473                global_pairs,
11474                route_w,
11475                gate_out,
11476                up_out,
11477                activation_bf16,
11478                activation_q8,
11479                activation_q8_scales,
11480                slot_rows,
11481                slot_rows_raw,
11482                route_weights: e.htod(&vec![0.0f32; capacity_pairs])?,
11483                graph_input: e.uninit(NVFP4_EP_GRAPH_BATCH_CAP * experts.input_width)?,
11484                graph_output: e.uninit(NVFP4_EP_GRAPH_BATCH_CAP * experts.input_width)?,
11485                graph_routes: None,
11486                graphs: std::iter::repeat_with(|| None)
11487                    .take(NVFP4_EP_GRAPH_BATCH_CAP + 1)
11488                    .collect(),
11489                ev_entry: e.ctx().new_event(None)?,
11490                ev_entry_device: e.ctx().ordinal(),
11491                ev_rank,
11492                phase_events: None,
11493                capacity_tokens,
11494                experts_per_token,
11495            });
11496        }
11497        let workspace = workspace_guard
11498            .as_mut()
11499            .expect("W4A16 NVFP4 device EP workspace initialized above");
11500        if workspace.experts_per_token != experts_per_token || tokens > workspace.capacity_tokens {
11501            return Err(format!(
11502                "W4A16 NVFP4 device EP workspace tokens={} experts/token={} cannot serve \
11503                 tokens={tokens} experts/token={experts_per_token}",
11504                workspace.capacity_tokens, workspace.experts_per_token,
11505            )
11506            .into());
11507        }
11508        if workspace.ev_entry_device != e.ctx().ordinal() {
11509            return Err("W4A16 NVFP4 device EP model engine changed".into());
11510        }
11511
11512        {
11513            let _main = e.gpu.enter_main()?;
11514            let mut destination = workspace.route_weights.slice_mut(0..pairs);
11515            e.stream()
11516                .memcpy_htod(&route_weights[..pairs], &mut destination)?;
11517            workspace.ev_entry.record(&e.stream())?;
11518        }
11519        for (rank_index, engine) in self.ranks.iter().enumerate() {
11520            let _main = engine.gpu.enter_main()?;
11521            engine.stream().wait(&workspace.ev_entry)?;
11522            {
11523                let mut destination = workspace.input[rank_index].slice_mut(0..active_input_values);
11524                engine
11525                    .stream()
11526                    .memcpy_dtod(&input_dev.slice(0..active_input_values), &mut destination)?;
11527            }
11528            engine.f32_to_bf16_into(
11529                &workspace.input[rank_index],
11530                &mut workspace.input_bf16[rank_index],
11531                tokens * experts.input_width,
11532            )?;
11533            let owner = &owner_routes[rank_index];
11534            debug_assert_eq!(owner.rank, rank_index);
11535            let local_count = owner.selected.len();
11536            if local_count > 0 {
11537                let local_selected = owner
11538                    .selected
11539                    .iter()
11540                    .map(|&expert| expert as i32)
11541                    .collect::<Vec<_>>();
11542                let local_token_rows = owner
11543                    .token_rows
11544                    .iter()
11545                    .map(|&token| token as i32)
11546                    .collect::<Vec<_>>();
11547                let local_global_pairs = owner
11548                    .global_pairs
11549                    .iter()
11550                    .map(|&pair| pair as i32)
11551                    .collect::<Vec<_>>();
11552                {
11553                    let mut destination = workspace.sel[rank_index].slice_mut(0..local_count);
11554                    engine
11555                        .stream()
11556                        .memcpy_htod(&local_selected, &mut destination)?;
11557                }
11558                {
11559                    let mut destination =
11560                        workspace.token_rows[rank_index].slice_mut(0..local_count);
11561                    engine
11562                        .stream()
11563                        .memcpy_htod(&local_token_rows, &mut destination)?;
11564                }
11565                {
11566                    let mut destination =
11567                        workspace.global_pairs[rank_index].slice_mut(0..local_count);
11568                    engine
11569                        .stream()
11570                        .memcpy_htod(&local_global_pairs, &mut destination)?;
11571                }
11572                let rank = &experts.ranks[rank_index];
11573                engine.qmatvec_nvfp4_bf16_sel_dual_rows_into(
11574                    &rank.gate,
11575                    &rank.up,
11576                    &workspace.sel[rank_index],
11577                    &workspace.token_rows[rank_index],
11578                    &workspace.input_bf16[rank_index],
11579                    &mut workspace.gate_out[rank_index],
11580                    &mut workspace.up_out[rank_index],
11581                    local_count,
11582                    experts.input_width,
11583                    experts.expert_width,
11584                    experts.gate_row_bytes,
11585                    rank.gate_expert_bytes,
11586                    tokens,
11587                )?;
11588                engine.silu_mul_scaled_host_expf_bf16_sel_into(
11589                    &workspace.gate_out[rank_index],
11590                    &workspace.up_out[rank_index],
11591                    &rank.macros_gate,
11592                    &rank.macros_up,
11593                    &workspace.sel[rank_index],
11594                    activation_limit,
11595                    &mut workspace.activation_bf16[rank_index],
11596                    experts.expert_width,
11597                    local_count,
11598                )?;
11599                engine.qmatvec_nvfp4_bf16_sel_down_rows_raw(
11600                    &rank.down,
11601                    &workspace.sel[rank_index],
11602                    &workspace.global_pairs[rank_index],
11603                    &workspace.activation_bf16[rank_index],
11604                    &rank.macros_down,
11605                    workspace.slot_rows_raw,
11606                    local_count,
11607                    experts.expert_width,
11608                    experts.input_width,
11609                    experts.down_row_bytes,
11610                    rank.down_expert_bytes,
11611                    pairs,
11612                )?;
11613            }
11614            workspace.ev_rank[rank_index].record(&engine.stream())?;
11615        }
11616
11617        let output = {
11618            let _main = e.gpu.enter_main()?;
11619            for event in &workspace.ev_rank {
11620                e.stream().wait(event)?;
11621            }
11622            let mut output = e.uninit(tokens * experts.input_width)?;
11623            e.axpy_rows_seq_tokens_into(
11624                &workspace.slot_rows,
11625                &workspace.route_weights,
11626                &mut output,
11627                experts.input_width,
11628                experts_per_token,
11629                tokens,
11630            )?;
11631            output
11632        };
11633        if let Some(started) = started {
11634            use std::sync::atomic::Ordering;
11635            e.stream().synchronize()?;
11636            let elapsed = started.elapsed().as_nanos() as u64;
11637            let ns = TIMING_NS.fetch_add(elapsed, Ordering::Relaxed) + elapsed;
11638            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11639            if calls.is_multiple_of(430) {
11640                eprintln!(
11641                    "[nvfp4-ep-w4a16-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11642                    ns as f64 / 1.0e6,
11643                    ns as f64 / calls as f64 / 1.0e3,
11644                );
11645            }
11646        }
11647        Ok(output)
11648    }
11649
11650    /// Fully device-routed W4A16 expert parallelism. Router ids/weights stay on the model GPU;
11651    /// each rank receives the fixed token/slot metadata, rejects non-owned experts in-kernel, and
11652    /// writes canonical token-major slot rows back to the root at every batch width. Preserving
11653    /// that one accumulation program is required by speculative verification: the former t=1
11654    /// owner-grouped FMA was a distinct numeric class and failed real HY3 MTP self-consistency.
11655    #[allow(clippy::too_many_arguments)]
11656    pub fn run_routed_experts_nvfp4_w4a16_device_routed(
11657        &self,
11658        experts: &ResidentNvfp4ExpertParallel,
11659        e: &Engine,
11660        input_dev: &crate::CudaSlice<f32>,
11661        selected_dev: &crate::CudaSlice<i32>,
11662        route_weights_dev: &crate::CudaSlice<f32>,
11663        tokens: usize,
11664        experts_per_token: usize,
11665        activation_limit: Option<f32>,
11666    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11667        self.run_routed_experts_nvfp4_w4a16_device_routed_inner(
11668            experts,
11669            e,
11670            input_dev,
11671            selected_dev,
11672            route_weights_dev,
11673            tokens,
11674            experts_per_token,
11675            activation_limit,
11676            None,
11677        )
11678    }
11679
11680    /// Automatic whole-expert EP with a PREJOIN hook. The hook runs after every rank's routed
11681    /// chain has been issued and before the root waits for rank completion, so independent
11682    /// root-device work can fill the peer drain without changing the routed accumulation order.
11683    #[allow(clippy::too_many_arguments)]
11684    pub fn run_routed_experts_nvfp4_w4a16_device_routed_prejoin(
11685        &self,
11686        experts: &ResidentNvfp4ExpertParallel,
11687        e: &Engine,
11688        input_dev: &crate::CudaSlice<f32>,
11689        selected_dev: &crate::CudaSlice<i32>,
11690        route_weights_dev: &crate::CudaSlice<f32>,
11691        tokens: usize,
11692        experts_per_token: usize,
11693        activation_limit: Option<f32>,
11694        mut pre_join: impl FnMut() -> Result<(), Box<dyn std::error::Error>>,
11695    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11696        self.run_routed_experts_nvfp4_w4a16_device_routed_inner(
11697            experts,
11698            e,
11699            input_dev,
11700            selected_dev,
11701            route_weights_dev,
11702            tokens,
11703            experts_per_token,
11704            activation_limit,
11705            Some(&mut pre_join),
11706        )
11707    }
11708
11709    #[allow(clippy::too_many_arguments)]
11710    fn run_routed_experts_nvfp4_w4a16_device_routed_inner(
11711        &self,
11712        experts: &ResidentNvfp4ExpertParallel,
11713        e: &Engine,
11714        input_dev: &crate::CudaSlice<f32>,
11715        selected_dev: &crate::CudaSlice<i32>,
11716        route_weights_dev: &crate::CudaSlice<f32>,
11717        tokens: usize,
11718        experts_per_token: usize,
11719        activation_limit: Option<f32>,
11720        mut pre_join: Option<&mut dyn FnMut() -> Result<(), Box<dyn std::error::Error>>>,
11721    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11722        if !self.native_p2p {
11723            return Err("W4A16 device-routed EP requires native P2P".into());
11724        }
11725        if self.devices.first().copied() != Some(e.ctx().ordinal()) {
11726            return Err(format!(
11727                "W4A16 device-routed EP root device {:?} != model engine device {}",
11728                self.devices.first(),
11729                e.ctx().ordinal()
11730            )
11731            .into());
11732        }
11733        let active_input_values =
11734            nvfp4_ep_active_input_values(input_dev.len(), tokens, experts.input_width)?;
11735        let pairs = tokens
11736            .checked_mul(experts_per_token)
11737            .ok_or("W4A16 device-routed EP route count overflow")?;
11738        if selected_dev.len() < pairs || route_weights_dev.len() < pairs {
11739            return Err(format!(
11740                "W4A16 device-routed EP metadata selected={} weights={} < pairs={pairs}",
11741                selected_dev.len(),
11742                route_weights_dev.len(),
11743            )
11744            .into());
11745        }
11746        let world = self.ranks.len();
11747        if world != experts.ranks.len() || !(2..=PRODUCT_MAX_CARDS).contains(&world) {
11748            return Err(format!(
11749                "W4A16 device-routed EP runtime ranks {world} != bank ranks {}",
11750                experts.ranks.len()
11751            )
11752            .into());
11753        }
11754
11755        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11756        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11757        static ISSUE_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11758        static JOIN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11759        static COPY_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11760        static GATE_UP_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11761        static ACTIVATION_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11762        static DOWN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11763        static RANK_SPAN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11764        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
11765        let started = timing.then(std::time::Instant::now);
11766        let graph_enabled = parallel_ep_graph_enabled()?;
11767        let pair_down_enabled = parallel_ep_pair_down_enabled()?;
11768
11769        let mut workspace_guard = experts
11770            .device_workspace
11771            .lock()
11772            .map_err(|_| "W4A16 device-routed EP workspace lock is poisoned")?;
11773        if workspace_guard.is_none() {
11774            let capacity_tokens = NVFP4_EP_DEVICE_BATCH_CAP;
11775            let capacity_pairs = capacity_tokens * experts_per_token;
11776            let mut input = Vec::with_capacity(world);
11777            let mut input_bf16 = Vec::with_capacity(world);
11778            let mut input_q8 = Vec::with_capacity(world);
11779            let mut input_q8_scales = Vec::with_capacity(world);
11780            let mut sel = Vec::with_capacity(world);
11781            let mut token_rows = Vec::with_capacity(world);
11782            let mut global_pairs = Vec::with_capacity(world);
11783            let mut route_w = Vec::with_capacity(world);
11784            let mut gate_out = Vec::with_capacity(world);
11785            let mut up_out = Vec::with_capacity(world);
11786            let mut activation_bf16 = Vec::with_capacity(world);
11787            let mut activation_q8 = Vec::with_capacity(world);
11788            let mut activation_q8_scales = Vec::with_capacity(world);
11789            let mut ev_rank = Vec::with_capacity(world);
11790            let mut phase_head = Vec::with_capacity(world);
11791            let mut phase_copy_done = Vec::with_capacity(world);
11792            let mut phase_gate_up_done = Vec::with_capacity(world);
11793            let mut phase_activation_done = Vec::with_capacity(world);
11794            let mut phase_down_done = Vec::with_capacity(world);
11795            for engine in &self.ranks {
11796                let _main = engine.gpu.enter_main()?;
11797                input.push(engine.uninit(capacity_tokens * experts.input_width)?);
11798                input_bf16.push(engine.alloc_u8_uninit(2 * capacity_tokens * experts.input_width)?);
11799                input_q8.push(engine.alloc_i8_uninit(capacity_tokens * experts.input_width)?);
11800                input_q8_scales
11801                    .push(engine.uninit(capacity_tokens * experts.input_width.div_ceil(32))?);
11802                sel.push(engine.htod_i32(&vec![0i32; capacity_pairs])?);
11803                token_rows.push(engine.htod_i32(&vec![0i32; capacity_pairs])?);
11804                global_pairs.push(engine.htod_i32(&vec![0i32; capacity_pairs])?);
11805                route_w.push(engine.htod(&vec![0.0f32; capacity_pairs])?);
11806                gate_out.push(engine.uninit(capacity_pairs * experts.expert_width)?);
11807                up_out.push(engine.uninit(capacity_pairs * experts.expert_width)?);
11808                activation_bf16
11809                    .push(engine.alloc_u8_uninit(2 * capacity_pairs * experts.expert_width)?);
11810                activation_q8.push(engine.alloc_i8_uninit(capacity_pairs * experts.expert_width)?);
11811                activation_q8_scales
11812                    .push(engine.uninit(capacity_pairs * experts.expert_width.div_ceil(32))?);
11813                ev_rank.push(engine.ctx().new_event(None)?);
11814                if timing {
11815                    phase_head.push(
11816                        engine.ctx().new_event(Some(
11817                            cudarc::driver::sys::CUevent_flags::CU_EVENT_DEFAULT,
11818                        ))?,
11819                    );
11820                    phase_copy_done.push(
11821                        engine.ctx().new_event(Some(
11822                            cudarc::driver::sys::CUevent_flags::CU_EVENT_DEFAULT,
11823                        ))?,
11824                    );
11825                    phase_gate_up_done.push(
11826                        engine.ctx().new_event(Some(
11827                            cudarc::driver::sys::CUevent_flags::CU_EVENT_DEFAULT,
11828                        ))?,
11829                    );
11830                    phase_activation_done.push(
11831                        engine.ctx().new_event(Some(
11832                            cudarc::driver::sys::CUevent_flags::CU_EVENT_DEFAULT,
11833                        ))?,
11834                    );
11835                    phase_down_done.push(
11836                        engine.ctx().new_event(Some(
11837                            cudarc::driver::sys::CUevent_flags::CU_EVENT_DEFAULT,
11838                        ))?,
11839                    );
11840                }
11841            }
11842            let _main = e.gpu.enter_main()?;
11843            let slot_rows = e.uninit(capacity_pairs * experts.input_width)?;
11844            let slot_rows_raw = {
11845                use cudarc::driver::DevicePtr;
11846                let stream = e.stream();
11847                let (pointer, _guard) = slot_rows.device_ptr(&stream);
11848                pointer
11849            };
11850            *workspace_guard = Some(Nvfp4EpDeviceWorkspace {
11851                input,
11852                input_bf16,
11853                input_q8,
11854                input_q8_scales,
11855                sel,
11856                token_rows,
11857                global_pairs,
11858                route_w,
11859                gate_out,
11860                up_out,
11861                activation_bf16,
11862                activation_q8,
11863                activation_q8_scales,
11864                slot_rows,
11865                slot_rows_raw,
11866                route_weights: e.htod(&vec![0.0f32; capacity_pairs])?,
11867                graph_input: e.uninit(NVFP4_EP_GRAPH_BATCH_CAP * experts.input_width)?,
11868                graph_output: e.uninit(NVFP4_EP_GRAPH_BATCH_CAP * experts.input_width)?,
11869                graph_routes: None,
11870                graphs: std::iter::repeat_with(|| None)
11871                    .take(NVFP4_EP_GRAPH_BATCH_CAP + 1)
11872                    .collect(),
11873                ev_entry: e.ctx().new_event(None)?,
11874                ev_entry_device: e.ctx().ordinal(),
11875                ev_rank,
11876                phase_events: timing.then_some(Nvfp4EpPhaseEvents {
11877                    head: phase_head,
11878                    copy_done: phase_copy_done,
11879                    gate_up_done: phase_gate_up_done,
11880                    activation_done: phase_activation_done,
11881                    down_done: phase_down_done,
11882                }),
11883                capacity_tokens,
11884                experts_per_token,
11885            });
11886        }
11887        let workspace = workspace_guard
11888            .as_mut()
11889            .expect("W4A16 device-routed EP workspace initialized above");
11890        if workspace.experts_per_token != experts_per_token || tokens > workspace.capacity_tokens {
11891            return Err(format!(
11892                "W4A16 device-routed EP workspace tokens={} experts/token={} cannot serve \
11893                 tokens={tokens} experts/token={experts_per_token}",
11894                workspace.capacity_tokens, workspace.experts_per_token,
11895            )
11896            .into());
11897        }
11898
11899        if tokens <= NVFP4_EP_Q8_BATCH_CAP && parallel_ep_q8_act_enabled()? {
11900            if graph_enabled {
11901                return Err("MEMRA_PARALLEL_EP_GRAPH=1 is exact W4A16-only; disable \
11902                     MEMRA_PARALLEL_EP_Q8_ACT or the graph door"
11903                    .into());
11904            }
11905            return self.run_routed_experts_nvfp4_w4a8_device_routed(
11906                experts,
11907                e,
11908                input_dev,
11909                selected_dev,
11910                route_weights_dev,
11911                workspace,
11912                tokens,
11913                experts_per_token,
11914                activation_limit,
11915                pre_join,
11916            );
11917        }
11918
11919        if graph_enabled && !timing && pre_join.is_none() && tokens <= NVFP4_EP_GRAPH_BATCH_CAP {
11920            use cudarc::driver::DevicePtr;
11921            let route_ptrs = {
11922                let stream = e.stream();
11923                let (sel_ptr, _sel_guard) = selected_dev.device_ptr(&stream);
11924                let (weight_ptr, _weight_guard) = route_weights_dev.device_ptr(&stream);
11925                (sel_ptr, weight_ptr)
11926            };
11927            if let Some(graph_exec) = workspace.graphs[tokens].as_ref().map(|graph| graph.exec) {
11928                if workspace.graph_routes != Some(route_ptrs) {
11929                    return Err(format!(
11930                        "W4A16 EP graph route buffers moved: built={:?} current={route_ptrs:?}",
11931                        workspace.graph_routes,
11932                    )
11933                    .into());
11934                }
11935                let _main = e.gpu.enter_main()?;
11936                e.stream().memcpy_dtod(
11937                    &input_dev.slice(0..active_input_values),
11938                    &mut workspace.graph_input.slice_mut(0..active_input_values),
11939                )?;
11940                e.memset_zeros_view(
11941                    &mut workspace
11942                        .slot_rows
11943                        .slice_mut(0..pairs * experts.input_width),
11944                )?;
11945                unsafe {
11946                    let result = cudarc::driver::sys::cuGraphLaunch(
11947                        graph_exec,
11948                        e.stream().cu_stream() as cudarc::driver::sys::CUstream,
11949                    );
11950                    if result != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
11951                        return Err(format!("W4A16 EP graph launch: {result:?}").into());
11952                    }
11953                }
11954                let mut output = e.uninit(active_input_values)?;
11955                e.stream().memcpy_dtod(
11956                    &workspace.graph_output.slice(0..active_input_values),
11957                    &mut output.slice_mut(0..active_input_values),
11958                )?;
11959                return Ok(output);
11960            }
11961        }
11962
11963        {
11964            let _main = e.gpu.enter_main()?;
11965            e.memset_zeros_view(
11966                &mut workspace
11967                    .slot_rows
11968                    .slice_mut(0..pairs * experts.input_width),
11969            )?;
11970            workspace.ev_entry.record(&e.stream())?;
11971        }
11972
11973        for (rank_index, engine) in self.ranks.iter().enumerate() {
11974            let _main = engine.gpu.enter_main()?;
11975            if let Some(events) = workspace.phase_events.as_ref() {
11976                events.head[rank_index].record(&engine.stream())?;
11977            }
11978            engine.stream().wait(&workspace.ev_entry)?;
11979            let Nvfp4EpDeviceWorkspace {
11980                input_bf16,
11981                sel,
11982                route_w,
11983                ..
11984            } = &mut *workspace;
11985            engine.nvfp4_ep_stage_inputs(
11986                input_dev,
11987                selected_dev,
11988                route_weights_dev,
11989                &mut input_bf16[rank_index],
11990                &mut sel[rank_index],
11991                &mut route_w[rank_index],
11992                active_input_values,
11993                pairs,
11994                false,
11995            )?;
11996            if let Some(events) = workspace.phase_events.as_ref() {
11997                events.copy_done[rank_index].record(&engine.stream())?;
11998            }
11999            let rank = &experts.ranks[rank_index];
12000            let owner_start = rank.expert_range.start;
12001            let owner_end = rank.expert_range.end;
12002            engine.qmatvec_nvfp4_bf16_ep_dual_slots_into(
12003                &rank.gate,
12004                &rank.up,
12005                &workspace.sel[rank_index],
12006                &workspace.input_bf16[rank_index],
12007                &mut workspace.gate_out[rank_index],
12008                &mut workspace.up_out[rank_index],
12009                pairs,
12010                experts_per_token,
12011                experts.input_width,
12012                experts.expert_width,
12013                owner_start,
12014                owner_end,
12015                experts.gate_row_bytes,
12016                rank.gate_expert_bytes,
12017            )?;
12018            if let Some(events) = workspace.phase_events.as_ref() {
12019                events.gate_up_done[rank_index].record(&engine.stream())?;
12020            }
12021            engine.silu_mul_scaled_host_expf_bf16_ep_slots_into(
12022                &workspace.gate_out[rank_index],
12023                &workspace.up_out[rank_index],
12024                &rank.macros_gate,
12025                &rank.macros_up,
12026                &workspace.sel[rank_index],
12027                owner_start,
12028                owner_end,
12029                activation_limit,
12030                &mut workspace.activation_bf16[rank_index],
12031                experts.expert_width,
12032                pairs,
12033            )?;
12034            if let Some(events) = workspace.phase_events.as_ref() {
12035                events.activation_done[rank_index].record(&engine.stream())?;
12036            }
12037            if tokens > 1 && pair_down_enabled {
12038                engine.qmatvec_nvfp4_bf16_ep_down_pairs_raw(
12039                    &rank.down,
12040                    &workspace.sel[rank_index],
12041                    &workspace.activation_bf16[rank_index],
12042                    &rank.macros_down,
12043                    workspace.slot_rows_raw,
12044                    pairs,
12045                    experts.expert_width,
12046                    experts.input_width,
12047                    owner_start,
12048                    owner_end,
12049                    experts.down_row_bytes,
12050                    rank.down_expert_bytes,
12051                )?;
12052            } else {
12053                engine.qmatvec_nvfp4_bf16_ep_down_slots_raw(
12054                    &rank.down,
12055                    &workspace.sel[rank_index],
12056                    &workspace.activation_bf16[rank_index],
12057                    &rank.macros_down,
12058                    workspace.slot_rows_raw,
12059                    pairs,
12060                    experts.expert_width,
12061                    experts.input_width,
12062                    owner_start,
12063                    owner_end,
12064                    experts.down_row_bytes,
12065                    rank.down_expert_bytes,
12066                )?;
12067            }
12068            if let Some(events) = workspace.phase_events.as_ref() {
12069                events.down_done[rank_index].record(&engine.stream())?;
12070            }
12071            workspace.ev_rank[rank_index].record(&engine.stream())?;
12072        }
12073
12074        if let Some(pre_join) = pre_join.as_mut() {
12075            pre_join()?;
12076        }
12077        let issue_ns_this = started
12078            .as_ref()
12079            .map(|started| started.elapsed().as_nanos() as u64);
12080        let join_started = timing.then(std::time::Instant::now);
12081        let output = {
12082            let _main = e.gpu.enter_main()?;
12083            for event in &workspace.ev_rank {
12084                e.stream().wait(event)?;
12085            }
12086            let mut output = e.uninit(tokens * experts.input_width)?;
12087            e.axpy_rows_seq_tokens_into(
12088                &workspace.slot_rows,
12089                route_weights_dev,
12090                &mut output,
12091                experts.input_width,
12092                experts_per_token,
12093                tokens,
12094            )?;
12095            output
12096        };
12097
12098        if let Some(started) = started {
12099            use std::sync::atomic::Ordering;
12100            e.stream().synchronize()?;
12101            let elapsed = started.elapsed().as_nanos() as u64;
12102            let join_ns_this = join_started
12103                .expect("timing join starts with total timing")
12104                .elapsed()
12105                .as_nanos() as u64;
12106            let mut phase_max_ms = [0.0f32; 5];
12107            if let Some(events) = workspace.phase_events.as_ref() {
12108                for rank_index in 0..world {
12109                    let engine = &self.ranks[rank_index];
12110                    let _main = engine.gpu.enter_main()?;
12111                    phase_max_ms[0] = phase_max_ms[0]
12112                        .max(events.head[rank_index].elapsed_ms(&events.copy_done[rank_index])?);
12113                    phase_max_ms[1] = phase_max_ms[1].max(
12114                        events.copy_done[rank_index]
12115                            .elapsed_ms(&events.gate_up_done[rank_index])?,
12116                    );
12117                    phase_max_ms[2] = phase_max_ms[2].max(
12118                        events.gate_up_done[rank_index]
12119                            .elapsed_ms(&events.activation_done[rank_index])?,
12120                    );
12121                    phase_max_ms[3] = phase_max_ms[3].max(
12122                        events.activation_done[rank_index]
12123                            .elapsed_ms(&events.down_done[rank_index])?,
12124                    );
12125                    phase_max_ms[4] = phase_max_ms[4]
12126                        .max(events.head[rank_index].elapsed_ms(&events.down_done[rank_index])?);
12127                }
12128            }
12129            let phase_ns = phase_max_ms.map(|ms| (ms as f64 * 1.0e6) as u64);
12130            let ns = TIMING_NS.fetch_add(elapsed, Ordering::Relaxed) + elapsed;
12131            let issue_ns = ISSUE_NS.fetch_add(
12132                issue_ns_this.expect("timing issue starts with total timing"),
12133                Ordering::Relaxed,
12134            ) + issue_ns_this.expect("timing issue starts with total timing");
12135            let join_ns = JOIN_NS.fetch_add(join_ns_this, Ordering::Relaxed) + join_ns_this;
12136            let copy_ns = COPY_NS.fetch_add(phase_ns[0], Ordering::Relaxed) + phase_ns[0];
12137            let gate_up_ns = GATE_UP_NS.fetch_add(phase_ns[1], Ordering::Relaxed) + phase_ns[1];
12138            let activation_ns =
12139                ACTIVATION_NS.fetch_add(phase_ns[2], Ordering::Relaxed) + phase_ns[2];
12140            let down_ns = DOWN_NS.fetch_add(phase_ns[3], Ordering::Relaxed) + phase_ns[3];
12141            let rank_span_ns = RANK_SPAN_NS.fetch_add(phase_ns[4], Ordering::Relaxed) + phase_ns[4];
12142            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
12143            if calls.is_multiple_of(430) {
12144                eprintln!(
12145                    "[nvfp4-ep-device-router-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
12146                    ns as f64 / 1.0e6,
12147                    ns as f64 / calls as f64 / 1.0e3,
12148                );
12149                eprintln!(
12150                    "[nvfp4-ep-device-router-phases] calls={calls} issue_us={:.1} \
12151                     join_us={:.1} rank_span_us={:.1} copy_us={:.1} gate_up_us={:.1} \
12152                     activation_us={:.1} down_us={:.1}",
12153                    issue_ns as f64 / calls as f64 / 1.0e3,
12154                    join_ns as f64 / calls as f64 / 1.0e3,
12155                    rank_span_ns as f64 / calls as f64 / 1.0e3,
12156                    copy_ns as f64 / calls as f64 / 1.0e3,
12157                    gate_up_ns as f64 / calls as f64 / 1.0e3,
12158                    activation_ns as f64 / calls as f64 / 1.0e3,
12159                    down_ns as f64 / calls as f64 / 1.0e3,
12160                );
12161            }
12162        }
12163        if graph_enabled
12164            && !timing
12165            && pre_join.is_none()
12166            && tokens <= NVFP4_EP_GRAPH_BATCH_CAP
12167            && workspace.graphs[tokens].is_none()
12168        {
12169            e.stream().synchronize()?;
12170            let graph = self.build_nvfp4_ep_routes_graph(
12171                experts,
12172                e,
12173                workspace,
12174                selected_dev,
12175                route_weights_dev,
12176                tokens,
12177                experts_per_token,
12178                activation_limit,
12179            )?;
12180            workspace.graphs[tokens] = Some(graph);
12181            eprintln!(
12182                "[parallel-ep-graph] captured devices={:?} tokens={tokens} \
12183                 experts/token={experts_per_token} input=staged routes=fixed \
12184                 device_arithmetic=unchanged performance_claim=false",
12185                self.devices,
12186            );
12187        }
12188        Ok(output)
12189    }
12190
12191    #[allow(clippy::too_many_arguments)]
12192    fn run_routed_experts_nvfp4_w4a8_device_routed(
12193        &self,
12194        experts: &ResidentNvfp4ExpertParallel,
12195        e: &Engine,
12196        input_dev: &crate::CudaSlice<f32>,
12197        selected_dev: &crate::CudaSlice<i32>,
12198        route_weights_dev: &crate::CudaSlice<f32>,
12199        workspace: &mut Nvfp4EpDeviceWorkspace,
12200        tokens: usize,
12201        experts_per_token: usize,
12202        activation_limit: Option<f32>,
12203        mut pre_join: Option<&mut dyn FnMut() -> Result<(), Box<dyn std::error::Error>>>,
12204    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
12205        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12206        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12207        static ISSUE_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12208        static JOIN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12209        static COPY_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12210        static GATE_UP_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12211        static ACTIVATION_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12212        static DOWN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12213        static RANK_SPAN_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12214        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
12215        let started = timing.then(std::time::Instant::now);
12216        let pairs = tokens
12217            .checked_mul(experts_per_token)
12218            .ok_or("W4A8 device-routed EP route count overflow")?;
12219        let input_values = tokens
12220            .checked_mul(experts.input_width)
12221            .ok_or("W4A8 device-routed EP input size overflow")?;
12222        let scope = parallel_ep_q8_scope()?.unwrap_or(ParallelEpQ8Scope::All);
12223        let gate_up_paired = parallel_ep_q8_gu_paired_enabled(true, Some(scope))?;
12224
12225        {
12226            let _main = e.gpu.enter_main()?;
12227            e.memset_zeros_view(
12228                &mut workspace
12229                    .slot_rows
12230                    .slice_mut(0..pairs * experts.input_width),
12231            )?;
12232            workspace.ev_entry.record(&e.stream())?;
12233        }
12234        for (rank_index, engine) in self.ranks.iter().enumerate() {
12235            let _main = engine.gpu.enter_main()?;
12236            if let Some(events) = workspace.phase_events.as_ref() {
12237                events.head[rank_index].record(&engine.stream())?;
12238            }
12239            engine.stream().wait(&workspace.ev_entry)?;
12240            let rank = &experts.ranks[rank_index];
12241            let owner_start = rank.expert_range.start;
12242            let owner_end = rank.expert_range.end;
12243            match scope {
12244                ParallelEpQ8Scope::All | ParallelEpQ8Scope::GateUp => {
12245                    engine.quantize_q8_1_into(
12246                        input_dev,
12247                        tokens,
12248                        experts.input_width,
12249                        &mut workspace.input_q8[rank_index],
12250                        &mut workspace.input_q8_scales[rank_index],
12251                    )?;
12252                    engine.moe_sel_w_mirror(
12253                        selected_dev,
12254                        route_weights_dev,
12255                        &mut workspace.sel[rank_index],
12256                        &mut workspace.route_w[rank_index],
12257                        pairs,
12258                    )?;
12259                    if let Some(events) = workspace.phase_events.as_ref() {
12260                        events.copy_done[rank_index].record(&engine.stream())?;
12261                    }
12262                    if gate_up_paired {
12263                        engine.qmatvec_nvfp4_q8_ep_paired_slots_into(
12264                            &rank.gate,
12265                            &rank.up,
12266                            &workspace.sel[rank_index],
12267                            &workspace.input_q8[rank_index],
12268                            &workspace.input_q8_scales[rank_index],
12269                            &mut workspace.gate_out[rank_index],
12270                            &mut workspace.up_out[rank_index],
12271                            pairs,
12272                            experts_per_token,
12273                            experts.input_width,
12274                            experts.expert_width,
12275                            owner_start,
12276                            owner_end,
12277                            experts.gate_row_bytes,
12278                            rank.gate_expert_bytes,
12279                        )?;
12280                    } else {
12281                        engine.qmatvec_nvfp4_q8_ep_dual_slots_into(
12282                            &rank.gate,
12283                            &rank.up,
12284                            &workspace.sel[rank_index],
12285                            &workspace.input_q8[rank_index],
12286                            &workspace.input_q8_scales[rank_index],
12287                            &mut workspace.gate_out[rank_index],
12288                            &mut workspace.up_out[rank_index],
12289                            pairs,
12290                            experts_per_token,
12291                            experts.input_width,
12292                            experts.expert_width,
12293                            owner_start,
12294                            owner_end,
12295                            experts.gate_row_bytes,
12296                            rank.gate_expert_bytes,
12297                        )?;
12298                    }
12299                }
12300                ParallelEpQ8Scope::Down => {
12301                    engine.nvfp4_ep_stage_inputs(
12302                        input_dev,
12303                        selected_dev,
12304                        route_weights_dev,
12305                        &mut workspace.input_bf16[rank_index],
12306                        &mut workspace.sel[rank_index],
12307                        &mut workspace.route_w[rank_index],
12308                        input_values,
12309                        pairs,
12310                        false,
12311                    )?;
12312                    if let Some(events) = workspace.phase_events.as_ref() {
12313                        events.copy_done[rank_index].record(&engine.stream())?;
12314                    }
12315                    engine.qmatvec_nvfp4_bf16_ep_dual_slots_into(
12316                        &rank.gate,
12317                        &rank.up,
12318                        &workspace.sel[rank_index],
12319                        &workspace.input_bf16[rank_index],
12320                        &mut workspace.gate_out[rank_index],
12321                        &mut workspace.up_out[rank_index],
12322                        pairs,
12323                        experts_per_token,
12324                        experts.input_width,
12325                        experts.expert_width,
12326                        owner_start,
12327                        owner_end,
12328                        experts.gate_row_bytes,
12329                        rank.gate_expert_bytes,
12330                    )?;
12331                }
12332            }
12333            if let Some(events) = workspace.phase_events.as_ref() {
12334                events.gate_up_done[rank_index].record(&engine.stream())?;
12335            }
12336            match scope {
12337                ParallelEpQ8Scope::All | ParallelEpQ8Scope::Down => {
12338                    engine.silu_mul_scaled_host_expf_q8_ep_slots_into(
12339                        &workspace.gate_out[rank_index],
12340                        &workspace.up_out[rank_index],
12341                        &rank.macros_gate,
12342                        &rank.macros_up,
12343                        &workspace.sel[rank_index],
12344                        owner_start,
12345                        owner_end,
12346                        activation_limit,
12347                        &mut workspace.activation_q8[rank_index],
12348                        &mut workspace.activation_q8_scales[rank_index],
12349                        experts.expert_width,
12350                        pairs,
12351                    )?;
12352                    if let Some(events) = workspace.phase_events.as_ref() {
12353                        events.activation_done[rank_index].record(&engine.stream())?;
12354                    }
12355                    engine.qmatvec_nvfp4_q8_ep_down_slots_raw(
12356                        &rank.down,
12357                        &workspace.sel[rank_index],
12358                        &workspace.activation_q8[rank_index],
12359                        &workspace.activation_q8_scales[rank_index],
12360                        &rank.macros_down,
12361                        workspace.slot_rows_raw,
12362                        pairs,
12363                        experts.expert_width,
12364                        experts.input_width,
12365                        owner_start,
12366                        owner_end,
12367                        experts.down_row_bytes,
12368                        rank.down_expert_bytes,
12369                    )?;
12370                }
12371                ParallelEpQ8Scope::GateUp => {
12372                    engine.silu_mul_scaled_host_expf_bf16_ep_slots_into(
12373                        &workspace.gate_out[rank_index],
12374                        &workspace.up_out[rank_index],
12375                        &rank.macros_gate,
12376                        &rank.macros_up,
12377                        &workspace.sel[rank_index],
12378                        owner_start,
12379                        owner_end,
12380                        activation_limit,
12381                        &mut workspace.activation_bf16[rank_index],
12382                        experts.expert_width,
12383                        pairs,
12384                    )?;
12385                    if let Some(events) = workspace.phase_events.as_ref() {
12386                        events.activation_done[rank_index].record(&engine.stream())?;
12387                    }
12388                    engine.qmatvec_nvfp4_bf16_ep_down_slots_raw(
12389                        &rank.down,
12390                        &workspace.sel[rank_index],
12391                        &workspace.activation_bf16[rank_index],
12392                        &rank.macros_down,
12393                        workspace.slot_rows_raw,
12394                        pairs,
12395                        experts.expert_width,
12396                        experts.input_width,
12397                        owner_start,
12398                        owner_end,
12399                        experts.down_row_bytes,
12400                        rank.down_expert_bytes,
12401                    )?;
12402                }
12403            }
12404            if let Some(events) = workspace.phase_events.as_ref() {
12405                events.down_done[rank_index].record(&engine.stream())?;
12406            }
12407            workspace.ev_rank[rank_index].record(&engine.stream())?;
12408        }
12409
12410        if let Some(pre_join) = pre_join.as_mut() {
12411            pre_join()?;
12412        }
12413        let issue_ns_this = started
12414            .as_ref()
12415            .map(|started| started.elapsed().as_nanos() as u64);
12416        let join_started = timing.then(std::time::Instant::now);
12417        let output = {
12418            let _main = e.gpu.enter_main()?;
12419            for event in &workspace.ev_rank {
12420                e.stream().wait(event)?;
12421            }
12422            let mut output = e.uninit(input_values)?;
12423            e.axpy_rows_seq_tokens_into(
12424                &workspace.slot_rows,
12425                route_weights_dev,
12426                &mut output,
12427                experts.input_width,
12428                experts_per_token,
12429                tokens,
12430            )?;
12431            output
12432        };
12433        if let Some(started) = started {
12434            use std::sync::atomic::Ordering;
12435            e.stream().synchronize()?;
12436            let elapsed = started.elapsed().as_nanos() as u64;
12437            let join_ns_this = join_started
12438                .expect("timing join starts with total timing")
12439                .elapsed()
12440                .as_nanos() as u64;
12441            let mut phase_max_ms = [0.0f32; 5];
12442            if let Some(events) = workspace.phase_events.as_ref() {
12443                for rank_index in 0..self.ranks.len() {
12444                    let engine = &self.ranks[rank_index];
12445                    let _main = engine.gpu.enter_main()?;
12446                    phase_max_ms[0] = phase_max_ms[0]
12447                        .max(events.head[rank_index].elapsed_ms(&events.copy_done[rank_index])?);
12448                    phase_max_ms[1] = phase_max_ms[1].max(
12449                        events.copy_done[rank_index]
12450                            .elapsed_ms(&events.gate_up_done[rank_index])?,
12451                    );
12452                    phase_max_ms[2] = phase_max_ms[2].max(
12453                        events.gate_up_done[rank_index]
12454                            .elapsed_ms(&events.activation_done[rank_index])?,
12455                    );
12456                    phase_max_ms[3] = phase_max_ms[3].max(
12457                        events.activation_done[rank_index]
12458                            .elapsed_ms(&events.down_done[rank_index])?,
12459                    );
12460                    phase_max_ms[4] = phase_max_ms[4]
12461                        .max(events.head[rank_index].elapsed_ms(&events.down_done[rank_index])?);
12462                }
12463            }
12464            let phase_ns = phase_max_ms.map(|ms| (ms as f64 * 1.0e6) as u64);
12465            let ns = TIMING_NS.fetch_add(elapsed, Ordering::Relaxed) + elapsed;
12466            let issue_ns = ISSUE_NS.fetch_add(
12467                issue_ns_this.expect("timing issue starts with total timing"),
12468                Ordering::Relaxed,
12469            ) + issue_ns_this.expect("timing issue starts with total timing");
12470            let join_ns = JOIN_NS.fetch_add(join_ns_this, Ordering::Relaxed) + join_ns_this;
12471            let copy_ns = COPY_NS.fetch_add(phase_ns[0], Ordering::Relaxed) + phase_ns[0];
12472            let gate_up_ns = GATE_UP_NS.fetch_add(phase_ns[1], Ordering::Relaxed) + phase_ns[1];
12473            let activation_ns =
12474                ACTIVATION_NS.fetch_add(phase_ns[2], Ordering::Relaxed) + phase_ns[2];
12475            let down_ns = DOWN_NS.fetch_add(phase_ns[3], Ordering::Relaxed) + phase_ns[3];
12476            let rank_span_ns = RANK_SPAN_NS.fetch_add(phase_ns[4], Ordering::Relaxed) + phase_ns[4];
12477            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
12478            if calls.is_multiple_of(430) {
12479                eprintln!(
12480                    "[nvfp4-ep-q8-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
12481                    ns as f64 / 1.0e6,
12482                    ns as f64 / calls as f64 / 1.0e3,
12483                );
12484                eprintln!(
12485                    "[nvfp4-ep-q8-phases] calls={calls} issue_us={:.1} join_us={:.1} \
12486                     rank_span_us={:.1} copy_us={:.1} gate_up_us={:.1} \
12487                     activation_us={:.1} down_us={:.1}",
12488                    issue_ns as f64 / calls as f64 / 1.0e3,
12489                    join_ns as f64 / calls as f64 / 1.0e3,
12490                    rank_span_ns as f64 / calls as f64 / 1.0e3,
12491                    copy_ns as f64 / calls as f64 / 1.0e3,
12492                    gate_up_ns as f64 / calls as f64 / 1.0e3,
12493                    activation_ns as f64 / calls as f64 / 1.0e3,
12494                    down_ns as f64 / calls as f64 / 1.0e3,
12495                );
12496            }
12497        }
12498        static LOGGED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
12499        if !LOGGED.swap(true, std::sync::atomic::Ordering::Relaxed) {
12500            let (expert_input, post_activation, numeric_class) = match scope {
12501                ParallelEpQ8Scope::All => ("q8_1", "q8_1", "w4a8-internal"),
12502                ParallelEpQ8Scope::GateUp => ("q8_1", "bf16", "w4a8-gate-up-internal"),
12503                ParallelEpQ8Scope::Down => ("bf16", "q8_1", "w4a8-down-internal"),
12504            };
12505            eprintln!(
12506                "[parallel-ep-q8] devices={:?} tokens={tokens} scope={} \
12507                 expert_input={expert_input} post_activation={post_activation} \
12508                 gate_up_schedule={} \
12509                 external_boundary=bf16 numeric_class={numeric_class} \
12510                 host_expf=true accumulation=token-slot-order performance_claim=false",
12511                self.devices,
12512                scope.label(),
12513                if gate_up_paired {
12514                    "paired-cta"
12515                } else {
12516                    "separate-cta"
12517                },
12518            );
12519        }
12520        Ok(output)
12521    }
12522
12523    #[allow(clippy::too_many_arguments)]
12524    fn build_nvfp4_ep_routes_graph(
12525        &self,
12526        experts: &ResidentNvfp4ExpertParallel,
12527        e: &Engine,
12528        workspace: &mut Nvfp4EpDeviceWorkspace,
12529        selected_dev: &crate::CudaSlice<i32>,
12530        route_weights_dev: &crate::CudaSlice<f32>,
12531        tokens: usize,
12532        experts_per_token: usize,
12533        activation_limit: Option<f32>,
12534    ) -> Result<RoutesGraph, Box<dyn std::error::Error>> {
12535        use cudarc::driver::DevicePtr;
12536        use cudarc::driver::sys;
12537
12538        fn cu_try(result: sys::CUresult, context: &str) -> Result<(), Box<dyn std::error::Error>> {
12539            if result == sys::CUresult::CUDA_SUCCESS {
12540                Ok(())
12541            } else {
12542                Err(format!("{context}: {result:?}").into())
12543            }
12544        }
12545
12546        let world = self.ranks.len();
12547        if world != experts.ranks.len() || !(2..=PRODUCT_MAX_CARDS).contains(&world) {
12548            return Err(format!(
12549                "W4A16 EP graph world {world} != expert ranks {}",
12550                experts.ranks.len()
12551            )
12552            .into());
12553        }
12554        let width = experts.input_width;
12555        if !(1..=NVFP4_EP_GRAPH_BATCH_CAP).contains(&tokens) {
12556            return Err(format!(
12557                "W4A16 EP graph tokens {tokens} outside 1..={NVFP4_EP_GRAPH_BATCH_CAP}"
12558            )
12559            .into());
12560        }
12561        let pairs = tokens
12562            .checked_mul(experts_per_token)
12563            .ok_or("W4A16 EP graph pair count overflow")?;
12564        let input_values = tokens
12565            .checked_mul(width)
12566            .ok_or("W4A16 EP graph input size overflow")?;
12567        let root_stream = e.stream();
12568        let (input_ptr, _input_guard) = workspace.graph_input.device_ptr(&root_stream);
12569        let (selected_ptr, _selected_guard) = selected_dev.device_ptr(&root_stream);
12570        let (weights_ptr, _weights_guard) = route_weights_dev.device_ptr(&root_stream);
12571        let route_ptrs = (selected_ptr, weights_ptr);
12572
12573        let mut children = Vec::with_capacity(world + 1);
12574        for rank_index in 0..world {
12575            let engine = &self.ranks[rank_index];
12576            let rank = &experts.ranks[rank_index];
12577            let owner_start = rank.expert_range.start;
12578            let owner_end = rank.expert_range.end;
12579            let _main = engine.gpu.enter_main()?;
12580            let (child, _retained) = engine.capture_graph_retained(|_| {
12581                engine.nvfp4_ep_stage_inputs_raw(
12582                    input_ptr,
12583                    selected_ptr,
12584                    weights_ptr,
12585                    &mut workspace.input_bf16[rank_index],
12586                    &mut workspace.sel[rank_index],
12587                    &mut workspace.route_w[rank_index],
12588                    input_values,
12589                    pairs,
12590                    false,
12591                )?;
12592                engine.qmatvec_nvfp4_bf16_ep_dual_slots_into(
12593                    &rank.gate,
12594                    &rank.up,
12595                    &workspace.sel[rank_index],
12596                    &workspace.input_bf16[rank_index],
12597                    &mut workspace.gate_out[rank_index],
12598                    &mut workspace.up_out[rank_index],
12599                    pairs,
12600                    experts_per_token,
12601                    width,
12602                    experts.expert_width,
12603                    owner_start,
12604                    owner_end,
12605                    experts.gate_row_bytes,
12606                    rank.gate_expert_bytes,
12607                )?;
12608                engine.silu_mul_scaled_host_expf_bf16_ep_slots_into(
12609                    &workspace.gate_out[rank_index],
12610                    &workspace.up_out[rank_index],
12611                    &rank.macros_gate,
12612                    &rank.macros_up,
12613                    &workspace.sel[rank_index],
12614                    owner_start,
12615                    owner_end,
12616                    activation_limit,
12617                    &mut workspace.activation_bf16[rank_index],
12618                    experts.expert_width,
12619                    pairs,
12620                )?;
12621                engine.qmatvec_nvfp4_bf16_ep_down_slots_raw(
12622                    &rank.down,
12623                    &workspace.sel[rank_index],
12624                    &workspace.activation_bf16[rank_index],
12625                    &rank.macros_down,
12626                    workspace.slot_rows_raw,
12627                    pairs,
12628                    experts.expert_width,
12629                    width,
12630                    owner_start,
12631                    owner_end,
12632                    experts.down_row_bytes,
12633                    rank.down_expert_bytes,
12634                )?;
12635                Ok(())
12636            })?;
12637            children.push(child);
12638        }
12639
12640        {
12641            let _main = e.gpu.enter_main()?;
12642            let (child, _retained) = e.capture_graph_retained(|_| {
12643                e.axpy_rows_seq_tokens_into(
12644                    &workspace.slot_rows,
12645                    route_weights_dev,
12646                    &mut workspace.graph_output,
12647                    width,
12648                    experts_per_token,
12649                    tokens,
12650                )
12651            })?;
12652            children.push(child);
12653        }
12654
12655        let mut parent: sys::CUgraph = std::ptr::null_mut();
12656        unsafe {
12657            cu_try(sys::cuGraphCreate(&mut parent, 0), "W4A16 EP cuGraphCreate")?;
12658        }
12659        let mut rank_nodes = Vec::with_capacity(world);
12660        for (rank_index, child) in children.iter().take(world).enumerate() {
12661            let mut node: sys::CUgraphNode = std::ptr::null_mut();
12662            unsafe {
12663                cu_try(
12664                    sys::cuGraphAddChildGraphNode(
12665                        &mut node,
12666                        parent,
12667                        std::ptr::null(),
12668                        0,
12669                        child.cu_graph(),
12670                    ),
12671                    &format!("W4A16 EP graph rank {rank_index}"),
12672                )?;
12673            }
12674            rank_nodes.push(node);
12675        }
12676        let mut combine_node: sys::CUgraphNode = std::ptr::null_mut();
12677        unsafe {
12678            cu_try(
12679                sys::cuGraphAddChildGraphNode(
12680                    &mut combine_node,
12681                    parent,
12682                    rank_nodes.as_ptr(),
12683                    rank_nodes.len(),
12684                    children[world].cu_graph(),
12685                ),
12686                "W4A16 EP graph combine",
12687            )?;
12688        }
12689        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
12690        unsafe {
12691            cu_try(
12692                sys::cuGraphInstantiateWithFlags(&mut exec, parent, 0),
12693                "W4A16 EP graph instantiate",
12694            )?;
12695        }
12696        workspace.graph_routes = Some(route_ptrs);
12697        Ok(RoutesGraph {
12698            exec,
12699            parent,
12700            _children: children,
12701        })
12702    }
12703
12704    /// Device-resident routed NVFP4 expert program (decode shape, t=1 rows). The geometry gift
12705    /// this exploits: gate/up column halves land on the SAME rank that owns the matching down
12706    /// canonical shard (act[rank r] is exactly down-shard r's input-column window), so the whole
12707    /// expert interior — gate, up, macro-scaled SwiGLU, down partial, route-weighted accumulate —
12708    /// runs rank-local with ZERO cross-rank transfer. Per (token, layer): one input upload per
12709    /// rank, one fenced peer copy of the remote accumulator, one root add, one readback.
12710    ///
12711    /// Numeric class: device silu (silu_mul_scaled) with gate/up macros folded as gs/us and the
12712    /// down macro folded into the accumulate scalar (weight * macro_down — exact, both are
12713    /// per-expert constants). This matches the owning-stage MoE dev-path semantics, NOT the
12714    /// host-canonical program bit-for-bit; gate it with argmax + relative bounds against the
12715    /// host-canonical oracle, and with repeat determinism against itself.
12716    /// Clamped layers refuse (they stay on the EP program).
12717    pub fn run_tensor_parallel_routes_nvfp4_device(
12718        &self,
12719        experts: &ResidentNvfp4TensorParallel,
12720        input: &[f32],
12721        selected: &[usize],
12722        route_weights: &[f32],
12723        experts_per_token: usize,
12724        activation_limit: Option<f32>,
12725    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
12726        validate_activations(input, 1, experts.input_width)?;
12727        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
12728            return Err(format!(
12729                "NVFP4 device routes selected={} weights={} != experts/token {experts_per_token}",
12730                selected.len(),
12731                route_weights.len(),
12732            )
12733            .into());
12734        }
12735        if !route_weights.iter().all(|weight| weight.is_finite()) {
12736            return Err("NVFP4 device route weights contain a non-finite value".into());
12737        }
12738        let world = self.ranks.len();
12739        if world != NVFP4_CANONICAL_ROW_SHARDS {
12740            return Err(format!(
12741                "NVFP4 device routes require world == canonical shard grid \
12742                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
12743            )
12744            .into());
12745        }
12746        let local_out = if experts.ep2 {
12747            experts.expert_width
12748        } else {
12749            experts.expert_width / world
12750        };
12751
12752        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of this program, printed every 430 calls
12753        // (~one 43-layer decode step's worth) so a bench run decomposes expert-program time vs
12754        // everything else without Nsight.
12755        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12756        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12757        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
12758        let started = timing.then(std::time::Instant::now);
12759
12760        let n_sel = experts_per_token;
12761        let mut workspace_guard = experts
12762            .device_workspace
12763            .lock()
12764            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
12765        if workspace_guard.is_none() {
12766            let mut gate_out = Vec::with_capacity(world);
12767            let mut up_out = Vec::with_capacity(world);
12768            let mut act_q = Vec::with_capacity(world);
12769            let mut act_d = Vec::with_capacity(world);
12770            let mut sel = Vec::with_capacity(world);
12771            let mut partial = Vec::with_capacity(world);
12772            let mut accumulator = Vec::with_capacity(world);
12773            let mut combine_w = Vec::with_capacity(world);
12774            let mut route_w = Vec::with_capacity(world);
12775            let mut in_q = Vec::with_capacity(world);
12776            let mut in_d = Vec::with_capacity(world);
12777            let mut input = Vec::with_capacity(world);
12778            let mut ev_rank = Vec::with_capacity(world);
12779            let moe_direct = moe_direct_on();
12780            for (rank, engine) in self.ranks.iter().enumerate() {
12781                let _main = engine.gpu.enter_main()?;
12782                gate_out.push(engine.uninit(n_sel * local_out)?);
12783                up_out.push(engine.uninit(n_sel * local_out)?);
12784                act_q.push(engine.uninit_i8(n_sel * local_out)?);
12785                act_d.push(engine.uninit(n_sel * local_out / 32)?);
12786                sel.push(engine.htod_i32(&vec![0i32; n_sel])?);
12787                partial.push(engine.uninit(n_sel * experts.input_width)?);
12788                // Direct join: peer accumulators live on ROOT (single P2P store pass).
12789                if moe_direct && rank != 0 {
12790                    let root = &self.ranks[0];
12791                    let _root_main = root.gpu.enter_main()?;
12792                    accumulator.push(root.zeros(experts.input_width)?);
12793                } else {
12794                    accumulator.push(engine.zeros(experts.input_width)?);
12795                }
12796                combine_w.push(engine.htod(&vec![0.0f32; n_sel])?);
12797                route_w.push(engine.htod(&vec![0.0f32; n_sel])?);
12798                in_q.push(engine.uninit_i8(experts.input_width)?);
12799                in_d.push(engine.uninit(experts.input_width / 32)?);
12800                input.push(engine.uninit(experts.input_width)?);
12801                ev_rank.push(engine.ctx().new_event(None)?);
12802            }
12803            let root = &self.ranks[0];
12804            let _main = root.gpu.enter_main()?;
12805            *workspace_guard = Some(Nvfp4DeviceRoutesWorkspace {
12806                prestaged: false,
12807                rank1_routed: false,
12808                ev_input: None,
12809                fence_flags_raw: 0,
12810                fence_ticket: 0,
12811                gate_out,
12812                up_out,
12813                act_q,
12814                act_d,
12815                sel,
12816                partial,
12817                accumulator,
12818                combine_w,
12819                route_w,
12820                in_q,
12821                in_d,
12822                dev_route_e: None,
12823                in_stage_e: None,
12824                out_stage_e: None,
12825                routes_graph: None,
12826                raw_dev_route_e: None,
12827                raw_combine: None,
12828                raw_input: Vec::new(),
12829                raw_sel: Vec::new(),
12830                raw_route_w: Vec::new(),
12831                remote: root.uninit(experts.input_width)?,
12832                combined: root.uninit(experts.input_width)?,
12833                n_sel,
12834                input,
12835                ev_rank,
12836                ev_done: Some(root.ctx().new_event(None)?),
12837                ev_entry: None,
12838            });
12839        }
12840        let workspace = workspace_guard
12841            .as_mut()
12842            .expect("NVFP4 device routes workspace initialized above");
12843        // EP2 uses this call only as the workspace-arming warmup (the prejoin path drives
12844        // decode); its host-routed sweep semantics do not apply to whole-expert banks.
12845        if experts.ep2 {
12846            return Ok(vec![0.0f32; experts.input_width]);
12847        }
12848        if workspace.n_sel != n_sel {
12849            return Err(format!(
12850                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
12851                workspace.n_sel
12852            )
12853            .into());
12854        }
12855        for &expert in selected {
12856            if expert >= experts.expert_count {
12857                return Err(format!(
12858                    "NVFP4 device selected expert {expert} outside 0..{}",
12859                    experts.expert_count
12860                )
12861                .into());
12862            }
12863        }
12864        let sel_i32 = selected
12865            .iter()
12866            .map(|&expert| expert as i32)
12867            .collect::<Vec<_>>();
12868
12869        // BATCHED program (2026-08-20): per rank, ONE launch per sweep (gate, up, SwiGLU,
12870        // down) covers every selected expert via the selection array and the contiguous bank —
12871        // the per-expert launch loop was pure host latency (~100 sequential launches/layer,
12872        // 291us wall for ~35us of arithmetic). Per (expert, row) the kernels are bit-identical
12873        // to the per-expert forms, and the route-weight axpy chain keeps its exact sequential
12874        // accumulation order — the program's values are unchanged.
12875        for (rank_index, engine) in self.ranks.iter().enumerate() {
12876            let _main = engine.gpu.enter_main()?;
12877            let device_input = engine.htod(input)?;
12878            let Nvfp4DeviceRoutesWorkspace { in_q, in_d, .. } = &mut *workspace;
12879            engine.quantize_q8_1_into(
12880                &device_input,
12881                1,
12882                experts.input_width,
12883                &mut in_q[rank_index],
12884                &mut in_d[rank_index],
12885            )?;
12886            // device_input frees on this rank's stream after the quantize — same-stream order.
12887        }
12888        self.nvfp4_routes_batched_sweeps(
12889            experts,
12890            workspace,
12891            selected,
12892            route_weights,
12893            &sel_i32,
12894            local_out,
12895            n_sel,
12896            activation_limit,
12897            false,
12898        )?;
12899
12900        // Combine: fence the remote shard's producer stream, peer-copy its accumulator to root,
12901        // reduce in canonical shard order, read back once.
12902        let root = &self.ranks[0];
12903        for engine in &self.ranks[1..] {
12904            let _main = engine.gpu.enter_main()?;
12905            engine.stream().synchronize()?;
12906        }
12907        let _main = root.gpu.enter_main()?;
12908        root.stream()
12909            .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
12910        root.add(
12911            &workspace.accumulator[0],
12912            &workspace.remote,
12913            &mut workspace.combined,
12914            experts.input_width,
12915        )?;
12916        let output = root.dtoh(&workspace.combined)?;
12917        if let Some(started) = started {
12918            use std::sync::atomic::Ordering;
12919            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
12920                + started.elapsed().as_nanos() as u64;
12921            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
12922            if calls.is_multiple_of(430) {
12923                eprintln!(
12924                    "[nvfp4-dev-routes-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
12925                    ns as f64 / 1.0e6,
12926                    ns as f64 / calls as f64 / 1.0e3,
12927                );
12928            }
12929        }
12930        Ok(output)
12931    }
12932
12933    /// The shared batched sweeps of the device routes program: per rank, upload the selection,
12934    /// reset the accumulator, run the gate/up/SwiGLU/down batched launches, then the
12935    /// route-weight axpy chain in exact sequential per-pair order. Every op queues on the
12936    /// owning rank's stream; callers own input acquisition and the combine.
12937    #[allow(clippy::too_many_arguments)]
12938    fn nvfp4_routes_batched_sweeps(
12939        &self,
12940        experts: &ResidentNvfp4TensorParallel,
12941        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12942        selected: &[usize],
12943        route_weights: &[f32],
12944        sel_i32: &[i32],
12945        local_out: usize,
12946        n_sel: usize,
12947        activation_limit: Option<f32>,
12948        device_routed: bool,
12949    ) -> Result<(), Box<dyn std::error::Error>> {
12950        for rank_index in 0..self.ranks.len() {
12951            self.nvfp4_routes_batched_sweeps_rank(
12952                experts,
12953                workspace,
12954                selected,
12955                route_weights,
12956                sel_i32,
12957                local_out,
12958                n_sel,
12959                activation_limit,
12960                device_routed,
12961                rank_index,
12962            )?;
12963        }
12964        Ok(())
12965    }
12966
12967    /// One rank's sweeps (the per-rank body of `nvfp4_routes_batched_sweeps`) — separated so
12968    /// the graph door can capture each rank's segment on its own stream.
12969    #[allow(clippy::too_many_arguments)]
12970    fn nvfp4_routes_batched_sweeps_rank(
12971        &self,
12972        experts: &ResidentNvfp4TensorParallel,
12973        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12974        selected: &[usize],
12975        route_weights: &[f32],
12976        sel_i32: &[i32],
12977        local_out: usize,
12978        n_sel: usize,
12979        activation_limit: Option<f32>,
12980        device_routed: bool,
12981        rank_index: usize,
12982    ) -> Result<(), Box<dyn std::error::Error>> {
12983        {
12984            let engine = &self.ranks[rank_index];
12985            let _main = engine.gpu.enter_main()?;
12986            // EP2: whole-expert full-width sweep, owner-guarded; down+combine fused writes
12987            // this rank's slot-ordered partial straight into its accumulator (the join is
12988            // unchanged). Device-routed only — the host-routed arm and the graph door refuse
12989            // at the caller.
12990            if experts.ep2 {
12991                if !device_routed {
12992                    return Err("NVFP4 EP2 banks support the device-routed decode arm only".into());
12993                }
12994                let gate_bank = &experts.gate[rank_index];
12995                let up_bank = &experts.up[rank_index];
12996                if gate_bank.local_out != experts.expert_width
12997                    || gate_bank.expert_bytes != up_bank.expert_bytes
12998                {
12999                    return Err("NVFP4 EP2 bank geometry drifted".into());
13000                }
13001                {
13002                    let Nvfp4DeviceRoutesWorkspace {
13003                        sel,
13004                        gate_out,
13005                        up_out,
13006                        in_q,
13007                        in_d,
13008                        ..
13009                    } = &mut *workspace;
13010                    engine.qmatvec_nvfp4_sel_gu_ep_into(
13011                        &gate_bank.bank,
13012                        &up_bank.bank,
13013                        &sel[rank_index],
13014                        &in_q[rank_index],
13015                        &in_d[rank_index],
13016                        &mut gate_out[rank_index],
13017                        &mut up_out[rank_index],
13018                        n_sel,
13019                        gate_bank.in_features,
13020                        gate_bank.local_out,
13021                        gate_bank.row_bytes,
13022                        gate_bank.expert_bytes,
13023                        rank_index,
13024                    )?;
13025                }
13026                {
13027                    let Nvfp4DeviceRoutesWorkspace {
13028                        gate_out,
13029                        up_out,
13030                        sel,
13031                        act_q,
13032                        act_d,
13033                        ..
13034                    } = &mut *workspace;
13035                    engine.silu_mul_scaled_q8_1_sel_ep_into(
13036                        &gate_out[rank_index],
13037                        &up_out[rank_index],
13038                        &experts.macros_gate_dev[rank_index],
13039                        &experts.macros_up_dev[rank_index],
13040                        &sel[rank_index],
13041                        activation_limit,
13042                        &mut act_q[rank_index],
13043                        &mut act_d[rank_index],
13044                        local_out,
13045                        n_sel,
13046                        rank_index,
13047                    )?;
13048                }
13049                let shard = &experts.down[rank_index];
13050                if shard.device_rank != rank_index || shard.local_in != local_out {
13051                    return Err("NVFP4 EP2 down bank placement drifted".into());
13052                }
13053                {
13054                    let Nvfp4DeviceRoutesWorkspace {
13055                        sel,
13056                        act_q,
13057                        act_d,
13058                        route_w,
13059                        accumulator,
13060                        ..
13061                    } = &mut *workspace;
13062                    engine.qmatvec_nvfp4_sel_down8_ep_into(
13063                        &shard.bank,
13064                        &sel[rank_index],
13065                        &act_q[rank_index],
13066                        &act_d[rank_index],
13067                        &route_w[rank_index],
13068                        &experts.macros_down_dev[rank_index],
13069                        &mut accumulator[rank_index],
13070                        n_sel,
13071                        shard.local_in,
13072                        shard.out_features,
13073                        shard.row_bytes,
13074                        shard.expert_bytes,
13075                        local_out,
13076                        local_out / 32,
13077                        rank_index,
13078                    )?;
13079                }
13080                return Ok(());
13081            }
13082            if !device_routed {
13083                engine.htod_i32_into(&mut workspace.sel[rank_index], sel_i32)?;
13084                // Folded combine weights (route_weight x down macro) — one 40-byte upload
13085                // replaces the accumulator reset + n_sel sequential axpy launches below.
13086                let folded = (0..n_sel)
13087                    .map(|pair| route_weights[pair] * experts.macros_down[selected[pair]])
13088                    .collect::<Vec<_>>();
13089                let mut view = workspace.combine_w[rank_index].slice_mut(0..n_sel);
13090                engine.stream().memcpy_htod(&folded, &mut view)?;
13091            }
13092            let gate_bank = &experts.gate[rank_index];
13093            let up_bank = &experts.up[rank_index];
13094            let (aq, ad) = (&workspace.in_q[rank_index], &workspace.in_d[rank_index]);
13095            // PROGRAM 2 (`MEMRA_NVFP4_SEL_GU`): the two sweeps share sel/aq/ad and, when the
13096            // geometry matches exactly, one launch covers both — per-row bit-identical, double
13097            // the grid fill. Armed by ITS OWN door, and additionally guarded on both banks
13098            // reporting slot-major, because the fused kernel reads only that byte map. Its door
13099            // is separate from PROGRAM 1's on purpose: in the removed implementation it armed
13100            // silently on the bank predicate, so the bank layout and this fusion could never be
13101            // priced apart (DIAGNOSIS.md, "the bisect could not name the mechanism").
13102            let gu_fused = sel_gu_fused_on()
13103                && gate_bank.slot_major
13104                && up_bank.slot_major
13105                && gate_bank.in_features == up_bank.in_features
13106                && gate_bank.local_out == up_bank.local_out
13107                && gate_bank.row_bytes == up_bank.row_bytes
13108                && gate_bank.expert_bytes == up_bank.expert_bytes;
13109            // ENGAGEMENT RECEIPT for PROGRAM 2, one line per DISTINCT decision combo. The
13110            // removed implementation had this behind MEMRA_SWEEP_TRACE and its own comment said
13111            // why it existed: "a silently-dead fusion reads as roofline physics without it".
13112            // It is unconditional here, because a perf row whose fusion never armed is worse
13113            // than no row -- it is a number that looks like evidence.
13114            {
13115                static SEEN_GU: std::sync::Mutex<Vec<(bool, bool, bool)>> =
13116                    std::sync::Mutex::new(Vec::new());
13117                let combo = (gu_fused, sel_gu_fused_on(), gate_bank.slot_major);
13118                let mut seen = SEEN_GU.lock().unwrap();
13119                if !seen.contains(&combo) {
13120                    seen.push(combo);
13121                    eprintln!(
13122                        "[nvfp4-sweep] gu_fused={} door={} slot_major={} geometry_match={} \
13123                         in_f={} out_f={} n_sel={n_sel}",
13124                        gu_fused,
13125                        sel_gu_fused_on(),
13126                        gate_bank.slot_major,
13127                        gate_bank.in_features == up_bank.in_features
13128                            && gate_bank.local_out == up_bank.local_out
13129                            && gate_bank.row_bytes == up_bank.row_bytes
13130                            && gate_bank.expert_bytes == up_bank.expert_bytes,
13131                        gate_bank.in_features,
13132                        gate_bank.local_out
13133                    );
13134                }
13135            }
13136            if gu_fused {
13137                let Nvfp4DeviceRoutesWorkspace {
13138                    sel,
13139                    gate_out,
13140                    up_out,
13141                    in_q,
13142                    in_d,
13143                    ..
13144                } = &mut *workspace;
13145                engine.qmatvec_nvfp4_sel_gu_into(
13146                    &gate_bank.bank,
13147                    &up_bank.bank,
13148                    &sel[rank_index],
13149                    &in_q[rank_index],
13150                    &in_d[rank_index],
13151                    &mut gate_out[rank_index],
13152                    &mut up_out[rank_index],
13153                    n_sel,
13154                    gate_bank.in_features,
13155                    gate_bank.local_out,
13156                    gate_bank.row_bytes,
13157                    gate_bank.expert_bytes,
13158                    gate_bank.slot_major,
13159                )?;
13160            } else {
13161                engine.qmatvec_nvfp4_sel_into(
13162                    &gate_bank.bank,
13163                    &workspace.sel[rank_index],
13164                    aq,
13165                    ad,
13166                    &mut workspace.gate_out[rank_index],
13167                    n_sel,
13168                    gate_bank.in_features,
13169                    gate_bank.local_out,
13170                    gate_bank.row_bytes,
13171                    gate_bank.expert_bytes,
13172                    0,
13173                    0,
13174                    gate_bank.slot_major,
13175                )?;
13176                engine.qmatvec_nvfp4_sel_into(
13177                    &up_bank.bank,
13178                    &workspace.sel[rank_index],
13179                    aq,
13180                    ad,
13181                    &mut workspace.up_out[rank_index],
13182                    n_sel,
13183                    up_bank.in_features,
13184                    up_bank.local_out,
13185                    up_bank.row_bytes,
13186                    up_bank.expert_bytes,
13187                    0,
13188                    0,
13189                    up_bank.slot_major,
13190                )?;
13191            }
13192            // Fused macro-scaled SwiGLU that EMITS q8_1 directly — down consumes it with no
13193            // separate quantize launch. act[rank] IS down canonical shard `rank_index`'s
13194            // input-column window (the geometry gift; see the method doc).
13195            {
13196                let Nvfp4DeviceRoutesWorkspace {
13197                    gate_out,
13198                    up_out,
13199                    sel,
13200                    act_q,
13201                    act_d,
13202                    ..
13203                } = &mut *workspace;
13204                engine.silu_mul_scaled_q8_1_sel_into(
13205                    &gate_out[rank_index],
13206                    &up_out[rank_index],
13207                    &experts.macros_gate_dev[rank_index],
13208                    &experts.macros_up_dev[rank_index],
13209                    &sel[rank_index],
13210                    activation_limit,
13211                    &mut act_q[rank_index],
13212                    &mut act_d[rank_index],
13213                    local_out,
13214                    n_sel,
13215                )?;
13216            }
13217            let shard = &experts.down[rank_index];
13218            if shard.device_rank != rank_index || shard.local_in != local_out {
13219                return Err(
13220                    "NVFP4 device routes: down canonical shard placement drifted from \
13221                     the gate/up column split"
13222                        .into(),
13223                );
13224            }
13225            // PROGRAM 3 (`MEMRA_NVFP4_SEL_DOWN8`): the down sweep and the route-weight combine
13226            // in ONE launch, one warp per SLOT instead of one warp per (row, slot), and the
13227            // `n_sel x out_f` partial round trip gone. Device-routed only — the host-routed arm
13228            // folds the macro into `combine_w` instead of reading `md` on device — and
13229            // slot-major only, read off the shard. `nsb <= 32` is the fit-block class the reduce
13230            // identity is argued at. Its own door, priced LAST and only on green gates for the
13231            // programs beneath it (lane mandate, milestone 5).
13232            let down8 =
13233                device_routed && sel_down8_on() && shard.slot_major && (shard.local_in >> 5) <= 32;
13234            // ENGAGEMENT RECEIPT for PROGRAM 3, one line per distinct combo. `device_routed`
13235            // and `nsb <= 32` are printed because they are the two eligibility conditions that
13236            // can silently disqualify the arm on a geometry or a route the operator did not
13237            // expect -- exactly the case where a flat perf row would be misread as "no win".
13238            {
13239                static SEEN_D8: std::sync::Mutex<Vec<(bool, bool, bool, bool)>> =
13240                    std::sync::Mutex::new(Vec::new());
13241                let combo = (down8, sel_down8_on(), device_routed, shard.slot_major);
13242                let mut seen = SEEN_D8.lock().unwrap();
13243                if !seen.contains(&combo) {
13244                    seen.push(combo);
13245                    // `door_source` is what makes this line a DEFAULT-flip receipt rather than
13246                    // only an engagement receipt: `door=true door_source=default-on` is the
13247                    // flip doing the work, `env=1` is a recipe doing it, and
13248                    // `down8=false door=true` is the silent-no-op shape that PROGRAM 1's
13249                    // default exists to prevent.
13250                    eprintln!(
13251                        "[nvfp4-sweep] down8={} door={} door_source={} device_routed={} \
13252                         slot_major={} nsb={} in_class={} n_sel={n_sel}",
13253                        down8,
13254                        sel_down8_on(),
13255                        sel_down8_source().1,
13256                        device_routed,
13257                        shard.slot_major,
13258                        shard.local_in >> 5,
13259                        (shard.local_in >> 5) <= 32
13260                    );
13261                }
13262            }
13263            if down8 {
13264                let Nvfp4DeviceRoutesWorkspace {
13265                    sel,
13266                    act_q,
13267                    act_d,
13268                    route_w,
13269                    accumulator,
13270                    ..
13271                } = &mut *workspace;
13272                engine.qmatvec_nvfp4_sel_down8_into(
13273                    &shard.bank,
13274                    &sel[rank_index],
13275                    &act_q[rank_index],
13276                    &act_d[rank_index],
13277                    &route_w[rank_index],
13278                    &experts.macros_down_dev[rank_index],
13279                    &mut accumulator[rank_index],
13280                    n_sel,
13281                    shard.local_in,
13282                    shard.out_features,
13283                    shard.row_bytes,
13284                    shard.expert_bytes,
13285                    local_out,
13286                    local_out / 32,
13287                    shard.slot_major,
13288                )?;
13289            } else {
13290                let Nvfp4DeviceRoutesWorkspace {
13291                    sel,
13292                    act_q,
13293                    act_d,
13294                    partial,
13295                    ..
13296                } = &mut *workspace;
13297                engine.qmatvec_nvfp4_sel_into(
13298                    &shard.bank,
13299                    &sel[rank_index],
13300                    &act_q[rank_index],
13301                    &act_d[rank_index],
13302                    &mut partial[rank_index],
13303                    n_sel,
13304                    shard.local_in,
13305                    shard.out_features,
13306                    shard.row_bytes,
13307                    shard.expert_bytes,
13308                    local_out,
13309                    local_out / 32,
13310                    shard.slot_major,
13311                )?;
13312            }
13313            // Route-weight accumulation: axpy_rows_seq keeps the exact sequential per-pair
13314            // FP chain of the reset + n_sel axpy launches in ONE launch. Device-routed calls
13315            // fold the down macro in-kernel from the device selection. (down8 already produced
13316            // the accumulator inside the sweep.)
13317            if !down8 {
13318                let Nvfp4DeviceRoutesWorkspace {
13319                    partial,
13320                    combine_w,
13321                    route_w,
13322                    sel,
13323                    accumulator,
13324                    ..
13325                } = &mut *workspace;
13326                if device_routed {
13327                    engine.axpy_rows_seq_md_into(
13328                        &partial[rank_index],
13329                        &route_w[rank_index],
13330                        &experts.macros_down_dev[rank_index],
13331                        &sel[rank_index],
13332                        &mut accumulator[rank_index],
13333                        experts.input_width,
13334                        n_sel,
13335                    )?;
13336                } else {
13337                    engine.axpy_rows_seq_into(
13338                        &partial[rank_index],
13339                        &combine_w[rank_index],
13340                        &mut accumulator[rank_index],
13341                        experts.input_width,
13342                        n_sel,
13343                    )?;
13344                }
13345            }
13346        }
13347        Ok(())
13348    }
13349
13350    /// Device-IO twin of `run_tensor_parallel_routes_nvfp4_device`: the layer input arrives as
13351    /// a device row on the model engine `e` and the combined output returns as a fresh
13352    /// `e`-context row — no host round-trip, no host stream sync. Ordering is evented (the v2
13353    /// attention discipline): `ev_entry` is recorded on `e`'s stream AFTER the caller queued
13354    /// the input's producer; each rank waits it before its peer read; the root reduce waits
13355    /// every rank's done event; `e` waits the root's done event before copying out. The
13356    /// program bytes are identical to the host-IO twin — dtoh/htod and dtod preserve f32 bits.
13357    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
13358    pub fn run_tensor_parallel_routes_nvfp4_device_io(
13359        &self,
13360        experts: &ResidentNvfp4TensorParallel,
13361        e: &Engine,
13362        input_dev: &crate::CudaSlice<f32>,
13363        selected: &[usize],
13364        route_weights: &[f32],
13365        experts_per_token: usize,
13366        activation_limit: Option<f32>,
13367    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
13368        if input_dev.len() != experts.input_width {
13369            return Err(format!(
13370                "NVFP4 device-io routes input {} != width {}",
13371                input_dev.len(),
13372                experts.input_width
13373            )
13374            .into());
13375        }
13376        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
13377            return Err(format!(
13378                "NVFP4 device-io routes selected={} weights={} != experts/token {experts_per_token}",
13379                selected.len(),
13380                route_weights.len(),
13381            )
13382            .into());
13383        }
13384        if !route_weights.iter().all(|weight| weight.is_finite()) {
13385            return Err("NVFP4 device route weights contain a non-finite value".into());
13386        }
13387        let world = self.ranks.len();
13388        if world != NVFP4_CANONICAL_ROW_SHARDS {
13389            return Err(format!(
13390                "NVFP4 device routes require world == canonical shard grid \
13391                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
13392            )
13393            .into());
13394        }
13395        let local_out = experts.expert_width / world;
13396        let n_sel = experts_per_token;
13397
13398        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
13399        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
13400        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
13401        let started = timing.then(std::time::Instant::now);
13402
13403        let mut workspace_guard = experts
13404            .device_workspace
13405            .lock()
13406            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
13407        if workspace_guard.is_none() {
13408            drop(workspace_guard);
13409            // Build through the host-IO ensure path exactly once: run it with a zero input.
13410            // Cheaper than duplicating the init; the first real call overwrites everything.
13411            let zero = vec![0.0f32; experts.input_width];
13412            let zero_sel = vec![0usize; n_sel];
13413            let zero_w = vec![0.0f32; n_sel];
13414            let _ = self.run_tensor_parallel_routes_nvfp4_device(
13415                experts,
13416                &zero,
13417                &zero_sel,
13418                &zero_w,
13419                n_sel,
13420                activation_limit,
13421            )?;
13422            workspace_guard = experts
13423                .device_workspace
13424                .lock()
13425                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
13426        }
13427        let workspace = workspace_guard
13428            .as_mut()
13429            .expect("NVFP4 device routes workspace initialized above");
13430        if workspace.n_sel != n_sel {
13431            return Err(format!(
13432                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
13433                workspace.n_sel
13434            )
13435            .into());
13436        }
13437        for &expert in selected {
13438            if expert >= experts.expert_count {
13439                return Err(format!(
13440                    "NVFP4 device selected expert {expert} outside 0..{}",
13441                    experts.expert_count
13442                )
13443                .into());
13444            }
13445        }
13446        let sel_i32 = selected
13447            .iter()
13448            .map(|&expert| expert as i32)
13449            .collect::<Vec<_>>();
13450
13451        // Entry fence: e's stream position covers the input's producer AND every consumer of
13452        // the previous layer's output (queued on e's stream before this call), guarding the
13453        // workspace reuse exactly like the v2 attention driver.
13454        if let Some((_, device)) = workspace.ev_entry.as_ref() {
13455            if *device != e.ctx().ordinal() {
13456                return Err("NVFP4 device-io routes engine changed".into());
13457            }
13458        } else {
13459            let _main = e.gpu.enter_main()?;
13460            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
13461        }
13462        {
13463            let _main = e.gpu.enter_main()?;
13464            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
13465            ev_entry.record(&e.stream())?;
13466        }
13467        for (rank_index, engine) in self.ranks.iter().enumerate() {
13468            let _main = engine.gpu.enter_main()?;
13469            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
13470            engine.stream().wait(ev_entry)?;
13471            {
13472                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
13473                engine
13474                    .stream()
13475                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
13476            }
13477            {
13478                let Nvfp4DeviceRoutesWorkspace {
13479                    input, in_q, in_d, ..
13480                } = &mut *workspace;
13481                engine.quantize_q8_1_into(
13482                    &input[rank_index],
13483                    1,
13484                    experts.input_width,
13485                    &mut in_q[rank_index],
13486                    &mut in_d[rank_index],
13487                )?;
13488            }
13489        }
13490        self.nvfp4_routes_batched_sweeps(
13491            experts,
13492            workspace,
13493            selected,
13494            route_weights,
13495            &sel_i32,
13496            local_out,
13497            n_sel,
13498            activation_limit,
13499            false,
13500        )?;
13501
13502        // Evented combine: rank done events replace the host stream syncs, the reduce runs on
13503        // the root stream in canonical shard order, and e copies the combined row out behind
13504        // the root's done event.
13505        // rank0 == root: its own stream order already covers its sweep; only the PEER
13506        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
13507        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
13508            let _main = engine.gpu.enter_main()?;
13509            workspace.ev_rank[rank_index].record(&engine.stream())?;
13510        }
13511        if moe_direct_on() && self.ranks.len() == 2 {
13512            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
13513            // rank0's is root-stream-ordered. One root event + rank1's own event order
13514            // the model engine's single add — same operand order as root's add
13515            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
13516            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
13517            // hazard class does not apply).
13518            {
13519                let root = &self.ranks[0];
13520                let _main = root.gpu.enter_main()?;
13521                workspace
13522                    .ev_done
13523                    .as_ref()
13524                    .expect("device routes done event")
13525                    .record(&root.stream())?;
13526            }
13527            let _main = e.gpu.enter_main()?;
13528            e.stream().wait(
13529                workspace
13530                    .ev_done
13531                    .as_ref()
13532                    .expect("device routes done event"),
13533            )?;
13534            for ev in workspace.ev_rank.iter().skip(1) {
13535                e.stream().wait(ev)?;
13536            }
13537            let mut output = e.uninit(experts.input_width)?;
13538            e.add(
13539                &workspace.accumulator[0],
13540                &workspace.accumulator[1],
13541                &mut output,
13542                experts.input_width,
13543            )?;
13544            let output = output;
13545            if let Some(started) = started {
13546                use std::sync::atomic::Ordering;
13547                let ns = TIMING_NS
13548                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
13549                    + started.elapsed().as_nanos() as u64;
13550                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
13551                if calls.is_multiple_of(430) {
13552                    eprintln!(
13553                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
13554                        ns as f64 / 1.0e6,
13555                        ns as f64 / calls as f64 / 1.0e3,
13556                    );
13557                }
13558            }
13559            return Ok(output);
13560        }
13561        {
13562            let root = &self.ranks[0];
13563            let _main = root.gpu.enter_main()?;
13564            for ev in workspace.ev_rank.iter().skip(1) {
13565                root.stream().wait(ev)?;
13566            }
13567            root.stream()
13568                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
13569            {
13570                let Nvfp4DeviceRoutesWorkspace {
13571                    accumulator,
13572                    remote,
13573                    combined,
13574                    ..
13575                } = &mut *workspace;
13576                root.add(&accumulator[0], remote, combined, experts.input_width)?;
13577            }
13578            workspace
13579                .ev_done
13580                .as_ref()
13581                .expect("device routes done event")
13582                .record(&root.stream())?;
13583        }
13584        let output = {
13585            let _main = e.gpu.enter_main()?;
13586            e.stream().wait(
13587                workspace
13588                    .ev_done
13589                    .as_ref()
13590                    .expect("device routes done event"),
13591            )?;
13592            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
13593            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
13594            let mut output = e.uninit(experts.input_width)?;
13595            e.stream().memcpy_dtod(
13596                &workspace.combined.slice(0..experts.input_width),
13597                &mut output.slice_mut(0..experts.input_width),
13598            )?;
13599            output
13600        };
13601        if let Some(started) = started {
13602            use std::sync::atomic::Ordering;
13603            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
13604                + started.elapsed().as_nanos() as u64;
13605            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
13606            if calls.is_multiple_of(430) {
13607                eprintln!(
13608                    "[nvfp4-dev-routes-io-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
13609                    ns as f64 / 1.0e6,
13610                    ns as f64 / calls as f64 / 1.0e3,
13611                );
13612            }
13613        }
13614        Ok(output)
13615    }
13616
13617    /// Device-routed twin of `run_tensor_parallel_routes_nvfp4_device_io`: the selection and
13618    /// route weights arrive as the device router's e-context outputs — the per-layer host
13619    /// logits readback disappears. The fresh router outputs are staged into persistent
13620    /// e-context buffers on e's stream (never-free discipline) before the entry event; each
13621    /// rank peer-reads them behind it. The down-macro fold happens in-kernel.
13622    #[allow(clippy::too_many_arguments)]
13623    /// Prestage the routed-expert input: pull the shared row to every rank and quantize it
13624    /// there, WITHOUT the selection — callable before the router so the rank chains overlap
13625    /// it. No-op (returns false) when the workspace is not built yet or the door is off;
13626    /// the routed run then does its own staging as before.
13627    pub fn nvfp4_routes_prestage(
13628        &self,
13629        experts: &ResidentNvfp4TensorParallel,
13630        e: &Engine,
13631        input_dev: &crate::CudaSlice<f32>,
13632    ) -> Result<bool, Box<dyn std::error::Error>> {
13633        self.nvfp4_routes_prestage_with(experts, e, input_dev, |_, _, _, _| Ok(false))
13634    }
13635
13636    /// `nvfp4_routes_prestage` with a PEER-ROUTER hook: after rank1's input pull +
13637    /// quantize, the hook may compute rank1's route selection LOCALLY (replicated router —
13638    /// deterministic kernels on identical input bits produce identical sel/w, so the
13639    /// selection is bit-equal to the root's). Returns true when it wrote sel/route_w; the
13640    /// routed run then skips rank1's sel pull.
13641    pub fn nvfp4_routes_prestage_with(
13642        &self,
13643        experts: &ResidentNvfp4TensorParallel,
13644        e: &Engine,
13645        input_dev: &crate::CudaSlice<f32>,
13646        rank1_router: impl FnOnce(
13647            &Engine,
13648            &crate::CudaSlice<f32>,
13649            &mut crate::CudaSlice<i32>,
13650            &mut crate::CudaSlice<f32>,
13651        ) -> Result<bool, Box<dyn std::error::Error>>,
13652    ) -> Result<bool, Box<dyn std::error::Error>> {
13653        if !routes_prestage_on() || step_tp_graph_enabled()? {
13654            return Ok(false);
13655        }
13656        if input_dev.len() != experts.input_width {
13657            return Err("NVFP4 prestage input width mismatch".into());
13658        }
13659        let mut workspace_guard = experts
13660            .device_workspace
13661            .lock()
13662            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
13663        let Some(workspace) = workspace_guard.as_mut() else {
13664            return Ok(false);
13665        };
13666        if workspace.ev_input.is_none() {
13667            let _main = e.gpu.enter_main()?;
13668            workspace.ev_input = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
13669        } else if workspace.ev_input.as_ref().map(|(_, d)| *d) != Some(e.ctx().ordinal()) {
13670            return Err("NVFP4 prestage engine changed".into());
13671        }
13672        {
13673            let _main = e.gpu.enter_main()?;
13674            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
13675            ev.record(&e.stream())?;
13676        }
13677        for (rank_index, engine) in self.ranks.iter().enumerate() {
13678            let _main = engine.gpu.enter_main()?;
13679            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
13680            engine.stream().wait(ev)?;
13681            {
13682                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
13683                engine
13684                    .stream()
13685                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
13686            }
13687            {
13688                let Nvfp4DeviceRoutesWorkspace {
13689                    input, in_q, in_d, ..
13690                } = &mut *workspace;
13691                engine.quantize_q8_1_into(
13692                    &input[rank_index],
13693                    1,
13694                    experts.input_width,
13695                    &mut in_q[rank_index],
13696                    &mut in_d[rank_index],
13697                )?;
13698            }
13699        }
13700        if self.ranks.len() == 2 {
13701            let rank1 = &self.ranks[1];
13702            let _r1 = rank1.gpu.enter_main()?;
13703            let Nvfp4DeviceRoutesWorkspace {
13704                input,
13705                sel,
13706                route_w,
13707                ..
13708            } = &mut *workspace;
13709            let (in1, rest_sel) = (&input[1], &mut sel[1]);
13710            if rank1_router(rank1, in1, rest_sel, &mut route_w[1])? {
13711                workspace.rank1_routed = true;
13712            }
13713        }
13714        workspace.prestaged = true;
13715        Ok(true)
13716    }
13717
13718    /// STEP TP2 GEMM PRIME (`MEMRA_STEP_GEMM_PRIME`, 2026-08-27, TTFT lane): one grouped
13719    /// f16 GEMM per projection over the RESIDENT NVFP4 banks for a prime chunk of `t` tokens.
13720    ///
13721    /// WHY: the t-row walk primes a 4,092-token prompt in 19.8 s at its widest (GEMV-bound) and
13722    /// the generic batch prime's decode-class MoE takes 240 s; the CUTLASS sizing rows put
13723    /// GEMM-class expert math at 170-270 TFLOP/s on this silicon, i.e. a sub-second cold prime.
13724    /// This reuses the grouped f16 lane end to end (`moe_f16g_act` -> `moe_f16_grouped`
13725    /// direct-from-NVFP4 -> silu pairs -> grouped down) once per RANK against that rank's bank
13726    /// half: gate/up are column-halves (silu runs on matching halves), down is the canonical
13727    /// row-shard pair producing partials joined in the pinned shard order, and the final
13728    /// weighted scatter runs a fixed slot-0..n_used-1 sum per token - no atomics anywhere.
13729    /// Per-expert NVFP4 macro scales land where they must: gate/up BEFORE silu (nonlinear),
13730    /// down folded into the scatter weight.
13731    ///
13732    /// NUMERIC CLASS: the f16-mirror grouped-prefill class other families already serve -
13733    /// admission is the prefill-KV acceptance gate plus the ship-shape tape, not byte identity.
13734    #[allow(clippy::too_many_arguments)]
13735    /// MEMRA_MOE_DETERM_STAGE=1: checksum a stage's device buffer so two back-to-back calls of the
13736    /// grouped routine can be compared STAGE BY STAGE. The routine's OUTPUT is nondeterministic above
13737    /// ~400 tokens on the direct lane (1.9e-7 / 99% of elements at t=4096) while its GEMM kernels are
13738    /// bit-exact in isolation, so the divergence enters somewhere between. The first stage whose
13739    /// checksum differs across the two calls is where.
13740    ///
13741    /// Sum-of-bits, not sum-of-floats: float addition would itself reorder and could mask exactly the
13742    /// class of difference being hunted.
13743    fn determ_stage_bytes(v: &[u8]) -> u64 {
13744        v.iter().fold(0u64, |a, b| {
13745            a.wrapping_mul(1_000_003).wrapping_add(*b as u64)
13746        })
13747    }
13748
13749    /// Checksum an i32 index/offset buffer. The CSR, the active-expert ids and the group
13750    /// offsets are inputs the gate kernel dereferences just as much as the activations are;
13751    /// leaving them unchecksummed is what let "identical inputs, different output" stand on a
13752    /// SUBSET of the inputs for six rounds of this investigation.
13753    fn determ_stage_i32(v: &[i32]) -> u64 {
13754        v.iter().fold(0u64, |a, b| {
13755            a.wrapping_mul(1_000_003).wrapping_add(*b as u32 as u64)
13756        })
13757    }
13758
13759    fn determ_stage_sum(v: &[f32]) -> u64 {
13760        v.iter().fold(0u64, |a, x| {
13761            a.wrapping_mul(1_000_003).wrapping_add(x.to_bits() as u64)
13762        })
13763    }
13764
13765    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
13766    pub fn run_tensor_parallel_routes_nvfp4_prime_grouped(
13767        &self,
13768        experts: &ResidentNvfp4TensorParallel,
13769        e: &Engine,
13770        z_t: &crate::CudaSlice<f32>,
13771        t: usize,
13772        sel: &[i32],
13773        w: &[f32],
13774        n_used: usize,
13775        activation_limit: Option<f32>,
13776    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
13777        let world = self.ranks.len();
13778        if world != NVFP4_CANONICAL_ROW_SHARDS {
13779            return Err("NVFP4 grouped prime requires the canonical 2-shard grid".into());
13780        }
13781        // The dequant must read the layout the bank was BUILT in (feeding slot-major bytes to
13782        // the v1 kernel was a garbage-output bug this line exists for). Taken from the BANK,
13783        // never from the environment: EP2 banks are always slot-major, TP shard banks are
13784        // slot-major only under PROGRAM 1 (`MEMRA_NVFP4_BANK_SM`). All three banks share one
13785        // decision at build (`nvfp4_repack_bank_matrix`), and the assert below refuses to run a
13786        // prime over banks that disagree instead of silently priming one of them wrong.
13787        //
13788        // THIS IS THE LINE THE 2026-08-29 CORRUPTION WENT THROUGH. `QT_NVFP4_V2` selects the
13789        // `kq_fetch` branch whose two prefetch callers omitted `in_f`; the codes stayed right
13790        // and the per-16 scale came from inside the packed-codes region, so the prime produced
13791        // fluent WRONG text. No v2 gate had ever run this GEMM. It is now covered device-side by
13792        // `nvfp4-bank-oracle` (both step37 layer geometries, all four tile forms) and end-to-end
13793        // by a prefill-heavy byte gate. Keep both: a decode-only byte gate proved nothing here.
13794        let slot_major = experts.gate.iter().all(|b| b.slot_major)
13795            && experts.up.iter().all(|b| b.slot_major)
13796            && experts.down.iter().all(|b| b.slot_major);
13797        let any_slot_major = experts.gate.iter().any(|b| b.slot_major)
13798            || experts.up.iter().any(|b| b.slot_major)
13799            || experts.down.iter().any(|b| b.slot_major);
13800        if any_slot_major != slot_major {
13801            return Err(
13802                "NVFP4 grouped prime: gate/up/down banks disagree on the row layout — \
13803                        one grouped GEMM cannot serve two byte maps"
13804                    .into(),
13805            );
13806        }
13807        let bank_qt = if slot_major {
13808            crate::QT_NVFP4_V2
13809        } else {
13810            crate::QT_NVFP4
13811        };
13812        let width = experts.input_width;
13813        let n_expert = experts.expert_count;
13814        let n_pairs = t * n_used;
13815        if sel.len() < n_pairs || w.len() < n_pairs || z_t.len() < t * width {
13816            return Err("NVFP4 grouped prime geometry".into());
13817        }
13818        // MEMRA_PRIME_PROF=1 sub-split of the grouped prime (2026-08-28). The [moe-prof] mark
13819        // around this whole call reads 90% of the MoE bucket, but the call is not just GEMMs:
13820        // it host-builds the CSR, allocates ~6 large device buffers per rank per layer (z_r is
13821        // 67 MB, act is 84 MB at t=4096), and does 5 H2D copies per rank. Tile form, occupancy,
13822        // padding, B double-buffering and register pressure have ALL come back null, which is
13823        // the signature of time that is not in the kernel. So measure HOST wall with no syncs
13824        // for the build and the issue, and let the join wait absorb the GPU time: host-bound and
13825        // GPU-bound then read differently instead of summing into one opaque number.
13826        let gprof = std::env::var("MEMRA_PRIME_PROF").as_deref() == Ok("1") && t >= 16;
13827        let g_t0 = std::time::Instant::now();
13828        // CSR: expert-major pair lists. Host-built - prime is chunk-granular, and the router
13829        // selections arrive host-side from the sigmoid router oracle.
13830        let mut buckets: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
13831        for (p, &s_id) in sel.iter().take(n_pairs).enumerate() {
13832            let s_id = s_id as usize;
13833            if s_id >= n_expert {
13834                return Err(format!("grouped prime selection {s_id} >= {n_expert}").into());
13835            }
13836            buckets[s_id].push(p as i32);
13837        }
13838        let mut ex_ids: Vec<i32> = Vec::new();
13839        let mut ex_off: Vec<i32> = vec![0];
13840        let mut ex_pairs: Vec<i32> = Vec::new();
13841        for (e_id, b) in buckets.iter().enumerate() {
13842            if !b.is_empty() {
13843                ex_ids.push(e_id as i32);
13844                ex_pairs.extend_from_slice(b);
13845                ex_off.push(ex_pairs.len() as i32);
13846            }
13847        }
13848        let n_active = ex_ids.len();
13849        if n_active == 0 {
13850            return e.zeros(t * width);
13851        }
13852        if n_active > 512 {
13853            return Err("grouped prime n_active > 512 (direct lane cap)".into());
13854        }
13855        let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
13856        // pair-id -> CSR row: lets the fused tail read the partials in place, so the prime skips
13857        // a whole [n_pairs, width] permute (532 MB read + write per rank per layer at 4k).
13858        let mut inv = vec![0i32; n_pairs];
13859        for (row, &pair) in ex_pairs.iter().enumerate() {
13860            inv[pair as usize] = row as i32;
13861        }
13862        // Per-CSR-row gate/up macro scales (before silu); down macro folds into the scatter w.
13863        let mg: Vec<f32> = ex_pairs
13864            .iter()
13865            .map(|&p| experts.macros_gate[sel[p as usize] as usize])
13866            .collect();
13867        let mu: Vec<f32> = ex_pairs
13868            .iter()
13869            .map(|&p| experts.macros_up[sel[p as usize] as usize])
13870            .collect();
13871        let wd: Vec<f32> = (0..n_pairs)
13872            .map(|p| w[p] * experts.macros_down[sel[p] as usize])
13873            .collect();
13874        // Pointer tables: built on first use and kept on the bank. Resident banks never move,
13875        // so the old per-rank-per-LAYER rebuild+upload of 3*n_expert u64s was pure prime-path
13876        // host churn (45 layers x 2 ranks x 864 entries per prime).
13877        {
13878            let mut tabs = experts
13879                .prime_tables
13880                .lock()
13881                .map_err(|_| "grouped prime table cache is poisoned")?;
13882            if tabs.len() != world {
13883                tabs.clear();
13884                for rank in 0..world {
13885                    let engine = &self.ranks[rank];
13886                    let _main = engine.gpu.enter_main()?;
13887                    let (gb, ub, db) =
13888                        (&experts.gate[rank], &experts.up[rank], &experts.down[rank]);
13889                    let mut tab = vec![0u64; 3 * n_expert];
13890                    {
13891                        use cudarc::driver::DevicePtr;
13892                        let stream = engine.stream();
13893                        let (pg, _g0) = gb.bank.device_ptr(&stream);
13894                        let (pu, _g1) = ub.bank.device_ptr(&stream);
13895                        let (pd, _g2) = db.bank.device_ptr(&stream);
13896                        for ex in 0..n_expert {
13897                            tab[ex] = pg + (ex * gb.expert_bytes) as u64;
13898                            tab[n_expert + ex] = pu + (ex * ub.expert_bytes) as u64;
13899                            tab[2 * n_expert + ex] = pd + (ex * db.expert_bytes) as u64;
13900                        }
13901                    }
13902                    tabs.push(engine.htod_u64(&tab)?);
13903                }
13904            }
13905        }
13906        let g_csr = g_t0.elapsed().as_secs_f64() * 1e3;
13907        let g_t1 = std::time::Instant::now();
13908        // WHAT ARE THESE RANKS, ACTUALLY (2026-08-28)? The grouped MoE measures join ~ span_sum
13909        // (strictly serialized) at t=4096 while the same kernel hits 40 TFLOP/s standalone, and
13910        // one intervention based on cudarc's peer-copy event was refuted. Before proposing an
13911        // eleventh mechanism, verify the premise the whole question rests on: that the two ranks
13912        // are on DISTINCT devices, contexts and streams. If they share any of those, the
13913        // serialization needs no further explanation. One line per process.
13914        {
13915            static SAID: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
13916            if gprof && !SAID.swap(true, std::sync::atomic::Ordering::Relaxed) {
13917                for rank in 0..world {
13918                    let e_r = &self.ranks[rank];
13919                    let _m = e_r.gpu.enter_main();
13920                    eprintln!(
13921                        "[rank-id] rank={rank} ordinal={} ctx={:?} stream={:?} root_ordinal={} \
13922                         root_stream={:?}",
13923                        e_r.ctx().ordinal(),
13924                        std::sync::Arc::as_ptr(e_r.ctx()),
13925                        e_r.stream().cu_stream(),
13926                        e.ctx().ordinal(),
13927                        e.stream().cu_stream(),
13928                    );
13929                }
13930            }
13931        }
13932
13933        let mut partials: Vec<crate::CudaSlice<f32>> = Vec::with_capacity(world);
13934        let mut ev_rank: Vec<CudaEvent> = Vec::with_capacity(world);
13935        let mut ev_head: Vec<CudaEvent> = Vec::with_capacity(world);
13936        let mut ev_tail_prof: Vec<CudaEvent> = Vec::with_capacity(world);
13937        for rank in 0..world {
13938            let engine = &self.ranks[rank];
13939            let _main = engine.gpu.enter_main()?;
13940            if gprof {
13941                // CU_EVENT_DEFAULT, not None: cudarc's new_event(None) creates the event with
13942                // CU_EVENT_DISABLE_TIMING, and cuEventElapsedTime then returns INVALID_HANDLE.
13943                // That is what failed every span query for two build cycles — the ordering
13944                // events below correctly keep the default, since they are never timed.
13945                let h = engine
13946                    .ctx()
13947                    .new_event(Some(cudarc::driver::sys::CUevent_flags::CU_EVENT_DEFAULT))?;
13948                h.record(&engine.stream())?;
13949                ev_head.push(h);
13950            }
13951            // The grouped-MoE FFI's raw launches follow the RUNTIME API's current device, not
13952            // the pushed driver context — bind it per rank or rank-1 calls die InvalidValue.
13953            engine.bind_runtime_device(engine.ctx().ordinal() as i32)?;
13954            let gb = &experts.gate[rank];
13955            let ub = &experts.up[rank];
13956            let db = &experts.down[rank];
13957            if db.device_rank != rank {
13958                return Err("grouped prime: down shard placement drifted".into());
13959            }
13960            let local_ff = gb.local_out;
13961            if ub.local_out != local_ff || db.local_in != local_ff || db.out_features != width {
13962                return Err("grouped prime: bank width mismatch".into());
13963            }
13964            // All of the rank's host-side staging lands before its first kernel, so the
13965            // launch chain below issues without host copies interleaved.
13966            let csr_tok_d = engine.htod_i32(&csr_tok)?;
13967            let exi_d = engine.htod_i32(&ex_ids)?;
13968            let exoff_d = engine.htod_i32(&ex_off)?;
13969            let mg_d = engine.htod(&mg)?;
13970            let mu_d = engine.htod(&mu)?;
13971            // Per-rank pointer table into the bank shards, slot-major like DevExps::ptr_row.
13972            let tabs_guard = experts
13973                .prime_tables
13974                .lock()
13975                .map_err(|_| "grouped prime table cache is poisoned")?;
13976            let tab_d = &tabs_guard[rank];
13977            let mut z_r = engine.uninit(t * width)?;
13978            {
13979                let mut dst = z_r.slice_mut(0..t * width);
13980                engine
13981                    .stream()
13982                    .memcpy_dtod(&z_t.slice(0..t * width), &mut dst)?;
13983            }
13984            let dstage = std::env::var("MEMRA_MOE_DETERM_STAGE").as_deref() == Ok("1") && t >= 16;
13985            let (z16, zs) = engine.moe_f16g_act(&z_r, Some(&csr_tok_d), width, n_pairs)?;
13986            if dstage {
13987                // z16 is the GEMM's actual DATA input and is a byte buffer; checksumming only
13988                // z_r and zs left "identical inputs" unestablished and produced a localization
13989                // that outran the measurement. Checksum it as bytes.
13990                let zr = engine.dtoh(&z_r)?;
13991                let zsv = engine.dtoh(&zs)?;
13992                let z16v = engine.dtoh_u8(&z16)?;
13993                eprintln!(
13994                    "[determ-stage] rank={rank} t={t} z_r={:016x} zs={:016x} z16={:016x}",
13995                    Self::determ_stage_sum(&zr),
13996                    Self::determ_stage_sum(&zsv),
13997                    Self::determ_stage_bytes(&z16v)
13998                );
13999            }
14000            if dstage {
14001                // INPUT CLOSURE. Everything the gate kernel dereferences, plus the launch
14002                // geometry that decides how it is summed, checksummed in ONE place. A kernel
14003                // proven bit-deterministic on live data, with no atomics, can only diverge if
14004                // (A) some byte it reads differs, (B) the launch differs, or (C) it reads
14005                // outside its declared inputs. This closes A and B; C is what compute-sanitizer
14006                // is for. Partial input sets are how the divergence kept retreating into the
14007                // part that was never measured.
14008                engine.stream().synchronize()?;
14009                let csr_v = engine.dtoh_i32(&csr_tok_d)?;
14010                let exi_v = engine.dtoh_i32(&exi_d)?;
14011                let exo_v = engine.dtoh_i32(&exoff_d)?;
14012                let mg_v = engine.dtoh(&mg_d)?;
14013                let mu_v = engine.dtoh(&mu_d)?;
14014                let tab_v = engine.dtoh_u64(tab_d)?;
14015                eprintln!(
14016                    "[determ-closure] rank={rank} t={t} csr_tok={:016x} exi={:016x} exoff={:016x}                      ex_off_host={:016x} mg={:016x} mu={:016x} tab={:016x} | n_active={n_active}                      n_pairs={n_pairs} width={width} local_ff={local_ff} n_expert={n_expert}                      qt={bank_qt} rb={}",
14017                    Self::determ_stage_i32(&csr_v),
14018                    Self::determ_stage_i32(&exi_v),
14019                    Self::determ_stage_i32(&exo_v),
14020                    Self::determ_stage_i32(&ex_off),
14021                    Self::determ_stage_sum(&mg_v),
14022                    Self::determ_stage_sum(&mu_v),
14023                    tab_v
14024                        .iter()
14025                        .fold(0u64, |a, b| a.wrapping_mul(1_000_003).wrapping_add(*b)),
14026                    gb.row_bytes
14027                );
14028                // The resident weight bank is the GEMM's OTHER operand and was never checked.
14029                // Opt-in because it is a ~424 MB dtoh per rank per layer.
14030                if std::env::var("MEMRA_MOE_DETERM_BANK").as_deref() == Ok("1") {
14031                    let bank_v = engine.dtoh_u8(&gb.bank)?;
14032                    eprintln!(
14033                        "[determ-closure] rank={rank} t={t} gate_bank={:016x} bytes={}",
14034                        Self::determ_stage_bytes(&bank_v),
14035                        bank_v.len()
14036                    );
14037                }
14038            }
14039            let mut g = engine.moe_f16_grouped(
14040                tab_d,
14041                0,
14042                n_expert,
14043                &exi_d,
14044                &ex_off,
14045                &exoff_d,
14046                &z16,
14047                &zs,
14048                width,
14049                local_ff,
14050                n_active,
14051                n_pairs,
14052                bank_qt,
14053                gb.row_bytes,
14054            )?;
14055            engine.scale_rows(&mut g, &mg_d, local_ff, n_pairs)?;
14056            let mut u = engine.moe_f16_grouped(
14057                tab_d,
14058                1,
14059                n_expert,
14060                &exi_d,
14061                &ex_off,
14062                &exoff_d,
14063                &z16,
14064                &zs,
14065                width,
14066                local_ff,
14067                n_active,
14068                n_pairs,
14069                bank_qt,
14070                ub.row_bytes,
14071            )?;
14072            engine.scale_rows(&mut u, &mu_d, local_ff, n_pairs)?;
14073            // step35 routed SwiGLU clamp (per-layer; live only on layers 43/44 for this
14074            // family): min(silu(g), lim) * clamp(u, +-lim). Dropping it was the second
14075            // correctness bug of the first engaged run.
14076            let act = match activation_limit.filter(|l| *l > 1e-6) {
14077                Some(lim) => {
14078                    let mut a = engine.uninit(n_pairs * local_ff)?;
14079                    engine.swiglu_clamped_mul_scaled(
14080                        &g,
14081                        &u,
14082                        1.0,
14083                        1.0,
14084                        lim,
14085                        &mut a,
14086                        n_pairs * local_ff,
14087                    )?;
14088                    a
14089                }
14090                None => engine.moe_pairs_silu_mul(&g, &u, n_pairs * local_ff)?,
14091            };
14092            if dstage {
14093                let gv = engine.dtoh(&g)?;
14094                let uv = engine.dtoh(&u)?;
14095                let av = engine.dtoh(&act)?;
14096                // A SUM tells you THAT gate differs; it does not tell you HOW. ULP-dense diffs
14097                // (nearly every element, ~1e-8) are an ordering/precision class; a handful of
14098                // huge ones are a corruption class. They need different hunts, so measure the
14099                // shape here instead of inferring it later.
14100                let key = (rank, t);
14101                let mut prev_map = DETERM_PREV
14102                    .get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))
14103                    .lock()
14104                    .map_err(|_| "determ prev map poisoned")?;
14105                let shape = match prev_map.get(&key) {
14106                    Some(prev) if prev.len() == gv.len() => {
14107                        let mut md = 0.0f32;
14108                        let mut n_diff = 0usize;
14109                        let mut n_big = 0usize;
14110                        for (a, b) in prev.iter().zip(gv.iter()) {
14111                            let d = (a - b).abs();
14112                            if d > 0.0 {
14113                                n_diff += 1;
14114                            }
14115                            if d > 1e-3 {
14116                                n_big += 1;
14117                            }
14118                            if d > md {
14119                                md = d;
14120                            }
14121                        }
14122                        format!(
14123                            " | vs_prev maxdiff={md:.3e} differing={n_diff}/{} big(>1e-3)={n_big}",
14124                            gv.len()
14125                        )
14126                    }
14127                    _ => String::new(),
14128                };
14129                prev_map.insert(key, gv.clone());
14130                drop(prev_map);
14131                eprintln!(
14132                    "[determ-stage] rank={rank} t={t} gate={:016x} up={:016x} silu={:016x}{shape}",
14133                    Self::determ_stage_sum(&gv),
14134                    Self::determ_stage_sum(&uv),
14135                    Self::determ_stage_sum(&av)
14136                );
14137            }
14138            let (a16, a_s) = engine.moe_f16g_act(&act, None, local_ff, n_pairs)?;
14139            let d_csr = engine.moe_f16_grouped(
14140                tab_d,
14141                2,
14142                n_expert,
14143                &exi_d,
14144                &ex_off,
14145                &exoff_d,
14146                &a16,
14147                &a_s,
14148                local_ff,
14149                width,
14150                n_active,
14151                n_pairs,
14152                bank_qt,
14153                db.row_bytes,
14154            )?;
14155
14156            // No host sync: both ranks' chains must be in flight before anything waits.
14157            // The rank's tail event orders the root's cross-device pulls below.
14158            if dstage {
14159                engine.stream().synchronize()?;
14160                let a16v = engine.dtoh_u8(&a16)?;
14161                let dv = engine.dtoh(&d_csr)?;
14162                eprintln!(
14163                    "[determ-stage] rank={rank} t={t} a16={:016x} down_partial={:016x}",
14164                    Self::determ_stage_bytes(&a16v),
14165                    Self::determ_stage_sum(&dv)
14166                );
14167            }
14168            let ev = engine.ctx().new_event(None)?;
14169            ev.record(&engine.stream())?;
14170            if gprof {
14171                // Per-rank GPU SPAN (2026-08-28). Keep the tail event; the elapsed time is read
14172                // AFTER the join sync below. Reading it here returns NOT_READY (the work has only
14173                // been queued) and cudarc's elapsed_ms synchronizes, which serialized the very
14174                // ranks this is meant to test: host issue jumped 1.9 ms -> 34-47 ms per call and
14175                // the join wall fell to match. A probe that changes the schedule measures its own
14176                // perturbation.
14177                // CudaEvent is not Clone, so record a second tail event on the same stream —
14178                // adjacent to `ev`, so it carries the same completion timestamp for timing.
14179                let tp = engine
14180                    .ctx()
14181                    .new_event(Some(cudarc::driver::sys::CUevent_flags::CU_EVENT_DEFAULT))?;
14182                tp.record(&engine.stream())?;
14183                ev_tail_prof.push(tp);
14184            }
14185            ev_rank.push(ev);
14186            partials.push(d_csr);
14187        }
14188        let _main = e.gpu.enter_main()?;
14189        e.bind_runtime_device(e.ctx().ordinal() as i32)?;
14190        // Host-only: every rank's chain is queued, nothing has been waited on yet.
14191        let g_issue = g_t1.elapsed().as_secs_f64() * 1e3;
14192        let g_t2 = std::time::Instant::now();
14193        for ev in &ev_rank {
14194            e.stream().wait(ev)?;
14195        }
14196        // Both partials land on the root (rank 1's crosses the link once), then ONE fused pass
14197        // does join + CSR permute + weight + scatter. Shard order stays pinned as (y0 + y1).
14198        let mut y0 = e.uninit(n_pairs * width)?;
14199        {
14200            let mut dst = y0.slice_mut(0..n_pairs * width);
14201            e.stream()
14202                .memcpy_dtod(&partials[0].slice(0..n_pairs * width), &mut dst)?;
14203        }
14204        let mut y1 = e.uninit(n_pairs * width)?;
14205        {
14206            let mut dst = y1.slice_mut(0..n_pairs * width);
14207            e.stream()
14208                .memcpy_dtod(&partials[1].slice(0..n_pairs * width), &mut dst)?;
14209        }
14210        let inv_d = e.htod_i32(&inv)?;
14211        let wd_d = e.htod(&wd)?;
14212        let mut out = e.uninit(t * width)?;
14213        e.moe_prime_join_scatter(&y0, &y1, &inv_d, &wd_d, &mut out, width, n_used, t)?;
14214        if gprof {
14215            let _ = e.stream().synchronize();
14216            let g_join = g_t2.elapsed().as_secs_f64() * 1e3;
14217            // Everything has completed, so both events of every pair are ready and elapsed_ms
14218            // cannot block. A negative entry means the query itself failed and the row must be
14219            // read as missing data, never as a zero-length span.
14220            // cuEventElapsedTime needs the events' OWN context current — computing it under the
14221            // root's pushed context returned an error for every pair, and the first version
14222            // swallowed that into -1.0 with no reason attached. Enter each rank's context, and
14223            // print the failure once so a dead probe can never again look like a zero-length span.
14224            let mut span_ms: Vec<f32> = Vec::with_capacity(world);
14225            for (rank, (h, tp)) in ev_head.iter().zip(ev_tail_prof.iter()).enumerate() {
14226                let guard = self.ranks[rank].gpu.enter_main();
14227                match guard.and_then(|_g| h.elapsed_ms(tp).map_err(|e| e.into())) {
14228                    Ok(v) => span_ms.push(v),
14229                    Err(err) => {
14230                        static SAID: std::sync::atomic::AtomicBool =
14231                            std::sync::atomic::AtomicBool::new(false);
14232                        if !SAID.swap(true, std::sync::atomic::Ordering::Relaxed) {
14233                            eprintln!("[grp-prof] span query failed on rank {rank}: {err}");
14234                        }
14235                        span_ms.push(-1.0);
14236                    }
14237                }
14238            }
14239            eprintln!(
14240                "[grp-prof] t={t} n_active={n_active} csr={g_csr:.1}ms issue={g_issue:.1}ms \
14241                 join={g_join:.1}ms spans={span_ms:?} span_sum={:.1}ms span_max={:.1}ms",
14242                span_ms.iter().sum::<f32>(),
14243                span_ms.iter().cloned().fold(0.0f32, f32::max)
14244            );
14245        }
14246        Ok(out)
14247    }
14248
14249    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
14250    pub fn run_tensor_parallel_routes_nvfp4_device_routed(
14251        &self,
14252        experts: &ResidentNvfp4TensorParallel,
14253        e: &Engine,
14254        input_dev: &crate::CudaSlice<f32>,
14255        sel_d: &crate::CudaSlice<i32>,
14256        w_d: &crate::CudaSlice<f32>,
14257        experts_per_token: usize,
14258        activation_limit: Option<f32>,
14259    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
14260        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
14261            experts,
14262            e,
14263            input_dev,
14264            sel_d,
14265            w_d,
14266            experts_per_token,
14267            activation_limit,
14268            || Ok(()),
14269        )
14270    }
14271
14272    /// `run_tensor_parallel_routes_nvfp4_device_routed` with a PREJOIN hook: `pre_join`
14273    /// runs on the host right before the join wait is enqueued on e's stream — work it
14274    /// issues there (e.g. the shexp overlap) executes WHILE the peer rank finishes its
14275    /// sweep, instead of after the join. Value-neutral by construction (the hook only
14276    /// reorders independent host issue).
14277    #[allow(clippy::too_many_arguments)]
14278    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
14279        &self,
14280        experts: &ResidentNvfp4TensorParallel,
14281        e: &Engine,
14282        input_dev: &crate::CudaSlice<f32>,
14283        sel_d: &crate::CudaSlice<i32>,
14284        w_d: &crate::CudaSlice<f32>,
14285        experts_per_token: usize,
14286        activation_limit: Option<f32>,
14287        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
14288    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
14289        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
14290            experts,
14291            e,
14292            input_dev,
14293            sel_d,
14294            w_d,
14295            experts_per_token,
14296            activation_limit,
14297            pre_join,
14298            None,
14299        )
14300    }
14301
14302    /// The prejoin variant with MOE TAIL FUSION M1: when `post_add = Some((sh_raw,
14303    /// scale_raw))`, the direct-join arm folds the shexp apply into the join add
14304    /// (`dst = (acc0+acc1) + sh*scale[0]`, exact split-pair sequence) — the caller skips
14305    /// its apply launch. Raw UVA pointers so no lock is held across the call.
14306    #[allow(clippy::too_many_arguments)]
14307    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
14308        &self,
14309        experts: &ResidentNvfp4TensorParallel,
14310        e: &Engine,
14311        input_dev: &crate::CudaSlice<f32>,
14312        sel_d: &crate::CudaSlice<i32>,
14313        w_d: &crate::CudaSlice<f32>,
14314        experts_per_token: usize,
14315        activation_limit: Option<f32>,
14316        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
14317        post_add: Option<(u64, u64)>,
14318    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
14319        if input_dev.len() != experts.input_width {
14320            return Err(format!(
14321                "NVFP4 device-routed input {} != width {}",
14322                input_dev.len(),
14323                experts.input_width
14324            )
14325            .into());
14326        }
14327        let n_sel = experts_per_token;
14328        if sel_d.len() < n_sel || w_d.len() < n_sel {
14329            return Err(format!(
14330                "NVFP4 device-routed routes sel={} w={} < experts/token {n_sel}",
14331                sel_d.len(),
14332                w_d.len()
14333            )
14334            .into());
14335        }
14336        let world = self.ranks.len();
14337        if world != NVFP4_CANONICAL_ROW_SHARDS {
14338            return Err(format!(
14339                "NVFP4 device routes require world == canonical shard grid \
14340                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
14341            )
14342            .into());
14343        }
14344        let local_out = if experts.ep2 {
14345            experts.expert_width
14346        } else {
14347            experts.expert_width / world
14348        };
14349
14350        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
14351        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
14352        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
14353        let started = timing.then(std::time::Instant::now);
14354
14355        let mut workspace_guard = experts
14356            .device_workspace
14357            .lock()
14358            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
14359        if workspace_guard.is_none() {
14360            drop(workspace_guard);
14361            let zero = vec![0.0f32; experts.input_width];
14362            let zero_sel = vec![0usize; n_sel];
14363            let zero_w = vec![0.0f32; n_sel];
14364            let _ = self.run_tensor_parallel_routes_nvfp4_device(
14365                experts,
14366                &zero,
14367                &zero_sel,
14368                &zero_w,
14369                n_sel,
14370                activation_limit,
14371            )?;
14372            workspace_guard = experts
14373                .device_workspace
14374                .lock()
14375                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
14376        }
14377        let workspace = workspace_guard
14378            .as_mut()
14379            .expect("NVFP4 device routes workspace initialized above");
14380        if workspace.n_sel != n_sel {
14381            return Err(format!(
14382                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
14383                workspace.n_sel
14384            )
14385            .into());
14386        }
14387
14388        // GRAPH DOOR (MEMRA_STEP_TP_GRAPH=1): the whole rank+root segment replays as one
14389        // stitched multi-device parent launched on e's stream — no events, no per-token node
14390        // updates (every address is persistent staging). VALUE-IDENTICAL to the eager path:
14391        // the children replay exactly the same kernel/copy sequence.
14392        //
14393        // GRAPH-LAUNCH HEADROOM GUARD (see spec::GRAPH_LAUNCH_MIN_FREE): below the
14394        // driver-free floor on the launching device this call falls through to the
14395        // eager routes path below — the exact body the graph captures, stateless per
14396        // call — instead of feeding cuGraphLaunch an exhausted card
14397        // (lane/graph-launch-guard-sweep-20260831).
14398        if step_tp_graph_enabled()? && step_tp_graph_headroom_ok(e) {
14399            if experts.ep2 {
14400                return Err(
14401                    "MEMRA_STEP_TP_GRAPH=1 with MEMRA_STEP_NVFP4_EP2=1 has never been \
14402                     co-gated; unset one"
14403                        .into(),
14404                );
14405            }
14406            if workspace.dev_route_e.is_none() {
14407                let _main = e.gpu.enter_main()?;
14408                workspace.dev_route_e = Some((
14409                    e.htod_i32(&vec![0i32; n_sel])?,
14410                    e.htod(&vec![0.0f32; n_sel])?,
14411                ));
14412            }
14413            if workspace.in_stage_e.is_none() {
14414                let _main = e.gpu.enter_main()?;
14415                workspace.in_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
14416                workspace.out_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
14417            }
14418            if workspace.routes_graph.is_none() {
14419                let graph = self.nvfp4_routes_build_graph(
14420                    experts,
14421                    workspace,
14422                    local_out,
14423                    n_sel,
14424                    activation_limit,
14425                )?;
14426                workspace.routes_graph = Some(graph);
14427                eprintln!(
14428                    "[step-tp-graph] routes segment captured: ranks={world} n_sel={n_sel} \
14429                     children=3 updates=none performance_claim=false"
14430                );
14431            }
14432            let output = {
14433                let _main = e.gpu.enter_main()?;
14434                {
14435                    let (sel_e, w_e) = workspace
14436                        .dev_route_e
14437                        .as_mut()
14438                        .expect("device route staging set above");
14439                    {
14440                        let mut dst = sel_e.slice_mut(0..n_sel);
14441                        e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
14442                    }
14443                    {
14444                        let mut dst = w_e.slice_mut(0..n_sel);
14445                        e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
14446                    }
14447                }
14448                {
14449                    let in_stage = workspace
14450                        .in_stage_e
14451                        .as_mut()
14452                        .expect("graph staging set above");
14453                    let mut dst = in_stage.slice_mut(0..experts.input_width);
14454                    e.stream()
14455                        .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut dst)?;
14456                }
14457                unsafe {
14458                    let r = cudarc::driver::sys::cuGraphLaunch(
14459                        workspace
14460                            .routes_graph
14461                            .as_ref()
14462                            .expect("routes graph built above")
14463                            .exec,
14464                        e.stream().cu_stream() as cudarc::driver::sys::CUstream,
14465                    );
14466                    if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
14467                        return Err(format!("routes graph launch: {r:?}").into());
14468                    }
14469                }
14470                let mut output = e.uninit(experts.input_width)?;
14471                {
14472                    let out_stage = workspace
14473                        .out_stage_e
14474                        .as_ref()
14475                        .expect("graph staging set above");
14476                    e.stream().memcpy_dtod(
14477                        &out_stage.slice(0..experts.input_width),
14478                        &mut output.slice_mut(0..experts.input_width),
14479                    )?;
14480                }
14481                output
14482            };
14483            if let Some(started) = started {
14484                use std::sync::atomic::Ordering;
14485                let ns = TIMING_NS
14486                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
14487                    + started.elapsed().as_nanos() as u64;
14488                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
14489                if calls.is_multiple_of(430) {
14490                    eprintln!(
14491                        "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
14492                        ns as f64 / 1.0e6,
14493                        ns as f64 / calls as f64 / 1.0e3,
14494                    );
14495                }
14496            }
14497            return Ok(output);
14498        }
14499
14500        // Entry fence + router-output staging, all on e's stream: the fresh sel/w slices are
14501        // copied into the persistent e-context pair, then the event is recorded — the caller's
14502        // sel_d/w_d can free on e's stream with no cross-stream reader.
14503        if let Some((_, device)) = workspace.ev_entry.as_ref() {
14504            if *device != e.ctx().ordinal() {
14505                return Err("NVFP4 device-routed routes engine changed".into());
14506            }
14507        } else {
14508            let _main = e.gpu.enter_main()?;
14509            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
14510        }
14511        if workspace.dev_route_e.is_none() {
14512            let _main = e.gpu.enter_main()?;
14513            workspace.dev_route_e = Some((
14514                e.htod_i32(&vec![0i32; n_sel])?,
14515                e.htod(&vec![0.0f32; n_sel])?,
14516            ));
14517        }
14518        // MEMRA_SEL_MIRROR: the staging pair exists so the rank streams read a persistent
14519        // e-context address. The caller's sel_d/w_d ARE persistent (the process-static
14520        // selection rows), so when every consuming rank shares e's device the ranks can read
14521        // them directly and this hop disappears. The graph door keeps the staging (its
14522        // captured copies read the fixed addresses).
14523        let mirror = sel_mirror_on() && !step_tp_graph_enabled()?;
14524        let e_device = e.ctx().ordinal();
14525        // rank1_routed is consumed (taken) below; peek it here for the staging decision.
14526        let rank1_routed_peek = workspace.rank1_routed;
14527        let stage_needed = !mirror
14528            || self.ranks.iter().enumerate().any(|(rank_index, engine)| {
14529                !(rank1_routed_peek && rank_index == 1) && engine.ctx().ordinal() != e_device
14530            });
14531        {
14532            let _main = e.gpu.enter_main()?;
14533            if stage_needed {
14534                let (sel_e, w_e) = workspace
14535                    .dev_route_e
14536                    .as_mut()
14537                    .expect("device route staging set above");
14538                {
14539                    let mut dst = sel_e.slice_mut(0..n_sel);
14540                    e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
14541                }
14542                {
14543                    let mut dst = w_e.slice_mut(0..n_sel);
14544                    e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
14545                }
14546            }
14547            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
14548            ev_entry.record(&e.stream())?;
14549        }
14550        // Prestage door: input pull + quantize were already issued on the rank streams
14551        // (before the router) — the rank stream order suffices, skip them here.
14552        let prestaged = std::mem::take(&mut workspace.prestaged);
14553        let rank1_routed = std::mem::take(&mut workspace.rank1_routed);
14554        for (rank_index, engine) in self.ranks.iter().enumerate() {
14555            let _main = engine.gpu.enter_main()?;
14556            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
14557            engine.stream().wait(ev_entry)?;
14558            if !prestaged {
14559                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
14560                engine
14561                    .stream()
14562                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
14563            }
14564            if !(rank1_routed && rank_index == 1) {
14565                // ONE mirror launch instead of two 32-byte copy-engine dispatches; source is
14566                // the caller's persistent rows when this rank shares e's device (UVA, ordered
14567                // by ev_entry), else the staged e-context pair.
14568                let same_dev = engine.ctx().ordinal() == e_device;
14569                if mirror {
14570                    // Split the workspace borrow so the source (the staged pair, when this
14571                    // rank is off-device) and the destination rows coexist.
14572                    let Nvfp4DeviceRoutesWorkspace {
14573                        sel,
14574                        route_w,
14575                        dev_route_e,
14576                        ..
14577                    } = &mut *workspace;
14578                    let (src_sel, src_w): (&crate::CudaSlice<i32>, &crate::CudaSlice<f32>) =
14579                        if same_dev {
14580                            (sel_d, w_d)
14581                        } else {
14582                            let (sel_e, w_e) = dev_route_e
14583                                .as_ref()
14584                                .expect("device route staging set above");
14585                            (sel_e, w_e)
14586                        };
14587                    engine.moe_sel_w_mirror(
14588                        src_sel,
14589                        src_w,
14590                        &mut sel[rank_index],
14591                        &mut route_w[rank_index],
14592                        n_sel,
14593                    )?;
14594                } else {
14595                    let (sel_e, w_e) = workspace
14596                        .dev_route_e
14597                        .as_ref()
14598                        .expect("device route staging set above");
14599                    {
14600                        let mut dst = workspace.sel[rank_index].slice_mut(0..n_sel);
14601                        engine
14602                            .stream()
14603                            .memcpy_dtod(&sel_e.slice(0..n_sel), &mut dst)?;
14604                    }
14605                    {
14606                        let mut dst = workspace.route_w[rank_index].slice_mut(0..n_sel);
14607                        engine
14608                            .stream()
14609                            .memcpy_dtod(&w_e.slice(0..n_sel), &mut dst)?;
14610                    }
14611                }
14612            }
14613            if !prestaged {
14614                let Nvfp4DeviceRoutesWorkspace {
14615                    input, in_q, in_d, ..
14616                } = &mut *workspace;
14617                engine.quantize_q8_1_into(
14618                    &input[rank_index],
14619                    1,
14620                    experts.input_width,
14621                    &mut in_q[rank_index],
14622                    &mut in_d[rank_index],
14623                )?;
14624            }
14625        }
14626        self.nvfp4_routes_batched_sweeps(
14627            experts,
14628            workspace,
14629            &[],
14630            &[],
14631            &[],
14632            local_out,
14633            n_sel,
14634            activation_limit,
14635            true,
14636        )?;
14637
14638        // rank0 == root: its own stream order already covers its sweep; only the PEER
14639        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
14640        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
14641            let _main = engine.gpu.enter_main()?;
14642            workspace.ev_rank[rank_index].record(&engine.stream())?;
14643        }
14644        // Doorbell fences (MEMRA_FENCE_MEMOPS=1): rank1 + root ring their flags; e waits
14645        // the tickets instead of the two events. Arm lazily; 0-len = unsupported.
14646        let memops = fence_memops_on() && moe_direct_on() && self.ranks.len() == 2;
14647        let mut ticket = 0u32;
14648        if memops {
14649            use cudarc::driver::sys;
14650            if workspace.fence_flags_raw == 0 {
14651                let root = &self.ranks[0];
14652                let _main = root.gpu.enter_main()?;
14653                let mut ptr: sys::CUdeviceptr = 0;
14654                let r = unsafe { sys::cuMemAlloc_v2(&mut ptr, 8) };
14655                if r != sys::CUresult::CUDA_SUCCESS {
14656                    return Err(format!("fence flag alloc: {r:?}").into());
14657                }
14658                let r = unsafe { sys::cuMemsetD8_v2(ptr, 0, 8) };
14659                if r != sys::CUresult::CUDA_SUCCESS {
14660                    return Err(format!("fence flag memset: {r:?}").into());
14661                }
14662                workspace.fence_flags_raw = ptr as u64;
14663            }
14664            workspace.fence_ticket = workspace.fence_ticket.wrapping_add(1).max(1);
14665            ticket = workspace.fence_ticket;
14666            let base = workspace.fence_flags_raw;
14667            // rank1's fence: a peer stream MEMOP is rejected over PCIe P2P
14668            // (CUDA_ERROR_INVALID_VALUE, receipted 2026-08-23), but a peer KERNEL STORE into
14669            // root memory is legal — the direct join already relies on it. Under
14670            // MEMRA_FENCE_RANK1 rank1 rings flag[0] that way and e waits it same-device,
14671            // replacing the cross-device event wait below.
14672            if fence_rank1_on() {
14673                let peer = &self.ranks[1];
14674                let _pmain = peer.gpu.enter_main()?;
14675                peer.ring_flag_raw(base, ticket)?;
14676            }
14677            {
14678                let root = &self.ranks[0];
14679                let _main = root.gpu.enter_main()?;
14680                let r = unsafe {
14681                    sys::cuStreamWriteValue32_v2(
14682                        root.stream().cu_stream() as sys::CUstream,
14683                        (base + 4) as sys::CUdeviceptr,
14684                        ticket,
14685                        0,
14686                    )
14687                };
14688                if r != sys::CUresult::CUDA_SUCCESS {
14689                    return Err(format!("fence write root: {r:?}").into());
14690                }
14691            }
14692        }
14693        // PREJOIN hook: rank work is fully issued (dev1 running); independent e-stream
14694        // kernels queued here execute while the peer rank drains its sweep.
14695        pre_join()?;
14696
14697        if moe_direct_on() && self.ranks.len() == 2 {
14698            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
14699            // rank0's is root-stream-ordered. One root event + rank1's own event order
14700            // the model engine's single add — same operand order as root's add
14701            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
14702            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
14703            // hazard class does not apply).
14704            let _main = e.gpu.enter_main()?;
14705            if memops {
14706                use cudarc::driver::sys;
14707                let base = workspace.fence_flags_raw;
14708                let r = unsafe {
14709                    sys::cuStreamWaitValue32_v2(
14710                        e.stream().cu_stream() as sys::CUstream,
14711                        (base + 4) as sys::CUdeviceptr,
14712                        ticket,
14713                        sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
14714                    )
14715                };
14716                if r != sys::CUresult::CUDA_SUCCESS {
14717                    return Err(format!("fence wait: {r:?}").into());
14718                }
14719                if fence_rank1_on() {
14720                    // Same-device wait on the flag rank1 rang over P2P.
14721                    let r = unsafe {
14722                        sys::cuStreamWaitValue32_v2(
14723                            e.stream().cu_stream() as sys::CUstream,
14724                            base as sys::CUdeviceptr,
14725                            ticket,
14726                            sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
14727                        )
14728                    };
14729                    if r != sys::CUresult::CUDA_SUCCESS {
14730                        return Err(format!("fence wait rank1: {r:?}").into());
14731                    }
14732                } else {
14733                    for ev in workspace.ev_rank.iter().skip(1) {
14734                        e.stream().wait(ev)?;
14735                    }
14736                }
14737            } else {
14738                {
14739                    let root = &self.ranks[0];
14740                    let _rmain = root.gpu.enter_main()?;
14741                    workspace
14742                        .ev_done
14743                        .as_ref()
14744                        .expect("device routes done event")
14745                        .record(&root.stream())?;
14746                }
14747                e.stream().wait(
14748                    workspace
14749                        .ev_done
14750                        .as_ref()
14751                        .expect("device routes done event"),
14752                )?;
14753                for ev in workspace.ev_rank.iter().skip(1) {
14754                    e.stream().wait(ev)?;
14755                }
14756            }
14757            let mut output = e.uninit(experts.input_width)?;
14758            if let Some((sh_raw, scale_raw)) = post_add {
14759                // MOE TAIL FUSION M1: fold the shexp apply into the join add —
14760                // dst = (acc0 + acc1) + sh*scale[0], the exact split-pair sequence.
14761                e.add3_raw(
14762                    &workspace.accumulator[0],
14763                    &workspace.accumulator[1],
14764                    sh_raw,
14765                    scale_raw,
14766                    &mut output,
14767                    experts.input_width,
14768                )?;
14769            } else {
14770                e.add(
14771                    &workspace.accumulator[0],
14772                    &workspace.accumulator[1],
14773                    &mut output,
14774                    experts.input_width,
14775                )?;
14776            }
14777            let output = output;
14778            if let Some(started) = started {
14779                use std::sync::atomic::Ordering;
14780                let ns = TIMING_NS
14781                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
14782                    + started.elapsed().as_nanos() as u64;
14783                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
14784                if calls.is_multiple_of(430) {
14785                    eprintln!(
14786                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
14787                        ns as f64 / 1.0e6,
14788                        ns as f64 / calls as f64 / 1.0e3,
14789                    );
14790                }
14791            }
14792            return Ok(output);
14793        }
14794        {
14795            let root = &self.ranks[0];
14796            let _main = root.gpu.enter_main()?;
14797            for ev in workspace.ev_rank.iter().skip(1) {
14798                root.stream().wait(ev)?;
14799            }
14800            root.stream()
14801                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
14802            {
14803                let Nvfp4DeviceRoutesWorkspace {
14804                    accumulator,
14805                    remote,
14806                    combined,
14807                    ..
14808                } = &mut *workspace;
14809                root.add(&accumulator[0], remote, combined, experts.input_width)?;
14810            }
14811            workspace
14812                .ev_done
14813                .as_ref()
14814                .expect("device routes done event")
14815                .record(&root.stream())?;
14816        }
14817        let output = {
14818            let _main = e.gpu.enter_main()?;
14819            e.stream().wait(
14820                workspace
14821                    .ev_done
14822                    .as_ref()
14823                    .expect("device routes done event"),
14824            )?;
14825            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
14826            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
14827            let mut output = e.uninit(experts.input_width)?;
14828            e.stream().memcpy_dtod(
14829                &workspace.combined.slice(0..experts.input_width),
14830                &mut output.slice_mut(0..experts.input_width),
14831            )?;
14832            output
14833        };
14834        if let Some(started) = started {
14835            use std::sync::atomic::Ordering;
14836            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
14837                + started.elapsed().as_nanos() as u64;
14838            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
14839            if calls.is_multiple_of(430) {
14840                eprintln!(
14841                    "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
14842                    ns as f64 / 1.0e6,
14843                    ns as f64 / calls as f64 / 1.0e3,
14844                );
14845            }
14846        }
14847        Ok(output)
14848    }
14849
14850    /// The fused finish's ROOT section (combine + shadow gathers), event-free: the eager
14851    /// caller wraps it with rank-event waits + the done record; the token graph captures it
14852    /// verbatim (parent edges provide the ordering).
14853    pub(crate) fn decode_v2_finish_root_fused(
14854        &self,
14855        ws: &mut StepTpDecodeV2Ws,
14856    ) -> Result<(), Box<dyn std::error::Error>> {
14857        let root = &self.ranks[0];
14858        let _main = root.gpu.enter_main()?;
14859        if ws.raw_peer_partial != 0 {
14860            // Capture-safe raw seams (arming happened in the stage flow).
14861            raw_copy_bytes(ws.raw_peer_partial, ws.raw_o_partial1, ws.o_out * 4, root)?;
14862        } else {
14863            root.stream()
14864                .memcpy_dtod(&ws.o_partials[1][0], &mut ws.peer_partial)?;
14865        }
14866        {
14867            let StepTpDecodeV2Ws {
14868                o_partials,
14869                peer_partial,
14870                reduce_a,
14871                o_out,
14872                ..
14873            } = &mut *ws;
14874            root.add(&o_partials[0][0], peer_partial, reduce_a, *o_out)?;
14875        }
14876        let shadows = !no_local_shadow_on() || ws.raw_mixed_stage_e != 0;
14877        if shadows {
14878            // rank0's shadows are same-context (root) copies; rank1's cross-context reads go
14879            // raw when armed.
14880            let mut k_dst = ws.k_shadow.slice_mut(0..ws.local_kv_dim);
14881            root.stream().memcpy_dtod(&ws.k[0], &mut k_dst)?;
14882            let mut v_dst = ws.v_shadow.slice_mut(0..ws.local_kv_dim);
14883            root.stream().memcpy_dtod(&ws.v_raw[0], &mut v_dst)?;
14884        }
14885        if shadows && ws.raw_peer_partial != 0 {
14886            raw_copy_bytes(
14887                ws.raw_k_shadow + (ws.local_kv_dim * 4) as u64,
14888                ws.raw_k1,
14889                ws.local_kv_dim * 4,
14890                root,
14891            )?;
14892            raw_copy_bytes(
14893                ws.raw_v_shadow + (ws.local_kv_dim * 4) as u64,
14894                ws.raw_v1,
14895                ws.local_kv_dim * 4,
14896                root,
14897            )?;
14898        } else if shadows {
14899            let start = ws.local_kv_dim;
14900            let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
14901            root.stream().memcpy_dtod(&ws.k[1], &mut k_dst)?;
14902            let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
14903            root.stream().memcpy_dtod(&ws.v_raw[1], &mut v_dst)?;
14904        }
14905        if ws.raw_mixed_stage_e != 0 {
14906            // Token-graph mirrors: the e-glue children read same-context copies of the
14907            // root-produced rows.
14908            raw_copy_bytes(ws.raw_mixed_stage_e, ws.raw_reduce_a, ws.o_out * 4, root)?;
14909            let (k_stage, v_stage) = ws.raw_shadow_stage_e;
14910            raw_copy_bytes(k_stage, ws.raw_k_shadow, 2 * ws.local_kv_dim * 4, root)?;
14911            raw_copy_bytes(v_stage, ws.raw_v_shadow, 2 * ws.local_kv_dim * 4, root)?;
14912        }
14913        Ok(())
14914    }
14915
14916    /// Arm the token-graph e-context mirrors (orchestrator-supplied fixed addresses) plus
14917    /// reduce_a's own pointer.
14918    pub(crate) fn decode_v2_arm_token_mirrors(
14919        &self,
14920        ws: &mut StepTpDecodeV2Ws,
14921        mixed_stage_e: u64,
14922        shadow_stage_e: (u64, u64),
14923    ) -> Result<(), Box<dyn std::error::Error>> {
14924        use cudarc::driver::DevicePtr;
14925        let root = &self.ranks[0];
14926        let _main = root.gpu.enter_main()?;
14927        let stream = root.stream();
14928        let (a, _g) = ws.reduce_a.device_ptr(&stream);
14929        ws.raw_reduce_a = a;
14930        ws.raw_mixed_stage_e = mixed_stage_e;
14931        ws.raw_shadow_stage_e = shadow_stage_e;
14932        Ok(())
14933    }
14934
14935    /// Build one layer's stitched routes graph: per-rank children captured on their own
14936    /// streams (raw cuMemcpyAsync at every cross-context seam — cudarc's slice tracking is
14937    /// capture-illegal there), a root combine child, and a multi-device parent with
14938    /// {rank0, rank1} -> root dependency edges. Zero per-token updates: every address the
14939    /// nodes touch is persistent workspace/staging.
14940    fn nvfp4_routes_build_graph(
14941        &self,
14942        experts: &ResidentNvfp4TensorParallel,
14943        workspace: &mut Nvfp4DeviceRoutesWorkspace,
14944        local_out: usize,
14945        n_sel: usize,
14946        activation_limit: Option<f32>,
14947    ) -> Result<RoutesGraph, Box<dyn std::error::Error>> {
14948        use cudarc::driver::DevicePtr;
14949        use cudarc::driver::sys;
14950        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
14951            if r == sys::CUresult::CUDA_SUCCESS {
14952                Ok(())
14953            } else {
14954                Err(format!("{what}: {r:?}").into())
14955            }
14956        }
14957        let world = self.ranks.len();
14958        if world != 2 {
14959            return Err("routes graph door is built for the TP2 pair".into());
14960        }
14961        let width = experts.input_width;
14962
14963        // Raw pointers cached before capture (each read with its owner's stream).
14964        let ptr_f32 = |buf: &crate::CudaSlice<f32>, engine: &Engine| -> u64 {
14965            let stream = engine.stream();
14966            let (ptr, _g) = buf.device_ptr(&stream);
14967            ptr
14968        };
14969        let ptr_i32 = |buf: &crate::CudaSlice<i32>, engine: &Engine| -> u64 {
14970            let stream = engine.stream();
14971            let (ptr, _g) = buf.device_ptr(&stream);
14972            ptr
14973        };
14974        let (sel_e, w_e) = workspace
14975            .dev_route_e
14976            .as_ref()
14977            .expect("device route staging set before graph build");
14978        let root_engine = &self.ranks[0];
14979        let p_in_stage = ptr_f32(
14980            workspace.in_stage_e.as_ref().expect("graph staging"),
14981            root_engine,
14982        );
14983        let p_out_stage = ptr_f32(
14984            workspace.out_stage_e.as_ref().expect("graph staging"),
14985            root_engine,
14986        );
14987        let p_sel_e = ptr_i32(sel_e, root_engine);
14988        let p_w_e = ptr_f32(w_e, root_engine);
14989        let p_input: Vec<u64> = (0..world)
14990            .map(|r| ptr_f32(&workspace.input[r], &self.ranks[r]))
14991            .collect();
14992        let p_sel: Vec<u64> = (0..world)
14993            .map(|r| ptr_i32(&workspace.sel[r], &self.ranks[r]))
14994            .collect();
14995        let p_route_w: Vec<u64> = (0..world)
14996            .map(|r| ptr_f32(&workspace.route_w[r], &self.ranks[r]))
14997            .collect();
14998        let p_acc1 = ptr_f32(&workspace.accumulator[1], &self.ranks[1]);
14999        let p_remote = ptr_f32(&workspace.remote, root_engine);
15000        let p_combined = ptr_f32(&workspace.combined, root_engine);
15001
15002        let raw_copy = |dst: u64,
15003                        src: u64,
15004                        bytes: usize,
15005                        engine: &Engine|
15006         -> Result<(), Box<dyn std::error::Error>> {
15007            unsafe {
15008                cu_try(
15009                    sys::cuMemcpyAsync(
15010                        dst as sys::CUdeviceptr,
15011                        src as sys::CUdeviceptr,
15012                        bytes,
15013                        engine.stream().cu_stream() as sys::CUstream,
15014                    ),
15015                    "routes graph cuMemcpyAsync",
15016                )
15017            }
15018        };
15019
15020        let mut children = Vec::with_capacity(3);
15021        for rank in 0..world {
15022            let engine = &self.ranks[rank];
15023            let _main = engine.gpu.enter_main()?;
15024            let (child, _retained) = engine.capture_graph_retained(|_| {
15025                raw_copy(p_input[rank], p_in_stage, width * 4, engine)?;
15026                raw_copy(p_sel[rank], p_sel_e, n_sel * 4, engine)?;
15027                raw_copy(p_route_w[rank], p_w_e, n_sel * 4, engine)?;
15028                {
15029                    let Nvfp4DeviceRoutesWorkspace {
15030                        input, in_q, in_d, ..
15031                    } = &mut *workspace;
15032                    engine.quantize_q8_1_into(
15033                        &input[rank],
15034                        1,
15035                        width,
15036                        &mut in_q[rank],
15037                        &mut in_d[rank],
15038                    )?;
15039                }
15040                self.nvfp4_routes_batched_sweeps_rank(
15041                    experts,
15042                    workspace,
15043                    &[],
15044                    &[],
15045                    &[],
15046                    local_out,
15047                    n_sel,
15048                    activation_limit,
15049                    true,
15050                    rank,
15051                )?;
15052                Ok(())
15053            })?;
15054            children.push(child);
15055        }
15056        {
15057            let root = &self.ranks[0];
15058            let _main = root.gpu.enter_main()?;
15059            let (child, _retained) = root.capture_graph_retained(|_| {
15060                raw_copy(p_remote, p_acc1, width * 4, root)?;
15061                {
15062                    let Nvfp4DeviceRoutesWorkspace {
15063                        accumulator,
15064                        remote,
15065                        combined,
15066                        ..
15067                    } = &mut *workspace;
15068                    root.add(&accumulator[0], remote, combined, width)?;
15069                }
15070                raw_copy(p_out_stage, p_combined, width * 4, root)?;
15071                Ok(())
15072            })?;
15073            children.push(child);
15074        }
15075
15076        let mut parent: sys::CUgraph = std::ptr::null_mut();
15077        unsafe {
15078            cu_try(sys::cuGraphCreate(&mut parent, 0), "routes cuGraphCreate")?;
15079        }
15080        let mut n0: sys::CUgraphNode = std::ptr::null_mut();
15081        let mut n1: sys::CUgraphNode = std::ptr::null_mut();
15082        let mut n2: sys::CUgraphNode = std::ptr::null_mut();
15083        unsafe {
15084            cu_try(
15085                sys::cuGraphAddChildGraphNode(
15086                    &mut n0,
15087                    parent,
15088                    std::ptr::null(),
15089                    0,
15090                    children[0].cu_graph(),
15091                ),
15092                "routes child r0",
15093            )?;
15094            cu_try(
15095                sys::cuGraphAddChildGraphNode(
15096                    &mut n1,
15097                    parent,
15098                    std::ptr::null(),
15099                    0,
15100                    children[1].cu_graph(),
15101                ),
15102                "routes child r1",
15103            )?;
15104            let deps = [n0, n1];
15105            cu_try(
15106                sys::cuGraphAddChildGraphNode(
15107                    &mut n2,
15108                    parent,
15109                    deps.as_ptr(),
15110                    2,
15111                    children[2].cu_graph(),
15112                ),
15113                "routes child root",
15114            )?;
15115        }
15116        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
15117        unsafe {
15118            cu_try(
15119                sys::cuGraphInstantiateWithFlags(&mut exec, parent, 0),
15120                "routes instantiate",
15121            )?;
15122        }
15123        Ok(RoutesGraph {
15124            exec,
15125            parent,
15126            _children: children,
15127        })
15128    }
15129
15130    /// One rank's routes section for the token graph (event-free): staged input copy (raw
15131    /// when the caller supplies the source pointer), quantize, and the batched sweeps.
15132    /// Eager device_routed wraps it with the entry-event wait.
15133    #[allow(clippy::too_many_arguments)]
15134    pub(crate) fn routes_rank_section(
15135        &self,
15136        experts: &ResidentNvfp4TensorParallel,
15137        workspace: &mut Nvfp4DeviceRoutesWorkspace,
15138        raw_input_src: u64,
15139        local_out: usize,
15140        n_sel: usize,
15141        activation_limit: Option<f32>,
15142        rank_index: usize,
15143    ) -> Result<(), Box<dyn std::error::Error>> {
15144        let engine = &self.ranks[rank_index];
15145        {
15146            let _main = engine.gpu.enter_main()?;
15147            // sel/route_w land via raw copies from the e staging (fixed addresses).
15148            let (sel_e_ptr, w_e_ptr) = workspace
15149                .raw_dev_route_e
15150                .ok_or("routes rank section requires armed staging pointers")?;
15151            raw_copy_bytes(
15152                workspace.raw_input[rank_index],
15153                raw_input_src,
15154                experts.input_width * 4,
15155                engine,
15156            )?;
15157            raw_copy_bytes(workspace.raw_sel[rank_index], sel_e_ptr, n_sel * 4, engine)?;
15158            raw_copy_bytes(
15159                workspace.raw_route_w[rank_index],
15160                w_e_ptr,
15161                n_sel * 4,
15162                engine,
15163            )?;
15164            {
15165                let Nvfp4DeviceRoutesWorkspace {
15166                    input, in_q, in_d, ..
15167                } = &mut *workspace;
15168                engine.quantize_q8_1_into(
15169                    &input[rank_index],
15170                    1,
15171                    experts.input_width,
15172                    &mut in_q[rank_index],
15173                    &mut in_d[rank_index],
15174                )?;
15175            }
15176        }
15177        self.nvfp4_routes_batched_sweeps_rank(
15178            experts,
15179            workspace,
15180            &[],
15181            &[],
15182            &[],
15183            local_out,
15184            n_sel,
15185            activation_limit,
15186            true,
15187            rank_index,
15188        )
15189    }
15190
15191    /// The routes ROOT combine section (event-free): peer accumulator read (raw), canonical
15192    /// add, combined row raw-copied into the fixed e-context out stage.
15193    pub(crate) fn routes_root_section(
15194        &self,
15195        experts: &ResidentNvfp4TensorParallel,
15196        workspace: &mut Nvfp4DeviceRoutesWorkspace,
15197    ) -> Result<(), Box<dyn std::error::Error>> {
15198        let root = &self.ranks[0];
15199        let _main = root.gpu.enter_main()?;
15200        let (acc1_ptr, remote_ptr, combined_ptr, out_stage_ptr) = workspace
15201            .raw_combine
15202            .ok_or("routes root section requires armed combine pointers")?;
15203        raw_copy_bytes(remote_ptr, acc1_ptr, experts.input_width * 4, root)?;
15204        {
15205            let Nvfp4DeviceRoutesWorkspace {
15206                accumulator,
15207                remote,
15208                combined,
15209                ..
15210            } = &mut *workspace;
15211            root.add(&accumulator[0], remote, combined, experts.input_width)?;
15212        }
15213        raw_copy_bytes(out_stage_ptr, combined_ptr, experts.input_width * 4, root)?;
15214        Ok(())
15215    }
15216
15217    /// Arm the routes raw pointers (once): staging pair, per-rank input/sel/route_w, and the
15218    /// combine set. Requires dev_route_e + in/out stages already allocated.
15219    pub(crate) fn routes_arm_raw(
15220        &self,
15221        experts: &ResidentNvfp4TensorParallel,
15222        workspace: &mut Nvfp4DeviceRoutesWorkspace,
15223    ) -> Result<(), Box<dyn std::error::Error>> {
15224        use cudarc::driver::DevicePtr;
15225        if workspace.raw_dev_route_e.is_some() {
15226            return Ok(());
15227        }
15228        let _ = experts;
15229        let (sel_e, w_e) = workspace
15230            .dev_route_e
15231            .as_ref()
15232            .ok_or("routes staging not armed")?;
15233        let root = &self.ranks[0];
15234        {
15235            let _main = root.gpu.enter_main()?;
15236            let stream = root.stream();
15237            let (a, _g) = sel_e.device_ptr(&stream);
15238            let (b, _g) = w_e.device_ptr(&stream);
15239            workspace.raw_dev_route_e = Some((a, b));
15240            let (c, _g) = workspace.accumulator[1].device_ptr(&stream);
15241            let (d, _g) = workspace.remote.device_ptr(&stream);
15242            let (f, _g) = workspace.combined.device_ptr(&stream);
15243            let out_stage = workspace
15244                .out_stage_e
15245                .as_ref()
15246                .ok_or("routes out stage not armed")?;
15247            let (g_, _g) = out_stage.device_ptr(&stream);
15248            workspace.raw_combine = Some((c, d, f, g_));
15249        }
15250        for rank in 0..self.ranks.len() {
15251            let engine = &self.ranks[rank];
15252            let _main = engine.gpu.enter_main()?;
15253            let stream = engine.stream();
15254            let (a, _g) = workspace.input[rank].device_ptr(&stream);
15255            let (b, _g) = workspace.sel[rank].device_ptr(&stream);
15256            let (c, _g) = workspace.route_w[rank].device_ptr(&stream);
15257            workspace.raw_input.push(a);
15258            workspace.raw_sel.push(b);
15259            workspace.raw_route_w.push(c);
15260        }
15261        Ok(())
15262    }
15263
15264    /// Routed NVFP4 expert program, host-canonical transport. Native/bulk P2P transport for the
15265    /// NVFP4 bank is a separate increment; this entry point is exactness-first and reports no
15266    /// throughput claim.
15267    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
15268    pub fn run_tensor_parallel_routes_nvfp4(
15269        &self,
15270        experts: &ResidentNvfp4TensorParallel,
15271        input: &[f32],
15272        tokens: usize,
15273        selected: &[usize],
15274        route_weights: &[f32],
15275        experts_per_token: usize,
15276        activation_limit: Option<f32>,
15277    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
15278        validate_activations(input, tokens, experts.input_width)?;
15279        let pairs = tokens
15280            .checked_mul(experts_per_token)
15281            .ok_or("NVFP4 TP route count overflow")?;
15282        if selected.len() != pairs || route_weights.len() != pairs {
15283            return Err(format!(
15284                "NVFP4 TP routes selected={} weights={} != tokens {tokens} x experts/token \
15285                 {experts_per_token} ({pairs})",
15286                selected.len(),
15287                route_weights.len(),
15288            )
15289            .into());
15290        }
15291        if !route_weights.iter().all(|weight| weight.is_finite()) {
15292            return Err("NVFP4 TP route weights contain a non-finite value".into());
15293        }
15294
15295        let mut output = vec![0.0f32; tokens * experts.input_width];
15296        for token in 0..tokens {
15297            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
15298            for slot in 0..experts_per_token {
15299                let pair = token * experts_per_token + slot;
15300                let expert = selected[pair];
15301                if expert >= experts.expert_count {
15302                    return Err(format!(
15303                        "NVFP4 TP selected expert {expert} outside 0..{}",
15304                        experts.expert_count
15305                    )
15306                    .into());
15307                }
15308                // EP2 banks hold the WHOLE expert on rank (expert & 1) at slot (expert >> 1);
15309                // per-row dots are the same full-width program either way (a column shard
15310                // splits ROWS, not the dot), so gate/up are bit-equal across layouts. Only
15311                // down's parenthesization moves (full-width dot vs canonical 2-shard sum) —
15312                // the numeric-class this door declares.
15313                let gate = if experts.ep2 {
15314                    self.run_full_bank_expert_nvfp4(
15315                        &experts.gate,
15316                        &experts.macros_gate,
15317                        expert,
15318                        input_row,
15319                    )?
15320                } else {
15321                    self.run_column_bank_expert_nvfp4(
15322                        &experts.gate,
15323                        &experts.macros_gate,
15324                        expert,
15325                        input_row,
15326                    )?
15327                };
15328                let up = if experts.ep2 {
15329                    self.run_full_bank_expert_nvfp4(
15330                        &experts.up,
15331                        &experts.macros_up,
15332                        expert,
15333                        input_row,
15334                    )?
15335                } else {
15336                    self.run_column_bank_expert_nvfp4(
15337                        &experts.up,
15338                        &experts.macros_up,
15339                        expert,
15340                        input_row,
15341                    )?
15342                };
15343                let activated: Vec<f32> = gate
15344                    .iter()
15345                    .zip(&up)
15346                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
15347                    .collect();
15348                debug_assert_eq!(activated.len(), experts.expert_width);
15349                let down = if experts.ep2 {
15350                    self.run_full_down_expert_nvfp4(
15351                        &experts.down,
15352                        &experts.macros_down,
15353                        expert,
15354                        &activated,
15355                    )?
15356                } else {
15357                    self.run_row_bank_expert_nvfp4(
15358                        &experts.down,
15359                        &experts.macros_down,
15360                        expert,
15361                        &activated,
15362                    )?
15363                };
15364                let weight = route_weights[pair];
15365                for (sum, value) in output
15366                    [token * experts.input_width..(token + 1) * experts.input_width]
15367                    .iter_mut()
15368                    .zip(down)
15369                {
15370                    *sum += weight * value;
15371                }
15372            }
15373        }
15374        Ok(output)
15375    }
15376}
15377
15378#[cfg(test)]
15379mod default_on_door_tests {
15380    use super::door_default_on_value;
15381
15382    /// The DEFAULT-ON parse, pinned in every state — including the two that only matter because
15383    /// the default is ON.
15384    ///
15385    /// While these doors were default OFF the parse was `== Ok("1")` and its failure mode was
15386    /// benign: any typo read as the default, which was OFF, which was the safe program. Flipping
15387    /// the default INVERTS that. Under a naive `!= Ok("0")` rule, `MEMRA_NVFP4_BANK_SM=false`
15388    /// (or `=off`, or `=no`) would leave the program ARMED while the operator believed they had
15389    /// rolled it back — a rollback seam that silently does nothing, on the exact door whose
15390    /// predecessor shipped fluent wrong text. So the unrecognized-value case is a named,
15391    /// tested branch that keeps the default AND warns, rather than an accident of `!=`.
15392    #[test]
15393    fn the_default_on_door_parses_every_state_and_names_its_source() {
15394        // unset: the flip is what arms it, and the source string says so — this is the string a
15395        // default-flip receipt needs, because in the flip arms there is no env var to point at.
15396        assert_eq!(
15397            door_default_on_value("MEMRA_TEST_DOOR", None),
15398            (true, "default-on")
15399        );
15400        // explicit 1: armed by a RECIPE, not by the default. Different fact, different label.
15401        assert_eq!(
15402            door_default_on_value("MEMRA_TEST_DOOR", Some("1")),
15403            (true, "env=1")
15404        );
15405        // THE ROLLBACK SEAM. This is the assertion the flip's safety rests on.
15406        assert_eq!(
15407            door_default_on_value("MEMRA_TEST_DOOR", Some("0")),
15408            (false, "env=0 (rollback seam)")
15409        );
15410        // Unrecognized values keep the DEFAULT (ON) and are flagged as such, for every shape an
15411        // operator plausibly types when they mean "off". Every one of these MUST still read ON:
15412        // a parse that guessed "off" from `false` would be a second, undocumented seam, and a
15413        // parse that guessed "off" from `2` would make a typo a silent program change.
15414        for bad in [
15415            "false", "off", "no", "", " 0", "0 ", "00", "true", "2", "-1",
15416        ] {
15417            let (on, source) = door_default_on_value("MEMRA_TEST_DOOR", Some(bad));
15418            assert!(on, "value {bad:?} must NOT disarm a default-ON door");
15419            assert!(
15420                source.contains("default-on") && source.contains("unrecognized"),
15421                "value {bad:?} gave source {source:?}, which does not announce itself as an \
15422                 ignored value — a receipt reader would take it for a clean default"
15423            );
15424        }
15425    }
15426}
15427
15428#[cfg(test)]
15429mod bank_v2_layout_tests {
15430    use super::{nvfp4_matrix_v2_permute, nvfp4_row_bytes};
15431
15432    /// The slot-major permutation had NO test at all until 2026-08-29, while its (since
15433    /// removed) `MEMRA_NVFP4_BANK_V2` FLAGS row carried a bit-identity claim and the live
15434    /// serving env pinned it on. This pins the DOCUMENTED mapping so a reader can be checked
15435    /// against something: per row, slot g's 16 qs bytes land contiguously at `g*16`, and its
15436    /// two UE4M3 scale bytes at `nslots*16 + g*2`. Source layout is memra `block_nvfp4`:
15437    /// 36-byte superblocks of [4 scale bytes | 32 packed e2m1], two 32-value slots per
15438    /// superblock. Since the 2026-08-29 door removal the permutation's ONLY consumer is the
15439    /// EP2 whole-expert bank build (`nvfp4_repack_bank_matrix(_, true)`), whose `*_ep`
15440    /// kernels and `qmatvec_nvfp4_fast_v2` oracle read this exact mapping.
15441    #[test]
15442    fn the_v2_bank_row_is_the_documented_slot_major_permutation() {
15443        // two rows, in_features 128 => 2 superblocks/row, 4 slots/row, 72 bytes/row.
15444        let (out_f, in_f) = (2usize, 128usize);
15445        let row_bytes = nvfp4_row_bytes(in_f);
15446        assert_eq!(row_bytes, 72);
15447        let v1: Vec<u8> = (0..out_f * row_bytes).map(|i| (i % 251) as u8).collect();
15448        let v2 = nvfp4_matrix_v2_permute(&v1, out_f, in_f);
15449        assert_eq!(v2.len(), v1.len(), "a permutation cannot change the size");
15450        let n_slots = in_f / 32;
15451        for row in 0..out_f {
15452            let src = &v1[row * row_bytes..(row + 1) * row_bytes];
15453            let dst = &v2[row * row_bytes..(row + 1) * row_bytes];
15454            for g in 0..n_slots {
15455                let (sblk, h) = (g / 2, g % 2);
15456                let sb = &src[sblk * 36..sblk * 36 + 36];
15457                assert_eq!(
15458                    &dst[g * 16..g * 16 + 16],
15459                    &sb[4 + 16 * h..4 + 16 * h + 16],
15460                    "row {row} slot {g} codes"
15461                );
15462                assert_eq!(
15463                    &dst[n_slots * 16 + g * 2..n_slots * 16 + g * 2 + 2],
15464                    &sb[2 * h..2 * h + 2],
15465                    "row {row} slot {g} scales"
15466                );
15467            }
15468            // and it moves bytes only: same multiset per row, rows never cross.
15469            let (mut a, mut b) = (src.to_vec(), dst.to_vec());
15470            a.sort_unstable();
15471            b.sort_unstable();
15472            assert_eq!(a, b, "row {row} is not a byte permutation");
15473        }
15474    }
15475}
15476
15477#[cfg(test)]
15478mod tests {
15479
15480    #[test]
15481    fn replicated_row_join_is_strictly_tp2_native_and_nonempty() {
15482        assert!(super::validate_tp2_replicated_row_join(2, true, 4096).is_ok());
15483        assert!(
15484            super::validate_tp2_replicated_row_join(1, true, 4096)
15485                .unwrap_err()
15486                .contains("exactly two ranks")
15487        );
15488        assert!(
15489            super::validate_tp2_replicated_row_join(4, true, 4096)
15490                .unwrap_err()
15491                .contains("exactly two ranks")
15492        );
15493        assert!(
15494            super::validate_tp2_replicated_row_join(2, false, 4096)
15495                .unwrap_err()
15496                .contains("native P2P")
15497        );
15498        assert!(super::validate_tp2_replicated_row_join(2, true, 0).is_err());
15499    }
15500
15501    #[test]
15502    fn door_composition_refuses_first_armed_flag_by_name() {
15503        let table: [(&str, &str); 2] = [
15504            ("MEMRA_DOOR_A", "gated on the unsharded walk only"),
15505            ("MEMRA_DOOR_B", "no sharded branches"),
15506        ];
15507        // cold doors pass
15508        super::refuse_door_composition("MEMRA_X_TP", &table, |_| false).expect("cold doors pass");
15509        // an armed door refuses with the exact byte format the glm5 gate asserts on
15510        let err = super::refuse_door_composition("MEMRA_X_TP", &table, |f| f == "MEMRA_DOOR_B")
15511            .expect_err("armed door must refuse");
15512        assert_eq!(
15513            err,
15514            "MEMRA_X_TP + MEMRA_DOOR_B: unproven composition, refused (no sharded branches)"
15515        );
15516        // a flag outside the table never trips it
15517        super::refuse_door_composition("MEMRA_X_TP", &table, |f| f == "MEMRA_DOOR_C")
15518            .expect("foreign flags are not the matrix");
15519    }
15520
15521    /// THE DEFECT, ASSERTED SO IT CANNOT COME BACK. The retired memo key hashed only the K
15522    /// pointer, the base pointer, the layer and t, while the table it returned ALSO carried
15523    /// the V and LEN pointers. Two different allocation generations that happen to share a K
15524    /// address therefore collide, and the entry the map hands back sends a live launch at
15525    /// another allocation's V and len. This test does not assert the key is fine; it asserts
15526    /// the key is BLIND, which is why `rows_tab_restage_on` exists and defaults ON.
15527    #[test]
15528    fn the_retired_rows_tab_key_cannot_see_the_v_and_len_pointers_it_hands_back() {
15529        let (kp, bp) = (0xdead_0000u64, 0u64);
15530        let live = [[kp, 0x00b1_0000u64, 0x00c1_0000u64, bp]];
15531        let recycled = [[kp, 0x00b2_0000u64, 0x00c2_0000u64, bp]];
15532        assert_eq!(
15533            super::retired_rows_tab_key(kp, bp, 20, 2),
15534            super::retired_rows_tab_key(kp, bp, 20, 2),
15535            "same layer and t must hash the same, or the test proves nothing"
15536        );
15537        let a = super::rows_tab_host(&live, 0x9000, true, 1);
15538        let b = super::rows_tab_host(&recycled, 0x9000, true, 1);
15539        assert_ne!(a, b, "the two generations write DIFFERENT tables");
15540        // ... yet one key covers both, which is exactly the use-after-free.
15541        assert_eq!(
15542            super::retired_rows_tab_key(live[0][0], live[0][3], 20, 1),
15543            super::retired_rows_tab_key(recycled[0][0], recycled[0][3], 20, 1),
15544            "the retired key collides across allocation generations"
15545        );
15546    }
15547
15548    /// The restage must be VALUE-NEUTRAL: on a fresh lookup the memo and the restage produce
15549    /// identical bytes, which is what makes spec-on output byte-identical to spec-off.
15550    #[test]
15551    fn rows_tab_layout_is_the_same_bytes_the_memo_would_have_cached() {
15552        let parts = [
15553            [0x00a0u64, 0x00b0u64, 0x00c0u64, 0x00d0u64],
15554            [0x00a1u64, 0x00b1u64, 0x00c1u64, 0x00d1u64],
15555        ];
15556        let same = super::rows_tab_host(&parts, 0x7000, true, 2);
15557        assert_eq!(
15558            same,
15559            vec![
15560                0x00a0u64, 0x00b0u64, 0x00c0u64, 0x00d0u64, 0x7000,
15561                1, // row 0: back = t-1-r = 1
15562                0x00a1u64, 0x00b1u64, 0x00c1u64, 0x00d1u64, 0x7000, 0, // row 1: back = 0
15563            ],
15564            "same-session rows share one counter cell and step back t-1-r"
15565        );
15566        let cross = super::rows_tab_host(&parts, 0x7000, false, 2);
15567        assert_eq!(
15568            cross,
15569            vec![
15570                0x00a0u64, 0x00b0u64, 0x00c0u64, 0x00d0u64, 0x7000, 0, 0x00a1u64, 0x00b1u64,
15571                0x00c1u64, 0x00d1u64, 0x7004, 0,
15572            ],
15573            "cross-session rows get their own counter cell and no step back"
15574        );
15575    }
15576    use super::*;
15577
15578    #[test]
15579    fn step_expert_activation_clamps_each_arm_by_the_official_contract() {
15580        let limit = Some(7.0);
15581        assert_eq!(step_expert_activation_host(20.0, 9.0, limit), 49.0);
15582        assert_eq!(step_expert_activation_host(20.0, -9.0, limit), -49.0);
15583        assert!(
15584            step_expert_activation_host(-20.0, 9.0, limit).abs()
15585                < step_expert_activation_host(-20.0, 9.0, None).abs()
15586        );
15587        assert!(validate_step_expert_activation_limit(Some(f32::NAN)).is_err());
15588        assert!(validate_step_expert_activation_limit(Some(0.0)).is_err());
15589        assert!(validate_step_expert_activation_limit(limit).is_ok());
15590    }
15591
15592    #[test]
15593    fn moe_residual_host_preserves_official_add_order() {
15594        let output = moe_residual_host(&[1.0e20], &[-1.0e20], &[1.0]).unwrap();
15595        assert_eq!(output, [0.0]);
15596        assert_eq!(
15597            moe_residual_host(&[0.0], &[0.0, 1.0], &[0.0]).unwrap_err(),
15598            "MoE residual lengths residual=1 routed=2 shared=1"
15599        );
15600    }
15601
15602    #[test]
15603    fn expert_owner_routes_preserve_global_pair_order_with_local_expert_ids() {
15604        let selected = [0, 36, 72, 108, 144, 180, 216, 252];
15605        let owners = partition_expert_owner_routes(288, 4, 1, 8, &selected).unwrap();
15606        assert_eq!(owners.len(), 4);
15607        for (rank, owner) in owners.iter().enumerate() {
15608            assert_eq!(owner.rank, rank);
15609            assert_eq!(owner.selected, vec![0, 36]);
15610            assert_eq!(owner.token_rows, vec![0, 0]);
15611            assert_eq!(owner.global_pairs, vec![rank * 2, rank * 2 + 1]);
15612        }
15613    }
15614
15615    #[test]
15616    fn expert_owner_routes_validate_geometry_and_selected_experts() {
15617        assert!(partition_expert_owner_routes(288, 5, 1, 8, &[0; 8]).is_err());
15618        assert!(partition_expert_owner_routes(288, 4, 2, 8, &[0; 8]).is_err());
15619        let error = partition_expert_owner_routes(288, 4, 1, 8, &[288; 8]).unwrap_err();
15620        assert!(error.contains("outside 0..288"));
15621    }
15622
15623    #[test]
15624    fn step_grouped_owner_routes_validate_dynamic_top8_shapes() {
15625        let selected = [
15626            1, 73, 80, 145, 152, 159, 217, 224, 12, 84, 91, 156, 163, 170, 228, 235,
15627        ];
15628        assert_eq!(
15629            validate_step_grouped_owner_routes(288, 2, &selected).unwrap(),
15630            16
15631        );
15632        let owners = partition_expert_owner_routes(288, 4, 2, 8, &selected).unwrap();
15633        assert_eq!(
15634            owners
15635                .iter()
15636                .map(|owner| owner.selected.len())
15637                .collect::<Vec<_>>(),
15638            vec![2, 4, 6, 4]
15639        );
15640        assert!(validate_step_grouped_owner_routes(288, 2, &selected[..8]).is_err());
15641        assert!(validate_step_grouped_owner_routes(288, 1, &[0; 8]).is_err());
15642        assert!(validate_step_grouped_owner_routes(287, 2, &selected).is_err());
15643    }
15644
15645    #[test]
15646    fn weighted_route_combine_requires_a_canonical_pair_permutation() {
15647        let owner0 = [0usize, 3];
15648        let owner1 = [1usize, 2];
15649        let owners = [owner0.as_slice(), owner1.as_slice()];
15650        assert_eq!(
15651            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, 0.2, 0.3, 0.4],)
15652                .unwrap(),
15653            WeightedRouteCombineShape {
15654                pairs: 4,
15655                max_pairs: 12,
15656            }
15657        );
15658        let duplicate = [owner0.as_slice(), &[1usize, 1][..]];
15659        assert!(
15660            validate_weighted_route_combine(4096, 4, 3, 1, &duplicate, &[0.1, 0.2, 0.3, 0.4],)
15661                .is_err()
15662        );
15663        assert!(
15664            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, f32::NAN, 0.3, 0.4],)
15665                .is_err()
15666        );
15667        assert!(
15668            validate_weighted_route_combine(4096, 4, 1, 2, &owners, &[0.1, 0.2, 0.3, 0.4],)
15669                .is_err()
15670        );
15671    }
15672
15673    #[test]
15674    fn native_p2p_door_is_strict_and_default_off() {
15675        assert!(!parse_step_tp_native_p2p(None).unwrap());
15676        assert!(!parse_step_tp_native_p2p(Some("")).unwrap());
15677        assert!(!parse_step_tp_native_p2p(Some("0")).unwrap());
15678        assert!(parse_step_tp_native_p2p(Some("1")).unwrap());
15679        assert!(parse_step_tp_native_p2p(Some("true")).is_err());
15680        assert!(parse_step_tp_native_p2p(Some("2")).is_err());
15681    }
15682
15683    #[test]
15684    fn bulk_p2p_door_is_strict_and_default_off() {
15685        assert!(!parse_step_tp_bulk_p2p(None).unwrap());
15686        assert!(!parse_step_tp_bulk_p2p(Some("")).unwrap());
15687        assert!(!parse_step_tp_bulk_p2p(Some("0")).unwrap());
15688        assert!(parse_step_tp_bulk_p2p(Some("1")).unwrap());
15689        assert!(parse_step_tp_bulk_p2p(Some("true")).is_err());
15690        assert!(parse_step_tp_bulk_p2p(Some("2")).is_err());
15691    }
15692
15693    #[test]
15694    fn ep_device_arithmetic_door_is_strict_and_default_off() {
15695        assert!(!parse_step_ep_device_arithmetic(None).unwrap());
15696        assert!(!parse_step_ep_device_arithmetic(Some("")).unwrap());
15697        assert!(!parse_step_ep_device_arithmetic(Some("0")).unwrap());
15698        assert!(parse_step_ep_device_arithmetic(Some("1")).unwrap());
15699        assert!(parse_step_ep_device_arithmetic(Some("true")).is_err());
15700        assert!(parse_step_ep_device_arithmetic(Some("2")).is_err());
15701    }
15702
15703    #[test]
15704    fn f32_mirror_door_is_strict_and_default_off() {
15705        assert!(!parse_step_tp_f32_mirror(None).unwrap());
15706        assert!(!parse_step_tp_f32_mirror(Some("")).unwrap());
15707        assert!(!parse_step_tp_f32_mirror(Some("0")).unwrap());
15708        assert!(parse_step_tp_f32_mirror(Some("1")).unwrap());
15709        assert!(parse_step_tp_f32_mirror(Some("true")).is_err());
15710        assert!(parse_step_tp_f32_mirror(Some("2")).is_err());
15711    }
15712
15713    fn matrix(out_features: usize, in_features: usize) -> (Vec<u8>, Vec<f32>) {
15714        let codes = (0..out_features * in_features)
15715            .map(|index| (index % 251) as u8)
15716            .collect();
15717        let scales = (0..out_features.div_ceil(FP8_BLOCK) * in_features.div_ceil(FP8_BLOCK))
15718            .map(|index| index as f32 + 1.0)
15719            .collect();
15720        (codes, scales)
15721    }
15722
15723    fn bf16_matrix_bytes(out_features: usize, in_features: usize) -> Vec<u8> {
15724        (0..out_features * in_features)
15725            .flat_map(|value| (value as u16).to_le_bytes())
15726            .collect()
15727    }
15728
15729    fn decode_u16(bytes: &[u8]) -> Vec<u16> {
15730        bytes
15731            .chunks_exact(2)
15732            .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
15733            .collect()
15734    }
15735
15736    #[test]
15737    fn bf16_matrix_rejects_wrong_byte_count() {
15738        let bytes = vec![0u8; 4 * 4 * 2 - 1];
15739        let matrix = Bf16Matrix {
15740            bytes: &bytes,
15741            out_features: 4,
15742            in_features: 4,
15743        };
15744        assert!(matrix.validate().unwrap_err().contains("4x4x2"));
15745    }
15746
15747    #[test]
15748    fn replicated_device_rows_require_exact_rank_local_shapes() {
15749        assert_eq!(
15750            replicated_device_row_values(3, 4096, 4, &[12_288; 4]).unwrap(),
15751            12_288
15752        );
15753        assert!(replicated_device_row_values(0, 4096, 4, &[0; 4]).is_err());
15754        assert!(replicated_device_row_values(3, 0, 4, &[0; 4]).is_err());
15755        assert!(replicated_device_row_values(3, 4096, 4, &[12_288; 3]).is_err());
15756        assert!(
15757            replicated_device_row_values(3, 4096, 4, &[12_288, 12_288, 12_287, 12_288]).is_err()
15758        );
15759        assert!(replicated_device_row_values(usize::MAX, 2, 1, &[0]).is_err());
15760    }
15761
15762    #[test]
15763    fn replicated_device_row_refresh_requires_exact_root_source() {
15764        assert_eq!(
15765            replicated_device_row_source_values(1, 12_288, 12_288, 3, 3).unwrap(),
15766            12_288
15767        );
15768        assert!(replicated_device_row_source_values(0, 12_288, 0, 3, 3).is_err());
15769        assert!(replicated_device_row_source_values(1, 0, 0, 3, 3).is_err());
15770        assert!(replicated_device_row_source_values(1, 12_288, 12_287, 3, 3).is_err());
15771        assert!(replicated_device_row_source_values(1, 12_288, 12_288, 2, 3).is_err());
15772        assert!(replicated_device_row_source_values(usize::MAX, 2, 0, 3, 3).is_err());
15773    }
15774
15775    #[test]
15776    fn step_bf16_canonical_rows_are_topology_invariant_through_tp8() {
15777        for tp in [1, 2, 4, 8] {
15778            assert_eq!(step_bf16_canonical_chunk_rows(8_192, tp).unwrap(), 1_024);
15779            assert_eq!(step_bf16_canonical_chunk_rows(12_288, tp).unwrap(), 1_536);
15780            assert_eq!(step_bf16_canonical_chunk_rows(1_024, tp).unwrap(), 128);
15781            assert_eq!(step_bf16_canonical_chunk_cols(8_192, tp).unwrap(), 1_024);
15782            assert_eq!(step_bf16_canonical_chunk_cols(12_288, tp).unwrap(), 1_536);
15783        }
15784        assert!(step_bf16_canonical_chunk_rows(12_288, 3).is_err());
15785        assert!(step_bf16_canonical_chunk_rows(1_001, 2).is_err());
15786        assert!(step_bf16_canonical_chunk_cols(12_288, 3).is_err());
15787        assert!(step_bf16_canonical_chunk_cols(1_001, 2).is_err());
15788    }
15789
15790    #[test]
15791    fn cache_rows_split_by_token_then_rank() {
15792        let rows = (0u8..24).collect::<Vec<_>>();
15793        assert_eq!(
15794            cache_rank_rows(&rows, 3, 4, 2, 0).unwrap(),
15795            vec![0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19]
15796        );
15797        assert_eq!(
15798            cache_rank_rows(&rows, 3, 4, 2, 1).unwrap(),
15799            vec![4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23]
15800        );
15801        assert!(cache_rank_rows(&rows[..23], 3, 4, 2, 0).is_err());
15802        assert!(cache_rank_rows(&rows, 3, 4, 2, 2).is_err());
15803    }
15804
15805    #[test]
15806    fn bf16_column_shard_preserves_contiguous_output_rows() {
15807        let bytes = bf16_matrix_bytes(4, 4);
15808        let matrix = Bf16Matrix {
15809            bytes: &bytes,
15810            out_features: 4,
15811            in_features: 4,
15812        };
15813        let shard = bf16_column_shard(matrix, 2, 1).unwrap();
15814        assert_eq!(shard.out_features, 2);
15815        assert_eq!(shard.in_features, 4);
15816        assert_eq!(decode_u16(shard.bytes), (8..16).collect::<Vec<_>>());
15817    }
15818
15819    #[test]
15820    fn bf16_row_shard_preserves_each_input_column_window() {
15821        let bytes = bf16_matrix_bytes(3, 4);
15822        let matrix = Bf16Matrix {
15823            bytes: &bytes,
15824            out_features: 3,
15825            in_features: 4,
15826        };
15827        let shard = bf16_row_shard(matrix, 2, 1).unwrap();
15828        assert_eq!(decode_u16(&shard), vec![2, 3, 6, 7, 10, 11]);
15829    }
15830
15831    #[test]
15832    fn bf16_row_block_preserves_global_column_order() {
15833        let bytes = bf16_matrix_bytes(3, 8);
15834        let matrix = Bf16Matrix {
15835            bytes: &bytes,
15836            out_features: 3,
15837            in_features: 8,
15838        };
15839        let block = bf16_row_block(matrix, 2, 3).unwrap();
15840        assert_eq!(decode_u16(&block), vec![2, 3, 4, 10, 11, 12, 18, 19, 20]);
15841    }
15842
15843    #[test]
15844    fn column_shard_preserves_contiguous_weight_and_scale_rows() {
15845        let (codes, scales) = matrix(1280, 4096);
15846        let matrix = E4m3BlockMatrix {
15847            codes: &codes,
15848            scales: &scales,
15849            out_features: 1280,
15850            in_features: 4096,
15851        };
15852        let shard = column_shard(matrix, 2, 1).unwrap();
15853        assert_eq!(shard.out_features, 640);
15854        assert_eq!(shard.codes, &codes[640 * 4096..]);
15855        assert_eq!(shard.scales, &scales[5 * 32..]);
15856    }
15857
15858    #[test]
15859    fn row_shard_preserves_each_weight_and_scale_column_window() {
15860        let (codes, scales) = matrix(4096, 1280);
15861        let matrix = E4m3BlockMatrix {
15862            codes: &codes,
15863            scales: &scales,
15864            out_features: 4096,
15865            in_features: 1280,
15866        };
15867        let (shard_codes, shard_scales) = row_shard(matrix, 2, 1).unwrap();
15868        assert_eq!(shard_codes.len(), 4096 * 640);
15869        assert_eq!(&shard_codes[..640], &codes[640..1280]);
15870        assert_eq!(&shard_codes[640..1280], &codes[1280 + 640..2560]);
15871        assert_eq!(shard_scales.len(), 32 * 5);
15872        assert_eq!(&shard_scales[..5], &scales[5..10]);
15873        assert_eq!(&shard_scales[5..10], &scales[15..20]);
15874    }
15875
15876    #[test]
15877    fn activation_shards_keep_token_rows_separate() {
15878        let activations: Vec<f32> = (0..2 * 8).map(|value| value as f32).collect();
15879        assert_eq!(
15880            activation_shard(&activations, 2, 8, 2, 1),
15881            vec![4.0, 5.0, 6.0, 7.0, 12.0, 13.0, 14.0, 15.0],
15882        );
15883    }
15884
15885    #[test]
15886    fn expert_bank_selects_expert_major_code_and_scale_planes() {
15887        let expert_count = 2;
15888        let out_features = 128;
15889        let in_features = 128;
15890        let code_stride = out_features * in_features;
15891        let codes: Vec<u8> = (0..expert_count * code_stride)
15892            .map(|index| (index % 251) as u8)
15893            .collect();
15894        let scales = vec![1.0f32, 2.0];
15895        let bank = E4m3ExpertBank {
15896            codes: &codes,
15897            scales: &scales,
15898            expert_count,
15899            out_features,
15900            in_features,
15901        };
15902        bank.validate().unwrap();
15903        let expert = bank.expert(1).unwrap();
15904        assert_eq!(expert.codes, &codes[code_stride..]);
15905        assert_eq!(expert.scales, &[2.0]);
15906    }
15907
15908    #[test]
15909    fn expert_bank_rejects_non_positive_scale() {
15910        let codes = vec![0u8; 128 * 128];
15911        let scales = vec![0.0f32];
15912        let bank = E4m3ExpertBank {
15913            codes: &codes,
15914            scales: &scales,
15915            expert_count: 1,
15916            out_features: 128,
15917            in_features: 128,
15918        };
15919        assert!(bank.validate().unwrap_err().contains("non-positive"));
15920    }
15921
15922    #[test]
15923    fn tensor_parallel_column_bank_keeps_each_expert_scale_plane_separate() {
15924        let expert_count = 2;
15925        let out_features = 256;
15926        let in_features = 128;
15927        let code_stride = out_features * in_features;
15928        let scale_stride = 2;
15929        let codes = (0..expert_count * code_stride)
15930            .map(|index| (index % 251) as u8)
15931            .collect::<Vec<_>>();
15932        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
15933        let bank = E4m3ExpertBank {
15934            codes: &codes,
15935            scales: &scales,
15936            expert_count,
15937            out_features,
15938            in_features,
15939        };
15940
15941        let rank = pack_column_bank_rank(bank, 2, 1).unwrap();
15942        assert_eq!(rank.out_features, 128);
15943        assert_eq!(rank.in_features, 128);
15944        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
15945        assert_eq!(rank.scales, vec![11.0, 21.0]);
15946        assert_eq!(&rank.codes[..128 * 128], &codes[128 * 128..256 * 128]);
15947        assert_eq!(
15948            &rank.codes[128 * 128..],
15949            &codes[code_stride + 128 * 128..2 * code_stride]
15950        );
15951        assert_eq!(scale_stride, scales.len() / expert_count);
15952    }
15953
15954    #[test]
15955    fn tensor_parallel_row_bank_keeps_each_expert_scale_plane_separate() {
15956        let expert_count = 2;
15957        let out_features = 128;
15958        let in_features = 256;
15959        let code_stride = out_features * in_features;
15960        let codes = (0..expert_count * code_stride)
15961            .map(|index| (index % 251) as u8)
15962            .collect::<Vec<_>>();
15963        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
15964        let bank = E4m3ExpertBank {
15965            codes: &codes,
15966            scales: &scales,
15967            expert_count,
15968            out_features,
15969            in_features,
15970        };
15971
15972        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
15973        assert_eq!(rank.out_features, 128);
15974        assert_eq!(rank.in_features, 128);
15975        assert_eq!(rank.k_blocks, Some(1));
15976        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
15977        assert_eq!(rank.scales, vec![11.0, 21.0]);
15978        assert_eq!(&rank.codes[..128], &codes[128..256]);
15979        assert_eq!(
15980            &rank.codes[128 * 128..128 * 128 + 128],
15981            &codes[code_stride + 128..code_stride + 256]
15982        );
15983    }
15984
15985    #[test]
15986    fn tensor_parallel_row_bank_preserves_global_k_block_order() {
15987        let expert_count = 2;
15988        let out_features = 256;
15989        let in_features = 512;
15990        let code_stride = out_features * in_features;
15991        let mut codes = vec![0u8; expert_count * code_stride];
15992        for expert in 0..expert_count {
15993            for row in 0..out_features {
15994                for block in 0..4 {
15995                    let value = (expert * 80 + block * 16 + row % 16) as u8;
15996                    let start = expert * code_stride + row * in_features + block * FP8_BLOCK;
15997                    codes[start..start + FP8_BLOCK].fill(value);
15998                }
15999            }
16000        }
16001        let scales = vec![
16002            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,
16003            112.0, 113.0, 114.0,
16004        ];
16005        let bank = E4m3ExpertBank {
16006            codes: &codes,
16007            scales: &scales,
16008            expert_count,
16009            out_features,
16010            in_features,
16011        };
16012
16013        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
16014        assert_eq!(rank.out_features, out_features);
16015        assert_eq!(rank.in_features, 256);
16016        assert_eq!(rank.k_blocks, Some(2));
16017        assert_eq!(rank.code_stride, out_features * 256);
16018        assert_eq!(rank.scale_stride, 4);
16019        assert_eq!(&rank.scales[..4], &[3.0, 13.0, 4.0, 14.0]);
16020        assert_eq!(&rank.scales[4..], &[103.0, 113.0, 104.0, 114.0]);
16021
16022        let block_stride = out_features * FP8_BLOCK;
16023        assert!(rank.codes[..FP8_BLOCK].iter().all(|&code| code == 32));
16024        assert!(
16025            rank.codes[block_stride..block_stride + FP8_BLOCK]
16026                .iter()
16027                .all(|&code| code == 48)
16028        );
16029        assert!(
16030            rank.codes[rank.code_stride..rank.code_stride + FP8_BLOCK]
16031                .iter()
16032                .all(|&code| code == 112)
16033        );
16034        assert!(
16035            rank.codes
16036                [rank.code_stride + block_stride..rank.code_stride + block_stride + FP8_BLOCK]
16037                .iter()
16038                .all(|&code| code == 128)
16039        );
16040    }
16041
16042    #[test]
16043    fn automatic_parallel_policy_needs_only_one_device_set_not_layer_recipes() {
16044        assert_eq!(parse_auto_parallel_devices(None, None).unwrap(), None);
16045        assert_eq!(
16046            parse_auto_parallel_devices(Some("auto"), Some("0,1,2,3")).unwrap(),
16047            Some(vec![0, 1, 2, 3])
16048        );
16049        assert!(parse_auto_parallel_devices(Some("auto"), None).is_err());
16050        assert!(parse_auto_parallel_devices(Some("auto"), Some("0,1,1")).is_err());
16051        assert!(parse_auto_parallel_devices(Some("auto"), Some("0,1,2,3,4")).is_err());
16052        assert!(parse_auto_parallel_devices(Some("ep"), Some("0,1")).is_err());
16053    }
16054
16055    #[test]
16056    fn automatic_ep_device_router_flag_is_strict() {
16057        assert!(!parse_parallel_ep_device_router(None).unwrap());
16058        assert!(!parse_parallel_ep_device_router(Some("0")).unwrap());
16059        assert!(parse_parallel_ep_device_router(Some("1")).unwrap());
16060        assert!(parse_parallel_ep_device_router(Some("true")).is_err());
16061    }
16062
16063    #[test]
16064    fn automatic_ep_graph_flag_is_strict_and_defaults_off() {
16065        assert!(!parse_parallel_ep_graph(None).unwrap());
16066        assert!(!parse_parallel_ep_graph(Some("0")).unwrap());
16067        assert!(parse_parallel_ep_graph(Some("1")).unwrap());
16068        assert!(parse_parallel_ep_graph(Some("true")).is_err());
16069    }
16070
16071    #[test]
16072    fn automatic_ep_pair_down_flag_is_strict_and_defaults_off() {
16073        assert!(!parse_parallel_ep_pair_down(None).unwrap());
16074        assert!(!parse_parallel_ep_pair_down(Some("0")).unwrap());
16075        assert!(parse_parallel_ep_pair_down(Some("1")).unwrap());
16076        assert!(parse_parallel_ep_pair_down(Some("true")).is_err());
16077    }
16078
16079    #[test]
16080    fn automatic_ep_q8_activation_flag_is_strict() {
16081        assert!(!parse_parallel_ep_q8_act(None).unwrap());
16082        assert!(!parse_parallel_ep_q8_act(Some("0")).unwrap());
16083        assert!(parse_parallel_ep_q8_act(Some("1")).unwrap());
16084        assert!(parse_parallel_ep_q8_act(Some("true")).is_err());
16085    }
16086
16087    #[test]
16088    fn automatic_ep_q8_scope_is_explicit_and_strict() {
16089        assert_eq!(parse_parallel_ep_q8_scope(None).unwrap(), None);
16090        assert_eq!(
16091            parse_parallel_ep_q8_scope(Some("all")).unwrap(),
16092            Some(ParallelEpQ8Scope::All)
16093        );
16094        assert_eq!(
16095            parse_parallel_ep_q8_scope(Some("gate-up")).unwrap(),
16096            Some(ParallelEpQ8Scope::GateUp)
16097        );
16098        assert_eq!(
16099            parse_parallel_ep_q8_scope(Some("down")).unwrap(),
16100            Some(ParallelEpQ8Scope::Down)
16101        );
16102        assert!(parse_parallel_ep_q8_scope(Some("input")).is_err());
16103    }
16104
16105    #[test]
16106    fn automatic_ep_q8_gate_up_paired_is_parent_scoped_and_strict() {
16107        assert_eq!(parse_parallel_ep_q8_gu_paired(None).unwrap(), None);
16108        assert_eq!(parse_parallel_ep_q8_gu_paired(Some("")).unwrap(), None);
16109        assert_eq!(
16110            parse_parallel_ep_q8_gu_paired(Some("0")).unwrap(),
16111            Some(false)
16112        );
16113        assert_eq!(
16114            parse_parallel_ep_q8_gu_paired(Some("1")).unwrap(),
16115            Some(true)
16116        );
16117        assert!(parse_parallel_ep_q8_gu_paired(Some("paired")).is_err());
16118        assert!(parse_parallel_ep_q8_gu_paired(Some("true")).is_err());
16119
16120        assert!(!resolve_parallel_ep_q8_gu_paired(None, false, None).unwrap());
16121        assert!(resolve_parallel_ep_q8_gu_paired(None, true, None).unwrap());
16122        assert!(
16123            resolve_parallel_ep_q8_gu_paired(None, true, Some(ParallelEpQ8Scope::GateUp)).unwrap()
16124        );
16125        assert!(
16126            !resolve_parallel_ep_q8_gu_paired(None, true, Some(ParallelEpQ8Scope::Down)).unwrap()
16127        );
16128        assert!(!resolve_parallel_ep_q8_gu_paired(Some("0"), false, None).unwrap());
16129        assert!(!resolve_parallel_ep_q8_gu_paired(Some("0"), true, None).unwrap());
16130        assert!(resolve_parallel_ep_q8_gu_paired(Some("1"), false, None).is_err());
16131        assert!(
16132            resolve_parallel_ep_q8_gu_paired(Some("1"), true, Some(ParallelEpQ8Scope::Down))
16133                .is_err()
16134        );
16135    }
16136
16137    #[test]
16138    fn w4a16_device_ep_accepts_a_capacity_backed_active_prefix() {
16139        let width = 4096;
16140        assert_eq!(
16141            nvfp4_ep_active_input_values(160 * width, 44, width).unwrap(),
16142            44 * width
16143        );
16144        assert_eq!(
16145            nvfp4_ep_active_input_values(44 * width, 44, width).unwrap(),
16146            44 * width
16147        );
16148        assert!(nvfp4_ep_active_input_values(43 * width, 44, width).is_err());
16149        assert!(
16150            nvfp4_ep_active_input_values(160 * width, NVFP4_EP_DEVICE_BATCH_CAP + 1, width)
16151                .is_err()
16152        );
16153    }
16154
16155    #[test]
16156    fn step_ep_layer_specs_are_literal_and_fail_closed() {
16157        assert!(parse_step_ep_layer_specs(None).unwrap().is_empty());
16158        assert!(parse_step_ep_layer_specs(Some("0")).unwrap().is_empty());
16159        assert_eq!(
16160            parse_step_ep_layer_specs(Some("24@1,2")).unwrap(),
16161            vec![StepEpLayerSpec {
16162                layer: 24,
16163                devices: vec![1, 2],
16164            }]
16165        );
16166        assert_eq!(
16167            parse_step_ep_layer_specs(Some("24-25@1,2;31@0,2")).unwrap(),
16168            vec![
16169                StepEpLayerSpec {
16170                    layer: 24,
16171                    devices: vec![1, 2],
16172                },
16173                StepEpLayerSpec {
16174                    layer: 25,
16175                    devices: vec![1, 2],
16176                },
16177                StepEpLayerSpec {
16178                    layer: 31,
16179                    devices: vec![0, 2],
16180                },
16181            ]
16182        );
16183        assert!(parse_step_ep_layer_specs(Some("24@1")).is_err());
16184        assert!(parse_step_ep_layer_specs(Some("24@1,1")).is_err());
16185        assert!(parse_step_ep_layer_specs(Some("layer@1,2")).is_err());
16186        assert!(parse_step_ep_layer_specs(Some("25-24@1,2")).is_err());
16187        assert!(parse_step_ep_layer_specs(Some("0-128@1,2")).is_err());
16188        assert!(parse_step_ep_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
16189        assert!(parse_step_ep_layer_specs(Some("all@0,1")).is_err());
16190    }
16191
16192    #[test]
16193    fn step_tp_layer_specs_share_the_fail_closed_layer_contract() {
16194        assert!(parse_step_tp_layer_specs(None).unwrap().is_empty());
16195        assert!(parse_step_tp_layer_specs(Some("0")).unwrap().is_empty());
16196        assert_eq!(
16197            parse_step_tp_layer_specs(Some("24-25@1,2")).unwrap(),
16198            vec![
16199                StepTpLayerSpec {
16200                    layer: 24,
16201                    devices: vec![1, 2],
16202                },
16203                StepTpLayerSpec {
16204                    layer: 25,
16205                    devices: vec![1, 2],
16206                },
16207            ]
16208        );
16209        let error = parse_step_tp_layer_specs(Some("24@1")).unwrap_err();
16210        assert!(error.contains("MEMRA_STEP_TP"));
16211        assert!(parse_step_tp_layer_specs(Some("24@1,1")).is_err());
16212        assert!(parse_step_tp_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
16213
16214        let all = parse_step_tp_layer_specs(Some("all@0,1,2,3,4,5,6,7")).unwrap();
16215        assert_eq!(all.len(), STEP37_TRUNK_LAYERS);
16216        assert_eq!(all.first().unwrap().layer, 0);
16217        assert_eq!(all.last().unwrap().layer, STEP37_TRUNK_LAYERS - 1);
16218        let devices = (0..8).collect::<Vec<_>>();
16219        assert!(all.iter().all(|spec| spec.devices == devices));
16220        assert!(parse_step_tp_layer_specs(Some("all@0,1;44@0,1")).is_err());
16221    }
16222}
16223
16224// ===== Whole-token graph builder (increment B) ==================================================
16225//
16226// The decode fns are already sectioned at every e/rank/root seam (the stage flow, sweeps_rank,
16227// finish splits, the dcw arm). `graph_section` is the one annotation those seams call: eager
16228// mode runs the closure verbatim; build mode wraps it in a stream capture on the section's
16229// device and records a child + its dependency edges. A token then assembles as ONE multi-device
16230// parent (children per section per layer), launched once per token — the launch-collapse the
16231// per-layer minis could not reach (routes-mini negative, 2026-08-21).
16232
16233/// One captured section: the child graph plus which parent node it became, and the CUDA
16234/// context it was captured under (exec memset updates need it).
16235struct TokenGraphChild {
16236    #[allow(dead_code)]
16237    // allow: keep-alive: the child graph must outlive the exec instantiated from it
16238    graph: cudarc::driver::CudaGraph,
16239    node: cudarc::driver::sys::CUgraphNode,
16240    ctx: cudarc::driver::sys::CUcontext,
16241}
16242
16243/// Exec-updatable fa geometry discovered in one attention rank child: the three partial-pool
16244/// memsets, the dcw fa kernel, and its combine — everything a bucket change touches. Node
16245/// handles address the parent's CLONED child graphs (the M1-probed update path).
16246struct TokenGraphFaSite {
16247    ctx: cudarc::driver::sys::CUcontext,
16248    memset_o: cudarc::driver::sys::CUgraphNode,
16249    memset_m: [cudarc::driver::sys::CUgraphNode; 2],
16250    fa: cudarc::driver::sys::CUgraphNode,
16251    combine: cudarc::driver::sys::CUgraphNode,
16252    window: usize,
16253    n_head: usize,
16254    n_head_kv: usize,
16255    head_dim: usize,
16256}
16257
16258pub struct TokenGraphBuilder {
16259    parent: cudarc::driver::sys::CUgraph,
16260    children: Vec<TokenGraphChild>,
16261    /// Nodes every NEXT section must depend on (the frontier): one node for serial flow,
16262    /// several while a parallel group is open.
16263    frontier: Vec<cudarc::driver::sys::CUgraphNode>,
16264    /// Detached sections: forked from the frontier at issue time, joined ONLY by the next
16265    /// non-group section (they never gate a parallel group merge — the SH1 shape).
16266    pending_detached: Vec<cudarc::driver::sys::CUgraphNode>,
16267    /// Open parallel group: sections issued under the same group id fork from the SAME
16268    /// predecessor set and merge into the frontier together when the group closes.
16269    group: Option<(
16270        u32,
16271        Vec<cudarc::driver::sys::CUgraphNode>,
16272        Vec<cudarc::driver::sys::CUgraphNode>,
16273    )>,
16274}
16275
16276// SAFETY: single decode thread; graph handles are process handles.
16277unsafe impl Send for TokenGraphBuilder {}
16278
16279impl TokenGraphBuilder {
16280    pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
16281        use cudarc::driver::sys;
16282        let mut parent: sys::CUgraph = std::ptr::null_mut();
16283        let r = unsafe { sys::cuGraphCreate(&mut parent, 0) };
16284        if r != sys::CUresult::CUDA_SUCCESS {
16285            return Err(format!("token graph create: {r:?}").into());
16286        }
16287        Ok(Self {
16288            parent,
16289            children: Vec::new(),
16290            frontier: Vec::new(),
16291            pending_detached: Vec::new(),
16292            group: None,
16293        })
16294    }
16295
16296    fn push_child(
16297        &mut self,
16298        graph: cudarc::driver::CudaGraph,
16299        parallel_group: Option<u32>,
16300        detached: bool,
16301        absorb: bool,
16302        ctx: cudarc::driver::sys::CUcontext,
16303    ) -> Result<(), Box<dyn std::error::Error>> {
16304        use cudarc::driver::sys;
16305        // Resolve the dependency set: serial sections depend on the current frontier; a
16306        // parallel-group section depends on the frontier AS OF the group opening; a
16307        // DETACHED section forks like a group member but joins only the next serial section.
16308        let deps: Vec<sys::CUgraphNode> = match (&mut self.group, parallel_group) {
16309            (Some((open, base, _)), Some(group)) if *open == group => base.clone(),
16310            (state, Some(group)) => {
16311                // opening a new group (closing any previous one first)
16312                if let Some((_, _, members)) = state.take() {
16313                    self.frontier = members;
16314                }
16315                let base = self.frontier.clone();
16316                *state = Some((group, base.clone(), Vec::new()));
16317                base
16318            }
16319            (state, None) if detached => match state.as_ref() {
16320                Some((_, base, _)) => base.clone(),
16321                None => self.frontier.clone(),
16322            },
16323            (state, None) => {
16324                if let Some((_, _, members)) = state.take() {
16325                    self.frontier = members;
16326                }
16327                let mut deps = self.frontier.clone();
16328                if absorb {
16329                    deps.append(&mut self.pending_detached);
16330                }
16331                deps
16332            }
16333        };
16334        let mut node: sys::CUgraphNode = std::ptr::null_mut();
16335        let r = unsafe {
16336            sys::cuGraphAddChildGraphNode(
16337                &mut node,
16338                self.parent,
16339                if deps.is_empty() {
16340                    std::ptr::null()
16341                } else {
16342                    deps.as_ptr()
16343                },
16344                deps.len(),
16345                graph.cu_graph(),
16346            )
16347        };
16348        if r != sys::CUresult::CUDA_SUCCESS {
16349            return Err(format!("token graph child: {r:?}").into());
16350        }
16351        match (&mut self.group, parallel_group, detached) {
16352            (_, None, true) => self.pending_detached.push(node),
16353            (Some((_, _, members)), Some(_), _) => members.push(node),
16354            _ => self.frontier = vec![node],
16355        }
16356        self.children.push(TokenGraphChild { graph, node, ctx });
16357        Ok(())
16358    }
16359
16360    pub fn finish(mut self) -> Result<TokenGraph, Box<dyn std::error::Error>> {
16361        use cudarc::driver::sys;
16362        if let Some((_, _, members)) = self.group.take() {
16363            self.frontier = members;
16364        }
16365        // Discover the fa sites BEFORE instantiate: the parent's cloned child graphs hold
16366        // the node handles the exec update path (M1) addresses.
16367        let mut fa_sites = Vec::new();
16368        for child in &self.children {
16369            if let Some(site) = discover_fa_site(child.node, child.ctx)? {
16370                fa_sites.push(site);
16371            }
16372        }
16373        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
16374        let r = unsafe { sys::cuGraphInstantiateWithFlags(&mut exec, self.parent, 0) };
16375        if r != sys::CUresult::CUDA_SUCCESS {
16376            return Err(format!("token graph instantiate: {r:?}").into());
16377        }
16378        Ok(TokenGraph {
16379            exec,
16380            parent: self.parent,
16381            _children: self.children,
16382            fa_sites,
16383        })
16384    }
16385}
16386
16387/// Walk one child graph; if it carries the attention-section signature (exactly three MEMSET
16388/// nodes chained memset->memset->memset->fa_kernel->combine_kernel), return its update site.
16389fn discover_fa_site(
16390    child_node: cudarc::driver::sys::CUgraphNode,
16391    ctx: cudarc::driver::sys::CUcontext,
16392) -> Result<Option<TokenGraphFaSite>, Box<dyn std::error::Error>> {
16393    use cudarc::driver::sys;
16394    fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
16395        if r == sys::CUresult::CUDA_SUCCESS {
16396            Ok(())
16397        } else {
16398            Err(format!("{what}: {r:?}").into())
16399        }
16400    }
16401    let mut graph: sys::CUgraph = std::ptr::null_mut();
16402    unsafe {
16403        cu_try(
16404            sys::cuGraphChildGraphNodeGetGraph(child_node, &mut graph),
16405            "fa-site child GetGraph",
16406        )?;
16407    }
16408    let mut count: usize = 0;
16409    unsafe {
16410        cu_try(
16411            sys::cuGraphGetNodes(graph, std::ptr::null_mut(), &mut count),
16412            "fa-site GetNodes(count)",
16413        )?;
16414    }
16415    let mut nodes: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); count];
16416    unsafe {
16417        cu_try(
16418            sys::cuGraphGetNodes(graph, nodes.as_mut_ptr(), &mut count),
16419            "fa-site GetNodes",
16420        )?;
16421    }
16422    nodes.truncate(count);
16423    let node_type =
16424        |node: sys::CUgraphNode| -> Result<sys::CUgraphNodeType, Box<dyn std::error::Error>> {
16425            let mut ty = sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_EMPTY;
16426            unsafe {
16427                cu_try(
16428                    sys::cuGraphNodeGetType(node, &mut ty),
16429                    "fa-site NodeGetType",
16430                )?;
16431            }
16432            Ok(ty)
16433        };
16434    let memsets: Vec<sys::CUgraphNode> = {
16435        let mut v = Vec::new();
16436        for &node in &nodes {
16437            if node_type(node)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_MEMSET {
16438                v.push(node);
16439            }
16440        }
16441        v
16442    };
16443    if memsets.len() != 3 {
16444        return Ok(None);
16445    }
16446    // Single-stream capture makes the chain linear: follow dependent edges from each memset.
16447    let dependents =
16448        |node: sys::CUgraphNode| -> Result<Vec<sys::CUgraphNode>, Box<dyn std::error::Error>> {
16449            let mut n: usize = 0;
16450            unsafe {
16451                cu_try(
16452                    sys::cuGraphNodeGetDependentNodes_v2(
16453                        node,
16454                        std::ptr::null_mut(),
16455                        std::ptr::null_mut(),
16456                        &mut n,
16457                    ),
16458                    "fa-site GetDependentNodes(count)",
16459                )?;
16460            }
16461            let mut v: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); n];
16462            unsafe {
16463                cu_try(
16464                    sys::cuGraphNodeGetDependentNodes_v2(
16465                        node,
16466                        v.as_mut_ptr(),
16467                        std::ptr::null_mut(),
16468                        &mut n,
16469                    ),
16470                    "fa-site GetDependentNodes",
16471                )?;
16472            }
16473            v.truncate(n);
16474            Ok(v)
16475        };
16476    // The LAST memset is the one whose direct dependent is a kernel (fa); the other two are
16477    // ordered among themselves but interchangeable for width updates.
16478    let mut fa: Option<sys::CUgraphNode> = None;
16479    let mut last_memset: Option<sys::CUgraphNode> = None;
16480    for &ms in &memsets {
16481        for dep in dependents(ms)? {
16482            if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
16483                fa = Some(dep);
16484                last_memset = Some(ms);
16485            }
16486        }
16487    }
16488    let (Some(fa), Some(_last)) = (fa, last_memset) else {
16489        return Ok(None);
16490    };
16491    let mut combine: Option<sys::CUgraphNode> = None;
16492    for dep in dependents(fa)? {
16493        if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
16494            combine = Some(dep);
16495        }
16496    }
16497    let Some(combine) = combine else {
16498        return Ok(None);
16499    };
16500    // Read the fa launch geometry from its baked args (arg order pinned by fa_decode_dcw):
16501    // 6=hd 7=nh 8=nhkv 11=win 13=nsp 14=ski.
16502    let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
16503    unsafe {
16504        cu_try(
16505            sys::cuGraphKernelNodeGetParams_v2(fa, &mut params),
16506            "fa-site KernelNodeGetParams",
16507        )?;
16508    }
16509    let arg_i32 =
16510        |slot: usize| -> i32 { unsafe { *(*params.kernelParams.add(slot) as *const i32) } };
16511    let (hd, nh, nhkv, win) = (arg_i32(6), arg_i32(7), arg_i32(8), arg_i32(11));
16512    // Identify the o-partial memset (hd x wider than the m/l pair).
16513    let width_of = |node: sys::CUgraphNode| -> Result<usize, Box<dyn std::error::Error>> {
16514        let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
16515        unsafe {
16516            cu_try(
16517                sys::cuGraphMemsetNodeGetParams(node, &mut mp),
16518                "fa-site MemsetNodeGetParams",
16519            )?;
16520        }
16521        Ok(mp.width)
16522    };
16523    let mut widest = memsets[0];
16524    for &ms in &memsets[1..] {
16525        if width_of(ms)? > width_of(widest)? {
16526            widest = ms;
16527        }
16528    }
16529    let memset_m: Vec<sys::CUgraphNode> =
16530        memsets.iter().copied().filter(|&m| m != widest).collect();
16531    Ok(Some(TokenGraphFaSite {
16532        ctx,
16533        memset_o: widest,
16534        memset_m: [memset_m[0], memset_m[1]],
16535        fa,
16536        combine,
16537        window: win as usize,
16538        n_head: nh as usize,
16539        n_head_kv: nhkv as usize,
16540        head_dim: hd as usize,
16541    }))
16542}
16543
16544pub struct TokenGraph {
16545    exec: cudarc::driver::sys::CUgraphExec,
16546    parent: cudarc::driver::sys::CUgraph,
16547    _children: Vec<TokenGraphChild>,
16548    fa_sites: Vec<TokenGraphFaSite>,
16549}
16550
16551unsafe impl Send for TokenGraph {}
16552
16553impl TokenGraph {
16554    /// Retarget every fa site to a new bucket via exec param updates (M1 path) — replaces the
16555    /// per-bucket whole-graph rebuild (~55ms) with ~450 node updates (~1ms). Per site the
16556    /// bucket caps at the layer window; nsp/ski/gridDimY and the partial-pool memset widths
16557    /// move together so the exec always matches what a fresh build at `bucket` would bake.
16558    pub fn retarget_bucket(&mut self, bucket: usize) -> Result<(), Box<dyn std::error::Error>> {
16559        use cudarc::driver::sys;
16560        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
16561            if r == sys::CUresult::CUDA_SUCCESS {
16562                Ok(())
16563            } else {
16564                Err(format!("{what}: {r:?}").into())
16565            }
16566        }
16567        for site in &self.fa_sites {
16568            let layer_bucket = if site.window > 0 {
16569                bucket.min(site.window)
16570            } else {
16571                bucket
16572            };
16573            let sp = crate::fa_split_keys(layer_bucket, site.n_head_kv);
16574            let nsp = layer_bucket.div_ceil(sp).max(1);
16575            // fa kernel: nsp (slot 13), ski (slot 14), gridDimY = nsp.
16576            let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
16577            unsafe {
16578                cu_try(
16579                    sys::cuGraphKernelNodeGetParams_v2(site.fa, &mut params),
16580                    "retarget fa GetParams",
16581                )?;
16582                *(*params.kernelParams.add(13) as *mut i32) = nsp as i32;
16583                *(*params.kernelParams.add(14) as *mut i32) = sp as i32;
16584                params.gridDimY = nsp as u32;
16585                cu_try(
16586                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.fa, &params),
16587                    "retarget fa SetParams",
16588                )?;
16589            }
16590            // combine: nsp (slot 6).
16591            let mut cparams: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
16592            unsafe {
16593                cu_try(
16594                    sys::cuGraphKernelNodeGetParams_v2(site.combine, &mut cparams),
16595                    "retarget combine GetParams",
16596                )?;
16597                *(*cparams.kernelParams.add(6) as *mut i32) = nsp as i32;
16598                cu_try(
16599                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.combine, &cparams),
16600                    "retarget combine SetParams",
16601                )?;
16602            }
16603            // partial-pool memsets: o = nh*nsp*hd elements, m/l = nh*nsp.
16604            let set_width =
16605                |node: sys::CUgraphNode, width: usize| -> Result<(), Box<dyn std::error::Error>> {
16606                    let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
16607                    unsafe {
16608                        cu_try(
16609                            sys::cuGraphMemsetNodeGetParams(node, &mut mp),
16610                            "retarget memset GetParams",
16611                        )?;
16612                    }
16613                    mp.width = width;
16614                    unsafe {
16615                        cu_try(
16616                            sys::cuGraphExecMemsetNodeSetParams(self.exec, node, &mp, site.ctx),
16617                            "retarget memset SetParams",
16618                        )?;
16619                    }
16620                    Ok(())
16621                };
16622            set_width(site.memset_o, site.n_head * nsp * site.head_dim)?;
16623            set_width(site.memset_m[0], site.n_head * nsp)?;
16624            set_width(site.memset_m[1], site.n_head * nsp)?;
16625        }
16626        Ok(())
16627    }
16628
16629    pub fn launch(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
16630        use cudarc::driver::sys;
16631        let _main = e.gpu.enter_main()?;
16632        let r = unsafe { sys::cuGraphLaunch(self.exec, e.stream().cu_stream() as sys::CUstream) };
16633        if r != sys::CUresult::CUDA_SUCCESS {
16634            return Err(format!("token graph launch: {r:?}").into());
16635        }
16636        Ok(())
16637    }
16638}
16639
16640impl Drop for TokenGraph {
16641    fn drop(&mut self) {
16642        unsafe {
16643            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
16644            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
16645        }
16646    }
16647}
16648
16649std::thread_local! {
16650    static TOKEN_GRAPH_BUILDER: std::cell::RefCell<Option<TokenGraphBuilder>> =
16651        const { std::cell::RefCell::new(None) };
16652}
16653
16654/// Arm the thread-local builder (build mode) — the next `graph_section` calls capture.
16655pub fn token_graph_build_begin() -> Result<(), Box<dyn std::error::Error>> {
16656    let builder = TokenGraphBuilder::new()?;
16657    TOKEN_GRAPH_BUILDER.with(|cell| *cell.borrow_mut() = Some(builder));
16658    Ok(())
16659}
16660
16661/// Take the finished parent (ends build mode).
16662pub fn token_graph_build_finish() -> Result<TokenGraph, Box<dyn std::error::Error>> {
16663    let builder = TOKEN_GRAPH_BUILDER
16664        .with(|cell| cell.borrow_mut().take())
16665        .ok_or("token graph build was not begun")?;
16666    builder.finish()
16667}
16668
16669/// True while the thread-local builder is armed.
16670pub fn token_graph_building() -> bool {
16671    TOKEN_GRAPH_BUILDER.with(|cell| cell.borrow().is_some())
16672}
16673
16674/// The section annotation: eager mode runs the closure verbatim; build mode wraps it in a
16675/// stream capture on `engine`'s stream and records the child. Sections sharing a
16676/// `parallel_group` id fork from the same predecessor set and merge together. The closure
16677/// must be capture-safe (raw copies at cross-context seams, no host syncs, no events).
16678pub fn graph_section<F>(
16679    engine: &Engine,
16680    parallel_group: Option<u32>,
16681    f: F,
16682) -> Result<(), Box<dyn std::error::Error>>
16683where
16684    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
16685{
16686    graph_section_opts(engine, parallel_group, false, false, f)
16687}
16688
16689/// Serial section that ALSO joins every pending detached section (the SH1 consumer shape).
16690pub fn graph_section_absorbing<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
16691where
16692    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
16693{
16694    graph_section_opts(engine, None, false, true, f)
16695}
16696
16697/// `graph_section` with the DETACHED shape: forks from the current frontier (or the open
16698/// group base) and is joined only by the next serial section — never gates a group merge.
16699pub fn graph_section_detached<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
16700where
16701    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
16702{
16703    graph_section_opts(engine, None, true, false, f)
16704}
16705
16706pub fn graph_section_opts<F>(
16707    engine: &Engine,
16708    parallel_group: Option<u32>,
16709    detached: bool,
16710    absorb: bool,
16711    f: F,
16712) -> Result<(), Box<dyn std::error::Error>>
16713where
16714    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
16715{
16716    let building = token_graph_building();
16717    if !building {
16718        let mut f = f;
16719        return f();
16720    }
16721    let (child, ctx) = {
16722        let _main = engine.gpu.enter_main()?;
16723        let mut ctx: cudarc::driver::sys::CUcontext = std::ptr::null_mut();
16724        let r = unsafe { cudarc::driver::sys::cuCtxGetCurrent(&mut ctx) };
16725        if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
16726            return Err(format!("graph section ctx query: {r:?}").into());
16727        }
16728        let mut f = f;
16729        // NO WARMUP RUNS: section bodies carry device side effects (dcw appends, counter
16730        // incs) that a warmup would really execute — the len_d-drift crash of 2026-08-21.
16731        let (child, _retained) = engine.capture_graph_retained_nowarm(|_| f())?;
16732        (child, ctx)
16733    };
16734    TOKEN_GRAPH_BUILDER.with(|cell| {
16735        cell.borrow_mut()
16736            .as_mut()
16737            .expect("builder checked above")
16738            .push_child(child, parallel_group, detached, absorb, ctx)
16739    })
16740}