[clang] [llvm] [Clang][SYCL] Detect Intel GPUs for --offload-arch=native on a SPIR-V target (PR #229938)

Srividya Sundaram via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 23 11:54:47 PDT 2026


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

>From 4e07d042e43ba094a8f05c838631d32c24853e84 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Sat, 12 Sep 2026 19:09:30 -0700
Subject: [PATCH 01/18] [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

>From e7d62635dc4043e29ac6c50812ed1eba3c8bd02e Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Wed, 7 Oct 2026 15:43:46 -0700
Subject: [PATCH 02/18] [Clang][SYCL] Detect Intel GPUs for
 --offload-arch=native on a SPIR-V target

With --offload-targets=spirv64-unknown-unknown, --offload-arch=native was
silently ignored: the SYCL toolchain did not implement getSystemGPUArchs, so
no GPU was detected and a plain SPIR-V job was built. Run
offload-arch --only=intel, as the NVPTX and AMDGPU toolchains run their
vendor's tool.

Assisted-by: Claude Opus 5.5
---
 clang/lib/Driver/ToolChains/SYCL.cpp          | 26 +++++++++++++++++++
 clang/lib/Driver/ToolChains/SYCL.h            |  3 +++
 .../offload_arch_sm_70_xe_pvc_by_vendor       |  7 +++++
 .../Driver/sycl-native-offload-targets.cpp    | 16 ++++++++++++
 4 files changed, 52 insertions(+)
 create mode 100755 clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor
 create mode 100644 clang/test/Driver/sycl-native-offload-targets.cpp

diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp b/clang/lib/Driver/ToolChains/SYCL.cpp
index b0c07dd1be2eeb..c8aa84ed8fc109 100644
--- a/clang/lib/Driver/ToolChains/SYCL.cpp
+++ b/clang/lib/Driver/ToolChains/SYCL.cpp
@@ -211,3 +211,29 @@ VersionTuple SYCLToolChain::computeMSVCVersion(const Driver *D,
                                                const ArgList &Args) const {
   return HostTC.computeMSVCVersion(D, Args);
 }
+
+Expected<SmallVector<std::string>>
+SYCLToolChain::getSystemGPUArchs(const ArgList &Args) const {
+  // Detect the Intel GPUs on the system, the only GPUs a SPIR-V SYCL target
+  // runs on.
+  std::string Program;
+  if (Arg *A = Args.getLastArg(options::OPT_offload_arch_tool_EQ))
+    Program = A->getValue();
+  else
+    Program = GetProgramPath("offload-arch");
+
+  auto StdoutOrErr = getDriver().executeProgram({Program, "--only=intel"});
+  if (!StdoutOrErr)
+    return StdoutOrErr.takeError();
+
+  SmallVector<std::string, 1> GPUArchs;
+  for (StringRef Arch : llvm::split((*StdoutOrErr)->getBuffer(), "\n"))
+    if (!Arch.empty())
+      GPUArchs.push_back(Arch.str());
+
+  if (GPUArchs.empty())
+    return llvm::createStringError(std::error_code(),
+                                   "No Intel GPU detected in the system");
+
+  return std::move(GPUArchs);
+}
diff --git a/clang/lib/Driver/ToolChains/SYCL.h b/clang/lib/Driver/ToolChains/SYCL.h
index 969199ef249494..a4c3f890837fec 100644
--- a/clang/lib/Driver/ToolChains/SYCL.h
+++ b/clang/lib/Driver/ToolChains/SYCL.h
@@ -34,6 +34,9 @@ class LLVM_LIBRARY_VISIBILITY SYCLToolChain : public ToolChain {
                         llvm::opt::ArgStringList &CC1Args, BoundArch BA,
                         Action::OffloadKind DeviceOffloadKind) const override;
 
+  Expected<SmallVector<std::string>>
+  getSystemGPUArchs(const llvm::opt::ArgList &Args) const override;
+
   bool useIntegratedAs() const override { return true; }
   bool isPICDefault() const override { return false; }
   llvm::codegenoptions::DebugInfoFormat getDefaultDebugFormat() const override {
diff --git a/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor b/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor
new file mode 100755
index 00000000000000..4e37a368690dff
--- /dev/null
+++ b/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor
@@ -0,0 +1,7 @@
+#!/bin/sh
+case "$*" in
+  *--only=intel*) echo "xe_12.60.7" ;;
+  *--only=nvptx*) echo "sm_70" ;;
+  *) echo "sm_70"; echo "xe_12.60.7" ;;
+esac
+exit 0
diff --git a/clang/test/Driver/sycl-native-offload-targets.cpp b/clang/test/Driver/sycl-native-offload-targets.cpp
new file mode 100644
index 00000000000000..c3bf0a75cf18ab
--- /dev/null
+++ b/clang/test/Driver/sycl-native-offload-targets.cpp
@@ -0,0 +1,16 @@
+// Needs chmod
+// UNSUPPORTED: system-windows
+// XFAIL: target={{.*}}-zos{{.*}}
+
+// RUN: mkdir -p %t
+// RUN: cp %S/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor %t/
+// RUN: chmod +x %t/offload_arch_sm_70_xe_pvc_by_vendor
+
+/// With a SPIR-V target, --offload-arch=native picks the system's Intel GPUs.
+// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=native \
+// RUN:   --offload-arch-tool=%t/offload_arch_sm_70_xe_pvc_by_vendor -c %s 2>&1 \
+// RUN:   | FileCheck %s
+// CHECK-NOT: sm_70
+// CHECK: offload, "device-sycl (spirv64-unknown-unknown:xe-pvc)"
+// CHECK-NOT: sm_70

>From c206cbf625c43d1aaeeef86eec221790838019bb Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Mon, 12 Oct 2026 15:10:29 -0700
Subject: [PATCH 03/18] [TargetParser] Simplify Intel GPU parser comments

Address review comments: say which typo the IGCA_FEATURE_SETS token catches,
note that xe-pvc and xe-pvc-sdv are the only rows sharing a version, and
shorten the parseArch comment.

Assisted-by: Claude Opus 5.5
---
 .../llvm/TargetParser/IntelGPUTargetParser.def   |  5 ++---
 .../llvm/TargetParser/IntelGPUTargetParser.h     | 16 ++++++----------
 2 files changed, 8 insertions(+), 13 deletions(-)

diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
index c8faba2a11e1b8..566045539c7233 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
@@ -21,9 +21,8 @@
 //     IGCA_FEATURE_SETS - Token naming the IGCA feature sets the device
 //                         implements: Core (none), Compute ("c"),
 //                         Render ("r"), ComputeExact ("ca") or
