docs/ROCM.md

Use the ROCm backend

The ROCm backend runs on integrated and discrete AMD GPUs. Contributors have run the HIP gates on gfx1100, gfx1103, gfx1151, gfx1200, and gfx1201. Gemma 3 1B IT is token-identical to two vLLM ROCm oracles on gfx1200. Qwen3 0.6B has one deterministic, version-sensitive near tie on that device.

Qwen3.5 0.8B runs through the native GDN stack, but its CPU and ROCm outputs still have an open correctness gap. The Gemma 4 FP8 MoE and SharedK WMMA path has runtime evidence on two gfx1201 GPUs. This repository has only CPU link coverage for that path and no matched vLLM ROCm performance result.

Build for ROCm#

Use a release build. A build without optimization can trigger a ROCm host-call teardown race on gfx1100.

cmake -S . -B build-hip \
  -DVLLM_CPP_HIP=ON \
  -DCMAKE_BUILD_TYPE=Release \
  -DROCM_PATH=/opt/rocm
cmake --build build-hip -j
ctest --test-dir build-hip -R 'rocm|cross_device' --output-on-failure

Set ROCM_PATH to your ROCm or TheRock installation prefix. CMake derives the compiler root and HIP flags from that prefix. See the build guide for all ROCm build options.

The TheRock nightly on gfx1103 can print Status: SUCCESS! and then hang during process teardown. This is a known deadlock in libamdhip64.so.7. Record the ROCm build when you encounter it.

Select the device#

The CLI does not accept --device rocm. On a system with AMD hardware and no CUDA device, the default --device auto selects ROCm through the platform priority order.

VT_OP_PROVIDER_STATS=1 ./build-hip/examples/vllm-cli \
  --model /path/to/a/small/dense/model \
  --prompt "The capital of France is" \
  --max-tokens 8 \
  --temperature 0

Run the same command with --device cpu to compare greedy tokens. VT_OP_PROVIDER_STATS=1 reports native operations and CPU fallbacks.

Select Q8_K activation quantization#

VT_ROCM_Q8K_BLOCK selects the Q8_K activation quantizer used by ROCm dense and grouped keep-quant operations. When it is unset, a queue device that resolves to gfx1100, including a valid feature-suffix spelling, uses the cooperative arm. Unset uses the legacy arm on gfx1200, gfx1201, an unknown architecture, or architecture-resolution failure. This policy does not imply runtime validation on gfx1200 or gfx1201.

The parser is strict. Set VT_ROCM_Q8K_BLOCK=0 for the permanent legacy A/B arm, or set VT_ROCM_Q8K_BLOCK=1 to force the cooperative diagnostic arm regardless of architecture. Explicit 1 is diagnostic-only outside validated gfx1100; it does not make a correctness, performance, or default claim for another architecture. Any value other than 0 or 1 refuses the operation instead of choosing an arm silently.

Understand fallback behavior#

An integrated GPU can use the CPU reference tier when the backend reports unified memory. A missing native operation then runs through its CPU implementation.

The backend reports unified memory when it allocates with hipMallocManaged, or when the device reports Integrated and PageableMemoryAccess together. It allocates with hipMallocManaged only on a managed-capable integrated device that also reports PageableMemoryAccess = 1. A device that reports PageableMemoryAccess = 0 cannot take a recoverable page fault, so a migratable allocation on it faults the GPU under load (#2511 measured 17 faults in 21 runs, against 0 in 21 with plain hipMalloc).

Strix Halo (gfx1151) and Radeon 780M (gfx1103) report PageableMemoryAccess = 0, so they get plain hipMalloc and no reference tier. They run a model when ROCm registers every operation the model needs, the same rule a discrete card follows. GetOp reports an error for a missing operation, and on these boards that error also names the attribute, the issue, and VT_ROCM_MANAGED_ALLOC=1, which restores the previous managed behaviour at that risk. See ENVIRONMENT.md for the knob.

A discrete GPU cannot use the reference tier. The model runs only when ROCm registers every operation that the model needs. GetOp reports an error for a missing operation.

Do not use a run with CPU fallbacks as a performance result. GetReferenceTierHits() must return 0 for a valid measurement.

Current backend surface#

SeamFileCurrent state
Device enuminclude/vt/device.hCompiled and routed through the shared device switch
Architecture mappinginclude/vt/rocm/rocm_arch.hUnit-tested gfx name mapping, and the unit-tested allocator/host-addressability policy table
Runtime backendsrc/vt/rocm/rocm_backend.hipRuns on five gfx architectures; managed allocation is measured on gfx1151 and narrowed to devices that can take a recoverable page fault (#2511)
Operation tablesrc/vt/rocm/rocm_ops.hipOne Registrar that names every OpId this backend serves natively. Recount it with the command below rather than quoting a number from here
Kernelssrc/vt/rocm/Dense, GDN, attention, sampling, and the contributor-tested Gemma 4 FP8 MoE path
Platformsrc/vllm/platforms/rocm.cppRuntime-verified on five gfx architectures
Attentionsrc/vt/rocm/rocm_paged_attn.hipNative paged attention and the SharedK WMMA prefill path
Grouped BF16 expertssrc/vt/rocm/rocm_moe_grouped_bf16.hipNative gate/up, weighted down, and preweighted combine through shared MoE operations; component tests pass on gfx1100. The Qwen3 MoE production token gate remains open (spec )
BuildCMakeLists.txtVLLM_CPP_HIP configuration and build verified on five architectures
Teststests/vt/test_rocm_backend.cppRuntime cases pass; the allocation-path case asserts the #2511 coupling on the board it runs on

Recount registered operations before you quote the total. The scan must not depend on where the argument list wraps: several calls in rocm_ops.hip break the line after RegisterOp(, and a line-based grep never sees RegisterOp( and OpId:: together on those (#2573). Read the whole file and match across newlines:

grep -rhoz 'RegisterOp([[:space:]]*OpId::[A-Za-z0-9_]*[[:space:]]*,[[:space:]]*DeviceType::kROCM' \
    src/vt/rocm/ | tr '\0' '\n' | grep -o 'OpId::[A-Za-z0-9_]*' | sort -u | wc -l

Substitute the DeviceType:: value for another backend. Naming the device in the pattern is what keeps the count answering the question a reader asked – the previous command counted every RegisterOp(OpId:: line in the directory regardless of which device it registered for.

Hardware notes#

HardwareArchitectureMemoryCurrent path
Strix Halogfx1151Unified, PageableMemoryAccess = 0Plain hipMalloc, no reference tier: run a model whose every operation ROCm registers natively (#2511)
Radeon 780Mgfx1103Shared, PageableMemoryAccess = 0Same path as Strix Halo, with a smaller model
Radeon 7900 XTXgfx1100DiscreteNative kernels are required; this class can also host the vLLM ROCm oracle
Radeon R9700gfx1201DiscreteContributor-tested Gemma 4 FP8 MoE and SharedK WMMA path
Radeon RX 9060 XTgfx1200DiscreteGemma 3 1B IT oracle parity; Qwen3 0.6B has a recorded near tie

Features that need a separate AMD implementation#

Do not hipify NVIDIA-specific implementations:

  • NVFP4, Marlin, and FP4 tensor-core kernels;
  • CUTLASS FlashAttention and scaled matrix multiplication kernels;
  • vendored Triton AOT cubins;
  • NCCL transport;
  • cuBLASLt plan caches.

Use the AMD equivalents where applicable. These include MXFP4, Composable Kernel or AITER, RCCL, and hipBLASLt.

For correctness evidence and implementation history, see the ROCm backend specification , the unified-memory decision , and the gfx1200 correctness record .