NVIDIA / NVIDIA/TensorRT-LLM

[None][feat] Add GPU SM capability checks to all AutoDeploy quantization and MOE transforms

Open
#12,303 0 comments 0 reactions 1 assignee View on GitHub

@Fridah-nv is already working on this.

Since Mar 18, 2026.

AutoDeploy
Dominant language
Python
Stars
14.7k
Forks
2.8k
Avg merge
2d 23h
Merged PRs (30d)
489

Description

Summary

AutoDeploy quantization and MOE transforms should validate GPU SM (compute capability) before attempting to apply architecture-specific kernels. Today, none of the transforms perform this check, so running an incompatible quant config on the wrong GPU leads to cryptic runtime failures instead of a clear early skip with a warning.

Problem

Different quantization formats require specific GPU architectures:

Format Minimum SM Architecture
FP8 (standard) 89 Ada Lovelace / Hopper
FP8 (fine-grained / block-scaled) 90 Hopper (with SM 120 Blackwell variant path)
NVFP4 100 Blackwell
MXFP4 TBD TBD
INT4 (GPTQ / W4A16) None currently known

When a user runs e.g. an NVFP4 model config on a pre-Blackwell GPU, the transform pipeline proceeds without error until it hits an unsupported kernel at runtime. The resulting error is hard to diagnose.

Example: What the guard would look like per-transform

As a concrete illustration, here is what a guard on FuseNVFP4Linear would look like:

from tensorrt_llm._utils import get_sm_version
from ...utils.logger import ad_logger

@TransformRegistry.register("fuse_nvfp4_linear")
class FuseNVFP4Linear(BaseTransform):

    def _apply(self, gm, cm, factory, shared_config):
        sm = get_sm_version()
        if sm < 100:
            ad_logger.warning(
                "Skipping FuseNVFP4Linear: NVFP4 requires Blackwell (SM >= 100), "
                "but current GPU has SM %d.",
                sm,
            )
            return gm, TransformInfo(skipped=True)
        # ... rest of transform

This pattern should be applied systematically to all quantization and MOE transforms listed below.

Transforms That Need SM Guards

Quantized Linear Transforms
  • fuse_fp8_linear (fuse_quant.py) — needs SM >= 89
  • fuse_nvfp4_linear (fuse_quant.py) — needs SM >= 100
  • fuse_finegrained_fp8_linear (fuse_quant.py) — needs SM >= 90
  • fuse_relu2_quant_nvfp4 (fuse_relu2_quant_nvfp4.py) — needs SM >= 100
  • quantize_fp8_linear_from_config (quantization.py) — needs SM >= 89
  • quantize_nvfp4_linear_from_config (quantization.py) — needs SM >= 100
  • quantize_fp8_from_graph (quantization.py) — needs SM >= 89
  • quantize_nvfp4_from_graph (quantization.py) — needs SM >= 100
  • quantize_finegrained_fp8_linear_from_config (quantization.py) — needs SM >= 90
  • quantize_int4_linear_from_config (quantization.py) — verify if any SM floor exists
  • quantize_int4_gptq_linear_from_config (quantization.py) — verify if any SM floor exists
  • quantize_fp8_bmm_from_config (quantization.py) — needs SM >= 89
Fusion Transforms
  • fuse_fp8_gemms (fusion.py) — needs SM >= 89
  • fuse_fp4_gemms (fusion.py) — needs SM >= 100
  • fuse_finegrained_fp8_gemms (fusion.py) — needs SM >= 90
  • match_nvfp4_swiglu_pattern / fuse_nvfp4_swiglu (fuse_swiglu.py) — needs SM >= 100
  • match_finegrained_fp8_swiglu_pattern / fuse_finegrained_fp8_swiglu (fuse_swiglu.py) — needs SM >= 90
MOE Transforms
  • quantize_fp8_moe (quantize_moe.py) — needs SM >= 89
  • quantize_nvfp4_moe (quantize_moe.py) — needs SM >= 100
  • quantize_finegrained_fp8_moe (quantize_moe.py) — needs SM >= 90
  • match_fp8_moe_pattern / fuse_fp8_moe (fused_moe.py) — needs SM >= 89
  • match_nvfp4_moe_pattern / fuse_nvfp4_moe (fused_moe.py) — needs SM >= 100
  • fuse_finegrained_fp8_moe (fused_moe.py) — needs SM >= 90
  • quantize_mxfp4_moe (mxfp4_moe.py) — verify SM floor

Proposed Solution

Option A: Per-transform guard (as shown in example above)

Each transform's _apply() method checks get_sm_version() and returns TransformInfo(skipped=True) with a warning when the GPU is incompatible.

Pros: Simple, explicit, easy to understand per-transform.
Cons: Repetitive boilerplate across 25+ transforms; easy to forget on new transforms.

Option B: Declarative SM requirement on TransformConfig / BaseTransform

Add an optional min_sm: int | None field to BaseTransform or TransformConfig. The base class apply() method checks SM before calling _apply() and auto-skips with a standard warning.

class FuseNVFP4LinearConfig(TransformConfig):
    min_sm: int = 100  # Blackwell
    ...

Pros: DRY, impossible to forget, consistent skip/warning behavior.
Cons: Slightly more abstraction; SM requirement must be statically known.

Recommendation

Option B is preferred for maintainability. The SM floor is a static property of the quantization format and fits naturally as a declarative config field. Individual transforms can still override _apply() for more nuanced checks (e.g., fine-grained FP8's SM 90 vs SM 120 code paths).

Acceptance Criteria

  • All NVFP4 transforms (linear, MOE, SwiGLU, fusion) skip gracefully on SM < 100
  • All FP8 transforms skip gracefully on SM < 89
  • All fine-grained FP8 transforms skip gracefully on SM < 90
  • A clear warning message is logged indicating which transform was skipped and why
  • New transforms get SM validation automatically (Option B) or via documented pattern (Option A)
  • Unit tests verify skip behavior with mocked SM versions

References

  • get_sm_version() utility: tensorrt_llm/_utils.py
  • SM-dependent code paths already exist in custom_ops/quantization/torch_quant.py and custom_ops/fused_moe/trtllm_moe.py

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.