https://github.com/srividya-sundaram updated 
https://github.com/llvm/llvm-project/pull/229247

>From 4e07d042e43ba094a8f05c838631d32c24853e84 Mon Sep 17 00:00:00 2001
From: srividya sundaram <[email protected]>
Date: Sat, 12 Sep 2026 19:09:30 -0700
Subject: [PATCH] [TargetParser][Clang] Parse Intel GPU architecture names

Add support for Intel GPU names in --offload-arch.

#222072 added the mapping from a detected GPU version to its name. This PR adds 
the reverse mapping needed when a user specifies a name through --offload-arch.
Previously, clang recognized only the hardcoded name bmg_g21.

The shared Intel GPU list now recognizes names such as xe-pvc, xe-dg2, bmg_g21, 
and xe_12.60.7. Names for the same GPU IP are treated as aliases, so SYCL 
creates one device job and matches all spellings consistently. 
clang-sycl-linker uses the canonical name for the image and the name expected 
by ocloc.

User-visible changes:
- A device is printed and labelled by its canonical name, e.g. bmg_g21 becomes 
xe-bmg-g21.
- Every listed Intel GPU name is compiled ahead of time with ocloc, as bmg_g21 
already was.
- Products on the same GPU IP become aliases, so 13 GPUKind enumerators are 
removed, e.g. GK_XE_ARL_U is now GK_XE_MTL_U.
- An Intel name is no longer accepted as an NVPTX CPU; -target-cpu bmg_g21 was 
accepted before.
- An Intel name with a target-ID suffix, e.g. xe-pvc:foo, is not turned into a 
device.

Canonicalization is limited to SYCL, the only offload kind that compiles for 
Intel GPUs today.

Selecting the SPIR-V target from an Intel GPU name and diagnosing unknown names 
are handled separately in follow-up PRs.

Assisted-by: Claude Opus 5.5
---
 clang/include/clang/Basic/OffloadArch.h       |  26 ++--
 clang/lib/Basic/OffloadArch.cpp               |  12 +-
 clang/lib/Basic/Targets/NVPTX.h               |  11 +-
 clang/lib/Driver/Driver.cpp                   |  22 ++-
 clang/lib/Driver/ToolChain.cpp                |   4 +
 clang/lib/Driver/ToolChains/Clang.cpp         |   8 +-
 clang/test/Driver/sycl-offload-jit.cpp        |  61 ++++++++-
 .../test/Misc/target-invalid-cpu-note/nvptx.c |   4 +
 .../OffloadTools/clang-sycl-linker/basic.ll   |  13 +-
 .../clang-sycl-linker/ClangSYCLLinker.cpp     |  15 ++-
 clang/unittests/Basic/OffloadArchTest.cpp     |  23 +++-
 .../TargetParser/IntelGPUTargetParser.def     |  59 +++++---
 .../llvm/TargetParser/IntelGPUTargetParser.h  |  41 +++++-
 .../lib/TargetParser/IntelGPUTargetParser.cpp | 127 +++++++++++++++++-
 .../TargetParser/IntelGPUTargetParserTest.cpp | 104 ++++++++++++++
 15 files changed, 473 insertions(+), 57 deletions(-)

