# SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project from typing import Any import torch from vllm.logger import init_logger from vllm.platforms import current_platform from vllm.utils.import_utils import has_triton_kernels from vllm.utils.torch_utils import direct_register_custom_op, is_torch_equal_or_newer logger = init_logger(__name__) # CK's pre-compiled MXFP4 MoE GEMM kernel instances require the # intermediate_size (after TP split) to be a multiple of this value. # This arises from FP4 packing (2 values per byte) combined with CK # tile size constraints. When violated, AITER raises: # "device_gemm ... does not support this GEMM problem". CK_MXFP4_MOE_DIM_ALIGNMENT = 256 def should_use_cdna4_mx_scale_swizzle() -> bool: """Whether to use the CDNA4 swizzled scale layout for mxfp4 on gfx950. CDNA4 swizzle requires BLOCK_K%256==0; at TP>=4 the A8W4 dispatch picks BK<256 tiles for the smaller per-rank shapes, so swizzle must be off. Used by both the weight-load swizzle in `_swizzle_mxfp4` and the kernel-argument gate in `aiter_mxfp4_w4a8_moe`; they must agree. """ from vllm.distributed import get_tensor_model_parallel_world_size from vllm.platforms.rocm import on_gfx950 return on_gfx950() and get_tensor_model_parallel_world_size() <= 2 def _swizzle_mxfp4(quant_tensor, scale, num_warps=8): """weight swizzle for mxfp4 moe, used for OAI mxfp4 kernel""" assert has_triton_kernels() import triton_kernels.matmul_ogs_details.opt_flags as opt_flags from triton_kernels.numerics import InFlexData from triton_kernels.tensor import FP4, convert_layout, wrap_torch_tensor from triton_kernels.tensor_details import layout from triton_kernels.tensor_details.layout import StridedLayout value_layout_opts: dict[str, Any] = {} scale_layout_opts: dict[str, Any] = {} if ( current_platform.is_cuda() and current_platform.is_device_capability(90) and not is_torch_equal_or_newer("2.8.1") ): logger.warning_once( "Mxfp4 on hopper is running on torch < 2.8.1, " "this cause swizling to be disabled, which may " "cause performance degradation. Please upgrade to torch nightly" ) value_layout = StridedLayout scale_layout = StridedLayout elif current_platform.is_rocm(): value_layout = StridedLayout if should_use_cdna4_mx_scale_swizzle(): try: # triton < 3.6 from triton_kernels.tensor_details.layout import GFX950MXScaleLayout scale_layout = GFX950MXScaleLayout except ImportError: # triton >= 3.6 from triton_kernels.tensor_details.layout import CDNA4MXScaleLayout scale_layout = CDNA4MXScaleLayout else: scale_layout = StridedLayout else: value_layout, value_layout_opts = layout.make_default_matmul_mxfp4_w_layout( mx_axis=1 ) scale_layout, scale_layout_opts = ( layout.make_default_matmul_mxfp4_w_scale_layout( mx_axis=1, num_warps=num_warps ) ) if current_platform.is_cuda(): if current_platform.is_device_capability(90): constraints = { "split_k": 1, } opt_flags.update_opt_flags_constraints(constraints) # Patches #47303: pad K (num scale groups) to 0 mod 4 # TODO: Remove once we upgrade to Triton 3.8.0+ kernels if scale.numel() > 0: K = scale.shape[-1] pad_k = -K % 4 scale = torch.nn.functional.pad(scale, (0, pad_k)) elif current_platform.is_device_capability_family(100): constraints = { "is_persistent": True, "epilogue_subtile": 1, } opt_flags.update_opt_flags_constraints(constraints) # transpose the tensor so that the quantization axis is on dim1 quant_tensor = quant_tensor.transpose(-2, -1) scale = scale.transpose(-2, -1) quant_tensor = convert_layout( wrap_torch_tensor(quant_tensor, dtype=FP4), value_layout, **value_layout_opts ) scale = convert_layout(wrap_torch_tensor(scale), scale_layout, **scale_layout_opts) return quant_tensor, InFlexData(), scale def _dequant_mxfp4( x: torch.Tensor, scale: torch.Tensor, float_dtype: torch.dtype ) -> torch.Tensor: try: from quark.torch.kernel import mx except ImportError as err: raise ImportError( "The package `amd-quark` is required to use " "MX-FP4 models. Please install it with `pip install " "amd-quark`." ) from err return mx.dq_mxfp4(x, scale, float_dtype) def _dequant_mxfp4_fake( x: torch.Tensor, scale: torch.Tensor, float_dtype: torch.dtype ) -> torch.Tensor: return torch.empty( (*x.shape[:-1], x.shape[-1] * 2), dtype=float_dtype, device=x.device ) def _quant_dequant_mxfp4( x: torch.Tensor, scale_calculation_mode: str = "even" ) -> torch.Tensor: try: from quark.torch.kernel import mx except ImportError as err: raise ImportError( "The package `amd-quark` is required to use " "MX-FP4 models. Please install it with `pip install " "amd-quark`." ) from err return mx.qdq_mxfp4(x, scale_calculation_mode) def _quant_dequant_mxfp4_fake( x: torch.Tensor, scale_calculation_mode: str = "even" ) -> torch.Tensor: return torch.empty_like(x) # Protect these operations into a torch custom op to avoid errors as # torch._dynamo.exc.Unsupported: Attempted to call function marked as skipped # Explanation: Dynamo does not know how to trace the builtin # `kernel_ext.PyCapsule.dq_uint8_mxfp4_to_half.` This function is either a # Python builtin (e.g. _warnings.warn) or a third-party C/C++ Python # extension (perhaps created with pybind). # TODO: Make sure there is no way to avoid having these functions # marked as skipped by dynamo. try: direct_register_custom_op( op_name="dequant_mxfp4", op_func=_dequant_mxfp4, fake_impl=_dequant_mxfp4_fake, ) dequant_mxfp4 = torch.ops.vllm.dequant_mxfp4 except AttributeError as error: raise error try: direct_register_custom_op( op_name="quant_dequant_mxfp4", op_func=_quant_dequant_mxfp4, fake_impl=_quant_dequant_mxfp4_fake, ) quant_dequant_mxfp4 = torch.ops.vllm.quant_dequant_mxfp4 except AttributeError as error: raise error def xpu_mxfp4_quantize(x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: return torch.ops.vllm.xpu_mxfp4_quantize(x)