Skip to content

[ROCm] Enable MXFP4 LoRA support on MI355X and add CDNA4 device IDs - #37268

Open
ChuanLi1101 wants to merge 1 commit into
vllm-project:mainfrom
ChuanLi1101:feat/rocm-mxfp4-lora-mi355x
Open

ChuanLi1101 wants to merge 1 commit into
vllm-project:mainfrom
ChuanLi1101:feat/rocm-mxfp4-lora-mi355x

Conversation

@ChuanLi1101

@ChuanLi1101 ChuanLi1101 commented Mar 17, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Enable MXFP4 + LoRA on ROCm: Extend get_mxfp4_backend_with_lora() to support AMD ROCm platforms (MI300X/MI325X/MI355X) via the Triton backend. Previously, this function returned Mxfp4Backend.NONE for all non-CUDA platforms, blocking MXFP4 quantized MoE models from using LoRA adapters on ROCm.
  • Add CDNA4 device identification: Add MI350X (PCI ID 0x75a0) and MI355X (PCI ID 0x75a3) to _ROCM_DEVICE_ID_NAME_MAP for proper GPU identification on CDNA4 hardware.
  • Add unit tests: Tests for MXFP4 backend selection on ROCm, device ID mapping, and GCN arch capability parsing.

Motivation

MXFP4 is a key quantization format for AMD's MI355X (CDNA4/gfx950) GPU, enabling higher throughput for large MoE models like DeepSeek-V3 and MiniMax-M2. However, LoRA fine-tuning support was blocked on ROCm because the MXFP4 backend selection did not consider ROCm platforms when LoRA is enabled.

The Triton backend already supports LoRA through UnfusedOAITritonExperts and works on ROCm when triton_kernels is available. The CK backend is monolithic and does not support the modular kernel path required for LoRA, so Triton is the correct choice for this use case.

Changes

File Change
vllm/model_executor/layers/quantization/mxfp4.py Add ROCm Triton backend path in get_mxfp4_backend_with_lora()
vllm/platforms/rocm.py Add MI350X/MI355X PCI device IDs to name map
tests/quantization/test_mxfp4_rocm_backend.py New unit tests for ROCm MXFP4 backend selection

Test Plan

  • Unit tests for get_mxfp4_backend_with_lora() with ROCm platform mocking
  • Unit tests for get_mxfp4_backend() with LoRA flag on ROCm
  • Unit tests for MI350X/MI355X device ID mapping
  • Unit tests for gfx950 capability parsing
  • Verified CUDA paths remain unchanged (Triton and Marlin backends)
  • E2E test: MXFP4 MoE model + LoRA on MI355X (requires hardware CI)

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request enables MXFP4 LoRA support on ROCm platforms by leveraging the Triton backend, which is a valuable enhancement. The changes are well-structured, with clear logic in get_mxfp4_backend_with_lora to handle the ROCm platform specifically. The addition of new CDNA4 device IDs for MI350X and MI355X is also correctly implemented. The accompanying unit tests are thorough, covering the new backend selection logic, device ID mappings, and capability parsing, which ensures the changes are robust and do not introduce regressions. Overall, this is a high-quality contribution.

@mergify

mergify Bot commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

Hi @ChuanLi1101, the pre-commit checks have failed. Please run:

uv pip install pre-commit>=4.5.1
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Tip

Is mypy failing?
mypy is run differently in CI. If the failure is related to this check, please use the following command to run it locally:
# For mypy (substitute "3.10" with the failing version if needed)
pre-commit run --hook-stage manual mypy-3.10

@mergify

mergify Bot commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @ChuanLi1101.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Mar 21, 2026
Enable MXFP4 quantized MoE models to use LoRA adapters on AMD ROCm
platforms (MI300X/MI325X/MI355X) via the Triton backend.

Previously, select_mxfp4_moe_backend() raised NotImplementedError for
all non-CUDA platforms when LoRA is enabled, blocking MXFP4+LoRA on
ROCm. The Triton backend already supports LoRA through
UnfusedOAITritonExperts and works on ROCm when triton_kernels is
available.

Also adds MI350X (0x75a0) and MI355X (0x75a3) PCI device IDs to the
ROCm device name map for proper GPU identification on CDNA4 hardware.

Made-with: Cursor
Signed-off-by: Li <chuali@amd.com>
@ChuanLi1101
ChuanLi1101 force-pushed the feat/rocm-mxfp4-lora-mi355x branch from ba36963 to 001eda3 Compare April 1, 2026 08:16
@mergify mergify Bot removed the needs-rebase label Apr 1, 2026
@ChuanLi1101

Copy link
Copy Markdown
Collaborator Author

Rebased onto latest main and resolved merge conflicts.

The main codebase refactored \Mxfp4Backend\ enum and backend selection logic from \mxfp4.py\ into the new \oracle/mxfp4.py\ module (\select_mxfp4_moe_backend\ / \Mxfp4MoeBackend). Updated the ROCm LoRA support to work with the new architecture — the change now adds ROCm handling in the LoRA path of \select_mxfp4_moe_backend(), using \TRITON_UNFUSED\ backend when \ riton_kernels\ is available. Tests have been rewritten to match the new API.

@AndreasKaratzas @tjtanaa @gshtras — could you take a look when you get a chance? This enables MXFP4+LoRA on ROCm (MI300X/MI325X/MI355X) via the Triton backend. Thanks!

@mergify

mergify Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Hi @ChuanLi1101, the pre-commit checks have failed. Please run:

uv pip install pre-commit>=4.5.1
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Tip

Is mypy failing?
mypy is run differently in CI. If the failure is related to this check, please use the following command to run it locally:
# For mypy (substitute "3.10" with the failing version if needed)
pre-commit run --hook-stage manual mypy-3.10

Comment on lines +3 to +132
"""Tests for MXFP4 backend selection on ROCm platforms."""

from unittest.mock import MagicMock, patch

import pytest

from vllm.model_executor.layers.fused_moe.oracle.mxfp4 import (
Mxfp4MoeBackend,
select_mxfp4_moe_backend,
)


def _make_lora_config():
"""Create a mock FusedMoEConfig with LoRA enabled."""
config = MagicMock()
config.is_lora_enabled = True
config.moe_backend = "auto"
config.moe_parallel_config.use_batched_activation_format = False
return config


def _make_non_lora_config():
"""Create a mock FusedMoEConfig with LoRA disabled."""
config = MagicMock()
config.is_lora_enabled = False
config.moe_backend = "auto"
config.moe_parallel_config.use_batched_activation_format = False
return config


class TestMxfp4LoraBackendROCm:
"""Test MXFP4 backend selection for LoRA on ROCm platforms."""

@patch(
"vllm.model_executor.layers.fused_moe.oracle.mxfp4.current_platform"
)
@patch(
"vllm.model_executor.layers.fused_moe.oracle.mxfp4.has_triton_kernels"
)
def test_rocm_lora_with_triton_kernels(
self, mock_has_triton, mock_platform
):
mock_platform.is_rocm.return_value = True
mock_platform.is_cuda.return_value = False
mock_platform.get_device_capability.return_value = (9, 5)
mock_has_triton.return_value = True

config = _make_lora_config()
backend, experts_cls = select_mxfp4_moe_backend(config)
assert backend == Mxfp4MoeBackend.TRITON_UNFUSED

@patch(
"vllm.model_executor.layers.fused_moe.oracle.mxfp4.current_platform"
)
@patch(
"vllm.model_executor.layers.fused_moe.oracle.mxfp4.has_triton_kernels"
)
def test_rocm_lora_without_triton_raises(
self, mock_has_triton, mock_platform
):
mock_platform.is_rocm.return_value = True
mock_platform.is_cuda.return_value = False
mock_platform.get_device_capability.return_value = (9, 5)
mock_has_triton.return_value = False

config = _make_lora_config()
with pytest.raises(NotImplementedError, match="triton_kernels"):
select_mxfp4_moe_backend(config)

@patch(
"vllm.model_executor.layers.fused_moe.oracle.mxfp4.current_platform"
)
@patch(
"vllm.model_executor.layers.fused_moe.oracle.mxfp4.has_triton_kernels"
)
def test_non_cuda_non_rocm_lora_raises(
self, mock_has_triton, mock_platform
):
mock_platform.is_rocm.return_value = False
mock_platform.is_cuda.return_value = False
mock_platform.get_device_capability.return_value = (0, 0)
mock_has_triton.return_value = False

config = _make_lora_config()
with pytest.raises(
NotImplementedError, match="only supported on CUDA and ROCm"
):
select_mxfp4_moe_backend(config)


class TestROCmDeviceIdNameMap:
"""Test that MI350X/MI355X device IDs are properly mapped."""

def test_mi350x_device_id(self):
from vllm.platforms.rocm import _ROCM_DEVICE_ID_NAME_MAP

assert "0x75a0" in _ROCM_DEVICE_ID_NAME_MAP
assert _ROCM_DEVICE_ID_NAME_MAP["0x75a0"] == "AMD_Instinct_MI350X"

def test_mi355x_device_id(self):
from vllm.platforms.rocm import _ROCM_DEVICE_ID_NAME_MAP

assert "0x75a3" in _ROCM_DEVICE_ID_NAME_MAP
assert _ROCM_DEVICE_ID_NAME_MAP["0x75a3"] == "AMD_Instinct_MI355X"

def test_existing_mi300x_unchanged(self):
from vllm.platforms.rocm import _ROCM_DEVICE_ID_NAME_MAP

assert _ROCM_DEVICE_ID_NAME_MAP["0x74a1"] == "AMD_Instinct_MI300X"

def test_existing_mi325x_unchanged(self):
from vllm.platforms.rocm import _ROCM_DEVICE_ID_NAME_MAP

assert _ROCM_DEVICE_ID_NAME_MAP["0x74a5"] == "AMD_Instinct_MI325X"


class TestGfx950CapabilityParsing:
"""Test that gfx950 arch string is correctly parsed."""

def test_gfx950_capability(self):
from vllm.platforms.rocm import _capability_from_gcn_arch

result = _capability_from_gcn_arch("gfx950")
assert result == (9, 5)

def test_gfx942_capability(self):
from vllm.platforms.rocm import _capability_from_gcn_arch

result = _capability_from_gcn_arch("gfx942")
assert result == (9, 4)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is the purpose of this test? I don't see any mxfp4 testing here with lora. There should be accuracy tests here. But also mxfp4 test for ROCm should include a much broader set of tests, which I think is out of the scope of this PR. So probably best if you find the lora or quantization test file that fits best this new feature and add an accuracy test there instead of this new file.

@mergify

mergify Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @ChuanLi1101.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@github-actions

Copy link
Copy Markdown

This pull request has been automatically marked as stale because it has not had any activity within 90 days. It will be automatically closed if no further activity occurs within 30 days. Leave a comment if you feel this pull request should remain open. Thank you!

@github-actions github-actions Bot added the stale Over 90 days of inactivity label Aug 22, 2026
@AndreasKaratzas

Copy link
Copy Markdown
Member

@ChuanLi1101 I think we can close this as Bowen is taking care of reworking the quant paths here:

@github-actions github-actions Bot added unstale Recieved activity after being labelled stale and removed stale Over 90 days of inactivity labels Aug 25, 2026
@mergify

mergify Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @ChuanLi1101.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

This branch has not been deployed

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

Labels

needs-rebase rocm Related to AMD ROCm unstale Recieved activity after being labelled stale

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

2 participants