https://github.com/srividya-sundaram updated https://github.com/llvm/llvm-project/pull/229247
>From 455ea8cf2e9a4409d61e9218424e63e470c30299 Mon Sep 17 00:00:00 2001 From: srividya sundaram <[email protected]> Date: Sat, 12 Sep 2026 19:09:30 -0700 Subject: [PATCH] [TargetParser][Clang] Parse Intel GPU architecture names Add support for Intel GPU names in --offload-arch. #222072 added the mapping from a detected GPU version to its name. This PR adds the reverse mapping needed when a user specifies a name through --offload-arch. Previously, clang recognized only the hardcoded name bmg_g21. The shared Intel GPU list now recognizes names such as xe-pvc, xe-dg2, bmg_g21, and xe_12.60.7. Names for the same GPU IP are treated as aliases, so SYCL creates one device job and matches all spellings consistently. clang-sycl-linker uses the canonical name for the image and the name expected by ocloc. User-visible changes: - A device is printed and labelled by its canonical name, e.g. bmg_g21 becomes xe-bmg-g21. - Every listed Intel GPU name is compiled ahead of time with ocloc, as bmg_g21 already was. - Products on the same GPU IP become aliases, so 13 GPUKind enumerators are removed, e.g. GK_XE_ARL_U is now GK_XE_MTL_U. - An Intel name is no longer accepted as an NVPTX CPU; -target-cpu bmg_g21 was accepted before. - An Intel name with a target-ID suffix, e.g. xe-pvc:foo, is not turned into a device. Canonicalization is limited to SYCL, the only offload kind that compiles for Intel GPUs today. Selecting the SPIR-V target from an Intel GPU name and diagnosing unknown names are handled separately in follow-up PRs. Assisted-by: Claude Opus 5.5 --- clang/include/clang/Basic/OffloadArch.h | 26 ++-- clang/lib/Basic/OffloadArch.cpp | 12 +- clang/lib/Basic/Targets/NVPTX.h | 11 +- clang/lib/Driver/Driver.cpp | 22 ++- clang/lib/Driver/ToolChain.cpp | 4 + clang/lib/Driver/ToolChains/Clang.cpp | 8 +- clang/test/Driver/sycl-offload-jit.cpp | 61 ++++++++- .../test/Misc/target-invalid-cpu-note/nvptx.c | 4 + .../OffloadTools/clang-sycl-linker/basic.ll | 13 +- .../clang-sycl-linker/ClangSYCLLinker.cpp | 15 ++- clang/unittests/Basic/OffloadArchTest.cpp | 23 +++- .../TargetParser/IntelGPUTargetParser.def | 58 +++++--- .../llvm/TargetParser/IntelGPUTargetParser.h | 41 +++++- .../lib/TargetParser/IntelGPUTargetParser.cpp | 127 +++++++++++++++++- .../TargetParser/IntelGPUTargetParserTest.cpp | 104 ++++++++++++++ 15 files changed, 472 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..c6a8525b050c6 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 also knows names that are never an NVPTX CPU, such as + // the Intel ones, so list the kinds accepted here; Sema would otherwise read + // another vendor's kind as an NVPTX one. 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 7649941a68b1c..ead0e3d19b3d9 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 f0d04bf4b6a83..3950f69f53f7e 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -9862,8 +9862,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 1813e9d346667..7c5c3ab65eeb1 100644 --- a/clang/test/Driver/sycl-offload-jit.cpp +++ b/clang/test/Driver/sycl-offload-jit.cpp @@ -77,11 +77,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..3b909c97c26a2 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,26 @@ 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 + +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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
