https://github.com/srividya-sundaram updated https://github.com/llvm/llvm-project/pull/229938
>From 4e07d042e43ba094a8f05c838631d32c24853e84 Mon Sep 17 00:00:00 2001 From: srividya sundaram <[email protected]> Date: Sat, 12 Sep 2026 19:09:30 -0700 Subject: [PATCH 1/4] [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 e7d62635dc4043e29ac6c50812ed1eba3c8bd02e Mon Sep 17 00:00:00 2001 From: srividya sundaram <[email protected]> Date: Wed, 7 Oct 2026 15:43:46 -0700 Subject: [PATCH 2/4] [Clang][SYCL] Detect Intel GPUs for --offload-arch=native on a SPIR-V target With --offload-targets=spirv64-unknown-unknown, --offload-arch=native was silently ignored: the SYCL toolchain did not implement getSystemGPUArchs, so no GPU was detected and a plain SPIR-V job was built. Run offload-arch --only=intel, as the NVPTX and AMDGPU toolchains run their vendor's tool. Assisted-by: Claude Opus 5.5 --- clang/lib/Driver/ToolChains/SYCL.cpp | 26 +++++++++++++++++++ clang/lib/Driver/ToolChains/SYCL.h | 3 +++ .../offload_arch_sm_70_xe_pvc_by_vendor | 7 +++++ .../Driver/sycl-native-offload-targets.cpp | 16 ++++++++++++ 4 files changed, 52 insertions(+) create mode 100755 clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor create mode 100644 clang/test/Driver/sycl-native-offload-targets.cpp diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp b/clang/lib/Driver/ToolChains/SYCL.cpp index b0c07dd1be2ee..c8aa84ed8fc10 100644 --- a/clang/lib/Driver/ToolChains/SYCL.cpp +++ b/clang/lib/Driver/ToolChains/SYCL.cpp @@ -211,3 +211,29 @@ VersionTuple SYCLToolChain::computeMSVCVersion(const Driver *D, const ArgList &Args) const { return HostTC.computeMSVCVersion(D, Args); } + +Expected<SmallVector<std::string>> +SYCLToolChain::getSystemGPUArchs(const ArgList &Args) const { + // Detect the Intel GPUs on the system, the only GPUs a SPIR-V SYCL target + // runs on. + std::string Program; + if (Arg *A = Args.getLastArg(options::OPT_offload_arch_tool_EQ)) + Program = A->getValue(); + else + Program = GetProgramPath("offload-arch"); + + auto StdoutOrErr = getDriver().executeProgram({Program, "--only=intel"}); + if (!StdoutOrErr) + return StdoutOrErr.takeError(); + + SmallVector<std::string, 1> GPUArchs; + for (StringRef Arch : llvm::split((*StdoutOrErr)->getBuffer(), "\n")) + if (!Arch.empty()) + GPUArchs.push_back(Arch.str()); + + if (GPUArchs.empty()) + return llvm::createStringError(std::error_code(), + "No Intel GPU detected in the system"); + + return std::move(GPUArchs); +} diff --git a/clang/lib/Driver/ToolChains/SYCL.h b/clang/lib/Driver/ToolChains/SYCL.h index 969199ef24949..a4c3f890837fe 100644 --- a/clang/lib/Driver/ToolChains/SYCL.h +++ b/clang/lib/Driver/ToolChains/SYCL.h @@ -34,6 +34,9 @@ class LLVM_LIBRARY_VISIBILITY SYCLToolChain : public ToolChain { llvm::opt::ArgStringList &CC1Args, BoundArch BA, Action::OffloadKind DeviceOffloadKind) const override; + Expected<SmallVector<std::string>> + getSystemGPUArchs(const llvm::opt::ArgList &Args) const override; + bool useIntegratedAs() const override { return true; } bool isPICDefault() const override { return false; } llvm::codegenoptions::DebugInfoFormat getDefaultDebugFormat() const override { diff --git a/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor b/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor new file mode 100755 index 0000000000000..4e37a368690df --- /dev/null +++ b/clang/test/Driver/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor @@ -0,0 +1,7 @@ +#!/bin/sh +case "$*" in + *--only=intel*) echo "xe_12.60.7" ;; + *--only=nvptx*) echo "sm_70" ;; + *) echo "sm_70"; echo "xe_12.60.7" ;; +esac +exit 0 diff --git a/clang/test/Driver/sycl-native-offload-targets.cpp b/clang/test/Driver/sycl-native-offload-targets.cpp new file mode 100644 index 0000000000000..c3bf0a75cf18a --- /dev/null +++ b/clang/test/Driver/sycl-native-offload-targets.cpp @@ -0,0 +1,16 @@ +// Needs chmod +// UNSUPPORTED: system-windows +// XFAIL: target={{.*}}-zos{{.*}} + +// RUN: mkdir -p %t +// RUN: cp %S/Inputs/offload-arch/offload_arch_sm_70_xe_pvc_by_vendor %t/ +// RUN: chmod +x %t/offload_arch_sm_70_xe_pvc_by_vendor + +/// With a SPIR-V target, --offload-arch=native picks the system's Intel GPUs. +// RUN: %clang -ccc-print-phases --target=x86_64-unknown-linux-gnu -fsycl \ +// RUN: --offload-targets=spirv64-unknown-unknown --offload-arch=native \ +// RUN: --offload-arch-tool=%t/offload_arch_sm_70_xe_pvc_by_vendor -c %s 2>&1 \ +// RUN: | FileCheck %s +// CHECK-NOT: sm_70 +// CHECK: offload, "device-sycl (spirv64-unknown-unknown:xe-pvc)" +// CHECK-NOT: sm_70 >From c206cbf625c43d1aaeeef86eec221790838019bb Mon Sep 17 00:00:00 2001 From: srividya sundaram <[email protected]> Date: Mon, 12 Oct 2026 15:10:29 -0700 Subject: [PATCH 3/4] [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 4/4] [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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
