Skip to content

Add MoE + expert-parallel (MoeEp) Python API with MegaMoE CuTe DSL backend - #448

Open
mhoqueanik wants to merge 10 commits into
NVIDIA:developfrom
mhoqueanik:moe_ep_megamoe_backend
Open

Add MoE + expert-parallel (MoeEp) Python API with MegaMoE CuTe DSL backend#448
mhoqueanik wants to merge 10 commits into
NVIDIA:developfrom
mhoqueanik:moe_ep_megamoe_backend

Conversation

@mhoqueanik

@mhoqueanik mhoqueanik commented Jul 29, 2026

Copy link
Copy Markdown

Wire the MegaMoE backend into cudnn.MoeEp

This PR completes the cudnn.MoeEp API introduced in #389 by replacing the allocate-only implementation with a real device backend.

MoeEp.__call__ and backward now execute fused SM100 (GB200) MegaMoE megakernels:

  • Forward: NVSHMEM dispatch → grouped FC1/SwiGLU/FC2 → combine in a single launch
  • Backward: fused mega backward producing all four gradients

The kernel sources are now bundled inside cuDNN, so a fresh clone requires no external checkout or path environment variables.

The bundle includes:

  • MXFP8 forward (training path)
  • CuTe DSL NVFP4 (FP4) forward
  • FP8 mega backward

Note: the backward (bprop) implementation is from the FlashInfer team, not the FastKernel team.

Backend behavior

Supported configurations:

  • SM100+
  • apply_topk_in_fc1=True
  • hidden/intermediate dimensions multiples of 32 (128 for the generate_c/backward path)
  • combine_format="bf16" for training

Internal compute uses MXFP8, so outputs match the FP32 reference within ~1e-2–7e-2 relative tolerance. route_metadata and quantized outputs remain bit-exact.

Validation

  • Pytest: 18 passed, 7 xfailed (expected strict bit-exact gates)
  • Kernel parity: forward rel_err 6.5e-2; all four backward gradients ≤ 6.6e-2 (gate 0.10); metadata equal; output quantizer bit-exact
  • Runner: test/python/fe_api/moe_ep/run_pr_tests.sh
  • Runbook: test/python/fe_api/moe_ep/RUNBOOK.md

Files touched

Integration

File Change
python/cudnn/moe_ep/_megamoe.py New backend shim and backend integration
python/cudnn/moe_ep/api.py MoeEp.__call__/backward delegate to backend while preserving fallback
test/python/fe_api/moe_ep/megamoe_backend_parity.py New kernel-vs-reference parity test
test/python/fe_api/moe_ep/run_pr_tests.sh One-shot validation runner
test/python/fe_api/moe_ep/RUNBOOK.md Environment setup and validation guide
docs/fe-oss-apis/moe_ep.md Backend status and attribution updates
.pre-commit-config.yaml Exclude vendored backend from Black

Vendored backend

103 files under python/cudnn/moe_ep/_megamoe_backend/ containing the MegaMoE Python package, CuTe DSL kernels (MXFP8 + NVFP4), FP8 mega backward, PyTorch reference package, NVSHMEM infrastructure, and provenance/requirements.

Summary by CodeRabbit

  • New Features

    • Added a public Mixture-of-Experts API with expert-parallel execution support.
    • Added BF16, MXFP8, and NVFP4 output and combine formats, including block-scaled tensor dequantization.
    • Added forward and backward support with routing metadata and training intermediates.
    • Added optimized GPU execution for supported configurations.
  • Documentation

    • Added API guidance covering tensor contracts, quantization formats, setup, and supported configurations.
  • Tests

    • Added reference implementations, quantization checks, parity validation, and single- and multi-device coverage.

Anerudhan and others added 5 commits July 14, 2026 10:49
…tests

MoeEp exposes the constructor/forward contract for the fused SwiGLU MoE
with EP dispatch (bf16, mxfp8, and nvfp4 public outputs); forward
allocates the output representation until the device kernel lands.
The pure-PyTorch MoeEpReference implements the full semantics —
routing, variable-size all-to-all dispatch, local experts, Form-A
top-k combine, and block-scaled quantization — and is validated by
test/python/fe_api/moe_ep against naive per-token references, including
2-rank (gloo) and 4-GPU (NCCL) expert-parallel runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The doc predated the training-integration work: it still listed "no
backward API" and "no auxiliary FC1 capture" as first-version
boundaries and described the reference as inference-forward only.
Refresh the status line, return-value contract, reference-scope notes,
and validation matrix to match MoeEpReference.backward, the
generate_c=True (output, fc1_c, route_metadata) return, and the current
test suite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The proposed-API section only showed the constructor and __call__; add
the backward signature, its argument/return tables, and the collective
requirement. Give MoeEp a matching allocation-only backward stub
(validating the generate_c stash shapes) with a passing allocation test
and a strict-xfail gate against MoeEpReference.backward.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MoeEp.__call__/backward now delegate to the megamoe training package
(SM100 MegaMoE MXFP8 mega-kernel + bwd_impl="mega" backward) when the
configuration is supported, falling back to the original allocate-only
stubs otherwise (CUDNN_MOE_EP_BACKEND=auto|megamoe|none,
CUDNN_MEGAMOE_ROOT locates the package).

The backend adapts the public contract to the kernel: weight layout
transpose + gate/up half swap, logical fc1_c/route_metadata extraction
from the kernel pools (32-block de-interleave, contract row order),
reference-bit-exact host quantization for mxfp8/nvfp4 output_format,
and fp32 gradient conversion from mega_backward.

megamoe_backend_parity.py validates MoeEp-on-megamoe against
MoeEpReference at kernel shapes (single-rank and torchrun EP).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds the public MoeEp expert-parallel API, block-scaled tensor formats, a PyTorch reference, MegaMoE device backends, vendored kernels, documentation, and forward/backward validation.

Changes

MoE Expert Parallel API