diff --git a/clang/include/clang/Basic/OffloadArch.h 
b/clang/include/clang/Basic/OffloadArch.h
index a0e5e72db220bb..95a17952d4a2a7 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 {
+enum GPUKind : uint8_t;
+}
 } // namespace llvm
 
 namespace clang {
@@ -37,17 +40,11 @@ class OffloadArch {
     NVPTX,       // Kind is an llvm::NVPTX::GPUKind.
     AMDGPU,      // Kind is an llvm::AMDGPU::GPUKind.
     AMDGCNSPIRV, // The 'amdgcnspirv' pseudo target.
-    IntelCPU,    // Kind is an IntelArch.
-    IntelGPU,    // Kind is an IntelArch.
+    IntelCPU,    // The 'graniterapids' processor; Kind is unused.
+    IntelGPU,    // Kind is an llvm::IntelGPU::GPUKind.
     Generic,     // The 'generic' processor model.
   };
 
-  // Intel architectures, which have no TargetParser list yet.
-  enum class IntelArch : uint32_t {
-    GRANITERAPIDS,
-    BMG_G21,
-  };
-
 private:
   // Interpreted according to V; unused for the tagless TargetArch values.
   uint32_t Kind = 0;
@@ -64,8 +61,13 @@ class OffloadArch {
   static OffloadArch getAMDGPU(llvm::AMDGPU::GPUKind K) {
     return {TargetArch::AMDGPU, static_cast<uint32_t>(K)};
   }
-  static constexpr OffloadArch getIntel(TargetArch V, IntelArch A) {
-    return {V, static_cast<uint32_t>(A)};
+  /// An Intel GPU, named by its entry in the TargetParser device list.
+  static OffloadArch getIntelGPU(llvm::IntelGPU::GPUKind K) {
+    return {TargetArch::IntelGPU, static_cast<uint32_t>(K)};
+  }
+  /// Intel CPU offload, which has a single target, so no kind is needed.
+  static constexpr OffloadArch getIntelCPU() {
+    return {TargetArch::IntelCPU, 0};
   }
   static constexpr OffloadArch getUnused() { return {TargetArch::Unused, 0}; }
   static constexpr OffloadArch getUnknown() { return {TargetArch::Unknown, 0}; 
}
@@ -97,6 +99,10 @@ class OffloadArch {
   llvm::AMDGPU::GPUKind amdgpuKind() const {
     return static_cast<llvm::AMDGPU::GPUKind>(Kind);
   }
+  /// Which Intel GPU this is; only meaningful when isIntelGPU().
+  llvm::IntelGPU::GPUKind intelGPUKind() const {
+    return static_cast<llvm::IntelGPU::GPUKind>(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 f8c811f552f387..adc94b35ce025b 100644
--- a/clang/lib/Basic/OffloadArch.cpp
+++ b/clang/lib/Basic/OffloadArch.cpp
@@ -10,6 +10,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/TargetParser/AMDGPUTargetParser.h"
+#include "llvm/TargetParser/IntelGPUTargetParser.h"
 #include "llvm/TargetParser/NVPTXTargetParser.h"
 #include "llvm/TargetParser/Triple.h"
 
@@ -38,7 +39,7 @@ const char *OffloadArchToString(OffloadArch A) {
   case OffloadArch::TargetArch::IntelCPU:
     return "graniterapids";
   case OffloadArch::TargetArch::IntelGPU:
-    return "bmg_g21";
+    return llvm::IntelGPU::getArchName(A.intelGPUKind()).data();
   case OffloadArch::TargetArch::Generic:
     return "generic";
   }
@@ -73,18 +74,17 @@ OffloadArch StringToOffloadArch(llvm::StringRef S) {
     return OffloadArch::getAMDGCNSPIRV();
   if (S == "generic")
     return OffloadArch::getGeneric();
+  // Intel CPU offload has a single processor and no TargetParser list.
   if (S == "graniterapids")
-    return OffloadArch::getIntel(OffloadArch::TargetArch::IntelCPU,
-                                 OffloadArch::IntelArch::GRANITERAPIDS);
-  if (S == "bmg_g21")
-    return OffloadArch::getIntel(OffloadArch::TargetArch::IntelGPU,
-                                 OffloadArch::IntelArch::BMG_G21);
+    return OffloadArch::getIntelCPU();
 
   // Otherwise defer to the vendor TargetParser GPU lists.
   if (llvm::NVPTX::GPUKind NV = llvm::NVPTX::parseArch(S))
     return OffloadArch::getNVPTX(NV);
   if (llvm::AMDGPU::GPUKind AK = llvm::AMDGPU::parseArchAMDGCN(S))
     return OffloadArch::getAMDGPU(AK);
+  if (llvm::IntelGPU::GPUKind IK = llvm::IntelGPU::parseArch(S))
+    return OffloadArch::getIntelGPU(IK);
   return OffloadArch::getUnknown();
 }
 
diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h
index b6d2bfd3201034..45ba8322cf5fca 100644
--- a/clang/lib/Basic/Targets/NVPTX.h
+++ b/clang/lib/Basic/Targets/NVPTX.h
@@ -135,8 +135,13 @@ class LLVM_LIBRARY_VISIBILITY NVPTXTargetInfo : public 
TargetInfo {
     return TargetInfo::CharPtrBuiltinVaList;
   }
 
+  // StringToOffloadArch recognizes architectures from all vendors. Accept only
+  // the ones NVPTX accepted before, so Sema does not accept an Intel GPU name
+  // as an NVPTX CPU.
   bool isValidCPUName(StringRef Name) const override {
-    return !StringToOffloadArch(Name).isUnknown();
+    OffloadArch Arch = StringToOffloadArch(Name);
+    return Arch.isNVPTX() || Arch.isAMDGPU() || Arch.isAMDGCNSPIRV() ||
+           Arch.isGeneric() || Arch.isUnused();
   }
 
   void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override {
@@ -144,8 +149,10 @@ class LLVM_LIBRARY_VISIBILITY NVPTXTargetInfo : public 
TargetInfo {
   }
 
   bool setCPU(StringRef Name) override {
+    if (!isValidCPUName(Name))
+      return false;
     GPU = StringToOffloadArch(Name);
-    return !GPU.isUnknown();
+    return true;
   }
 
   void setSupportedOpenCLOpts() override {
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 57beab92011a9f..bbcf88d8237762 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3899,15 +3899,20 @@ void Driver::BuildActions(Compilation &C, 
DerivedArgList &Args,
 }
 
 /// Returns the canonical name for the offloading architecture when using a HIP
-/// or CUDA architecture.
+/// or CUDA architecture, or an Intel GPU for SYCL.
 static StringRef getCanonicalArchString(Compilation &C,
                                         const llvm::opt::DerivedArgList &Args,
                                         StringRef ArchStr,
-                                        const llvm::Triple &Triple) {
+                                        const llvm::Triple &Triple,
+                                        Action::OffloadKind Kind) {
   // Lookup the CUDA / HIP architecture string. Only report an error if we were
   // expecting the triple to be only NVPTX / AMDGPU.
   OffloadArch Arch =
       StringToOffloadArch(getProcessorFromTargetID(Triple, ArchStr));
+  // An Intel name takes no target features, so it must parse in full; 
otherwise
+  // "xe-pvc:garbage" would silently become "xe-pvc".
+  if (Arch.isIntel() && ArchStr.contains(':'))
+    Arch = OffloadArch::getUnknown();
   if (Triple.isNVPTX() && (Arch.isUnknown() || !Arch.isNVPTX())) {
     C.getDriver().Diag(clang::diag::err_drv_offload_bad_gpu_arch)
         << "CUDA" << ArchStr;
@@ -3930,7 +3935,10 @@ static StringRef getCanonicalArchString(Compilation &C,
     }
   }
 
-  if (Arch.isNVPTX())
+  // A SYCL Intel GPU has several accepted spellings, e.g. an alias or a 
numeric
+  // name, so canonicalize it for the same reason as an NVPTX one: two 
spellings
+  // of one device must be one architecture.
+  if (Arch.isNVPTX() || (Kind == Action::OFK_SYCL && Arch.isIntelGPU()))
     return Args.MakeArgStringRef(OffloadArchToString(Arch));
 
   if (Arch.isAMDGPU() || Arch.isAMDGCNSPIRV()) {
@@ -3991,7 +3999,7 @@ Driver::getOffloadArchs(Compilation &C, const 
llvm::opt::DerivedArgList &Args,
 
           for (auto ArchStr : *GPUsOrErr) {
             StringRef CanonicalStr = getCanonicalArchString(
-                C, Args, Args.MakeArgString(ArchStr), TC.getTriple());
+                C, Args, Args.MakeArgString(ArchStr), TC.getTriple(), Kind);
             if (!CanonicalStr.empty())
               Archs.insert(CanonicalStr);
             else
@@ -3999,7 +4007,7 @@ Driver::getOffloadArchs(Compilation &C, const 
llvm::opt::DerivedArgList &Args,
           }
         } else {
           StringRef CanonicalStr =
-              getCanonicalArchString(C, Args, Arch, TC.getTriple());
+              getCanonicalArchString(C, Args, Arch, TC.getTriple(), Kind);
           if (!CanonicalStr.empty())
             Archs.insert(CanonicalStr);
           else
@@ -4012,7 +4020,7 @@ Driver::getOffloadArchs(Compilation &C, const 
llvm::opt::DerivedArgList &Args,
           Archs.clear();
         } else {
           StringRef ArchStr =
-              getCanonicalArchString(C, Args, Arch, TC.getTriple());
+              getCanonicalArchString(C, Args, Arch, TC.getTriple(), Kind);
           Archs.erase(ArchStr);
         }
       }
@@ -4068,7 +4076,7 @@ Driver::getOffloadArchs(Compilation &C, const 
llvm::opt::DerivedArgList &Args,
                                   ? ""
                                   : OffloadArchToString(TripleOffloadArch);
     StringRef CanonicalStr =
-        getCanonicalArchString(C, Args, ArchStr, TC.getTriple());
+        getCanonicalArchString(C, Args, ArchStr, TC.getTriple(), Kind);
     if (!CanonicalStr.empty())
       Archs.insert(CanonicalStr);
   }
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 255a9c9602b540..d7928cd10ea242 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -2189,7 +2189,11 @@ llvm::opt::DerivedArgList *ToolChain::TranslateXarchArgs(
       Skip = IsDevice;
     } else if (A->getOption().matches(options::OPT_Xarch__)) {
       StringRef Val = A->getValue();
+      // A SYCL Intel GPU has several spellings, so -Xarch_ may name it by
+      // another.
       NeedTrans = Val == getArchName() || (BA && Val == BA.ArchName) ||
+                  (OFK == Action::OFK_SYCL && BA && BA.Arch.isIntelGPU() &&
+                   StringToOffloadArch(Val) == BA.Arch) ||
                   isXArchCompatibleTripleArch(Triple, Val);
       Skip = !NeedTrans;
     }
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 2325e7cc552ef9..7071ee6b3bebe7 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -9864,8 +9864,14 @@ void LinkerWrapper::ConstructJob(Compilation &C, const 
JobAction &JA,
 
       // If the user explicitly requested it via `--offload-arch` we should
       // extract it from any static libraries if present.
-      for (StringRef Arg : ToolChainArgs.getAllArgValues(OPT_offload_arch_EQ))
+      // A SYCL Intel GPU is requested by its canonical name, the one its 
device
+      // images are labeled with, whichever spelling the user wrote.
+      for (StringRef Arg : ToolChainArgs.getAllArgValues(OPT_offload_arch_EQ)) 
{
+        OffloadArch ID = StringToOffloadArch(Arg);
+        if (Kind == Action::OFK_SYCL && ID.isIntelGPU())
+          Arg = OffloadArchToString(ID);
         CmdArgs.emplace_back(Args.MakeArgString("--should-extract=" + Arg));
+      }
 
       // If this is OpenMP the device linker will need `-lompdevice`.
       if (Kind == Action::OFK_OpenMP && !Args.hasArg(OPT_no_offloadlib) &&
diff --git a/clang/test/Driver/sycl-offload-jit.cpp 
b/clang/test/Driver/sycl-offload-jit.cpp
index 82733ccd2bc6f2..cb4283db51d25b 100644
--- a/clang/test/Driver/sycl-offload-jit.cpp
+++ b/clang/test/Driver/sycl-offload-jit.cpp
@@ -90,11 +90,68 @@
 // RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl 
-fno-gpu-rdc \
 // RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=generic 
--offload-arch=bmg_g21 \
 // RUN:   -c %s 2>&1 | FileCheck -check-prefixes=CHK-PHASES-NORDC-ARCHS %s
-// CHK-PHASES-NORDC-ARCHS: 7: offload, "device-sycl 
(spirv64-unknown-unknown:bmg_g21)" {6}, ir
-// CHK-PHASES-NORDC-ARCHS: 12: offload, "device-sycl 
(spirv64-unknown-unknown:generic)" {11}, ir
+// CHK-PHASES-NORDC-ARCHS: 7: offload, "device-sycl 
(spirv64-unknown-unknown:generic)" {6}, ir
+// CHK-PHASES-NORDC-ARCHS: 12: offload, "device-sycl 
(spirv64-unknown-unknown:xe-bmg-g21)" {11}, ir
 // CHK-PHASES-NORDC-ARCHS-NEXT: 13: llvm-offload-binary, {7, 12}, image, 
(device-sycl)
 // CHK-PHASES-NORDC-ARCHS-NEXT: 14: clang-linker-wrapper, {13}, sycl-fatbin, 
(device-sycl)
 
+/// Every spelling of an Intel GPU names one architecture, spelled canonically,
+/// so several spellings of one device build it once.
+// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=bmg_g21 \
+// RUN:   --offload-arch=xe-bmg-g21 --offload-arch=xe_20.1.0 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-INTEL-SPELLINGS %s
+// CHK-INTEL-SPELLINGS-NOT: offload, "device-sycl
+// CHK-INTEL-SPELLINGS: offload, "device-sycl 
(spirv64-unknown-unknown:xe-bmg-g21)"
+// CHK-INTEL-SPELLINGS-NOT: offload, "device-sycl
+
+/// Products built on one GPU IP version are one architecture too.
+// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe-arl-u \
+// RUN:   --offload-arch=xe-mtl-u --offload-arch=xe_12.70 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-INTEL-SAME-IP %s
+// CHK-INTEL-SAME-IP-NOT: offload, "device-sycl
+// CHK-INTEL-SAME-IP: offload, "device-sycl (spirv64-unknown-unknown:xe-mtl-u)"
+// CHK-INTEL-SAME-IP-NOT: offload, "device-sycl
+
+/// Removing any spelling of a device removes the device.
+// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe-bmg-g21 \
+// RUN:   --offload-arch=generic --no-offload-arch=bmg_g21 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-INTEL-NO-ARCH %s
+// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=bmg_g21 \
+// RUN:   --offload-arch=generic --no-offload-arch=xe_20.1.0 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-INTEL-NO-ARCH %s
+// CHK-INTEL-NO-ARCH-NOT: bmg
+// CHK-INTEL-NO-ARCH: offload, "device-sycl (spirv64-unknown-unknown:generic)"
+// CHK-INTEL-NO-ARCH-NOT: bmg
+
+/// An Intel GPU name takes no target-ID features, so a suffix is not dropped 
to
+/// turn the name into a device.
+// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe-pvc:foo 
-c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-INTEL-SUFFIX %s
+// CHK-INTEL-SUFFIX: offload, "device-sycl 
(spirv64-unknown-unknown:xe-pvc:foo)"
+
+/// -Xarch_ may name an Intel GPU by any of its spellings.
+// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=bmg_g21 \
+// RUN:   -Xarch_bmg_g21 -DBMG -Xarch_xe-pvc -DPVC -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-INTEL-XARCH %s
+// CHK-INTEL-XARCH: "-fsycl-is-device"
+// CHK-INTEL-XARCH-SAME: "-D" "BMG"
+// CHK-INTEL-XARCH-NOT: "PVC"
+
+/// A static library's device code is requested by the canonical name.
+// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=bmg_g21 %s 
2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-INTEL-EXTRACT %s
+// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe_20.1.0 
%s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-INTEL-EXTRACT %s
+// CHK-INTEL-EXTRACT: clang-linker-wrapper{{.*}}"--should-extract=xe-bmg-g21"
+
 /// Multiple device triples are not supported today in non-RDC mode.
 // RUN: not %clang -### --target=x86_64-unknown-linux-gnu -fsycl -fno-gpu-rdc \
 // RUN:   --offload-targets=spirv64-unknown-unknown,spirv32-unknown-unknown -c 
%s 2>&1 \
diff --git a/clang/test/Misc/target-invalid-cpu-note/nvptx.c 
b/clang/test/Misc/target-invalid-cpu-note/nvptx.c
index 20dbd8dc6aa3b6..b2a38b6b268253 100644
--- a/clang/test/Misc/target-invalid-cpu-note/nvptx.c
+++ b/clang/test/Misc/target-invalid-cpu-note/nvptx.c
@@ -2,6 +2,10 @@
 // previous check finished matching (specifically, caret is not treated as
 // matching a start of line when used like this in FileCheck).
 
+// An Intel GPU name is an offload architecture but never an NVPTX CPU.
+// RUN: not %clang_cc1 -triple nvptx64-nvidia-cuda -target-cpu xe-pvc 
-fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=INTEL
+// INTEL: error: unknown target CPU 'xe-pvc'
+
 // RUN: not %clang_cc1 -triple nvptx--- -target-cpu not-a-cpu -fsyntax-only %s 
2>&1 | FileCheck %s --implicit-check-not={{[a-zA-Z0-9]}}
 // CHECK: error: unknown target CPU 'not-a-cpu'
 // CHECK-NEXT: note: valid target CPU values are:
diff --git a/clang/test/OffloadTools/clang-sycl-linker/basic.ll 
b/clang/test/OffloadTools/clang-sycl-linker/basic.ll
index 0ba81f61005a43..0376e7bf883388 100644
--- a/clang/test/OffloadTools/clang-sycl-linker/basic.ll
+++ b/clang/test/OffloadTools/clang-sycl-linker/basic.ll
@@ -115,9 +115,20 @@
 ; AOT-INTEL-GPU:      link: inputs: {{.*}}.bc, {{.*}}.bc output: 
[[LLVMLINKOUT:.*]].bc
 ; AOT-INTEL-GPU-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: 
[[SPVSTEM:.*]].spv
 ; AOT-INTEL-GPU-NEXT: "{{.*}}ocloc{{.*}}" {{.*}}-device bmg_g21 -a -b 
{{.*}}-output [[SPVSTEM]].out -file [[SPVSTEM]].spv
-; AOT-INTEL-GPU-NEXT: sycl-bundle: image kind: o, triple: spirv64, arch: 
bmg_g21
+; AOT-INTEL-GPU-NEXT: sycl-bundle: image kind: o, triple: spirv64, arch: 
xe-bmg-g21
 ; AOT-INTEL-GPU-NOT:  {{.+}}
 ;
+; Test that every accepted spelling of an Intel GPU reaches ocloc as ocloc's 
own.
+; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit 
-arch=xe-bmg-g21 %t/input1.bc -o %t/aot-gpu.out 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=AOT-INTEL-NAME
+; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit 
-arch=xe_20.1.0 %t/input1.bc -o %t/aot-gpu.out 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=AOT-INTEL-NAME
+; AOT-INTEL-NAME: "{{.*}}ocloc{{.*}}" {{.*}}-device bmg_g21 {{.*}}-output
+; AOT-INTEL-NAME: sycl-bundle: image kind: o, triple: spirv64, arch: xe-bmg-g21
+; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit 
-arch=xe-pvc %t/input1.bc -o %t/aot-gpu.out 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=AOT-INTEL-PVC
+; AOT-INTEL-PVC: "{{.*}}ocloc{{.*}}" {{.*}}-device pvc {{.*}}-output
+
 ; Test that all --ocloc-options are passed to ocloc, even if they contain 
spaces or quotes.
 ; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit 
-arch=bmg_g21 %t/input1.bc -o %t/aot-gpu.out 2>&1 \
 ; RUN:     --ocloc-options="-a -b" --ocloc-options=-c --ocloc-options="d" 
--ocloc-options='"-e -f"' \
diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp 
b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
index 60ab7dc4e2a7d1..01463c15aa0dbf 100644
--- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
+++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
@@ -716,8 +716,15 @@ static Error runAOTCompileIntelGPU(StringRef InputFile, 
StringRef OutputFile,
 
   StringRef Arch(Args.getLastArgValue(OPT_arch_EQ));
   assert(!Arch.empty() && "Arch must be specified for AOT compilation");
+  // ocloc spells a device without the "xe-" prefix and with "_" between words,
+  // e.g. "bmg_g21" for "xe-bmg-g21", so translate any accepted spelling.
+  StringRef Canonical = OffloadArchToString(StringToOffloadArch(Arch));
+  [[maybe_unused]] bool HasPrefix = Canonical.consume_front("xe-");
+  assert(HasPrefix && "Intel GPU name without the xe- prefix");
+  std::string Device = Canonical.str();
+  llvm::replace(Device, '-', '_');
   CmdArgs.push_back("-device");
-  CmdArgs.push_back(Arch);
+  CmdArgs.push_back(Device);
 
   // getAllArgValues returns a temporary vector; retain it so the StringRefs
   // remain valid through the executeCommands call below.
@@ -1045,8 +1052,12 @@ static Error 
runSYCLLink(ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs,
     TheImage.TheOffloadKind = OFK_SYCL;
     TheImage.StringData["triple"] =
         Args.MakeArgString(Result.TargetTriple.str());
+    // Label an Intel GPU image with its canonical name, whichever spelling was
+    // given, so that one device's images always carry one arch.
+    StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
+    OffloadArch OA = StringToOffloadArch(Arch);
     TheImage.StringData["arch"] =
-        Args.MakeArgString(Args.getLastArgValue(OPT_arch_EQ));
+        OA.isIntelGPU() ? OffloadArchToString(OA) : Args.MakeArgString(Arch);
     TheImage.StringData["symbols"] = SI.Symbols;
     TheImage.Image = std::move(*FileOrErr);
     Images.emplace_back(std::move(TheImage));
diff --git a/clang/unittests/Basic/OffloadArchTest.cpp 
b/clang/unittests/Basic/OffloadArchTest.cpp
index 013ed75fbd57eb..980a88aba6114b 100644
--- a/clang/unittests/Basic/OffloadArchTest.cpp
+++ b/clang/unittests/Basic/OffloadArchTest.cpp
@@ -36,10 +36,12 @@ TEST(OffloadArchTest, TargetArchClassification) {
   EXPECT_TRUE(IntelCPU.isIntelCPU());
   EXPECT_FALSE(IntelCPU.isIntelGPU());
 
-  OffloadArch IntelGPU = parse("bmg_g21");
+  OffloadArch IntelGPU = parse("xe-bmg-g21");
   EXPECT_TRUE(IntelGPU.isIntel());
   EXPECT_FALSE(IntelGPU.isIntelCPU());
   EXPECT_TRUE(IntelGPU.isIntelGPU());
+  EXPECT_TRUE(parse("xe-dg2").isIntelGPU());
+  EXPECT_TRUE(parse("xe_12.60.7").isIntelGPU());
 
   OffloadArch Generic = parse("generic");
   EXPECT_FALSE(Generic.isNVPTX());
@@ -57,13 +59,30 @@ TEST(OffloadArchTest, Unknown) {
 TEST(OffloadArchTest, RoundTrip) {
   for (const char *Name :
        {"sm_52", "sm_90a", "gfx906", "gfx1201", "gfx12-generic", "amdgcnspirv",
-        "graniterapids", "bmg_g21", "generic"}) {
+        "graniterapids", "xe-bmg-g21", "xe-pvc", "xe-dg2", "generic"}) {
     OffloadArch A = parse(Name);
     EXPECT_FALSE(A.isUnknown()) << Name;
     EXPECT_STREQ(OffloadArchToString(A), Name);
   }
 }
 
+// A spelling that is not the architecture name still denotes the device, and
+// canonicalizes to that name rather than round-tripping to itself.
+TEST(OffloadArchTest, IntelGPUAliases) {
+  // An alias, here the spelling accepted before the Intel GPU list existed.
+  EXPECT_EQ(parse("bmg_g21"), parse("xe-bmg-g21"));
+  // A numeric name, whose revision takes no part in the lookup.
+  EXPECT_EQ(parse("xe_12.60.0"), parse("xe-pvc"));
+  EXPECT_EQ(parse("xe_12.60.7"), parse("xe-pvc"));
+  EXPECT_EQ(parse("xe_12.60"), parse("xe-pvc"));
+  EXPECT_STREQ(OffloadArchToString(parse("bmg_g21")), "xe-bmg-g21");
+  EXPECT_STREQ(OffloadArchToString(parse("xe_12.60.7")), "xe-pvc");
+
+  // A well-formed numeric name for a device this build does not know is as
+  // unknown as any other unknown name.
+  EXPECT_TRUE(parse("xe_99.99.0").isUnknown());
+}
+
 TEST(OffloadArchTest, Defaults) {
   EXPECT_STREQ(OffloadArchToString(OffloadArch::CudaDefault()), "sm_52");
   EXPECT_STREQ(OffloadArchToString(OffloadArch::HIPDefault()), "gfx906");
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def 
b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
index bd3b0a4634e066..c8faba2a11e1b8 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
@@ -21,7 +21,9 @@
 //     IGCA_FEATURE_SETS - Token naming the IGCA feature sets the device
 //                         implements: Core (none), Compute ("c"),
 //                         Render ("r"), ComputeExact ("ca") or
-//                         RenderExact ("ra").
+//                         RenderExact ("ra"). It is a token rather than the
+//                         suffix itself so that a typo is a build error;
+//                         getIGCAName maps it onto the spelling.
 //
 //   INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS)
 //     A compatibility name that covers several minor versions, e.g. "xe-dg2",
@@ -30,12 +32,28 @@
 //     offload-arch utility never prints one, but they are legal --offload-arch
 //     values. The columns mean the same as above.
 //
+//   INTEL_GPU_ALIAS(NAME, KIND)
+//     A further name for a device that already has a row: "bmg_g21", which
+//     --offload-arch accepted before the list existed, or another product
+//     built on the same GPU IP version, e.g. "xe-arl-u" for "xe-mtl-u". Code
+//     for one GPU IP version is the same whichever product runs it, so these
+//     are one architecture. An alias declares no kind of its own and is never
+//     printed, so a device keeps one canonical name; it only widens what
+//     --offload-arch accepts.
+//
 // A device is keyed by its major and minor version alone. The revision
 // component of the GPU IP version is deliberately left out of the key, so that
 // every revision of a device shares one name and one IGCA target.
 //
-// Several devices can share a major and a minor version. The rows are ordered
-// so that the name to print for such a group comes first.
+// Products that share a major and a minor version form one INTEL_GPU row, 
named
+// after the product the offload-arch utility prints; the other products are
+// aliases. The exception is "xe-pvc-sdv": it has the same major and minor
+// version as "xe-pvc", but the AOT compiler generates different code for it, 
so
+// it gets its own row.
+//
+// Devices without a published name are left out on purpose; offload-arch names
+// them by GPU IP version, as "xe_*.*.*". A device gets a row only once it has
+// an official name.
 //
 
//===----------------------------------------------------------------------===//
 
@@ -46,10 +64,7 @@
 INTEL_GPU("xe-cri",      XE_CRI,      35, 11,  60, Compute)
 INTEL_GPU("xe-nvl-p",    XE_NVL_P,    35, 10,  60, Render)
 INTEL_GPU("xe-nvl-u",    XE_NVL_U,    30, 5,   60, Render)
-INTEL_GPU("xe-nvl-h",    XE_NVL_H,    30, 5,   60, Render)
 INTEL_GPU("xe-nvl-s",    XE_NVL_S,    30, 4,   60, Render)
-INTEL_GPU("xe-nvl-hx",   XE_NVL_HX,   30, 4,   60, Render)
-INTEL_GPU("xe-nvl-ul",   XE_NVL_UL,   30, 4,   60, Render)
 INTEL_GPU("xe-wcl",      XE_WCL,      30, 3,   50, Render)
 INTEL_GPU("xe-ptl-u",    XE_PTL_U,    30, 1,   50, Render)
 INTEL_GPU("xe-ptl-h",    XE_PTL_H,    30, 0,   50, Render)
@@ -59,28 +74,18 @@ INTEL_GPU("xe-bmg-g21",  XE_BMG_G21,  20, 1,   40, Render)
 INTEL_GPU("xe-arl-h",    XE_ARL_H,    12, 74,  35, Render)
 INTEL_GPU("xe-mtl-h",    XE_MTL_H,    12, 71,  30, Render)
 INTEL_GPU("xe-mtl-u",    XE_MTL_U,    12, 70,  30, Render)
-INTEL_GPU("xe-arl-u",    XE_ARL_U,    12, 70,  30, Render)
-INTEL_GPU("xe-arl-s",    XE_ARL_S,    12, 70,  30, Render)
 INTEL_GPU("xe-pvc-vg",   XE_PVC_VG,   12, 61,  20, ComputeExact)
 INTEL_GPU("xe-pvc",      XE_PVC,      12, 60,  20, ComputeExact)
 INTEL_GPU("xe-pvc-sdv",  XE_PVC_SDV,  12, 60,  20, ComputeExact)
 INTEL_GPU("xe-acm-g12",  XE_ACM_G12,  12, 57,  15, RenderExact)
-INTEL_GPU("xe-dg2-g12",  XE_DG2_G12,  12, 57,  15, RenderExact)
 INTEL_GPU("xe-acm-g11",  XE_ACM_G11,  12, 56,  15, RenderExact)
-INTEL_GPU("xe-dg2-g11",  XE_DG2_G11,  12, 56,  15, RenderExact)
-INTEL_GPU("xe-ats-m75",  XE_ATS_M75,  12, 56,  15, RenderExact)
 INTEL_GPU("xe-acm-g10",  XE_ACM_G10,  12, 55,  15, RenderExact)
-INTEL_GPU("xe-dg2-g10",  XE_DG2_G10,  12, 55,  15, RenderExact)
-INTEL_GPU("xe-ats-m150", XE_ATS_M150, 12, 55,  15, RenderExact)
 INTEL_GPU("xe-dg1",      XE_DG1,      12, 10,  10, Render)
 INTEL_GPU("xe-adl-n",    XE_ADL_N,    12, 4,   10, Render)
 INTEL_GPU("xe-adl-p",    XE_ADL_P,    12, 3,   10, Render)
-INTEL_GPU("xe-rpl-p",    XE_RPL_P,    12, 3,   10, Render)
 INTEL_GPU("xe-adl-s",    XE_ADL_S,    12, 2,   10, Render)
-INTEL_GPU("xe-rpl-s",    XE_RPL_S,    12, 2,   10, Render)
 INTEL_GPU("xe-rkl",      XE_RKL,      12, 1,   10, Render)
 INTEL_GPU("xe-tgllp",    XE_TGLLP,    12, 0,   10, Render)
-INTEL_GPU("xe-tgl",      XE_TGL,      12, 0,   10, Render)
 
 // Compatibility names, which cover a group of platforms
 #ifndef INTEL_GPU_COMPAT
@@ -92,5 +97,27 @@ 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)
 
+// Aliases, which are further names of a device named above
+#ifndef INTEL_GPU_ALIAS
+#define INTEL_GPU_ALIAS(NAME, KIND)
+#endif
+
+// Accepted before this list existed; to be removed in favor of "xe-bmg-g21".
+INTEL_GPU_ALIAS("bmg_g21",     XE_BMG_G21)
+INTEL_GPU_ALIAS("xe-nvl-h",    XE_NVL_U)
+INTEL_GPU_ALIAS("xe-nvl-hx",   XE_NVL_S)
+INTEL_GPU_ALIAS("xe-nvl-ul",   XE_NVL_S)
+INTEL_GPU_ALIAS("xe-arl-u",    XE_MTL_U)
+INTEL_GPU_ALIAS("xe-arl-s",    XE_MTL_U)
+INTEL_GPU_ALIAS("xe-dg2-g12",  XE_ACM_G12)
+INTEL_GPU_ALIAS("xe-dg2-g11",  XE_ACM_G11)
+INTEL_GPU_ALIAS("xe-ats-m75",  XE_ACM_G11)
+INTEL_GPU_ALIAS("xe-dg2-g10",  XE_ACM_G10)
+INTEL_GPU_ALIAS("xe-ats-m150", XE_ACM_G10)
+INTEL_GPU_ALIAS("xe-rpl-p",    XE_ADL_P)
+INTEL_GPU_ALIAS("xe-rpl-s",    XE_ADL_S)
+INTEL_GPU_ALIAS("xe-tgl",      XE_TGLLP)
+
 #undef INTEL_GPU
 #undef INTEL_GPU_COMPAT
+#undef INTEL_GPU_ALIAS
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h 
b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index 4ae635e97167ba..8713de9579b48f 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -6,10 +6,8 @@
 //
 
//===----------------------------------------------------------------------===//
 //
-// This file provides access to the Intel GPU list in IntelGPUTargetParser.def.
-// Only what is needed to name the device a driver reports is declared here; 
the
-// table itself carries more, and a consumer that needs the rest either 
declares
-// it here as well or expands the table directly.
+// This file names the Intel GPU a driver reports, and finds the Intel GPU a
+// user names.
 //
 
//===----------------------------------------------------------------------===//
 
@@ -22,10 +20,15 @@
 #include <string>
 
 namespace llvm {
+template <typename T> class SmallVectorImpl;
+
 namespace IntelGPU {
 
 /// Intel GPU architecture names, covering both physical devices and the
 /// compatibility names that stand for a whole product line.
+///
+/// The underlying type is fixed because clang/Basic/OffloadArch.h forward
+/// declares this enumeration; the two declarations have to agree.
 enum GPUKind : uint8_t {
   GK_NONE = 0,
 #define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS)    
\
@@ -44,6 +47,36 @@ 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);
 
+/// \return the human-friendly name of \p Kind, e.g. "xe-pvc", or an empty
+/// string for GK_NONE.
+LLVM_ABI StringRef getArchName(GPUKind Kind);
+
+/// \return the device \p Name denotes, or GK_NONE for a name this build does
+/// not know.
+///
+/// Every spelling the user may write is accepted: a human-friendly name such 
as
+/// "xe-pvc", a compatibility name such as "xe-dg2", an alias such as 
"bmg_g21",
+/// and a numeric name such as "xe_12.60.7". Only the human-friendly name is
+/// reported back for a device, so a spelling that is not one canonicalizes to
+/// the one that is. The revision of a numeric name takes no part in the 
lookup,
+/// just as it takes none in getArchName, so "xe_12.60.0" and "xe_12.60.7" name
+/// the same device; the revision may also be omitted. A numeric name for a
+/// device that is not in the table yields GK_NONE like any other unknown name.
+LLVM_ABI GPUKind parseArch(StringRef Name);
+
+/// \return the IGCA target name to compile \p Kind for, e.g. "xe-pvc" ->
+/// "igca_20ca", or an empty string for GK_NONE. This is the spelling
+/// -target-cpu is invoked with.
+///
+/// The name is the numeric IGCA target followed by the suffix of the feature
+/// sets: igca_60 is the core features, igca_60c adds the compute features, and
+/// igca_60ca is exact, meaning that only a device at that target will do.
+LLVM_ABI StringRef getIGCAName(GPUKind Kind);
+
+/// Append every architecture name this build accepts, for diagnostics that
+/// offer the user an alternative to a name that did not parse.
+LLVM_ABI void fillValidArchList(SmallVectorImpl<StringRef> &Values);
+
 } // namespace IntelGPU
 } // namespace llvm
 
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp 
b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index 4b4d162ffbfff7..044daddc868f1e 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -11,8 +11,12 @@
 
//===----------------------------------------------------------------------===//
 
 #include "llvm/TargetParser/IntelGPUTargetParser.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/ADT/Twine.h"
+#include "llvm/Support/ErrorHandling.h"
 #include <cassert>
+#include <tuple>
 
 using namespace llvm;
 using namespace IntelGPU;
@@ -28,7 +32,7 @@ using namespace IntelGPU;
 // The reserved bits carry no information.
 static constexpr uint32_t GPUIPMajorShift = 22;
 static constexpr uint32_t GPUIPMinorShift = 14;
-[[maybe_unused]] static constexpr uint32_t GPUIPMajorMask = 0x3ff;
+static constexpr uint32_t GPUIPMajorMask = 0x3ff;
 static constexpr uint32_t GPUIPMinorMask = 0xff;
 static constexpr uint32_t GPUIPRevisionMask = 0x3f;
 
@@ -47,13 +51,20 @@ static constexpr uint32_t packDevice(uint32_t Major, 
uint32_t Minor) {
   return (Major << GPUIPMajorShift) | (Minor << GPUIPMinorShift);
 }
 
-StringRef llvm::IntelGPU::getArchName(uint32_t GPUIPVersion) {
+// The device that \p GPUIPVersion identifies, or GK_NONE if the table lists
+// none. Only INTEL_GPU rows are expanded, so a compatibility name can never
+// match.
+static GPUKind getKindForVersion(uint32_t GPUIPVersion) {
   const uint32_t Device = GPUIPVersion & GPUIPDeviceMask;
 #define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS)    
\
   if (Device == packDevice(MAJOR, MINOR))                                      
\
-    return NAME;
+    return GK_##KIND;
 #include "llvm/TargetParser/IntelGPUTargetParser.def"
-  return "";
+  return GK_NONE;
+}
+
+StringRef llvm::IntelGPU::getArchName(uint32_t GPUIPVersion) {
+  return getArchName(getKindForVersion(GPUIPVersion));
 }
 
 std::string llvm::IntelGPU::getNumericArchName(uint32_t GPUIPVersion) {
@@ -63,3 +74,111 @@ std::string llvm::IntelGPU::getNumericArchName(uint32_t 
GPUIPVersion) {
   return ("xe_" + Twine(Major) + "." + Twine(Minor) + "." + Twine(Revision))
       .str();
 }
+
+StringRef llvm::IntelGPU::getArchName(GPUKind Kind) {
+  switch (Kind) {
+  case GK_NONE:
+    return "";
+#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS)    
\
+  case GK_##KIND:                                                              
\
+    return NAME;
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS)           
\
+  case GK_##KIND:                                                              
\
+    return NAME;
+#include "llvm/TargetParser/IntelGPUTargetParser.def"
+  }
+  llvm_unreachable("invalid Intel GPU GPUKind");
+}
+
+// Read a numeric architecture name, e.g. "xe_12.60.7", into the GPU IP version
+// it spells. The revision may be omitted, since it takes no part in a lookup
+// either way. Anything else is not a numeric name, which is not the same as
+// naming no device: a caller distinguishes the two by whether this succeeds.
+static bool parseNumericArchName(StringRef Name, uint32_t &GPUIPVersion) {
+  if (!Name.consume_front("xe_"))
+    return false;
+
+  StringRef MajorStr, MinorStr, RevisionStr;
+  std::tie(MajorStr, Name) = Name.split('.');
+  // The revision may be omitted, but a separator with no revision after it is
+  // malformed rather than an omitted revision.
+  const bool HasRevision = Name.contains('.');
+  std::tie(MinorStr, RevisionStr) = Name.split('.');
+  uint32_t Major, Minor, Revision = 0;
+  if (MajorStr.getAsInteger(10, Major) || MinorStr.getAsInteger(10, Minor))
+    return false;
+  if (HasRevision && RevisionStr.getAsInteger(10, Revision))
+    return false;
+
+  // A field too wide for the GPU IP version it spells describes no GPU that
+  // could ever report it, so this is not a numeric name rather than one naming
+  // no device.
+  if (Major > GPUIPMajorMask || Minor > GPUIPMinorMask ||
+      Revision > GPUIPRevisionMask)
+    return false;
+  GPUIPVersion = packDevice(Major, Minor) | Revision;
+  return true;
+}
+
+GPUKind llvm::IntelGPU::parseArch(StringRef Name) {
+  GPUKind Kind = StringSwitch<GPUKind>(Name)
+#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS)    
\
+  .Case(NAME, GK_##KIND)
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS)           
\
+  .Case(NAME, GK_##KIND)
+#define INTEL_GPU_ALIAS(NAME, KIND) .Case(NAME, GK_##KIND)
+#include "llvm/TargetParser/IntelGPUTargetParser.def"
+                     .Default(GK_NONE);
+  if (Kind != GK_NONE)
+    return Kind;
+
+  // A device with no human-friendly name is spelled numerically, so the same
+  // lookup the driver does for a reported GPU IP version has to be reachable 
by
+  // name.
+  uint32_t GPUIPVersion;
+  if (parseNumericArchName(Name, GPUIPVersion))
+    return getKindForVersion(GPUIPVersion);
+  return GK_NONE;
+}
+
+// The suffix each IGCA_FEATURE_SETS token contributes to a target name.
+#define IGCA_FEATURE_SETS_Core ""
+#define IGCA_FEATURE_SETS_Compute "c"
+#define IGCA_FEATURE_SETS_Render "r"
+#define IGCA_FEATURE_SETS_ComputeExact "ca"
+#define IGCA_FEATURE_SETS_RenderExact "ra"
+
+StringRef llvm::IntelGPU::getIGCAName(GPUKind Kind) {
+  // Unlike the NVPTX virtual architecture name, this is not a column of its
+  // own: the target and the feature sets already spell it, and a column would
+  // let the three disagree.
+  switch (Kind) {
+  case GK_NONE:
+    return "";
+#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS)    
\
+  case GK_##KIND:                                                              
\
+    return "igca_" #IGCA_TARGET IGCA_FEATURE_SETS_##IGCA_FEATURE_SETS;
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS)           
\
+  case GK_##KIND:                                                              
\
+    return "igca_" #IGCA_TARGET IGCA_FEATURE_SETS_##IGCA_FEATURE_SETS;
+#include "llvm/TargetParser/IntelGPUTargetParser.def"
+  }
+  llvm_unreachable("invalid Intel GPU GPUKind");
+}
+
+#undef IGCA_FEATURE_SETS_Core
+#undef IGCA_FEATURE_SETS_Compute
+#undef IGCA_FEATURE_SETS_Render
+#undef IGCA_FEATURE_SETS_ComputeExact
+#undef IGCA_FEATURE_SETS_RenderExact
+
+void llvm::IntelGPU::fillValidArchList(SmallVectorImpl<StringRef> &Values) {
+  // An alias is a name the user may write, so it belongs here even though it 
is
+  // never the name reported for a device.
+#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS)    
\
+  Values.push_back(NAME);
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS)           
\
+  Values.push_back(NAME);
+#define INTEL_GPU_ALIAS(NAME, KIND) Values.push_back(NAME);
+#include "llvm/TargetParser/IntelGPUTargetParser.def"
+}
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp 
b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index ab8d456769d0da..147a3e3b4485c7 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -7,6 +7,8 @@
 
//===----------------------------------------------------------------------===//
 
 #include "llvm/TargetParser/IntelGPUTargetParser.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
 #include "gtest/gtest.h"
 #include <cassert>
 
@@ -78,4 +80,106 @@ TEST(IntelGPUTargetParserTest, NumericArchName) {
       "xe_12.60.7");
 }
 
+TEST(IntelGPUTargetParserTest, KindArchNames) {
+  EXPECT_EQ(IntelGPU::getArchName(IntelGPU::GK_XE_PVC), "xe-pvc");
+  EXPECT_EQ(IntelGPU::getArchName(IntelGPU::GK_XE_DG2), "xe-dg2");
+  EXPECT_EQ(IntelGPU::getArchName(IntelGPU::GK_NONE), "");
+}
+
+TEST(IntelGPUTargetParserTest, ParseArch) {
+  // A human-friendly name, a compatibility name, and an alias.
+  EXPECT_EQ(IntelGPU::parseArch("xe-pvc"), IntelGPU::GK_XE_PVC);
+  EXPECT_EQ(IntelGPU::parseArch("xe-dg2"), IntelGPU::GK_XE_DG2);
+  EXPECT_EQ(IntelGPU::parseArch("bmg_g21"), IntelGPU::GK_XE_BMG_G21);
+  // Products built on one GPU IP version are one device, named after the one
+  // the offload-arch utility prints.
+  EXPECT_EQ(IntelGPU::parseArch("xe-arl-u"), IntelGPU::GK_XE_MTL_U);
+  EXPECT_EQ(IntelGPU::parseArch("xe-ats-m150"), IntelGPU::GK_XE_ACM_G10);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.70"), IntelGPU::GK_XE_MTL_U);
+  // Except PVC-SDV, which the AOT compiler builds for its own stepping.
+  EXPECT_EQ(IntelGPU::parseArch("xe-pvc-sdv"), IntelGPU::GK_XE_PVC_SDV);
+
+  // A numeric name names the same device the driver would have reported.
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.0"), IntelGPU::GK_XE_PVC);
+  // The revision takes no part in the lookup, and may be left out.
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.7"), IntelGPU::GK_XE_PVC);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60"), IntelGPU::GK_XE_PVC);
+  // A numeric name for a device this build does not know names no device.
+  EXPECT_EQ(IntelGPU::parseArch("xe_40.11.0"), IntelGPU::GK_NONE);
+
+  // Neither an empty name nor a malformed one names a device.
+  EXPECT_EQ(IntelGPU::parseArch(""), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("pvc"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.pvc"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.7.1"), IntelGPU::GK_NONE);
+  // A trailing separator is not an omitted revision.
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60."), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12."), IntelGPU::GK_NONE);
+
+  // A field wider than the GPU IP version field it spells describes no GPU
+  // that could ever report it, so it names no device however well the rest of
+  // the name matches a row. The major version holds 10 bits, the minor 8, the
+  // revision 6.
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.63"), IntelGPU::GK_XE_PVC);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.64"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.256"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_1024.60"), IntelGPU::GK_NONE);
+  // The widest values that fit name no row either.
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.255"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_1023.60"), IntelGPU::GK_NONE);
+  // Truncated to its field, each of these would name xe-pvc.
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.316"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_1036.60"), IntelGPU::GK_NONE);
+  // Wider than the whole version, so the integer itself does not fit.
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.99999999999"), IntelGPU::GK_NONE);
+}
+
+TEST(IntelGPUTargetParserTest, EveryNameParses) {
+  // Every name the table declares has to be an --offload-arch value, and has 
to
+  // name the row it came from.
+#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS)    
\
+  EXPECT_EQ(IntelGPU::parseArch(NAME), IntelGPU::GK_##KIND) << NAME;
+#define INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS)           
\
+  EXPECT_EQ(IntelGPU::parseArch(NAME), IntelGPU::GK_##KIND) << NAME;
+#define INTEL_GPU_ALIAS(NAME, KIND)                                            
\
+  EXPECT_EQ(IntelGPU::parseArch(NAME), IntelGPU::GK_##KIND) << NAME;
+#include "llvm/TargetParser/IntelGPUTargetParser.def"
+}
+
+TEST(IntelGPUTargetParserTest, AliasesAreNeverReported) {
+  // An alias declares no kind of its own, so the name reported for a device is
+  // the row's own name. A device with two spellings would otherwise depend on
+  // which one the table happened to reach first.
+#define INTEL_GPU_ALIAS(NAME, KIND)                                            
\
+  EXPECT_NE(IntelGPU::getArchName(IntelGPU::GK_##KIND), NAME) << NAME;
+#include "llvm/TargetParser/IntelGPUTargetParser.def"
+}
+
+TEST(IntelGPUTargetParserTest, IGCANames) {
+  // One of each suffix, since the target and the feature sets are pasted
+  // together.
+  EXPECT_EQ(IntelGPU::getIGCAName(IntelGPU::GK_XE_CRI), "igca_60c");
+  EXPECT_EQ(IntelGPU::getIGCAName(IntelGPU::GK_XE_NVL_P), "igca_60r");
+  EXPECT_EQ(IntelGPU::getIGCAName(IntelGPU::GK_XE_PVC), "igca_20ca");
+  EXPECT_EQ(IntelGPU::getIGCAName(IntelGPU::GK_XE_DG2), "igca_15ra");
+  EXPECT_EQ(IntelGPU::getIGCAName(IntelGPU::GK_NONE), "");
+}
+
+TEST(IntelGPUTargetParserTest, ValidArchList) {
+  SmallVector<StringRef> Values;
+  IntelGPU::fillValidArchList(Values);
+
+  EXPECT_FALSE(Values.empty());
+  EXPECT_NE(llvm::find(Values, "xe-pvc"), Values.end());
+  // A compatibility name and an alias are as valid as any other name.
+  EXPECT_NE(llvm::find(Values, "xe-dg2"), Values.end());
+  EXPECT_NE(llvm::find(Values, "bmg_g21"), Values.end());
+  // The list is offered to a user whose name did not parse, so every entry has
+  // to be a name that would have.
+  for (StringRef Value : Values)
+    EXPECT_NE(IntelGPU::parseArch(Value), IntelGPU::GK_NONE) << Value;
+}
+
 } // namespace

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to