-//                         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.
+//                         RenderExact ("ra"). It is a token, so a misspelled
+//                         one such as Compte fails to build.
 //
 //   INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS)
 //     A compatibility name that covers several minor versions, e.g. "xe-dg2",
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index 8713de9579b48f..1131ccf52e64c9 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -40,8 +40,8 @@ enum GPUKind : uint8_t {
 /// \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
-/// of a device resolves to the same name.
-/// If several rows match, the first one in IntelGPUTargetParser.def wins.
+/// of a device resolves to the same name. Only xe-pvc and xe-pvc-sdv share a
+/// version; xe-pvc is returned.
 LLVM_ABI StringRef getArchName(uint32_t GPUIPVersion);
 
 /// \return the numeric name of \p GPUIPVersion, e.g. "xe_35.11.0".
@@ -54,14 +54,10 @@ 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.
+/// \p Name may be a human-friendly name ("xe-pvc"), a compatibility name
+/// ("xe-dg2"), an alias ("bmg_g21") or a numeric name ("xe_12.60.7"). The
+/// revision of a numeric name is ignored and may be omitted, so "xe_12.60",
+/// "xe_12.60.0" and "xe_12.60.7" all denote the same device.
 LLVM_ABI GPUKind parseArch(StringRef Name);
 
 /// \return the IGCA target name to compile \p Kind for, e.g. "xe-pvc" ->

>From 4f6c6b294342e5c61552439b4716082477bceb58 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Mon, 12 Oct 2026 18:07:42 -0700
Subject: [PATCH 04/18] [TargetParser][Clang] Address review: one name per
 Intel GPU

- Drop aliases: each Intel GPU has one name, plus its numeric name.
  bmg_g21 is no longer accepted; use xe-bmg-g21.
- Remove getIGCAName and fillValidArchList until they have a user.
- Canonicalize Intel GPU names for every offload language, not just SYCL.
- Keep clang-sycl-linker's image label as given; the driver passes the
  canonical name.
- Leave the NVPTX CPU check to a separate change.
- Move the Intel GPU driver tests to sycl-intel-gpu-arch.cpp.

Assisted-by: Claude Opus 5.5
---
 clang/docs/ClangSYCLLinker.md                 |  2 +-
 clang/docs/ReleaseNotes.md                    |  4 ++
 clang/lib/Basic/Targets/NVPTX.h               | 11 +---
 clang/lib/Driver/Driver.cpp                   | 25 ++++----
 clang/lib/Driver/ToolChain.cpp                | 11 ++--
 clang/lib/Driver/ToolChains/Clang.cpp         |  5 +-
 clang/test/Driver/sycl-intel-gpu-arch.cpp     | 55 +++++++++++++++++
 clang/test/Driver/sycl-offload-jit.cpp        | 61 +------------------
 .../test/Misc/target-invalid-cpu-note/nvptx.c |  4 --
 .../OffloadTools/clang-sycl-linker/basic.ll   |  9 ++-
 .../clang-sycl-linker/ClangSYCLLinker.cpp     |  8 +--
 clang/unittests/Basic/OffloadArchTest.cpp     | 12 ++--
 .../TargetParser/IntelGPUTargetParser.def     | 43 ++-----------
 .../llvm/TargetParser/IntelGPUTargetParser.h  | 26 ++------
 .../lib/TargetParser/IntelGPUTargetParser.cpp | 46 +-------------
 .../TargetParser/IntelGPUTargetParserTest.cpp | 49 ++-------------
 16 files changed, 110 insertions(+), 261 deletions(-)
 create mode 100644 clang/test/Driver/sycl-intel-gpu-arch.cpp

diff --git a/clang/docs/ClangSYCLLinker.md b/clang/docs/ClangSYCLLinker.md
index b4f5e751b03bb7..c527afa8b41f6d 100644
--- a/clang/docs/ClangSYCLLinker.md
+++ b/clang/docs/ClangSYCLLinker.md
@@ -105,7 +105,7 @@ linked device images that are ready to be wrapped and linked with host code to
 generate the final executable.
 
 ```console
-clang-sycl-linker --triple spirv64 --arch bmg_g21 input.bc
+clang-sycl-linker --triple spirv64 --arch xe-bmg-g21 input.bc
 ```
 
 ### Linking with Device Libraries
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 4465396bd60428..8fbca2642b617b 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1171,6 +1171,10 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use
 
 ### SYCL Support
 
+- `--offload-arch` accepts the Intel GPU names from
+  `llvm/TargetParser/IntelGPUTargetParser.def`, e.g. `xe-pvc`, and their numeric
+  names, e.g. `xe_12.60.7`. `bmg_g21` is no longer accepted; use `xe-bmg-g21`.
+
 #### Improvements
 
 ## Additional Information
diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h
index 45ba8322cf5fca..b6d2bfd3201034 100644
--- a/clang/lib/Basic/Targets/NVPTX.h
+++ b/clang/lib/Basic/Targets/NVPTX.h
@@ -135,13 +135,8 @@ 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 {
-    OffloadArch Arch = StringToOffloadArch(Name);
-    return Arch.isNVPTX() || Arch.isAMDGPU() || Arch.isAMDGCNSPIRV() ||
-           Arch.isGeneric() || Arch.isUnused();
+    return !StringToOffloadArch(Name).isUnknown();
   }
 
   void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override {
@@ -149,10 +144,8 @@ class LLVM_LIBRARY_VISIBILITY NVPTXTargetInfo : public TargetInfo {
   }
 
   bool setCPU(StringRef Name) override {
-    if (!isValidCPUName(Name))
-      return false;
     GPU = StringToOffloadArch(Name);
-    return true;
+    return !GPU.isUnknown();
   }
 
   void setSupportedOpenCLOpts() override {
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index bbcf88d8237762..d48d5339f1934f 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3898,19 +3898,17 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
   Args.ClaimAllArgs(options::OPT_cl_ignored_Group);
 }
 
-/// Returns the canonical name for the offloading architecture when using a HIP
-/// or CUDA architecture, or an Intel GPU for SYCL.
+/// Returns the canonical name for the offloading architecture when using a HIP,
+/// CUDA or Intel GPU architecture.
 static StringRef getCanonicalArchString(Compilation &C,
                                         const llvm::opt::DerivedArgList &Args,
                                         StringRef ArchStr,
-                                        const llvm::Triple &Triple,
-                                        Action::OffloadKind Kind) {
+                                        const llvm::Triple &Triple) {
   // 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".
+  // An Intel name takes no target features, so "xe-pvc:foo" is not "xe-pvc".
   if (Arch.isIntel() && ArchStr.contains(':'))
     Arch = OffloadArch::getUnknown();
   if (Triple.isNVPTX() && (Arch.isUnknown() || !Arch.isNVPTX())) {
@@ -3935,10 +3933,9 @@ static StringRef getCanonicalArchString(Compilation &C,
     }
   }
 
-  // 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()))
+  // Several spellings of one device must be one architecture, e.g. "xe-pvc" and
+  // "xe_12.60.7".
+  if (Arch.isNVPTX() || Arch.isIntelGPU())
     return Args.MakeArgStringRef(OffloadArchToString(Arch));
 
   if (Arch.isAMDGPU() || Arch.isAMDGCNSPIRV()) {
@@ -3999,7 +3996,7 @@ Driver::getOffloadArchs(Compilation &C, const llvm::opt::DerivedArgList &Args,
 
           for (auto ArchStr : *GPUsOrErr) {
             StringRef CanonicalStr = getCanonicalArchString(
-                C, Args, Args.MakeArgString(ArchStr), TC.getTriple(), Kind);
+                C, Args, Args.MakeArgString(ArchStr), TC.getTriple());
             if (!CanonicalStr.empty())
               Archs.insert(CanonicalStr);
             else
@@ -4007,7 +4004,7 @@ Driver::getOffloadArchs(Compilation &C, const llvm::opt::DerivedArgList &Args,
           }
         } else {
           StringRef CanonicalStr =
-              getCanonicalArchString(C, Args, Arch, TC.getTriple(), Kind);
+              getCanonicalArchString(C, Args, Arch, TC.getTriple());
           if (!CanonicalStr.empty())
             Archs.insert(CanonicalStr);
           else
@@ -4020,7 +4017,7 @@ Driver::getOffloadArchs(Compilation &C, const llvm::opt::DerivedArgList &Args,
           Archs.clear();
         } else {
           StringRef ArchStr =
-              getCanonicalArchString(C, Args, Arch, TC.getTriple(), Kind);
+              getCanonicalArchString(C, Args, Arch, TC.getTriple());
           Archs.erase(ArchStr);
         }
       }
@@ -4076,7 +4073,7 @@ Driver::getOffloadArchs(Compilation &C, const llvm::opt::DerivedArgList &Args,
                                   ? ""
                                   : OffloadArchToString(TripleOffloadArch);
     StringRef CanonicalStr =
-        getCanonicalArchString(C, Args, ArchStr, TC.getTriple(), Kind);
+        getCanonicalArchString(C, Args, ArchStr, TC.getTriple());
     if (!CanonicalStr.empty())
       Archs.insert(CanonicalStr);
   }
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index d7928cd10ea242..ae6087a155211c 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -2189,12 +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);
+      // An Intel GPU has several spellings, so -Xarch_ may name it by another.
+      NeedTrans =
+          Val == getArchName() || (BA && Val == BA.ArchName) ||
+          (BA && BA.Arch.isIntelGPU() && StringToOffloadArch(Val) == BA.Arch) ||
+          isXArchCompatibleTripleArch(Triple, Val);
       Skip = !NeedTrans;
     }
     if (NeedTrans || Skip)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 7071ee6b3bebe7..7258a86f842823 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -9864,11 +9864,10 @@ 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.
-      // A SYCL Intel GPU is requested by its canonical name, the one its device
-      // images are labeled with, whichever spelling the user wrote.
+      // Request an Intel GPU by the canonical name its images are labeled with.
       for (StringRef Arg : ToolChainArgs.getAllArgValues(OPT_offload_arch_EQ)) {
         OffloadArch ID = StringToOffloadArch(Arg);
-        if (Kind == Action::OFK_SYCL && ID.isIntelGPU())
+        if (ID.isIntelGPU())
           Arg = OffloadArchToString(ID);
         CmdArgs.emplace_back(Args.MakeArgString("--should-extract=" + Arg));
       }
diff --git a/clang/test/Driver/sycl-intel-gpu-arch.cpp b/clang/test/Driver/sycl-intel-gpu-arch.cpp
new file mode 100644
index 00000000000000..9818d18647ce8e
--- /dev/null
+++ b/clang/test/Driver/sycl-intel-gpu-arch.cpp
@@ -0,0 +1,55 @@
+/// Tests for Intel GPU names in --offload-arch.
+
+/// A device name and its numeric name are one architecture, spelled with the
+/// device name.
+// 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=xe_20.1.0 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-SPELLINGS %s
+// CHK-SPELLINGS-NOT: offload, "device-sycl
+// CHK-SPELLINGS: offload, "device-sycl (spirv64-unknown-unknown:xe-bmg-g21)"
+// CHK-SPELLINGS-NOT: offload, "device-sycl
+
+/// Removing either spelling 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=xe_20.1.0 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-NO-ARCH %s
+// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe_20.1.0 \
+// RUN:   --offload-arch=generic --no-offload-arch=xe-bmg-g21 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-NO-ARCH %s
+// CHK-NO-ARCH-NOT: bmg
+// CHK-NO-ARCH: offload, "device-sycl (spirv64-unknown-unknown:generic)"
+// CHK-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-SUFFIX %s
+// CHK-SUFFIX: offload, "device-sycl (spirv64-unknown-unknown:xe-pvc:foo)"
+
+/// -Xarch_ may name an Intel GPU by either spelling.
+// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe_20.1.0 \
+// RUN:   -Xarch_xe-bmg-g21 -DBMG -Xarch_xe-pvc -DPVC -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-XARCH %s
+// CHK-XARCH: "-fsycl-is-device"
+// CHK-XARCH-SAME: "-D" "BMG"
+// CHK-XARCH-NOT: "PVC"
+
+/// A static library's device code is requested by the device name.
+// 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-EXTRACT %s
+// CHK-EXTRACT: clang-linker-wrapper{{.*}}"--should-extract=xe-bmg-g21"
+
+/// The same holds for any offload language, e.g. OpenMP.
+// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fopenmp=libomp \
+// RUN:   --offload-targets=spirv64-intel --offload-arch=xe-bmg-g21 \
+// RUN:   --offload-arch=xe_20.1.0 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-OPENMP %s
+// CHK-OPENMP-NOT: offload, "device-openmp
+// CHK-OPENMP: offload, "device-openmp (spirv64-intel:xe-bmg-g21)"
+// CHK-OPENMP-NOT: offload, "device-openmp
diff --git a/clang/test/Driver/sycl-offload-jit.cpp b/clang/test/Driver/sycl-offload-jit.cpp
index cb4283db51d25b..82733ccd2bc6f2 100644
--- a/clang/test/Driver/sycl-offload-jit.cpp
+++ b/clang/test/Driver/sycl-offload-jit.cpp
@@ -90,68 +90,11 @@
 // 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:generic)" {6}, ir
-// CHK-PHASES-NORDC-ARCHS: 12: offload, "device-sycl (spirv64-unknown-unknown:xe-bmg-g21)" {11}, ir
+// 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-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 b2a38b6b268253..20dbd8dc6aa3b6 100644
--- a/clang/test/Misc/target-invalid-cpu-note/nvptx.c
+++ b/clang/test/Misc/target-invalid-cpu-note/nvptx.c
@@ -2,10 +2,6 @@
 // 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 0376e7bf883388..23c17f3bb07f45 100644
--- a/clang/test/OffloadTools/clang-sycl-linker/basic.ll
+++ b/clang/test/OffloadTools/clang-sycl-linker/basic.ll
@@ -109,7 +109,7 @@
 ;
 ; Test AOT compilation for an Intel GPU.
 ; Test that IMG_Object image kind is set for AOT compilation (Intel GPU).
-; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit -arch=bmg_g21 %t/input1.bc %t/input2.bc -o %t/aot-gpu.out 2>&1 \
+; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit -arch=xe-bmg-g21 %t/input1.bc %t/input2.bc -o %t/aot-gpu.out 2>&1 \
 ; RUN:     --ocloc-options="-a -b" \
 ; RUN:   | FileCheck %s --check-prefix=AOT-INTEL-GPU
 ; AOT-INTEL-GPU:      link: inputs: {{.*}}.bc, {{.*}}.bc output: [[LLVMLINKOUT:.*]].bc
@@ -118,19 +118,18 @@
 ; 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.
+; Test that an Intel GPU name, or its numeric name, 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: 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:     --ocloc-options="-a -b" --ocloc-options=-c --ocloc-options="d" --ocloc-options='"-e -f"' \
 ; RUN:   | FileCheck %s --check-prefix=AOT-INTEL-OCLOC-OPTIONS
 ; AOT-INTEL-OCLOC-OPTIONS: "{{.*}}ocloc{{.*}}" {{.*}}-device bmg_g21 -a -b -c d "-e -f"
@@ -151,7 +150,7 @@
 ; current directory (with a unique suffix to avoid collisions between
 ; concurrent invocations sharing an output basename).
 ; RUN: rm -rf %t/outdir && mkdir -p %t/outdir
-; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit -arch=bmg_g21 %t/input1.bc -o %t/outdir/nested.out 2>&1 \
+; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit -arch=xe-bmg-g21 %t/input1.bc -o %t/outdir/nested.out 2>&1 \
 ; RUN:   | FileCheck %s --check-prefix=AOT-STEM-BASENAME
 ; AOT-STEM-BASENAME:      LLVM backend: input: {{.*}}.bc, output: [[SPVSTEM:nested.*]].spv
 ; AOT-STEM-BASENAME-NEXT: "{{.*}}ocloc{{.*}}" {{.*}}-output [[SPVSTEM]].out -file [[SPVSTEM]].spv
diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
index 01463c15aa0dbf..50f1d55450cfd2 100644
--- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
+++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
@@ -717,7 +717,7 @@ 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.
+  // e.g. "bmg_g21" for "xe-bmg-g21".
   StringRef Canonical = OffloadArchToString(StringToOffloadArch(Arch));
   [[maybe_unused]] bool HasPrefix = Canonical.consume_front("xe-");
   assert(HasPrefix && "Intel GPU name without the xe- prefix");
@@ -1052,12 +1052,8 @@ 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"] =
-        OA.isIntelGPU() ? OffloadArchToString(OA) : Args.MakeArgString(Arch);
+        Args.MakeArgString(Args.getLastArgValue(OPT_arch_EQ));
     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 980a88aba6114b..ac87d8395eb264 100644
--- a/clang/unittests/Basic/OffloadArchTest.cpp
+++ b/clang/unittests/Basic/OffloadArchTest.cpp
@@ -66,21 +66,19 @@ TEST(OffloadArchTest, RoundTrip) {
   }
 }
 
-// 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.
+// A numeric name denotes the device, and canonicalizes to the device name.
+TEST(OffloadArchTest, IntelGPUNumericNames) {
+  // The 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());
+  // So is the spelling clang accepted before the Intel GPU list existed.
+  EXPECT_TRUE(parse("bmg_g21").isUnknown());
 }
 
 TEST(OffloadArchTest, Defaults) {
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
index 566045539c7233..d722d999444b68 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
@@ -21,8 +21,7 @@
 //     IGCA_FEATURE_SETS - Token naming the IGCA feature sets the device
 //                         implements: Core (none), Compute ("c"),
 //                         Render ("r"), ComputeExact ("ca") or
-//                         RenderExact ("ra"). It is a token, so a misspelled
-//                         one such as Compte fails to build.
+//                         RenderExact ("ra").
 //
 //   INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS)
 //     A compatibility name that covers several minor versions, e.g. "xe-dg2",
@@ -31,24 +30,14 @@
 //     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.
 //
-// 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.
+// Products that share a major and a minor version have one row, named after
+// the product the offload-arch utility prints. 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
@@ -96,27 +85,5 @@ 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 1131ccf52e64c9..940a6b36f6c846 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -20,7 +20,6 @@
 #include <string>
 
 namespace llvm {
-template <typename T> class SmallVectorImpl;
 
 namespace IntelGPU {
 
@@ -44,35 +43,22 @@ enum GPUKind : uint8_t {
 /// version; xe-pvc is returned.
 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 numeric name of \p GPUIPVersion, e.g. "xe_35.11.0".
+LLVM_ABI std::string getNumericArchName(uint32_t GPUIPVersion);
+
 /// \return the device \p Name denotes, or GK_NONE for a name this build does
 /// not know.
 ///
 /// \p Name may be a human-friendly name ("xe-pvc"), a compatibility name
-/// ("xe-dg2"), an alias ("bmg_g21") or a numeric name ("xe_12.60.7"). The
-/// revision of a numeric name is ignored and may be omitted, so "xe_12.60",
-/// "xe_12.60.0" and "xe_12.60.7" all denote the same device.
+/// ("xe-dg2") or a numeric name ("xe_12.60.7"). The revision of a numeric name
+/// is ignored and may be omitted, so "xe_12.60", "xe_12.60.0" and "xe_12.60.7"
+/// all denote the same device.
 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 044daddc868f1e..2f29fa2f282166 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -11,7 +11,6 @@
 //===----------------------------------------------------------------------===//
 
 #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"
@@ -87,7 +86,7 @@ StringRef llvm::IntelGPU::getArchName(GPUKind Kind) {
     return NAME;
 #include "llvm/TargetParser/IntelGPUTargetParser.def"
   }
-  llvm_unreachable("invalid Intel GPU GPUKind");
+  llvm_unreachable("invalid Intel GPU kind");
 }
 
 // Read a numeric architecture name, e.g. "xe_12.60.7", into the GPU IP version
@@ -126,7 +125,6 @@ GPUKind llvm::IntelGPU::parseArch(StringRef Name) {
   .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)
@@ -140,45 +138,3 @@ GPUKind llvm::IntelGPU::parseArch(StringRef Name) {
     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 147a3e3b4485c7..e04e93dc69e7a8 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -7,8 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/TargetParser/IntelGPUTargetParser.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallVector.h"
 #include "gtest/gtest.h"
 #include <cassert>
 
@@ -87,14 +85,13 @@ TEST(IntelGPUTargetParserTest, KindArchNames) {
 }
 
 TEST(IntelGPUTargetParserTest, ParseArch) {
-  // A human-friendly name, a compatibility name, and an alias.
+  // A human-friendly name and a compatibility name.
   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);
+  // Each device has one name; another product on its GPU IP version is named
+  // by its numeric name instead.
+  EXPECT_EQ(IntelGPU::parseArch("bmg_g21"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe-arl-u"), IntelGPU::GK_NONE);
   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);
@@ -143,43 +140,7 @@ TEST(IntelGPUTargetParserTest, EveryNameParses) {
   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

>From 8bf280443889135fd10afb41cb247a3856d25c5e Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Wed, 14 Oct 2026 17:58:02 -0700
Subject: [PATCH 05/18] [Clang][Offload] Request canonical architectures in
 --should-extract

The linker wrapper extracts device code from a static library only when a
--should-extract value matches the image's arch label exactly. The label is
the canonical architecture, but --should-extract was built from the raw
--offload-arch values, so --offload-arch=gfx908:xnack+:sramecc- labeled the
image gfx908:sramecc-:xnack+ yet requested gfx908:xnack+:sramecc-, and an
architecture removed with --no-offload-arch was still requested. Take the
values from getOffloadArchs, which canonicalizes and applies
--no-offload-arch.

Assisted-by: Claude Opus 5.5
---
 clang/lib/Driver/ToolChains/Clang.cpp      | 11 ++++++++---
 clang/test/Driver/offload-should-extract.c | 17 +++++++++++++++++
 2 files changed, 25 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/Driver/offload-should-extract.c

diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index eb7bd3ad1ad5e2..a67f6a761d0e6b 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -9909,9 +9909,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))
-        CmdArgs.emplace_back(Args.MakeArgString("--should-extract=" + Arg));
+      // extract it from any static libraries if present. Use the canonical
+      // name, which device images are labeled with.
+      if (ToolChainArgs.hasArg(OPT_offload_arch_EQ))
+        for (const BoundArch &BA :
+             C.getDriver().getOffloadArchs(C, ToolChainArgs, Kind, *TC))
+          if (!BA.ArchName.empty())
+            CmdArgs.emplace_back(
+                Args.MakeArgString("--should-extract=" + BA.ArchName));
 
       // 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/offload-should-extract.c b/clang/test/Driver/offload-should-extract.c
new file mode 100644
index 00000000000000..f610466e9a562b
--- /dev/null
+++ b/clang/test/Driver/offload-should-extract.c
@@ -0,0 +1,17 @@
+// The linker wrapper extracts device code from static libraries by the
+// architecture its images are labeled with, so request the canonical name.
+
+// RUN: %clang -### --target=x86_64-unknown-linux-gnu -x hip -nogpulib -nogpuinc \
+// RUN:   --offload-arch=gfx908:xnack+:sramecc- %s 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CANONICAL
+// CANONICAL: "--image=file={{.*}},triple={{[^,]*}},arch=gfx908:sramecc-:xnack+,kind=hip"
+// CANONICAL: clang-linker-wrapper{{.*}} "--should-extract=gfx908:sramecc-:xnack+"
+
+/// An architecture removed with --no-offload-arch is not requested.
+// RUN: %clang -### --target=x86_64-unknown-linux-gnu -x hip -nogpulib -nogpuinc \
+// RUN:   --offload-arch=gfx906 --offload-arch=gfx90a --no-offload-arch=gfx906 %s 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=REMOVED
+// REMOVED: clang-linker-wrapper
+// REMOVED-NOT: "--should-extract=gfx906"
+// REMOVED-SAME: "--should-extract=gfx90a"
+// REMOVED-NOT: "--should-extract=gfx906"

>From 15e07259b4072141cf3c066fb04a07602eba223c Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Wed, 14 Oct 2026 18:01:25 -0700
Subject: [PATCH 06/18] [Clang] Address review: update a comment, move the ':'
 check

Update the comment in getCanonicalArchString, and move the check that an
Intel name takes no target features to the change that reports it as an
error. --should-extract now comes from getOffloadArchs, so it needs no
Intel-specific code.

Assisted-by: Claude Opus 5.5
---
 clang/lib/Driver/Driver.cpp               | 7 ++-----
 clang/test/Driver/sycl-intel-gpu-arch.cpp | 7 -------
 2 files changed, 2 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index a36a8d1cecf917..34de6a4ea5ea85 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3905,13 +3905,10 @@ static StringRef getCanonicalArchString(Compilation &C,
                                         const llvm::opt::DerivedArgList &Args,
                                         StringRef ArchStr,
                                         const llvm::Triple &Triple) {
-  // Lookup the CUDA / HIP architecture string. Only report an error if we were
-  // expecting the triple to be only NVPTX / AMDGPU.
+  // Look up the architecture. Only report an error if the triple expects an
+  // NVPTX or AMDGPU one.
   OffloadArch Arch =
       StringToOffloadArch(getProcessorFromTargetID(Triple, ArchStr));
-  // An Intel name takes no target features, so "xe-pvc:foo" is not "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;
diff --git a/clang/test/Driver/sycl-intel-gpu-arch.cpp b/clang/test/Driver/sycl-intel-gpu-arch.cpp
index 9818d18647ce8e..51fc46afed17cd 100644
--- a/clang/test/Driver/sycl-intel-gpu-arch.cpp
+++ b/clang/test/Driver/sycl-intel-gpu-arch.cpp
@@ -23,13 +23,6 @@
 // CHK-NO-ARCH: offload, "device-sycl (spirv64-unknown-unknown:generic)"
 // CHK-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-SUFFIX %s
-// CHK-SUFFIX: offload, "device-sycl (spirv64-unknown-unknown:xe-pvc:foo)"
-
 /// -Xarch_ may name an Intel GPU by either spelling.
 // RUN: %clang -### --target=x86_64-unknown-linux-gnu -fsycl \
 // RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe_20.1.0 \

>From 1415842113580ca9d05fc88701ff412aa5764833 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Wed, 14 Oct 2026 19:08:53 -0700
Subject: [PATCH 07/18] [Clang] Pass the Intel GPU name to ocloc unchanged

ocloc is to accept the names in IntelGPUTargetParser.def, and nothing
guarantees that a name without its "xe-" prefix is an ocloc device name, so
drop the translation in clang-sycl-linker and keep the original code.

Assisted-by: Claude Opus 5.5
---
 clang/test/OffloadTools/clang-sycl-linker/basic.ll | 14 ++------------
 clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp  |  9 +--------
 2 files changed, 3 insertions(+), 20 deletions(-)

diff --git a/clang/test/OffloadTools/clang-sycl-linker/basic.ll b/clang/test/OffloadTools/clang-sycl-linker/basic.ll
index 23c17f3bb07f45..6dcba6174c44cb 100644
--- a/clang/test/OffloadTools/clang-sycl-linker/basic.ll
+++ b/clang/test/OffloadTools/clang-sycl-linker/basic.ll
@@ -114,25 +114,15 @@
 ; RUN:   | FileCheck %s --check-prefix=AOT-INTEL-GPU
 ; 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: "{{.*}}ocloc{{.*}}" {{.*}}-device xe-bmg-g21 -a -b {{.*}}-output [[SPVSTEM]].out -file [[SPVSTEM]].spv
 ; AOT-INTEL-GPU-NEXT: sycl-bundle: image kind: o, triple: spirv64, arch: xe-bmg-g21
 ; AOT-INTEL-GPU-NOT:  {{.+}}
 ;
-; Test that an Intel GPU name, or its numeric name, 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
-; 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=xe-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"' \
 ; RUN:   | FileCheck %s --check-prefix=AOT-INTEL-OCLOC-OPTIONS
-; AOT-INTEL-OCLOC-OPTIONS: "{{.*}}ocloc{{.*}}" {{.*}}-device bmg_g21 -a -b -c d "-e -f"
+; AOT-INTEL-OCLOC-OPTIONS: "{{.*}}ocloc{{.*}}" {{.*}}-device xe-bmg-g21 -a -b -c d "-e -f"
 ;
 ; Test AOT compilation for an Intel CPU.
 ; Test that IMG_Object image kind is set for AOT compilation (Intel CPU).
diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
index 50f1d55450cfd2..60ab7dc4e2a7d1 100644
--- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
+++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
@@ -716,15 +716,8 @@ 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".
-  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(Device);
+  CmdArgs.push_back(Arch);
 
   // getAllArgValues returns a temporary vector; retain it so the StringRefs
   // remain valid through the executeCommands call below.

>From a8105f5936156515c5c4241a57166784f66b776f Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Mon, 19 Oct 2026 10:42:13 -0700
Subject: [PATCH 08/18] [TargetParser] Drop xe-pvc-sdv from the Intel GPU list

Upstream does not need to target it.

Assisted-by: Claude Opus 5.5
---
 llvm/include/llvm/TargetParser/IntelGPUTargetParser.def  | 5 +----
 llvm/include/llvm/TargetParser/IntelGPUTargetParser.h    | 3 +--
 llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp | 2 --
 3 files changed, 2 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
index d722d999444b68..76a82fa3879efb 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
@@ -35,9 +35,7 @@
 // every revision of a device shares one name and one IGCA target.
 //
 // Products that share a major and a minor version have one row, named after
-// the product the offload-arch utility prints. 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.
+// the product the offload-arch utility prints.
 //
 // 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
@@ -64,7 +62,6 @@ 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-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-acm-g11",  XE_ACM_G11,  12, 56,  15, RenderExact)
 INTEL_GPU("xe-acm-g10",  XE_ACM_G10,  12, 55,  15, RenderExact)
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index 940a6b36f6c846..7ebf4134683a89 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -39,8 +39,7 @@ enum GPUKind : uint8_t {
 /// \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
-/// of a device resolves to the same name. Only xe-pvc and xe-pvc-sdv share a
-/// version; xe-pvc is returned.
+/// of a device resolves to the same name.
 LLVM_ABI StringRef getArchName(uint32_t GPUIPVersion);
 
 /// \return the human-friendly name of \p Kind, e.g. "xe-pvc", or an empty
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index e04e93dc69e7a8..924dc11f867cec 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -93,8 +93,6 @@ TEST(IntelGPUTargetParserTest, ParseArch) {
   EXPECT_EQ(IntelGPU::parseArch("bmg_g21"), IntelGPU::GK_NONE);
   EXPECT_EQ(IntelGPU::parseArch("xe-arl-u"), IntelGPU::GK_NONE);
   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);

>From 687ccb1f1d15967b7eeec39ae788b85d859370c6 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Mon, 19 Oct 2026 17:04:01 -0700
Subject: [PATCH 09/18] [TargetParser][Clang] Address review: one
 --offload-arch value per Intel GPU

Drop the numeric names, so each Intel GPU has exactly one --offload-arch
value. An Intel name is then already canonical, so remove the driver code
that canonicalized Intel GPU names, -Xarch_ matching by another spelling,
and the --should-extract change merged in from the base branch.

Assisted-by: Claude Opus 5.5
---
 clang/docs/ReleaseNotes.md                    |  4 +-
 clang/lib/Driver/Driver.cpp                   | 12 ++---
 clang/lib/Driver/ToolChain.cpp                |  7 +--
 clang/lib/Driver/ToolChains/Clang.cpp         | 11 ++---
 clang/test/Driver/offload-should-extract.c    | 17 -------
 clang/test/Driver/sycl-intel-gpu-arch.cpp     | 45 +++---------------
 clang/unittests/Basic/OffloadArchTest.cpp     | 16 ++-----
 .../llvm/TargetParser/IntelGPUTargetParser.h  |  9 +---
 .../lib/TargetParser/IntelGPUTargetParser.cpp | 47 ++-----------------
 .../TargetParser/IntelGPUTargetParserTest.cpp | 43 ++---------------
 10 files changed, 31 insertions(+), 180 deletions(-)
 delete mode 100644 clang/test/Driver/offload-should-extract.c

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 5a20b774704c60..1aa58f38e9726a 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1323,8 +1323,8 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use
 ### SYCL Support
 
 - `--offload-arch` accepts the Intel GPU names from
-  `llvm/TargetParser/IntelGPUTargetParser.def`, e.g. `xe-pvc`, and their numeric
-  names, e.g. `xe_12.60.7`. `bmg_g21` is no longer accepted; use `xe-bmg-g21`.
+  `llvm/TargetParser/IntelGPUTargetParser.def`, e.g. `xe-pvc`. `bmg_g21` is no
+  longer accepted; use `xe-bmg-g21`.
 
 #### Improvements
 
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 34de6a4ea5ea85..a9a21175af9c5e 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3899,14 +3899,14 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
   Args.ClaimAllArgs(options::OPT_cl_ignored_Group);
 }
 
-/// Returns the canonical name for the offloading architecture when using a HIP,
-/// CUDA or Intel GPU architecture.
+/// Returns the canonical name for the offloading architecture when using a HIP
+/// or CUDA architecture.
 static StringRef getCanonicalArchString(Compilation &C,
                                         const llvm::opt::DerivedArgList &Args,
                                         StringRef ArchStr,
                                         const llvm::Triple &Triple) {
-  // Look up the architecture. Only report an error if the triple expects an
-  // NVPTX or AMDGPU one.
+  // 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));
   if (Triple.isNVPTX() && (Arch.isUnknown() || !Arch.isNVPTX())) {
@@ -3931,9 +3931,7 @@ static StringRef getCanonicalArchString(Compilation &C,
     }
   }
 
-  // Several spellings of one device must be one architecture, e.g. "xe-pvc" and
-  // "xe_12.60.7".
-  if (Arch.isNVPTX() || Arch.isIntelGPU())
+  if (Arch.isNVPTX())
     return Args.MakeArgStringRef(OffloadArchToString(Arch));
 
   // AMDGCN target IDs carry a processor and xnack/sramecc modifiers to
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index ae6087a155211c..255a9c9602b540 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -2189,11 +2189,8 @@ llvm::opt::DerivedArgList *ToolChain::TranslateXarchArgs(
       Skip = IsDevice;
     } else if (A->getOption().matches(options::OPT_Xarch__)) {
       StringRef Val = A->getValue();
-      // An Intel GPU has several spellings, so -Xarch_ may name it by another.
-      NeedTrans =
-          Val == getArchName() || (BA && Val == BA.ArchName) ||
-          (BA && BA.Arch.isIntelGPU() && StringToOffloadArch(Val) == BA.Arch) ||
-          isXArchCompatibleTripleArch(Triple, Val);
+      NeedTrans = Val == getArchName() || (BA && Val == BA.ArchName) ||
+                  isXArchCompatibleTripleArch(Triple, Val);
       Skip = !NeedTrans;
     }
     if (NeedTrans || Skip)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index a67f6a761d0e6b..eb7bd3ad1ad5e2 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -9909,14 +9909,9 @@ 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. Use the canonical
-      // name, which device images are labeled with.
-      if (ToolChainArgs.hasArg(OPT_offload_arch_EQ))
-        for (const BoundArch &BA :
-             C.getDriver().getOffloadArchs(C, ToolChainArgs, Kind, *TC))
-          if (!BA.ArchName.empty())
-            CmdArgs.emplace_back(
-                Args.MakeArgString("--should-extract=" + BA.ArchName));
+      // extract it from any static libraries if present.
+      for (StringRef Arg : ToolChainArgs.getAllArgValues(OPT_offload_arch_EQ))
+        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/offload-should-extract.c b/clang/test/Driver/offload-should-extract.c
deleted file mode 100644
index f610466e9a562b..00000000000000
--- a/clang/test/Driver/offload-should-extract.c
+++ /dev/null
@@ -1,17 +0,0 @@
-// The linker wrapper extracts device code from static libraries by the
-// architecture its images are labeled with, so request the canonical name.
-
-// RUN: %clang -### --target=x86_64-unknown-linux-gnu -x hip -nogpulib -nogpuinc \
-// RUN:   --offload-arch=gfx908:xnack+:sramecc- %s 2>&1 \
-// RUN:   | FileCheck %s --check-prefix=CANONICAL
-// CANONICAL: "--image=file={{.*}},triple={{[^,]*}},arch=gfx908:sramecc-:xnack+,kind=hip"
-// CANONICAL: clang-linker-wrapper{{.*}} "--should-extract=gfx908:sramecc-:xnack+"
-
-/// An architecture removed with --no-offload-arch is not requested.
-// RUN: %clang -### --target=x86_64-unknown-linux-gnu -x hip -nogpulib -nogpuinc \
-// RUN:   --offload-arch=gfx906 --offload-arch=gfx90a --no-offload-arch=gfx906 %s 2>&1 \
-// RUN:   | FileCheck %s --check-prefix=REMOVED
-// REMOVED: clang-linker-wrapper
-// REMOVED-NOT: "--should-extract=gfx906"
-// REMOVED-SAME: "--should-extract=gfx90a"
-// REMOVED-NOT: "--should-extract=gfx906"
diff --git a/clang/test/Driver/sycl-intel-gpu-arch.cpp b/clang/test/Driver/sycl-intel-gpu-arch.cpp
index 51fc46afed17cd..bd7f018fa854df 100644
--- a/clang/test/Driver/sycl-intel-gpu-arch.cpp
+++ b/clang/test/Driver/sycl-intel-gpu-arch.cpp
@@ -1,48 +1,15 @@
 /// Tests for Intel GPU names in --offload-arch.
 
-/// A device name and its numeric name are one architecture, spelled with the
-/// device name.
+/// An Intel GPU name names a device job.
 // 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=xe_20.1.0 -c %s 2>&1 \
-// RUN:   | FileCheck -check-prefixes=CHK-SPELLINGS %s
-// CHK-SPELLINGS-NOT: offload, "device-sycl
-// CHK-SPELLINGS: offload, "device-sycl (spirv64-unknown-unknown:xe-bmg-g21)"
-// CHK-SPELLINGS-NOT: offload, "device-sycl
-
-/// Removing either spelling 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=xe_20.1.0 -c %s 2>&1 \
-// RUN:   | FileCheck -check-prefixes=CHK-NO-ARCH %s
-// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
-// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe_20.1.0 \
-// RUN:   --offload-arch=generic --no-offload-arch=xe-bmg-g21 -c %s 2>&1 \
-// RUN:   | FileCheck -check-prefixes=CHK-NO-ARCH %s
-// CHK-NO-ARCH-NOT: bmg
-// CHK-NO-ARCH: offload, "device-sycl (spirv64-unknown-unknown:generic)"
-// CHK-NO-ARCH-NOT: bmg
-
-/// -Xarch_ may name an Intel GPU by either spelling.
-// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fsycl \
-// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe_20.1.0 \
-// RUN:   -Xarch_xe-bmg-g21 -DBMG -Xarch_xe-pvc -DPVC -c %s 2>&1 \
-// RUN:   | FileCheck -check-prefixes=CHK-XARCH %s
-// CHK-XARCH: "-fsycl-is-device"
-// CHK-XARCH-SAME: "-D" "BMG"
-// CHK-XARCH-NOT: "PVC"
-
-/// A static library's device code is requested by the device name.
-// 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-EXTRACT %s
-// CHK-EXTRACT: clang-linker-wrapper{{.*}}"--should-extract=xe-bmg-g21"
+// RUN:   --offload-arch=xe-dg2 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-SYCL %s
+// CHK-SYCL-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-bmg-g21)"
+// CHK-SYCL-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-dg2)"
 
 /// The same holds for any offload language, e.g. OpenMP.
 // RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fopenmp=libomp \
-// RUN:   --offload-targets=spirv64-intel --offload-arch=xe-bmg-g21 \
-// RUN:   --offload-arch=xe_20.1.0 -c %s 2>&1 \
+// RUN:   --offload-targets=spirv64-intel --offload-arch=xe-bmg-g21 -c %s 2>&1 \
 // RUN:   | FileCheck -check-prefixes=CHK-OPENMP %s
-// CHK-OPENMP-NOT: offload, "device-openmp
 // CHK-OPENMP: offload, "device-openmp (spirv64-intel:xe-bmg-g21)"
-// CHK-OPENMP-NOT: offload, "device-openmp
diff --git a/clang/unittests/Basic/OffloadArchTest.cpp b/clang/unittests/Basic/OffloadArchTest.cpp
index ac87d8395eb264..db86e63572e952 100644
--- a/clang/unittests/Basic/OffloadArchTest.cpp
+++ b/clang/unittests/Basic/OffloadArchTest.cpp
@@ -41,7 +41,6 @@ TEST(OffloadArchTest, TargetArchClassification) {
   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());
@@ -66,19 +65,10 @@ TEST(OffloadArchTest, RoundTrip) {
   }
 }
 
-// A numeric name denotes the device, and canonicalizes to the device name.
-TEST(OffloadArchTest, IntelGPUNumericNames) {
-  // The 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("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());
-  // So is the spelling clang accepted before the Intel GPU list existed.
+// Each Intel GPU has one name, so other spellings are unknown.
+TEST(OffloadArchTest, IntelGPUOtherSpellings) {
   EXPECT_TRUE(parse("bmg_g21").isUnknown());
+  EXPECT_TRUE(parse("xe_12.60.7").isUnknown());
 }
 
 TEST(OffloadArchTest, Defaults) {
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index 7ebf4134683a89..0ae088a6954d36 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -49,13 +49,8 @@ LLVM_ABI StringRef getArchName(GPUKind Kind);
 /// \return the numeric name of \p GPUIPVersion, e.g. "xe_35.11.0".
 LLVM_ABI std::string getNumericArchName(uint32_t GPUIPVersion);
 
-/// \return the device \p Name denotes, or GK_NONE for a name this build does
-/// not know.
-///
-/// \p Name may be a human-friendly name ("xe-pvc"), a compatibility name
-/// ("xe-dg2") or a numeric name ("xe_12.60.7"). The revision of a numeric name
-/// is ignored and may be omitted, so "xe_12.60", "xe_12.60.0" and "xe_12.60.7"
-/// all denote the same device.
+/// \return the device \p Name denotes, e.g. "xe-pvc" or the compatibility name
+/// "xe-dg2", or GK_NONE for a name this build does not know.
 LLVM_ABI GPUKind parseArch(StringRef Name);
 
 } // namespace IntelGPU
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index 2f29fa2f282166..1ed3a4876fa5e9 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -15,7 +15,6 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <cassert>
-#include <tuple>
 
 using namespace llvm;
 using namespace IntelGPU;
@@ -31,7 +30,7 @@ using namespace IntelGPU;
 // The reserved bits carry no information.
 static constexpr uint32_t GPUIPMajorShift = 22;
 static constexpr uint32_t GPUIPMinorShift = 14;
-static constexpr uint32_t GPUIPMajorMask = 0x3ff;
+[[maybe_unused]] static constexpr uint32_t GPUIPMajorMask = 0x3ff;
 static constexpr uint32_t GPUIPMinorMask = 0xff;
 static constexpr uint32_t GPUIPRevisionMask = 0x3f;
 
@@ -89,52 +88,12 @@ StringRef llvm::IntelGPU::getArchName(GPUKind Kind) {
   llvm_unreachable("invalid Intel GPU kind");
 }
 
-// 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)
+  return 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)
 #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;
+      .Default(GK_NONE);
 }
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index 924dc11f867cec..9b62c8cd7e91b2 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -88,47 +88,14 @@ TEST(IntelGPUTargetParserTest, ParseArch) {
   // A human-friendly name and a compatibility name.
   EXPECT_EQ(IntelGPU::parseArch("xe-pvc"), IntelGPU::GK_XE_PVC);
   EXPECT_EQ(IntelGPU::parseArch("xe-dg2"), IntelGPU::GK_XE_DG2);
-  // Each device has one name; another product on its GPU IP version is named
-  // by its numeric name instead.
+
+  // Each device has one name, so other spellings name no device.
   EXPECT_EQ(IntelGPU::parseArch("bmg_g21"), IntelGPU::GK_NONE);
   EXPECT_EQ(IntelGPU::parseArch("xe-arl-u"), IntelGPU::GK_NONE);
-  EXPECT_EQ(IntelGPU::parseArch("xe_12.70"), IntelGPU::GK_XE_MTL_U);
-
-  // 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("xe_12.60.7"), 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);
+  EXPECT_EQ(IntelGPU::parseArch("XE-PVC"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch(""), IntelGPU::GK_NONE);
 }
 
 TEST(IntelGPUTargetParserTest, EveryNameParses) {

>From 89b2ea8fde0d1929615add02e1cc95240e189385 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Mon, 19 Oct 2026 17:07:13 -0700
Subject: [PATCH 10/18] [Clang][SYCL] Report xe-pvc from the fake offload-arch
 tool

offload-arch prints a listed GPU by its name, and numeric names are no
longer --offload-arch values.

Assisted-by: Claude Opus 5.5
---
 .../Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor b/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor
index 4e37a368690dff..5fc2c7ce481d68 100755
--- a/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor
+++ b/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor
@@ -1,7 +1,7 @@
 #!/bin/sh
 case "$*" in
-  *--only=intel*) echo "xe_12.60.7" ;;
+  *--only=intel*) echo "xe-pvc" ;;
   *--only=nvptx*) echo "sm_70" ;;
-  *) echo "sm_70"; echo "xe_12.60.7" ;;
+  *) echo "sm_70"; echo "xe-pvc" ;;
 esac
 exit 0

>From 6ead47141225726aaee1d08ad984334f67a454f6 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Mon, 19 Oct 2026 18:23:00 -0700
Subject: [PATCH 11/18] [Clang] Drop the release note for Intel GPU names

bmg_g21 was never in a release, and on its own this change does not yet
make --offload-arch with an Intel GPU do anything users can rely on.

Assisted-by: Claude Opus 5.5
---
 clang/docs/ReleaseNotes.md | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 1aa58f38e9726a..bdb35c448d0d99 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1322,10 +1322,6 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use
 
 ### SYCL Support
 
-- `--offload-arch` accepts the Intel GPU names from
-  `llvm/TargetParser/IntelGPUTargetParser.def`, e.g. `xe-pvc`. `bmg_g21` is no
-  longer accepted; use `xe-bmg-g21`.
-
 #### Improvements
 
 ## Additional Information

>From f64a1510f76455c3d017d9d1a475c552557184d1 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Mon, 19 Oct 2026 18:24:14 -0700
Subject: [PATCH 12/18] [TargetParser] Name the TGL row xe-tgl

Address review: prefer xe-tgl to xe-tgllp, keeping one --offload-arch
value per GPU.

Assisted-by: Claude Opus 5.5
---
 clang/unittests/tools/offload-arch/OffloadArchTest.cpp  | 2 +-
 llvm/include/llvm/TargetParser/IntelGPUTargetParser.def | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/unittests/tools/offload-arch/OffloadArchTest.cpp b/clang/unittests/tools/offload-arch/OffloadArchTest.cpp
index 1c14209a537e57..67b98a96bd3a09 100644
--- a/clang/unittests/tools/offload-arch/OffloadArchTest.cpp
+++ b/clang/unittests/tools/offload-arch/OffloadArchTest.cpp
@@ -324,7 +324,7 @@ TEST(IntelGPUArchName, KnownArchitecturesGetAFriendlyName) {
   EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 60, 7)), "xe-pvc");
   EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(20, 1, 4)), "xe-bmg-g21");
   EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(35, 10, 0)), "xe-nvl-p");
