Skip to content

[#15853][fix] Gate B12x MoE on CUDA13 CuTe DSL#15851

Open
mihai-chiorean wants to merge 6 commits into
NVIDIA:mainfrom
mihai-chiorean:fix/b12x-sm121-cuda13-guard
Open

[#15853][fix] Gate B12x MoE on CUDA13 CuTe DSL#15851
mihai-chiorean wants to merge 6 commits into
NVIDIA:mainfrom
mihai-chiorean:fix/b12x-sm121-cuda13-guard

Conversation

@mihai-chiorean

@mihai-chiorean mihai-chiorean commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Fixes #15853

Summary

  • enable the SM120/SM121 B12x NVFP4 MoE path only when the active CuTe DSL native payload reports CUDA 13 or newer
  • fall back to CUTLASS with an actionable warning for CUDA 12.x / missing CuTe DSL payloads, avoiding the ptxas _mma.block_scale failure
  • add selector tests for SM120 and SM121 CUDA12, missing CUDA version, and CUDA13 behavior

Validation

  • Focused pytest: 36 passed
  • Focused touched-file pre-commit: passed
  • Full-model Qwen3.6 NVFP4 with CUDA13 CuTe DSL + B12x selected: coherent
  • Perf reference, Qwen3.6 NVFP4, TRTLLM attention, FP8 KV, fixed 128-token output:
    • batch1: 16.19 ms TPOT / 61.78 tok/s
    • batch4 steady: 24.39 ms/request / 164.03 tok/s aggregate
    • batch8: 35.81 ms/request / 224.58 tok/s aggregate
  • MTP validation on the same B12x path:
    • batch1: 16.55 ms TPOT / 60.46 tok/s, 81.56% draft acceptance
    • batch4 steady: 23.50 ms/request / 170.20 tok/s aggregate, 77.99% draft acceptance

Notes

The root cause was package payload ordering: nvidia-cutlass-dsl-libs-cu13==4.5.0 was installed, but the active files came from nvidia-cutlass-dsl-libs-base==4.5.0, so CuTe DSL reported CUDA 12.9 and generated PTX without kind::mxf4nvf4. Reinstalling the cu13 libs after base made CuTe DSL report CUDA 13.1 and the B12x path compiled and ran on SM121.

The same CUDA 12.x lowering failure was reproduced for an SM120 target, so the guard applies to the whole current B12x SM12x support set.

MTP did not require an additional code change here; it works once the B12x path is available and acceptance is read from iteration stats rather than request-level perf metrics.

Summary by CodeRabbit

  • Bug Fixes

    • Improved backend selection so unsupported runtime environments now fall back safely instead of attempting an unavailable accelerated path.
    • Added stricter compatibility checks for the fused MoE backend, including CUDA/CuTe DSL runtime version validation.
    • Reduced the chance of startup failures by logging a warning and selecting a compatible fallback backend when needed.
  • Tests

    • Expanded coverage for backend selection and rejection cases across supported and unsupported runtime combinations.
    • Added validation for both accepted and fallback paths under different CUDA runtime versions.

Signed-off-by: Mihai Chiorean <mihai.v.chiorean@gmail.com>
@mihai-chiorean mihai-chiorean changed the title [None][fix] Gate SM121 B12x MoE on CUDA13 CuTe DSL [#15853][fix] Gate SM121 B12x MoE on CUDA13 CuTe DSL Jul 1, 2026
Signed-off-by: Mihai Chiorean <mihai.v.chiorean@gmail.com>
@mihai-chiorean mihai-chiorean changed the title [#15853][fix] Gate SM121 B12x MoE on CUDA13 CuTe DSL [#15853][fix] Gate B12x MoE on CUDA13 CuTe DSL Jul 1, 2026
@mihai-chiorean
mihai-chiorean marked this pull request as ready for review July 1, 2026 22:23
@mihai-chiorean
mihai-chiorean requested a review from a team as a code owner July 1, 2026 22:23
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a runtime capability gate to CuteDslB12xFusedMoE that checks the active CUTLASS CuTe DSL CUDA major version via a new get_runtime_disable_reason helper, wires this check into both can_implement and backend selection in create_moe.py to fall back to CutlassFusedMoE when CUDA 13 is unavailable, and updates unit tests to mock the new CUDA major detection across gating scenarios.

Changes

B12x runtime gating

Layer / File(s) Summary
Runtime disable-reason helpers and can_implement gating
tensorrt_llm/_torch/modules/fused_moe/fused_moe_cute_dsl_b12x.py
Adds _MIN_CUTLASS_DSL_CUDA_MAJOR_FOR_B12X = 13 constant and new get_runtime_disable_reason, _is_cutlass_dsl_runtime_available, and _get_cutlass_dsl_cuda_major methods; can_implement now calls get_runtime_disable_reason(sm_version) and early-returns on a non-None reason.
Backend selection early fallback
tensorrt_llm/_torch/modules/fused_moe/create_moe.py
CUTEDSL backend selection now checks get_runtime_disable_reason(sm_version) for supported SMs and falls back to CutlassFusedMoE with a logged warning before the existing flashinfer probe.
Test coverage for CUDA major gating
tests/unittest/_torch/modules/moe/test_cute_dsl_b12x_moe_backend.py
Tests for can_implement and get_moe_cls are updated/expanded to mock _get_cutlass_dsl_cuda_major, covering CUDA 12 rejection, CUDA 13 acceptance, missing CUDA major rejection, and deterministic rejection reasons for unrelated failure cases.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CreateMoe as create_moe.py
  participant B12x as CuteDslB12xFusedMoE
  participant DslRuntime as CuTe DSL Runtime
  participant Cutlass as CutlassFusedMoE

  CreateMoe->>B12x: get_runtime_disable_reason(sm_version)
  B12x->>B12x: check sm_version in supported list
  B12x->>DslRuntime: _get_cutlass_dsl_cuda_major()
  DslRuntime-->>B12x: CUDA major or None
  alt CUDA major < 13 or unavailable
    B12x-->>CreateMoe: disable reason string
    CreateMoe->>Cutlass: select as fallback (log warning)
  else CUDA major >= 13
    B12x-->>CreateMoe: None
    CreateMoe->>CreateMoe: continue flashinfer probe / select B12x
  end
Loading

Related Issues: #15853

Related PRs: None mentioned

Suggested labels: bug, moe, fused_moe

Suggested reviewers: Tracin, Fridah-nv

🐰 A rabbit checks the CUDA before it jumps,
Twelve won't do, it must be thirteen bumps,
If the DSL payload's old and gray,
Cutlass hops in to save the day!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, specific, and matches the PR's main change: gating B12x MoE on CUDA 13 CuTe DSL.
Description check ✅ Passed The description explains the bug, the fix, and validation, though it doesn't follow the repository template headings exactly.
Linked Issues check ✅ Passed The code adds the requested runtime gate and CUTLASS fallback for SM120/SM121 when CuTe DSL is CUDA 12.x or missing, matching #15853.
Out of Scope Changes check ✅ Passed The changes stay focused on the B12x CuTe DSL gating logic and related tests without unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 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: 1

🧹 Nitpick comments (4)
tests/unittest/_torch/modules/moe/test_cute_dsl_b12x_moe_backend.py (3)

181-191: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate test: test_get_moe_cls_cutedsl_selects_b12x_on_supported_sm vs test_get_moe_cls_cutedsl_selects_b12x_on_supported_sm_with_cuda13.

These two tests are functionally identical (same SM parametrize, same CUDA-major=13 mock, same assertion cls is CuteDslB12xFusedMoE). Suggest consolidating into a single test.

Also applies to: 208-218

🤖 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 `@tests/unittest/_torch/modules/moe/test_cute_dsl_b12x_moe_backend.py` around
lines 181 - 191, The test
`test_get_moe_cls_cutedsl_selects_b12x_on_supported_sm` duplicates
`test_get_moe_cls_cutedsl_selects_b12x_on_supported_sm_with_cuda13` with the
same `sm_version` parametrization, the same
`CuteDslB12xFusedMoE._get_cutlass_dsl_cuda_major` mock returning 13, and the
same `get_moe_cls(cfg)` assertion. Consolidate the two into a single test by
keeping one representative name and removing the redundant copy, while
preserving the shared setup around `ModelConfig`, `QuantConfig`,
`QuantAlgo.NVFP4`, and the `get_moe_cls` assertion.

Source: Path instructions


51-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate test: test_can_implement_accepts_supported_sm_with_nvfp4 vs test_can_implement_accepts_supported_sm_with_cuda13_cute_dsl.

Both tests mock the same sm_version parametrize source and _get_cutlass_dsl_cuda_major=13, and assert the exact same outcome (ok, reason is None). Consider removing one (keep the more descriptive ..._with_cuda13_cute_dsl name) to avoid duplicate maintenance.

Also applies to: 85-93

🤖 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 `@tests/unittest/_torch/modules/moe/test_cute_dsl_b12x_moe_backend.py` around
lines 51 - 58, The test `test_can_implement_accepts_supported_sm_with_nvfp4`
duplicates `test_can_implement_accepts_supported_sm_with_cuda13_cute_dsl`, since
both use the same `sm_version` parametrization, patch
`CuteDslB12xFusedMoE._get_cutlass_dsl_cuda_major` to 13, and assert the same
`can_implement` result. Remove the redundant test (prefer keeping the more
descriptive `test_can_implement_accepts_supported_sm_with_cuda13_cute_dsl`) and,
if needed, update any nearby assertions in `CuteDslB12xFusedMoE.can_implement`
coverage to preserve distinct behavior coverage.

Source: Path instructions


61-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Coverage gap: _get_cutlass_dsl_cuda_major's own implementation is never exercised.

Every test in this file mocks _get_cutlass_dsl_cuda_major directly, so the actual import/getattr/isinstance logic inside it (ImportError handling, non-int major, missing CUDA_VERSION attribute) has no dedicated test. Consider adding a focused unit test in this file that monkeypatches sys.modules["cutlass.base_dsl.version_info"] (or similarly stubs the cutlass package) to verify _get_cutlass_dsl_cuda_major() itself returns the right value/None for: (a) module not importable, (b) CUDA_VERSION.major missing, (c) CUDA_VERSION.major non-int, (d) a valid int major. Coverage is otherwise sufficient for the gating behavior end-to-end (can_implement + get_moe_cls); this gap is isolated to the low-level version-parsing helper and could reasonably be addressed as a small follow-up rather than blocking this PR.

Also applies to: 194-218

🤖 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 `@tests/unittest/_torch/modules/moe/test_cute_dsl_b12x_moe_backend.py` around
lines 61 - 93, Add focused unit coverage for
CuteDslB12xFusedMoE._get_cutlass_dsl_cuda_major itself, since current tests only
mock it and never exercise its import/version parsing path. Create tests that
stub the cutlass.base_dsl.version_info import (via sys.modules or equivalent) to
verify the helper returns None when the module is missing, when CUDA_VERSION or
CUDA_VERSION.major is absent, and when major is non-int, plus returns the int
major for a valid module. Keep the existing can_implement tests as-is; this
change should target the helper’s own import/getattr/isinstance logic.

Source: Path instructions

tensorrt_llm/_torch/modules/fused_moe/fused_moe_cute_dsl_b12x.py (1)

123-144: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a docstring to the new public get_runtime_disable_reason.

This classmethod is now called cross-file from create_moe.get_moe_cls, but it only has an inline comment explaining the CUDA 12 vs 13 PTX issue, no docstring describing its contract (input/return semantics).

📝 Suggested docstring
     `@classmethod`
     def get_runtime_disable_reason(cls, sm_version: int) -> Optional[str]:
+        """Return a human-readable reason this backend can't run on ``sm_version``
+        right now, or ``None`` if all runtime requirements are met.
+
+        Checked here (rather than only in ``can_implement``) so
+        ``create_moe.get_moe_cls`` can fall back to CutlassFusedMoE before
+        ever attempting to build/JIT this backend.
+
+        Args:
+            sm_version: SM version as returned by ``get_sm_version()``.
+        """
         if sm_version not in cls._SUPPORTED_SM_VERSIONS:

As per coding guidelines, "For interfaces used outside a file, prefer docstrings over comments; reserve comments for code within functions or interfaces local to a file."

🤖 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 `@tensorrt_llm/_torch/modules/fused_moe/fused_moe_cute_dsl_b12x.py` around
lines 123 - 144, Add a docstring to
CuteDslB12xFusedMoE.get_runtime_disable_reason describing its contract for
external callers such as create_moe.get_moe_cls: document that it takes an
sm_version integer, returns a human-readable disable reason string when the
runtime is unsupported or unavailable, and returns None when the runtime can be
used. Keep the existing CUDA 12 vs 13 logic intact, and place the docstring on
the classmethod itself so the public interface is documented instead of relying
on the inline comment.

Source: Coding guidelines

🤖 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 `@tensorrt_llm/_torch/modules/fused_moe/fused_moe_cute_dsl_b12x.py`:
- Around line 146-160: The CUTLASS runtime check is using an internal import
path in _get_cutlass_dsl_cuda_major, which is brittle and can break if CUTLASS
internals change. Update that helper to read the public
cutlass.CUDA_VERSION.major API directly, and keep
_is_cutlass_dsl_runtime_available using the returned major version for its
threshold check.

---

Nitpick comments:
In `@tensorrt_llm/_torch/modules/fused_moe/fused_moe_cute_dsl_b12x.py`:
- Around line 123-144: Add a docstring to
CuteDslB12xFusedMoE.get_runtime_disable_reason describing its contract for
external callers such as create_moe.get_moe_cls: document that it takes an
sm_version integer, returns a human-readable disable reason string when the
runtime is unsupported or unavailable, and returns None when the runtime can be
used. Keep the existing CUDA 12 vs 13 logic intact, and place the docstring on
the classmethod itself so the public interface is documented instead of relying
on the inline comment.

In `@tests/unittest/_torch/modules/moe/test_cute_dsl_b12x_moe_backend.py`:
- Around line 181-191: The test
`test_get_moe_cls_cutedsl_selects_b12x_on_supported_sm` duplicates
`test_get_moe_cls_cutedsl_selects_b12x_on_supported_sm_with_cuda13` with the
same `sm_version` parametrization, the same
`CuteDslB12xFusedMoE._get_cutlass_dsl_cuda_major` mock returning 13, and the
same `get_moe_cls(cfg)` assertion. Consolidate the two into a single test by
keeping one representative name and removing the redundant copy, while
preserving the shared setup around `ModelConfig`, `QuantConfig`,
`QuantAlgo.NVFP4`, and the `get_moe_cls` assertion.
- Around line 51-58: The test
`test_can_implement_accepts_supported_sm_with_nvfp4` duplicates
`test_can_implement_accepts_supported_sm_with_cuda13_cute_dsl`, since both use
the same `sm_version` parametrization, patch
`CuteDslB12xFusedMoE._get_cutlass_dsl_cuda_major` to 13, and assert the same
`can_implement` result. Remove the redundant test (prefer keeping the more
descriptive `test_can_implement_accepts_supported_sm_with_cuda13_cute_dsl`) and,
if needed, update any nearby assertions in `CuteDslB12xFusedMoE.can_implement`
coverage to preserve distinct behavior coverage.
- Around line 61-93: Add focused unit coverage for
CuteDslB12xFusedMoE._get_cutlass_dsl_cuda_major itself, since current tests only
mock it and never exercise its import/version parsing path. Create tests that
stub the cutlass.base_dsl.version_info import (via sys.modules or equivalent) to
verify the helper returns None when the module is missing, when CUDA_VERSION or
CUDA_VERSION.major is absent, and when major is non-int, plus returns the int
major for a valid module. Keep the existing can_implement tests as-is; this
change should target the helper’s own import/getattr/isinstance logic.
🪄 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: c47b1f54-6586-4d54-a7b0-6039ef5d1b05

📥 Commits

Reviewing files that changed from the base of the PR and between 920a50a and f632d62.

📒 Files selected for processing (3)
  • tensorrt_llm/_torch/modules/fused_moe/create_moe.py
  • tensorrt_llm/_torch/modules/fused_moe/fused_moe_cute_dsl_b12x.py
  • tests/unittest/_torch/modules/moe/test_cute_dsl_b12x_moe_backend.py

Comment thread tensorrt_llm/_torch/modules/fused_moe/fused_moe_cute_dsl_b12x.py
Signed-off-by: Mihai Chiorean <mihai.v.chiorean@gmail.com>
@mihai-chiorean

Copy link
Copy Markdown
Contributor Author

Addressed the CodeRabbit review in 8bd8778341:

  • switched _get_cutlass_dsl_cuda_major() to the public cutlass.CUDA_VERSION.major API
  • added direct helper coverage for missing/invalid/valid CUDA_VERSION.major
  • added a docstring for get_runtime_disable_reason()
  • removed the duplicate acceptance/selector tests

Validation after the follow-up: focused pytest 37 passed; touched-file pre-commit passed; GitHub PR checks are green.

Comment thread tensorrt_llm/_torch/modules/fused_moe/create_moe.py
Signed-off-by: Mihai Chiorean <mihai.v.chiorean@gmail.com>
@waynehacking8

Copy link
Copy Markdown

Checked the family-modes angle on e4cf2cc, since NVFP4_AWQ and W4A8_NVFP4_FP8 share the NVFP4 QuantMode bit (mode.py:427) and previously entered the gate via has_nvfp4(): can_implement already exact-matches QuantAlgo.NVFP4 (fused_moe_cute_dsl_b12x.py:108), so the tighter gate can't regress them -- it just makes the selector consistent with the backend's own check. Source-read only, on the same tree I ran the #12704 tile probes from.

Comment thread tensorrt_llm/_torch/modules/fused_moe/fused_moe_cute_dsl_b12x.py

@leslie-fang25 leslie-fang25 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. Better land after #12704

@mihai-chiorean

Copy link
Copy Markdown
Contributor Author

LGTM. Better land after #12704

thank you!! appreciate it! @leslie-fang25 any chance you can help me with CI on #12704 ? it's approve, but I've been waiting on CI for awhile.

@mihai-chiorean

Copy link
Copy Markdown
Contributor Author

LGTM. Better land after #12704

landed #12704

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.

[Bug]: B12x NVFP4 MoE JIT fails on SM120/SM121 with CUDA 12 CuTe DSL payload

3 participants