From 0c542715bcf7b2b74c8277deb75a8fd2107652c3 Mon Sep 17 00:00:00 2001 From: "Kornev, Nikita" Date: Fri, 28 Aug 2026 14:42:27 +0200 Subject: [PATCH 1/7] [offload-arch] Print Intel GPU architecture names instead of device names The output of this utility is expected to be a list of names that are legal --offload-arch parameters, but for Intel GPUs it printed the name of the device, e.g. "Intel(R) Data Center GPU Max 1100", which is not one. Query the GMDID of each device with zeDeviceGetProperties and the device IP version extension, and translate its architecture and release components into an architecture name using the table in IntelGPUArch.def, which is meant to be generated from the data published by the GPU driver. A device that has no entry in the table is named after all three components of its GMDID, e.g. xe_40.11.0, so that a GPU newer than the compiler is still usable. Co-Authored-By: Claude Opus 5 (cherry picked from commit a7e6682f197f7603915e5de75bc17683f8be6455) --- clang/tools/offload-arch/IntelGPUArch.def | 69 ++++++++++++++++++ clang/tools/offload-arch/LevelZeroArch.cpp | 73 ++++++++++++++++++- clang/unittests/offload-arch/CMakeLists.txt | 23 ++++-- .../offload-arch/OffloadArchTest.cpp | 47 ++++++++++++ 4 files changed, 202 insertions(+), 10 deletions(-) create mode 100644 clang/tools/offload-arch/IntelGPUArch.def diff --git a/clang/tools/offload-arch/IntelGPUArch.def b/clang/tools/offload-arch/IntelGPUArch.def new file mode 100644 index 0000000000000..75c64b72e2f4a --- /dev/null +++ b/clang/tools/offload-arch/IntelGPUArch.def @@ -0,0 +1,69 @@ +//===--- IntelGPUArch.def - Intel GPU architecture names --------*- 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 lists the human-friendly names of the Intel GPU architectures, +// keyed by the architecture and release components of the GMDID that the +// device reports. The revision component is deliberately not part of the key: +// every stepping of an architecture shares one name. +// +// Several devices can share an architecture and release. The entries are +// ordered so that the name to print for such a group comes first, which lets +// this file be generated from a device list without having to pick a +// representative device. Groups that cover more than one release, such as +// xe-dg2 or xe-mtl, have no GMDID of their own and are therefore not listed. +// +// The intent is for this table to be generated from the data published by the +// GPU driver, so keep it free of anything a generator cannot produce. +// +//===----------------------------------------------------------------------===// + +#ifndef INTEL_GPU_ARCH +#error "Define INTEL_GPU_ARCH prior to including this file!" +#endif + +// INTEL_GPU_ARCH(ARCHITECTURE, RELEASE, NAME) +INTEL_GPU_ARCH(35, 11, "xe-cri") +INTEL_GPU_ARCH(35, 10, "xe-nvl-p") +INTEL_GPU_ARCH(30, 5, "xe-nvl-u") +INTEL_GPU_ARCH(30, 5, "xe-nvl-h") +INTEL_GPU_ARCH(30, 4, "xe-nvl-s") +INTEL_GPU_ARCH(30, 4, "xe-nvl-hx") +INTEL_GPU_ARCH(30, 4, "xe-nvl-ul") +INTEL_GPU_ARCH(30, 3, "xe-wcl") +INTEL_GPU_ARCH(30, 1, "xe-ptl-u") +INTEL_GPU_ARCH(30, 0, "xe-ptl-h") +INTEL_GPU_ARCH(20, 4, "xe-lnl-m") +INTEL_GPU_ARCH(20, 2, "xe-bmg-g31") +INTEL_GPU_ARCH(20, 1, "xe-bmg-g21") +INTEL_GPU_ARCH(12, 74, "xe-arl-h") +INTEL_GPU_ARCH(12, 71, "xe-mtl-h") +INTEL_GPU_ARCH(12, 70, "xe-mtl-u") +INTEL_GPU_ARCH(12, 70, "xe-arl-u") +INTEL_GPU_ARCH(12, 70, "xe-arl-s") +INTEL_GPU_ARCH(12, 61, "xe-pvc-vg") +INTEL_GPU_ARCH(12, 60, "xe-pvc") +INTEL_GPU_ARCH(12, 60, "xe-pvc-sdv") +INTEL_GPU_ARCH(12, 57, "xe-acm-g12") +INTEL_GPU_ARCH(12, 57, "xe-dg2-g12") +INTEL_GPU_ARCH(12, 56, "xe-acm-g11") +INTEL_GPU_ARCH(12, 56, "xe-dg2-g11") +INTEL_GPU_ARCH(12, 56, "xe-ats-m75") +INTEL_GPU_ARCH(12, 55, "xe-acm-g10") +INTEL_GPU_ARCH(12, 55, "xe-dg2-g10") +INTEL_GPU_ARCH(12, 55, "xe-ats-m150") +INTEL_GPU_ARCH(12, 10, "xe-dg1") +INTEL_GPU_ARCH(12, 4, "xe-adl-n") +INTEL_GPU_ARCH(12, 3, "xe-adl-p") +INTEL_GPU_ARCH(12, 3, "xe-rpl-p") +INTEL_GPU_ARCH(12, 2, "xe-adl-s") +INTEL_GPU_ARCH(12, 2, "xe-rpl-s") +INTEL_GPU_ARCH(12, 1, "xe-rkl") +INTEL_GPU_ARCH(12, 0, "xe-tgllp") +INTEL_GPU_ARCH(12, 0, "xe-tgl") + +#undef INTEL_GPU_ARCH diff --git a/clang/tools/offload-arch/LevelZeroArch.cpp b/clang/tools/offload-arch/LevelZeroArch.cpp index 5e543e3231c11..7e1b5c3bb6894 100644 --- a/clang/tools/offload-arch/LevelZeroArch.cpp +++ b/clang/tools/offload-arch/LevelZeroArch.cpp @@ -11,10 +11,12 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/Twine.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/Error.h" #include +#include #define ZE_MAX_DEVICE_NAME 256 #define ZE_MAX_DEVICE_UUID_SIZE 16 @@ -30,6 +32,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 +75,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, @@ -144,6 +154,48 @@ static bool loadLevelZero() { } \ } while (0) +// 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; + +// Human-friendly names of the known Intel GPU architectures, keyed by the +// architecture and release components of the GMDID. Several devices can share +// an architecture and a release, in which case the first of them names the +// whole group. +static constexpr struct { + uint32_t Architecture; + uint32_t Release; + const char *Name; +} IntelGPUArchNames[] = { +#define INTEL_GPU_ARCH(ARCHITECTURE, RELEASE, NAME) \ + {ARCHITECTURE, RELEASE, NAME}, +#include "IntelGPUArch.def" +}; + +// Translate a GMDID into an architecture name that is a legal --offload-arch +// parameter. Known architectures get a human-friendly name, which covers +// almost every device a user is likely to have; anything else gets a numeric +// name built from all three components of the GMDID. +std::string getIntelGPUArchName(uint32_t IPVersion) { + uint32_t Architecture = IPVersion >> GMDIDArchitectureShift; + uint32_t Release = (IPVersion >> GMDIDReleaseShift) & GMDIDReleaseMask; + uint32_t Revision = IPVersion & GMDIDRevisionMask; + + for (const auto &Entry : IntelGPUArchNames) + if (Entry.Architecture == Architecture && Entry.Release == Release) + return Entry.Name; + + // A device this build has never heard of still has to be named, so that it + // can be used with a compiler that predates it. The numeric name spells out + // the revision as well, because it is the only thing left to distinguish two + // steppings of an architecture that has no entry above. + return ("xe_" + Twine(Architecture) + "." + Twine(Release) + "." + + Twine(Revision)) + .str(); +} + int printGPUsByLevelZero() { if (!loadLevelZero()) return 1; @@ -169,11 +221,28 @@ 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"; + + llvm::outs() << getIntelGPUArchName(IPVersion.ipVersion) << '\n'; } } diff --git a/clang/unittests/offload-arch/CMakeLists.txt b/clang/unittests/offload-arch/CMakeLists.txt index db4fa5ceba5ea..87bb291498371 100644 --- a/clang/unittests/offload-arch/CMakeLists.txt +++ b/clang/unittests/offload-arch/CMakeLists.txt @@ -1,10 +1,17 @@ +set(OffloadArchTestSources + OffloadArchTest.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../tools/offload-arch/LevelZeroArch.cpp + ) + if(CMAKE_SYSTEM_NAME STREQUAL "Windows") - add_distinct_clang_unittest(OffloadArchTests - OffloadArchTest.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../tools/offload-arch/AMDGPUArchByHIP.cpp - CLANG_LIBS - clangBasic - LLVM_COMPONENTS - Support - ) + list(APPEND OffloadArchTestSources + ${CMAKE_CURRENT_SOURCE_DIR}/../../tools/offload-arch/AMDGPUArchByHIP.cpp) endif() + +add_distinct_clang_unittest(OffloadArchTests + ${OffloadArchTestSources} + CLANG_LIBS + clangBasic + LLVM_COMPONENTS + Support + ) diff --git a/clang/unittests/offload-arch/OffloadArchTest.cpp b/clang/unittests/offload-arch/OffloadArchTest.cpp index 4b07af39a918a..c7813e6ea0c03 100644 --- a/clang/unittests/offload-arch/OffloadArchTest.cpp +++ b/clang/unittests/offload-arch/OffloadArchTest.cpp @@ -19,6 +19,9 @@ bool compareVersions(llvm::StringRef A, llvm::StringRef B); llvm::SmallVector getCandidateBinPaths(llvm::StringRef ExeDir); #endif +// Defined in LevelZeroArch.cpp (non-static, compiled into this test). +std::string getIntelGPUArchName(uint32_t IPVersion); + using namespace llvm; cl::opt Verbose("offload-arch-test-verbose", cl::Hidden, cl::init(false)); @@ -112,3 +115,47 @@ TEST(CandidateBinPaths, NoDriveRootBin) { } #endif // _WIN32 + +// --- 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 IntelGPUArch.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 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_EQ(getIntelGPUArchName(gmdid(9, 0, 9)), "xe_9.0.9"); +} + From cd8e2fa5a09021944db8040714d4d376a65126a3 Mon Sep 17 00:00:00 2001 From: "Kornev, Nikita" Date: Mon, 31 Aug 2026 14:23:06 +0200 Subject: [PATCH 2/7] format --- clang/unittests/offload-arch/OffloadArchTest.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/unittests/offload-arch/OffloadArchTest.cpp b/clang/unittests/offload-arch/OffloadArchTest.cpp index c7813e6ea0c03..ccb248bf9fc65 100644 --- a/clang/unittests/offload-arch/OffloadArchTest.cpp +++ b/clang/unittests/offload-arch/OffloadArchTest.cpp @@ -158,4 +158,3 @@ TEST(IntelGPUArchName, UnknownArchitecturesGetANumericName) { TEST(IntelGPUArchName, LegacyArchitecture) { EXPECT_EQ(getIntelGPUArchName(gmdid(9, 0, 9)), "xe_9.0.9"); } - From da2643b71e491e29c3a63c1462df8b70ea6c4ae3 Mon Sep 17 00:00:00 2001 From: "Kornev, Nikita" Date: Mon, 31 Aug 2026 14:27:45 +0200 Subject: [PATCH 3/7] apply-review --- clang/unittests/offload-arch/OffloadArchTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/unittests/offload-arch/OffloadArchTest.cpp b/clang/unittests/offload-arch/OffloadArchTest.cpp index ccb248bf9fc65..be6d83d5ed4ff 100644 --- a/clang/unittests/offload-arch/OffloadArchTest.cpp +++ b/clang/unittests/offload-arch/OffloadArchTest.cpp @@ -19,7 +19,7 @@ bool compareVersions(llvm::StringRef A, llvm::StringRef B); llvm::SmallVector getCandidateBinPaths(llvm::StringRef ExeDir); #endif -// Defined in LevelZeroArch.cpp (non-static, compiled into this test). +// Defined in LevelZeroArch.cpp. std::string getIntelGPUArchName(uint32_t IPVersion); using namespace llvm; From 4ddb3e89f136f307104b01bd97377adcb07b2998 Mon Sep 17 00:00:00 2001 From: "Kornev, Nikita" Date: Mon, 31 Aug 2026 20:15:09 +0200 Subject: [PATCH 4/7] [SYCL][Driver] Validate the Intel GPU names of '--offload-arch' Record the IGCA level of every device in IntelGPUArch.def, so that the table lists both names a device answers to, and add the names that cover more than one release (xe-dg2, xe-mtl, xe-bmg, xe-ptl) with a sentinel GMDID of zero. Move the table to clang/include/clang/Basic/ so that the driver and the offload-arch tool can share it. The driver now accepts, and validates against that table, the three forms of a name the GPU driver uses: the name of an architecture ('xe-lnl-m'), the IGCA level shared by a group of architectures ('igca_40r'), and the numeric form the offload-arch tool prints for an architecture this build has no name for ('xe_20.4.5'). Of the numeric form only the architecture and the release are validated, as every stepping of an architecture shares one name. Anything else is rejected with the existing 'unsupported offload gpu architecture' diagnostic. Co-Authored-By: Claude Opus 5 --- clang/include/clang/Basic/IntelGPUArch.def | 82 ++++++++++++++++++ clang/include/clang/Basic/OffloadArch.h | 18 +++- clang/lib/Basic/OffloadArch.cpp | 83 +++++++++++++++++++ .../Driver/sycl-offload-arch-intel-gpu.cpp | 48 +++++++++++ clang/tools/offload-arch/IntelGPUArch.def | 69 --------------- clang/tools/offload-arch/LevelZeroArch.cpp | 13 ++- clang/unittests/Basic/OffloadArchTest.cpp | 52 +++++++++++- .../offload-arch/OffloadArchTest.cpp | 6 ++ 8 files changed, 294 insertions(+), 77 deletions(-) create mode 100644 clang/include/clang/Basic/IntelGPUArch.def delete mode 100644 clang/tools/offload-arch/IntelGPUArch.def diff --git a/clang/include/clang/Basic/IntelGPUArch.def b/clang/include/clang/Basic/IntelGPUArch.def new file mode 100644 index 0000000000000..a7e01afb08ac7 --- /dev/null +++ b/clang/include/clang/Basic/IntelGPUArch.def @@ -0,0 +1,82 @@ +//===--- IntelGPUArch.def - Intel GPU architecture names --------*- 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 lists the human-friendly names of the Intel GPU architectures, +// keyed by the architecture and release components of the GMDID that the +// device reports. The revision component is deliberately not part of the key: +// every stepping of an architecture shares one name. +// +// Several devices can share an architecture and release. The entries are +// ordered so that the name to print for such a group comes first, which lets +// this file be generated from a device list without having to pick a +// representative device. A name that covers more than one release, such as +// xe-dg2 or xe-mtl, has no GMDID of its own and is listed with a sentinel +// architecture and release of zero; consumers that name a device must skip +// those entries. +// +// Every entry also records the IGCA level of the device, the level of the +// Intel Graphics Compiler architecture that the device implements. Devices +// that share an IGCA level are binary compatible with each other. +// +// Together, the names and the IGCA levels listed here are the Intel GPU +// architectures that '--offload-arch' accepts. +// +// The intent is for this table to be generated from the data published by the +// GPU driver, so keep it free of anything a generator cannot produce. +// +//===----------------------------------------------------------------------===// + +#ifndef INTEL_GPU_ARCH +#error "Define INTEL_GPU_ARCH prior to including this file!" +#endif + +// INTEL_GPU_ARCH(ARCHITECTURE, RELEASE, NAME, IGCA) +INTEL_GPU_ARCH(35, 11, "xe-cri", "igca_60c") +INTEL_GPU_ARCH(35, 10, "xe-nvl-p", "igca_60r") +INTEL_GPU_ARCH(30, 5, "xe-nvl-u", "igca_60r") +INTEL_GPU_ARCH(30, 5, "xe-nvl-h", "igca_60r") +INTEL_GPU_ARCH(30, 4, "xe-nvl-s", "igca_60r") +INTEL_GPU_ARCH(30, 4, "xe-nvl-hx", "igca_60r") +INTEL_GPU_ARCH(30, 4, "xe-nvl-ul", "igca_60r") +INTEL_GPU_ARCH(30, 3, "xe-wcl", "igca_50r") +INTEL_GPU_ARCH(30, 1, "xe-ptl-u", "igca_50r") +INTEL_GPU_ARCH(30, 0, "xe-ptl-h", "igca_50r") +INTEL_GPU_ARCH(0, 0, "xe-ptl", "igca_50r") +INTEL_GPU_ARCH(20, 4, "xe-lnl-m", "igca_40r") +INTEL_GPU_ARCH(20, 2, "xe-bmg-g31", "igca_40r") +INTEL_GPU_ARCH(20, 1, "xe-bmg-g21", "igca_40r") +INTEL_GPU_ARCH(0, 0, "xe-bmg", "igca_40r") +INTEL_GPU_ARCH(12, 74, "xe-arl-h", "igca_35r") +INTEL_GPU_ARCH(12, 71, "xe-mtl-h", "igca_30r") +INTEL_GPU_ARCH(12, 70, "xe-mtl-u", "igca_30r") +INTEL_GPU_ARCH(12, 70, "xe-arl-u", "igca_30r") +INTEL_GPU_ARCH(12, 70, "xe-arl-s", "igca_30r") +INTEL_GPU_ARCH(0, 0, "xe-mtl", "igca_30r") +INTEL_GPU_ARCH(12, 61, "xe-pvc-vg", "igca_20ca") +INTEL_GPU_ARCH(12, 60, "xe-pvc", "igca_20ca") +INTEL_GPU_ARCH(12, 60, "xe-pvc-sdv", "igca_20ca") +INTEL_GPU_ARCH(12, 57, "xe-acm-g12", "igca_15ra") +INTEL_GPU_ARCH(12, 57, "xe-dg2-g12", "igca_15ra") +INTEL_GPU_ARCH(12, 56, "xe-acm-g11", "igca_15ra") +INTEL_GPU_ARCH(12, 56, "xe-dg2-g11", "igca_15ra") +INTEL_GPU_ARCH(12, 56, "xe-ats-m75", "igca_15ra") +INTEL_GPU_ARCH(12, 55, "xe-acm-g10", "igca_15ra") +INTEL_GPU_ARCH(12, 55, "xe-dg2-g10", "igca_15ra") +INTEL_GPU_ARCH(12, 55, "xe-ats-m150", "igca_15ra") +INTEL_GPU_ARCH(0, 0, "xe-dg2", "igca_15ra") +INTEL_GPU_ARCH(12, 10, "xe-dg1", "igca_10r") +INTEL_GPU_ARCH(12, 4, "xe-adl-n", "igca_10r") +INTEL_GPU_ARCH(12, 3, "xe-adl-p", "igca_10r") +INTEL_GPU_ARCH(12, 3, "xe-rpl-p", "igca_10r") +INTEL_GPU_ARCH(12, 2, "xe-adl-s", "igca_10r") +INTEL_GPU_ARCH(12, 2, "xe-rpl-s", "igca_10r") +INTEL_GPU_ARCH(12, 1, "xe-rkl", "igca_10r") +INTEL_GPU_ARCH(12, 0, "xe-tgllp", "igca_10r") +INTEL_GPU_ARCH(12, 0, "xe-tgl", "igca_10r") + +#undef INTEL_GPU_ARCH diff --git a/clang/include/clang/Basic/OffloadArch.h b/clang/include/clang/Basic/OffloadArch.h index fe1ebca8f2558..176497714fa94 100644 --- a/clang/include/clang/Basic/OffloadArch.h +++ b/clang/include/clang/Basic/OffloadArch.h @@ -40,7 +40,11 @@ class OffloadArch { SPIRV, // The 'amdgcnspirv' pseudo target. IntelCPU, // Kind is an IntelArch. IntelGPU, // Kind is an IntelArch. - Generic, // The 'generic' processor model. + // An Intel GPU named the way the GPU driver names it: by the architecture + // name or by the IGCA level of an entry of IntelGPUArch.def. Kind is + // opaque; use StringToOffloadArch to build one. + IntelXeGPU, + Generic, // The 'generic' processor model. }; // Intel architectures, which have no TargetParser list yet. @@ -127,6 +131,9 @@ class OffloadArch { static constexpr OffloadArch getIntel(TargetArch V, IntelArch A) { return {V, static_cast(A)}; } + static constexpr OffloadArch getIntelXeGPU(uint32_t Kind) { + return {TargetArch::IntelXeGPU, Kind}; + } static constexpr OffloadArch getUnused() { return {TargetArch::Unused, 0}; } static constexpr OffloadArch getUnknown() { return {TargetArch::Unknown, 0}; } static constexpr OffloadArch getSPIRV() { return {TargetArch::SPIRV, 0}; } @@ -142,7 +149,10 @@ class OffloadArch { bool isAMDGPU() const { return V == TargetArch::AMDGPU; } bool isSPIRV() const { return V == TargetArch::SPIRV; } bool isIntelCPU() const { return V == TargetArch::IntelCPU; } - bool isIntelGPU() const { return V == TargetArch::IntelGPU; } + bool isIntelXeGPU() const { return V == TargetArch::IntelXeGPU; } + bool isIntelGPU() const { + return V == TargetArch::IntelGPU || isIntelXeGPU(); + } bool isIntel() const { return isIntelCPU() || isIntelGPU(); } bool isGeneric() const { return V == TargetArch::Generic; } bool isUnused() const { return V == TargetArch::Unused; } @@ -155,8 +165,10 @@ class OffloadArch { llvm::AMDGPU::GPUKind amdgpuKind() const { return static_cast(Kind); } - // Only valid when isIntelCPU() / isIntelGPU(). + // Only valid when isIntelCPU(), or when isIntelGPU() and !isIntelXeGPU(). IntelArch intelKind() const { return static_cast(Kind); } + // Only valid when isIntelXeGPU(); opaque outside of OffloadArch.cpp. + uint32_t intelXeKind() const { return Kind; } bool operator==(const OffloadArch &Other) const { return V == Other.V && Kind == Other.Kind; diff --git a/clang/lib/Basic/OffloadArch.cpp b/clang/lib/Basic/OffloadArch.cpp index 3784291d9d482..c33a5ba9758be 100644 --- a/clang/lib/Basic/OffloadArch.cpp +++ b/clang/lib/Basic/OffloadArch.cpp @@ -12,6 +12,7 @@ #include "llvm/TargetParser/AMDGPUTargetParser.h" #include "llvm/TargetParser/NVPTXTargetParser.h" #include "llvm/TargetParser/Triple.h" +#include namespace clang { @@ -101,6 +102,78 @@ static const IntelArchNameMap IntelArchNames[] = { #undef INTEL_CPU #undef INTEL_GPU +namespace { +// The Intel GPU architectures the GPU driver knows about, each with the IGCA +// level it implements. Architectures that share an architecture and a release +// are listed with the one that names the group first; a name that covers more +// than one release has a sentinel architecture and release of zero. +struct IntelGPUArchEntry { + uint32_t Architecture; + uint32_t Release; + const char *Name; + const char *IGCALevel; + + // The entries with a sentinel GMDID name a group of architectures, so the + // numeric form of a name never refers to one of them. + bool namesAGroup() const { return Architecture == 0 && Release == 0; } +}; +} // namespace + +static constexpr IntelGPUArchEntry IntelGPUArchs[] = { +#define INTEL_GPU_ARCH(ARCHITECTURE, RELEASE, NAME, IGCA) \ + {ARCHITECTURE, RELEASE, NAME, IGCA}, +#include "clang/Basic/IntelGPUArch.def" +}; + +static constexpr uint32_t NumIntelGPUArchs = std::size(IntelGPUArchs); + +// An intelXeKind holds the index of the entry it names. Its top bit indicates +// whether the Name or IGCALevel was passed: it is set when the IGCALevel was +// passed. +static constexpr uint32_t IntelGPUIGCALevelFlag = 1u << 31; + +static const IntelGPUArchEntry *lookupIntelGPUArch(OffloadArch A) { + uint32_t Index = A.intelXeKind() & ~IntelGPUIGCALevelFlag; + return Index < NumIntelGPUArchs ? &IntelGPUArchs[Index] : nullptr; +} + +// Parse one of the Intel GPU architecture names listed in IntelGPUArch.def: the +// name of an architecture ("xe-lnl-m"), the IGCA level shared by a group of +// architectures ("igca_40r"), or the numeric form the offload-arch tool prints +// for an architecture that has no name in this build ("xe_20.4.5"). +static OffloadArch parseIntelGPUArch(llvm::StringRef S) { + for (uint32_t Index = 0; Index != NumIntelGPUArchs; ++Index) { + const IntelGPUArchEntry &Entry = IntelGPUArchs[Index]; + if (S == Entry.Name) + return OffloadArch::getIntelXeGPU(Index); + if (S == Entry.IGCALevel) + return OffloadArch::getIntelXeGPU(Index | IntelGPUIGCALevelFlag); + } + + // The numeric form spells out all three components of the GMDID, but only the + // architecture and the release are validated: the revision names a stepping + // of an architecture, and a table keyed by architecture and release cannot + // tell which steppings exist. + if (!S.consume_front("xe_")) + return OffloadArch::getUnknown(); + llvm::StringRef ArchitectureStr, ReleaseStr, RevisionStr; + std::tie(ArchitectureStr, S) = S.split('.'); + std::tie(ReleaseStr, RevisionStr) = S.split('.'); + uint32_t Architecture, Release, Revision; + if (ArchitectureStr.getAsInteger(10, Architecture) || + ReleaseStr.getAsInteger(10, Release) || + RevisionStr.getAsInteger(10, Revision)) + return OffloadArch::getUnknown(); + + for (uint32_t Index = 0; Index != NumIntelGPUArchs; ++Index) { + const IntelGPUArchEntry &Entry = IntelGPUArchs[Index]; + if (!Entry.namesAGroup() && Entry.Architecture == Architecture && + Entry.Release == Release) + return OffloadArch::getIntelXeGPU(Index); + } + return OffloadArch::getUnknown(); +} + static const IntelArchNameMap *lookupIntelArch(OffloadArch::TargetArch V, OffloadArch::IntelArch Arch) { for (const IntelArchNameMap &Entry : IntelArchNames) @@ -134,6 +207,13 @@ const char *OffloadArchToString(OffloadArch A) { lookupIntelArch(A.targetArch(), A.intelKind()); return Entry ? Entry->Name : "unknown"; } + case OffloadArch::TargetArch::IntelXeGPU: { + const IntelGPUArchEntry *Entry = lookupIntelGPUArch(A); + if (!Entry) + return "unknown"; + return A.intelXeKind() & IntelGPUIGCALevelFlag ? Entry->IGCALevel + : Entry->Name; + } case OffloadArch::TargetArch::Generic: return "generic"; } @@ -152,6 +232,7 @@ const char *OffloadArchToVirtualArchString(OffloadArch A) { case OffloadArch::TargetArch::Unused: case OffloadArch::TargetArch::IntelCPU: case OffloadArch::TargetArch::IntelGPU: + case OffloadArch::TargetArch::IntelXeGPU: case OffloadArch::TargetArch::Generic: return ""; } @@ -170,6 +251,8 @@ OffloadArch StringToOffloadArch(llvm::StringRef S) { return OffloadArch::getGeneric(); if (const IntelArchNameMap *Entry = lookupIntelArch(S)) return OffloadArch::getIntel(Entry->V, Entry->Arch); + if (OffloadArch A = parseIntelGPUArch(S); !A.isUnknown()) + return A; // Otherwise defer to the vendor TargetParser GPU lists. if (llvm::NVPTX::GPUKind NV = llvm::NVPTX::parseArch(S)) diff --git a/clang/test/Driver/sycl-offload-arch-intel-gpu.cpp b/clang/test/Driver/sycl-offload-arch-intel-gpu.cpp index 8c73a02072645..6eb906701db02 100644 --- a/clang/test/Driver/sycl-offload-arch-intel-gpu.cpp +++ b/clang/test/Driver/sycl-offload-arch-intel-gpu.cpp @@ -117,7 +117,55 @@ // RUN: %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=lnl_m %s 2>&1 | \ // RUN: FileCheck %s --check-prefixes=TARGET-TRIPLE-GPU,CLANG-OFFLOAD-PACKAGER-GPU-OPTS -DDEV_STR=lnl_m -DMAC_STR=LNL_M +// The architecture names, the IGCA levels and the numeric names listed in +// clang/include/clang/Basic/IntelGPUArch.def are accepted as well. + +// RUN: %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=xe-lnl-m %s 2>&1 | \ +// RUN: FileCheck %s --check-prefixes=TARGET-TRIPLE-GPU-ONLY,CLANG-OFFLOAD-PACKAGER-GPU-OPTS -DDEV_STR=xe-lnl-m + +// RUN: %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=xe-cri %s 2>&1 | \ +// RUN: FileCheck %s --check-prefixes=TARGET-TRIPLE-GPU-ONLY,CLANG-OFFLOAD-PACKAGER-GPU-OPTS -DDEV_STR=xe-cri + +// A name that covers more than one release is accepted too. +// RUN: %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=xe-dg2 %s 2>&1 | \ +// RUN: FileCheck %s --check-prefixes=TARGET-TRIPLE-GPU-ONLY,CLANG-OFFLOAD-PACKAGER-GPU-OPTS -DDEV_STR=xe-dg2 + +// RUN: %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=igca_40r %s 2>&1 | \ +// RUN: FileCheck %s --check-prefixes=TARGET-TRIPLE-GPU-ONLY,CLANG-OFFLOAD-PACKAGER-GPU-OPTS -DDEV_STR=igca_40r + +// The revision component of a numeric name is not validated, as every stepping +// of an architecture shares one name. +// RUN: %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=xe_20.4.0 %s 2>&1 | \ +// RUN: FileCheck %s --check-prefixes=TARGET-TRIPLE-GPU-ONLY,CLANG-OFFLOAD-PACKAGER-GPU-OPTS -DDEV_STR=xe_20.4.0 + +// RUN: %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=xe_20.4.63 %s 2>&1 | \ +// RUN: FileCheck %s --check-prefixes=TARGET-TRIPLE-GPU-ONLY,CLANG-OFFLOAD-PACKAGER-GPU-OPTS -DDEV_STR=xe_20.4.63 + // TARGET-TRIPLE-GPU: clang{{.*}} "-triple" "spir64_gen-unknown-unknown" // TARGET-TRIPLE-GPU: "-D__SYCL_TARGET_INTEL_GPU_[[MAC_STR]]__" // CLANG-OFFLOAD-PACKAGER-GPU: llvm-offload-binary{{.*}} "--image={{.*}}triple=spir64_gen-unknown-unknown,arch=[[DEV_STR]],kind=sycl" +// TARGET-TRIPLE-GPU-ONLY: clang{{.*}} "-triple" "spir64_gen-unknown-unknown" // CLANG-OFFLOAD-PACKAGER-GPU-OPTS: llvm-offload-binary{{.*}} "--image={{.*}}triple=spir64_gen-unknown-unknown,arch=[[DEV_STR]],kind=sycl{{.*}}" + +// Tests for handling an incorrect architecture. +// +// RUN: not %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=xe-lnl %s 2>&1 | \ +// RUN: FileCheck %s --check-prefix=BAD-ARCH -DBAD_STR=xe-lnl + +// RUN: not %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=igca_99 %s 2>&1 | \ +// RUN: FileCheck %s --check-prefix=BAD-ARCH -DBAD_STR=igca_99 + +// The architecture and the release of a numeric name are validated. +// RUN: not %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=xe_20.99.0 %s 2>&1 | \ +// RUN: FileCheck %s --check-prefix=BAD-ARCH -DBAD_STR=xe_20.99.0 + +// A numeric name spells out all three components of the GMDID. +// RUN: not %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=xe_20.4 %s 2>&1 | \ +// RUN: FileCheck %s --check-prefix=BAD-ARCH -DBAD_STR=xe_20.4 + +// The sentinel GMDID of a name that covers more than one release is not a GMDID +// that a device reports. +// RUN: not %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl --offload-arch=xe_0.0.0 %s 2>&1 | \ +// RUN: FileCheck %s --check-prefix=BAD-ARCH -DBAD_STR=xe_0.0.0 + +// BAD-ARCH: error: unsupported offload gpu architecture: [[BAD_STR]] diff --git a/clang/tools/offload-arch/IntelGPUArch.def b/clang/tools/offload-arch/IntelGPUArch.def deleted file mode 100644 index 75c64b72e2f4a..0000000000000 --- a/clang/tools/offload-arch/IntelGPUArch.def +++ /dev/null @@ -1,69 +0,0 @@ -//===--- IntelGPUArch.def - Intel GPU architecture names --------*- 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 lists the human-friendly names of the Intel GPU architectures, -// keyed by the architecture and release components of the GMDID that the -// device reports. The revision component is deliberately not part of the key: -// every stepping of an architecture shares one name. -// -// Several devices can share an architecture and release. The entries are -// ordered so that the name to print for such a group comes first, which lets -// this file be generated from a device list without having to pick a -// representative device. Groups that cover more than one release, such as -// xe-dg2 or xe-mtl, have no GMDID of their own and are therefore not listed. -// -// The intent is for this table to be generated from the data published by the -// GPU driver, so keep it free of anything a generator cannot produce. -// -//===----------------------------------------------------------------------===// - -#ifndef INTEL_GPU_ARCH -#error "Define INTEL_GPU_ARCH prior to including this file!" -#endif - -// INTEL_GPU_ARCH(ARCHITECTURE, RELEASE, NAME) -INTEL_GPU_ARCH(35, 11, "xe-cri") -INTEL_GPU_ARCH(35, 10, "xe-nvl-p") -INTEL_GPU_ARCH(30, 5, "xe-nvl-u") -INTEL_GPU_ARCH(30, 5, "xe-nvl-h") -INTEL_GPU_ARCH(30, 4, "xe-nvl-s") -INTEL_GPU_ARCH(30, 4, "xe-nvl-hx") -INTEL_GPU_ARCH(30, 4, "xe-nvl-ul") -INTEL_GPU_ARCH(30, 3, "xe-wcl") -INTEL_GPU_ARCH(30, 1, "xe-ptl-u") -INTEL_GPU_ARCH(30, 0, "xe-ptl-h") -INTEL_GPU_ARCH(20, 4, "xe-lnl-m") -INTEL_GPU_ARCH(20, 2, "xe-bmg-g31") -INTEL_GPU_ARCH(20, 1, "xe-bmg-g21") -INTEL_GPU_ARCH(12, 74, "xe-arl-h") -INTEL_GPU_ARCH(12, 71, "xe-mtl-h") -INTEL_GPU_ARCH(12, 70, "xe-mtl-u") -INTEL_GPU_ARCH(12, 70, "xe-arl-u") -INTEL_GPU_ARCH(12, 70, "xe-arl-s") -INTEL_GPU_ARCH(12, 61, "xe-pvc-vg") -INTEL_GPU_ARCH(12, 60, "xe-pvc") -INTEL_GPU_ARCH(12, 60, "xe-pvc-sdv") -INTEL_GPU_ARCH(12, 57, "xe-acm-g12") -INTEL_GPU_ARCH(12, 57, "xe-dg2-g12") -INTEL_GPU_ARCH(12, 56, "xe-acm-g11") -INTEL_GPU_ARCH(12, 56, "xe-dg2-g11") -INTEL_GPU_ARCH(12, 56, "xe-ats-m75") -INTEL_GPU_ARCH(12, 55, "xe-acm-g10") -INTEL_GPU_ARCH(12, 55, "xe-dg2-g10") -INTEL_GPU_ARCH(12, 55, "xe-ats-m150") -INTEL_GPU_ARCH(12, 10, "xe-dg1") -INTEL_GPU_ARCH(12, 4, "xe-adl-n") -INTEL_GPU_ARCH(12, 3, "xe-adl-p") -INTEL_GPU_ARCH(12, 3, "xe-rpl-p") -INTEL_GPU_ARCH(12, 2, "xe-adl-s") -INTEL_GPU_ARCH(12, 2, "xe-rpl-s") -INTEL_GPU_ARCH(12, 1, "xe-rkl") -INTEL_GPU_ARCH(12, 0, "xe-tgllp") -INTEL_GPU_ARCH(12, 0, "xe-tgl") - -#undef INTEL_GPU_ARCH diff --git a/clang/tools/offload-arch/LevelZeroArch.cpp b/clang/tools/offload-arch/LevelZeroArch.cpp index 7e1b5c3bb6894..de7752b90f59f 100644 --- a/clang/tools/offload-arch/LevelZeroArch.cpp +++ b/clang/tools/offload-arch/LevelZeroArch.cpp @@ -163,15 +163,16 @@ static constexpr uint32_t GMDIDRevisionMask = 0x3f; // Human-friendly names of the known Intel GPU architectures, keyed by the // architecture and release components of the GMDID. Several devices can share // an architecture and a release, in which case the first of them names the -// whole group. +// whole group. Names that cover more than one release have a sentinel GMDID of +// zero, as no single device reports them. static constexpr struct { uint32_t Architecture; uint32_t Release; const char *Name; } IntelGPUArchNames[] = { -#define INTEL_GPU_ARCH(ARCHITECTURE, RELEASE, NAME) \ +#define INTEL_GPU_ARCH(ARCHITECTURE, RELEASE, NAME, IGCA) \ {ARCHITECTURE, RELEASE, NAME}, -#include "IntelGPUArch.def" +#include "clang/Basic/IntelGPUArch.def" }; // Translate a GMDID into an architecture name that is a legal --offload-arch @@ -183,9 +184,13 @@ std::string getIntelGPUArchName(uint32_t IPVersion) { uint32_t Release = (IPVersion >> GMDIDReleaseShift) & GMDIDReleaseMask; uint32_t Revision = IPVersion & GMDIDRevisionMask; - for (const auto &Entry : IntelGPUArchNames) + for (const auto &Entry : IntelGPUArchNames) { + // The entries that name a group of releases have no GMDID to match against. + if (Entry.Architecture == 0 && Entry.Release == 0) + continue; if (Entry.Architecture == Architecture && Entry.Release == Release) return Entry.Name; + } // A device this build has never heard of still has to be named, so that it // can be used with a compiler that predates it. The numeric name spells out diff --git a/clang/unittests/Basic/OffloadArchTest.cpp b/clang/unittests/Basic/OffloadArchTest.cpp index 24f42d668183c..888a98926d14e 100644 --- a/clang/unittests/Basic/OffloadArchTest.cpp +++ b/clang/unittests/Basic/OffloadArchTest.cpp @@ -56,7 +56,8 @@ TEST(OffloadArchTest, Unknown) { TEST(OffloadArchTest, RoundTrip) { for (const char *Name : {"sm_52", "sm_90a", "gfx906", "gfx1201", "gfx12-generic", "amdgcnspirv", - "graniterapids", "bmg_g21", "generic"}) { + "graniterapids", "bmg_g21", "generic", "xe-bmg-g21", "xe-dg2", + "igca_40r"}) { OffloadArch A = parse(Name); EXPECT_FALSE(A.isUnknown()) << Name; EXPECT_STREQ(OffloadArchToString(A), Name); @@ -89,3 +90,52 @@ TEST(OffloadArchTest, IntelGPUFamilyArchParsing) { OffloadArch::getIntel(OffloadArch::TargetArch::IntelGPU, OffloadArch::IntelArch::PTL)); } + +// The names the GPU driver uses, as listed in IntelGPUArch.def: the name of an +// architecture, the IGCA level of a group of architectures, or the numeric form +// of a GMDID. +TEST(OffloadArchTest, IntelGPUArchNames) { + EXPECT_TRUE(parse("xe-lnl-m").isIntelXeGPU()); + EXPECT_TRUE(parse("xe-lnl-m").isIntelGPU()); + EXPECT_TRUE(parse("xe-lnl-m").isIntel()); + EXPECT_TRUE(parse("xe-cri").isIntelXeGPU()); + EXPECT_TRUE(parse("igca_40r").isIntelXeGPU()); + EXPECT_TRUE(parse("xe_20.4.5").isIntelXeGPU()); + + // A name that covers more than one release has no GMDID of its own, but is + // still a name. + EXPECT_TRUE(parse("xe-dg2").isIntelXeGPU()); + + EXPECT_TRUE(parse("xe-lnl").isUnknown()); + EXPECT_TRUE(parse("igca_99").isUnknown()); +} + +// The revision component of a numeric name is not validated: every stepping of +// an architecture shares one name. +TEST(OffloadArchTest, IntelGPUNumericArchNames) { + EXPECT_EQ(parse("xe_20.4.0"), parse("xe-lnl-m")); + EXPECT_EQ(parse("xe_20.4.63"), parse("xe-lnl-m")); + + // Several architectures can share an architecture and a release, in which + // case the first of them names the group. + EXPECT_EQ(parse("xe_12.55.0"), parse("xe-acm-g10")); + + // An architecture and release that no entry has. + EXPECT_TRUE(parse("xe_20.99.0").isUnknown()); + + // The sentinel GMDID of a name that covers more than one release is not a + // GMDID that a device reports. + EXPECT_TRUE(parse("xe_0.0.0").isUnknown()); + + for (const char *Name : {"xe_20.4", "xe_20.4.5.6", "xe_20.4.x", "xe_.4.5", + "xe_-20.4.5", "xe_20.4.-5", "xe_"}) + EXPECT_TRUE(parse(Name).isUnknown()) << Name; +} + +// The IGCA level and the name of an architecture stay apart, as they name +// different sets of devices. +TEST(OffloadArchTest, IntelGPUArchNameAndIGCALevelDiffer) { + EXPECT_NE(parse("igca_40r"), parse("xe-lnl-m")); + EXPECT_STREQ(OffloadArchToString(parse("igca_40r")), "igca_40r"); + EXPECT_STREQ(OffloadArchToString(parse("xe-bmg-g31")), "xe-bmg-g31"); +} diff --git a/clang/unittests/offload-arch/OffloadArchTest.cpp b/clang/unittests/offload-arch/OffloadArchTest.cpp index be6d83d5ed4ff..b16c52eac2db6 100644 --- a/clang/unittests/offload-arch/OffloadArchTest.cpp +++ b/clang/unittests/offload-arch/OffloadArchTest.cpp @@ -158,3 +158,9 @@ TEST(IntelGPUArchName, UnknownArchitecturesGetANumericName) { TEST(IntelGPUArchName, LegacyArchitecture) { EXPECT_EQ(getIntelGPUArchName(gmdid(9, 0, 9)), "xe_9.0.9"); } + +// The entries that name a group of releases carry a sentinel GMDID that no +// device reports, so they never name a device. +TEST(IntelGPUArchName, GroupNamesDoNotNameADevice) { + EXPECT_EQ(getIntelGPUArchName(gmdid(0, 0, 0)), "xe_0.0.0"); +} From d992cfcab50b81bc1e96012d05f2199fe499b659 Mon Sep 17 00:00:00 2001 From: "Kornev, Nikita" Date: Thu, 3 Sep 2026 14:50:02 +0200 Subject: [PATCH 5/7] apply-review --- clang/include/clang/Basic/OffloadArch.h | 18 +++++++++++++----- clang/lib/Basic/OffloadArch.cpp | 23 ++++++++++++++--------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/clang/include/clang/Basic/OffloadArch.h b/clang/include/clang/Basic/OffloadArch.h index 176497714fa94..9472592dd8b13 100644 --- a/clang/include/clang/Basic/OffloadArch.h +++ b/clang/include/clang/Basic/OffloadArch.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_BASIC_OFFLOADARCH_H #include "llvm/ADT/StringRef.h" +#include #include #include @@ -131,9 +132,9 @@ class OffloadArch { static constexpr OffloadArch getIntel(TargetArch V, IntelArch A) { return {V, static_cast(A)}; } - static constexpr OffloadArch getIntelXeGPU(uint32_t Kind) { - return {TargetArch::IntelXeGPU, Kind}; - } + // Defined out of line, so that Kind can be checked against the table of + // IntelGPUArch.def entries that gives it its meaning. + static OffloadArch getIntelXeGPU(uint32_t Kind); static constexpr OffloadArch getUnused() { return {TargetArch::Unused, 0}; } static constexpr OffloadArch getUnknown() { return {TargetArch::Unknown, 0}; } static constexpr OffloadArch getSPIRV() { return {TargetArch::SPIRV, 0}; } @@ -166,9 +167,16 @@ class OffloadArch { return static_cast(Kind); } // Only valid when isIntelCPU(), or when isIntelGPU() and !isIntelXeGPU(). - IntelArch intelKind() const { return static_cast(Kind); } + IntelArch intelKind() const { + assert((isIntelCPU() || (isIntelGPU() && !isIntelXeGPU())) && + "not an Intel CPU or a non-Xe Intel GPU"); + return static_cast(Kind); + } // Only valid when isIntelXeGPU(); opaque outside of OffloadArch.cpp. - uint32_t intelXeKind() const { return Kind; } + uint32_t intelXeKind() const { + assert(isIntelXeGPU() && "not an Intel Xe GPU"); + return Kind; + } bool operator==(const OffloadArch &Other) const { return V == Other.V && Kind == Other.Kind; diff --git a/clang/lib/Basic/OffloadArch.cpp b/clang/lib/Basic/OffloadArch.cpp index c33a5ba9758be..a40f83180ed51 100644 --- a/clang/lib/Basic/OffloadArch.cpp +++ b/clang/lib/Basic/OffloadArch.cpp @@ -132,15 +132,22 @@ static constexpr uint32_t NumIntelGPUArchs = std::size(IntelGPUArchs); // passed. static constexpr uint32_t IntelGPUIGCALevelFlag = 1u << 31; -static const IntelGPUArchEntry *lookupIntelGPUArch(OffloadArch A) { - uint32_t Index = A.intelXeKind() & ~IntelGPUIGCALevelFlag; - return Index < NumIntelGPUArchs ? &IntelGPUArchs[Index] : nullptr; +OffloadArch OffloadArch::getIntelXeGPU(uint32_t Kind) { + assert((Kind & ~IntelGPUIGCALevelFlag) < NumIntelGPUArchs && + "Kind does not name an entry of IntelGPUArch.def"); + return {TargetArch::IntelXeGPU, Kind}; +} + +static const IntelGPUArchEntry &lookupIntelGPUArch(OffloadArch A) { + return IntelGPUArchs[A.intelXeKind() & ~IntelGPUIGCALevelFlag]; } // Parse one of the Intel GPU architecture names listed in IntelGPUArch.def: the // name of an architecture ("xe-lnl-m"), the IGCA level shared by a group of // architectures ("igca_40r"), or the numeric form the offload-arch tool prints -// for an architecture that has no name in this build ("xe_20.4.5"). +// for an architecture that has no name in this build ("xe_20.4.5"). A name that +// is none of these yields an unknown architecture, which the driver reports as +// 'unsupported offload gpu architecture: '. static OffloadArch parseIntelGPUArch(llvm::StringRef S) { for (uint32_t Index = 0; Index != NumIntelGPUArchs; ++Index) { const IntelGPUArchEntry &Entry = IntelGPUArchs[Index]; @@ -208,11 +215,9 @@ const char *OffloadArchToString(OffloadArch A) { return Entry ? Entry->Name : "unknown"; } case OffloadArch::TargetArch::IntelXeGPU: { - const IntelGPUArchEntry *Entry = lookupIntelGPUArch(A); - if (!Entry) - return "unknown"; - return A.intelXeKind() & IntelGPUIGCALevelFlag ? Entry->IGCALevel - : Entry->Name; + const IntelGPUArchEntry &Entry = lookupIntelGPUArch(A); + return A.intelXeKind() & IntelGPUIGCALevelFlag ? Entry.IGCALevel + : Entry.Name; } case OffloadArch::TargetArch::Generic: return "generic"; From b8a37cef668a90a259af0ccda8b9726440f79b12 Mon Sep 17 00:00:00 2001 From: "Kornev, Nikita" Date: Thu, 3 Sep 2026 15:00:00 +0200 Subject: [PATCH 6/7] restore --- clang/lib/Basic/OffloadArch.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/clang/lib/Basic/OffloadArch.cpp b/clang/lib/Basic/OffloadArch.cpp index a40f83180ed51..f8338f190db77 100644 --- a/clang/lib/Basic/OffloadArch.cpp +++ b/clang/lib/Basic/OffloadArch.cpp @@ -138,8 +138,9 @@ OffloadArch OffloadArch::getIntelXeGPU(uint32_t Kind) { return {TargetArch::IntelXeGPU, Kind}; } -static const IntelGPUArchEntry &lookupIntelGPUArch(OffloadArch A) { - return IntelGPUArchs[A.intelXeKind() & ~IntelGPUIGCALevelFlag]; +static const IntelGPUArchEntry *lookupIntelGPUArch(OffloadArch A) { + uint32_t Index = A.intelXeKind() & ~IntelGPUIGCALevelFlag; + return Index < NumIntelGPUArchs ? &IntelGPUArchs[Index] : nullptr; } // Parse one of the Intel GPU architecture names listed in IntelGPUArch.def: the @@ -215,9 +216,11 @@ const char *OffloadArchToString(OffloadArch A) { return Entry ? Entry->Name : "unknown"; } case OffloadArch::TargetArch::IntelXeGPU: { - const IntelGPUArchEntry &Entry = lookupIntelGPUArch(A); - return A.intelXeKind() & IntelGPUIGCALevelFlag ? Entry.IGCALevel - : Entry.Name; + const IntelGPUArchEntry *Entry = lookupIntelGPUArch(A); + if (!Entry) + return "unknown"; + return A.intelXeKind() & IntelGPUIGCALevelFlag ? Entry->IGCALevel + : Entry->Name; } case OffloadArch::TargetArch::Generic: return "generic"; From 9709b0a6acabf0313fdf4aea216cc7b8340df8db Mon Sep 17 00:00:00 2001 From: "Kornev, Nikita" Date: Thu, 3 Sep 2026 18:11:32 +0200 Subject: [PATCH 7/7] [SYCL][Driver] Restrict '--offload-arch=native' to Intel GPUs When '--offload-arch=native' is passed, the driver invokes the 'offload-arch' utility and uses its output as the list of ARCH values. The utility prints the names of all the offloading devices present in the system, but SYCL is not supported for every vendor: currently only Intel GPU devices are supported. Pass '--only=intel' to 'offload-arch' when the source language is SYCL, so that only the devices which do support SYCL are reported. Co-Authored-By: Claude Opus 5 --- clang/lib/Driver/Driver.cpp | 4 +++ .../offload-arch/offload_arch_only_intel | 15 +++++++++ .../test/Driver/sycl-offload-arch-native.cpp | 32 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 clang/test/Driver/Inputs/offload-arch/offload_arch_only_intel create mode 100644 clang/test/Driver/sycl-offload-arch-native.cpp diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 6546e611d11e6..5323049f11a21 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -963,6 +963,10 @@ getSystemOffloadArchs(Compilation &C, Action::OffloadKind Kind) { Args.push_back("--only=amdgpu"); else if (Kind == Action::OFK_Cuda) Args.push_back("--only=nvptx"); + else if (Kind == Action::OFK_SYCL) + // SYCL offloading is currently supported for Intel GPU devices only, so + // the devices of the other vendors present in the system are ignored. + Args.push_back("--only=intel"); auto StdoutOrErr = C.getDriver().executeProgram(Args); if (!StdoutOrErr) { diff --git a/clang/test/Driver/Inputs/offload-arch/offload_arch_only_intel b/clang/test/Driver/Inputs/offload-arch/offload_arch_only_intel new file mode 100644 index 0000000000000..b03a604079da4 --- /dev/null +++ b/clang/test/Driver/Inputs/offload-arch/offload_arch_only_intel @@ -0,0 +1,15 @@ +#!/bin/sh +# Mimics the 'offload-arch' utility of a system which has GPUs of several +# vendors installed: the Intel GPU is printed only when the utility is +# restricted to the Intel devices. +case "$*" in +*--only=intel*) + echo "bmg_g21" + ;; +*) + echo "sm_70" + echo "gfx906" + echo "bmg_g21" + ;; +esac +exit 0 diff --git a/clang/test/Driver/sycl-offload-arch-native.cpp b/clang/test/Driver/sycl-offload-arch-native.cpp new file mode 100644 index 0000000000000..efeeb7973651d --- /dev/null +++ b/clang/test/Driver/sycl-offload-arch-native.cpp @@ -0,0 +1,32 @@ +/// Tests the behavior of using -fsycl --offload-new-driver +// --offload-arch=native. +// +// SYCL offloading is supported for Intel GPU devices only, so the driver is +// expected to restrict the 'offload-arch' utility to the Intel devices. + +// Needs chmod +// UNSUPPORTED: system-windows + +// RUN: mkdir -p %t +// RUN: cp %S/Inputs/offload-arch/offload_arch_only_intel %t/ +// RUN: echo '#!/bin/sh' > %t/offload_arch_empty +// RUN: chmod +x %t/offload_arch_only_intel %t/offload_arch_empty + +// The devices of the other vendors reported by 'offload-arch' are not turned +// into SYCL offloading targets. +// RUN: %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl \ +// RUN: --offload-arch=native --offload-arch-tool=%t/offload_arch_only_intel %s 2>&1 | \ +// RUN: FileCheck %s --check-prefix=NATIVE \ +// RUN: --implicit-check-not="nvptx64-nvidia-cuda" \ +// RUN: --implicit-check-not="amdgcn-amd-amdhsa" + +// NATIVE: clang{{.*}} "-triple" "spir64_gen-unknown-unknown" +// NATIVE: "-D__SYCL_TARGET_INTEL_GPU_BMG_G21__" +// NATIVE: llvm-offload-binary{{.*}} "--image={{.*}}triple=spir64_gen-unknown-unknown,arch=bmg_g21,kind=sycl{{.*}}" + +// Case when no device supporting SYCL is detected in the system. +// RUN: not %clangxx -### --offload-new-driver --sysroot=%S/Inputs/SYCL -fsycl \ +// RUN: --offload-arch=native --offload-arch-tool=%t/offload_arch_empty %s 2>&1 | \ +// RUN: FileCheck %s --check-prefix=NO-DEVICE + +// NO-DEVICE: error: cannot determine sycl architecture: No GPU detected in the system