https://github.com/ianayl updated https://github.com/llvm/llvm-project/pull/227468
>From 2c070fcf7fdc8965e9949ffd6ea4ca9a2c4a49ca Mon Sep 17 00:00:00 2001 From: "Li, Ian" <[email protected]> Date: Mon, 28 Sep 2026 17:35:39 -0700 Subject: [PATCH 1/7] WIP addition of IGCA to OffloadArch --- clang/include/clang/Basic/OffloadArch.h | 14 ++++- clang/lib/Basic/OffloadArch.cpp | 17 ++++++ clang/unittests/Basic/OffloadArchTest.cpp | 2 + .../llvm/TargetParser/IntelGPUTargetParser.h | 57 ++++++++++++++++++ .../lib/TargetParser/IntelGPUTargetParser.cpp | 60 +++++++++++++++++++ 5 files changed, 149 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/OffloadArch.h b/clang/include/clang/Basic/OffloadArch.h index ba8040ebe5dc2c..16ed08bcb1d00a 100644 --- a/clang/include/clang/Basic/OffloadArch.h +++ b/clang/include/clang/Basic/OffloadArch.h @@ -22,6 +22,9 @@ enum GPUKind : uint8_t; namespace AMDGPU { enum GPUKind : uint8_t; } +namespace IntelGPU { +struct IGCATarget; +} } // namespace llvm namespace clang { @@ -39,6 +42,7 @@ class OffloadArch { AMDGCNSPIRV, // The 'amdgcnspirv' pseudo target. IntelCPU, // Kind is an IntelArch. IntelGPU, // Kind is an IntelArch. + IntelIGCA, // Kind is an Intel IGCA Target Generic, // The 'generic' processor model. }; @@ -64,6 +68,7 @@ class OffloadArch { static OffloadArch getAMDGPU(llvm::AMDGPU::GPUKind K) { return {TargetArch::AMDGPU, static_cast<uint32_t>(K)}; } + static OffloadArch getIntelIGCA(llvm::IntelGPU::IGCATarget T); static constexpr OffloadArch getIntel(TargetArch V, IntelArch A) { return {V, static_cast<uint32_t>(A)}; } @@ -85,18 +90,23 @@ class OffloadArch { bool isAMDGCNSPIRV() const { return V == TargetArch::AMDGCNSPIRV; } bool isIntelCPU() const { return V == TargetArch::IntelCPU; } bool isIntelGPU() const { return V == TargetArch::IntelGPU; } + // TODO: Decide whether IntelIGCA counts as Intel here. clang-sycl-linker + // uses isIntel() to decide on AOT compilation, and runAOTCompile would then + // need an IntelIGCA dispatch. bool isIntel() const { return isIntelCPU() || isIntelGPU(); } bool isGeneric() const { return V == TargetArch::Generic; } bool isUnused() const { return V == TargetArch::Unused; } bool isUnknown() const { return V == TargetArch::Unknown; } + bool isIntelIGCA() const { return V == TargetArch::IntelIGCA; } - // Only valid when isNVPTX() / isAMDGPU() respectively. + // Only valid when isNVPTX() / isAMDGPU() / isIntelIGCA() respectively. llvm::NVPTX::GPUKind nvptxKind() const { return static_cast<llvm::NVPTX::GPUKind>(Kind); } llvm::AMDGPU::GPUKind amdgpuKind() const { return static_cast<llvm::AMDGPU::GPUKind>(Kind); } + llvm::IntelGPU::IGCATarget igcaTarget() const; bool operator==(const OffloadArch &Other) const { return V == Other.V && Kind == Other.Kind; @@ -106,6 +116,8 @@ class OffloadArch { bool operator<(const OffloadArch &Other) const { return std::tie(V, Kind) < std::tie(Other.V, Other.Kind); } + // TODO test cases for whether or not IntelIGCA functions properly for these + // comparisons }; const char *OffloadArchToString(OffloadArch A); diff --git a/clang/lib/Basic/OffloadArch.cpp b/clang/lib/Basic/OffloadArch.cpp index ca8dfab142dbb1..8ec02edfa6649c 100644 --- a/clang/lib/Basic/OffloadArch.cpp +++ b/clang/lib/Basic/OffloadArch.cpp @@ -11,6 +11,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/TargetParser/AMDGPUTargetParser.h" #include "llvm/TargetParser/NVPTXTargetParser.h" +#include "llvm/TargetParser/IntelGPUTargetParser.h" #include "llvm/TargetParser/Triple.h" namespace clang { @@ -23,6 +24,14 @@ OffloadArch OffloadArch::HIPDefault() { return getAMDGPU(llvm::AMDGPU::parseArchAMDGCN("gfx906")); } +OffloadArch OffloadArch::getIntelIGCA(llvm::IntelGPU::IGCATarget T) { + return {TargetArch::IntelIGCA, T.pack()}; +} + +llvm::IntelGPU::IGCATarget OffloadArch::igcaTarget() const { + return llvm::IntelGPU::IGCATarget::unpack(Kind); +} + const char *OffloadArchToString(OffloadArch A) { switch (A.targetArch()) { case OffloadArch::TargetArch::Unused: @@ -39,6 +48,8 @@ const char *OffloadArchToString(OffloadArch A) { return "graniterapids"; case OffloadArch::TargetArch::IntelGPU: return "bmg_g21"; + case OffloadArch::TargetArch::IntelIGCA: + return llvm::IntelGPU::getIGCATargetName(A.igcaTarget()).data(); case OffloadArch::TargetArch::Generic: return "generic"; } @@ -54,6 +65,8 @@ const char *OffloadArchToVirtualArchString(OffloadArch A) { return "compute_amdgcn"; case OffloadArch::TargetArch::Unknown: return "unknown"; + case OffloadArch::TargetArch::IntelIGCA: + return llvm::IntelGPU::getIGCATargetName(A.igcaTarget()).data(); case OffloadArch::TargetArch::Unused: case OffloadArch::TargetArch::IntelCPU: case OffloadArch::TargetArch::IntelGPU: @@ -85,6 +98,8 @@ OffloadArch StringToOffloadArch(llvm::StringRef S) { return OffloadArch::getNVPTX(NV); if (llvm::AMDGPU::GPUKind AK = llvm::AMDGPU::parseArchAMDGCN(S)) return OffloadArch::getAMDGPU(AK); + if (llvm::IntelGPU::IGCATarget IT = llvm::IntelGPU::parseIGCATarget(S)) + return OffloadArch::getIntelIGCA(IT); return OffloadArch::getUnknown(); } @@ -119,6 +134,8 @@ llvm::Triple OffloadArchToTriple(const llvm::Triple &DefaultToolchainTriple, return llvm::Triple(llvm::Triple::amdgpu, llvm::Triple::NoSubArch, llvm::Triple::AMD, llvm::Triple::AMDHSA); + // TODO: Handle IntelIGCA, which currently gets an empty triple, so + // --offload-arch=igca_* cannot pick an offload toolchain. return {}; } diff --git a/clang/unittests/Basic/OffloadArchTest.cpp b/clang/unittests/Basic/OffloadArchTest.cpp index 013ed75fbd57eb..f46ec566c7f037 100644 --- a/clang/unittests/Basic/OffloadArchTest.cpp +++ b/clang/unittests/Basic/OffloadArchTest.cpp @@ -54,6 +54,8 @@ TEST(OffloadArchTest, Unknown) { } // Names must round-trip through parse -> string. +// TODO: Add IGCA targets (e.g. "igca_20ca"), plus IntelIGCA classification and +// ==/< ordering tests. TEST(OffloadArchTest, RoundTrip) { for (const char *Name : {"sm_52", "sm_90a", "gfx906", "gfx1201", "gfx12-generic", "amdgcnspirv", diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h index 4ae635e97167ba..b344e99070b485 100644 --- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h +++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h @@ -34,6 +34,48 @@ enum GPUKind : uint8_t { #include "llvm/TargetParser/IntelGPUTargetParser.def" }; +// TODO Do I want the IGCA_ prefix here? +enum class IGCAFeatureSet : uint8_t { + IGCA_CORE = 0, + IGCA_COMPUTE, + IGCA_RENDER +}; + +// TODO these need to be tested: +struct IGCATarget { + uint16_t Target = 0; + IGCAFeatureSet FeatureSet = IGCAFeatureSet::IGCA_CORE; + bool IsExactFeatureSet = false; + + /// Return true if the IGCATarget is a valid, properly initialized target. + bool isValid() const { return Target != 0; } + explicit operator bool() const { return isValid(); } + + bool isCore() const { return FeatureSet == IGCAFeatureSet::IGCA_CORE; } + bool isExact() const { return IsExactFeatureSet; } + bool isCompute() const { return FeatureSet == IGCAFeatureSet::IGCA_COMPUTE; } + bool isRender() const { return FeatureSet == IGCAFeatureSet::IGCA_RENDER; } + bool isComputeExact() const { return isCompute() && isExact(); }; + bool isRenderExact() const { return isRender() && isExact(); }; + + /// Pack an IGCATarget into an uint32_t. + /// + /// We encode as [31:16] Target, [2:1] FeatureSet, [0] IsExactFeatureSet. + /// [15:3] is left as reserved. + uint32_t pack() const; + + /// Obtain an IGCATarget from a packed uint32_t. + static IGCATarget unpack(uint32_t V); + + friend bool operator==(IGCATarget A, IGCATarget B) { + // TODO is this bad? + return A.pack() == B.pack(); + } + + /// Return an invalid IGCA Target that returns false on IGCATarget::isValid(). + static IGCATarget invalid() { return IGCATarget{}; } +}; + /// \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. Only the major and minor versions are looked at, so every revision @@ -44,6 +86,21 @@ LLVM_ABI StringRef getArchName(uint32_t GPUIPVersion); /// \return the numeric name of \p GPUIPVersion, e.g. "xe_35.11.0". LLVM_ABI std::string getNumericArchName(uint32_t GPUIPVersion); +/// Parse an IGCA Target. +IGCATarget parseIGCATarget(StringRef TargetStr); + +/// Return the IGCA string for a given IGCA Target, i.e. "igca_60ca". Returns +/// empty string if Target is not a valid target. +std::string getIGCATargetName(IGCATarget Target); +// TODO to carry stringref's, I need to create that IGCA Target table +// If that IGCA Target table gets really verbose, we should just use the +// strings from that table and use StringRef here instead of std::string. +// TODO DO STRINGREF'S + +// TODO +// - get igcatarget +// - translate other targets? + } // namespace IntelGPU } // namespace llvm diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp index 4b4d162ffbfff7..67721b5fda0fda 100644 --- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp +++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp @@ -63,3 +63,63 @@ std::string llvm::IntelGPU::getNumericArchName(uint32_t GPUIPVersion) { return ("xe_" + Twine(Major) + "." + Twine(Minor) + "." + Twine(Revision)) .str(); } + +IGCATarget llvm::IntelGPU::parseIGCATarget(StringRef MaybeTarget) { + if (!MaybeTarget.consume_front("igca_")) + return IGCATarget::invalid(); + + uint16_t Target; + if (MaybeTarget.consumeInteger(10, Target)) + return IGCATarget::invalid(); + // TODO check that Target contains a valid target level + + IGCAFeatureSet FS = IGCAFeatureSet::IGCA_CORE; + if (MaybeTarget.consume_front("c")) + FS = IGCAFeatureSet::IGCA_COMPUTE; + else if (MaybeTarget.consume_front("r")) + FS = IGCAFeatureSet::IGCA_RENDER; + bool IsExactFS = MaybeTarget.consume_front("a"); + if (!MaybeTarget.empty()) + return IGCATarget::invalid(); + + return { Target, FS, IsExactFS }; +} + +static constexpr uint32_t IGCATargetShift = 16; +static constexpr uint32_t IGCAFeatureSetShift = 1; +static constexpr uint32_t IGCAIsExactFSShift = 0; +static constexpr uint32_t IGCAFeatureSetMask = 0x03; +static constexpr uint32_t IGCAIsExactFSMask = 0x1; + +// An IGCA Target is packed as follows: +// +// 31 16 15 3 2 1 0 0 +// +------------------+--------------+-------------+-----------+ +// | Target | Reserved | Feature set | Is Exact? | +// +------------------+--------------+-------------+-----------+ +// 16 bits 13 bits 2 bits 1 bit +// +uint32_t llvm::IntelGPU::IGCATarget::pack() const { + // TODO add debug mode asserts here + return uint32_t(Target) << IGCATargetShift | + (uint32_t(FeatureSet) & IGCAFeatureSetMask) << IGCAFeatureSetShift | + (uint32_t(IsExactFeatureSet) & IGCAIsExactFSMask) + << IGCAIsExactFSShift; +} + +IGCATarget llvm::IntelGPU::IGCATarget::unpack(uint32_t V) { + return { + uint16_t(V >> IGCATargetShift), + IGCAFeatureSet((V >> IGCAFeatureSetShift) & IGCAFeatureSetMask), + bool((V >> IGCAIsExactFSShift) & IGCAIsExactFSMask) + }; +} + +std::string llvm::IntelGPU::getIGCATargetName(IGCATarget T) { + if (!T) + return ""; + + StringRef FS = T.isCompute() ? "c" : T.isRender() ? "r" : ""; + StringRef Exact = T.isExact() ? "a" : ""; + return ("igca_" + Twine(T.Target) + FS + Exact).str(); +} >From 5f13c9b7c26a1626318a387a8d8b0c4eb203ecaa Mon Sep 17 00:00:00 2001 From: "Li, Ian" <[email protected]> Date: Tue, 29 Sep 2026 12:22:01 -0700 Subject: [PATCH 2/7] Properly define IGCA targets in targetparser.def, and collapse IGCATarget struct to wrap around uint32_t packed representation instead --- clang/include/clang/Basic/OffloadArch.h | 2 +- .../TargetParser/IntelGPUTargetParser.def | 27 ++++- .../llvm/TargetParser/IntelGPUTargetParser.h | 103 ++++++++++-------- .../lib/TargetParser/IntelGPUTargetParser.cpp | 80 +++++++------- .../TargetParser/IntelGPUTargetParserTest.cpp | 89 +++++++++++++++ 5 files changed, 218 insertions(+), 83 deletions(-) diff --git a/clang/include/clang/Basic/OffloadArch.h b/clang/include/clang/Basic/OffloadArch.h index 16ed08bcb1d00a..bdbbcc2c8b13f2 100644 --- a/clang/include/clang/Basic/OffloadArch.h +++ b/clang/include/clang/Basic/OffloadArch.h @@ -23,7 +23,7 @@ namespace AMDGPU { enum GPUKind : uint8_t; } namespace IntelGPU { -struct IGCATarget; +class IGCATarget; } } // namespace llvm diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def index bc9ba1d3e438b3..dba4da8b7c0a86 100644 --- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def +++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def @@ -17,7 +17,8 @@ // GK_<KIND>. // MAJOR - Major component of the device's GPU IP version. // MINOR - Minor component of the device's GPU IP version. -// IGCA_TARGET - Numeric IGCA target. +// IGCA_TARGET - Numeric IGCA target: Targets should also be defined +// in the INTEL_IGCA_TARGET table. // IGCA_FEATURE_SETS - Token naming the IGCA feature sets the device // implements: Core (none), Compute ("c"), // Render ("r"), ComputeExact ("ca") or @@ -37,6 +38,15 @@ // 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. // +// This file also includes a list of valid IGCA targets, which can be used to +// compile binaries with support different GPU architectures and feature sets: +// +// INTEL_IGCA_TARGET(TARGET) +// TARGET - Numeric IGCA target level, e.g. 60. corresponds to +// "igca_60". A defined IGCA target means feature sets, +// i.e. "igca_<TARGET>c", or "igca_<Target>ra" are also +// accepted. +// //===----------------------------------------------------------------------===// #ifndef INTEL_GPU @@ -92,5 +102,20 @@ 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) +// IGCA target levels. Only targets defined here are valid. +#ifndef INTEL_IGCA_TARGET +#define INTEL_IGCA_TARGET(TARGET) +#endif + +INTEL_IGCA_TARGET(10) +INTEL_IGCA_TARGET(15) +INTEL_IGCA_TARGET(20) +INTEL_IGCA_TARGET(30) +INTEL_IGCA_TARGET(35) +INTEL_IGCA_TARGET(40) +INTEL_IGCA_TARGET(50) +INTEL_IGCA_TARGET(60) + #undef INTEL_GPU #undef INTEL_GPU_COMPAT +#undef INTEL_IGCA_TARGET diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h index b344e99070b485..4931937be3af40 100644 --- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h +++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h @@ -22,6 +22,8 @@ #include <string> namespace llvm { +template <typename T> class SmallVectorImpl; + namespace IntelGPU { /// Intel GPU architecture names, covering both physical devices and the @@ -34,46 +36,62 @@ enum GPUKind : uint8_t { #include "llvm/TargetParser/IntelGPUTargetParser.def" }; -// TODO Do I want the IGCA_ prefix here? -enum class IGCAFeatureSet : uint8_t { +enum IGCAFeatureSet : uint8_t { IGCA_CORE = 0, IGCA_COMPUTE, IGCA_RENDER }; -// TODO these need to be tested: -struct IGCATarget { - uint16_t Target = 0; - IGCAFeatureSet FeatureSet = IGCAFeatureSet::IGCA_CORE; - bool IsExactFeatureSet = false; - - /// Return true if the IGCATarget is a valid, properly initialized target. - bool isValid() const { return Target != 0; } +/// Wrapper around an IGCA target's uint32_t representation, packed using the +/// following format: +/// +/// 31 16 15 3 2 1 0 0 +/// +------------------+--------------+-------------+-----------+ +/// | Target | Reserved | Feature set | Is Exact? | +/// +------------------+--------------+-------------+-----------+ +/// 16 bits 13 bits 2 bits 1 bit +/// +/// Target == 0 denotes an invalid IGCA Target. +class IGCATarget { + uint32_t V = 0; + constexpr explicit IGCATarget(uint32_t V) : V(V) {} + +public: + static constexpr uint32_t TargetShift = 16; + static constexpr uint32_t FeatureSetShift = 1; + static constexpr uint32_t IsExactShift = 0; + static constexpr uint32_t FeatureSetMask = 0x3; + static constexpr uint32_t IsExactMask = 0x1; + + constexpr IGCATarget(uint16_t Target, IGCAFeatureSet FeatureSet, bool IsExact) + : V(uint32_t(Target) << TargetShift | + (uint32_t(FeatureSet) & FeatureSetMask) << FeatureSetShift | + (uint32_t(IsExact) & IsExactMask) << IsExactShift) {} + + /// \return an invalid IGCATarget. + static constexpr IGCATarget invalid() { return IGCATarget(0); } + + /// \return the packed uint32_t representation of the IGCA target. + constexpr uint32_t pack() const { return V; } + /// Wrap a packed uint32_t IGCA target with an IGCATarget class. + static constexpr IGCATarget unpack(uint32_t V) { return IGCATarget(V); } + + uint16_t getTarget() const { return uint16_t(V >> TargetShift); } + IGCAFeatureSet getFeatureSet() const { + return IGCAFeatureSet((V >> FeatureSetShift) & FeatureSetMask); + } + bool isExact() const { return (V >> IsExactShift) & IsExactMask; } + bool isValid() const { return getTarget() != 0; } explicit operator bool() const { return isValid(); } - bool isCore() const { return FeatureSet == IGCAFeatureSet::IGCA_CORE; } - bool isExact() const { return IsExactFeatureSet; } - bool isCompute() const { return FeatureSet == IGCAFeatureSet::IGCA_COMPUTE; } - bool isRender() const { return FeatureSet == IGCAFeatureSet::IGCA_RENDER; } - bool isComputeExact() const { return isCompute() && isExact(); }; - bool isRenderExact() const { return isRender() && isExact(); }; - - /// Pack an IGCATarget into an uint32_t. - /// - /// We encode as [31:16] Target, [2:1] FeatureSet, [0] IsExactFeatureSet. - /// [15:3] is left as reserved. - uint32_t pack() const; - - /// Obtain an IGCATarget from a packed uint32_t. - static IGCATarget unpack(uint32_t V); - - friend bool operator==(IGCATarget A, IGCATarget B) { - // TODO is this bad? - return A.pack() == B.pack(); - } + bool isCore() const { return getFeatureSet() == IGCA_CORE; } + bool isCompute() const { return getFeatureSet() == IGCA_COMPUTE; } + bool isRender() const { return getFeatureSet() == IGCA_RENDER; } + bool isComputeExact() const { return isCompute() && isExact(); } + bool isRenderExact() const { return isRender() && isExact(); } - /// Return an invalid IGCA Target that returns false on IGCATarget::isValid(). - static IGCATarget invalid() { return IGCATarget{}; } + friend bool operator==(IGCATarget A, IGCATarget B) { return A.V == B.V; } + friend bool operator!=(IGCATarget A, IGCATarget B) { return A.V != B.V; } }; /// \return the device name that \p GPUIPVersion identifies, as reported by the @@ -86,20 +104,17 @@ LLVM_ABI StringRef getArchName(uint32_t GPUIPVersion); /// \return the numeric name of \p GPUIPVersion, e.g. "xe_35.11.0". LLVM_ABI std::string getNumericArchName(uint32_t GPUIPVersion); -/// Parse an IGCA Target. -IGCATarget parseIGCATarget(StringRef TargetStr); +/// Parse an IGCA target string, such as "igca_60ca". \return an invalid +/// IGCATarget if \p TargetStr is not a known target in +/// IntelGPUTargetParser.def +LLVM_ABI IGCATarget parseIGCATarget(StringRef TargetStr); -/// Return the IGCA string for a given IGCA Target, i.e. "igca_60ca". Returns -/// empty string if Target is not a valid target. -std::string getIGCATargetName(IGCATarget Target); -// TODO to carry stringref's, I need to create that IGCA Target table -// If that IGCA Target table gets really verbose, we should just use the -// strings from that table and use StringRef here instead of std::string. -// TODO DO STRINGREF'S +/// \return the \p Target as a string, i.e. "igca_60ca". \return an empty string +/// if \p Target is invalid or not a known target in IntelGPUTargetParser.def. +LLVM_ABI StringRef getIGCATargetName(IGCATarget Target); -// TODO -// - get igcatarget -// - translate other targets? +/// Append every legal IGCA target spelling to \p Values. +LLVM_ABI void fillValidIGCATargetList(SmallVectorImpl<StringRef> &Values); } // namespace IntelGPU } // namespace llvm diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp index 67721b5fda0fda..7b861b71e4c6a6 100644 --- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp +++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp @@ -64,62 +64,68 @@ std::string llvm::IntelGPU::getNumericArchName(uint32_t GPUIPVersion) { .str(); } +/// Check that Level is a known IGCA Target in IntelGPUTargetParser.def. +bool isKnownIGCATargetLevel(uint16_t Level) { + switch (Level) { +#define INTEL_IGCA_TARGET(TARGET) case TARGET: +#include "llvm/TargetParser/IntelGPUTargetParser.def" + return true; + default: + return false; + } +} + IGCATarget llvm::IntelGPU::parseIGCATarget(StringRef MaybeTarget) { if (!MaybeTarget.consume_front("igca_")) return IGCATarget::invalid(); uint16_t Target; - if (MaybeTarget.consumeInteger(10, Target)) + if (MaybeTarget.consumeInteger(10, Target) || !isKnownIGCATargetLevel(Target)) return IGCATarget::invalid(); - // TODO check that Target contains a valid target level IGCAFeatureSet FS = IGCAFeatureSet::IGCA_CORE; if (MaybeTarget.consume_front("c")) FS = IGCAFeatureSet::IGCA_COMPUTE; else if (MaybeTarget.consume_front("r")) FS = IGCAFeatureSet::IGCA_RENDER; - bool IsExactFS = MaybeTarget.consume_front("a"); + // Exact form needs to either be compute or render: + bool IsExactFS = ( + FS != IGCAFeatureSet::IGCA_CORE && MaybeTarget.consume_front("a") + ); if (!MaybeTarget.empty()) return IGCATarget::invalid(); return { Target, FS, IsExactFS }; } -static constexpr uint32_t IGCATargetShift = 16; -static constexpr uint32_t IGCAFeatureSetShift = 1; -static constexpr uint32_t IGCAIsExactFSShift = 0; -static constexpr uint32_t IGCAFeatureSetMask = 0x03; -static constexpr uint32_t IGCAIsExactFSMask = 0x1; - -// An IGCA Target is packed as follows: -// -// 31 16 15 3 2 1 0 0 -// +------------------+--------------+-------------+-----------+ -// | Target | Reserved | Feature set | Is Exact? | -// +------------------+--------------+-------------+-----------+ -// 16 bits 13 bits 2 bits 1 bit -// -uint32_t llvm::IntelGPU::IGCATarget::pack() const { - // TODO add debug mode asserts here - return uint32_t(Target) << IGCATargetShift | - (uint32_t(FeatureSet) & IGCAFeatureSetMask) << IGCAFeatureSetShift | - (uint32_t(IsExactFeatureSet) & IGCAIsExactFSMask) - << IGCAIsExactFSShift; -} - -IGCATarget llvm::IntelGPU::IGCATarget::unpack(uint32_t V) { - return { - uint16_t(V >> IGCATargetShift), - IGCAFeatureSet((V >> IGCAFeatureSetShift) & IGCAFeatureSetMask), - bool((V >> IGCAIsExactFSShift) & IGCAIsExactFSMask) - }; +StringRef llvm::IntelGPU::getIGCATargetName(IGCATarget T) { + switch (T.pack()) { +#define INTEL_IGCA_TARGET(TARGET) \ + case IGCATarget(TARGET, IGCAFeatureSet::IGCA_CORE, false).pack(): \ + return "igca_" #TARGET; \ + case IGCATarget(TARGET, IGCAFeatureSet::IGCA_COMPUTE, false).pack(): \ + return "igca_" #TARGET "c"; \ + case IGCATarget(TARGET, IGCAFeatureSet::IGCA_COMPUTE, true).pack(): \ + return "igca_" #TARGET "ca"; \ + case IGCATarget(TARGET, IGCAFeatureSet::IGCA_RENDER, false).pack(): \ + return "igca_" #TARGET "r"; \ + case IGCATarget(TARGET, IGCAFeatureSet::IGCA_RENDER, true).pack(): \ + return "igca_" #TARGET "ra"; +#include "llvm/TargetParser/IntelGPUTargetParser.def" + default: + return ""; + } } -std::string llvm::IntelGPU::getIGCATargetName(IGCATarget T) { - if (!T) - return ""; +#define INTEL_IGCA_TARGET_FEATURESETS(TARGET) \ + "igca_" #TARGET, "igca_" #TARGET "c", "igca_" #TARGET "ca", \ + "igca_" #TARGET "r", "igca_" #TARGET "ra" - StringRef FS = T.isCompute() ? "c" : T.isRender() ? "r" : ""; - StringRef Exact = T.isExact() ? "a" : ""; - return ("igca_" + Twine(T.Target) + FS + Exact).str(); +void llvm::IntelGPU::fillValidIGCATargetList( + SmallVectorImpl<StringRef> &Values) { +#define INTEL_IGCA_TARGET(TARGET) \ + Values.append({INTEL_IGCA_TARGET_FEATURESETS(TARGET)}); +#include "llvm/TargetParser/IntelGPUTargetParser.def" } + +#undef INTEL_IGCA_TARGET_FEATURESETS diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp index ab8d456769d0da..93a31558e7e03e 100644 --- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp +++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp @@ -78,4 +78,93 @@ TEST(IntelGPUTargetParserTest, NumericArchName) { "xe_12.60.7"); } +TEST(IntelGPUTargetParserTest, IGCATargetBehavior) { + IntelGPU::IGCATarget T(60, IntelGPU::IGCA_COMPUTE, true); + EXPECT_TRUE(T.isValid()); + EXPECT_EQ(T.getTarget(), 60); + EXPECT_EQ(T.getFeatureSet(), IntelGPU::IGCA_COMPUTE); + EXPECT_TRUE(T.isExact()); + EXPECT_TRUE(T.isComputeExact()); + EXPECT_FALSE(T.isRender()); + EXPECT_EQ(IntelGPU::IGCATarget::unpack(T.pack()), T); + EXPECT_NE(T, IntelGPU::IGCATarget(60, IntelGPU::IGCA_COMPUTE, false)); + // Invalid behavior: + EXPECT_FALSE(IntelGPU::IGCATarget::invalid()); + EXPECT_EQ(IntelGPU::IGCATarget::invalid().pack(), 0u); +} + +TEST(IntelGPUTargetParserTest, ParseIGCATarget) { + using IntelGPU::IGCATarget; + EXPECT_EQ(IntelGPU::parseIGCATarget("igca_10"), + IGCATarget(10, IntelGPU::IGCA_CORE, false)); + EXPECT_EQ(IntelGPU::parseIGCATarget("igca_20c"), + IGCATarget(20, IntelGPU::IGCA_COMPUTE, false)); + EXPECT_EQ(IntelGPU::parseIGCATarget("igca_20ca"), + IGCATarget(20, IntelGPU::IGCA_COMPUTE, true)); + EXPECT_EQ(IntelGPU::parseIGCATarget("igca_15r"), + IGCATarget(15, IntelGPU::IGCA_RENDER, false)); + EXPECT_EQ(IntelGPU::parseIGCATarget("igca_15ra"), + IGCATarget(15, IntelGPU::IGCA_RENDER, true)); + // Only levels in the table are valid targets. + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_42")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_0")); + // Cannot have exact without naming feature set: + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_10a")); + // Malformed spellings: + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_10x")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_10cr")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_10caa")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca10")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("IGCA_10")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("")); +} + +TEST(IntelGPUTargetParserTest, IGCATargetName) { + using IntelGPU::IGCATarget; + EXPECT_EQ(IntelGPU::getIGCATargetName( + IGCATarget(60, IntelGPU::IGCA_CORE, false)), + "igca_60"); + EXPECT_EQ(IntelGPU::getIGCATargetName( + IGCATarget(60, IntelGPU::IGCA_COMPUTE, true)), + "igca_60ca"); + EXPECT_EQ(IntelGPU::getIGCATargetName( + IGCATarget(15, IntelGPU::IGCA_RENDER, false)), + "igca_15r"); + EXPECT_EQ(IntelGPU::getIGCATargetName(IGCATarget::invalid()), ""); + // A level that is not in the table has no spelling, and neither does an + // exact core target. + EXPECT_EQ(IntelGPU::getIGCATargetName( + IGCATarget(11, IntelGPU::IGCA_CORE, false)), + ""); + EXPECT_EQ(IntelGPU::getIGCATargetName( + IGCATarget(10, IntelGPU::IGCA_CORE, true)), + ""); +} + +TEST(IntelGPUTargetParserTest, EveryIGCASpellingRoundTrips) { + SmallVector<StringRef> Names; + IntelGPU::fillValidIGCATargetList(Names); + EXPECT_FALSE(Names.empty()); + for (StringRef Name : Names) { + IntelGPU::IGCATarget T = IntelGPU::parseIGCATarget(Name); + EXPECT_TRUE(T.isValid()) << Name; + EXPECT_EQ(IntelGPU::getIGCATargetName(T), Name); + } +} + +TEST(IntelGPUTargetParserTest, EveryDeviceHasValidIGCATargetLevel) { + // Every device's defined IGCA target should have a corresponding entry in + // INTEL_IGCA_TARGET: +#define INTEL_IGCA_TARGET_CHECK(NAME, IGCA_TARGET) \ + EXPECT_TRUE(IntelGPU::parseIGCATarget("igca_" #IGCA_TARGET).isValid()) \ + << NAME; +#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS) \ + INTEL_IGCA_TARGET_CHECK(NAME, IGCA_TARGET) +#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS) \ + INTEL_IGCA_TARGET_CHECK(NAME, IGCA_TARGET) +#include "llvm/TargetParser/IntelGPUTargetParser.def" +#undef INTEL_IGCA_TARGET_CHECK +} + } // namespace >From 9f66c948d302026f4f2ac8b940933bfe351ffcdf Mon Sep 17 00:00:00 2001 From: "Li, Ian" <[email protected]> Date: Tue, 29 Sep 2026 13:24:24 -0700 Subject: [PATCH 3/7] clang-format --- clang/lib/Basic/OffloadArch.cpp | 2 +- .../llvm/TargetParser/IntelGPUTargetParser.h | 8 ++--- .../lib/TargetParser/IntelGPUTargetParser.cpp | 9 +++--- .../TargetParser/IntelGPUTargetParserTest.cpp | 30 +++++++++---------- 4 files changed, 22 insertions(+), 27 deletions(-) diff --git a/clang/lib/Basic/OffloadArch.cpp b/clang/lib/Basic/OffloadArch.cpp index 8ec02edfa6649c..c3004bd3333cb0 100644 --- a/clang/lib/Basic/OffloadArch.cpp +++ b/clang/lib/Basic/OffloadArch.cpp @@ -10,8 +10,8 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/TargetParser/AMDGPUTargetParser.h" -#include "llvm/TargetParser/NVPTXTargetParser.h" #include "llvm/TargetParser/IntelGPUTargetParser.h" +#include "llvm/TargetParser/NVPTXTargetParser.h" #include "llvm/TargetParser/Triple.h" namespace clang { diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h index 4931937be3af40..339becdd102225 100644 --- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h +++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h @@ -36,11 +36,7 @@ enum GPUKind : uint8_t { #include "llvm/TargetParser/IntelGPUTargetParser.def" }; -enum IGCAFeatureSet : uint8_t { - IGCA_CORE = 0, - IGCA_COMPUTE, - IGCA_RENDER -}; +enum IGCAFeatureSet : uint8_t { IGCA_CORE = 0, IGCA_COMPUTE, IGCA_RENDER }; /// Wrapper around an IGCA target's uint32_t representation, packed using the /// following format: @@ -105,7 +101,7 @@ LLVM_ABI StringRef getArchName(uint32_t GPUIPVersion); LLVM_ABI std::string getNumericArchName(uint32_t GPUIPVersion); /// Parse an IGCA target string, such as "igca_60ca". \return an invalid -/// IGCATarget if \p TargetStr is not a known target in +/// IGCATarget if \p TargetStr is not a known target in /// IntelGPUTargetParser.def LLVM_ABI IGCATarget parseIGCATarget(StringRef TargetStr); diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp index 7b861b71e4c6a6..0e1163dff9aec8 100644 --- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp +++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp @@ -82,20 +82,19 @@ IGCATarget llvm::IntelGPU::parseIGCATarget(StringRef MaybeTarget) { uint16_t Target; if (MaybeTarget.consumeInteger(10, Target) || !isKnownIGCATargetLevel(Target)) return IGCATarget::invalid(); - + IGCAFeatureSet FS = IGCAFeatureSet::IGCA_CORE; if (MaybeTarget.consume_front("c")) FS = IGCAFeatureSet::IGCA_COMPUTE; else if (MaybeTarget.consume_front("r")) FS = IGCAFeatureSet::IGCA_RENDER; // Exact form needs to either be compute or render: - bool IsExactFS = ( - FS != IGCAFeatureSet::IGCA_CORE && MaybeTarget.consume_front("a") - ); + bool IsExactFS = + (FS != IGCAFeatureSet::IGCA_CORE && MaybeTarget.consume_front("a")); if (!MaybeTarget.empty()) return IGCATarget::invalid(); - return { Target, FS, IsExactFS }; + return {Target, FS, IsExactFS}; } StringRef llvm::IntelGPU::getIGCATargetName(IGCATarget T) { diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp index 93a31558e7e03e..f1c55912305ece 100644 --- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp +++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp @@ -122,24 +122,24 @@ TEST(IntelGPUTargetParserTest, ParseIGCATarget) { TEST(IntelGPUTargetParserTest, IGCATargetName) { using IntelGPU::IGCATarget; - EXPECT_EQ(IntelGPU::getIGCATargetName( - IGCATarget(60, IntelGPU::IGCA_CORE, false)), - "igca_60"); - EXPECT_EQ(IntelGPU::getIGCATargetName( - IGCATarget(60, IntelGPU::IGCA_COMPUTE, true)), - "igca_60ca"); - EXPECT_EQ(IntelGPU::getIGCATargetName( - IGCATarget(15, IntelGPU::IGCA_RENDER, false)), - "igca_15r"); + EXPECT_EQ( + IntelGPU::getIGCATargetName(IGCATarget(60, IntelGPU::IGCA_CORE, false)), + "igca_60"); + EXPECT_EQ( + IntelGPU::getIGCATargetName(IGCATarget(60, IntelGPU::IGCA_COMPUTE, true)), + "igca_60ca"); + EXPECT_EQ( + IntelGPU::getIGCATargetName(IGCATarget(15, IntelGPU::IGCA_RENDER, false)), + "igca_15r"); EXPECT_EQ(IntelGPU::getIGCATargetName(IGCATarget::invalid()), ""); // A level that is not in the table has no spelling, and neither does an // exact core target. - EXPECT_EQ(IntelGPU::getIGCATargetName( - IGCATarget(11, IntelGPU::IGCA_CORE, false)), - ""); - EXPECT_EQ(IntelGPU::getIGCATargetName( - IGCATarget(10, IntelGPU::IGCA_CORE, true)), - ""); + EXPECT_EQ( + IntelGPU::getIGCATargetName(IGCATarget(11, IntelGPU::IGCA_CORE, false)), + ""); + EXPECT_EQ( + IntelGPU::getIGCATargetName(IGCATarget(10, IntelGPU::IGCA_CORE, true)), + ""); } TEST(IntelGPUTargetParserTest, EveryIGCASpellingRoundTrips) { >From 2f89fa423ffc15e3ca033c574d92c92845e3e980 Mon Sep 17 00:00:00 2001 From: "Li, Ian" <[email protected]> Date: Thu, 1 Oct 2026 15:58:22 -0700 Subject: [PATCH 4/7] Enforce that only certain IGCA target feature sets can exist, simplify code and logic --- .../TargetParser/IntelGPUTargetParser.def | 53 +++++-- .../llvm/TargetParser/IntelGPUTargetParser.h | 62 +++++--- .../lib/TargetParser/IntelGPUTargetParser.cpp | 63 ++------ .../TargetParser/IntelGPUTargetParserTest.cpp | 139 +++++++++++++----- 4 files changed, 199 insertions(+), 118 deletions(-) diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def index dba4da8b7c0a86..7ad6b201c3081d 100644 --- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def +++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def @@ -41,11 +41,15 @@ // This file also includes a list of valid IGCA targets, which can be used to // compile binaries with support different GPU architectures and feature sets: // -// INTEL_IGCA_TARGET(TARGET) -// TARGET - Numeric IGCA target level, e.g. 60. corresponds to -// "igca_60". A defined IGCA target means feature sets, -// i.e. "igca_<TARGET>c", or "igca_<Target>ra" are also -// accepted. +// INTEL_IGCA_TARGET(NAME, TARGET, FEATURE_SET) +// One row per valid IGCA target spelling. Only the combinations listed +// are valid. +// NAME - Name of the IGCA target, e.g. "igca_15ra". Name must +// agree with the other columns. +// TARGET - Numeric IGCA target level, e.g. 60. +// FEATURE_SET - Feature set of the current target, i.e. "Compute" for +// "c" suffix, "Render" for "r", "ComputeExact" for +// "ca", "Core" for "", etc. // //===----------------------------------------------------------------------===// @@ -102,19 +106,38 @@ 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) -// IGCA target levels. Only targets defined here are valid. +// IGCA targets. Only targets listed here are valid. #ifndef INTEL_IGCA_TARGET -#define INTEL_IGCA_TARGET(TARGET) +#define INTEL_IGCA_TARGET(NAME, TARGET, FEATURE_SET) #endif -INTEL_IGCA_TARGET(10) -INTEL_IGCA_TARGET(15) -INTEL_IGCA_TARGET(20) -INTEL_IGCA_TARGET(30) -INTEL_IGCA_TARGET(35) -INTEL_IGCA_TARGET(40) -INTEL_IGCA_TARGET(50) -INTEL_IGCA_TARGET(60) +INTEL_IGCA_TARGET("igca_10", 10, Core) +INTEL_IGCA_TARGET("igca_10r", 10, Render) + +INTEL_IGCA_TARGET("igca_15", 15, Core) +INTEL_IGCA_TARGET("igca_15r", 15, Render) +INTEL_IGCA_TARGET("igca_15ra", 15, RenderExact) + +INTEL_IGCA_TARGET("igca_20", 20, Core) +INTEL_IGCA_TARGET("igca_20c", 20, Compute) +INTEL_IGCA_TARGET("igca_20ca", 20, ComputeExact) + +INTEL_IGCA_TARGET("igca_30", 30, Core) +INTEL_IGCA_TARGET("igca_30r", 30, Render) + +INTEL_IGCA_TARGET("igca_35", 35, Core) +INTEL_IGCA_TARGET("igca_35r", 35, Render) +INTEL_IGCA_TARGET("igca_35ra", 35, RenderExact) + +INTEL_IGCA_TARGET("igca_40", 40, Core) +INTEL_IGCA_TARGET("igca_40r", 40, Render) + +INTEL_IGCA_TARGET("igca_50", 50, Core) +INTEL_IGCA_TARGET("igca_50r", 50, Render) + +INTEL_IGCA_TARGET("igca_60", 60, Core) +INTEL_IGCA_TARGET("igca_60c", 60, Compute) +INTEL_IGCA_TARGET("igca_60r", 60, Render) #undef INTEL_GPU #undef INTEL_GPU_COMPAT diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h index 339becdd102225..7d8e1cf1ab4f8b 100644 --- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h +++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h @@ -16,6 +16,7 @@ #ifndef LLVM_TARGETPARSER_INTELGPUTARGETPARSER_H #define LLVM_TARGETPARSER_INTELGPUTARGETPARSER_H +#include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" #include <cstdint> @@ -36,7 +37,16 @@ enum GPUKind : uint8_t { #include "llvm/TargetParser/IntelGPUTargetParser.def" }; -enum IGCAFeatureSet : uint8_t { IGCA_CORE = 0, IGCA_COMPUTE, IGCA_RENDER }; +/// Represents an IGCA feature set, including whether or not it is exact. The +/// values are bitfields where the last bit represents exact. See IGCA Target +/// representation below for more info. +enum class IGCAFeatureSet : uint8_t { + Core = 0b000, + Compute = 0b010, + ComputeExact = 0b011, + Render = 0b100, + RenderExact = 0b101, +}; /// Wrapper around an IGCA target's uint32_t representation, packed using the /// following format: @@ -54,15 +64,12 @@ class IGCATarget { public: static constexpr uint32_t TargetShift = 16; - static constexpr uint32_t FeatureSetShift = 1; - static constexpr uint32_t IsExactShift = 0; - static constexpr uint32_t FeatureSetMask = 0x3; + static constexpr uint32_t FeatureSetMask = 0x7; static constexpr uint32_t IsExactMask = 0x1; - constexpr IGCATarget(uint16_t Target, IGCAFeatureSet FeatureSet, bool IsExact) + constexpr IGCATarget(uint16_t Target, IGCAFeatureSet FeatureSet) : V(uint32_t(Target) << TargetShift | - (uint32_t(FeatureSet) & FeatureSetMask) << FeatureSetShift | - (uint32_t(IsExact) & IsExactMask) << IsExactShift) {} + (llvm::to_underlying(FeatureSet) & FeatureSetMask)) {} /// \return an invalid IGCATarget. static constexpr IGCATarget invalid() { return IGCATarget(0); } @@ -73,18 +80,39 @@ class IGCATarget { static constexpr IGCATarget unpack(uint32_t V) { return IGCATarget(V); } uint16_t getTarget() const { return uint16_t(V >> TargetShift); } + /// \return the feature set, including whether it is exact. + /// + /// To check if a target has a feature set regardless of exactness, use + /// hasCompute() or hasRender() instead of raw comparison between + /// IGCAFeatureSet enums. IGCAFeatureSet getFeatureSet() const { - return IGCAFeatureSet((V >> FeatureSetShift) & FeatureSetMask); + return IGCAFeatureSet(V & FeatureSetMask); } - bool isExact() const { return (V >> IsExactShift) & IsExactMask; } + bool isExact() const { return V & IsExactMask; } bool isValid() const { return getTarget() != 0; } explicit operator bool() const { return isValid(); } - bool isCore() const { return getFeatureSet() == IGCA_CORE; } - bool isCompute() const { return getFeatureSet() == IGCA_COMPUTE; } - bool isRender() const { return getFeatureSet() == IGCA_RENDER; } - bool isComputeExact() const { return isCompute() && isExact(); } - bool isRenderExact() const { return isRender() && isExact(); } + bool isCore() const { return getFeatureSet() == IGCAFeatureSet::Core; } + /// \return true if the target has a non-exact Compute feature set. + bool isCompute() const { return getFeatureSet() == IGCAFeatureSet::Compute; } + /// \return true if the target has a non-exact Render feature set. + bool isRender() const { return getFeatureSet() == IGCAFeatureSet::Render; } + bool isComputeExact() const { + return getFeatureSet() == IGCAFeatureSet::ComputeExact; + } + bool isRenderExact() const { + return getFeatureSet() == IGCAFeatureSet::RenderExact; + } + /// \return true if target has either Compute or ComputeExact feature set. + bool hasCompute() const { + return (llvm::to_underlying(getFeatureSet()) & ~IsExactMask) == + llvm::to_underlying(IGCAFeatureSet::Compute); + } + /// \return true if target has either Render or RenderExact feature set. + bool hasRender() const { + return (llvm::to_underlying(getFeatureSet()) & ~IsExactMask) == + llvm::to_underlying(IGCAFeatureSet::Render); + } friend bool operator==(IGCATarget A, IGCATarget B) { return A.V == B.V; } friend bool operator!=(IGCATarget A, IGCATarget B) { return A.V != B.V; } @@ -100,16 +128,16 @@ LLVM_ABI StringRef getArchName(uint32_t GPUIPVersion); /// \return the numeric name of \p GPUIPVersion, e.g. "xe_35.11.0". LLVM_ABI std::string getNumericArchName(uint32_t GPUIPVersion); -/// Parse an IGCA target string, such as "igca_60ca". \return an invalid +/// Parse an IGCA target string, such as "igca_20ca". \return an invalid /// IGCATarget if \p TargetStr is not a known target in /// IntelGPUTargetParser.def LLVM_ABI IGCATarget parseIGCATarget(StringRef TargetStr); -/// \return the \p Target as a string, i.e. "igca_60ca". \return an empty string +/// \return the \p Target as a string, i.e. "igca_20ca". \return an empty string /// if \p Target is invalid or not a known target in IntelGPUTargetParser.def. LLVM_ABI StringRef getIGCATargetName(IGCATarget Target); -/// Append every legal IGCA target spelling to \p Values. +/// Append every valid IGCA target spelling to \p Values. LLVM_ABI void fillValidIGCATargetList(SmallVectorImpl<StringRef> &Values); } // namespace IntelGPU diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp index 0e1163dff9aec8..20335d30dc392b 100644 --- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp +++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/TargetParser/IntelGPUTargetParser.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include <cassert> @@ -64,67 +65,31 @@ std::string llvm::IntelGPU::getNumericArchName(uint32_t GPUIPVersion) { .str(); } -/// Check that Level is a known IGCA Target in IntelGPUTargetParser.def. -bool isKnownIGCATargetLevel(uint16_t Level) { - switch (Level) { -#define INTEL_IGCA_TARGET(TARGET) case TARGET: -#include "llvm/TargetParser/IntelGPUTargetParser.def" - return true; - default: - return false; - } -} - IGCATarget llvm::IntelGPU::parseIGCATarget(StringRef MaybeTarget) { - if (!MaybeTarget.consume_front("igca_")) - return IGCATarget::invalid(); - - uint16_t Target; - if (MaybeTarget.consumeInteger(10, Target) || !isKnownIGCATargetLevel(Target)) - return IGCATarget::invalid(); - - IGCAFeatureSet FS = IGCAFeatureSet::IGCA_CORE; - if (MaybeTarget.consume_front("c")) - FS = IGCAFeatureSet::IGCA_COMPUTE; - else if (MaybeTarget.consume_front("r")) - FS = IGCAFeatureSet::IGCA_RENDER; - // Exact form needs to either be compute or render: - bool IsExactFS = - (FS != IGCAFeatureSet::IGCA_CORE && MaybeTarget.consume_front("a")); - if (!MaybeTarget.empty()) - return IGCATarget::invalid(); - - return {Target, FS, IsExactFS}; + return StringSwitch<IGCATarget>(MaybeTarget) +#define INTEL_IGCA_TARGET(NAME, TARGET, FEATURE_SET) \ + .Case(NAME, IGCATarget(TARGET, IGCAFeatureSet::FEATURE_SET)) +#include "llvm/TargetParser/IntelGPUTargetParser.def" + .Default(IGCATarget::invalid()); } StringRef llvm::IntelGPU::getIGCATargetName(IGCATarget T) { switch (T.pack()) { -#define INTEL_IGCA_TARGET(TARGET) \ - case IGCATarget(TARGET, IGCAFeatureSet::IGCA_CORE, false).pack(): \ - return "igca_" #TARGET; \ - case IGCATarget(TARGET, IGCAFeatureSet::IGCA_COMPUTE, false).pack(): \ - return "igca_" #TARGET "c"; \ - case IGCATarget(TARGET, IGCAFeatureSet::IGCA_COMPUTE, true).pack(): \ - return "igca_" #TARGET "ca"; \ - case IGCATarget(TARGET, IGCAFeatureSet::IGCA_RENDER, false).pack(): \ - return "igca_" #TARGET "r"; \ - case IGCATarget(TARGET, IGCAFeatureSet::IGCA_RENDER, true).pack(): \ - return "igca_" #TARGET "ra"; +#define INTEL_IGCA_TARGET(NAME, TARGET, FEATURE_SET) \ + case IGCATarget(TARGET, IGCAFeatureSet::FEATURE_SET).pack(): \ + return NAME; #include "llvm/TargetParser/IntelGPUTargetParser.def" default: return ""; } } -#define INTEL_IGCA_TARGET_FEATURESETS(TARGET) \ - "igca_" #TARGET, "igca_" #TARGET "c", "igca_" #TARGET "ca", \ - "igca_" #TARGET "r", "igca_" #TARGET "ra" - +// TODO: Ensure -fsycl --offload-arch provides a list of valid IGCA +// architectures, similar to how compiling for an nvptx triple returns a list +// of valid GPU architectures. The user trying to input an invalid IGCA target +// without being told what IGCA targets actually exist might get confusing. void llvm::IntelGPU::fillValidIGCATargetList( SmallVectorImpl<StringRef> &Values) { -#define INTEL_IGCA_TARGET(TARGET) \ - Values.append({INTEL_IGCA_TARGET_FEATURESETS(TARGET)}); +#define INTEL_IGCA_TARGET(NAME, TARGET, FEATURE_SET) Values.push_back(NAME); #include "llvm/TargetParser/IntelGPUTargetParser.def" } - -#undef INTEL_IGCA_TARGET_FEATURESETS diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp index f1c55912305ece..b9685a61c4271d 100644 --- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp +++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp @@ -79,33 +79,68 @@ TEST(IntelGPUTargetParserTest, NumericArchName) { } TEST(IntelGPUTargetParserTest, IGCATargetBehavior) { - IntelGPU::IGCATarget T(60, IntelGPU::IGCA_COMPUTE, true); - EXPECT_TRUE(T.isValid()); - EXPECT_EQ(T.getTarget(), 60); - EXPECT_EQ(T.getFeatureSet(), IntelGPU::IGCA_COMPUTE); - EXPECT_TRUE(T.isExact()); - EXPECT_TRUE(T.isComputeExact()); - EXPECT_FALSE(T.isRender()); - EXPECT_EQ(IntelGPU::IGCATarget::unpack(T.pack()), T); - EXPECT_NE(T, IntelGPU::IGCATarget(60, IntelGPU::IGCA_COMPUTE, false)); + using IntelGPU::IGCAFeatureSet; + using IntelGPU::IGCATarget; + IGCATarget C{60, IGCAFeatureSet::ComputeExact}; + EXPECT_TRUE(C.isValid()); + EXPECT_EQ(C.getTarget(), 60); + EXPECT_EQ(C.getFeatureSet(), IGCAFeatureSet::ComputeExact); + EXPECT_TRUE(C.isExact()); + // An exact feature set is still of its class: + EXPECT_TRUE(C.hasCompute()); + EXPECT_TRUE(C.isComputeExact()); + EXPECT_FALSE(C.isCore()); + EXPECT_FALSE(C.isCompute()); + EXPECT_FALSE(C.isRender()); + EXPECT_FALSE(C.hasRender()); + EXPECT_FALSE(C.isRenderExact()); + EXPECT_EQ(IGCATarget::unpack(C.pack()), C); + EXPECT_NE(C, IGCATarget(60, IGCAFeatureSet::Compute)); + // A feature set that is not exact: + IGCATarget R{60, IGCAFeatureSet::Render}; + EXPECT_TRUE(R.isRender()); + EXPECT_FALSE(R.isRenderExact()); + EXPECT_FALSE(R.isExact()); + EXPECT_FALSE(R.isCompute()); + EXPECT_TRUE(R.hasRender()); + EXPECT_FALSE(R.hasCompute()); + EXPECT_EQ(IGCATarget::unpack(R.pack()), R); + IGCATarget Core{60, IGCAFeatureSet::Core}; + EXPECT_TRUE(Core.isCore()); // Invalid behavior: - EXPECT_FALSE(IntelGPU::IGCATarget::invalid()); - EXPECT_EQ(IntelGPU::IGCATarget::invalid().pack(), 0u); + EXPECT_FALSE(IGCATarget::invalid()); + EXPECT_EQ(IGCATarget::invalid().pack(), 0u); } TEST(IntelGPUTargetParserTest, ParseIGCATarget) { + using IntelGPU::IGCAFeatureSet; using IntelGPU::IGCATarget; EXPECT_EQ(IntelGPU::parseIGCATarget("igca_10"), - IGCATarget(10, IntelGPU::IGCA_CORE, false)); + IGCATarget(10, IGCAFeatureSet::Core)); EXPECT_EQ(IntelGPU::parseIGCATarget("igca_20c"), - IGCATarget(20, IntelGPU::IGCA_COMPUTE, false)); + IGCATarget(20, IGCAFeatureSet::Compute)); EXPECT_EQ(IntelGPU::parseIGCATarget("igca_20ca"), - IGCATarget(20, IntelGPU::IGCA_COMPUTE, true)); + IGCATarget(20, IGCAFeatureSet::ComputeExact)); EXPECT_EQ(IntelGPU::parseIGCATarget("igca_15r"), - IGCATarget(15, IntelGPU::IGCA_RENDER, false)); + IGCATarget(15, IGCAFeatureSet::Render)); EXPECT_EQ(IntelGPU::parseIGCATarget("igca_15ra"), - IGCATarget(15, IntelGPU::IGCA_RENDER, true)); - // Only levels in the table are valid targets. + IGCATarget(15, IGCAFeatureSet::RenderExact)); + EXPECT_EQ(IntelGPU::parseIGCATarget("igca_35ra"), + IGCATarget(35, IGCAFeatureSet::RenderExact)); + EXPECT_EQ(IntelGPU::parseIGCATarget("igca_60c"), + IGCATarget(60, IGCAFeatureSet::Compute)); + EXPECT_EQ(IntelGPU::parseIGCATarget("igca_60r"), + IGCATarget(60, IGCAFeatureSet::Render)); + // Not all target levels support all feature sets: + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_10c")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_20r")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_20ra")); + // Only some target levels have an exact feature set: + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_10ra")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_30ra")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_60ca")); + EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_60ra")); + // Only target levels in the table are valid targets: EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_42")); EXPECT_FALSE(IntelGPU::parseIGCATarget("igca_0")); // Cannot have exact without naming feature set: @@ -121,25 +156,25 @@ TEST(IntelGPUTargetParserTest, ParseIGCATarget) { } TEST(IntelGPUTargetParserTest, IGCATargetName) { + using IntelGPU::IGCAFeatureSet; using IntelGPU::IGCATarget; + EXPECT_EQ(IntelGPU::getIGCATargetName(IGCATarget(60, IGCAFeatureSet::Core)), + "igca_60"); EXPECT_EQ( - IntelGPU::getIGCATargetName(IGCATarget(60, IntelGPU::IGCA_CORE, false)), - "igca_60"); - EXPECT_EQ( - IntelGPU::getIGCATargetName(IGCATarget(60, IntelGPU::IGCA_COMPUTE, true)), - "igca_60ca"); - EXPECT_EQ( - IntelGPU::getIGCATargetName(IGCATarget(15, IntelGPU::IGCA_RENDER, false)), - "igca_15r"); + IntelGPU::getIGCATargetName(IGCATarget(20, IGCAFeatureSet::ComputeExact)), + "igca_20ca"); + EXPECT_EQ(IntelGPU::getIGCATargetName(IGCATarget(15, IGCAFeatureSet::Render)), + "igca_15r"); + // A target that does not exist / is not valid should produce "" to signify + // invalid target. EXPECT_EQ(IntelGPU::getIGCATargetName(IGCATarget::invalid()), ""); - // A level that is not in the table has no spelling, and neither does an - // exact core target. + EXPECT_EQ(IntelGPU::getIGCATargetName(IGCATarget(11, IGCAFeatureSet::Core)), + ""); EXPECT_EQ( - IntelGPU::getIGCATargetName(IGCATarget(11, IntelGPU::IGCA_CORE, false)), + IntelGPU::getIGCATargetName(IGCATarget(60, IGCAFeatureSet::ComputeExact)), ""); EXPECT_EQ( - IntelGPU::getIGCATargetName(IGCATarget(10, IntelGPU::IGCA_CORE, true)), - ""); + IntelGPU::getIGCATargetName(IGCATarget(10, IGCAFeatureSet::Compute)), ""); } TEST(IntelGPUTargetParserTest, EveryIGCASpellingRoundTrips) { @@ -153,16 +188,46 @@ TEST(IntelGPUTargetParserTest, EveryIGCASpellingRoundTrips) { } } -TEST(IntelGPUTargetParserTest, EveryDeviceHasValidIGCATargetLevel) { - // Every device's defined IGCA target should have a corresponding entry in - // INTEL_IGCA_TARGET: -#define INTEL_IGCA_TARGET_CHECK(NAME, IGCA_TARGET) \ - EXPECT_TRUE(IntelGPU::parseIGCATarget("igca_" #IGCA_TARGET).isValid()) \ +// Spell an IGCA target from its fields, independently of the NAME column. +std::string expectedIGCASpelling(uint16_t Target, + IntelGPU::IGCAFeatureSet FeatureSet) { + std::string Name = "igca_" + std::to_string(Target); + switch (FeatureSet) { + case IntelGPU::IGCAFeatureSet::Core: + return Name; + case IntelGPU::IGCAFeatureSet::Compute: + return Name + "c"; + case IntelGPU::IGCAFeatureSet::ComputeExact: + return Name + "ca"; + case IntelGPU::IGCAFeatureSet::Render: + return Name + "r"; + case IntelGPU::IGCAFeatureSet::RenderExact: + return Name + "ra"; + } + return ""; +} + +TEST(IntelGPUTargetParserTest, IGCASpellingsMatchFields) { + // Every INTEL_IGCA_TARGET's NAME must agree with information in the rest of + // its row. +#define INTEL_IGCA_TARGET(NAME, TARGET, FEATURE_SET) \ + EXPECT_EQ(NAME, expectedIGCASpelling( \ + TARGET, IntelGPU::IGCAFeatureSet::FEATURE_SET)); +#include "llvm/TargetParser/IntelGPUTargetParser.def" +} + +TEST(IntelGPUTargetParserTest, EveryDeviceHasValidIGCATarget) { + // Every device's IGCA target and feature sets should have a corresponding + // entry in INTEL_IGCA_TARGET: +#define INTEL_IGCA_TARGET_CHECK(NAME, IGCA_TARGET, IGCA_FEATURE_SETS) \ + EXPECT_NE(IntelGPU::getIGCATargetName(IntelGPU::IGCATarget( \ + IGCA_TARGET, IntelGPU::IGCAFeatureSet::IGCA_FEATURE_SETS)), \ + "") \ << NAME; #define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS) \ - INTEL_IGCA_TARGET_CHECK(NAME, IGCA_TARGET) + INTEL_IGCA_TARGET_CHECK(NAME, IGCA_TARGET, IGCA_FEATURE_SETS) #define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS) \ - INTEL_IGCA_TARGET_CHECK(NAME, IGCA_TARGET) + INTEL_IGCA_TARGET_CHECK(NAME, IGCA_TARGET, IGCA_FEATURE_SETS) #include "llvm/TargetParser/IntelGPUTargetParser.def" #undef INTEL_IGCA_TARGET_CHECK } >From 88050039b3e3cd31c802211b58c8d9ff8f65546b Mon Sep 17 00:00:00 2001 From: "Li, Ian" <[email protected]> Date: Thu, 22 Oct 2026 13:56:30 -0700 Subject: [PATCH 5/7] Reject bad igca targets when fed into --offload-arch, add tests --- clang/lib/Driver/Driver.cpp | 5 +++++ clang/test/Driver/sycl-offload-arch-igca.cpp | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 clang/test/Driver/sycl-offload-arch-igca.cpp diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index a22a81a8ba6e1c..741ddefad97e49 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -3928,6 +3928,11 @@ static StringRef getCanonicalArchString(Compilation &C, return StringRef(); } } + } else if (Triple.isSPIRV() && ArchStr.starts_with("igca_") && + !Arch.isIntelIGCA()) { + C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch) + << "IGCA" << ArchStr; + return StringRef(); } if (Arch.isNVPTX()) diff --git a/clang/test/Driver/sycl-offload-arch-igca.cpp b/clang/test/Driver/sycl-offload-arch-igca.cpp new file mode 100644 index 00000000000000..ecb1705bcc36d7 --- /dev/null +++ b/clang/test/Driver/sycl-offload-arch-igca.cpp @@ -0,0 +1,19 @@ +/// Tests for Intel IGCA targets in --offload-arch. + +// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fsycl \ +// RUN: --offload-targets=spirv64-unknown-unknown --offload-arch=igca_20ca \ +// RUN: -c %s 2>&1 | FileCheck %s +// CHECK: llvm-offload-binary{{.*}} "--image=file={{.*}}.bc,triple=spirv64-unknown-unknown,arch=igca_20ca,kind=sycl" + +/// Reject IGCA targets not listed in IntelGPUTargetParser.def: +// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -fsycl \ +// RUN: --offload-targets=spirv64-unknown-unknown --offload-arch=igca_42a \ +// RUN: -c %s 2>&1 | FileCheck -check-prefix=CHK-INVALID %s +// CHK-INVALID: error: unsupported IGCA gpu architecture: igca_42a +// CHK-INVALID-NOT: llvm-offload-binary + +/// TODO: Enable this when OffloadArchToTriple gives IGCA targets a triple. +// TODO-RUN %clang -### --target=x86_64-unknown-linux-gnu -fsycl \ +// TODO-RUN --offload-arch=igca_20ca -c %s 2>&1 | FileCheck -check-prefix=CHK-INFER %s +// CHK-INFER-NOT: error: +// CHK-INFER: llvm-offload-binary{{.*}} "--image=file={{.*}}.bc,triple=spirv64{{[^,]*}},arch=igca_20ca,kind=sycl" >From 51e0883c2d86f6b71fd1e210a8c3ac8c908dd276 Mon Sep 17 00:00:00 2001 From: "Li, Ian" <[email protected]> Date: Thu, 22 Oct 2026 15:10:39 -0700 Subject: [PATCH 6/7] Address TODOs --- clang/include/clang/Basic/OffloadArch.h | 7 +--- .../clang-sycl-linker/ClangSYCLLinker.cpp | 4 ++ clang/unittests/Basic/OffloadArchTest.cpp | 41 +++++++++++++++++-- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Basic/OffloadArch.h b/clang/include/clang/Basic/OffloadArch.h index bdbbcc2c8b13f2..cc3de55ca4bf92 100644 --- a/clang/include/clang/Basic/OffloadArch.h +++ b/clang/include/clang/Basic/OffloadArch.h @@ -90,10 +90,7 @@ class OffloadArch { bool isAMDGCNSPIRV() const { return V == TargetArch::AMDGCNSPIRV; } bool isIntelCPU() const { return V == TargetArch::IntelCPU; } bool isIntelGPU() const { return V == TargetArch::IntelGPU; } - // TODO: Decide whether IntelIGCA counts as Intel here. clang-sycl-linker - // uses isIntel() to decide on AOT compilation, and runAOTCompile would then - // need an IntelIGCA dispatch. - bool isIntel() const { return isIntelCPU() || isIntelGPU(); } + bool isIntel() const { return isIntelCPU() || isIntelGPU() || isIntelIGCA(); } bool isGeneric() const { return V == TargetArch::Generic; } bool isUnused() const { return V == TargetArch::Unused; } bool isUnknown() const { return V == TargetArch::Unknown; } @@ -116,8 +113,6 @@ class OffloadArch { bool operator<(const OffloadArch &Other) const { return std::tie(V, Kind) < std::tie(Other.V, Other.Kind); } - // TODO test cases for whether or not IntelIGCA functions properly for these - // comparisons }; const char *OffloadArchToString(OffloadArch A); diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index 53ef6375039ac3..ea5ce961677d19 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -748,6 +748,10 @@ static Error runAOTCompile(StringRef InputFile, StringRef OutputFile, return runAOTCompileIntelGPU(InputFile, OutputFile, Args); if (OA.isIntelCPU()) return runAOTCompileIntelCPU(InputFile, OutputFile, Args); + // FIXME(IGCA): Implement IGCA behavior. + if (OA.isIntelIGCA()) + return createStringError("AOT compilation for IGCA target '" + Arch + + "' is not yet implemented"); llvm_unreachable("runAOTCompile dispatched on unsupported arch"); } diff --git a/clang/unittests/Basic/OffloadArchTest.cpp b/clang/unittests/Basic/OffloadArchTest.cpp index f46ec566c7f037..319d510ec88562 100644 --- a/clang/unittests/Basic/OffloadArchTest.cpp +++ b/clang/unittests/Basic/OffloadArchTest.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "clang/Basic/OffloadArch.h" +#include "llvm/TargetParser/IntelGPUTargetParser.h" #include "llvm/TargetParser/Triple.h" #include "gtest/gtest.h" @@ -41,6 +42,15 @@ TEST(OffloadArchTest, TargetArchClassification) { EXPECT_FALSE(IntelGPU.isIntelCPU()); EXPECT_TRUE(IntelGPU.isIntelGPU()); + OffloadArch IGCA = parse("igca_20ca"); + EXPECT_TRUE(IGCA.isIntelIGCA()); + EXPECT_FALSE(IGCA.isIntelGPU()); + EXPECT_FALSE(IGCA.isIntelCPU()); + EXPECT_TRUE(IGCA.isIntel()); + EXPECT_FALSE(IGCA.isNVPTX()); + EXPECT_FALSE(IGCA.isAMDGPU()); + EXPECT_FALSE(IGCA.isAMDGCNSPIRV()); + OffloadArch Generic = parse("generic"); EXPECT_FALSE(Generic.isNVPTX()); EXPECT_FALSE(Generic.isAMDGPU()); @@ -54,12 +64,11 @@ TEST(OffloadArchTest, Unknown) { } // Names must round-trip through parse -> string. -// TODO: Add IGCA targets (e.g. "igca_20ca"), plus IntelIGCA classification and -// ==/< ordering tests. TEST(OffloadArchTest, RoundTrip) { for (const char *Name : {"sm_52", "sm_90a", "gfx906", "gfx1201", "gfx12-generic", "amdgcnspirv", - "graniterapids", "bmg_g21", "generic"}) { + "graniterapids", "bmg_g21", "igca_10", "igca_20c", "igca_20ca", + "igca_15r", "igca_15ra", "generic"}) { OffloadArch A = parse(Name); EXPECT_FALSE(A.isUnknown()) << Name; EXPECT_STREQ(OffloadArchToString(A), Name); @@ -94,3 +103,29 @@ TEST(OffloadArchTest, AMDGPUSubArchRoundTrip) { EXPECT_EQ(getOffloadArchSubArch(OffloadArch::getAMDGCNSPIRV()), llvm::Triple::NoSubArch); } + +// Test Intel IGCA target equal and comparsion operators: +TEST(OffloadArchTest, IntelIGCAComparison) { + // Listed in ascending order: by target level, then by feature set. + const char *Ordered[] = {"igca_10", "igca_15r", "igca_20c", "igca_20ca", + "igca_35"}; + for (size_t I = 0; I < std::size(Ordered); ++I) { + OffloadArch A = parse(Ordered[I]); + ASSERT_TRUE(A.isIntelIGCA()) << Ordered[I]; + EXPECT_EQ(A, parse(Ordered[I])) << Ordered[I]; + EXPECT_FALSE(A < A) << Ordered[I]; + if (I + 1 < std::size(Ordered)) { + OffloadArch Next = parse(Ordered[I + 1]); + EXPECT_NE(A, Next) << Ordered[I] << " vs " << Ordered[I + 1]; + EXPECT_TRUE(A < Next) << Ordered[I] << " vs " << Ordered[I + 1]; + EXPECT_FALSE(Next < A) << Ordered[I] << " vs " << Ordered[I + 1]; + } + } + + // The same 'Kind' under another TargetArch is still a different architecture. + OffloadArch IGCA = parse("igca_20ca"); + OffloadArch SameKind = OffloadArch::getIntel( + OffloadArch::TargetArch::IntelGPU, + static_cast<OffloadArch::IntelArch>(IGCA.igcaTarget().pack())); + EXPECT_NE(IGCA, SameKind); +} >From 0811ea6ff830f23c459006d6ab945b019214cd31 Mon Sep 17 00:00:00 2001 From: "Li, Ian" <[email protected]> Date: Thu, 22 Oct 2026 15:13:04 -0700 Subject: [PATCH 7/7] add nice label for which TODOs are related to IGCA --- clang/lib/Basic/OffloadArch.cpp | 2 +- clang/test/Driver/sycl-offload-arch-igca.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Basic/OffloadArch.cpp b/clang/lib/Basic/OffloadArch.cpp index c3004bd3333cb0..00ba58cc8550b0 100644 --- a/clang/lib/Basic/OffloadArch.cpp +++ b/clang/lib/Basic/OffloadArch.cpp @@ -134,7 +134,7 @@ llvm::Triple OffloadArchToTriple(const llvm::Triple &DefaultToolchainTriple, return llvm::Triple(llvm::Triple::amdgpu, llvm::Triple::NoSubArch, llvm::Triple::AMD, llvm::Triple::AMDHSA); - // TODO: Handle IntelIGCA, which currently gets an empty triple, so + // FIXME(IGCA): Handle IntelIGCA, which currently gets an empty triple, so // --offload-arch=igca_* cannot pick an offload toolchain. return {}; } diff --git a/clang/test/Driver/sycl-offload-arch-igca.cpp b/clang/test/Driver/sycl-offload-arch-igca.cpp index ecb1705bcc36d7..5e2c45f180a913 100644 --- a/clang/test/Driver/sycl-offload-arch-igca.cpp +++ b/clang/test/Driver/sycl-offload-arch-igca.cpp @@ -12,7 +12,7 @@ // CHK-INVALID: error: unsupported IGCA gpu architecture: igca_42a // CHK-INVALID-NOT: llvm-offload-binary -/// TODO: Enable this when OffloadArchToTriple gives IGCA targets a triple. +/// FIXME(IGCA): Enable this when OffloadArchToTriple gives IGCA targets a triple. // TODO-RUN %clang -### --target=x86_64-unknown-linux-gnu -fsycl \ // TODO-RUN --offload-arch=igca_20ca -c %s 2>&1 | FileCheck -check-prefix=CHK-INFER %s // CHK-INFER-NOT: error: _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
