https://github.com/srividya-sundaram updated https://github.com/llvm/llvm-project/pull/229248
>From 4e07d042e43ba094a8f05c838631d32c24853e84 Mon Sep 17 00:00:00 2001 From: srividya sundaram <[email protected]> Date: Sat, 12 Sep 2026 19:09:30 -0700 Subject: [PATCH 1/6] [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 a0e5e72db220b..95a17952d4a2a 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 f8c811f552f38..adc94b35ce025 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 b6d2bfd320103..45ba8322cf5fc 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 57beab92011a9..bbcf88d823776 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 255a9c9602b54..d7928cd10ea24 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 2325e7cc552ef..7071ee6b3bebe 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 82733ccd2bc6f..cb4283db51d25 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 20dbd8dc6aa3b..b2a38b6b26825 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 0ba81f61005a4..0376e7bf88338 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 60ab7dc4e2a7d..01463c15aa0db 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 013ed75fbd57e..980a88aba6114 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 bd3b0a4634e06..c8faba2a11e1b 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 4ae635e97167b..8713de9579b48 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 4b4d162ffbfff..044daddc868f1 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 ab8d456769d0d..147a3e3b4485c 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 3f32c4853a511540a559f42d3556ffef24b73f8d Mon Sep 17 00:00:00 2001 From: srividya sundaram <[email protected]> Date: Mon, 5 Oct 2026 14:29:55 -0700 Subject: [PATCH 2/6] [Clang][SYCL] Infer the SPIR-V target from an Intel offload architecture Select the SPIR-V target from an Intel offload architecture in --offload-arch. Previously, clang++ -fsycl --offload-arch=xe-pvc failed with "invalid or unsupported offload target" unless --offload-targets was also given, although the name alone says which target to use. For CUDA and HIP, an NVIDIA or AMD name already selects the target. For SYCL, an Intel GPU name, an Intel CPU name (graniterapids) or generic now selects SPIR-V of the host's width, the same target SYCL uses when no architecture is given. generic previously had no inferred target. With -foffload-via-llvm the target gets the LLVM environment, as it does for CUDA and HIP. OpenMP, CUDA and HIP still reject an Intel name. Assisted-by: Claude Opus 5.5 --- clang/lib/Driver/Driver.cpp | 20 +++++++++++-- clang/test/Driver/sycl-offload-jit.cpp | 40 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index bbcf88d823776..8d4eb664c4808 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -967,21 +967,29 @@ using TripleSet = std::multiset<llvm::Triple>; // requested offloading kind and architectures. static TripleSet inferOffloadToolchains(Compilation &C, Action::OffloadKind Kind) { + // A SYCL Intel GPU has several accepted spellings, so key it by its canonical + // name, or --no-offload-arch with another spelling would not remove it. + auto canonicalize = [Kind](StringRef Arch) { + OffloadArch ID = StringToOffloadArch(Arch); + return Kind == Action::OFK_SYCL && ID.isIntelGPU() + ? std::string(OffloadArchToString(ID)) + : Arch.str(); + }; std::set<std::string> Archs; for (Arg *A : C.getInputArgs()) { for (StringRef Arch : A->getValues()) { if (A->getOption().matches(options::OPT_offload_arch_EQ)) { if (Arch == "native") { for (StringRef Str : getSystemOffloadArchs(C, Kind)) - Archs.insert(Str.str()); + Archs.insert(canonicalize(Str)); } else { - Archs.insert(Arch.str()); + Archs.insert(canonicalize(Arch)); } } else if (A->getOption().matches(options::OPT_no_offload_arch_EQ)) { if (Arch == "all") Archs.clear(); else - Archs.erase(Arch.str()); + Archs.erase(canonicalize(Arch)); } } } @@ -1023,6 +1031,12 @@ static TripleSet inferOffloadToolchains(Compilation &C, llvm::Triple Triple = OffloadArchToTriple(C.getDefaultToolChain().getTriple(), ID); + // A SYCL Intel device, or a generic one, is SPIR-V of the host's width, the + // same target SYCL picks when no architecture is given. + if (Kind == Action::OFK_SYCL && (ID.isIntel() || ID.isGeneric())) + Triple = llvm::Triple(C.getDefaultToolChain().getTriple().isArch64Bit() + ? llvm::Triple::spirv64 + : llvm::Triple::spirv32); if (UsesLLVMOffloading) Triple.setEnvironment(llvm::Triple::LLVM); diff --git a/clang/test/Driver/sycl-offload-jit.cpp b/clang/test/Driver/sycl-offload-jit.cpp index cb4283db51d25..81adeb0941240 100644 --- a/clang/test/Driver/sycl-offload-jit.cpp +++ b/clang/test/Driver/sycl-offload-jit.cpp @@ -134,6 +134,46 @@ // RUN: | FileCheck -check-prefixes=CHK-INTEL-SUFFIX %s // CHK-INTEL-SUFFIX: offload, "device-sycl (spirv64-unknown-unknown:xe-pvc:foo)" +/// Without --offload-targets, an Intel device or "generic" picks SPIR-V of the +/// host's width, and removing any spelling still removes the device. +// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \ +// RUN: --offload-arch=xe-pvc --offload-arch=bmg_g21 --offload-arch=graniterapids \ +// RUN: --offload-arch=generic -c %s 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHK-INTEL-NO-TARGETS %s +// CHK-INTEL-NO-TARGETS-DAG: offload, "device-sycl (spirv64-unknown-unknown:generic)" +// CHK-INTEL-NO-TARGETS-DAG: offload, "device-sycl (spirv64-unknown-unknown:graniterapids)" +// CHK-INTEL-NO-TARGETS-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-bmg-g21)" +// CHK-INTEL-NO-TARGETS-DAG: offload, "device-sycl (spirv64-unknown-unknown:xe-pvc)" +// RUN: %clang -ccc-print-phases --target=i386-unknown-linux-gnu -fsycl \ +// RUN: --offload-arch=xe-pvc -c %s 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHK-INTEL-NO-TARGETS-32 %s +// CHK-INTEL-NO-TARGETS-32: offload, "device-sycl (spirv32-unknown-unknown:xe-pvc)" +// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \ +// RUN: --offload-arch=xe-pvc --offload-arch=xe_12.60.7 -c %s 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHK-INTEL-NO-TARGETS-ONE %s +// CHK-INTEL-NO-TARGETS-ONE-NOT: offload, "device-sycl +// CHK-INTEL-NO-TARGETS-ONE: offload, "device-sycl (spirv64-unknown-unknown:xe-pvc)" +// CHK-INTEL-NO-TARGETS-ONE-NOT: offload, "device-sycl +// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \ +// RUN: --offload-arch=bmg_g21 --offload-arch=xe-pvc --no-offload-arch=xe-bmg-g21 \ +// RUN: -c %s 2>&1 | FileCheck -check-prefixes=CHK-INTEL-NO-TARGETS-NO-ARCH %s +// CHK-INTEL-NO-TARGETS-NO-ARCH-NOT: bmg +// CHK-INTEL-NO-TARGETS-NO-ARCH: offload, "device-sycl (spirv64-unknown-unknown:xe-pvc)" +// CHK-INTEL-NO-TARGETS-NO-ARCH-NOT: bmg + +/// As for CUDA and HIP, -foffload-via-llvm gives the inferred target the LLVM +/// environment. +// RUN: %clang -### --target=x86_64-unknown-linux-gnu -fsycl -foffload-via-llvm \ +// RUN: --offload-arch=xe-pvc -c %s 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHK-INTEL-VIA-LLVM %s +// CHK-INTEL-VIA-LLVM: "-cc1" "-triple" "spirv64-unknown-unknown-llvm" + +/// Only SYCL infers a target from an Intel name. +// RUN: not %clang -### --target=x86_64-unknown-linux-gnu -x cuda -nocudainc \ +// RUN: -nocudalib --offload-arch=xe-pvc -c %s 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHK-INTEL-CUDA %s +// CHK-INTEL-CUDA: error: unsupported CUDA gpu architecture: xe-pvc + /// -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 \ >From 578085074dc4e1c97a5006d8a1b124247c47e41e Mon Sep 17 00:00:00 2001 From: srividya sundaram <[email protected]> Date: Wed, 7 Oct 2026 15:33:55 -0700 Subject: [PATCH 3/6] [Clang][SYCL] Skip NVIDIA GPUs in --offload-arch=native for SYCL SYCL cannot target NVIDIA GPUs, so on a machine with both an Intel and an NVIDIA GPU, -fsycl --offload-arch=native created an NVIDIA device job that hit an assertion. Skip NVIDIA GPUs for SYCL, add a release note for the inferred SPIR-V target, and shorten a comment. Assisted-by: Claude Opus 5.5 --- clang/docs/ReleaseNotes.md | 4 ++++ clang/lib/Driver/Driver.cpp | 8 +++++--- .../Inputs/offload-arch/offload_arch_sm_70_xe_pvc | 4 ++++ clang/test/Driver/sycl-system-arch.cpp | 15 +++++++++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) create mode 100755 clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc create mode 100644 clang/test/Driver/sycl-system-arch.cpp diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 4465396bd6042..74c043f471124 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -1173,6 +1173,10 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use #### Improvements +- `-fsycl --offload-arch=<Intel GPU>`, e.g. `xe-pvc`, now selects the SPIR-V + target without `--offload-targets`. `--offload-arch=native` skips NVIDIA GPUs, + which SYCL cannot target. + ## Additional Information A wide variety of additional information is available on the [Clang web diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 8d4eb664c4808..daf028f801c62 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -953,7 +953,9 @@ getSystemOffloadArchs(Compilation &C, Action::OffloadKind Kind) { } for (StringRef Arch : llvm::split((*StdoutOrErr)->getBuffer(), "\n")) - if (!Arch.empty()) + // SYCL cannot target NVIDIA GPUs, so skip them. + if (!Arch.empty() && + !(Kind == Action::OFK_SYCL && StringToOffloadArch(Arch).isNVPTX())) GPUArchs.push_back(Arch.str()); } else { C.getDriver().Diag(diag::err_drv_command_failure) << "offload-arch"; @@ -1031,8 +1033,8 @@ static TripleSet inferOffloadToolchains(Compilation &C, llvm::Triple Triple = OffloadArchToTriple(C.getDefaultToolChain().getTriple(), ID); - // A SYCL Intel device, or a generic one, is SPIR-V of the host's width, the - // same target SYCL picks when no architecture is given. + // For SYCL, an Intel name or "generic" means SPIR-V of the host's width, + // the target SYCL already uses when no architecture is given. if (Kind == Action::OFK_SYCL && (ID.isIntel() || ID.isGeneric())) Triple = llvm::Triple(C.getDefaultToolChain().getTriple().isArch64Bit() ? llvm::Triple::spirv64 diff --git a/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc b/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc new file mode 100755 index 0000000000000..299c133dbb552 --- /dev/null +++ b/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc @@ -0,0 +1,4 @@ +#!/bin/sh +echo "sm_70" +echo "xe_12.60.7" +exit 0 diff --git a/clang/test/Driver/sycl-system-arch.cpp b/clang/test/Driver/sycl-system-arch.cpp new file mode 100644 index 0000000000000..b67acebbccf29 --- /dev/null +++ b/clang/test/Driver/sycl-system-arch.cpp @@ -0,0 +1,15 @@ +// Needs chmod +// UNSUPPORTED: system-windows +// XFAIL: target={{.*}}-zos{{.*}} + +// RUN: mkdir -p %t +// RUN: cp %S/Inputs/offload-arch/offload_arch_sm_70_xe_pvc %t/ +// RUN: chmod +x %t/offload_arch_sm_70_xe_pvc + +/// SYCL cannot target NVIDIA GPUs, so --offload-arch=native skips them. +// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \ +// RUN: --offload-arch=native --offload-arch-tool=%t/offload_arch_sm_70_xe_pvc \ +// RUN: -c %s 2>&1 | 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 <[email protected]> Date: Mon, 12 Oct 2026 15:10:29 -0700 Subject: [PATCH 4/6] [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 c8faba2a11e1b..566045539c723 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 8713de9579b48..1131ccf52e64c 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 <[email protected]> Date: Mon, 12 Oct 2026 18:07:42 -0700 Subject: [PATCH 5/6] [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 b4f5e751b03bb..c527afa8b41f6 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 4465396bd6042..8fbca2642b617 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 45ba8322cf5fc..b6d2bfd320103 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 bbcf88d823776..d48d5339f1934 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 d7928cd10ea24..ae6087a155211 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 7071ee6b3bebe..7258a86f84282 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 0000000000000..9818d18647ce8 --- /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 cb4283db51d25..82733ccd2bc6f 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 b2a38b6b26825..20dbd8dc6aa3b 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 0376e7bf88338..23c17f3bb07f4 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 01463c15aa0db..50f1d55450cfd 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 980a88aba6114..ac87d8395eb26 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 566045539c723..d722d999444b6 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 1131ccf52e64c..940a6b36f6c84 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 044daddc868f1..2f29fa2f28216 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 147a3e3b4485c..e04e93dc69e7a 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 3c04b745b46cb8663e29a975891b053947bd0939 Mon Sep 17 00:00:00 2001 From: srividya sundaram <[email protected]> Date: Mon, 12 Oct 2026 18:09:07 -0700 Subject: [PATCH 6/6] [Clang] Canonicalize an inferred Intel GPU name for every offload language Follow the base change: key an Intel GPU by its canonical name whatever the offload kind, not only for SYCL. Assisted-by: Claude Opus 5.5 --- clang/lib/Driver/Driver.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 426b34e2b3ce9..42561acc10679 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -969,13 +969,11 @@ using TripleSet = std::multiset<llvm::Triple>; // requested offloading kind and architectures. static TripleSet inferOffloadToolchains(Compilation &C, Action::OffloadKind Kind) { - // A SYCL Intel GPU has several accepted spellings, so key it by its canonical - // name, or --no-offload-arch with another spelling would not remove it. - auto canonicalize = [Kind](StringRef Arch) { + // An Intel GPU has several spellings, so key it by its canonical name, or + // --no-offload-arch with another spelling would not remove it. + auto canonicalize = [](StringRef Arch) { OffloadArch ID = StringToOffloadArch(Arch); - return Kind == Action::OFK_SYCL && ID.isIntelGPU() - ? std::string(OffloadArchToString(ID)) - : Arch.str(); + return ID.isIntelGPU() ? std::string(OffloadArchToString(ID)) : Arch.str(); }; std::set<std::string> Archs; for (Arg *A : C.getInputArgs()) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
