SPIR-V on ROCm: A Portable IR for AMD GPUs — ROCm Blogs
Skip to main content
Back to top
Ctrl+K
ROCm blogs
SPIR-V on ROCm: A Portable IR for AMD GPUs
Contents
SPIR-V on ROCm: A Portable IR for AMD GPUs#
July 20, 2026 by Hongxia Yang, Alexandru Voicu, Mark Searles, Jeff Daily, Lakhinder Walia, James Brodman, Peng Sun.
22 min read. | 5412 total words.
Software tools & optimizations
Compiler, PyTorch, AI/ML
Developers, Systems, AI, HPC
Hongxia Yang, Alexandru Voicu, Mark Searles, Jeff Daily, Lakhinder Walia, James Brodman, Peng Sun
English
-->
ROCm has compiled GPU code ahead of time (AOT), once per target<br>architecture, since its first release. CPU software shifted to runtime<br>just-in-time (JIT) compilation in the 1990s — Java HotSpot, V8, LuaJIT,<br>.NET RyuJIT, PyPy — once the matrix of target × workload × deployment<br>became too large to enumerate at build time. With its adoption of SPIR-V,<br>ROCm now brings the compile-once, specialize-on-device model to AMD GPUs.
This post covers what SPIR-V is, why AMD adopted it, the compilation<br>model and its trade-offs, and a reproducible single-kernel HIP example<br>with measurements on MI350X (CDNA4), spot-checked on RX 9070 XT (RDNA4).<br>A FAQ and status section at the end cover common questions and what is<br>shipping, in flight, and not yet solved.
A follow-up post will discuss a real-world SPIR-V deployment at scale:<br>PyTorch.
The Problem: AOT-per-Target Does Not Scale#
A ROCm application today is built by enumerating every GPU architecture<br>it needs to run on. For each entry, the device-side code of every HIP<br>translation unit is compiled once and the resulting code objects are<br>bundled into a fat binary. With N targets, that is N device code<br>objects per TU — a substantial multiplier on build time and on the<br>size of the shipped artifacts. Host code is compiled once. Beyond the<br>build system, developers themselves must be aware of every target: writing<br>per-arch #ifdef blocks, testing on each architecture, and updating those<br>lists whenever a new GPU ships.
An instance of this is PyTorch’s PYTORCH_ROCM_ARCH,<br>whose upstream default is currently fifteen targets spanning CDNA1/2/3<br>and RDNA2/3/4:
PYTORCH_ROCM_ARCH="gfx900;gfx906;gfx908;gfx90a;gfx942;gfx950;\<br>gfx1030;gfx1100;gfx1101;gfx1102;gfx1103;\<br>gfx1150;gfx1151;gfx1200;gfx1201"
Each entry expands into one --offload-arch=gfxXXX flag, and the<br>consequences cascade regardless of whether the build sits inside PyTorch,<br>a HIP application, or a downstream library:
Long build times. The device pipeline runs N times.
Large fat binaries. N native code objects per TU, all shipped together.
Build systems must enumerate every target. The framework has to<br>maintain an explicit arch list and update it whenever a new GPU ships.
No forward compatibility. A binary built today cannot run on a GPU<br>architecture that did not exist at build time — a new ASIC requires a<br>new build.
This was tolerable when GPU architectures evolved on multi-year cycles. It<br>strains under the current pace: new ASICs land frequently, cloud fleets mix<br>generations, and frameworks ship precompiled artifacts that have to cover<br>all of them.
The shift SPIR-V enables is from ahead-of-time compilation per target<br>to compile once, just-in-time specialize on device. Figure 1 illustrates<br>the difference.
What SPIR-V Is, and What AMD’s Flavor Adds#
SPIR-V — Standard Portable Intermediate Representation – V — is a binary,<br>SSA-based IR defined by the Khronos Group. It originated as a<br>portable IR for GPU programs (graphics shaders for Vulkan, compute kernels<br>for OpenCL) and has since broadened into a standardized, toolchain-friendly<br>IR that can be lowered to a vendor’s concrete ISA at a later stage. We treat<br>SPIR-V here as an intermediate representation (IR), not a source language.
AMD’s AMDGCN-flavored SPIR-V carries vendor-specific constructs through two<br>mechanisms. Inline assembly is encoded via the SPV_INTEL_inline_assembly<br>extension, which wraps the raw assembly string in the SPIR-V binary. AMDGCN<br>intrinsics and target-specific builtins, by contrast, are handled transparently<br>by the compiler and translator pipeline: they are lowered to function<br>declarations and reverse-translated accordingly, with no extension required.<br>Together, these mechanisms preserve expressiveness that a strictly-portable IR<br>would lose.<br>This variant is exposed through the amdgcnspirv abstract target in<br>LLVM/Clang.
In ROCm, AMDGCN-flavored SPIR-V serves three roles:
A target-agnostic GPU IR that can be produced once.
A late-binding abstraction over AMD GPU architectures.
A way to decouple compilation from device-specific codegen .
How AMD Got SPIR-V into Upstream LLVM#
The amdgcnspirv target is not a fork. It is a multi-year effort, in<br>upstream LLVM, that incrementally turned generic SPIR-V into a usable<br>flavor of AMDGCN. The relevant pieces, listed in roughly the order<br>they were built because each layer depends on the one before it:
Target plumbing. A new Clang target...