Layer / File(s) Summary
API contract and public surface
docs/fe-oss-apis/..., python/cudnn/__init__.py, python/cudnn/moe_ep/...
Defines MoeEp, MoeFormat, BlockScaledTensor, routing, quantization, stash, and gradient contracts, then exposes them through package exports.
Reference routing and gradients
test/python/fe_api/moe_ep/moe_ep_reference.py
Implements block quantization, expert dispatch, SwiGLU execution, output combination, generate_c metadata, and backward gradients.
MegaMoE backend execution
python/cudnn/moe_ep/_megamoe.py, python/cudnn/moe_ep/_megamoe_backend/megamoe/...
Adds backend selection, capability checks, kernel setup, quantized output handling, stash extraction, and backward integration.
Vendored device kernels and communication
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/...
Adds MXFP8/NVFP4 kernels, dispatch and token communication, schedulers, workspace management, quantization, reduction, and backward device paths.
Validation and operational tooling
test/python/fe_api/moe_ep/..., python/cudnn/moe_ep/_megamoe_backend/.../tests/*, *.sh, *.md
Adds unit, CUDA, parity, distributed, functional, and runbook coverage.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • NVIDIA/cudnn-frontend#389: Extends the related MoE-EP API, reference, export, and test contracts with MegaMoE integration.
  • NVIDIA/cudnn-frontend#476: Relates to the grouped GEMM, SwiGLU, and block-scaled quantization kernel work used by this backend.

Suggested labels: cat-feature, mod-frontend, mod-cutedsl, mod-frost

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description explains implementation and testing but omits the template checklist, affected area, why, related issues, and explicit API compatibility section. Add the missing template sections, complete the checklist, identify the affected area, explain the rationale, list related issues, and state API compatibility impact.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the addition of the MoeEp Python API and its MegaMoE expert-parallel backend.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch moe_ep_megamoe_backend
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (3)
test/python/fe_api/moe_ep/test_moe_ep.py (2)

847-855: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider promoting this out of L0.

The module-level pytestmark = pytest.mark.L0 (line 19) applies here too, but a 4-rank NCCL spawn is not a fast test. Overriding it with a higher level keeps L0 quick.

As per coding guidelines, "keep L0 tests fast and place large parameter sweeps at higher levels."

♻️ Proposed tweak
+@pytest.mark.L2
 `@pytest.mark.skipif`(
     not dist.is_available() or not dist.is_nccl_available() or torch.cuda.device_count() < 4,
     reason="requires NCCL and at least 4 GPUs",
 )
 def test_four_rank_expert_parallel(tmp_path):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/fe_api/moe_ep/test_moe_ep.py` around lines 847 - 855, Override
the module-level L0 marker on test_four_rank_expert_parallel with the
appropriate higher-level pytest marker, while preserving its existing skip
conditions and four-rank NCCL behavior.

Source: Coding guidelines


216-216: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Pin the accumulator's device.

torch.zeros(...) defaults to CPU, so this shared helper silently breaks the moment it's called with CUDA inputs. device=a.device costs nothing.

♻️ Proposed tweak
-        acc = torch.zeros(a.shape[1], dtype=torch.float32)
+        acc = torch.zeros(a.shape[1], dtype=torch.float32, device=a.device)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/fe_api/moe_ep/test_moe_ep.py` at line 216, Update the accumulator
initialization in the shared helper to create the tensor on the same device as
input tensor a by reusing a.device, while preserving its shape and float32
dtype.
python/cudnn/moe_ep/_megamoe.py (1)

214-271: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated quantizer with only its MXFP8 branch pinned. The backend re-implements the reference quantize_blockwise, and the only bit-exactness check covers MXFP8, leaving the NVFP4 nibble path free to drift from the reference.

  • python/cudnn/moe_ep/_megamoe.py#L214-L271: import/share the reference quantizer instead of keeping a hand copy of _quantize_output/_nearest_e2m1_codes (or move the single implementation into a non-test module both sides can use).
  • test/python/fe_api/moe_ep/megamoe_backend_parity.py#L139-L147: add an NVFP4 case alongside the MXFP8 torch.equal check so both branches are verified.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe.py` around lines 214 - 271, Replace the
duplicated _quantize_output and _nearest_e2m1_codes implementation in
python/cudnn/moe_ep/_megamoe.py:214-271 with the shared reference quantizer, or
move that single implementation into a non-test module used by both callers. In
test/python/fe_api/moe_ep/megamoe_backend_parity.py:139-147, add an NVFP4
torch.equal parity assertion alongside the existing MXFP8 check; no other site
changes are required.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/fe-oss-apis/moe_ep.md`:
- Around line 3-8: The status paragraph in the MoeEp API documentation is
outdated because MoeEp.__call__ now uses the MegaMoE device backend when
configured. Update the forward and backward descriptions to distinguish the
backend path from the fallback implementation, document CUDNN_MOE_EP_BACKEND and
CUDNN_MEGAMOE_ROOT as selectors, and retain the uninitialized-storage/no-kernel
behavior only for the fallback path.

In `@python/cudnn/moe_ep/_megamoe.py`:
- Around line 336-345: Rename the ambiguous I local in _to_kernel_weights and
the corresponding locals at the referenced locations to inter or intermediate,
updating every use consistently while preserving the existing tensor slicing and
shape transformations.
- Around line 400-412: The backend selection and _extract_stash collective path
must use a consistent decision across all EP ranks. When ep_group is set, make
maybe_create’s availability result collective by all-reducing the per-rank
success flag, and ensure every rank either selects the same backend or falls
back together before executing the two collectives in _extract_stash. Preserve
the existing behavior for non-distributed execution.
- Around line 454-462: Update the backward path around the visible fc1_c and
route_metadata parameters to honor the module docstring: extract the current
pool stash and cheaply validate both arguments, checking fc1_c shape and
route_metadata equality before computing gradients. Reject mismatches rather
than silently ignoring stale forward arguments; keep the pool stash as the
source of truth after validation.
- Around line 132-140: Replace tempfile.mktemp in the process-group
initialization with tempfile.mkdtemp to create a private 0700 temporary
directory, and pass that directory path to dist.FileStore while preserving the
existing prefix and initialization behavior.
- Around line 209-211: Update _weight_signature to use a tensor-object identity
or weak-reference-based invalidation signal rather than only data_ptr() and
_version, including the underlying data tensor for BlockScaledTensor. Ensure
replacing a weight with a fresh tensor cannot match the prior signature, so
_sync_weights refreshes kernel weights.

In `@python/cudnn/moe_ep/api.py`:
- Around line 139-180: The MoE EP API lacks the required frontend-only
scaffolding. In python/cudnn/moe_ep/api.py lines 139-180, make MoeEp subclass
APIBase and implement check_support() returning bool while setting
self._is_supported, compile(), execute(..., current_stream=None), and
moe_ep_wrapper() that allocates outputs and returns a TupleDict with documented
key order; alternatively document the proposal-stage deviation in
docs/fe-oss-apis/moe_ep.md. In python/cudnn/moe_ep/__init__.py lines 4-6, export
moe_ep_wrapper alongside MoeEp through __all__.
- Around line 386-398: Update MoeEp.backward to resolve or validate _backend
before dispatching to _backend.backward, rather than treating an unresolved
backend as a valid path. Reuse the same backend-resolution logic as __call__,
ensuring backward either invokes the available MegaMoE backend or raises clearly
when it cannot be resolved, never returning uninitialized gradients.
- Around line 63-98: Strengthen BlockScaledTensor.__post_init__ to validate data
and scale shapes against the normalized logical_shape/axis, require the
supported scale dtype, and ensure data and scale are on the same device. Update
dequantize’s NVFP4 unpacking path to reinterpret self.data as torch.uint8 before
applying nibble bit operations, preserving support for both uint8 and
torch.float4_e2m1fn_x2 payloads.
- Around line 331-337: Update the backend initialization flow in the enclosing
forward method so `_MegamoeBackend` is not permanently sized from the first
call’s token_count when max_tokens_per_rank is unset. Cache or recreate backends
keyed by both device and token_count, or otherwise initialize them using an
explicit upper bound, while preserving reuse for compatible calls and ensuring
later larger batches do not exceed _max_tokens.

In `@test/python/fe_api/moe_ep/megamoe_backend_parity.py`:
- Around line 115-121: Update the parity failure aggregation near the existing
fc1_c diagnostics so fc1_c shape mismatches and relative-error violations
contribute to failures alongside the other forward and metadata checks. Reuse
the established tolerance and failure-reporting pattern, ensuring an incorrect
fc1_c stash causes the test to report failure rather than only printing inf.

In `@test/python/fe_api/moe_ep/test_moe_ep.py`:
- Around line 498-520: Add a module- or test-level CUDA availability gate for
the GPU-dependent Moe EP tests, including the strict xfail variants, using the
project’s supported capability checks so CPU-only runners skip them instead of
passing spuriously. Apply the gate around the relevant tests in test_moe_ep.py
while preserving their existing assertions and xfail behavior on supported CUDA
environments.

---

Nitpick comments:
In `@python/cudnn/moe_ep/_megamoe.py`:
- Around line 214-271: Replace the duplicated _quantize_output and
_nearest_e2m1_codes implementation in python/cudnn/moe_ep/_megamoe.py:214-271
with the shared reference quantizer, or move that single implementation into a
non-test module used by both callers. In
test/python/fe_api/moe_ep/megamoe_backend_parity.py:139-147, add an NVFP4
torch.equal parity assertion alongside the existing MXFP8 check; no other site
changes are required.

In `@test/python/fe_api/moe_ep/test_moe_ep.py`:
- Around line 847-855: Override the module-level L0 marker on
test_four_rank_expert_parallel with the appropriate higher-level pytest marker,
while preserving its existing skip conditions and four-rank NCCL behavior.
- Line 216: Update the accumulator initialization in the shared helper to create
the tensor on the same device as input tensor a by reusing a.device, while
preserving its shape and float32 dtype.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 68f67f5b-0bc8-4458-96bf-e5755031f123

📥 Commits

Reviewing files that changed from the base of the PR and between 5b5e60f and d5cb1d4.

📒 Files selected for processing (9)
  • docs/fe-oss-apis/moe_ep.md
  • docs/fe-oss-apis/overview.md
  • python/cudnn/__init__.py
  • python/cudnn/moe_ep/__init__.py
  • python/cudnn/moe_ep/_megamoe.py
  • python/cudnn/moe_ep/api.py
  • test/python/fe_api/moe_ep/megamoe_backend_parity.py
  • test/python/fe_api/moe_ep/moe_ep_reference.py
  • test/python/fe_api/moe_ep/test_moe_ep.py

Comment on lines +3 to +8
Status: public API stub, implementation proposal, and executable PyTorch
reference for both forward and backward. The API currently allocates
uninitialized output storage without launching a device kernel. Its numerical
comparison is therefore marked as a strict expected failure under
`test/python/fe_api/moe_ep`; remove that marker when backend execution is
connected.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Status paragraph is stale relative to the MegaMoE backend in this PR.

MoeEp.__call__ now dispatches to the MegaMoE device backend when available (api.py lines 157-164, selected via CUDNN_MOE_EP_BACKEND / CUDNN_MEGAMOE_ROOT), so "allocates uninitialized output storage without launching a device kernel" is only the fallback path. Same for the backward note at lines 156-160. Consider documenting the backend-selection env vars and the fallback semantics here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/fe-oss-apis/moe_ep.md` around lines 3 - 8, The status paragraph in the
MoeEp API documentation is outdated because MoeEp.__call__ now uses the MegaMoE
device backend when configured. Update the forward and backward descriptions to
distinguish the backend path from the fallback implementation, document
CUDNN_MOE_EP_BACKEND and CUDNN_MEGAMOE_ROOT as selectors, and retain the
uninitialized-storage/no-kernel behavior only for the fallback path.

Source: Path instructions

Comment on lines +132 to +140
import tempfile

store_path = tempfile.mktemp(prefix="cudnn_moe_ep_pg_")
dist.init_process_group(
"nccl",
store=dist.FileStore(store_path, 1),
world_size=1,
rank=0,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Replace tempfile.mktemp with a private temp directory.

mktemp is deprecated and racy in shared /tmp (another process can pre-create/symlink the path before FileStore opens it). A mkdtemp directory keeps the same FileStore semantics with 0700 ownership.

🔒 Proposed fix
-    import tempfile
+    import tempfile
 
-    store_path = tempfile.mktemp(prefix="cudnn_moe_ep_pg_")
+    store_path = os.path.join(tempfile.mkdtemp(prefix="cudnn_moe_ep_pg_"), "store")
     dist.init_process_group(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import tempfile
store_path = tempfile.mktemp(prefix="cudnn_moe_ep_pg_")
dist.init_process_group(
"nccl",
store=dist.FileStore(store_path, 1),
world_size=1,
rank=0,
)
import tempfile
store_path = os.path.join(tempfile.mkdtemp(prefix="cudnn_moe_ep_pg_"), "store")
dist.init_process_group(
"nccl",
store=dist.FileStore(store_path, 1),
world_size=1,
rank=0,
)
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 133-133: The function mktemp is deprecated. When using this function, it is possible for an attacker to modify the created file before the filename is returned. Use NamedTemporaryFile() instead and pass it the delete=False parameter.
Context: tempfile.mktemp(prefix="cudnn_moe_ep_pg_")
Note: [CWE-377]: Insecure Temporary File [OWASP A01:2021]: Broken Access Control

(avoid-mktemp-python)

🪛 Ruff (0.16.0)

[error] 134-134: Use of insecure and deprecated function (mktemp)

(S306)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe.py` around lines 132 - 140, Replace
tempfile.mktemp in the process-group initialization with tempfile.mkdtemp to
create a private 0700 temporary directory, and pass that directory path to
dist.FileStore while preserving the existing prefix and initialization behavior.

Source: Linters/SAST tools

Comment on lines +209 to +211
def _weight_signature(tensor: MoeTensor) -> Tuple:
data = tensor.data if isinstance(tensor, BlockScaledTensor) else tensor
return (data.data_ptr(), data._version)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== File map ==\n'
git ls-files 'python/cudnn/moe_ep/_megamoe.py' 'python/cudnn/moe_ep/*.py' 'test/python/fe_api/*' | sed 's#^`#-` #'

printf '\n== Relevant lines in python/cudnn/moe_ep/_megamoe.py ==\n'
nl -ba python/cudnn/moe_ep/_megamoe.py | sed -n '180,280p'

printf '\n== Search for weight signature / sync logic ==\n'
rg -n "_weight_signature|_sync_weights|data_ptr\(|_version|weight cache|cache key" python/cudnn/moe_ep -S

printf '\n== Related tensor replacement / weakref usage ==\n'
rg -n "weakref|id\(|data_ptr\(|_version" python/cudnn/moe_ep -S

printf '\n== Test coverage nearby ==\n'
rg -n "_weight_signature|_sync_weights|megamoe|moe_ep" test/python -S

Repository: NVIDIA/cudnn-frontend

Length of output: 3673


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== python/cudnn/moe_ep/_megamoe.py (selected range) ==\n'
sed -n '180,280p' python/cudnn/moe_ep/_megamoe.py | cat -n

printf '\n== Full outline of python/cudnn/moe_ep/_megamoe.py ==\n'
python3 - <<'PY'
from pathlib import Path
p = Path("python/cudnn/moe_ep/_megamoe.py")
for i, line in enumerate(p.read_text().splitlines(), 1):
    if "_weight_signature" in line or "_sync_weights" in line or "cache" in line or "weight" in line:
        print(f"{i}: {line}")
PY

printf '\n== moe_ep tests and references ==\n'
sed -n '1,260p' test/python/fe_api/moe_ep/test_moe_ep.py | cat -n
printf '\n== backend parity test ==\n'
sed -n '1,260p' test/python/fe_api/moe_ep/megamoe_backend_parity.py | cat -n

printf '\n== Search for tensor replacement / identity assumptions in moe_ep ==\n'
rg -n "reassign|replace|new_tensor|weakref|data_ptr\(|_version|signature|sync" python/cudnn/moe_ep test/python/fe_api/moe_ep -S

Repository: NVIDIA/cudnn-frontend

Length of output: 24711


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== _MegamoeBackend weight sync implementation ==\n'
sed -n '286,380p' python/cudnn/moe_ep/_megamoe.py | cat -n

printf '\n== Forward/backward call sites around sync ==\n'
sed -n '380,490p' python/cudnn/moe_ep/_megamoe.py | cat -n

printf '\n== Search for layer.refresh_weights and weight mutation semantics ==\n'
rg -n "refresh_weights|_weight_sig|_layer =|build.*weights|setattr\(.*weight|weight =" python/cudnn/moe_ep -S

printf '\n== Public API entrypoints for MoeEp ==\n'
sed -n '1,220p' python/cudnn/moe_ep/__init__.py | cat -n
sed -n '1,240p' python/cudnn/moe_ep/api.py | cat -n

Repository: NVIDIA/cudnn-frontend

Length of output: 21744


🏁 Script executed:

python3 - <<'PY'
import gc
import torch

print("torch:", torch.__version__)

x = torch.empty(8)
print("initial version:", x._version, "ptr:", x.data_ptr())
x.add_(1)
print("after in-place add_ version:", x._version, "ptr:", x.data_ptr())

# Try to observe pointer reuse for fresh tensors of the same size.
seen = {}
reuse = []
for i in range(20000):
    t = torch.empty(1024)
    ptr = t.data_ptr()
    if ptr in seen:
        reuse.append((i, seen[ptr], ptr, t._version))
        break
    seen[ptr] = i
    del t
    if i % 100 == 0:
        gc.collect()

print("pointer reuse observed:", bool(reuse))
if reuse:
    i, j, ptr, ver = reuse[0]
    print("first reuse at iter", i, "previous iter", j, "ptr", ptr, "version", ver)

# Show that a brand-new tensor typically starts at version 0.
y = torch.empty(8)
print("fresh tensor version:", y._version)
PY

Repository: NVIDIA/cudnn-frontend

Length of output: 277


🏁 Script executed:

python3 - <<'PY'
import gc
import torch

print("torch:", torch.__version__)

x = torch.empty(8)
print("initial version:", x._version, "ptr:", x.data_ptr())
x.add_(1)
print("after in-place add_ version:", x._version, "ptr:", x.data_ptr())

seen = {}
reuse = []
for i in range(20000):
    t = torch.empty(1024)
    ptr = t.data_ptr()
    if ptr in seen:
        reuse.append((i, seen[ptr], ptr, t._version))
        break
    seen[ptr] = i
    del t
    if i % 100 == 0:
        gc.collect()

print("pointer reuse observed:", bool(reuse))
if reuse:
    i, j, ptr, ver = reuse[0]
    print("first reuse at iter", i, "previous iter", j, "ptr", ptr, "version", ver)

y = torch.empty(8)
print("fresh tensor version:", y._version)
PY

Repository: NVIDIA/cudnn-frontend

Length of output: 277


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== BlockScaledTensor / MoeTensor usage ==\n'
rg -n "BlockScaledTensor|MoeTensor|dequantize\(" python/cudnn/moe_ep test/python/fe_api -S

printf '\n== weight-related docs and examples ==\n'
sed -n '1,220p' test/python/fe_api/moe_ep/moe_ep_reference.py | cat -n
sed -n '1,240p' python/cudnn/moe_ep/api.py | cat -n

printf '\n== any tests exercising repeated calls with changing weights ==\n'
rg -n "forward\(.*fc1_weight|backward\(.*fc1_weight|same tensor|reassign|clone\(\)|copy_\(" test/python/fe_api/moe_ep -S

Repository: NVIDIA/cudnn-frontend

Length of output: 28377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '380,460p' test/python/fe_api/moe_ep/test_moe_ep.py | cat -n

Repository: NVIDIA/cudnn-frontend

Length of output: 3112


🌐 Web query:

PyTorch tensor _version in-place operations version counter and caching allocator pointer reuse data_ptr reused same address

💡 Result:

In PyTorch, the relationship between tensor versioning and memory management is critical for autograd safety, but they operate as distinct mechanisms [1][2]. Tensor Versioning and Autograd Every tensor maintains a version counter, accessible via ._version, which is tracked by the autograd engine [1][3]. When an in-place operation is performed on a tensor, its version counter is incremented [1][3]. This mechanism allows the autograd engine to detect stale data [1]. If a SavedVariable (used during the backward pass) holds a reference to a tensor, the engine compares the saved version with the current version of the tensor; a mismatch indicates that an illegal in-place modification occurred, triggering an error [1][3]. If you perform custom in-place operations that bypass standard PyTorch functions (such as modifying memory directly via a custom kernel using the tensor's data_ptr), PyTorch cannot automatically detect the change [4][5]. To maintain autograd integrity in these cases, you must manually increment the version counter using torch.autograd.graph.increment_version(tensor) [4][5]. Note that torch.compile may sometimes produce different version counter behaviors compared to eager mode, as the compiler may fuse operations in ways that do not map 1:1 to eager-mode counter increments [6]. Caching Allocator and Memory Reuse The PyTorch Caching Allocator (e.g., CUDACachingAllocator) manages memory independently of the autograd version counter [7][8]. It caches and reuses memory blocks to avoid the overhead of frequent cudaMalloc/cudaFree calls [9][8]. When a tensor is deleted or its storage is freed, the memory address (the data_ptr) is returned to the allocator's pool and may be assigned to a new tensor request [7][9]. It is entirely normal for a newly allocated tensor to reuse the same memory address (data_ptr) as a previously freed tensor [9][8]. The autograd system does not rely on the memory address to determine if a tensor has been modified; it relies strictly on the version counter and associated metadata [1][2]. Therefore, the reuse of a data_ptr does not cause autograd to incorrectly identify a new tensor as being a modified version of an old one [1]. Autograd effectively tracks the identity of the tensor object and its specific version state, ensuring that memory reuse by the allocator remains transparent to the functional correctness of the autograd graph [1][3].

Citations:


_weight_signature can skip required weight refreshes.
(data_ptr, _version) is not a stable cache key across tensor replacement: a fresh weight tensor can come back at the same address with version 0, match the previous signature, and leave _sync_weights using stale kernel weights. Use tensor object identity/weakrefs or another stronger invalidation signal here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe.py` around lines 209 - 211, Update
_weight_signature to use a tensor-object identity or weak-reference-based
invalidation signal rather than only data_ptr() and _version, including the
underlying data tensor for BlockScaledTensor. Ensure replacing a weight with a
fresh tensor cannot match the prior signature, so _sync_weights refreshes kernel
weights.

Comment on lines +336 to +345
def _to_kernel_weights(self, fc1_weight: MoeTensor, fc2_weight: MoeTensor):
"""(E,H,2I) gate-first / (E,I,H) -> w13 (E,2I,H) up-first / w2 (E,H,I)."""

I = self._op.intermediate_size
fc1 = _dequant_to_bf16(fc1_weight)
gate = fc1[..., :I].transpose(1, 2)
up = fc1[..., I:].transpose(1, 2)
w13 = torch.cat([up, gate], dim=1).contiguous()
w2 = _dequant_to_bf16(fc2_weight).transpose(1, 2).contiguous()
return w13, w2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Rename the ambiguous I locals.

Ruff flags I as an ambiguous variable name (E741) here and at lines 397 and 477. inter / intermediate reads better and clears the lint.

🧰 Tools
🪛 Ruff (0.16.0)

[error] 339-339: Ambiguous variable name: I

(E741)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe.py` around lines 336 - 345, Rename the ambiguous
I local in _to_kernel_weights and the corresponding locals at the referenced
locations to inter or intermediate, updating every use consistently while
preserving the existing tensor slicing and shape transformations.

Source: Linters/SAST tools

Comment on lines +400 to +412
if world > 1:
t_all = torch.full((world,), T, dtype=torch.int64, device=device)
dist.all_gather_into_tensor(
t_all,
torch.tensor([T], dtype=torch.int64, device=device),
group=op.ep_group,
)
if not bool((t_all == T).all().item()):
raise RuntimeError("megamoe backend requires the same token count on every " f"EP rank, got {t_all.tolist()}")
ids_all = torch.empty((world, T, K), dtype=topk_idx.dtype, device=device)
dist.all_gather_into_tensor(ids_all, topk_idx.contiguous(), group=op.ep_group)
else:
ids_all = topk_idx.view(1, T, K)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Verify the collective ordering constraint is safe.

_extract_stash runs two collectives, but only when op.generate_c is set (via forward). Any rank that takes the non-generate_c path, or that fell back to the allocate-only backend because maybe_create returned None, will not participate — an asymmetric fallback deadlocks the group instead of raising. Since maybe_create swallows all failures per-rank under the default auto policy, that asymmetry is reachable. Consider making the backend decision itself collective (all-reduce the availability flag) when ep_group is not None.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe.py` around lines 400 - 412, The backend
selection and _extract_stash collective path must use a consistent decision
across all EP ranks. When ep_group is set, make maybe_create’s availability
result collective by all-reducing the per-rank success flag, and ensure every
rank either selects the same backend or falls back together before executing the
two collectives in _extract_stash. Preserve the existing behavior for
non-distributed execution.

Comment on lines +139 to +180
class MoeEp:
"""Fused SwiGLU MoE operator with contiguous expert parallel sharding.

Global expert ``e`` belongs to group-relative EP rank
``e // experts_per_rank``. The constructor captures static configuration;
calling the instance accepts runtime tensors for this rank.

With ``generate_c=True`` (training integration), ``__call__`` additionally
returns ``fc1_c`` and ``route_metadata``. ``fc1_c`` is the raw pre-SwiGLU
FC1 accumulator for every route this rank's experts processed, BF16, shape
``(local_routes, 2 * intermediate)``. Rows are grouped by local expert
(ascending) and ordered within each expert by source rank, then the source
rank's token-major route order. The rows are captured before the gate/up
clamp and carry no router weight. ``route_metadata`` is Int32
``(local_routes, 4)`` with columns
``(local_expert, src_rank, src_token, src_slot)``, row-aligned with
``fc1_c``, identifying each route for the backward gradient re-dispatch.

Backend execution: when the MegaMoE device backend is available and the
configuration is supported (see ``moe_ep._megamoe``), ``__call__`` and
``backward`` launch the fused SM100 kernels and return real results.
Otherwise they fall back to returning newly allocated, uninitialized
output storage with the correct public representation (the original
API-stub behavior). ``CUDNN_MOE_EP_BACKEND=megamoe|auto|none`` selects
the policy; ``CUDNN_MEGAMOE_ROOT`` locates the megamoe package.
"""

def __init__(
self,
*,
num_experts: int,
hidden_size: int,
intermediate_size: int,
top_k: int,
ep_group: Optional[dist.ProcessGroup] = None,
max_tokens_per_rank: Optional[int] = None,
output_format: Union[MoeFormat, str] = MoeFormat.BF16,
combine_format: Union[MoeFormat, str] = MoeFormat.BF16,
apply_topk_in_fc1: bool = True,
gate_up_clamp: Optional[float] = None,
generate_c: bool = False,
) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

MoE EP package does not follow the frontend-only OSS API contract. The root cause is a single missing piece of scaffolding: no APIBase subclass and no <operation>_wrapper(), which then leaves the family __init__.py with nothing to export beyond the class.

  • python/cudnn/moe_ep/api.py#L139-L180: make MoeEp subclass APIBase with check_support() -> bool (setting self._is_supported), compile(), and execute(..., current_stream=None), and add a moe_ep_wrapper() that allocates outputs and returns a TupleDict with a documented key order — or document the proposal-stage deviation in docs/fe-oss-apis/moe_ep.md.
  • python/cudnn/moe_ep/__init__.py#L4-L6: once the wrapper exists, export it alongside MoeEp in __all__.

As per coding guidelines: "Every frontend-only OSS kernel API must subclass APIBase, implement check_support() -> bool, compile(), and execute(..., current_stream=None)" and "Kernel family __init__.py files must export the API class and wrapper through __all__."

📍 Affects 2 files
  • python/cudnn/moe_ep/api.py#L139-L180 (this comment)
  • python/cudnn/moe_ep/__init__.py#L4-L6
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/api.py` around lines 139 - 180, The MoE EP API lacks the
required frontend-only scaffolding. In python/cudnn/moe_ep/api.py lines 139-180,
make MoeEp subclass APIBase and implement check_support() returning bool while
setting self._is_supported, compile(), execute(..., current_stream=None), and
moe_ep_wrapper() that allocates outputs and returns a TupleDict with documented
key order; alternatively document the proposal-stage deviation in
docs/fe-oss-apis/moe_ep.md. In python/cudnn/moe_ep/__init__.py lines 4-6, export
moe_ep_wrapper alongside MoeEp through __all__.

Source: Coding guidelines

Comment on lines +331 to +337
if not self._backend_resolved:
from . import _megamoe

self._backend = _megamoe.maybe_create(self, device, token_count)
self._backend_resolved = True
if self._backend is not None:
return self._backend.forward(activation, fc1_weight, fc2_weight, topk_idx, topk_weights)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -t f '_megamoe.py' python/cudnn | xargs -r ast-grep outline --items all
rg -nP -C8 'def maybe_create' python/cudnn

Repository: NVIDIA/cudnn-frontend

Length of output: 2533


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## _megamoe.py around maybe_create and backend methods\n'
sed -n '170,360p' python/cudnn/moe_ep/_megamoe.py

printf '\n## moe_ep/api.py around backend resolution\n'
sed -n '250,360p' python/cudnn/moe_ep/api.py

Repository: NVIDIA/cudnn-frontend

Length of output: 12743


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('python/cudnn/moe_ep/_megamoe.py')
lines = p.read_text().splitlines()
for i in range(178, 361):
    if i <= len(lines):
        if i in range(179, 204) or i in range(274, 361):
            print(f"{i:4d}: {lines[i-1]}")
PY

Repository: NVIDIA/cudnn-frontend

Length of output: 4980


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C6 'self\._device|def forward|_max_tokens|token_count' python/cudnn/moe_ep/_megamoe.py

Repository: NVIDIA/cudnn-frontend

Length of output: 3616


Backend size is frozen on the first call. maybe_create() builds _MegamoeBackend with that call’s token_count when max_tokens_per_rank is unset, and forward() fails once a later batch exceeds _max_tokens. Cache per (device, token_count) or size the backend from an explicit upper bound.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/api.py` around lines 331 - 337, Update the backend
initialization flow in the enclosing forward method so `_MegamoeBackend` is not
permanently sized from the first call’s token_count when max_tokens_per_rank is
unset. Cache or recreate backends keyed by both device and token_count, or
otherwise initialize them using an explicit upper bound, while preserving reuse
for compatible calls and ensuring later larger batches do not exceed
_max_tokens.

Comment on lines +386 to +398

device = _tensor_device(activation)
if self._backend is not None:
return self._backend.backward(
grad_output,
activation,
fc1_weight,
fc2_weight,
topk_idx,
topk_weights,
fc1_c,
route_metadata,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

backward never resolves the backend, so it silently returns garbage if called before any forward.

_backend is only populated inside __call__ (line 331-335). A caller that constructs MoeEp(generate_c=True), runs forward through a different instance (or restores a stash across instances), and then calls backward gets uninitialized torch.empty gradients even though the MegaMoE backend is available — a silent wrong-results path rather than an error. Resolve the backend here too, or raise when it is unresolved.

🐛 Resolve the backend in `backward` as well
         device = _tensor_device(activation)
+        if not self._backend_resolved:
+            from . import _megamoe
+
+            self._backend = _megamoe.maybe_create(self, device, token_count)
+            self._backend_resolved = True
         if self._backend is not None:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
device = _tensor_device(activation)
if self._backend is not None:
return self._backend.backward(
grad_output,
activation,
fc1_weight,
fc2_weight,
topk_idx,
topk_weights,
fc1_c,
route_metadata,
)
device = _tensor_device(activation)
if not self._backend_resolved:
from . import _megamoe
self._backend = _megamoe.maybe_create(self, device, token_count)
self._backend_resolved = True
if self._backend is not None:
return self._backend.backward(
grad_output,
activation,
fc1_weight,
fc2_weight,
topk_idx,
topk_weights,
fc1_c,
route_metadata,
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/api.py` around lines 386 - 398, Update MoeEp.backward to
resolve or validate _backend before dispatching to _backend.backward, rather
than treating an unresolved backend as a valid path. Reuse the same
backend-resolution logic as __call__, ensuring backward either invokes the
available MegaMoE backend or raises clearly when it cannot be resolved, never
returning uninitialized gradients.

Comment on lines +115 to +121
fc1_err = rel_err(fc1_c, ref_fc1_c) if fc1_c.shape == ref_fc1_c.shape else float("inf")
print(
f"[rank {rank}] fwd rel_err={fwd_err:.3e} "
f"fc1_c shape={tuple(fc1_c.shape)} vs ref {tuple(ref_fc1_c.shape)} "
f"rel_err={fc1_err:.3e} metadata equal={meta_ok}",
flush=True,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

fc1_c parity never affects the exit status.

fc1_err is computed and printed but excluded from failures at line 150, and a shape mismatch only yields inf in the log. Since fc1_c is the stash the whole backward path depends on (including the de-interleave order in _megamoe._extract_stash), a wrong stash would still print PASS.

💚 Proposed fix
-    failures = [k for k, v in {**errs, "forward": fwd_err}.items() if v > tol]
+    failures = [k for k, v in {**errs, "forward": fwd_err, "fc1_c": fc1_err}.items() if v > tol]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/fe_api/moe_ep/megamoe_backend_parity.py` around lines 115 - 121,
Update the parity failure aggregation near the existing fc1_c diagnostics so
fc1_c shape mismatches and relative-error violations contribute to failures
alongside the other forward and metadata checks. Reuse the established tolerance
and failure-reporting pattern, ensuring an incorrect fc1_c stash causes the test
to report failure rather than only printing inf.

Comment on lines +498 to +520
@pytest.mark.parametrize("input_format", [MoeFormat.MXFP8, MoeFormat.NVFP4])
def test_moe_ep_api_accepts_block_scaled_inputs(input_format):
"""The API takes data+scale bundles where the kernel takes separate sf args."""

from cudnn import MoeEp

torch.manual_seed(29)
device = torch.device("cuda")
experts, tokens, hidden, intermediate = 2, 4, 32, 16
activation, fc1_weight, fc2_weight = _block_scaled_inputs(input_format, experts, tokens, hidden, intermediate, device)

api = MoeEp(num_experts=experts, hidden_size=hidden, intermediate_size=intermediate, top_k=1)
output = api(
activation,
fc1_weight,
fc2_weight,
torch.tensor([[0], [1], [0], [1]], dtype=torch.int64, device=device),
torch.ones(tokens, 1, device=device),
)

assert isinstance(output, torch.Tensor)
assert output.shape == (tokens, hidden)
assert output.dtype == torch.bfloat16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Missing CUDA gating on the GPU tests.

These tests hard-code torch.device("cuda") with no availability skip, so they error on CPU-only runners. Worse, the xfail(strict=True) variants (lines 405-409, 556-559, 635-638, 676-679) would swallow a "no CUDA" RuntimeError as the expected failure and report green without exercising anything. Add a module- or test-level skip.

As per coding guidelines, "Gate tests on supported capabilities and skip unsupported architecture, dtype, or backend-version combinations using support checks, cudnn.backend_version(), and torch.cuda.get_device_capability()."

💚 Suggested gate
+requires_cuda = pytest.mark.skipif(not torch.cuda.is_available(), reason="requires a CUDA device")
+
+
 `@pytest.mark.parametrize`("input_format", [MoeFormat.MXFP8, MoeFormat.NVFP4])
+@requires_cuda
 def test_moe_ep_api_accepts_block_scaled_inputs(input_format):
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pytest.mark.parametrize("input_format", [MoeFormat.MXFP8, MoeFormat.NVFP4])
def test_moe_ep_api_accepts_block_scaled_inputs(input_format):
"""The API takes data+scale bundles where the kernel takes separate sf args."""
from cudnn import MoeEp
torch.manual_seed(29)
device = torch.device("cuda")
experts, tokens, hidden, intermediate = 2, 4, 32, 16
activation, fc1_weight, fc2_weight = _block_scaled_inputs(input_format, experts, tokens, hidden, intermediate, device)
api = MoeEp(num_experts=experts, hidden_size=hidden, intermediate_size=intermediate, top_k=1)
output = api(
activation,
fc1_weight,
fc2_weight,
torch.tensor([[0], [1], [0], [1]], dtype=torch.int64, device=device),
torch.ones(tokens, 1, device=device),
)
assert isinstance(output, torch.Tensor)
assert output.shape == (tokens, hidden)
assert output.dtype == torch.bfloat16
requires_cuda = pytest.mark.skipif(not torch.cuda.is_available(), reason="requires a CUDA device")
`@pytest.mark.parametrize`("input_format", [MoeFormat.MXFP8, MoeFormat.NVFP4])
`@requires_cuda`
def test_moe_ep_api_accepts_block_scaled_inputs(input_format):
"""The API takes data+scale bundles where the kernel takes separate sf args."""
from cudnn import MoeEp
torch.manual_seed(29)
device = torch.device("cuda")
experts, tokens, hidden, intermediate = 2, 4, 32, 16
activation, fc1_weight, fc2_weight = _block_scaled_inputs(input_format, experts, tokens, hidden, intermediate, device)
api = MoeEp(num_experts=experts, hidden_size=hidden, intermediate_size=intermediate, top_k=1)
output = api(
activation,
fc1_weight,
fc2_weight,
torch.tensor([[0], [1], [0], [1]], dtype=torch.int64, device=device),
torch.ones(tokens, 1, device=device),
)
assert isinstance(output, torch.Tensor)
assert output.shape == (tokens, hidden)
assert output.dtype == torch.bfloat16
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/fe_api/moe_ep/test_moe_ep.py` around lines 498 - 520, Add a
module- or test-level CUDA availability gate for the GPU-dependent Moe EP tests,
including the strict xfail variants, using the project’s supported capability
checks so CPU-only runners skip them instead of passing spuriously. Apply the
gate around the relevant tests in test_moe_ep.py while preserving their existing
assertions and xfail behavior on supported CUDA environments.

Source: Coding guidelines

mhoqueanik and others added 5 commits August 5, 2026 14:17
Copy the megamoe training package, its pt reference package, and the four
cutedsl_megamoe kernel subpackages (common, moe_mxfp8_glu, moe_nvfp4_swapab,
src) verbatim into python/cudnn/moe_ep/_megamoe_backend, preserving the
sibling layout megamoe/repo_path.py expects.  The bundle carries both
megakernels: the CuTe DSL NVFP4 forward (forward_nvfp4.py + moe_nvfp4_swapab)
and the FP8 mega backward (bwd_kernel).  Provenance (source repos and commit
hashes) is recorded in the bundle README.

_megamoe.py now defaults to the bundled tree via bundled_root();
CUDNN_MEGAMOE_ROOT becomes an optional override for kernel development.
The parity driver picks up the same default in multi-rank mode, the vendored
path is excluded from black so it stays verbatim, and run_pr_tests.sh runs
the full validation (pytest suite, vendored-import check, single-rank kernel
parity) in the container with no external checkout.

Validated on GB200 with CUDNN_MEGAMOE_ROOT unset: 18 passed / 7 xfailed,
parity fwd rel_err 6.5e-2, bwd rel_err <= 6.6e-2, metadata equal, PASS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CUDNN_MEGAMOE_ROOT is gone: _megamoe.py resolves the megamoe package only
from the in-tree _megamoe_backend bundle (bundled_root() now raises a clear
BackendUnavailable if the bundle is missing), and the parity driver,
run_pr_tests.sh, and all docstrings/README lose the override mentions.
CUDNN_MOE_EP_BACKEND remains the only backend env knob.

Also state in _megamoe.py, the bundle README, and the moe_ep design doc
that the backward (bprop) implementation in megamoe/bwd_kernel comes from
the Flashinfer team, not the FastKernel team.

Revalidated on GB200: 18 passed / 7 xfailed, parity fwd rel_err 6.5e-2,
bwd rel_err <= 6.6e-2, metadata equal, PASS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers the one-shot run_pr_tests.sh wrapper and the step-by-step
equivalents (pytest suite with the wheel graft, single-rank and 4-rank
kernel-vs-oracle parity), the expected baselines and how to read them
(the 7 strict xfails, MXFP8-vs-FP32 tolerance band, hard invariants),
and the operational gotchas (backend policy knob, supported envelope,
one-backward-per-forward, frozen process mode, compile cost).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New RUNBOOK §2a: build the environment from nvcr.io/nvidia/pytorch:26.05-py3
by installing the bundle's own requirements file, with the load-bearing pins
called out (cutlass-dsl 4.5.2, nvshmem4py-cu13, torch>=2.10), a smoke-check
snippet, and the sqsh-baking variant for SLURM/pyxis.

Vendor cutedsl_megamoe/ci/requirements.txt (verbatim) into the bundle so
the environment recipe ships in-tree like the kernels do.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop the internal container-image path and cluster account/partition from
the known-good-container note and the one-shot srun example; validation now
shows a plain in-environment invocation plus a generic SLURM/pyxis job
using the image baked per section 2a.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

🟠 Major comments (24)
test/python/fe_api/moe_ep/run_pr_tests.sh-8-14 (1)

8-14: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validation mode is not enforced across the wrapper and runbook.

The pytest phase can inherit auto, even though the runbook documents an uninitialized-memory fallback. This can produce a false validation result.

  • test/python/fe_api/moe_ep/run_pr_tests.sh#L8-L14: set CUDNN_MOE_EP_BACKEND=megamoe on the pytest command.
  • test/python/fe_api/moe_ep/RUNBOOK.md#L85-L86: make the one-shot command enforce strict backend mode or state that the wrapper does so.
  • test/python/fe_api/moe_ep/RUNBOOK.md#L91-L95: apply the same requirement to the SLURM command.
  • test/python/fe_api/moe_ep/RUNBOOK.md#L113-L114: set strict backend mode on the direct pytest command.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/fe_api/moe_ep/run_pr_tests.sh` around lines 8 - 14, Enforce
strict Megablocks validation mode consistently: update run_pr_tests.sh lines
8-14 to set CUDNN_MOE_EP_BACKEND=megamoe on the pytest command; update
RUNBOOK.md lines 85-86, 91-95, and 113-114 so the one-shot, SLURM, and direct
pytest commands respectively set or explicitly rely on that strict backend
setting.
test/python/fe_api/moe_ep/run_pr_tests.sh-5-6 (1)

5-6: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Make setup fail closed. Add set -euo pipefail, assign CLONE before exporting it, and quote $CLONE and $SITE in cp, grep, the append redirection, and both cd commands. export CLONE=$(...) can return success with an empty value when the inner cd fails. Without fail-fast handling, setup failures can leave the wrapper in the wrong directory and produce misleading results.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/fe_api/moe_ep/run_pr_tests.sh` around lines 5 - 6, Update the
setup script near the existing `set -x` and `CLONE` initialization to enable
`set -euo pipefail`, assign `CLONE` before exporting it, and quote `$CLONE` and
`$SITE` in the specified `cp`, `grep`, append redirection, and both `cd`
commands. Preserve the existing setup flow while ensuring directory and command
failures stop execution.

Source: Linters/SAST tools

python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/inputs_process.py-543-548 (1)

543-548: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Replace the generic E8M0 cast at inputs_process.py:548.

stg_e8m0_from_f32 states that .to(Float8E8M0FNU) does not lower correctly. The preceding PTX round trip does not fix the subsequent generic cast. Use a PTX-backed E8M0 conversion and add compiled-kernel coverage for this path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/inputs_process.py`
around lines 543 - 548, Replace the generic Float32.to(cutlass.Float8E8M0FNU)
conversion assigned to sf_e8m0 with the PTX-backed stg_e8m0_from_f32 conversion
used by the backend. Add compiled-kernel coverage for this E8M0 conversion path,
verifying the generated kernel lowers and executes correctly.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/kernel_fc12.py-529-534 (1)

529-534: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

_compute_stages can return a non-positive num_ab_stage.

num_ab_stage is an unchecked floor division. When misc_budget + c_bytes_total approaches smem_capacity // occupancy, the result becomes 0 or negative. The value then flows into PipelineTmaUmma.create(num_stages=...) and into the sA/sB SMEM layouts, so the failure surfaces far from its cause. The MegaMoE subclass adds a dispatch SMEM region to misc_budget, which makes this reachable for large hidden.

Raise a clear error here instead.

🛡️ Proposed guard
         num_ab_stage = (
             smem_capacity // occupancy - fixed_overhead
         ) // ab_bytes_per_stage
+        if num_ab_stage < 1:
+            raise ValueError(
+                f"SMEM budget leaves no AB stage: capacity/occupancy="
+                f"{smem_capacity // occupancy}, fixed_overhead={fixed_overhead} "
+                f"(misc={misc_budget}, c={c_bytes_total}), "
+                f"ab_bytes_per_stage={ab_bytes_per_stage}."
+            )
         return num_acc_stage, num_ab_stage, num_sched_stages
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/kernel_fc12.py`
around lines 529 - 534, Update _compute_stages to validate the calculated
num_ab_stage before returning it. If the value is zero or negative, raise a
clear error describing that available shared memory cannot support a positive
number of AB stages, instead of allowing it to reach PipelineTmaUmma.create or
the sA/sB layouts.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/moe_persistent_scheduler.py-1207-1212 (1)

1207-1212: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Derive the params slice length from its serialized values.

MoEStaticSchedulerParams.__extract_mlir_values__ serializes only Int32 fields. With static_expert_shape, all three fields are Python integers, so the serialized length is zero. The hardcoded three-value slice then causes __new_from_mlir_values__ to fail its length assertion and shifts every subsequent field. Use len(extract_mlir_values(self.params)), as in the dynamic scheduler.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/moe_persistent_scheduler.py`
around lines 1207 - 1212, Update the parameter deserialization logic around
new_from_mlir_values to derive the params slice length from
len(extract_mlir_values(self.params)) instead of assuming three values.
Increment idx by that computed length so static_expert_shape produces an empty
params slice and subsequent fields remain correctly aligned.
python/cudnn/moe_ep/_megamoe_backend/megamoe/tests/test_turboquant_numerics.py-178-189 (1)

178-189: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Run the distributed teardown even when an assertion fails.

The asserts at lines 179-183 raise out of main. That skips finalize_dist_and_nvshmem() at line 188. Under torchrun --nproc_per_node=4, one failing rank exits without finalizing NVSHMEM or the process group, so the remaining ranks block at their next collective and the job hangs instead of failing fast. megamoe/tests/test_bwd_v0_parity.py avoids this by collecting failures into a list and always reaching teardown.

Put the measurement body in try / finally, or collect failures and assert after teardown.

🛡️ Proposed teardown fix
-    if rank == 0:
-        print("TURBOQUANT NUMERICS PASS")
-    if not _NO_DIST:
-        finalize_dist_and_nvshmem()
-    return 0
+    if rank == 0:
+        print("TURBOQUANT NUMERICS PASS")
+    return 0

Then wrap the call site so teardown always runs:

if __name__ == "__main__":
    try:
        rc = main()
    finally:
        if not _NO_DIST:
            from src.bootstrap import finalize_dist_and_nvshmem

            finalize_dist_and_nvshmem()
    sys.exit(rc)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/megamoe/tests/test_turboquant_numerics.py`
around lines 178 - 189, Ensure distributed teardown always executes when
assertions in main fail: move finalize_dist_and_nvshmem out of the
normal-success path and wrap the measurement/assertion flow in try/finally, or
collect failures and defer assertion until after teardown. Update main and its
__main__ call site so _NO_DIST remains respected and failures still propagate
after finalize_dist_and_nvshmem runs.
python/cudnn/moe_ep/_megamoe_backend/megamoe/turboquant.py-85-88 (1)

85-88: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

load_weights forwards an fp32 w13 and a bf16 w2.

rotate_hidden calls .float() at line 58 and never casts back, so line 88 passes an fp32 w13 to super().load_weights() while w2 keeps its original dtype. The sibling test in megamoe/tests/test_turboquant_numerics.py performs the same fold and casts back explicitly with rotate_hidden(w13g.float(), q).bfloat16(). Restore the input dtype so the mixin matches the plain forward path and the test.

Also note the fp32 w13 doubles the transient host/device memory for the weight fold on large expert counts.

🔧 Proposed fix
     def load_weights(self, w13: torch.Tensor, w2: torch.Tensor) -> None:
         # Fold Q into fc1's K(hidden) axis; w2 is untouched (its K is the
         # intermediate dim, whose quant lives inside the kernel).
-        super().load_weights(rotate_hidden(w13, self.q_fp32), w2)
+        w13_rot = rotate_hidden(w13, self.q_fp32).to(w13.dtype)
+        super().load_weights(w13_rot, w2)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe_backend/megamoe/turboquant.py` around lines 85 -
88, Update MegamoeBackend.load_weights to preserve w13’s original dtype after
applying rotate_hidden: perform the rotation using the existing q_fp32 value,
then cast the folded result back to w13.dtype before passing it to
super().load_weights. Leave w2 unchanged and preserve the existing
weight-folding behavior.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/runner_fc12_common.py-113-116 (1)

113-116: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

ProblemDesc validates MXFP8 shapes with NVFP4 constants.

This descriptor is kind-parameterized, and Lines 117-128 correctly select the interleave and SF vector size from self.kind. Two nearby checks do not:

  • Line 113 requires hidden % Nvfp4BlockSize == 0 for every kind. Nvfp4BlockSize is 16, but MXFP8 uses a 32-element block (Mxfp8BlockSize, already imported at Line 52 and used at Line 123). An mxfp8_e4m3 problem with hidden = 16 or hidden = 48 passes this guard while violating the MXFP8 block contract.
  • Lines 198-222 pass Nvfp4DataDtype to check_tma_leading_dim_align for activation, fc1_weight, fc2_weight, and fc1_output regardless of kind. MXFP8 stores one byte per element, not two, so the derived byte count is wrong for MXFP8. The direction is conservative, so no unaligned MXFP8 shape is accepted; the visible effect is a misleading rejection and an NVFP4-specific "2 fp4 packed per byte" error for an MXFP8 problem with an odd hidden.

Derive both from self.kind, the same way Line 123 already does.

🐛 Proposed fix
-        if self.hidden <= 0 or self.hidden % Nvfp4BlockSize != 0:
+        _block = Nvfp4BlockSize if self.kind == "nvfp4" else Mxfp8BlockSize
+        if self.hidden <= 0 or self.hidden % _block != 0:
             raise ValueError(
-                f"hidden ({self.hidden}) must be a positive multiple of {Nvfp4BlockSize}."
+                f"hidden ({self.hidden}) must be a positive multiple of "
+                f"{_block} (kind={self.kind!r})."
             )
         from moe_nvfp4_swapab.runner_common import (
             check_tma_leading_dim_align as _check_tma_leading_dim_align,
         )
+        _tma_data_dtype = kind_data_dtype(self.kind)
         _check_tma_leading_dim_align(
             "activation",
             {"k_major": self.hidden}[self.fc1_activation_layout],
-            Nvfp4DataDtype,
+            _tma_data_dtype,
         )
         _check_tma_leading_dim_align(
             "fc1_weight",
             {"k_major": self.hidden}[self.fc1_weight_layout],
-            Nvfp4DataDtype,
+            _tma_data_dtype,
         )
         _check_tma_leading_dim_align(
             "fc2_weight",
             {"k_major": self.intermediate // 2, "n_major": self.hidden}[
                 self.fc2_weight_layout
             ],
-            Nvfp4DataDtype,
+            _tma_data_dtype,
         )
         _check_tma_leading_dim_align(
             "fc1_output (kernel-internal, fixed k_major)",
             self.intermediate // 2,
-            Nvfp4DataDtype,
+            _tma_data_dtype,
         )

Also applies to: 198-227

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/runner_fc12_common.py`
around lines 113 - 116, Update ProblemDesc validation to derive the
hidden-dimension block size from self.kind, using Mxfp8BlockSize for MXFP8 and
Nvfp4BlockSize otherwise. In the check_tma_leading_dim_align calls for
activation, fc1_weight, fc2_weight, and fc1_output, likewise select the
kind-appropriate data dtype so MXFP8 uses its one-byte-per-element
representation while NVFP4 retains Nvfp4DataDtype.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/megamoe_kernel_mxfp8.py-234-274 (1)

234-274: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

dedup_dispatch and combine_pre_reduce are narrowed on self but forwarded raw to TokenInPullTokenBackPush.

Line 234 narrows the flag: self.dedup_dispatch = dedup_dispatch and num_topk > 1. Line 267 narrows the second one against it. The workspace layout and __call__ then follow the narrowed values:

  • _build_shared_region_specs allocates dup_link only when self.dedup_dispatch (Line 665).
  • _build_local_region_specs allocates reduce_list only when self.combine_pre_reduce (Line 600).
  • __call__ sets dup_link = None and reduce_list = None on the same narrowed flags (Lines 1003-1010).

Lines 350 and 352 instead forward the raw parameters. With dedup_dispatch=True and num_topk == 1, the token-comm helper is configured with dedup enabled while no dup_link region exists and token_comm_args.dup_link is None, so the dedup fan-out dereferences a null tensor inside the kernel. combine_pre_reduce has the same shape against reduce_list.

The validation at Lines 270-274 also reads the raw dedup_dispatch, so a caller that requests combine_pre_reduce=True with num_topk == 1 passes validation and gets the feature silently disabled. Line 306 already uses the narrowed self.combine_pre_reduce, which shows the intent.

Forward the narrowed flags and validate against them.

🐛 Proposed fix
         self.combine_pre_reduce = (
             combine_pre_reduce and self.dedup_dispatch
         )
-        if combine_pre_reduce and not (dedup_dispatch and combine_in_flight_reduce):
+        if combine_pre_reduce and not (
+            self.dedup_dispatch and combine_in_flight_reduce
+        ):
             raise ValueError(
                 "combine_pre_reduce requires dedup_dispatch=True and "
-                "combine_in_flight_reduce=True."
+                "combine_in_flight_reduce=True (dedup_dispatch also requires "
+                "num_topk > 1)."
             )
             token_back_schedule_mode=self.token_back_schedule_mode,
-            dedup_dispatch=dedup_dispatch,
+            dedup_dispatch=self.dedup_dispatch,
             token_back_reduce_topk=combine_in_flight_reduce,
-            combine_pre_reduce=combine_pre_reduce,
+            combine_pre_reduce=self.combine_pre_reduce,
         )

Also applies to: 344-353

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/megamoe_kernel_mxfp8.py`
around lines 234 - 274, Use the normalized instance flags consistently in the
token-communication setup: update the `TokenInPullTokenBackPush` construction to
receive `self.dedup_dispatch` and `self.combine_pre_reduce` rather than the raw
parameters. Change the `combine_pre_reduce` validation to check the narrowed
`self.combine_pre_reduce`, preserving the existing `num_topk > 1` gating and
ensuring helper configuration matches allocated workspace regions.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/runner_fc12.py-220-232 (1)

220-232: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

fc2_output_dtype=cutlass.BFloat16 is hardcoded while the buffer follows problem.fc2_output_dtype.

--fc2_output_dtype fp16 is a supported CLI value. parse_output_dtype accepts it, and ProblemDesc.__post_init__ in runner_fc12_common.py permits torch.float16. _alloc_fc2_output at Line 190 then allocates the buffer with problem.fc2_output_dtype, but Line 226 always instantiates the kernel with cutlass.BFloat16.

With --fc2_output_dtype fp16 the kernel writes bf16 bit patterns into a buffer the host reads as fp16, so validate() compares reinterpreted bytes and reports meaningless diffs. Either derive the kernel dtype from problem.fc2_output_dtype, or reject fp16 for this kind so the failure is explicit.

🐛 Option A: derive the kernel dtype from the problem descriptor
     def _instantiate_kernel(self, common_kwargs: dict):
         import cutlass
         from moe_nvfp4_swapab.kernel_fc12 import Sm100SwapABSwigluFp4Fc12Kernel
 
+        _cutlass_out_dtype = {
+            torch.bfloat16: cutlass.BFloat16,
+            torch.float16: cutlass.Float16,
+        }[self.problem.fc2_output_dtype]
         return Sm100SwapABSwigluFp4Fc12Kernel(
             **common_kwargs,
-            fc2_output_dtype=cutlass.BFloat16,
+            fc2_output_dtype=_cutlass_out_dtype,
🛡️ Option B: reject the unsupported dtype up front
     def _instantiate_kernel(self, common_kwargs: dict):
         import cutlass
         from moe_nvfp4_swapab.kernel_fc12 import Sm100SwapABSwigluFp4Fc12Kernel
 
+        if self.problem.fc2_output_dtype is not torch.bfloat16:
+            raise ValueError(
+                "the NVFP4 fused fc12 kernel emits bf16 only; got "
+                f"fc2_output_dtype={self.problem.fc2_output_dtype}"
+            )
         return Sm100SwapABSwigluFp4Fc12Kernel(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/runner_fc12.py`
around lines 220 - 232, Update _instantiate_kernel so fc2_output_dtype matches
self.problem.fc2_output_dtype, including the supported torch.float16/fp16
configuration, using the appropriate CUTLASS dtype mapping; keep the kernel
output dtype consistent with the buffer allocated by _alloc_fc2_output.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/common/moe_utils.py-178-181 (1)

178-181: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Raise on an unsupported fp8 type instead of returning None.

The else branch prints a device-side message and executes a bare return, so the function returns None. The caller at line 269 then evaluates cute.arch.rcp_approx(qpvscale_up) on None. That produces the opaque trace error "None to integer conversion is not supported", which moe_mxfp8_glu/run_functional_tests.sh lines 118-123 record as the reason the e5m2 test cases were removed from the functional suite.

fp8_type is a compile-time value here, so an unsupported type is a host-side configuration error. Raise it at trace time with the offending type in the message. The diagnostic then points at the real cause instead of at an unrelated arithmetic site.

cvt_f32x4_to_f8x4_pack_i32 has the same printf and bare return pattern at lines 228-231. Apply the same change there.

🐛 Proposed fix for lines 178-181
     else:
-        with cute.arch.elect_one():
-            cute.printf("error: unsupported fp8 element type")
-        return
+        raise TypeError(f"cvt_f32_to_f8_to_f32: unsupported fp8 element type {fp8_type}")

Apply the same change in cvt_f32x4_to_f8x4_pack_i32:

     else:
-        with cute.arch.elect_one():
-            cute.printf("error: unsupported fp8 element type")
-        return
+        raise TypeError(
+            f"cvt_f32x4_to_f8x4_pack_i32: unsupported fp8 element type {fp8_type}"
+        )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/common/moe_utils.py`
around lines 178 - 181, Replace the unsupported-type else branches in the fp8
scale helper and cvt_f32x4_to_f8x4_pack_i32 with host-side exceptions that
include the offending fp8_type; remove the device-side printf and bare returns
so invalid compile-time configurations fail during tracing.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/flag_batch.py-75-81 (1)

75-81: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Replace Python or with a traced logical OR. Dynamic Boolean.__bool__ raises PHASE_DYNAMIC_TO_STATIC_BOOL, so line 75 cannot trace. Combine the predicates with |. Keep cutlass.const_expr(not no_fire) because callers pass no_fire as cutlass.Constexpr.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/flag_batch.py`
around lines 75 - 81, Update the condition in the batch-processing flow around
`_make(...).fire()` to combine `cumulated == flush_threshold` and `next_phase !=
self.phase` with the traced logical-OR operator `|` instead of Python `or`.
Preserve `cutlass.const_expr(not no_fire)` for the `no_fire` check, since it is
passed as a `cutlass.Constexpr`.
python/cudnn/moe_ep/_megamoe_backend/pt/config.py-40-55 (1)

40-55: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate EpConfig against process_group. When a group is supplied and distributed is initialized, compare its world size and rank with ep_size and ep_rank. An ep_size mismatch gives all_to_all_single split lists with the wrong length and can fail or hang. An ep_rank mismatch assigns the wrong local expert range. Keep the torch.distributed import local and add coverage for both mismatches.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe_backend/pt/config.py` around lines 40 - 55,
Update EpConfig.__post_init__ to validate an optional process_group when
torch.distributed is initialized: compare its world size with ep_size and its
rank with ep_rank, raising clear ValueErrors on either mismatch before
distributed communication. Keep the torch.distributed import local, and add
coverage for both world-size and rank mismatch cases.

Source: Coding guidelines

python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/weights_bwd.py-65-65 (1)

65-65: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Preserve the input device instead of calling .cuda().

Both lines call .cuda(), which targets the current CUDA device. Under expert parallelism the caller can hold w13 and w2 on a device other than the current one, so the returned operands land on the wrong device. The consumer in megamoe/bwd_kernel/backward.py (lines 128-140) then performs an in-place copy_ into existing buffers, which masks the mismatch behind an implicit cross-device copy. Use the source tensor's device.

🔧 Proposed change
-    g1_f32 = w2.float().cuda().transpose(1, 2).contiguous()
+    g1_f32 = w2.to(device=w2.device, dtype=torch.float32).transpose(1, 2).contiguous()
-    g2_f32 = interleave_gate_up(w13).float().cuda().transpose(1, 2).contiguous()
+    g2_f32 = (
+        interleave_gate_up(w13)
+        .to(device=w13.device, dtype=torch.float32)
+        .transpose(1, 2)
+        .contiguous()
+    )

Also applies to: 73-73

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/weights_bwd.py` at
line 65, Update the tensor conversions in the weight backward path, including
the lines assigning g1_f32 and the corresponding w13 operand, to preserve each
source tensor’s device instead of calling .cuda(). Keep the existing float
conversion, transpose, and contiguous operations unchanged.
python/cudnn/moe_ep/_megamoe_backend/megamoe/repo_path.py-17-29 (1)

17-29: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Remove the global import-path shim and MEGAMOE_REPO override.

  • The lazy backend path reaches megamoe.repo_path; import cudnn does not. Keep this backend import lazy.
  • The shim exposes src, common, and pt through process-global sys.path entries. Use package-qualified imports or an isolated loader to prevent module shadowing.
  • Remove MEGAMOE_REPO if the bundled kernels are authoritative. Remove the internal GitLab URL from the error message.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe_backend/megamoe/repo_path.py` around lines 17 -
29, Update the repo_path module’s REPO_ROOT setup to use the bundled
cutedsl_megamoe location without honoring MEGAMOE_REPO, and remove the sys.path
insertion loop. Keep the backend import lazy so importing cudnn does not load
megamoe.repo_path, and replace shim-dependent imports with package-qualified
imports or an isolated loader. Simplify the missing-repository ImportError to
remove the internal GitLab URL and override guidance.

Source: Coding guidelines

python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/mega_runner.py-2730-2750 (1)

2730-2750: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Return a non-zero exit code when the kernel launch is skipped.

main catches NotImplementedError at line 2733 and leaves return_code at 0. Line 2749 then calls os._exit(0). A run that never launched the kernel and never validated therefore reports success. Any CI job or run_mega_tests.sh wrapper that checks the exit code treats that as a pass.

Set a non-zero return_code in the handler, or re-raise.

🔧 Proposed fix
     except NotImplementedError as exc:
         # Expected until the MegaMoE kernel side is wired; the host
         # orchestration above is the part being smoke-tested for now.
+        return_code = 1
         if rank == 0:
             print(f"[mega_runner] kernel launch skipped: {exc}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/mega_runner.py`
around lines 2730 - 2750, Update the NotImplementedError handler in main to set
return_code to a non-zero value before teardown, or re-raise the exception, so
skipped kernel launches cause os._exit to report failure while preserving normal
successful execution.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/kernel_mxfp8_glu_fc12.py-100-111 (1)

100-111: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

valid_ab_tuple is undefined in both ab_dtype validators. Both kernels bind valid_ab from VALID_AB_DTYPE_SF_SIZE and then format an undefined valid_ab_tuple in the error message. An unsupported ab_dtype therefore raises NameError while building the message instead of the intended ValueError. The shared root cause is one wrong identifier duplicated by copy-paste.

  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/kernel_mxfp8_glu_fc12.py#L100-L111: change valid_ab_tuple to valid_ab at line 106.
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/kernel_bwd_fc12.py#L100-L111: change valid_ab_tuple to valid_ab at line 106.
🐛 Proposed fix (identical in both files)
                 raise ValueError(
                     f"ab_dtype={ab_dtype.__name__} is not valid for "
                     f"sf_vec_size={sf_vec_size}. "
-                    f"Expected one of: {[t.__name__ for t in valid_ab_tuple]}."
+                    f"Expected one of: {[t.__name__ for t in valid_ab]}."
                 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/kernel_mxfp8_glu_fc12.py`
around lines 100 - 111, Replace the undefined valid_ab_tuple identifier with the
existing valid_ab variable in both ab_dtype validators:
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/kernel_mxfp8_glu_fc12.py
lines 100-111 and
python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/kernel_bwd_fc12.py lines
100-111. Preserve the intended ValueError message listing the valid dtypes.

Source: Linters/SAST tools

python/cudnn/moe_ep/_megamoe_backend/pt/tests/parity_ep_vs_reference_fp4.py-108-113 (1)

108-113: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Set the CUDA device before init_process_group.

Line 108 initializes the NCCL process group, and line 112 selects the device afterwards. NCCL binds the communicator to the current device during initialization. If every rank still points at device 0 at that moment, the initialization can hang or the communicator can bind to the wrong device.

Read LOCAL_RANK and call torch.cuda.set_device first, then initialize the process group.

🔧 Proposed fix for the initialization order
-    dist.init_process_group("nccl")
-    rank = dist.get_rank()
-    world = dist.get_world_size()
-    local_rank = int(os.environ.get("LOCAL_RANK", rank % max(torch.cuda.device_count(), 1)))
-    torch.cuda.set_device(local_rank)
-    device = torch.device("cuda", local_rank)
+    local_rank = int(os.environ.get("LOCAL_RANK", 0))
+    torch.cuda.set_device(local_rank)
+    device = torch.device("cuda", local_rank)
+    dist.init_process_group("nccl")
+    rank = dist.get_rank()
+    world = dist.get_world_size()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe_backend/pt/tests/parity_ep_vs_reference_fp4.py`
around lines 108 - 113, In the distributed setup before rank/world queries, read
LOCAL_RANK and call torch.cuda.set_device first, then invoke
dist.init_process_group("nccl") so communicator initialization uses the correct
GPU. Preserve the existing local_rank fallback and device construction after
initialization.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/dynamic_mainloop.py-147-153 (1)

147-153: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Mask the extracted SF IDs before inserting them into idesc.

Int32 right shifts are signed. When bit 31 of an SF TMEM address is set, sign extension writes outside the a_sf_id_ or b_sf_id_ fields and can corrupt other descriptor fields, including k_size_. Extract the top two bits, mask with Int32(0x3), then shift them into their target fields.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/dynamic_mainloop.py`
around lines 147 - 153, Update the SF ID packing in the descriptor construction
flow around sfa_top, sfb_top, and idesc so each signed right-shifted address
value is masked with Int32(0x3) before shifting into the a_sf_id_ or b_sf_id_
field. Preserve the existing top-two-bit extraction and ensure no sign-extension
bits can modify other descriptor fields such as k_size_.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/topk_reduce.py-322-327 (1)

322-327: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Derive the output store width from out_dtype.

All three kernels hardcode num_bits_per_copy for the final store: 128 in _reduce_bf16, 256 in _reduce_mxfp8 and _reduce_fp4. Each value is only correct for a 16-bit out_dtype. _mark_alignment already computes the byte width as hidden_per_thread * out_dtype.width // 8, so the alignment adapts while the copy width does not. With an fp32 reduced_output the mxfp8 kernel needs 512 bits per thread and stores 256, which drops half of every thread's output. Compute the width from hidden_per_thread * out_dtype.width in all three kernels.

🛡️ Proposed width derivation
             out = cute.make_rmem_tensor((hidden_per_thread,), out_dtype)
             out.store(acc.load().to(out_dtype))
+            store_bits = hidden_per_thread * out_dtype.width
             cute.copy(
-                cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), out_dtype, num_bits_per_copy=256),
-                out, self._mark_alignment(dst, hidden_per_thread * out_dtype.width // 8),
+                cute.make_copy_atom(cute.nvgpu.CopyUniversalOp(), out_dtype, num_bits_per_copy=store_bits),
+                out, self._mark_alignment(dst, store_bits // 8),
             )

Apply the same change in _reduce_bf16 and _reduce_fp4.

Also applies to: 408-413, 500-505

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/topk_reduce.py`
around lines 322 - 327, Update the final copy operations in _reduce_bf16,
_reduce_mxfp8, and _reduce_fp4 to derive num_bits_per_copy from
hidden_per_thread multiplied by out_dtype.width instead of hardcoded constants.
Keep the existing _mark_alignment calculation and ensure the copy width matches
the full per-thread output for every supported out_dtype.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/epilogue_mxfp8.py-205-208 (1)

205-208: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

allow_overlap_acc and the tile-shape condition are overwritten in both epilogues. Each constructor computes _overlapping_accum from allow_overlap_acc and the _cta_tile_n == EpiWarpCount * EpilogueTileN * 2 condition, then immediately assigns True. The parameter and the shape guard have no effect, while the TMEM column budget and the 256 - _num_sf_tmem_cols phase offsets assume the overlap layout.

  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/epilogue_mxfp8.py#L205-L208: delete the unconditional self._overlapping_accum = True and reject an unsupported cta_tile_n in __init__, or remove allow_overlap_acc and document that overlap is mandatory.
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/epilogue_bwd.py#L225-L228: apply the same decision here, next to the existing generate_c and use_stg_fc1 validation block.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/epilogue_mxfp8.py`
around lines 205 - 208, The overlap flag is unconditionally overriding the
validated configuration in both epilogue constructors. In
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/epilogue_mxfp8.py#L205-L208,
remove the unconditional assignment and validate or reject unsupported
cta_tile_n values while preserving allow_overlap_acc; apply the same decision in
python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/epilogue_bwd.py#L225-L228
near the generate_c and use_stg_fc1 validation block.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/epilogue_mxfp8.py-243-250 (1)

243-250: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Quantized combine without static_expert_shape collapses the fc2 scale stride in both epilogues. When combine_format.is_quantized is true and static_expert_shape is None, both constructors set _fc2_sf_block_pad = 0 and _hidden_fc2 = 0. _stg_sf_fc2 then computes pool_token_global * 0, so every pool token writes its E8M0 scales to the same row, and the token_back_by_dispatch data store multiplies the pool row stride by _hidden_fc2 = 0. The combine output dequantizes with the wrong scale for every token after the first, with no error raised. Both constructors already reject unsupported configurations, so add the check there.

  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/epilogue_mxfp8.py#L243-L250: raise in __init__ when combine_format.is_quantized and static_expert_shape is None, instead of falling through to the zero-padding branch.
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/epilogue_bwd.py#L263-L270: add the same check beside the existing generate_c and use_stg_fc1 validation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/epilogue_mxfp8.py`
around lines 243 - 250, Reject quantized combine configurations without
static_expert_shape in both constructors: update epilogue_mxfp8.py lines 243-250
and epilogue_bwd.py lines 263-270 to raise during __init__ when
combine_format.is_quantized and static_expert_shape is None, alongside the
existing validation, instead of assigning zero _fc2_sf_block_pad and _hidden_fc2
values.
python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/epilogue_bwd.py-148-152 (1)

148-152: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Extract the shared epilogue members instead of copying the forward file.

This module duplicates a large part of cutedsl_megamoe/moe_mxfp8_glu/epilogue_mxfp8.py, including the class name GluMxfp8Epilogue. Fc2OutputDest, tma_store_fc1_output, _store_fc1_c_subtile, _subtile_fc2_tmem_tensor, _advance_fc2_tmem_tensor, _acc_pipeline_consumer_release, _run_fc2_subtile, _write_sf_fc2_buffer, _stg_sf_fc2, and every codegen-time property are byte-identical to their forward counterparts. The __init__ body diverges only in the BWD validation block and _subtile_cnt.

Two identical class names in the same package also make tracebacks and imports ambiguous. Move the shared members into a common base class or module, keep the BWD subclass limited to _subtile_local_tmem_tensor_single, _run_fc1_task_tile, _run_fc1_subtile, _stg_sf_fc1, and the added validation, and give the subclass a distinct name. The other findings on this pair of files already show the cost: each one must be fixed in both copies.

Also applies to: 382-428, 866-879, 1074-1082

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/epilogue_bwd.py`
around lines 148 - 152, Extract the byte-identical members and codegen-time
properties from GluMxfp8Epilogue into a shared base class or module, then make
the BWD-specific epilogue inherit from it. Rename the BWD subclass to a distinct
name and keep it limited to _subtile_local_tmem_tensor_single,
_run_fc1_task_tile, _run_fc1_subtile, _stg_sf_fc1, the BWD validation, and its
differing _subtile_cnt; preserve the shared forward behavior through
inheritance.
python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/mega_runner.py-885-921 (1)

885-921: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Report a failure exit code and always tear down.

Two problems live in this block. return_code is initialized to 0 and never reassigned, so a NotImplementedError from tester.run() prints the skip message and the process still exits 0. A CI job that gates on the exit status treats an unrun kernel as a pass. Any other exception propagates out of main() before the nvshmem free loop and finalize_dist_and_nvshmem() at line 920 run, which leaves the symmetric heap allocated and the other ranks blocked in their next collective.

🛡️ Proposed exit-code and teardown fix
     return_code = 0
     try:
         tester.run()
     except NotImplementedError as exc:
         if rank == 0:
             print(f"[mega_runner_mxfp8] kernel launch skipped: {exc}")
-
-    if not _NO_DIST:
-        tester._compiled_kernel = None
-        tester._kernel = None
-        gc.collect()
-        torch.cuda.synchronize()
-        try:
-            import nvshmem.core
+        return_code = 2
+    finally:
+        if not _NO_DIST:
+            tester._compiled_kernel = None
+            tester._kernel = None
+            gc.collect()
+            torch.cuda.synchronize()
+            try:
+                import nvshmem.core
+                ...
+            except ImportError:
+                pass
+            gc.collect()
+            finalize_dist_and_nvshmem()
     return return_code

Keep the existing free loop body inside the finally block. If a skipped launch must stay non-fatal, set a distinct non-zero code or print an explicit marker the harness can assert on.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/mega_runner.py`
around lines 885 - 921, Update the runner cleanup around tester.run() so every
execution path performs the existing nvshmem free loop and
finalize_dist_and_nvshmem() in a finally block, including unexpected exceptions.
Set return_code to a distinct non-zero value when tester.run() raises
NotImplementedError while preserving the skip message, and return that code so
skipped launches cannot report success.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 5e0cc53d-9bf2-4186-893c-db7236035046

📥 Commits

Reviewing files that changed from the base of the PR and between d5cb1d4 and 2dfd8e0.

📒 Files selected for processing (110)
  • .pre-commit-config.yaml
  • docs/fe-oss-apis/moe_ep.md
  • python/cudnn/moe_ep/_megamoe.py
  • python/cudnn/moe_ep/_megamoe_backend/README.md
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/README.md
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/ci/requirements.txt
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/common/__init__.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/common/host_utils.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/common/megamoe_constants.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/common/moe_utils.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/__init__.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/epilogue_mxfp8.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/kernel_mxfp8_glu_fc12.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/mega_reference_mxfp8.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/mega_runner.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/megamoe_kernel_mxfp8.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/run_functional_tests.sh
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/run_mega_tests.sh
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/runner_common.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_mxfp8_glu/runner_fc12.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/__init__.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/benchmark_p2p.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/contract.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/custom_ext.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/dynamic_mainloop.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/epilogue.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/epilogue_refactor.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/fc1_fc2_fuse_sched.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/kernel_fc12.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/mega_reference.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/mega_runner.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/megamoe_kernel.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/moe_persistent_scheduler.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/moe_utils.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/run_functional_tests.sh
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/run_mega_tests.sh
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/runner_common.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/runner_fc12.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/runner_fc12_common.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/simulate_fc1_fc2_sched.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/moe_nvfp4_swapab/topk_reduce.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/__init__.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/bootstrap.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/cleanup_kernel.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/config.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/dispatch_kernel.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/flag_batch.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/grid_sync.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/iket_compat.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/inputs_process.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/ptx_helpers.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/reference.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/sf_swizzle.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/sym_buffer.py
  • python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/token_comm.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/BWD_DESIGN.md
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/README.md
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/__init__.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/__init__.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/backward.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/epilogue_bwd.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/kernel_bwd_fc12.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/kernel_bwd_mega.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/moe_utils_bwd.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/repro_mega.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/test_bwd_fc12.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/weights_bwd.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_v0.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/forward.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/forward_nvfp4.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/fp8_bwd.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/pools.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/quant_kernels.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/repo_path.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/tests/__init__.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/tests/probe_bwd_contracts.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/tests/smoke_generate_c.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/tests/test_bwd_v0_parity.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/tests/test_forward_parity.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/tests/test_hybrid_training.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/tests/test_hybrid_training_dist.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/tests/test_turboquant_numerics.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/training.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/turboquant.py
  • python/cudnn/moe_ep/_megamoe_backend/megamoe/weights.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/__init__.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/comm/__init__.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/comm/base.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/comm/torch_dist.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/config.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/dispatch_combine.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/experts.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/experts_fp4.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/layer.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/layer_fp4.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/quant.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/reference.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/reference_fp4.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/routing.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/tests/parity_ep_vs_reference.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/tests/parity_ep_vs_reference_fp4.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/tests/run_tests.sh
  • python/cudnn/moe_ep/_megamoe_backend/pt/tests/test_fp4_qat_numerics.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/tests/test_quant_vs_kernel.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/tests/test_reference.py
  • python/cudnn/moe_ep/_megamoe_backend/pt/tests/test_scaled_grouped_mm_mxfp8.py
  • python/cudnn/moe_ep/api.py
  • test/python/fe_api/moe_ep/RUNBOOK.md
  • test/python/fe_api/moe_ep/megamoe_backend_parity.py
  • test/python/fe_api/moe_ep/run_pr_tests.sh
🚧 Files skipped from review as they are similar to previous changes (4)
  • test/python/fe_api/moe_ep/megamoe_backend_parity.py
  • python/cudnn/moe_ep/_megamoe.py
  • python/cudnn/moe_ep/api.py
  • docs/fe-oss-apis/moe_ep.md

Comment on lines +331 to +341
dout_q, dout_sf = mxfp8_rowquant(dout.to(torch.bfloat16))
bwd.my_dout.view(torch.uint8).copy_(dout_q.view(torch.uint8))
bwd.my_dout_sf.view(torch.uint8)[:, : H // 32].copy_(
dout_sf.contiguous().view(torch.uint8)
)
bwd.recv_count_sum.copy_(
torch.tensor(counts, device=device, dtype=torch.int64)
)
bwd.act_pool.view(torch.uint8).zero_()
bwd.act_sf_swz.view(torch.uint8).zero_()
bwd.fc1_ready_counter.zero_()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

The my_dout staging copy fails when T < max_tokens_per_rank.

bwd.my_dout has shape (self.T, H) with self.T = fwd.cfg.max_tokens_per_rank (Line 77). dout_q has T rows. Line 332 copies into the full buffer, so copy_ raises a shape error for every step with fewer tokens than the configured capacity. The SF copy on Line 333 already slices columns but also not rows.

Slice both destinations to [:T]. Zero the padding tail as well, otherwise the peer-pull can read stale rows from the previous step.

🐛 Proposed fix
                 dout_q, dout_sf = mxfp8_rowquant(dout.to(torch.bfloat16))
-                bwd.my_dout.view(torch.uint8).copy_(dout_q.view(torch.uint8))
-                bwd.my_dout_sf.view(torch.uint8)[:, : H // 32].copy_(
+                my_dout_u8 = bwd.my_dout.view(torch.uint8)
+                my_dout_sf_u8 = bwd.my_dout_sf.view(torch.uint8)
+                if T < bwd.T:
+                    my_dout_u8[T:].zero_()
+                    my_dout_sf_u8[T:].zero_()
+                my_dout_u8[:T].copy_(dout_q.view(torch.uint8))
+                my_dout_sf_u8[:T, : H // 32].copy_(
                     dout_sf.contiguous().view(torch.uint8)
                 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
dout_q, dout_sf = mxfp8_rowquant(dout.to(torch.bfloat16))
bwd.my_dout.view(torch.uint8).copy_(dout_q.view(torch.uint8))
bwd.my_dout_sf.view(torch.uint8)[:, : H // 32].copy_(
dout_sf.contiguous().view(torch.uint8)
)
bwd.recv_count_sum.copy_(
torch.tensor(counts, device=device, dtype=torch.int64)
)
bwd.act_pool.view(torch.uint8).zero_()
bwd.act_sf_swz.view(torch.uint8).zero_()
bwd.fc1_ready_counter.zero_()
dout_q, dout_sf = mxfp8_rowquant(dout.to(torch.bfloat16))
my_dout_u8 = bwd.my_dout.view(torch.uint8)
my_dout_sf_u8 = bwd.my_dout_sf.view(torch.uint8)
if T < bwd.T:
my_dout_u8[T:].zero_()
my_dout_sf_u8[T:].zero_()
my_dout_u8[:T].copy_(dout_q.view(torch.uint8))
my_dout_sf_u8[:T, : H // 32].copy_(
dout_sf.contiguous().view(torch.uint8)
)
bwd.recv_count_sum.copy_(
torch.tensor(counts, device=device, dtype=torch.int64)
)
bwd.act_pool.view(torch.uint8).zero_()
bwd.act_sf_swz.view(torch.uint8).zero_()
bwd.fc1_ready_counter.zero_()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/backward.py` around
lines 331 - 341, Update the staging copies in the backward path around
mxfp8_rowquant so both bwd.my_dout and bwd.my_dout_sf destinations are sliced to
[:T] before copying dout_q and dout_sf. After copying, explicitly zero the
remaining padding rows in both buffers from T onward so peer-pull cannot read
stale data.

Comment on lines +201 to +252
with cute.arch.elect_one():
pull_buffer_warp_ptr = pull_buffer_ptr + (
warp_idx * Int32(self.hidden_bytes)
)
tma_src_addr = (
inp_tok_local_base
+ cur_peer_offset
+ Int64(src_token * Int32(self.hidden_bytes))
)
tma_load_1d_raw(
pull_buffer_warp_ptr,
tma_src_addr,
pull_mbar_ptr + warp_idx,
Int32(self.hidden_bytes),
)
cute.arch.sync_warp()

sf_passes: cutlass.Constexpr[int] = (
self.sf_uint32_per_token + 31
) // 32
sf_vals = []
for _ in cutlass.range_constexpr(0, sf_passes, 1):
sf_vals.append(Int32(0))
for i in cutlass.range_constexpr(0, sf_passes, 1):
j = Int32(i * self.warp_threads) + lane_idx
if j < Int32(self.sf_uint32_per_token):
sf_addr = (
inp_sf_local_base
+ cur_peer_offset
+ Int64(
(src_token * Int32(self.sf_uint32_per_token) + j)
* Int32(4)
)
)
sf_vals[i] = ldg_b32_raw(sf_addr)

for i in cutlass.range_constexpr(0, sf_passes, 1):
j = Int32(i * self.warp_threads) + lane_idx
if j < Int32(self.sf_uint32_per_token):
sf_int32_pos = sf_atom_int32_offset(
sf_token_in_pool_axis,
j,
num_k_atoms=self.sf_uint32_per_token,
)
fc1_input_sf_buffer[sf_int32_pos] = sf_vals[i]
cute.arch.sync_warp()

with cute.arch.elect_one():
cute.arch.mbarrier_arrive_and_expect_tx(
pull_mbar_ptr + warp_idx, Int32(self.hidden_bytes)
)
cute.arch.mbarrier_wait(pull_mbar_ptr + warp_idx, phase_bit)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Extract the forward comm's per-slot barrier/TMA ordering to confirm the expected sequence.
set -uo pipefail

F=python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/token_comm.py
ast-grep outline "$F" --items all

echo "== barrier / TMA call order in the forward pull path"
rg -n 'mbarrier_init|mbarrier_arrive_and_expect_tx|mbarrier_wait|tma_load_1d|tma_store_1d|cp_async_bulk' "$F"

echo "== same order in the reviewed backward gather"
rg -n 'mbarrier_init|mbarrier_arrive_and_expect_tx|mbarrier_wait|tma_load_1d|tma_store_1d|cp_async_bulk' \
  python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/kernel_bwd_mega.py

Repository: NVIDIA/cudnn-frontend

Length of output: 4617


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "== forward dispatch pull sequence =="
sed -n '1115,1270p' python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/token_comm.py

echo "== backward gather sequence =="
sed -n '180,275p' python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/kernel_bwd_mega.py

echo "== documented forward sequence =="
rg -n -C 8 'arrive_and_expect_tx|tma_load_1d|per-slot|pull' \
  python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/README.md

Repository: NVIDIA/cudnn-frontend

Length of output: 16379


🌐 Web query:

CUDA PTX mbarrier.arrive.expect_tx ordering before cp.async.bulk TMA transaction barrier expected transaction count

💡 Result:

The mbarrier.arrive.expect_tx PTX instruction is a fused operation that performs two distinct functions to manage asynchronous memory operations, specifically those using the Tensor Memory Accelerator (TMA) [1][2][3]. 1. Transaction Expectation: It increases the barrier's "transaction debt" by a specified byte count (tx-count). This informs the barrier that it must track an additional number of asynchronous transactions (typically bytes) before the current barrier phase can be considered complete [2][4][5]. 2. Thread Arrival: It performs a standard thread arrival on the barrier, decrementing the pending-arrival count [2][4][3]. The critical ordering requirement is that mbarrier.arrive.expect_tx must be executed before the associated TMA cp.async.bulk operation is issued [1][6]. This ensures that the barrier object is properly initialized with the expected transaction count so that the TMA engine can correctly report completion as data arrives [1][4][5]. Regarding completion semantics: - The barrier phase completes only when both criteria are met: the pending-arrival count reaches zero, AND the pending transaction byte count reaches zero [7][1][2][4]. - The cp.async.bulk instructions used with TMA are typically configured with the mbarrier::complete_tx::bytes modifier [8][1][9]. As the TMA engine transfers data, it implicitly uses this modifier to perform complete_tx operations on the specified barrier, decrementing the transaction byte count [2][9][10]. - Consumers (e.g., MMA instructions) wait on the barrier phase to flip using mbarrier.try_wait or similar instructions [1][9][10]. Once the phase flips, it guarantees that all threads have arrived and all expected TMA bytes have been committed to shared memory [1][4][5]. In summary, the mbarrier.arrive.expect_tx instruction sets the "debt" that the TMA operation later "pays off" via complete_tx notifications; issuing the expectation before the transfer is essential to prevent race conditions where the hardware reports completion before the barrier is configured to track it [1][2][6].

Citations:


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "== num_global_warps and token_idx definitions in backward kernel =="
rg -n -C 8 'num_global_warps|sm_count|token_idx|num_sms' \
  python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/kernel_bwd_mega.py

echo "== TokenCommArgs sm_count declarations and extraction =="
rg -n -C 10 'sm_count|__extract_mlir_values__|__extract_mlir_attributes__|_CONST_FIELDS' \
  python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/token_comm.py

echo "== backward constructor and launch call sites =="
rg -n -C 10 'BwdGatherComm|kernel_bwd_mega|num_sms|sm_count' \
  python/cudnn/moe_ep/_megamoe_backend/megamoe \
  python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src

Repository: NVIDIA/cudnn-frontend

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "== TMA and barrier helper implementations =="
rg -n -C 20 'def tma_load_1d_raw|def tma_store_1d|mbarrier_arrive_and_expect_tx|mbarrier_wait' \
  python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src \
  python/cudnn/moe_ep/_megamoe_backend/megamoe

echo "== focused backward class signatures and constructor values =="
sed -n '1,180p' python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/kernel_bwd_mega.py
sed -n '280,380p' python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/kernel_bwd_mega.py

echo "== all local transaction-order patterns =="
python3 - <<'PY'
from pathlib import Path
import re

roots = [
    Path("python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src"),
    Path("python/cudnn/moe_ep/_megamoe_backend/megamoe"),
]
for root in roots:
    for path in root.rglob("*.py"):
        text = path.read_text(errors="replace")
        calls = [(m.start(), m.group(0).split("(")[0]) for m in re.finditer(
            r"\b(?:tma_load_1d_raw|mbarrier_arrive_and_expect_tx|mbarrier_wait)\s*\(",
            text,
        )]
        for (pos1, name1), (pos2, name2) in zip(calls, calls[1:]):
            if name1.endswith("tma_load_1d_raw") and name2.endswith("mbarrier_arrive_and_expect_tx"):
                print(f"{path}: load -> expect")
            if name1.endswith("mbarrier_arrive_and_expect_tx") and name2.endswith("tma_load_1d_raw"):
                print(f"{path}: expect -> load")
PY

Repository: NVIDIA/cudnn-frontend

Length of output: 48116


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "== TokenCommArgs construction sites =="
rg -n -C 12 'TokenCommArgs\s*\(' \
  python/cudnn/moe_ep/_megamoe_backend

echo "== sm_count assignment and kernel argument declarations =="
rg -n -C 6 'sm_count\s*=|sm_count:' \
  python/cudnn/moe_ep/_megamoe_backend \
  -g '*.py'

echo "== base TokenInPullTokenBackPush constructor =="
rg -n -C 12 'class TokenInPullTokenBackPush|def __init__' \
  python/cudnn/moe_ep/_megamoe_backend/cutedsl_megamoe/src/token_comm.py \
  python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/kernel_bwd_mega.py

Repository: NVIDIA/cudnn-frontend

Length of output: 49669


Register the TMA transaction count before issuing the load.

tma_load_1d_raw uses complete_tx::bytes, so mbarrier_arrive_and_expect_tx must run first. Otherwise, mbarrier_wait does not reliably guarantee that pull_buffer contains the transferred row, which can corrupt the FC1 A operand or hang. Apply the same ordering fix in the forward implementation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudnn/moe_ep/_megamoe_backend/megamoe/bwd_kernel/kernel_bwd_mega.py`
around lines 201 - 252, In the backward pull path, update the warp elected block
around tma_load_1d_raw so mbarrier_arrive_and_expect_tx registers hidden_bytes
before issuing the TMA load, while keeping mbarrier_wait after the load and
preserving the existing phase handling. Apply the identical ordering change in
the corresponding forward implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants