Skip to content

fix(w4a): gate the FP8/NVFP4 kernels off ROCm - #3554

Merged
Qubitium merged 2 commits into
mainfrom
fix/w4a-rocm-gating
Nov 6, 2026
Merged

Qubitium merged 2 commits into
mainfrom
fix/w4a-rocm-gating

Conversation

@ZX-ModelCloud

Copy link
Copy Markdown
Collaborator

Problem

tests/kernels/test_w4a_cache_lifecycle.py fails 7/53 on gfx1201:

RuntimeError: PassManager::run failed
LLVM ERROR: Unsupported DotOp found when converting TritonGPU to LLVM   #ttg.nvidia_mma

The W4A kernels are NVIDIA-only by construction:

  • _grouped_fp8_gemm uses NVMMADistributedLayout and Gluon mma_v2, so Triton emits ttg.nvidia_mma and the AMD backend aborts.
  • The INT4->E4M3 packers round through PTX (cvt.rn.satfinite.e4m3x2.f32).

torch.cuda.is_available() is true under HIP, and get_device_capability() reports a synthetic (12, 0) on gfx1201 that clears the FP8 floor, so neither can gate this backend.

Second, worse failure mode: the INT4->E4M3 unpack kernel does compile on gfx1201, but it returns wrong codes (all zeros against the Torch decode). That is silent accuracy corruption, not a crash — it must be refused, not left to fail at runtime.

Fix

#3550 already resolves cuda/cuda:N/ordinals to DEVICE.ROCM on HIP and enforces SUPPORTS_DEVICES, so kernel selection no longer dispatches to W4A. Two paths still reached the kernels:

Path Before After
validate(device=None) (AUTO) accepted a ROCm host; first forward aborted inside Triton resolves the target with normalize_device(), returns an actionable NotImplementedError
direct construction / packer entry points unpacked to wrong codes silently native_fp8_gemm_error(), unpack_int4_to_e4m3(), fp8_pack(), nvfp4_pack_and_swizzle() refuse HIP up front

Per .agents/skills/gptqmodel-device-families/SKILL.md, the decision comes from the resolved family (normalize_device()), not a scattered IS_ROCM check, and the kernels keep their static SUPPORTS_DEVICES = [DEVICE.CUDA] declaration.

W4A tests now gate on w4a_kernels_available(), which compares the resolved default family against the kernel's own SUPPORTS_DEVICES, instead of torch.cuda.is_available(). _capability() no longer probes an NVIDIA SM version on AMD.

No weight representation or lifetime changes: the guards only reject a platform, and the lifecycle tests still assert that no expanded weight is retained.

Tests

AMD Radeon AI PRO R9700 (gfx1201, HIP 7.16.26385, Triton 3.9.0, HIP_VISIBLE_DEVICES=2,3):

test_w4a_cache_lifecycle.py   before: 7 failed, 46 passed
                              after:  47 passed, 9 skipped   (every skip is an NVIDIA-only case)

Final set on this branch — test_w4a_cache_lifecycle.py, test_w4a_activation_policy.py, test_rocm_kernel_contracts.py, test_w4afp8_gb10.py, test_w4a_nvfp4_gb10.py, test_w4a_stream.py:

474 passed, 74 skipped, 0 failed

Live checks on the same host: w4a_kernels_available() -> False; W4AFP8Linear.validate(device=None) -> (False, NotImplementedError); select_quant_linear(GPTQ_W4AFP8) raises a ValueError whose text is the platform reason. ruff clean, 119-column limit respected.

NVIDIA behaviour is unchanged by construction (normalize_device(0) is DEVICE.CUDA, which is in SUPPORTS_DEVICES), but this branch was not executed on an NVIDIA GPU.

Note on main

tests/test_kernel_device_error_reason.py::test_rocm_rejection_reason_survives is red on origin/main at 5cf7b7bd: #3550 changed the message to "is CUDA-only and does not run on ROCm" but the assertion still expects "not supported on ROCm". I reproduced it on a pristine origin/main worktree; it is unrelated to this branch and not fixed here.

The W4A kernels cannot be lowered by the AMD backend:

- `_grouped_fp8_gemm` uses `NVMMADistributedLayout` and Gluon `mma_v2`, so
  Triton emits `ttg.nvidia_mma` and gfx1201 aborts with "Unsupported DotOp
  found when converting TritonGPU to LLVM" (`PassManager::run failed`).
- The INT4->E4M3 packers round through the PTX instruction
  `cvt.rn.satfinite.e4m3x2.f32`. The unpack kernel does compile on gfx1201,
  but it returns wrong codes (zeros against the Torch decode), so leaving it
  to fail at runtime would corrupt accuracy silently instead.

`torch.cuda.is_available()` is true under HIP and `get_device_capability()`
reports a synthetic `(12, 0)` on gfx1201 that clears the FP8 floor, so neither
can gate this backend.

Kernel selection is already covered: `SUPPORTS_DEVICES` declares these kernels
NVIDIA-only and the device family resolves through `normalize_device()`. Two
paths still reached the kernels:

- `validate(device=None)`: the AUTO path skips the base family check, so a
  ROCm host was accepted and the first forward aborted inside Triton.
- Direct construction and the packer entry points, including the unpack that
  returns wrong codes instead of failing.

So `validate()` now resolves the requested target with `normalize_device()`
(the centralized HIP resolver) and reports an actionable error, and
`native_fp8_gemm_error()`, `unpack_int4_to_e4m3()`, `fp8_pack()` and
`nvfp4_pack_and_swizzle()` refuse HIP up front.

W4A tests gate on `w4a_kernels_available()`, which compares the resolved
default family against the kernel's own `SUPPORTS_DEVICES` declaration,
instead of `torch.cuda.is_available()`.

No weight representation or lifetime changes: the guards only reject a
platform, and the lifecycle tests still assert that no expanded weight is
retained.

Measured on AMD Radeon AI PRO R9700 (gfx1201, HIP 7.16.26385, Triton 3.9.0,
GPUs 2/3) before the fix: 7 failed / 46 passed in
test_w4a_cache_lifecycle.py. After: 47 passed / 9 skipped, where every skip
is an NVIDIA-only kernel case.
@Qubitium
Qubitium merged commit 62f27b1 into main Nov 6, 2026
6 checks passed
@Qubitium
Qubitium deleted the fix/w4a-rocm-gating branch November 6, 2026 06:33
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