-  EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 0, 0)), "xe-tgllp");
+  EXPECT_EQ(getIntelGPUArchName(gpuIPVersion(12, 0, 0)), "xe-tgl");
 }
 
 // When several devices share a major and a minor version, the first one listed
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
index 76a82fa3879efb..6346a7829ad325 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
@@ -70,7 +70,7 @@ 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-adl-s",    XE_ADL_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_TGLLP,    12, 0,   10, Render)
 
 // Compatibility names, which cover a group of platforms
 #ifndef INTEL_GPU_COMPAT

>From 1207d5e7f5d789fe128e1f0ccf3801cbfcdd765c Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Tue, 20 Oct 2026 17:09:50 -0700
Subject: [PATCH 13/18] Address review: drop stale xe-dg2-* mention, use
 xe-bmg-g21 in JIT test

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
 clang/test/Driver/sycl-offload-jit.cpp                  | 6 +++---
 llvm/include/llvm/TargetParser/IntelGPUTargetParser.def | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/test/Driver/sycl-offload-jit.cpp b/clang/test/Driver/sycl-offload-jit.cpp
index 82733ccd2bc6f2..ab38aad61ca6df 100644
--- a/clang/test/Driver/sycl-offload-jit.cpp
+++ b/clang/test/Driver/sycl-offload-jit.cpp
@@ -88,10 +88,10 @@
 /// With multiple architectures the packaged binary holds an entry per
 /// architecture, and a single fat binary is expected to reach the host.
 // 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:   --offload-targets=spirv64-unknown-unknown --offload-arch=generic --offload-arch=xe-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)
 
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
index aa78e00171b309..3c75b8788a51f6 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
@@ -25,7 +25,7 @@
 //
 //   INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS)
 //     A compatibility name that covers several minor versions, e.g. "xe-dg2",
