[clang] [llvm] [TargetParser] Add a list of Intel GPUs, and use it in offload-arch (PR #222072)
Nikita Kornev via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 21 10:50:57 PDT 2026
https://github.com/KornevNikita updated https://github.com/llvm/llvm-project/pull/222072
>From 330342afd07913a9e351ea7604ef44eed0820cca Mon Sep 17 00:00:00 2001
From: "Kornev, Nikita" <nikita.kornev at intel.com>
Date: Tue, 8 Sep 2026 18:34:21 +0200
Subject: [PATCH 01/11] [TargetParser] Add a list of Intel GPUs, and use it in
offload-arch
Currently offload-arch prints Intel GPU names which are not a legal parameter
for --offload-arch, e.g. "Intel(R) Data Center GPU Max 1100".
Print an architecture name instead, e.g. "xe-pvc". The driver reports a GPU IP
version, the GMDID, for every device. Add a table that maps a GMDID to a name,
and look the device up in it.
The table goes in llvm/TargetParser, next to the other GPU lists, because
other tools need it too. Some of them are LLVM libraries, which cannot
include a clang header.
Every row of IntelGPUTargetParser.def holds three things: a name that
--offload-arch accepts, the GMDID that the device reports, and the IGCA
(Intel Graphics Compute Architecture) level, which is the virtual
architecture that the compiler targets. A few names, such as xe-dg2,
cover a whole product line. No device reports a GMDID for those, so
offload-arch never prints them.
A device that is not in the table is reported as an error. Making up a
name from its GMDID would not help, because the compiler would not know
which IGCA level to compile for.
The header declares only the functions that offload-arch needs. More will
follow when something needs them.
Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>
---
clang/tools/offload-arch/CMakeLists.txt | 2 +-
clang/tools/offload-arch/LevelZeroArch.cpp | 51 +++++++++-
clang/unittests/offload-arch/CMakeLists.txt | 2 +
.../offload-arch/OffloadArchTest.cpp | 47 ++++++++++
.../TargetParser/IntelGPUTargetParser.def | 94 +++++++++++++++++++
.../llvm/TargetParser/IntelGPUTargetParser.h | 67 +++++++++++++
llvm/include/module.modulemap | 1 +
llvm/lib/TargetParser/CMakeLists.txt | 1 +
.../lib/TargetParser/IntelGPUTargetParser.cpp | 60 ++++++++++++
llvm/unittests/TargetParser/CMakeLists.txt | 1 +
.../TargetParser/IntelGPUTargetParserTest.cpp | 76 +++++++++++++++
11 files changed, 399 insertions(+), 3 deletions(-)
create mode 100644 llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
create mode 100644 llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
create mode 100644 llvm/lib/TargetParser/IntelGPUTargetParser.cpp
create mode 100644 llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
diff --git a/clang/tools/offload-arch/CMakeLists.txt b/clang/tools/offload-arch/CMakeLists.txt
index f7d7012cf7272e..8e37e3d2ae5db9 100644
--- a/clang/tools/offload-arch/CMakeLists.txt
+++ b/clang/tools/offload-arch/CMakeLists.txt
@@ -1,4 +1,4 @@
-set(LLVM_LINK_COMPONENTS Support)
+set(LLVM_LINK_COMPONENTS Support TargetParser)
add_clang_tool(offload-arch OffloadArch.cpp NVPTXArch.cpp AMDGPUArchByKFD.cpp
AMDGPUArchByHIP.cpp LevelZeroArch.cpp)
diff --git a/clang/tools/offload-arch/LevelZeroArch.cpp b/clang/tools/offload-arch/LevelZeroArch.cpp
index 47d80aa813085b..77716e2d52f66a 100644
--- a/clang/tools/offload-arch/LevelZeroArch.cpp
+++ b/clang/tools/offload-arch/LevelZeroArch.cpp
@@ -14,6 +14,7 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Error.h"
+#include "llvm/TargetParser/IntelGPUTargetParser.h"
#include <cstdio>
#define ZE_MAX_DEVICE_NAME 256
@@ -30,6 +31,7 @@ enum ze_result_t {
enum ze_structure_type_t {
ZE_STRUCTURE_TYPE_INIT_DRIVER_TYPE_DESC = 0x00020021,
ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES = 0x3,
+ ZE_STRUCTURE_TYPE_DEVICE_IP_VERSION_EXT = 0x1000f,
ZE_STRUCTURE_TYPE_FORCE_UINT32 = 0x7fffffff
};
@@ -72,6 +74,13 @@ struct ze_device_properties_t {
char name[ZE_MAX_DEVICE_NAME];
};
+// Chained onto ze_device_properties_t::pNext to request the device IP version.
+struct ze_device_ip_version_ext_t {
+ ze_structure_type_t stype;
+ const void *pNext;
+ uint32_t ipVersion;
+};
+
ze_result_t zeInitDrivers(uint32_t *pCount, ze_driver_handle_t *phDrivers,
ze_init_driver_type_desc_t *desc);
ze_result_t zeDeviceGet(ze_driver_handle_t hDriver, uint32_t *pCount,
@@ -148,6 +157,13 @@ static bool loadLevelZero() {
} \
} while (0)
+// Translate a GMDID into an architecture name that is a legal --offload-arch
+// parameter, or "" if this build does not know the device.
+StringRef getIntelGPUArchName(uint32_t IPVersion) {
+ return IntelGPU::getArchName(
+ IntelGPU::getKindForGMDID(IntelGPU::decodeGMDID(IPVersion)));
+}
+
int printGPUsByLevelZero() {
if (!loadLevelZero())
return 1;
@@ -173,11 +189,42 @@ int printGPUsByLevelZero() {
CALL_ZE_AND_CHECK(zeDeviceGet, Driver, &DeviceCount, Devices.data());
for (auto Device : Devices) {
+ ze_device_ip_version_ext_t IPVersion = {};
+ IPVersion.stype = ZE_STRUCTURE_TYPE_DEVICE_IP_VERSION_EXT;
+ IPVersion.pNext = nullptr;
+
ze_device_properties_t DeviceProperties = {};
DeviceProperties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES;
- DeviceProperties.pNext = nullptr;
+ DeviceProperties.pNext = &IPVersion;
CALL_ZE_AND_CHECK(zeDeviceGetProperties, Device, &DeviceProperties);
- llvm::outs() << DeviceProperties.name << '\n';
+
+ // A driver that does not support the extension leaves the chained
+ // structure untouched, in which case there is no architecture to name.
+ if (IPVersion.ipVersion == 0) {
+ if (Verbose)
+ llvm::errs() << "Unable to query the IP version of device '"
+ << DeviceProperties.name << "'\n";
+ continue;
+ }
+
+ if (Verbose)
+ llvm::errs() << "Found device '" << DeviceProperties.name << "'\n";
+
+ // Naming an unknown device after its GMDID would print something that
+ // --offload-arch cannot accept, because this build knows no IGCA level to
+ // compile for. Report it instead, spelling out the GMDID so that the
+ // device can be identified.
+ StringRef Arch = getIntelGPUArchName(IPVersion.ipVersion);
+ if (Arch.empty()) {
+ llvm::errs() << "Unknown Intel GPU '" << DeviceProperties.name
+ << "', which reports the architecture "
+ << IntelGPU::getNumericArchName(
+ IntelGPU::decodeGMDID(IPVersion.ipVersion))
+ << "\n";
+ return 1;
+ }
+
+ llvm::outs() << Arch << '\n';
}
}
diff --git a/clang/unittests/offload-arch/CMakeLists.txt b/clang/unittests/offload-arch/CMakeLists.txt
index 8d9cbf5c602056..523b5f33ed6b31 100644
--- a/clang/unittests/offload-arch/CMakeLists.txt
+++ b/clang/unittests/offload-arch/CMakeLists.txt
@@ -1,6 +1,7 @@
set(OffloadArchTestSources
OffloadArchTest.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../tools/offload-arch/AMDGPUArchByKFD.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../tools/offload-arch/LevelZeroArch.cpp
)
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
@@ -16,4 +17,5 @@ add_distinct_clang_unittest(OffloadArchTests
LLVMTestingSupport
LLVM_COMPONENTS
Support
+ TargetParser
)
diff --git a/clang/unittests/offload-arch/OffloadArchTest.cpp b/clang/unittests/offload-arch/OffloadArchTest.cpp
index 5f5e49f5c72ccc..dbdcf52703dc31 100644
--- a/clang/unittests/offload-arch/OffloadArchTest.cpp
+++ b/clang/unittests/offload-arch/OffloadArchTest.cpp
@@ -26,6 +26,9 @@ llvm::SmallVector<std::string, 8> getCandidateBinPaths(llvm::StringRef ExeDir);
// Defined in AMDGPUArchByKFD.cpp (non-static, compiled into this test).
int printGPUsByKFD(llvm::StringRef NodePath);
+// Defined in LevelZeroArch.cpp.
+llvm::StringRef getIntelGPUArchName(uint32_t IPVersion);
+
using namespace llvm;
cl::opt<bool> Verbose("offload-arch-test-verbose", cl::Hidden, cl::init(false));
@@ -207,3 +210,47 @@ TEST(KFDTopology, MultipleGPUsArePrintedInNodeOrder) {
EXPECT_EQ(printGPUsByKFDCapturingStdout(Dir.path(), Output), 0);
EXPECT_EQ(Output, "gfx1101\ngfx90a\n");
}
+
+// --- getIntelGPUArchName ---
+
+namespace {
+// Build a GMDID the way the Level Zero driver reports it.
+constexpr uint32_t gmdid(uint32_t Architecture, uint32_t Release,
+ uint32_t Revision) {
+ return (Architecture << 22) | (Release << 14) | Revision;
+}
+} // namespace
+
+TEST(IntelGPUArchName, KnownArchitecturesGetAFriendlyName) {
+ EXPECT_EQ(getIntelGPUArchName(gmdid(12, 60, 7)), "xe-pvc");
+ EXPECT_EQ(getIntelGPUArchName(gmdid(20, 1, 4)), "xe-bmg-g21");
+ EXPECT_EQ(getIntelGPUArchName(gmdid(35, 10, 0)), "xe-nvl-p");
+ EXPECT_EQ(getIntelGPUArchName(gmdid(12, 0, 0)), "xe-tgllp");
+}
+
+// When several devices share an architecture and a release, the first one
+// listed in IntelGPUTargetParser.def names the whole group.
+TEST(IntelGPUArchName, FirstNameOfAGroupWins) {
+ EXPECT_EQ(getIntelGPUArchName(gmdid(30, 5, 0)), "xe-nvl-u");
+ EXPECT_EQ(getIntelGPUArchName(gmdid(12, 55, 0)), "xe-acm-g10");
+}
+
+// The revision is not part of the lookup: every stepping of an architecture
+// shares one name.
+TEST(IntelGPUArchName, RevisionDoesNotAffectTheName) {
+ EXPECT_EQ(getIntelGPUArchName(gmdid(12, 60, 0)), "xe-pvc");
+ EXPECT_EQ(getIntelGPUArchName(gmdid(12, 60, 63)), "xe-pvc");
+}
+
+// An architecture that is not in the table has no name at all. Naming it after
+// its GMDID would print something that --offload-arch cannot accept, so the
+// utility reports it as an error instead.
+TEST(IntelGPUArchName, UnknownArchitecturesHaveNoName) {
+ EXPECT_TRUE(getIntelGPUArchName(gmdid(40, 11, 0)).empty());
+ EXPECT_TRUE(getIntelGPUArchName(gmdid(12, 99, 3)).empty());
+}
+
+// Pre-Xe devices report a GMDID too, and none of them are in the table.
+TEST(IntelGPUArchName, LegacyArchitecture) {
+ EXPECT_TRUE(getIntelGPUArchName(gmdid(9, 0, 9)).empty());
+}
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
new file mode 100644
index 00000000000000..7af65e547e93d4
--- /dev/null
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
@@ -0,0 +1,94 @@
+//===--- IntelGPUTargetParser.def - Intel GPU target data ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is the single source of truth for the Intel GPU list. Each row
+// describes one architecture name that --offload-arch accepts, along with the
+// IGCA (Intel Graphics Compute Architecture) level the compiler targets when
+// the user names it. Adding a device is a single row here.
+//
+// INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX)
+// NAME - Human-friendly device name, e.g. "xe-cri".
+// KIND - GPUKind enumerator suffix; the enumerator is GK_<KIND>.
+// ARCHITECTURE - Architecture component of the GMDID the device reports.
+// RELEASE - Release component of the GMDID the device reports.
+// IGCA_LEVEL - Numeric IGCA level.
+// IGCA_SUFFIX - Token naming the feature sets that the level comprises:
+// Core (no suffix), Compute ("c"), Render ("r"),
+// ComputeExact ("ca") or RenderExact ("ra"). A consumer maps
+// the token onto an enumerator of its own.
+//
+// INTEL_GPU_COMPAT(NAME, KIND, IGCA_LEVEL, IGCA_SUFFIX)
+// A compatibility name that covers several releases, e.g. "xe-dg2", which
+// covers every xe-dg2-* and xe-acm-* platform. These names have no GMDID
+// of their own, so no device reports one and the offload-arch utility
+// never prints one, but they are legal --offload-arch values. The columns
+// mean the same as above.
+//
+// The revision (stepping) component of the GMDID is deliberately not part of
+// the key: every stepping of a release shares one name and one IGCA level.
+//
+// Several devices can share an architecture and a release. The rows are ordered
+// so that the name to print for such a group comes first.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef INTEL_GPU
+#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX)
+#endif
+
+#ifndef INTEL_GPU_COMPAT
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_LEVEL, IGCA_SUFFIX)
+#endif
+
+INTEL_GPU("xe-cri", XE_CRI, 35, 11, 60, Compute)
+INTEL_GPU("xe-nvl-p", XE_NVL_P, 35, 10, 60, Render)
+INTEL_GPU("xe-nvl-u", XE_NVL_U, 30, 5, 60, Render)
+INTEL_GPU("xe-nvl-h", XE_NVL_H, 30, 5, 60, Render)
+INTEL_GPU("xe-nvl-s", XE_NVL_S, 30, 4, 60, Render)
+INTEL_GPU("xe-nvl-hx", XE_NVL_HX, 30, 4, 60, Render)
+INTEL_GPU("xe-nvl-ul", XE_NVL_UL, 30, 4, 60, Render)
+INTEL_GPU("xe-wcl", XE_WCL, 30, 3, 50, Render)
+INTEL_GPU("xe-ptl-u", XE_PTL_U, 30, 1, 50, Render)
+INTEL_GPU("xe-ptl-h", XE_PTL_H, 30, 0, 50, Render)
+INTEL_GPU("xe-lnl-m", XE_LNL_M, 20, 4, 40, Render)
+INTEL_GPU("xe-bmg-g31", XE_BMG_G31, 20, 2, 40, Render)
+INTEL_GPU("xe-bmg-g21", XE_BMG_G21, 20, 1, 40, Render)
+INTEL_GPU("xe-arl-h", XE_ARL_H, 12, 74, 35, Render)
+INTEL_GPU("xe-mtl-h", XE_MTL_H, 12, 71, 30, Render)
+INTEL_GPU("xe-mtl-u", XE_MTL_U, 12, 70, 30, Render)
+INTEL_GPU("xe-arl-u", XE_ARL_U, 12, 70, 30, Render)
+INTEL_GPU("xe-arl-s", XE_ARL_S, 12, 70, 30, Render)
+INTEL_GPU("xe-pvc-vg", XE_PVC_VG, 12, 61, 20, ComputeExact)
+INTEL_GPU("xe-pvc", XE_PVC, 12, 60, 20, ComputeExact)
+INTEL_GPU("xe-pvc-sdv", XE_PVC_SDV, 12, 60, 20, ComputeExact)
+INTEL_GPU("xe-acm-g12", XE_ACM_G12, 12, 57, 15, RenderExact)
+INTEL_GPU("xe-dg2-g12", XE_DG2_G12, 12, 57, 15, RenderExact)
+INTEL_GPU("xe-acm-g11", XE_ACM_G11, 12, 56, 15, RenderExact)
+INTEL_GPU("xe-dg2-g11", XE_DG2_G11, 12, 56, 15, RenderExact)
+INTEL_GPU("xe-ats-m75", XE_ATS_M75, 12, 56, 15, RenderExact)
+INTEL_GPU("xe-acm-g10", XE_ACM_G10, 12, 55, 15, RenderExact)
+INTEL_GPU("xe-dg2-g10", XE_DG2_G10, 12, 55, 15, RenderExact)
+INTEL_GPU("xe-ats-m150", XE_ATS_M150, 12, 55, 15, RenderExact)
+INTEL_GPU("xe-dg1", XE_DG1, 12, 10, 10, Render)
+INTEL_GPU("xe-adl-n", XE_ADL_N, 12, 4, 10, Render)
+INTEL_GPU("xe-adl-p", XE_ADL_P, 12, 3, 10, Render)
+INTEL_GPU("xe-rpl-p", XE_RPL_P, 12, 3, 10, Render)
+INTEL_GPU("xe-adl-s", XE_ADL_S, 12, 2, 10, Render)
+INTEL_GPU("xe-rpl-s", XE_RPL_S, 12, 2, 10, Render)
+INTEL_GPU("xe-rkl", XE_RKL, 12, 1, 10, Render)
+INTEL_GPU("xe-tgllp", XE_TGLLP, 12, 0, 10, Render)
+INTEL_GPU("xe-tgl", XE_TGL, 12, 0, 10, Render)
+
+// Compatibility names, which cover a group of platforms
+INTEL_GPU_COMPAT("xe-ptl", XE_PTL, 50, Render)
+INTEL_GPU_COMPAT("xe-bmg", XE_BMG, 40, Render)
+INTEL_GPU_COMPAT("xe-mtl", XE_MTL, 30, Render)
+INTEL_GPU_COMPAT("xe-dg2", XE_DG2, 15, RenderExact)
+
+#undef INTEL_GPU
+#undef INTEL_GPU_COMPAT
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
new file mode 100644
index 00000000000000..d0ed929a5b9149
--- /dev/null
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -0,0 +1,67 @@
+//===-- IntelGPUTargetParser.h - Parser for Intel GPU targets ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides access to the Intel GPU list in IntelGPUTargetParser.def.
+// Only what is needed to name the device a driver reports is declared here; the
+// table itself carries more, and a consumer that needs the rest either declares
+// it here as well or expands the table directly.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TARGETPARSER_INTELGPUTARGETPARSER_H
+#define LLVM_TARGETPARSER_INTELGPUTARGETPARSER_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Compiler.h"
+#include <cstdint>
+#include <string>
+
+namespace llvm {
+namespace IntelGPU {
+
+/// The Intel GPU architecture names this build knows, covering both physical
+/// devices and the compatibility names that stand for a whole product line.
+enum GPUKind : uint16_t {
+ GK_NONE = 0,
+#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX) \
+ GK_##KIND,
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_LEVEL, IGCA_SUFFIX) GK_##KIND,
+#include "llvm/TargetParser/IntelGPUTargetParser.def"
+};
+
+/// The components that a GPU IP version, the "GMDID", packs into one 32-bit
+/// value. The revision identifies the hardware stepping.
+struct GMDID {
+ unsigned Architecture = 0;
+ unsigned Release = 0;
+ unsigned Revision = 0;
+};
+
+/// Split the GPU IP version \p IPVersion, as reported by the driver, into its
+/// components.
+LLVM_ABI GMDID decodeGMDID(uint32_t IPVersion);
+
+/// The device whose GMDID has the same architecture and release as \p ID, or
+/// GK_NONE if this build knows no such device. The revision is ignored: as far
+/// as the compiler is concerned, every stepping of a release is one device.
+/// When several devices share an architecture and a release, the first one
+/// listed in IntelGPUTargetParser.def names the group and is returned.
+LLVM_ABI GPUKind getKindForGMDID(GMDID ID);
+
+/// The human-friendly name of \p Kind, e.g. "xe-pvc", or "" for GK_NONE.
+LLVM_ABI StringRef getArchName(GPUKind Kind);
+
+/// Spell \p ID the way an architecture name spells a GMDID, e.g. "xe_35.11.0".
+/// Every device has such a name, including one that is not in the table, which
+/// makes this the only way to name a device this build does not know.
+LLVM_ABI std::string getNumericArchName(GMDID ID);
+
+} // namespace IntelGPU
+} // namespace llvm
+
+#endif // LLVM_TARGETPARSER_INTELGPUTARGETPARSER_H
diff --git a/llvm/include/module.modulemap b/llvm/include/module.modulemap
index 69836bf2e3158d..03cab392cb619f 100644
--- a/llvm/include/module.modulemap
+++ b/llvm/include/module.modulemap
@@ -438,6 +438,7 @@ module LLVM_Utils {
// These are intended for textual inclusion.
textual header "llvm/TargetParser/ARMTargetParser.def"
textual header "llvm/TargetParser/CSKYTargetParser.def"
+ textual header "llvm/TargetParser/IntelGPUTargetParser.def"
textual header "llvm/TargetParser/X86TargetParser.def"
textual header "llvm/TargetParser/LoongArchTargetParser.def"
textual header "llvm/TargetParser/NVPTXTargetParser.def"
diff --git a/llvm/lib/TargetParser/CMakeLists.txt b/llvm/lib/TargetParser/CMakeLists.txt
index cb45571583d23d..27d6511ec08491 100644
--- a/llvm/lib/TargetParser/CMakeLists.txt
+++ b/llvm/lib/TargetParser/CMakeLists.txt
@@ -21,6 +21,7 @@ add_llvm_component_library(LLVMTargetParser
AVRTargetParser.cpp
CSKYTargetParser.cpp
Host.cpp
+ IntelGPUTargetParser.cpp
LoongArchTargetParser.cpp
NVPTXTargetParser.cpp
PPCTargetParser.cpp
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
new file mode 100644
index 00000000000000..33e8c93b326124
--- /dev/null
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -0,0 +1,60 @@
+//===-- IntelGPUTargetParser - Parser for Intel GPU targets ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a target parser for the Intel GPU list.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/TargetParser/IntelGPUTargetParser.h"
+#include "llvm/ADT/Twine.h"
+
+using namespace llvm;
+using namespace IntelGPU;
+
+// A GMDID packs the architecture, release and revision of the GPU IP.
+static constexpr uint32_t GMDIDArchitectureShift = 22;
+static constexpr uint32_t GMDIDReleaseShift = 14;
+static constexpr uint32_t GMDIDReleaseMask = 0xff;
+static constexpr uint32_t GMDIDRevisionMask = 0x3f;
+
+GMDID llvm::IntelGPU::decodeGMDID(uint32_t IPVersion) {
+ return {IPVersion >> GMDIDArchitectureShift,
+ (IPVersion >> GMDIDReleaseShift) & GMDIDReleaseMask,
+ IPVersion & GMDIDRevisionMask};
+}
+
+GPUKind llvm::IntelGPU::getKindForGMDID(GMDID ID) {
+ // Only INTEL_GPU rows are expanded, so a compatibility name can never match.
+ // The rows are ordered so that the first match in a group names the group.
+#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX) \
+ if (ID.Architecture == ARCHITECTURE && ID.Release == RELEASE) \
+ return GK_##KIND;
+#include "llvm/TargetParser/IntelGPUTargetParser.def"
+ return GK_NONE;
+}
+
+StringRef llvm::IntelGPU::getArchName(GPUKind Kind) {
+ switch (Kind) {
+ case GK_NONE:
+ return "";
+#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX) \
+ case GK_##KIND: \
+ return NAME;
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_LEVEL, IGCA_SUFFIX) \
+ case GK_##KIND: \
+ return NAME;
+#include "llvm/TargetParser/IntelGPUTargetParser.def"
+ }
+ llvm_unreachable("invalid Intel GPU GPUKind");
+}
+
+std::string llvm::IntelGPU::getNumericArchName(GMDID ID) {
+ return ("xe_" + Twine(ID.Architecture) + "." + Twine(ID.Release) + "." +
+ Twine(ID.Revision))
+ .str();
+}
diff --git a/llvm/unittests/TargetParser/CMakeLists.txt b/llvm/unittests/TargetParser/CMakeLists.txt
index 9ef532603517bc..65b2ff085a2993 100644
--- a/llvm/unittests/TargetParser/CMakeLists.txt
+++ b/llvm/unittests/TargetParser/CMakeLists.txt
@@ -7,6 +7,7 @@ add_llvm_unittest(TargetParserTests
AtomicScopeTest.cpp
CSKYTargetParserTest.cpp
Host.cpp
+ IntelGPUTargetParserTest.cpp
NVPTXTargetParserTest.cpp
RISCVISAInfoTest.cpp
RISCVTargetParserTest.cpp
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
new file mode 100644
index 00000000000000..a80aa80e5dab7c
--- /dev/null
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -0,0 +1,76 @@
+//===------- IntelGPUTargetParserTest.cpp - Intel GPU Target Parser -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/TargetParser/IntelGPUTargetParser.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+// Build a GMDID the way the Level Zero driver reports it.
+constexpr uint32_t gmdid(uint32_t Architecture, uint32_t Release,
+ uint32_t Revision) {
+ return (Architecture << 22) | (Release << 14) | Revision;
+}
+
+TEST(IntelGPUTargetParserTest, DecodeGMDID) {
+ IntelGPU::GMDID ID = IntelGPU::decodeGMDID(gmdid(35, 11, 7));
+ EXPECT_EQ(ID.Architecture, 35u);
+ EXPECT_EQ(ID.Release, 11u);
+ EXPECT_EQ(ID.Revision, 7u);
+
+ // The revision occupies the low 6 bits and the release the 8 above it, so
+ // neither can bleed into the architecture.
+ ID = IntelGPU::decodeGMDID(gmdid(12, 0xff, 0x3f));
+ EXPECT_EQ(ID.Architecture, 12u);
+ EXPECT_EQ(ID.Release, 0xffu);
+ EXPECT_EQ(ID.Revision, 0x3fu);
+}
+
+TEST(IntelGPUTargetParserTest, KindForGMDID) {
+ EXPECT_EQ(IntelGPU::getKindForGMDID({12, 60, 7}), IntelGPU::GK_XE_PVC);
+ EXPECT_EQ(IntelGPU::getKindForGMDID({35, 11, 0}), IntelGPU::GK_XE_CRI);
+ // The revision is not part of the key: every stepping of a release is the
+ // same device.
+ EXPECT_EQ(IntelGPU::getKindForGMDID({12, 60, 0}), IntelGPU::GK_XE_PVC);
+ EXPECT_EQ(IntelGPU::getKindForGMDID({12, 60, 63}), IntelGPU::GK_XE_PVC);
+ // When several devices share an architecture and a release, the first row of
+ // the group wins.
+ EXPECT_EQ(IntelGPU::getKindForGMDID({30, 5, 0}), IntelGPU::GK_XE_NVL_U);
+ EXPECT_EQ(IntelGPU::getKindForGMDID({12, 55, 0}), IntelGPU::GK_XE_ACM_G10);
+ // A device that is not in the table has no kind at all.
+ EXPECT_EQ(IntelGPU::getKindForGMDID({40, 11, 0}), IntelGPU::GK_NONE);
+ EXPECT_EQ(IntelGPU::getKindForGMDID({9, 0, 9}), IntelGPU::GK_NONE);
+}
+
+TEST(IntelGPUTargetParserTest, ArchNames) {
+ EXPECT_EQ(IntelGPU::getArchName(IntelGPU::GK_XE_PVC), "xe-pvc");
+ EXPECT_EQ(IntelGPU::getArchName(IntelGPU::GK_XE_ATS_M150), "xe-ats-m150");
+ EXPECT_EQ(IntelGPU::getArchName(IntelGPU::GK_XE_MTL), "xe-mtl");
+ EXPECT_EQ(IntelGPU::getArchName(IntelGPU::GK_NONE), "");
+}
+
+TEST(IntelGPUTargetParserTest, EveryKindIsNamed) {
+ // A row with no name would make the offload-arch utility print an empty
+ // architecture, so every kind the table declares must have a spelling.
+#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX) \
+ EXPECT_FALSE(IntelGPU::getArchName(IntelGPU::GK_##KIND).empty()) << #KIND;
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_LEVEL, IGCA_SUFFIX) \
+ EXPECT_FALSE(IntelGPU::getArchName(IntelGPU::GK_##KIND).empty()) << #KIND;
+#include "llvm/TargetParser/IntelGPUTargetParser.def"
+}
+
+TEST(IntelGPUTargetParserTest, NumericArchName) {
+ EXPECT_EQ(IntelGPU::getNumericArchName({35, 11, 0}), "xe_35.11.0");
+ EXPECT_EQ(IntelGPU::getNumericArchName({12, 99, 3}), "xe_12.99.3");
+ // Pre-Xe devices report a GMDID too, and none of them are in the table.
+ EXPECT_EQ(IntelGPU::getNumericArchName({9, 0, 9}), "xe_9.0.9");
+}
+
+} // namespace
>From 232df8b794beba4bf2731b570af3ab21e4ce4310 Mon Sep 17 00:00:00 2001
From: "Kornev, Nikita" <nikita.kornev at intel.com>
Date: Fri, 11 Sep 2026 10:47:24 +0200
Subject: [PATCH 02/11] [offload-arch] Name an unknown Intel GPU after its
GMDID
Fold the numeric fallback into getIntelGPUArchName(), so that the GMDID is
decoded once and every device gets a name.
---
clang/tools/offload-arch/LevelZeroArch.cpp | 29 +++++++------------
.../offload-arch/OffloadArchTest.cpp | 15 +++++-----
2 files changed, 17 insertions(+), 27 deletions(-)
diff --git a/clang/tools/offload-arch/LevelZeroArch.cpp b/clang/tools/offload-arch/LevelZeroArch.cpp
index 77716e2d52f66a..a7c58b0efccc61 100644
--- a/clang/tools/offload-arch/LevelZeroArch.cpp
+++ b/clang/tools/offload-arch/LevelZeroArch.cpp
@@ -16,6 +16,7 @@
#include "llvm/Support/Error.h"
#include "llvm/TargetParser/IntelGPUTargetParser.h"
#include <cstdio>
+#include <string>
#define ZE_MAX_DEVICE_NAME 256
#define ZE_MAX_DEVICE_UUID_SIZE 16
@@ -158,10 +159,14 @@ static bool loadLevelZero() {
} while (0)
// Translate a GMDID into an architecture name that is a legal --offload-arch
-// parameter, or "" if this build does not know the device.
-StringRef getIntelGPUArchName(uint32_t IPVersion) {
- return IntelGPU::getArchName(
- IntelGPU::getKindForGMDID(IntelGPU::decodeGMDID(IPVersion)));
+// parameter. A device this build knows no name for is named after its GMDID, so
+// that it is reported like any other one.
+std::string getIntelGPUArchName(uint32_t IPVersion) {
+ IntelGPU::GMDID ID = IntelGPU::decodeGMDID(IPVersion);
+ StringRef Name = IntelGPU::getArchName(IntelGPU::getKindForGMDID(ID));
+ if (!Name.empty())
+ return Name.str();
+ return IntelGPU::getNumericArchName(ID);
}
int printGPUsByLevelZero() {
@@ -210,21 +215,7 @@ int printGPUsByLevelZero() {
if (Verbose)
llvm::errs() << "Found device '" << DeviceProperties.name << "'\n";
- // Naming an unknown device after its GMDID would print something that
- // --offload-arch cannot accept, because this build knows no IGCA level to
- // compile for. Report it instead, spelling out the GMDID so that the
- // device can be identified.
- StringRef Arch = getIntelGPUArchName(IPVersion.ipVersion);
- if (Arch.empty()) {
- llvm::errs() << "Unknown Intel GPU '" << DeviceProperties.name
- << "', which reports the architecture "
- << IntelGPU::getNumericArchName(
- IntelGPU::decodeGMDID(IPVersion.ipVersion))
- << "\n";
- return 1;
- }
-
- llvm::outs() << Arch << '\n';
+ llvm::outs() << getIntelGPUArchName(IPVersion.ipVersion) << '\n';
}
}
diff --git a/clang/unittests/offload-arch/OffloadArchTest.cpp b/clang/unittests/offload-arch/OffloadArchTest.cpp
index dbdcf52703dc31..de9fe398fcdae1 100644
--- a/clang/unittests/offload-arch/OffloadArchTest.cpp
+++ b/clang/unittests/offload-arch/OffloadArchTest.cpp
@@ -27,7 +27,7 @@ llvm::SmallVector<std::string, 8> getCandidateBinPaths(llvm::StringRef ExeDir);
int printGPUsByKFD(llvm::StringRef NodePath);
// Defined in LevelZeroArch.cpp.
-llvm::StringRef getIntelGPUArchName(uint32_t IPVersion);
+std::string getIntelGPUArchName(uint32_t IPVersion);
using namespace llvm;
@@ -242,15 +242,14 @@ TEST(IntelGPUArchName, RevisionDoesNotAffectTheName) {
EXPECT_EQ(getIntelGPUArchName(gmdid(12, 60, 63)), "xe-pvc");
}
-// An architecture that is not in the table has no name at all. Naming it after
-// its GMDID would print something that --offload-arch cannot accept, so the
-// utility reports it as an error instead.
-TEST(IntelGPUArchName, UnknownArchitecturesHaveNoName) {
- EXPECT_TRUE(getIntelGPUArchName(gmdid(40, 11, 0)).empty());
- EXPECT_TRUE(getIntelGPUArchName(gmdid(12, 99, 3)).empty());
+// An architecture that is not in the table still has to be named, so that a
+// newer device is usable with a compiler that predates it.
+TEST(IntelGPUArchName, UnknownArchitecturesGetANumericName) {
+ EXPECT_EQ(getIntelGPUArchName(gmdid(40, 11, 0)), "xe_40.11.0");
+ EXPECT_EQ(getIntelGPUArchName(gmdid(12, 99, 3)), "xe_12.99.3");
}
// Pre-Xe devices report a GMDID too, and none of them are in the table.
TEST(IntelGPUArchName, LegacyArchitecture) {
- EXPECT_TRUE(getIntelGPUArchName(gmdid(9, 0, 9)).empty());
+ EXPECT_EQ(getIntelGPUArchName(gmdid(9, 0, 9)), "xe_9.0.9");
}
>From fbe6ef57c6eb5edff0e9e1bd29edbd327b95e3f2 Mon Sep 17 00:00:00 2001
From: "Kornev, Nikita" <nikita.kornev at intel.com>
Date: Wed, 16 Sep 2026 13:01:19 +0200
Subject: [PATCH 03/11] Add a more specific error message
---
clang/tools/offload-arch/LevelZeroArch.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/tools/offload-arch/LevelZeroArch.cpp b/clang/tools/offload-arch/LevelZeroArch.cpp
index a7c58b0efccc61..e49239bb7ab606 100644
--- a/clang/tools/offload-arch/LevelZeroArch.cpp
+++ b/clang/tools/offload-arch/LevelZeroArch.cpp
@@ -203,12 +203,12 @@ int printGPUsByLevelZero() {
DeviceProperties.pNext = &IPVersion;
CALL_ZE_AND_CHECK(zeDeviceGetProperties, Device, &DeviceProperties);
- // A driver that does not support the extension leaves the chained
- // structure untouched, in which case there is no architecture to name.
if (IPVersion.ipVersion == 0) {
if (Verbose)
- llvm::errs() << "Unable to query the IP version of device '"
- << DeviceProperties.name << "'\n";
+ llvm::errs() << "warning: skipping device '" << DeviceProperties.name
+ << "': this version of the Level Zero driver does not "
+ "support ZE_STRUCTURE_TYPE_DEVICE_IP_VERSION_EXT, so "
+ "the device architecture cannot be determined\n";
continue;
}
>From 0aef5a2a9d2ce89b8d7ac1b196b45384b810291c Mon Sep 17 00:00:00 2001
From: "Kornev, Nikita" <nikita.kornev at intel.com>
Date: Wed, 16 Sep 2026 13:36:41 +0200
Subject: [PATCH 04/11] cosmetics
---
clang/tools/offload-arch/LevelZeroArch.cpp | 3 +--
.../llvm/TargetParser/IntelGPUTargetParser.h | 25 ++++++++-----------
.../lib/TargetParser/IntelGPUTargetParser.cpp | 10 ++++----
.../TargetParser/IntelGPUTargetParserTest.cpp | 2 +-
4 files changed, 18 insertions(+), 22 deletions(-)
diff --git a/clang/tools/offload-arch/LevelZeroArch.cpp b/clang/tools/offload-arch/LevelZeroArch.cpp
index e49239bb7ab606..ba8cdf1fbf060c 100644
--- a/clang/tools/offload-arch/LevelZeroArch.cpp
+++ b/clang/tools/offload-arch/LevelZeroArch.cpp
@@ -159,8 +159,7 @@ static bool loadLevelZero() {
} while (0)
// Translate a GMDID into an architecture name that is a legal --offload-arch
-// parameter. A device this build knows no name for is named after its GMDID, so
-// that it is reported like any other one.
+// parameter. A device that has no name in the table is named after its GMDID.
std::string getIntelGPUArchName(uint32_t IPVersion) {
IntelGPU::GMDID ID = IntelGPU::decodeGMDID(IPVersion);
StringRef Name = IntelGPU::getArchName(IntelGPU::getKindForGMDID(ID));
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index d0ed929a5b9149..78a79b2d9f0f9a 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -24,8 +24,8 @@
namespace llvm {
namespace IntelGPU {
-/// The Intel GPU architecture names this build knows, covering both physical
-/// devices and the compatibility names that stand for a whole product line.
+/// Intel GPU architecture names, covering both physical devices and the
+/// compatibility names that stand for a whole product line.
enum GPUKind : uint16_t {
GK_NONE = 0,
#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX) \
@@ -42,23 +42,20 @@ struct GMDID {
unsigned Revision = 0;
};
-/// Split the GPU IP version \p IPVersion, as reported by the driver, into its
-/// components.
-LLVM_ABI GMDID decodeGMDID(uint32_t IPVersion);
+/// Split the \p GPUIPVersion, as reported by the driver, into its components.
+LLVM_ABI GMDID decodeGMDID(uint32_t GPUIPVersion);
-/// The device whose GMDID has the same architecture and release as \p ID, or
-/// GK_NONE if this build knows no such device. The revision is ignored: as far
-/// as the compiler is concerned, every stepping of a release is one device.
-/// When several devices share an architecture and a release, the first one
-/// listed in IntelGPUTargetParser.def names the group and is returned.
+/// Return the kind matching the architecture and release of \p ID, or GK_NONE
+/// if the table lists no such device. The revision is ignored: every stepping
+/// of a release is one device. If several rows match, the first one in
+/// IntelGPUTargetParser.def wins.
LLVM_ABI GPUKind getKindForGMDID(GMDID ID);
-/// The human-friendly name of \p Kind, e.g. "xe-pvc", or "" for GK_NONE.
+/// Return the human-friendly name of \p Kind, e.g. "xe-pvc", or "" for GK_NONE.
LLVM_ABI StringRef getArchName(GPUKind Kind);
-/// Spell \p ID the way an architecture name spells a GMDID, e.g. "xe_35.11.0".
-/// Every device has such a name, including one that is not in the table, which
-/// makes this the only way to name a device this build does not know.
+/// Return the numeric name of \p ID, e.g. "xe_35.11.0", which every device has,
+/// even one that the table does not list.
LLVM_ABI std::string getNumericArchName(GMDID ID);
} // namespace IntelGPU
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index 33e8c93b326124..976719936824c3 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -1,4 +1,4 @@
-//===-- IntelGPUTargetParser - Parser for Intel GPU targets ----*- C++ -*-===//
+//===-- IntelGPUTargetParser - Parser for Intel GPU targets ---------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -22,10 +22,10 @@ static constexpr uint32_t GMDIDReleaseShift = 14;
static constexpr uint32_t GMDIDReleaseMask = 0xff;
static constexpr uint32_t GMDIDRevisionMask = 0x3f;
-GMDID llvm::IntelGPU::decodeGMDID(uint32_t IPVersion) {
- return {IPVersion >> GMDIDArchitectureShift,
- (IPVersion >> GMDIDReleaseShift) & GMDIDReleaseMask,
- IPVersion & GMDIDRevisionMask};
+GMDID llvm::IntelGPU::decodeGMDID(uint32_t GPUIPVersion) {
+ return {GPUIPVersion >> GMDIDArchitectureShift,
+ (GPUIPVersion >> GMDIDReleaseShift) & GMDIDReleaseMask,
+ GPUIPVersion & GMDIDRevisionMask};
}
GPUKind llvm::IntelGPU::getKindForGMDID(GMDID ID) {
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index a80aa80e5dab7c..a6af20b9651963 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -1,4 +1,4 @@
-//===------- IntelGPUTargetParserTest.cpp - Intel GPU Target Parser -------===//
+//===-- IntelGPUTargetParserTest.cpp - Intel GPU Target Parser Test -------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
>From 40477eab5ff994719a413a81dc83f07bea85a0e4 Mon Sep 17 00:00:00 2001
From: "Kornev, Nikita" <nikita.kornev at intel.com>
Date: Wed, 16 Sep 2026 14:25:53 +0200
Subject: [PATCH 05/11] Use igca target instead of igca level
---
.../llvm/TargetParser/IntelGPUTargetParser.def | 18 +++++++++---------
.../llvm/TargetParser/IntelGPUTargetParser.h | 4 ++--
llvm/lib/TargetParser/IntelGPUTargetParser.cpp | 6 +++---
.../TargetParser/IntelGPUTargetParserTest.cpp | 4 ++--
4 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
index 7af65e547e93d4..c712e90249002e 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
@@ -8,21 +8,21 @@
//
// This file is the single source of truth for the Intel GPU list. Each row
// describes one architecture name that --offload-arch accepts, along with the
-// IGCA (Intel Graphics Compute Architecture) level the compiler targets when
-// the user names it. Adding a device is a single row here.
+// IGCA (Intel Graphics Compute Architecture) target the compiler compiles for
+// when the user names it. Adding a device is a single row here.
//
-// INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX)
+// INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX)
// NAME - Human-friendly device name, e.g. "xe-cri".
// KIND - GPUKind enumerator suffix; the enumerator is GK_<KIND>.
// ARCHITECTURE - Architecture component of the GMDID the device reports.
// RELEASE - Release component of the GMDID the device reports.
-// IGCA_LEVEL - Numeric IGCA level.
-// IGCA_SUFFIX - Token naming the feature sets that the level comprises:
+// IGCA_TARGET - Numeric IGCA target.
+// IGCA_SUFFIX - Token naming the feature sets that the target comprises:
// Core (no suffix), Compute ("c"), Render ("r"),
// ComputeExact ("ca") or RenderExact ("ra"). A consumer maps
// the token onto an enumerator of its own.
//
-// INTEL_GPU_COMPAT(NAME, KIND, IGCA_LEVEL, IGCA_SUFFIX)
+// INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_SUFFIX)
// A compatibility name that covers several releases, e.g. "xe-dg2", which
// covers every xe-dg2-* and xe-acm-* platform. These names have no GMDID
// of their own, so no device reports one and the offload-arch utility
@@ -30,7 +30,7 @@
// mean the same as above.
//
// The revision (stepping) component of the GMDID is deliberately not part of
-// the key: every stepping of a release shares one name and one IGCA level.
+// the key: every stepping of a release shares one name and one IGCA target.
//
// Several devices can share an architecture and a release. The rows are ordered
// so that the name to print for such a group comes first.
@@ -38,11 +38,11 @@
//===----------------------------------------------------------------------===//
#ifndef INTEL_GPU
-#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX)
+#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX)
#endif
#ifndef INTEL_GPU_COMPAT
-#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_LEVEL, IGCA_SUFFIX)
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_SUFFIX)
#endif
INTEL_GPU("xe-cri", XE_CRI, 35, 11, 60, Compute)
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index 78a79b2d9f0f9a..ef2aa4dc7aa65f 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -28,9 +28,9 @@ namespace IntelGPU {
/// compatibility names that stand for a whole product line.
enum GPUKind : uint16_t {
GK_NONE = 0,
-#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX) \
+#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
GK_##KIND,
-#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_LEVEL, IGCA_SUFFIX) GK_##KIND,
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_SUFFIX) GK_##KIND,
#include "llvm/TargetParser/IntelGPUTargetParser.def"
};
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index 976719936824c3..6fac7660c41674 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -31,7 +31,7 @@ GMDID llvm::IntelGPU::decodeGMDID(uint32_t GPUIPVersion) {
GPUKind llvm::IntelGPU::getKindForGMDID(GMDID ID) {
// Only INTEL_GPU rows are expanded, so a compatibility name can never match.
// The rows are ordered so that the first match in a group names the group.
-#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX) \
+#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
if (ID.Architecture == ARCHITECTURE && ID.Release == RELEASE) \
return GK_##KIND;
#include "llvm/TargetParser/IntelGPUTargetParser.def"
@@ -42,10 +42,10 @@ StringRef llvm::IntelGPU::getArchName(GPUKind Kind) {
switch (Kind) {
case GK_NONE:
return "";
-#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX) \
+#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
case GK_##KIND: \
return NAME;
-#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_LEVEL, IGCA_SUFFIX) \
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_SUFFIX) \
case GK_##KIND: \
return NAME;
#include "llvm/TargetParser/IntelGPUTargetParser.def"
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index a6af20b9651963..7bf35f5b27b30f 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -59,9 +59,9 @@ TEST(IntelGPUTargetParserTest, ArchNames) {
TEST(IntelGPUTargetParserTest, EveryKindIsNamed) {
// A row with no name would make the offload-arch utility print an empty
// architecture, so every kind the table declares must have a spelling.
-#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_LEVEL, IGCA_SUFFIX) \
+#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
EXPECT_FALSE(IntelGPU::getArchName(IntelGPU::GK_##KIND).empty()) << #KIND;
-#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_LEVEL, IGCA_SUFFIX) \
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_SUFFIX) \
EXPECT_FALSE(IntelGPU::getArchName(IntelGPU::GK_##KIND).empty()) << #KIND;
#include "llvm/TargetParser/IntelGPUTargetParser.def"
}
>From cde1dcac57babc6c86443e757d485e407d0769a8 Mon Sep 17 00:00:00 2001
From: "Kornev, Nikita" <nikita.kornev at intel.com>
Date: Wed, 16 Sep 2026 15:14:31 +0200
Subject: [PATCH 06/11] simplify the new API
---
clang/tools/offload-arch/LevelZeroArch.cpp | 7 +-
.../llvm/TargetParser/IntelGPUTargetParser.h | 32 +++-----
.../lib/TargetParser/IntelGPUTargetParser.cpp | 50 ++++++------
.../TargetParser/IntelGPUTargetParserTest.cpp | 77 +++++++++----------
4 files changed, 70 insertions(+), 96 deletions(-)
diff --git a/clang/tools/offload-arch/LevelZeroArch.cpp b/clang/tools/offload-arch/LevelZeroArch.cpp
index ba8cdf1fbf060c..0fbed1458daf77 100644
--- a/clang/tools/offload-arch/LevelZeroArch.cpp
+++ b/clang/tools/offload-arch/LevelZeroArch.cpp
@@ -161,11 +161,8 @@ static bool loadLevelZero() {
// Translate a GMDID into an architecture name that is a legal --offload-arch
// parameter. A device that has no name in the table is named after its GMDID.
std::string getIntelGPUArchName(uint32_t IPVersion) {
- IntelGPU::GMDID ID = IntelGPU::decodeGMDID(IPVersion);
- StringRef Name = IntelGPU::getArchName(IntelGPU::getKindForGMDID(ID));
- if (!Name.empty())
- return Name.str();
- return IntelGPU::getNumericArchName(ID);
+ StringRef Name = IntelGPU::getArchName(IPVersion);
+ return Name.empty() ? IntelGPU::getNumericArchName(IPVersion) : Name.str();
}
int printGPUsByLevelZero() {
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index ef2aa4dc7aa65f..43c148db1a7e0c 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -26,7 +26,7 @@ namespace IntelGPU {
/// Intel GPU architecture names, covering both physical devices and the
/// compatibility names that stand for a whole product line.
-enum GPUKind : uint16_t {
+enum GPUKind : uint8_t {
GK_NONE = 0,
#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
GK_##KIND,
@@ -34,29 +34,15 @@ enum GPUKind : uint16_t {
#include "llvm/TargetParser/IntelGPUTargetParser.def"
};
-/// The components that a GPU IP version, the "GMDID", packs into one 32-bit
-/// value. The revision identifies the hardware stepping.
-struct GMDID {
- unsigned Architecture = 0;
- unsigned Release = 0;
- unsigned Revision = 0;
-};
-
-/// Split the \p GPUIPVersion, as reported by the driver, into its components.
-LLVM_ABI GMDID decodeGMDID(uint32_t GPUIPVersion);
-
-/// Return the kind matching the architecture and release of \p ID, or GK_NONE
-/// if the table lists no such device. The revision is ignored: every stepping
-/// of a release is one device. If several rows match, the first one in
-/// IntelGPUTargetParser.def wins.
-LLVM_ABI GPUKind getKindForGMDID(GMDID ID);
-
-/// Return the human-friendly name of \p Kind, e.g. "xe-pvc", or "" for GK_NONE.
-LLVM_ABI StringRef getArchName(GPUKind Kind);
+/// Return the name of the device that \p GPUIPVersion, the "GMDID" reported by
+/// the driver, identifies, e.g. "xe-pvc", or "" if the table lists no such
+/// device. The revision is ignored: every stepping of a release is one device.
+/// If several rows match, the first one in IntelGPUTargetParser.def wins.
+LLVM_ABI StringRef getArchName(uint32_t GPUIPVersion);
-/// Return the numeric name of \p ID, e.g. "xe_35.11.0", which every device has,
-/// even one that the table does not list.
-LLVM_ABI std::string getNumericArchName(GMDID ID);
+/// Return the numeric name of \p GPUIPVersion, e.g. "xe_35.11.0", which every
+/// device has, even one that the table does not list.
+LLVM_ABI std::string getNumericArchName(uint32_t GPUIPVersion);
} // namespace IntelGPU
} // namespace llvm
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index 6fac7660c41674..e4b7ac7962cb37 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -16,45 +16,41 @@
using namespace llvm;
using namespace IntelGPU;
-// A GMDID packs the architecture, release and revision of the GPU IP.
+// A GPU IP version, the "GMDID", packs the architecture into the bits above the
+// release, which in turn sits above the revision. The bits between the release
+// and the revision are reserved.
static constexpr uint32_t GMDIDArchitectureShift = 22;
static constexpr uint32_t GMDIDReleaseShift = 14;
static constexpr uint32_t GMDIDReleaseMask = 0xff;
static constexpr uint32_t GMDIDRevisionMask = 0x3f;
-GMDID llvm::IntelGPU::decodeGMDID(uint32_t GPUIPVersion) {
- return {GPUIPVersion >> GMDIDArchitectureShift,
- (GPUIPVersion >> GMDIDReleaseShift) & GMDIDReleaseMask,
- GPUIPVersion & GMDIDRevisionMask};
-}
+// The bits that identify a device: the architecture and the release. Neither
+// the revision nor the reserved bits take part in the lookup, because every
+// stepping of a release is one device as far as the compiler is concerned.
+static constexpr uint32_t GMDIDDeviceMask = ~0u << GMDIDReleaseShift;
-GPUKind llvm::IntelGPU::getKindForGMDID(GMDID ID) {
- // Only INTEL_GPU rows are expanded, so a compatibility name can never match.
- // The rows are ordered so that the first match in a group names the group.
-#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
- if (ID.Architecture == ARCHITECTURE && ID.Release == RELEASE) \
- return GK_##KIND;
-#include "llvm/TargetParser/IntelGPUTargetParser.def"
- return GK_NONE;
+// Pack an architecture and a release the way a GPU IP version does, so that a
+// row of the table can be compared against a reported version as it is.
+static constexpr uint32_t packDevice(uint32_t Architecture, uint32_t Release) {
+ return (Architecture << GMDIDArchitectureShift) |
+ (Release << GMDIDReleaseShift);
}
-StringRef llvm::IntelGPU::getArchName(GPUKind Kind) {
- switch (Kind) {
- case GK_NONE:
- return "";
+StringRef llvm::IntelGPU::getArchName(uint32_t GPUIPVersion) {
+ const uint32_t Device = GPUIPVersion & GMDIDDeviceMask;
#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
- case GK_##KIND: \
- return NAME;
-#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_SUFFIX) \
- case GK_##KIND: \
+ if (Device == packDevice(ARCHITECTURE, RELEASE)) \
return NAME;
#include "llvm/TargetParser/IntelGPUTargetParser.def"
- }
- llvm_unreachable("invalid Intel GPU GPUKind");
+ return "";
}
-std::string llvm::IntelGPU::getNumericArchName(GMDID ID) {
- return ("xe_" + Twine(ID.Architecture) + "." + Twine(ID.Release) + "." +
- Twine(ID.Revision))
+std::string llvm::IntelGPU::getNumericArchName(uint32_t GPUIPVersion) {
+ const uint32_t Architecture = GPUIPVersion >> GMDIDArchitectureShift;
+ const uint32_t Release =
+ (GPUIPVersion >> GMDIDReleaseShift) & GMDIDReleaseMask;
+ const uint32_t Revision = GPUIPVersion & GMDIDRevisionMask;
+ return ("xe_" + Twine(Architecture) + "." + Twine(Release) + "." +
+ Twine(Revision))
.str();
}
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index 7bf35f5b27b30f..89b1a7ab426a8a 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -13,64 +13,59 @@ using namespace llvm;
namespace {
-// Build a GMDID the way the Level Zero driver reports it.
+// Build a GPU IP version the way the Level Zero driver reports it.
constexpr uint32_t gmdid(uint32_t Architecture, uint32_t Release,
uint32_t Revision) {
return (Architecture << 22) | (Release << 14) | Revision;
}
-TEST(IntelGPUTargetParserTest, DecodeGMDID) {
- IntelGPU::GMDID ID = IntelGPU::decodeGMDID(gmdid(35, 11, 7));
- EXPECT_EQ(ID.Architecture, 35u);
- EXPECT_EQ(ID.Release, 11u);
- EXPECT_EQ(ID.Revision, 7u);
+// The bits between the release and the revision, which no component uses.
+constexpr uint32_t GMDIDReservedBits = 0x3fc0;
- // The revision occupies the low 6 bits and the release the 8 above it, so
- // neither can bleed into the architecture.
- ID = IntelGPU::decodeGMDID(gmdid(12, 0xff, 0x3f));
- EXPECT_EQ(ID.Architecture, 12u);
- EXPECT_EQ(ID.Release, 0xffu);
- EXPECT_EQ(ID.Revision, 0x3fu);
-}
-
-TEST(IntelGPUTargetParserTest, KindForGMDID) {
- EXPECT_EQ(IntelGPU::getKindForGMDID({12, 60, 7}), IntelGPU::GK_XE_PVC);
- EXPECT_EQ(IntelGPU::getKindForGMDID({35, 11, 0}), IntelGPU::GK_XE_CRI);
+TEST(IntelGPUTargetParserTest, ArchNames) {
+ EXPECT_EQ(IntelGPU::getArchName(gmdid(12, 60, 7)), "xe-pvc");
+ EXPECT_EQ(IntelGPU::getArchName(gmdid(35, 11, 0)), "xe-cri");
+ EXPECT_EQ(IntelGPU::getArchName(gmdid(12, 55, 3)), "xe-acm-g10");
// The revision is not part of the key: every stepping of a release is the
// same device.
- EXPECT_EQ(IntelGPU::getKindForGMDID({12, 60, 0}), IntelGPU::GK_XE_PVC);
- EXPECT_EQ(IntelGPU::getKindForGMDID({12, 60, 63}), IntelGPU::GK_XE_PVC);
+ EXPECT_EQ(IntelGPU::getArchName(gmdid(12, 60, 0)), "xe-pvc");
+ EXPECT_EQ(IntelGPU::getArchName(gmdid(12, 60, 63)), "xe-pvc");
+ // Neither are the reserved bits, whatever a driver reports in them.
+ EXPECT_EQ(IntelGPU::getArchName(gmdid(12, 60, 7) | GMDIDReservedBits),
+ "xe-pvc");
// When several devices share an architecture and a release, the first row of
- // the group wins.
- EXPECT_EQ(IntelGPU::getKindForGMDID({30, 5, 0}), IntelGPU::GK_XE_NVL_U);
- EXPECT_EQ(IntelGPU::getKindForGMDID({12, 55, 0}), IntelGPU::GK_XE_ACM_G10);
- // A device that is not in the table has no kind at all.
- EXPECT_EQ(IntelGPU::getKindForGMDID({40, 11, 0}), IntelGPU::GK_NONE);
- EXPECT_EQ(IntelGPU::getKindForGMDID({9, 0, 9}), IntelGPU::GK_NONE);
+ // the group names the whole group.
+ EXPECT_EQ(IntelGPU::getArchName(gmdid(30, 5, 0)), "xe-nvl-u");
+ // A device that is not in the table has no name at all.
+ EXPECT_EQ(IntelGPU::getArchName(gmdid(40, 11, 0)), "");
+ EXPECT_EQ(IntelGPU::getArchName(gmdid(9, 0, 9)), "");
}
-TEST(IntelGPUTargetParserTest, ArchNames) {
- EXPECT_EQ(IntelGPU::getArchName(IntelGPU::GK_XE_PVC), "xe-pvc");
- EXPECT_EQ(IntelGPU::getArchName(IntelGPU::GK_XE_ATS_M150), "xe-ats-m150");
- EXPECT_EQ(IntelGPU::getArchName(IntelGPU::GK_XE_MTL), "xe-mtl");
- EXPECT_EQ(IntelGPU::getArchName(IntelGPU::GK_NONE), "");
-}
-
-TEST(IntelGPUTargetParserTest, EveryKindIsNamed) {
- // A row with no name would make the offload-arch utility print an empty
- // architecture, so every kind the table declares must have a spelling.
+TEST(IntelGPUTargetParserTest, EveryDeviceIsNamed) {
+ // A row that no GMDID can reach would make the offload-arch utility print an
+ // empty architecture for a device the table does list, so every physical
+ // device must resolve to some name. It need not be the name of the row
+ // itself: a row that shares its architecture and release with an earlier one
+ // is named after that earlier row. Compatibility names have no GMDID and so
+ // cannot be looked up at all, which is why INTEL_GPU_COMPAT is left alone.
#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
- EXPECT_FALSE(IntelGPU::getArchName(IntelGPU::GK_##KIND).empty()) << #KIND;
-#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_SUFFIX) \
- EXPECT_FALSE(IntelGPU::getArchName(IntelGPU::GK_##KIND).empty()) << #KIND;
+ EXPECT_FALSE(IntelGPU::getArchName(gmdid(ARCHITECTURE, RELEASE, 0)).empty()) \
+ << NAME;
#include "llvm/TargetParser/IntelGPUTargetParser.def"
}
TEST(IntelGPUTargetParserTest, NumericArchName) {
- EXPECT_EQ(IntelGPU::getNumericArchName({35, 11, 0}), "xe_35.11.0");
- EXPECT_EQ(IntelGPU::getNumericArchName({12, 99, 3}), "xe_12.99.3");
+ EXPECT_EQ(IntelGPU::getNumericArchName(gmdid(35, 11, 0)), "xe_35.11.0");
+ EXPECT_EQ(IntelGPU::getNumericArchName(gmdid(12, 99, 3)), "xe_12.99.3");
// Pre-Xe devices report a GMDID too, and none of them are in the table.
- EXPECT_EQ(IntelGPU::getNumericArchName({9, 0, 9}), "xe_9.0.9");
+ EXPECT_EQ(IntelGPU::getNumericArchName(gmdid(9, 0, 9)), "xe_9.0.9");
+ // The revision occupies the low 6 bits and the release the 8 above it, so
+ // neither can bleed into the architecture.
+ EXPECT_EQ(IntelGPU::getNumericArchName(gmdid(12, 0xff, 0x3f)),
+ "xe_12.255.63");
+ // The reserved bits belong to no component, so they are not spelled out.
+ EXPECT_EQ(IntelGPU::getNumericArchName(gmdid(12, 60, 7) | GMDIDReservedBits),
+ "xe_12.60.7");
}
} // namespace
>From 986375f82622267ed186e5e1654c2638ec282fcf Mon Sep 17 00:00:00 2001
From: "Kornev, Nikita" <nikita.kornev at intel.com>
Date: Wed, 16 Sep 2026 15:31:53 +0200
Subject: [PATCH 07/11] use GPUIP instead of GMDID
---
clang/tools/offload-arch/LevelZeroArch.cpp | 5 +-
.../offload-arch/OffloadArchTest.cpp | 30 +++++-----
.../TargetParser/IntelGPUTargetParser.def | 19 ++++---
.../llvm/TargetParser/IntelGPUTargetParser.h | 8 +--
.../lib/TargetParser/IntelGPUTargetParser.cpp | 28 ++++-----
.../TargetParser/IntelGPUTargetParserTest.cpp | 57 ++++++++++---------
6 files changed, 78 insertions(+), 69 deletions(-)
diff --git a/clang/tools/offload-arch/LevelZeroArch.cpp b/clang/tools/offload-arch/LevelZeroArch.cpp
index 0fbed1458daf77..cb03a80f5c6525 100644
--- a/clang/tools/offload-arch/LevelZeroArch.cpp
+++ b/clang/tools/offload-arch/LevelZeroArch.cpp
@@ -158,8 +158,9 @@ static bool loadLevelZero() {
} \
} while (0)
-// Translate a GMDID into an architecture name that is a legal --offload-arch
-// parameter. A device that has no name in the table is named after its GMDID.
+// Translate a GPU IP version into an architecture name that is a legal
+// --offload-arch parameter. A device that has no name in the table is named
+// after its version.
std::string getIntelGPUArchName(uint32_t IPVersion) {
StringRef Name = IntelGPU::getArchName(IPVersion);
return Name.empty() ? IntelGPU::getNumericArchName(IPVersion) : Name.str();
diff --git a/clang/unittests/offload-arch/OffloadArchTest.cpp b/clang/unittests/offload-arch/OffloadArchTest.cpp
index de9fe398fcdae1..f8229d8078056e 100644
--- a/clang/unittests/offload-arch/OffloadArchTest.cpp
+++ b/clang/unittests/offload-arch/OffloadArchTest.cpp
@@ -214,42 +214,42 @@ TEST(KFDTopology, MultipleGPUsArePrintedInNodeOrder) {
// --- getIntelGPUArchName ---
namespace {
-// Build a GMDID the way the Level Zero driver reports it.
-constexpr uint32_t gmdid(uint32_t Architecture, uint32_t Release,
- uint32_t Revision) {
+// Build a GPU IP version the way the Level Zero driver reports it.
+constexpr uint32_t gpuIPVersion(uint32_t Architecture, uint32_t Release,
+ uint32_t Revision) {
return (Architecture << 22) | (Release << 14) | Revision;
}
} // namespace
TEST(IntelGPUArchName, KnownArchitecturesGetAFriendlyName) {
- EXPECT_EQ(getIntelGPUArchName(gmdid(12, 60, 7)), "xe-pvc");
- EXPECT_EQ(getIntelGPUArchName(gmdid(20, 1, 4)), "xe-bmg-g21");
- EXPECT_EQ(getIntelGPUArchName(gmdid(35, 10, 0)), "xe-nvl-p");
- EXPECT_EQ(getIntelGPUArchName(gmdid(12, 0, 0)), "xe-tgllp");
+ EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 60, 7)), "xe-pvc");
+ EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(20, 1, 4)), "xe-bmg-g21");
+ EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(35, 10, 0)), "xe-nvl-p");
+ EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 0, 0)), "xe-tgllp");
}
// When several devices share an architecture and a release, the first one
// listed in IntelGPUTargetParser.def names the whole group.
TEST(IntelGPUArchName, FirstNameOfAGroupWins) {
- EXPECT_EQ(getIntelGPUArchName(gmdid(30, 5, 0)), "xe-nvl-u");
- EXPECT_EQ(getIntelGPUArchName(gmdid(12, 55, 0)), "xe-acm-g10");
+ EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(30, 5, 0)), "xe-nvl-u");
+ EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 55, 0)), "xe-acm-g10");
}
// The revision is not part of the lookup: every stepping of an architecture
// shares one name.
TEST(IntelGPUArchName, RevisionDoesNotAffectTheName) {
- EXPECT_EQ(getIntelGPUArchName(gmdid(12, 60, 0)), "xe-pvc");
- EXPECT_EQ(getIntelGPUArchName(gmdid(12, 60, 63)), "xe-pvc");
+ EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 60, 0)), "xe-pvc");
+ EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 60, 63)), "xe-pvc");
}
// An architecture that is not in the table still has to be named, so that a
// newer device is usable with a compiler that predates it.
TEST(IntelGPUArchName, UnknownArchitecturesGetANumericName) {
- EXPECT_EQ(getIntelGPUArchName(gmdid(40, 11, 0)), "xe_40.11.0");
- EXPECT_EQ(getIntelGPUArchName(gmdid(12, 99, 3)), "xe_12.99.3");
+ EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(40, 11, 0)), "xe_40.11.0");
+ EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 99, 3)), "xe_12.99.3");
}
-// Pre-Xe devices report a GMDID too, and none of them are in the table.
+// Pre-Xe devices report a version too, and none of them are in the table.
TEST(IntelGPUArchName, LegacyArchitecture) {
- EXPECT_EQ(getIntelGPUArchName(gmdid(9, 0, 9)), "xe_9.0.9");
+ EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(9, 0, 9)), "xe_9.0.9");
}
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
index c712e90249002e..434f6c2ac0b3de 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
@@ -14,8 +14,10 @@
// INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX)
// NAME - Human-friendly device name, e.g. "xe-cri".
// KIND - GPUKind enumerator suffix; the enumerator is GK_<KIND>.
-// ARCHITECTURE - Architecture component of the GMDID the device reports.
-// RELEASE - Release component of the GMDID the device reports.
+// ARCHITECTURE - Architecture component of the GPU IP version the device
+// reports.
+// RELEASE - Release component of the GPU IP version the device
+// reports.
// IGCA_TARGET - Numeric IGCA target.
// IGCA_SUFFIX - Token naming the feature sets that the target comprises:
// Core (no suffix), Compute ("c"), Render ("r"),
@@ -24,13 +26,14 @@
//
// INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_SUFFIX)
// A compatibility name that covers several releases, e.g. "xe-dg2", which
-// covers every xe-dg2-* and xe-acm-* platform. These names have no GMDID
-// of their own, so no device reports one and the offload-arch utility
-// never prints one, but they are legal --offload-arch values. The columns
-// mean the same as above.
+// covers every xe-dg2-* and xe-acm-* platform. These names have no GPU IP
+// version of their own, so no device reports one and the offload-arch
+// utility never prints one, but they are legal --offload-arch values. The
+// columns mean the same as above.
//
-// The revision (stepping) component of the GMDID is deliberately not part of
-// the key: every stepping of a release shares one name and one IGCA target.
+// The revision (stepping) component of the GPU IP version is deliberately not
+// part of the key: every stepping of a release shares one name and one IGCA
+// target.
//
// Several devices can share an architecture and a release. The rows are ordered
// so that the name to print for such a group comes first.
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index 43c148db1a7e0c..39f17def08a762 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -34,10 +34,10 @@ enum GPUKind : uint8_t {
#include "llvm/TargetParser/IntelGPUTargetParser.def"
};
-/// Return the name of the device that \p GPUIPVersion, the "GMDID" reported by
-/// the driver, identifies, e.g. "xe-pvc", or "" if the table lists no such
-/// device. The revision is ignored: every stepping of a release is one device.
-/// If several rows match, the first one in IntelGPUTargetParser.def wins.
+/// Return the name of the device that \p GPUIPVersion, as reported by the
+/// driver, identifies, e.g. "xe-pvc", or "" if the table lists no such device.
+/// The revision is ignored: every stepping of a release is one device. If
+/// several rows match, the first one in IntelGPUTargetParser.def wins.
LLVM_ABI StringRef getArchName(uint32_t GPUIPVersion);
/// Return the numeric name of \p GPUIPVersion, e.g. "xe_35.11.0", which every
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index e4b7ac7962cb37..6ab276cfc6d596 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -16,28 +16,28 @@
using namespace llvm;
using namespace IntelGPU;
-// A GPU IP version, the "GMDID", packs the architecture into the bits above the
-// release, which in turn sits above the revision. The bits between the release
-// and the revision are reserved.
-static constexpr uint32_t GMDIDArchitectureShift = 22;
-static constexpr uint32_t GMDIDReleaseShift = 14;
-static constexpr uint32_t GMDIDReleaseMask = 0xff;
-static constexpr uint32_t GMDIDRevisionMask = 0x3f;
+// A GPU IP version packs the architecture into the bits above the release,
+// which in turn sits above the revision. The bits between the release and the
+// revision are reserved.
+static constexpr uint32_t GPUIPArchitectureShift = 22;
+static constexpr uint32_t GPUIPReleaseShift = 14;
+static constexpr uint32_t GPUIPReleaseMask = 0xff;
+static constexpr uint32_t GPUIPRevisionMask = 0x3f;
// The bits that identify a device: the architecture and the release. Neither
// the revision nor the reserved bits take part in the lookup, because every
// stepping of a release is one device as far as the compiler is concerned.
-static constexpr uint32_t GMDIDDeviceMask = ~0u << GMDIDReleaseShift;
+static constexpr uint32_t GPUIPDeviceMask = ~0u << GPUIPReleaseShift;
// Pack an architecture and a release the way a GPU IP version does, so that a
// row of the table can be compared against a reported version as it is.
static constexpr uint32_t packDevice(uint32_t Architecture, uint32_t Release) {
- return (Architecture << GMDIDArchitectureShift) |
- (Release << GMDIDReleaseShift);
+ return (Architecture << GPUIPArchitectureShift) |
+ (Release << GPUIPReleaseShift);
}
StringRef llvm::IntelGPU::getArchName(uint32_t GPUIPVersion) {
- const uint32_t Device = GPUIPVersion & GMDIDDeviceMask;
+ const uint32_t Device = GPUIPVersion & GPUIPDeviceMask;
#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
if (Device == packDevice(ARCHITECTURE, RELEASE)) \
return NAME;
@@ -46,10 +46,10 @@ StringRef llvm::IntelGPU::getArchName(uint32_t GPUIPVersion) {
}
std::string llvm::IntelGPU::getNumericArchName(uint32_t GPUIPVersion) {
- const uint32_t Architecture = GPUIPVersion >> GMDIDArchitectureShift;
+ const uint32_t Architecture = GPUIPVersion >> GPUIPArchitectureShift;
const uint32_t Release =
- (GPUIPVersion >> GMDIDReleaseShift) & GMDIDReleaseMask;
- const uint32_t Revision = GPUIPVersion & GMDIDRevisionMask;
+ (GPUIPVersion >> GPUIPReleaseShift) & GPUIPReleaseMask;
+ const uint32_t Revision = GPUIPVersion & GPUIPRevisionMask;
return ("xe_" + Twine(Architecture) + "." + Twine(Release) + "." +
Twine(Revision))
.str();
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index 89b1a7ab426a8a..508b2822780b23 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -14,58 +14,63 @@ using namespace llvm;
namespace {
// Build a GPU IP version the way the Level Zero driver reports it.
-constexpr uint32_t gmdid(uint32_t Architecture, uint32_t Release,
- uint32_t Revision) {
+constexpr uint32_t gpuIPVersion(uint32_t Architecture, uint32_t Release,
+ uint32_t Revision) {
return (Architecture << 22) | (Release << 14) | Revision;
}
// The bits between the release and the revision, which no component uses.
-constexpr uint32_t GMDIDReservedBits = 0x3fc0;
+constexpr uint32_t GPUIPReservedBits = 0x3fc0;
TEST(IntelGPUTargetParserTest, ArchNames) {
- EXPECT_EQ(IntelGPU::getArchName(gmdid(12, 60, 7)), "xe-pvc");
- EXPECT_EQ(IntelGPU::getArchName(gmdid(35, 11, 0)), "xe-cri");
- EXPECT_EQ(IntelGPU::getArchName(gmdid(12, 55, 3)), "xe-acm-g10");
+ EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(12, 60, 7)), "xe-pvc");
+ EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(35, 11, 0)), "xe-cri");
+ EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(12, 55, 3)), "xe-acm-g10");
// The revision is not part of the key: every stepping of a release is the
// same device.
- EXPECT_EQ(IntelGPU::getArchName(gmdid(12, 60, 0)), "xe-pvc");
- EXPECT_EQ(IntelGPU::getArchName(gmdid(12, 60, 63)), "xe-pvc");
+ EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(12, 60, 0)), "xe-pvc");
+ EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(12, 60, 63)), "xe-pvc");
// Neither are the reserved bits, whatever a driver reports in them.
- EXPECT_EQ(IntelGPU::getArchName(gmdid(12, 60, 7) | GMDIDReservedBits),
+ EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(12, 60, 7) | GPUIPReservedBits),
"xe-pvc");
// When several devices share an architecture and a release, the first row of
// the group names the whole group.
- EXPECT_EQ(IntelGPU::getArchName(gmdid(30, 5, 0)), "xe-nvl-u");
+ EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(30, 5, 0)), "xe-nvl-u");
// A device that is not in the table has no name at all.
- EXPECT_EQ(IntelGPU::getArchName(gmdid(40, 11, 0)), "");
- EXPECT_EQ(IntelGPU::getArchName(gmdid(9, 0, 9)), "");
+ EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(40, 11, 0)), "");
+ EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(9, 0, 9)), "");
}
TEST(IntelGPUTargetParserTest, EveryDeviceIsNamed) {
- // A row that no GMDID can reach would make the offload-arch utility print an
- // empty architecture for a device the table does list, so every physical
- // device must resolve to some name. It need not be the name of the row
- // itself: a row that shares its architecture and release with an earlier one
- // is named after that earlier row. Compatibility names have no GMDID and so
- // cannot be looked up at all, which is why INTEL_GPU_COMPAT is left alone.
+ // A row that no GPU IP version can reach would make the offload-arch utility
+ // print an empty architecture for a device the table does list, so every
+ // physical device must resolve to some name. It need not be the name of the
+ // row itself: a row that shares its architecture and release with an earlier
+ // one is named after that earlier row. Compatibility names have no version of
+ // their own and so cannot be looked up, which is why INTEL_GPU_COMPAT is left
+ // alone.
#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
- EXPECT_FALSE(IntelGPU::getArchName(gmdid(ARCHITECTURE, RELEASE, 0)).empty()) \
+ EXPECT_FALSE( \
+ IntelGPU::getArchName(gpuIPVersion(ARCHITECTURE, RELEASE, 0)).empty()) \
<< NAME;
#include "llvm/TargetParser/IntelGPUTargetParser.def"
}
TEST(IntelGPUTargetParserTest, NumericArchName) {
- EXPECT_EQ(IntelGPU::getNumericArchName(gmdid(35, 11, 0)), "xe_35.11.0");
- EXPECT_EQ(IntelGPU::getNumericArchName(gmdid(12, 99, 3)), "xe_12.99.3");
- // Pre-Xe devices report a GMDID too, and none of them are in the table.
- EXPECT_EQ(IntelGPU::getNumericArchName(gmdid(9, 0, 9)), "xe_9.0.9");
+ EXPECT_EQ(IntelGPU::getNumericArchName(gpuIPVersion(35, 11, 0)),
+ "xe_35.11.0");
+ EXPECT_EQ(IntelGPU::getNumericArchName(gpuIPVersion(12, 99, 3)),
+ "xe_12.99.3");
+ // Pre-Xe devices report a version too, and none of them are in the table.
+ EXPECT_EQ(IntelGPU::getNumericArchName(gpuIPVersion(9, 0, 9)), "xe_9.0.9");
// The revision occupies the low 6 bits and the release the 8 above it, so
// neither can bleed into the architecture.
- EXPECT_EQ(IntelGPU::getNumericArchName(gmdid(12, 0xff, 0x3f)),
+ EXPECT_EQ(IntelGPU::getNumericArchName(gpuIPVersion(12, 0xff, 0x3f)),
"xe_12.255.63");
// The reserved bits belong to no component, so they are not spelled out.
- EXPECT_EQ(IntelGPU::getNumericArchName(gmdid(12, 60, 7) | GMDIDReservedBits),
- "xe_12.60.7");
+ EXPECT_EQ(
+ IntelGPU::getNumericArchName(gpuIPVersion(12, 60, 7) | GPUIPReservedBits),
+ "xe_12.60.7");
}
} // namespace
>From de160bb0b270dd25c4c67c082766028567a6ba70 Mon Sep 17 00:00:00 2001
From: "Kornev, Nikita" <nikita.kornev at intel.com>
Date: Wed, 16 Sep 2026 16:20:27 +0200
Subject: [PATCH 08/11] add a comment for GPU IP content
---
llvm/lib/TargetParser/IntelGPUTargetParser.cpp | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index 6ab276cfc6d596..132941f8725bb0 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -16,9 +16,15 @@
using namespace llvm;
using namespace IntelGPU;
-// A GPU IP version packs the architecture into the bits above the release,
-// which in turn sits above the revision. The bits between the release and the
-// revision are reserved.
+// A GPU IP version packs four fields, from the most significant bit down:
+//
+// 31 22 21 14 13 6 5 0
+// +---------------+----------+----------+----------+
+// | architecture | release | reserved | revision |
+// +---------------+----------+----------+----------+
+// 10 bits 8 bits 8 bits 6 bits
+//
+// The reserved bits carry no information.
static constexpr uint32_t GPUIPArchitectureShift = 22;
static constexpr uint32_t GPUIPReleaseShift = 14;
static constexpr uint32_t GPUIPReleaseMask = 0xff;
>From d7ae1ceeb4eb19ec5f8ceb7e3fda61ad5e5f9a28 Mon Sep 17 00:00:00 2001
From: "Kornev, Nikita" <nikita.kornev at intel.com>
Date: Wed, 16 Sep 2026 19:34:21 +0200
Subject: [PATCH 09/11] apply review comments
---
clang/tools/offload-arch/LevelZeroArch.cpp | 7 +++----
clang/unittests/offload-arch/OffloadArchTest.cpp | 3 ++-
llvm/include/llvm/TargetParser/IntelGPUTargetParser.h | 11 +++++------
3 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/clang/tools/offload-arch/LevelZeroArch.cpp b/clang/tools/offload-arch/LevelZeroArch.cpp
index cb03a80f5c6525..6720618d2a7d86 100644
--- a/clang/tools/offload-arch/LevelZeroArch.cpp
+++ b/clang/tools/offload-arch/LevelZeroArch.cpp
@@ -202,10 +202,9 @@ int printGPUsByLevelZero() {
if (IPVersion.ipVersion == 0) {
if (Verbose)
- llvm::errs() << "warning: skipping device '" << DeviceProperties.name
- << "': this version of the Level Zero driver does not "
- "support ZE_STRUCTURE_TYPE_DEVICE_IP_VERSION_EXT, so "
- "the device architecture cannot be determined\n";
+ llvm::errs()
+ << "warning: skipping device '" << DeviceProperties.name
+ << "': the device does not support Device IP Version Extension\n";
continue;
}
diff --git a/clang/unittests/offload-arch/OffloadArchTest.cpp b/clang/unittests/offload-arch/OffloadArchTest.cpp
index f8229d8078056e..512fd2eba147d6 100644
--- a/clang/unittests/offload-arch/OffloadArchTest.cpp
+++ b/clang/unittests/offload-arch/OffloadArchTest.cpp
@@ -249,7 +249,8 @@ TEST(IntelGPUArchName, UnknownArchitecturesGetANumericName) {
EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 99, 3)), "xe_12.99.3");
}
-// Pre-Xe devices report a version too, and none of them are in the table.
+// Pre-Xe devices report a GPU IP version too, but they have no human-friendly
+// identifier.
TEST(IntelGPUArchName, LegacyArchitecture) {
EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(9, 0, 9)), "xe_9.0.9");
}
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index 39f17def08a762..70fe57419c66f0 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -34,14 +34,13 @@ enum GPUKind : uint8_t {
#include "llvm/TargetParser/IntelGPUTargetParser.def"
};
-/// Return the name of the device that \p GPUIPVersion, as reported by the
-/// driver, identifies, e.g. "xe-pvc", or "" if the table lists no such device.
-/// The revision is ignored: every stepping of a release is one device. If
-/// several rows match, the first one in IntelGPUTargetParser.def wins.
+/// \return the device name that \p GPUIPVersion identifies, as reported by the
+/// driver, e.g. "xe-pvc". If the table lists no such device, return an empty
+/// string. The revision is ignored: every stepping of a release is one device.
+/// If several rows match, the first one in IntelGPUTargetParser.def wins.
LLVM_ABI StringRef getArchName(uint32_t GPUIPVersion);
-/// Return the numeric name of \p GPUIPVersion, e.g. "xe_35.11.0", which every
-/// device has, even one that the table does not list.
+/// \return the numeric name of \p GPUIPVersion, e.g. "xe_35.11.0".
LLVM_ABI std::string getNumericArchName(uint32_t GPUIPVersion);
} // namespace IntelGPU
>From fb19155388ca66362eeecfd8e382b0e5500582ac Mon Sep 17 00:00:00 2001
From: "Kornev, Nikita" <nikita.kornev at intel.com>
Date: Mon, 21 Sep 2026 19:30:56 +0200
Subject: [PATCH 10/11] use major/minor instead of architecture/release
---
.../offload-arch/OffloadArchTest.cpp | 12 +++---
.../TargetParser/IntelGPUTargetParser.def | 42 +++++++++----------
.../llvm/TargetParser/IntelGPUTargetParser.h | 6 +--
.../lib/TargetParser/IntelGPUTargetParser.cpp | 35 +++++++---------
.../TargetParser/IntelGPUTargetParserTest.cpp | 21 +++++-----
5 files changed, 55 insertions(+), 61 deletions(-)
diff --git a/clang/unittests/offload-arch/OffloadArchTest.cpp b/clang/unittests/offload-arch/OffloadArchTest.cpp
index d799fb61d55efe..d40b63c4c4131d 100644
--- a/clang/unittests/offload-arch/OffloadArchTest.cpp
+++ b/clang/unittests/offload-arch/OffloadArchTest.cpp
@@ -250,9 +250,9 @@ TEST(KFDTopology, GFX1250NonA0IsPrintedPlain) {
namespace {
// Build a GPU IP version the way the Level Zero driver reports it.
-constexpr uint32_t gpuIPVersion(uint32_t Architecture, uint32_t Release,
+constexpr uint32_t gpuIPVersion(uint32_t Major, uint32_t Minor,
uint32_t Revision) {
- return (Architecture << 22) | (Release << 14) | Revision;
+ return (Major << 22) | (Minor << 14) | Revision;
}
} // namespace
@@ -263,15 +263,15 @@ TEST(IntelGPUArchName, KnownArchitecturesGetAFriendlyName) {
EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 0, 0)), "xe-tgllp");
}
-// When several devices share an architecture and a release, the first one
-// listed in IntelGPUTargetParser.def names the whole group.
+// When several devices share a major and a minor version, the first one listed
+// in IntelGPUTargetParser.def names the whole group.
TEST(IntelGPUArchName, FirstNameOfAGroupWins) {
EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(30, 5, 0)), "xe-nvl-u");
EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 55, 0)), "xe-acm-g10");
}
-// The revision is not part of the lookup: every stepping of an architecture
-// shares one name.
+// Devices with the same major and minor versions but different revisions are
+// the same device.
TEST(IntelGPUArchName, RevisionDoesNotAffectTheName) {
EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 60, 0)), "xe-pvc");
EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 60, 63)), "xe-pvc");
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
index 434f6c2ac0b3de..37c51bd18a1194 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
@@ -11,37 +11,35 @@
// IGCA (Intel Graphics Compute Architecture) target the compiler compiles for
// when the user names it. Adding a device is a single row here.
//
-// INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX)
-// NAME - Human-friendly device name, e.g. "xe-cri".
-// KIND - GPUKind enumerator suffix; the enumerator is GK_<KIND>.
-// ARCHITECTURE - Architecture component of the GPU IP version the device
-// reports.
-// RELEASE - Release component of the GPU IP version the device
-// reports.
-// IGCA_TARGET - Numeric IGCA target.
-// IGCA_SUFFIX - Token naming the feature sets that the target comprises:
-// Core (no suffix), Compute ("c"), Render ("r"),
-// ComputeExact ("ca") or RenderExact ("ra"). A consumer maps
-// the token onto an enumerator of its own.
+// INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_SUFFIX)
+// NAME - Human-friendly device name, e.g. "xe-cri".
+// KIND - GPUKind enumerator suffix; the enumerator is GK_<KIND>.
+// MAJOR - Major component of the GPU IP version the device reports.
+// MINOR - Minor component of the GPU IP version the device reports.
+// IGCA_TARGET - Numeric IGCA target.
+// IGCA_SUFFIX - Token naming the feature sets that the target comprises:
+// Core (no suffix), Compute ("c"), Render ("r"),
+// ComputeExact ("ca") or RenderExact ("ra"). A consumer maps
+// the token onto an enumerator of its own.
//
// INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_SUFFIX)
-// A compatibility name that covers several releases, e.g. "xe-dg2", which
-// covers every xe-dg2-* and xe-acm-* platform. These names have no GPU IP
-// version of their own, so no device reports one and the offload-arch
-// utility never prints one, but they are legal --offload-arch values. The
-// columns mean the same as above.
+// A compatibility name that covers several minor versions, e.g. "xe-dg2",
+// which covers every xe-dg2-* and xe-acm-* platform. These names have no
+// GPU IP version of their own, so no device reports one and the
+// offload-arch utility never prints one, but they are legal --offload-arch
+// values. The columns mean the same as above.
//
-// The revision (stepping) component of the GPU IP version is deliberately not
-// part of the key: every stepping of a release shares one name and one IGCA
-// target.
+// A device is keyed by its major and minor version alone. The revision
+// component of the GPU IP version is deliberately left out of the key, so that
+// every revision of a device shares one name and one IGCA target.
//
-// Several devices can share an architecture and a release. The rows are ordered
+// Several devices can share a major and a minor version. The rows are ordered
// so that the name to print for such a group comes first.
//
//===----------------------------------------------------------------------===//
#ifndef INTEL_GPU
-#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX)
+#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_SUFFIX)
#endif
#ifndef INTEL_GPU_COMPAT
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index 70fe57419c66f0..45b0301e4d1edd 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -28,15 +28,15 @@ namespace IntelGPU {
/// compatibility names that stand for a whole product line.
enum GPUKind : uint8_t {
GK_NONE = 0,
-#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
- GK_##KIND,
+#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_SUFFIX) GK_##KIND,
#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_SUFFIX) GK_##KIND,
#include "llvm/TargetParser/IntelGPUTargetParser.def"
};
/// \return the device name that \p GPUIPVersion identifies, as reported by the
/// driver, e.g. "xe-pvc". If the table lists no such device, return an empty
-/// string. The revision is ignored: every stepping of a release is one device.
+/// string. Only the major and minor versions are looked at, so every revision
+/// of a device resolves to the same name.
/// If several rows match, the first one in IntelGPUTargetParser.def wins.
LLVM_ABI StringRef getArchName(uint32_t GPUIPVersion);
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index 132941f8725bb0..d24c52bf86d09b 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -20,43 +20,40 @@ using namespace IntelGPU;
//
// 31 22 21 14 13 6 5 0
// +---------------+----------+----------+----------+
-// | architecture | release | reserved | revision |
+// | major | minor | reserved | revision |
// +---------------+----------+----------+----------+
// 10 bits 8 bits 8 bits 6 bits
//
// The reserved bits carry no information.
-static constexpr uint32_t GPUIPArchitectureShift = 22;
-static constexpr uint32_t GPUIPReleaseShift = 14;
-static constexpr uint32_t GPUIPReleaseMask = 0xff;
+static constexpr uint32_t GPUIPMajorShift = 22;
+static constexpr uint32_t GPUIPMinorShift = 14;
+static constexpr uint32_t GPUIPMinorMask = 0xff;
static constexpr uint32_t GPUIPRevisionMask = 0x3f;
-// The bits that identify a device: the architecture and the release. Neither
-// the revision nor the reserved bits take part in the lookup, because every
-// stepping of a release is one device as far as the compiler is concerned.
-static constexpr uint32_t GPUIPDeviceMask = ~0u << GPUIPReleaseShift;
+// The bits that identify a device: the major and the minor version. Neither the
+// revision nor the reserved bits take part in the lookup, because every
+// revision of a device is one device as far as the compiler is concerned.
+static constexpr uint32_t GPUIPDeviceMask = ~0u << GPUIPMinorShift;
-// Pack an architecture and a release the way a GPU IP version does, so that a
+// Pack a major and a minor version the way a GPU IP version does, so that a
// row of the table can be compared against a reported version as it is.
-static constexpr uint32_t packDevice(uint32_t Architecture, uint32_t Release) {
- return (Architecture << GPUIPArchitectureShift) |
- (Release << GPUIPReleaseShift);
+static constexpr uint32_t packDevice(uint32_t Major, uint32_t Minor) {
+ return (Major << GPUIPMajorShift) | (Minor << GPUIPMinorShift);
}
StringRef llvm::IntelGPU::getArchName(uint32_t GPUIPVersion) {
const uint32_t Device = GPUIPVersion & GPUIPDeviceMask;
-#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
- if (Device == packDevice(ARCHITECTURE, RELEASE)) \
+#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_SUFFIX) \
+ if (Device == packDevice(MAJOR, MINOR)) \
return NAME;
#include "llvm/TargetParser/IntelGPUTargetParser.def"
return "";
}
std::string llvm::IntelGPU::getNumericArchName(uint32_t GPUIPVersion) {
- const uint32_t Architecture = GPUIPVersion >> GPUIPArchitectureShift;
- const uint32_t Release =
- (GPUIPVersion >> GPUIPReleaseShift) & GPUIPReleaseMask;
+ const uint32_t Major = GPUIPVersion >> GPUIPMajorShift;
+ const uint32_t Minor = (GPUIPVersion >> GPUIPMinorShift) & GPUIPMinorMask;
const uint32_t Revision = GPUIPVersion & GPUIPRevisionMask;
- return ("xe_" + Twine(Architecture) + "." + Twine(Release) + "." +
- Twine(Revision))
+ return ("xe_" + Twine(Major) + "." + Twine(Minor) + "." + Twine(Revision))
.str();
}
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index 508b2822780b23..2f2405b325812a 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -14,26 +14,26 @@ using namespace llvm;
namespace {
// Build a GPU IP version the way the Level Zero driver reports it.
-constexpr uint32_t gpuIPVersion(uint32_t Architecture, uint32_t Release,
+constexpr uint32_t gpuIPVersion(uint32_t Major, uint32_t Minor,
uint32_t Revision) {
- return (Architecture << 22) | (Release << 14) | Revision;
+ return (Major << 22) | (Minor << 14) | Revision;
}
-// The bits between the release and the revision, which no component uses.
+// The bits between the minor version and the revision, which no component uses.
constexpr uint32_t GPUIPReservedBits = 0x3fc0;
TEST(IntelGPUTargetParserTest, ArchNames) {
EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(12, 60, 7)), "xe-pvc");
EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(35, 11, 0)), "xe-cri");
EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(12, 55, 3)), "xe-acm-g10");
- // The revision is not part of the key: every stepping of a release is the
+ // The revision is not part of the key: every revision of a device is that
// same device.
EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(12, 60, 0)), "xe-pvc");
EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(12, 60, 63)), "xe-pvc");
// Neither are the reserved bits, whatever a driver reports in them.
EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(12, 60, 7) | GPUIPReservedBits),
"xe-pvc");
- // When several devices share an architecture and a release, the first row of
+ // When several devices share a major and a minor version, the first row of
// the group names the whole group.
EXPECT_EQ(IntelGPU::getArchName(gpuIPVersion(30, 5, 0)), "xe-nvl-u");
// A device that is not in the table has no name at all.
@@ -45,13 +45,12 @@ TEST(IntelGPUTargetParserTest, EveryDeviceIsNamed) {
// A row that no GPU IP version can reach would make the offload-arch utility
// print an empty architecture for a device the table does list, so every
// physical device must resolve to some name. It need not be the name of the
- // row itself: a row that shares its architecture and release with an earlier
+ // row itself: a row that shares its major and minor version with an earlier
// one is named after that earlier row. Compatibility names have no version of
// their own and so cannot be looked up, which is why INTEL_GPU_COMPAT is left
// alone.
-#define INTEL_GPU(NAME, KIND, ARCHITECTURE, RELEASE, IGCA_TARGET, IGCA_SUFFIX) \
- EXPECT_FALSE( \
- IntelGPU::getArchName(gpuIPVersion(ARCHITECTURE, RELEASE, 0)).empty()) \
+#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_SUFFIX) \
+ EXPECT_FALSE(IntelGPU::getArchName(gpuIPVersion(MAJOR, MINOR, 0)).empty()) \
<< NAME;
#include "llvm/TargetParser/IntelGPUTargetParser.def"
}
@@ -63,8 +62,8 @@ TEST(IntelGPUTargetParserTest, NumericArchName) {
"xe_12.99.3");
// Pre-Xe devices report a version too, and none of them are in the table.
EXPECT_EQ(IntelGPU::getNumericArchName(gpuIPVersion(9, 0, 9)), "xe_9.0.9");
- // The revision occupies the low 6 bits and the release the 8 above it, so
- // neither can bleed into the architecture.
+ // The revision occupies the low 6 bits and the minor version the 8 above it,
+ // so neither can bleed into the major version.
EXPECT_EQ(IntelGPU::getNumericArchName(gpuIPVersion(12, 0xff, 0x3f)),
"xe_12.255.63");
// The reserved bits belong to no component, so they are not spelled out.
>From 67fe13cb60f2a7b3375d6ef5b3fa074aaba19948 Mon Sep 17 00:00:00 2001
From: "Kornev, Nikita" <nikita.kornev at intel.com>
Date: Mon, 21 Sep 2026 19:48:37 +0200
Subject: [PATCH 11/11] add assert for version component sizes
---
clang/unittests/offload-arch/OffloadArchTest.cpp | 8 +++++++-
llvm/lib/TargetParser/IntelGPUTargetParser.cpp | 8 +++++++-
llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp | 8 +++++++-
3 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/clang/unittests/offload-arch/OffloadArchTest.cpp b/clang/unittests/offload-arch/OffloadArchTest.cpp
index d40b63c4c4131d..b9fff3901d20e4 100644
--- a/clang/unittests/offload-arch/OffloadArchTest.cpp
+++ b/clang/unittests/offload-arch/OffloadArchTest.cpp
@@ -15,6 +15,7 @@
#include "llvm/Testing/Support/SupportHelpers.h"
#include "gtest/gtest.h"
#include <algorithm>
+#include <cassert>
#include <string>
// Defined in AMDGPUArchByHIP.cpp (non-static, compiled into this test).
@@ -249,9 +250,14 @@ TEST(KFDTopology, GFX1250NonA0IsPrintedPlain) {
// --- getIntelGPUArchName ---
namespace {
-// Build a GPU IP version the way the Level Zero driver reports it.
+// Build a GPU IP version the way the Level Zero driver reports it. A component
+// too wide for its field would corrupt the fields above it and quietly test
+// something other than what it spells out.
constexpr uint32_t gpuIPVersion(uint32_t Major, uint32_t Minor,
uint32_t Revision) {
+ assert((Major & ~0x3ffu) == 0 && "major version too wide");
+ assert((Minor & ~0xffu) == 0 && "minor version too wide");
+ assert((Revision & ~0x3fu) == 0 && "revision too wide");
return (Major << 22) | (Minor << 14) | Revision;
}
} // namespace
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index d24c52bf86d09b..c1162fa1f5a4c8 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -12,6 +12,7 @@
#include "llvm/TargetParser/IntelGPUTargetParser.h"
#include "llvm/ADT/Twine.h"
+#include <cassert>
using namespace llvm;
using namespace IntelGPU;
@@ -27,6 +28,7 @@ using namespace IntelGPU;
// The reserved bits carry no information.
static constexpr uint32_t GPUIPMajorShift = 22;
static constexpr uint32_t GPUIPMinorShift = 14;
+static constexpr uint32_t GPUIPMajorMask = 0x3ff;
static constexpr uint32_t GPUIPMinorMask = 0xff;
static constexpr uint32_t GPUIPRevisionMask = 0x3f;
@@ -36,8 +38,12 @@ static constexpr uint32_t GPUIPRevisionMask = 0x3f;
static constexpr uint32_t GPUIPDeviceMask = ~0u << GPUIPMinorShift;
// Pack a major and a minor version the way a GPU IP version does, so that a
-// row of the table can be compared against a reported version as it is.
+// row of the table can be compared against a reported version as it is. A value
+// too wide for its field would silently corrupt the fields above it, which
+// would mean a typo in IntelGPUTargetParser.def going unnoticed.
static constexpr uint32_t packDevice(uint32_t Major, uint32_t Minor) {
+ assert((Major & ~GPUIPMajorMask) == 0 && "major version too wide");
+ assert((Minor & ~GPUIPMinorMask) == 0 && "minor version too wide");
return (Major << GPUIPMajorShift) | (Minor << GPUIPMinorShift);
}
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index 2f2405b325812a..4645da356ab927 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -8,14 +8,20 @@
#include "llvm/TargetParser/IntelGPUTargetParser.h"
#include "gtest/gtest.h"
+#include <cassert>
using namespace llvm;
namespace {
-// Build a GPU IP version the way the Level Zero driver reports it.
+// Build a GPU IP version the way the Level Zero driver reports it. A component
+// too wide for its field would corrupt the fields above it and quietly test
+// something other than what it spells out.
constexpr uint32_t gpuIPVersion(uint32_t Major, uint32_t Minor,
uint32_t Revision) {
+ assert((Major & ~0x3ffu) == 0 && "major version too wide");
+ assert((Minor & ~0xffu) == 0 && "minor version too wide");
+ assert((Revision & ~0x3fu) == 0 && "revision too wide");
return (Major << 22) | (Minor << 14) | Revision;
}
More information about the llvm-commits
mailing list