-//     which covers every xe-dg2-* and xe-acm-* platform. These names have no
+//     which covers every xe-acm-* platform. These names have no
 //     GPU IP version of their own, so no device reports one and the
 //     offload-arch utility never prints one, but they are legal --offload-arch
 //     values. The columns mean the same as above.

>From adfceeaf8553628d4d29e0549972b623c701655b Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Wed, 21 Oct 2026 12:00:49 -0700
Subject: [PATCH 14/18] [TargetParser][Clang] Restore aliases and numeric Intel
 GPU names

Accept an alias for a product that shares a GPU IP version (e.g. xe-arl-u
for xe-mtl-u) and a numeric name (e.g. xe_20.1.0), and canonicalize Intel
GPU names in the driver for every offload language, so that every spelling
of a device builds one device job with one label and -Xarch_ matches any of
them. xe-tgl stays the device name, with xe-tgllp as an alias.
---
 clang/lib/Driver/Driver.cpp                   |  8 +--
 clang/lib/Driver/ToolChain.cpp                |  7 ++-
 clang/test/Driver/sycl-intel-gpu-arch.cpp     | 50 ++++++++++++++++++-
 clang/unittests/Basic/OffloadArchTest.cpp     | 10 +++-
 .../TargetParser/IntelGPUTargetParser.def     | 28 ++++++++++-
 .../llvm/TargetParser/IntelGPUTargetParser.h  |  9 +++-
 .../lib/TargetParser/IntelGPUTargetParser.cpp | 40 +++++++++++++--
 .../TargetParser/IntelGPUTargetParserTest.cpp | 45 +++++++++++++++--
 8 files changed, 177 insertions(+), 20 deletions(-)

diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index b9c9fee18e9c3e..209e7e6b6b53e7 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3898,8 +3898,8 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
   Args.ClaimAllArgs(options::OPT_cl_ignored_Group);
 }
 
-/// Returns the canonical name for the offloading architecture when using a HIP
-/// or CUDA architecture.
+/// Returns the canonical name for the offloading architecture when using a HIP,
+/// CUDA or Intel GPU architecture.
 static StringRef getCanonicalArchString(Compilation &C,
                                         const llvm::opt::DerivedArgList &Args,
                                         StringRef ArchStr,
@@ -3930,7 +3930,9 @@ static StringRef getCanonicalArchString(Compilation &C,
     }
   }
 
-  if (Arch.isNVPTX())
+  // An Intel GPU has several spellings, e.g. "xe-bmg-g21" and "xe_20.1.0". Use
+  // one, so that both build one device job with one label.
+  if (Arch.isNVPTX() || Arch.isIntelGPU())
     return Args.MakeArgStringRef(OffloadArchToString(Arch));
 
   if (Arch.isAMDGPU() || Arch.isAMDGCNSPIRV()) {
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 255a9c9602b540..ae6087a155211c 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -2189,8 +2189,11 @@ llvm::opt::DerivedArgList *ToolChain::TranslateXarchArgs(
       Skip = IsDevice;
     } else if (A->getOption().matches(options::OPT_Xarch__)) {
       StringRef Val = A->getValue();
-      NeedTrans = Val == getArchName() || (BA && Val == BA.ArchName) ||
-                  isXArchCompatibleTripleArch(Triple, Val);
+      // An Intel GPU has several spellings, so -Xarch_ may name it by another.
+      NeedTrans =
+          Val == getArchName() || (BA && Val == BA.ArchName) ||
+          (BA && BA.Arch.isIntelGPU() && StringToOffloadArch(Val) == BA.Arch) ||
+          isXArchCompatibleTripleArch(Triple, Val);
       Skip = !NeedTrans;
     }
     if (NeedTrans || Skip)
diff --git a/clang/test/Driver/sycl-intel-gpu-arch.cpp b/clang/test/Driver/sycl-intel-gpu-arch.cpp
index bd7f018fa854df..dbd2a3973a6838 100644
--- a/clang/test/Driver/sycl-intel-gpu-arch.cpp
+++ b/clang/test/Driver/sycl-intel-gpu-arch.cpp
@@ -8,8 +8,56 @@
 // CHK-SYCL-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-bmg-g21)"
 // CHK-SYCL-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-dg2)"
 
+/// A device name, its numeric name and an alias are one device job, labeled
+/// with the device name.
+// 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=xe_20.1.0 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-ONE-BMG %s
+// CHK-ONE-BMG-NOT: offload, "device-sycl
+// CHK-ONE-BMG: offload, "device-sycl (spirv64-unknown-unknown:xe-bmg-g21)"
+// CHK-ONE-BMG-NOT: offload, "device-sycl
+// 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-ONE-MTL %s
+// CHK-ONE-MTL-NOT: offload, "device-sycl
+// CHK-ONE-MTL: offload, "device-sycl (spirv64-unknown-unknown:xe-mtl-u)"
+// CHK-ONE-MTL-NOT: offload, "device-sycl
+// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe-tgllp -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-TGL %s
+// CHK-TGL: offload, "device-sycl (spirv64-unknown-unknown:xe-tgl)"
+
+/// Removing any spelling 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=xe_20.1.0 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-NO-ARCH %s
+// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe_20.1.0 \
+// RUN:   --offload-arch=generic --no-offload-arch=xe-bmg-g21 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-NO-ARCH %s
+// CHK-NO-ARCH-NOT: bmg
+// CHK-NO-ARCH: offload, "device-sycl (spirv64-unknown-unknown:generic)"
+// CHK-NO-ARCH-NOT: bmg
+
+/// -Xarch_ may name an Intel GPU by any spelling, including the one the user
+/// gave --offload-arch.
+// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fsycl \
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe_20.1.0 \
+// RUN:   -Xarch_xe_20.1.0 -DNUM -Xarch_xe-bmg-g21 -DBMG -Xarch_xe-pvc -DPVC -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-XARCH %s
+// CHK-XARCH: "-fsycl-is-device"
+// CHK-XARCH-SAME: "-D" "NUM"
+// CHK-XARCH-SAME: "-D" "BMG"
+// CHK-XARCH-NOT: "PVC"
+
 /// The same holds for any offload language, e.g. OpenMP.
 // RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fopenmp=libomp \
-// RUN:   --offload-targets=spirv64-intel --offload-arch=xe-bmg-g21 -c %s 2>&1 \
+// RUN:   --offload-targets=spirv64-intel --offload-arch=xe-bmg-g21 \
+// RUN:   --offload-arch=xe_20.1.0 -c %s 2>&1 \
 // RUN:   | FileCheck -check-prefixes=CHK-OPENMP %s
+// CHK-OPENMP-NOT: offload, "device-openmp
 // CHK-OPENMP: offload, "device-openmp (spirv64-intel:xe-bmg-g21)"
+// CHK-OPENMP-NOT: offload, "device-openmp
diff --git a/clang/unittests/Basic/OffloadArchTest.cpp b/clang/unittests/Basic/OffloadArchTest.cpp
index db86e63572e952..859be91a6cf713 100644
--- a/clang/unittests/Basic/OffloadArchTest.cpp
+++ b/clang/unittests/Basic/OffloadArchTest.cpp
@@ -65,10 +65,16 @@ TEST(OffloadArchTest, RoundTrip) {
   }
 }
 
-// Each Intel GPU has one name, so other spellings are unknown.
 TEST(OffloadArchTest, IntelGPUOtherSpellings) {
+  // An alias and a numeric name denote the device and print as its name.
+  EXPECT_EQ(parse("xe-arl-u"), parse("xe-mtl-u"));
+  EXPECT_EQ(parse("xe_12.60.7"), parse("xe-pvc"));
+  EXPECT_STREQ(OffloadArchToString(parse("xe-tgllp")), "xe-tgl");
+  EXPECT_STREQ(OffloadArchToString(parse("xe_20.1.0")), "xe-bmg-g21");
+
+  // An unlisted numeric name and the old bmg_g21 spelling are unknown.
+  EXPECT_TRUE(parse("xe_40.11.0").isUnknown());
   EXPECT_TRUE(parse("bmg_g21").isUnknown());
-  EXPECT_TRUE(parse("xe_12.60.7").isUnknown());
 }
 
 TEST(OffloadArchTest, Defaults) {
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
index 3c75b8788a51f6..2f89195ae83b54 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.def
@@ -25,17 +25,21 @@
 //
 //   INTEL_GPU_COMPAT(NAME, KIND, IGCA_TARGET, IGCA_FEATURE_SETS)
 //     A compatibility name that covers several minor versions, e.g. "xe-dg2",
-//     which covers every xe-acm-* platform. These names have no
+//     which covers every xe-acm-* and xe-dg2-* platform. These names have no
 //     GPU IP version of their own, so no device reports one and the
 //     offload-arch utility never prints one, but they are legal --offload-arch
 //     values. The columns mean the same as above.
 //
+//   INTEL_GPU_ALIAS(NAME, KIND)
+//     Another name for a device that already has a row, e.g. "xe-arl-u" for
+//     "xe-mtl-u", which share a GPU IP version. An alias is never printed.
+//
 // 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.
 //
 // Products that share a major and a minor version have one row, named after
-// the product the offload-arch utility prints.
+// the product the offload-arch utility prints, and the others are aliases.
 //
 // 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
@@ -82,5 +86,25 @@ 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 other names of a device named above
+#ifndef INTEL_GPU_ALIAS
+#define INTEL_GPU_ALIAS(NAME, KIND)
+#endif
+
+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-tgllp",    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 0ae088a6954d36..7d5572b83c0ea1 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -49,8 +49,13 @@ LLVM_ABI StringRef getArchName(GPUKind Kind);
 /// \return the numeric name of \p GPUIPVersion, e.g. "xe_35.11.0".
 LLVM_ABI std::string getNumericArchName(uint32_t GPUIPVersion);
 
-/// \return the device \p Name denotes, e.g. "xe-pvc" or the compatibility name
-/// "xe-dg2", or GK_NONE for a name this build does not know.
+/// \return the device \p Name denotes, or GK_NONE for a name this build does
+/// not know.
+///
+/// \p Name may be a human-friendly name ("xe-pvc"), a compatibility name
+/// ("xe-dg2"), an alias ("xe-arl-u") or a numeric name ("xe_12.60.7"). The
+/// revision of a numeric name is ignored and may be omitted, so "xe_12.60",
+/// "xe_12.60.0" and "xe_12.60.7" all denote the same device.
 LLVM_ABI GPUKind parseArch(StringRef Name);
 
 } // namespace IntelGPU
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index 1ed3a4876fa5e9..343a4b74823bc3 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -15,6 +15,7 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <cassert>
+#include <tuple>
 
 using namespace llvm;
 using namespace IntelGPU;
@@ -30,7 +31,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;
 
@@ -88,12 +89,45 @@ StringRef llvm::IntelGPU::getArchName(GPUKind Kind) {
   llvm_unreachable("invalid Intel GPU kind");
 }
 
+// Read a numeric name, e.g. "xe_12.60.7", into the GPU IP version it spells.
+// The revision may be omitted.
+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('.');
+  // A separator with no revision after it is malformed.
+  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;
+
+  // Each field must fit its GPU IP version field.
+  if (Major > GPUIPMajorMask || Minor > GPUIPMinorMask ||
+      Revision > GPUIPRevisionMask)
+    return false;
+  GPUIPVersion = packDevice(Major, Minor) | Revision;
+  return true;
+}
+
 GPUKind llvm::IntelGPU::parseArch(StringRef Name) {
-  return StringSwitch<GPUKind>(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);
+                     .Default(GK_NONE);
+  if (Kind != GK_NONE)
+    return Kind;
+
+  uint32_t GPUIPVersion;
+  if (parseNumericArchName(Name, GPUIPVersion))
+    return getKindForVersion(GPUIPVersion);
+  return GK_NONE;
 }
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index 9b62c8cd7e91b2..f32dca8770d5bb 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -85,17 +85,43 @@ TEST(IntelGPUTargetParserTest, KindArchNames) {
 }
 
 TEST(IntelGPUTargetParserTest, ParseArch) {
-  // A human-friendly name and a compatibility name.
+  // 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("xe-arl-u"), IntelGPU::GK_XE_MTL_U);
+  EXPECT_EQ(IntelGPU::parseArch("xe-tgllp"), IntelGPU::GK_XE_TGLLP);
 
-  // Each device has one name, so other spellings name no device.
+  // A numeric name names the device with that GPU IP version. 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.0"), IntelGPU::GK_XE_PVC);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60"), IntelGPU::GK_XE_PVC);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.70"), IntelGPU::GK_XE_MTL_U);
+  // 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);
+
+  // Unknown or malformed names name no device.
+  EXPECT_EQ(IntelGPU::parseArch(""), IntelGPU::GK_NONE);
   EXPECT_EQ(IntelGPU::parseArch("bmg_g21"), IntelGPU::GK_NONE);
-  EXPECT_EQ(IntelGPU::parseArch("xe-arl-u"), IntelGPU::GK_NONE);
-  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.7"), IntelGPU::GK_NONE);
   EXPECT_EQ(IntelGPU::parseArch("pvc"), IntelGPU::GK_NONE);
   EXPECT_EQ(IntelGPU::parseArch("XE-PVC"), IntelGPU::GK_NONE);
-  EXPECT_EQ(IntelGPU::parseArch(""), 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);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60."), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12."), IntelGPU::GK_NONE);
+
+  // Each field must fit its GPU IP version field: 10 bits for the major
+  // version, 8 for the minor, 6 for the revision.
+  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);
+  // 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);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.99999999999"), IntelGPU::GK_NONE);
 }
 
 TEST(IntelGPUTargetParserTest, EveryNameParses) {
@@ -105,6 +131,15 @@ TEST(IntelGPUTargetParserTest, EveryNameParses) {
   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) {
+  // A device is always reported by its row's name, not an alias.
+#define INTEL_GPU_ALIAS(NAME, KIND)                                            \
+  EXPECT_NE(IntelGPU::getArchName(IntelGPU::GK_##KIND), NAME) << NAME;
 #include "llvm/TargetParser/IntelGPUTargetParser.def"
 }
 

>From 66d76f3b3ff8b663b1aa048c7e453181e36ddbb6 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Thu, 22 Oct 2026 17:25:59 -0700
Subject: [PATCH 15/18] [Clang] Keep Intel GPU names as given in --offload-arch

Drop the Intel GPU canonicalization in getCanonicalArchString and -Xarch_
matching. An alias or numeric name now builds its own device job with the
name the user gave, so that name reaches ocloc unchanged. Building one
device job per IGCA target is left to a follow-up.
---
 clang/lib/Driver/Driver.cpp               |  8 ++-
 clang/lib/Driver/ToolChain.cpp            |  7 +--
 clang/test/Driver/sycl-intel-gpu-arch.cpp | 59 +++--------------------
 3 files changed, 12 insertions(+), 62 deletions(-)

diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 209e7e6b6b53e7..b9c9fee18e9c3e 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -3898,8 +3898,8 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
   Args.ClaimAllArgs(options::OPT_cl_ignored_Group);
 }
 
-/// Returns the canonical name for the offloading architecture when using a HIP,
-/// CUDA or Intel GPU architecture.
+/// Returns the canonical name for the offloading architecture when using a HIP
+/// or CUDA architecture.
 static StringRef getCanonicalArchString(Compilation &C,
                                         const llvm::opt::DerivedArgList &Args,
                                         StringRef ArchStr,
@@ -3930,9 +3930,7 @@ static StringRef getCanonicalArchString(Compilation &C,
     }
   }
 
-  // An Intel GPU has several spellings, e.g. "xe-bmg-g21" and "xe_20.1.0". Use
-  // one, so that both build one device job with one label.
-  if (Arch.isNVPTX() || Arch.isIntelGPU())
+  if (Arch.isNVPTX())
     return Args.MakeArgStringRef(OffloadArchToString(Arch));
 
   if (Arch.isAMDGPU() || Arch.isAMDGCNSPIRV()) {
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index ae6087a155211c..255a9c9602b540 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -2189,11 +2189,8 @@ llvm::opt::DerivedArgList *ToolChain::TranslateXarchArgs(
       Skip = IsDevice;
     } else if (A->getOption().matches(options::OPT_Xarch__)) {
       StringRef Val = A->getValue();
-      // An Intel GPU has several spellings, so -Xarch_ may name it by another.
-      NeedTrans =
-          Val == getArchName() || (BA && Val == BA.ArchName) ||
-          (BA && BA.Arch.isIntelGPU() && StringToOffloadArch(Val) == BA.Arch) ||
-          isXArchCompatibleTripleArch(Triple, Val);
+      NeedTrans = Val == getArchName() || (BA && Val == BA.ArchName) ||
+                  isXArchCompatibleTripleArch(Triple, Val);
       Skip = !NeedTrans;
     }
     if (NeedTrans || Skip)
diff --git a/clang/test/Driver/sycl-intel-gpu-arch.cpp b/clang/test/Driver/sycl-intel-gpu-arch.cpp
index dbd2a3973a6838..1d79c61b022590 100644
--- a/clang/test/Driver/sycl-intel-gpu-arch.cpp
+++ b/clang/test/Driver/sycl-intel-gpu-arch.cpp
@@ -8,56 +8,11 @@
 // CHK-SYCL-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-bmg-g21)"
 // CHK-SYCL-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-dg2)"
 
-/// A device name, its numeric name and an alias are one device job, labeled
-/// with the device name.
+/// A device name, an alias and a numeric name are kept as given.
 // 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=xe_20.1.0 -c %s 2>&1 \
-// RUN:   | FileCheck -check-prefixes=CHK-ONE-BMG %s
-// CHK-ONE-BMG-NOT: offload, "device-sycl
-// CHK-ONE-BMG: offload, "device-sycl (spirv64-unknown-unknown:xe-bmg-g21)"
-// CHK-ONE-BMG-NOT: offload, "device-sycl
-// 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-ONE-MTL %s
-// CHK-ONE-MTL-NOT: offload, "device-sycl
-// CHK-ONE-MTL: offload, "device-sycl (spirv64-unknown-unknown:xe-mtl-u)"
-// CHK-ONE-MTL-NOT: offload, "device-sycl
-// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
-// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe-tgllp -c %s 2>&1 \
-// RUN:   | FileCheck -check-prefixes=CHK-TGL %s
-// CHK-TGL: offload, "device-sycl (spirv64-unknown-unknown:xe-tgl)"
-
-/// Removing any spelling 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=xe_20.1.0 -c %s 2>&1 \
-// RUN:   | FileCheck -check-prefixes=CHK-NO-ARCH %s
-// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
-// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe_20.1.0 \
-// RUN:   --offload-arch=generic --no-offload-arch=xe-bmg-g21 -c %s 2>&1 \
-// RUN:   | FileCheck -check-prefixes=CHK-NO-ARCH %s
-// CHK-NO-ARCH-NOT: bmg
-// CHK-NO-ARCH: offload, "device-sycl (spirv64-unknown-unknown:generic)"
-// CHK-NO-ARCH-NOT: bmg
-
-/// -Xarch_ may name an Intel GPU by any spelling, including the one the user
-/// gave --offload-arch.
-// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fsycl \
-// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe_20.1.0 \
-// RUN:   -Xarch_xe_20.1.0 -DNUM -Xarch_xe-bmg-g21 -DBMG -Xarch_xe-pvc -DPVC -c %s 2>&1 \
-// RUN:   | FileCheck -check-prefixes=CHK-XARCH %s
-// CHK-XARCH: "-fsycl-is-device"
-// CHK-XARCH-SAME: "-D" "NUM"
-// CHK-XARCH-SAME: "-D" "BMG"
-// CHK-XARCH-NOT: "PVC"
-
-/// The same holds for any offload language, e.g. OpenMP.
-// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fopenmp=libomp \
-// RUN:   --offload-targets=spirv64-intel --offload-arch=xe-bmg-g21 \
-// RUN:   --offload-arch=xe_20.1.0 -c %s 2>&1 \
-// RUN:   | FileCheck -check-prefixes=CHK-OPENMP %s
-// CHK-OPENMP-NOT: offload, "device-openmp
-// CHK-OPENMP: offload, "device-openmp (spirv64-intel:xe-bmg-g21)"
-// CHK-OPENMP-NOT: offload, "device-openmp
+// RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe-mtl-u \
+// RUN:   --offload-arch=xe-arl-u --offload-arch=xe_12.70 -c %s 2>&1 \
+// RUN:   | FileCheck -check-prefixes=CHK-AS-GIVEN %s
+// CHK-AS-GIVEN-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-mtl-u)"
+// CHK-AS-GIVEN-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-arl-u)"
+// CHK-AS-GIVEN-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe_12.70)"

>From 2f789eb61d90ccb7b1d0d0ed543f99abc21a80f5 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Fri, 23 Oct 2026 10:08:25 -0700
Subject: [PATCH 16/18] [TargetParser] Generate Intel GPU numeric names from
 the table

Match "xe_<major>.<minor>" with a StringSwitch case per INTEL_GPU row
instead of parsing the GPU IP version, as suggested in review. An optional
revision is still accepted if it fits its 6-bit field.
---
 .../lib/TargetParser/IntelGPUTargetParser.cpp | 64 +++++--------------
 .../TargetParser/IntelGPUTargetParserTest.cpp | 19 ++++--
 2 files changed, 29 insertions(+), 54 deletions(-)

diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index 343a4b74823bc3..26d3ddc3f7715d 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -15,7 +15,6 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <cassert>
-#include <tuple>
 
 using namespace llvm;
 using namespace IntelGPU;
@@ -31,7 +30,7 @@ using namespace IntelGPU;
 // The reserved bits carry no information.
 static constexpr uint32_t GPUIPMajorShift = 22;
 static constexpr uint32_t GPUIPMinorShift = 14;
-static constexpr uint32_t GPUIPMajorMask = 0x3ff;
+[[maybe_unused]] static constexpr uint32_t GPUIPMajorMask = 0x3ff;
 static constexpr uint32_t GPUIPMinorMask = 0xff;
 static constexpr uint32_t GPUIPRevisionMask = 0x3f;
 
@@ -50,20 +49,13 @@ static constexpr uint32_t packDevice(uint32_t Major, uint32_t Minor) {
   return (Major << GPUIPMajorShift) | (Minor << GPUIPMinorShift);
 }
 
-// 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) {
+StringRef llvm::IntelGPU::getArchName(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 GK_##KIND;
+    return NAME;
 #include "llvm/TargetParser/IntelGPUTargetParser.def"
-  return GK_NONE;
-}
-
-StringRef llvm::IntelGPU::getArchName(uint32_t GPUIPVersion) {
-  return getArchName(getKindForVersion(GPUIPVersion));
+  return "";
 }
 
 std::string llvm::IntelGPU::getNumericArchName(uint32_t GPUIPVersion) {
@@ -89,45 +81,23 @@ StringRef llvm::IntelGPU::getArchName(GPUKind Kind) {
   llvm_unreachable("invalid Intel GPU kind");
 }
 
-// Read a numeric name, e.g. "xe_12.60.7", into the GPU IP version it spells.
-// The revision may be omitted.
-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('.');
-  // A separator with no revision after it is malformed.
-  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;
-
-  // Each field must fit its GPU IP version field.
-  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)
+  // A numeric name may end in a revision, e.g. "xe_12.60.7". Every revision of
+  // a device is the same device, so look the name up without it.
+  if (Name.starts_with("xe_") && Name.count('.') == 2) {
+    auto [Device, RevisionStr] = Name.rsplit('.');
+    unsigned Revision;
+    if (RevisionStr.getAsInteger(10, Revision) || Revision > GPUIPRevisionMask)
+      return GK_NONE;
+    Name = Device;
+  }
+
+  return StringSwitch<GPUKind>(Name)
 #define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS)    \
-  .Case(NAME, GK_##KIND)
+  .Case(NAME, GK_##KIND).Case("xe_" #MAJOR "." #MINOR, 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;
-
-  uint32_t GPUIPVersion;
-  if (parseNumericArchName(Name, GPUIPVersion))
-    return getKindForVersion(GPUIPVersion);
-  return GK_NONE;
+      .Default(GK_NONE);
 }
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index f32dca8770d5bb..0505eb8140ccde 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -111,16 +111,11 @@ TEST(IntelGPUTargetParserTest, ParseArch) {
   EXPECT_EQ(IntelGPU::parseArch("xe_12.60.7.1"), IntelGPU::GK_NONE);
   EXPECT_EQ(IntelGPU::parseArch("xe_12.60."), IntelGPU::GK_NONE);
   EXPECT_EQ(IntelGPU::parseArch("xe_12."), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_012.60"), IntelGPU::GK_NONE);
 
-  // Each field must fit its GPU IP version field: 10 bits for the major
-  // version, 8 for the minor, 6 for the revision.
+  // The revision must fit its 6-bit GPU IP version field.
   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);
-  // 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);
   EXPECT_EQ(IntelGPU::parseArch("xe_12.60.99999999999"), IntelGPU::GK_NONE);
 }
 
@@ -136,6 +131,16 @@ TEST(IntelGPUTargetParserTest, EveryNameParses) {
 #include "llvm/TargetParser/IntelGPUTargetParser.def"
 }
 
+TEST(IntelGPUTargetParserTest, NumericNamesParse) {
+  // The numeric name offload-arch prints for a device names that device.
+#define INTEL_GPU(NAME, KIND, MAJOR, MINOR, IGCA_TARGET, IGCA_FEATURE_SETS)    \
+  EXPECT_EQ(IntelGPU::parseArch(                                               \
+                IntelGPU::getNumericArchName(gpuIPVersion(MAJOR, MINOR, 0))),  \
+            IntelGPU::GK_##KIND)                                               \
+      << NAME;
+#include "llvm/TargetParser/IntelGPUTargetParser.def"
+}
+
 TEST(IntelGPUTargetParserTest, AliasesAreNeverReported) {
   // A device is always reported by its row's name, not an alias.
 #define INTEL_GPU_ALIAS(NAME, KIND)                                            \

>From 745cd02bc174843a9f6f33a72ff04d00568341b4 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Fri, 23 Oct 2026 10:54:10 -0700
Subject: [PATCH 17/18] [TargetParser] Require the revision in Intel GPU
 numeric names

A numeric name is now always xe_<major>.<minor>.<revision>, the form
offload-arch prints. The revision still takes no part in the lookup.
---
 clang/test/Driver/sycl-intel-gpu-arch.cpp                | 4 ++--
 llvm/include/llvm/TargetParser/IntelGPUTargetParser.h    | 6 +++---
 llvm/lib/TargetParser/IntelGPUTargetParser.cpp           | 9 +++++----
 llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp | 8 ++++----
 4 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/clang/test/Driver/sycl-intel-gpu-arch.cpp b/clang/test/Driver/sycl-intel-gpu-arch.cpp
index 1d79c61b022590..555391b379ac0e 100644
--- a/clang/test/Driver/sycl-intel-gpu-arch.cpp
+++ b/clang/test/Driver/sycl-intel-gpu-arch.cpp
@@ -11,8 +11,8 @@
 /// A device name, an alias and a numeric name are kept as given.
 // RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \
 // RUN:   --offload-targets=spirv64-unknown-unknown --offload-arch=xe-mtl-u \
-// RUN:   --offload-arch=xe-arl-u --offload-arch=xe_12.70 -c %s 2>&1 \
+// RUN:   --offload-arch=xe-arl-u --offload-arch=xe_12.70.0 -c %s 2>&1 \
 // RUN:   | FileCheck -check-prefixes=CHK-AS-GIVEN %s
 // CHK-AS-GIVEN-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-mtl-u)"
 // CHK-AS-GIVEN-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-arl-u)"
-// CHK-AS-GIVEN-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe_12.70)"
+// CHK-AS-GIVEN-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe_12.70.0)"
diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index 7d5572b83c0ea1..43986469750296 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -53,9 +53,9 @@ LLVM_ABI std::string getNumericArchName(uint32_t GPUIPVersion);
 /// not know.
 ///
 /// \p Name may be a human-friendly name ("xe-pvc"), a compatibility name
-/// ("xe-dg2"), an alias ("xe-arl-u") or a numeric name ("xe_12.60.7"). The
-/// revision of a numeric name is ignored and may be omitted, so "xe_12.60",
-/// "xe_12.60.0" and "xe_12.60.7" all denote the same device.
+/// ("xe-dg2"), an alias ("xe-arl-u") or a numeric name ("xe_12.60.7"). A
+/// numeric name must give a revision, but the revision is ignored, so
+/// "xe_12.60.0" and "xe_12.60.7" denote the same device.
 LLVM_ABI GPUKind parseArch(StringRef Name);
 
 } // namespace IntelGPU
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index 26d3ddc3f7715d..732a3fc112643e 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -82,12 +82,13 @@ StringRef llvm::IntelGPU::getArchName(GPUKind Kind) {
 }
 
 GPUKind llvm::IntelGPU::parseArch(StringRef Name) {
-  // A numeric name may end in a revision, e.g. "xe_12.60.7". Every revision of
-  // a device is the same device, so look the name up without it.
-  if (Name.starts_with("xe_") && Name.count('.') == 2) {
+  // A numeric name ends in a revision, e.g. "xe_12.60.7". Every revision of a
+  // device is the same device, so look the name up without it.
+  if (Name.starts_with("xe_")) {
     auto [Device, RevisionStr] = Name.rsplit('.');
     unsigned Revision;
-    if (RevisionStr.getAsInteger(10, Revision) || Revision > GPUIPRevisionMask)
+    if (Device.count('.') != 1 || RevisionStr.getAsInteger(10, Revision) ||
+        Revision > GPUIPRevisionMask)
       return GK_NONE;
     Name = Device;
   }
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index 0505eb8140ccde..515a85ff8320f5 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -92,11 +92,11 @@ TEST(IntelGPUTargetParserTest, ParseArch) {
   EXPECT_EQ(IntelGPU::parseArch("xe-tgllp"), IntelGPU::GK_XE_TGLLP);
 
   // A numeric name names the device with that GPU IP version. The revision
-  // takes no part in the lookup and may be left out.
+  // must be given but takes no part in the lookup.
   EXPECT_EQ(IntelGPU::parseArch("xe_12.60.7"), IntelGPU::GK_XE_PVC);
   EXPECT_EQ(IntelGPU::parseArch("xe_12.60.0"), IntelGPU::GK_XE_PVC);
-  EXPECT_EQ(IntelGPU::parseArch("xe_12.60"), IntelGPU::GK_XE_PVC);
-  EXPECT_EQ(IntelGPU::parseArch("xe_12.70"), IntelGPU::GK_XE_MTL_U);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.70.4"), IntelGPU::GK_XE_MTL_U);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60"), IntelGPU::GK_NONE);
   // 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);
 
@@ -111,7 +111,7 @@ TEST(IntelGPUTargetParserTest, ParseArch) {
   EXPECT_EQ(IntelGPU::parseArch("xe_12.60.7.1"), IntelGPU::GK_NONE);
   EXPECT_EQ(IntelGPU::parseArch("xe_12.60."), IntelGPU::GK_NONE);
   EXPECT_EQ(IntelGPU::parseArch("xe_12."), IntelGPU::GK_NONE);
-  EXPECT_EQ(IntelGPU::parseArch("xe_012.60"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_012.60.0"), IntelGPU::GK_NONE);
 
   // The revision must fit its 6-bit GPU IP version field.
   EXPECT_EQ(IntelGPU::parseArch("xe_12.60.63"), IntelGPU::GK_XE_PVC);

>From d360dc387381e13009cff68e4b203fd9fff5f040 Mon Sep 17 00:00:00 2001
From: srividya sundaram <srividya.sundaram at intel.com>
Date: Fri, 23 Oct 2026 11:51:21 -0700
Subject: [PATCH 18/18] [TargetParser] Accept any revision in Intel GPU numeric
 names

Only the major and minor versions of xe_<major>.<minor>.<revision> name a
device, so require the revision to be a number but no longer limit its
value; ocloc validates it.
---
 .../llvm/TargetParser/IntelGPUTargetParser.h       |  4 ++--
 llvm/lib/TargetParser/IntelGPUTargetParser.cpp     | 14 ++++++++------
 .../TargetParser/IntelGPUTargetParserTest.cpp      |  9 +++++----
 3 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
index 43986469750296..0ff01304538c07 100644
--- a/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/IntelGPUTargetParser.h
@@ -54,8 +54,8 @@ LLVM_ABI std::string getNumericArchName(uint32_t GPUIPVersion);
 ///
 /// \p Name may be a human-friendly name ("xe-pvc"), a compatibility name
 /// ("xe-dg2"), an alias ("xe-arl-u") or a numeric name ("xe_12.60.7"). A
-/// numeric name must give a revision, but the revision is ignored, so
-/// "xe_12.60.0" and "xe_12.60.7" denote the same device.
+/// numeric name must end in a revision, which may be any number and is ignored,
+/// so "xe_12.60.0" and "xe_12.60.7" denote the same device.
 LLVM_ABI GPUKind parseArch(StringRef Name);
 
 } // namespace IntelGPU
diff --git a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
index 732a3fc112643e..8513606b1c7ddc 100644
--- a/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/IntelGPUTargetParser.cpp
@@ -11,6 +11,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/TargetParser/IntelGPUTargetParser.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -82,13 +84,13 @@ StringRef llvm::IntelGPU::getArchName(GPUKind Kind) {
 }
 
 GPUKind llvm::IntelGPU::parseArch(StringRef Name) {
-  // A numeric name ends in a revision, e.g. "xe_12.60.7". Every revision of a
-  // device is the same device, so look the name up without it.
+  // A numeric name ends in a revision, e.g. "xe_12.60.7". Only the major and
+  // minor versions name a device, so the revision may be any number and is not
+  // looked up.
   if (Name.starts_with("xe_")) {
-    auto [Device, RevisionStr] = Name.rsplit('.');
-    unsigned Revision;
-    if (Device.count('.') != 1 || RevisionStr.getAsInteger(10, Revision) ||
-        Revision > GPUIPRevisionMask)
+    auto [Device, Revision] = Name.rsplit('.');
+    if (Device.count('.') != 1 || Revision.empty() ||
+        !all_of(Revision, isDigit))
       return GK_NONE;
     Name = Device;
   }
diff --git a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
index 515a85ff8320f5..17131bbe877b53 100644
--- a/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/IntelGPUTargetParserTest.cpp
@@ -112,11 +112,12 @@ TEST(IntelGPUTargetParserTest, ParseArch) {
   EXPECT_EQ(IntelGPU::parseArch("xe_12.60."), IntelGPU::GK_NONE);
   EXPECT_EQ(IntelGPU::parseArch("xe_12."), IntelGPU::GK_NONE);
   EXPECT_EQ(IntelGPU::parseArch("xe_012.60.0"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.abc"), IntelGPU::GK_NONE);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.-1"), IntelGPU::GK_NONE);
 
-  // The revision must fit its 6-bit GPU IP version field.
-  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.60.99999999999"), IntelGPU::GK_NONE);
+  // The revision may be any number.
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.64"), IntelGPU::GK_XE_PVC);
+  EXPECT_EQ(IntelGPU::parseArch("xe_12.60.99999999999"), IntelGPU::GK_XE_PVC);
 }
 
 TEST(IntelGPUTargetParserTest, EveryNameParses) {



More information about the cfe-commits mailing list