https://github.com/ronlieb created https://github.com/llvm/llvm-project/pull/223983
Reverts llvm/llvm-project#213847 Matt please look into our downstream failure : https://amd-hub.atlassian.net/browse/LCOMPILER-2800 >From 7ef918096a8b2251ff778b4392ef5792b7386ae2 Mon Sep 17 00:00:00 2001 From: theRonShark <[email protected]> Date: Wed, 16 Sep 2026 07:24:23 -0400 Subject: [PATCH] =?UTF-8?q?Revert=20"Reapply=20"clang:=20Use=20TargetID=20?= =?UTF-8?q?parsing=20from=20AMDGPUTargetParser"=20(#21382=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 492410de73b0b99b1778bfcd746bc8735bdaba58. --- clang/include/clang/Basic/TargetID.h | 44 +++- clang/lib/Basic/TargetID.cpp | 197 +++++++++++++++--- clang/lib/Basic/Targets/AMDGPU.cpp | 43 ++-- clang/lib/Basic/Targets/AMDGPU.h | 34 ++- clang/lib/Driver/Driver.cpp | 31 +-- clang/lib/Driver/OffloadBundler.cpp | 83 ++++---- clang/lib/Driver/ToolChains/AMDGPU.cpp | 119 +++++------ clang/lib/Driver/ToolChains/AMDGPU.h | 23 +- .../clang-offload-bundler/basic.c | 11 - .../ClangOffloadBundler.cpp | 23 +- .../llvm/TargetParser/AMDGPUTargetParser.h | 2 +- llvm/lib/TargetParser/AMDGPUTargetParser.cpp | 9 +- .../TargetParser/TargetParserTest.cpp | 14 +- 13 files changed, 379 insertions(+), 254 deletions(-) diff --git a/clang/include/clang/Basic/TargetID.h b/clang/include/clang/Basic/TargetID.h index 8871b76859fd71..902151d76556de 100644 --- a/clang/include/clang/Basic/TargetID.h +++ b/clang/include/clang/Basic/TargetID.h @@ -9,29 +9,53 @@ #ifndef LLVM_CLANG_BASIC_TARGETID_H #define LLVM_CLANG_BASIC_TARGETID_H -#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" #include "llvm/TargetParser/Triple.h" #include <optional> -#include <string> -#include <utility> +#include <set> namespace clang { +/// Get all feature strings that can be used in target ID for \p Processor. +/// Target ID is a processor name with optional feature strings +/// postfixed by a plus or minus sign delimited by colons, e.g. +/// gfx908:xnack+:sramecc-. Each processor have a limited +/// number of predefined features when showing up in a target ID. +llvm::SmallVector<llvm::StringRef, 4> +getAllPossibleTargetIDFeatures(const llvm::Triple &T, + llvm::StringRef Processor); + /// Get processor name from target ID. /// Returns canonical processor name or empty if the processor name is invalid. llvm::StringRef getProcessorFromTargetID(const llvm::Triple &T, llvm::StringRef OffloadArch); -/// A device triple paired with a target ID (processor and feature modifiers) -/// for that triple, e.g. {amdgcn-amd-amdhsa, "gfx906:xnack+"}. -using TargetIDEntry = std::pair<const llvm::Triple &, llvm::StringRef>; +/// Parse a target ID to get processor and feature map. +/// Returns canonicalized processor name or std::nullopt if the target ID is +/// invalid. Returns target ID features in \p FeatureMap if it is not null +/// pointer. This function assumes \p OffloadArch is a valid target ID. +/// If the target ID contains feature+, map it to true. +/// If the target ID contains feature-, map it to false. +/// If the target ID does not contain a feature (default), do not map it. +std::optional<llvm::StringRef> parseTargetID(const llvm::Triple &T, + llvm::StringRef OffloadArch, + llvm::StringMap<bool> *FeatureMap); + +/// Returns canonical target ID, assuming \p Processor is canonical and all +/// entries in \p Features are valid. +std::string getCanonicalTargetID(llvm::StringRef Processor, + const llvm::StringMap<bool> &Features); /// Get the conflicted pair of target IDs for a compilation or a bundled code -/// object. Two entries conflict when they resolve to the same processor but -/// disagree on whether a feature (xnack/sramecc) is explicitly specified. If -/// there is no conflict, returns std::nullopt. +/// object, assuming \p TargetIDs are canonicalized. If there is no conflicts, +/// returns std::nullopt. std::optional<std::pair<llvm::StringRef, llvm::StringRef>> -getConflictTargetIDCombination(llvm::ArrayRef<TargetIDEntry> Entries); +getConflictTargetIDCombination(const std::set<llvm::StringRef> &TargetIDs); + +/// Check whether the provided target ID is compatible with the requested +/// target ID. +bool isCompatibleTargetID(llvm::StringRef Provided, llvm::StringRef Requested); /// Sanitize a target ID string for use in a file name. /// Replaces invalid characters (like ':') with safe characters (like '@'). diff --git a/clang/lib/Basic/TargetID.cpp b/clang/lib/Basic/TargetID.cpp index e0e704e71dd042..cc3c302bd9e762 100644 --- a/clang/lib/Basic/TargetID.cpp +++ b/clang/lib/Basic/TargetID.cpp @@ -8,63 +8,200 @@ #include "clang/Basic/TargetID.h" #include "clang/Basic/OffloadArch.h" -#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/Path.h" #include "llvm/TargetParser/AMDGPUTargetParser.h" +#include "llvm/TargetParser/Triple.h" +#include <map> +#include <optional> +#include <string> namespace clang { -llvm::StringRef getProcessorFromTargetID(const llvm::Triple &T, - llvm::StringRef ArchName) { - auto Split = ArchName.split(':'); +static llvm::SmallVector<llvm::StringRef, 4> +getAllPossibleAMDGPUTargetIDFeatures(const llvm::Triple &T, + llvm::StringRef Proc) { + // Entries in returned vector should be in alphabetical order. + llvm::SmallVector<llvm::StringRef, 4> Ret; + if (!T.isAMDGCN()) + return Ret; + llvm::AMDGPU::GPUKind ProcKind = llvm::AMDGPU::parseArchAMDGCN(Proc); + if (ProcKind == llvm::AMDGPU::GK_NONE) + return Ret; + const llvm::AMDGPU::AMDGPUFeatureBitset &Features = + llvm::AMDGPU::getFeatureBitset(ProcKind); + if (Features.test(llvm::AMDGPU::FEAT_SRAMECC_SUPPORT)) + Ret.push_back("sramecc"); + // Only allow xnack in target ID if the processor supports on/off modes. + if (Features.test(llvm::AMDGPU::FEAT_XNACK_ON_OFF_MODES)) + Ret.push_back("xnack"); + return Ret; +} + +llvm::SmallVector<llvm::StringRef, 4> +getAllPossibleTargetIDFeatures(const llvm::Triple &T, + llvm::StringRef Processor) { + llvm::SmallVector<llvm::StringRef, 4> Ret; + if (T.isAMDGPU()) + return getAllPossibleAMDGPUTargetIDFeatures(T, Processor); + return Ret; +} + +/// Returns canonical processor name or empty string if \p Processor is invalid. +static llvm::StringRef getCanonicalProcessorName(const llvm::Triple &T, + llvm::StringRef Processor) { if (!T.isAMDGPU()) - return Split.first; + return Processor; - if (llvm::StringRef Name = llvm::AMDGPU::getCanonicalArchName(T, Split.first); + if (llvm::StringRef Name = llvm::AMDGPU::getCanonicalArchName(T, Processor); !Name.empty()) return Name; // Accept the AMDGPU subarch triple spelling (e.g. "amdgpu9.00") as an alias // for the corresponding gfx processor. OffloadArch Arch = - getSubArchOffloadArch(llvm::Triple::parseSubArch(Split.first)); + getSubArchOffloadArch(llvm::Triple::parseSubArch(Processor)); if (Arch.isUnknown()) return {}; return OffloadArchToString(Arch); } +llvm::StringRef getProcessorFromTargetID(const llvm::Triple &T, + llvm::StringRef TargetID) { + auto Split = TargetID.split(':'); + return getCanonicalProcessorName(T, Split.first); +} + +// Parse a target ID with format checking only. Do not check whether processor +// name or features are valid for the processor. +// +// A target ID is a processor name followed by a list of target features +// delimited by colon. Each target feature is a string post-fixed by a plus +// or minus sign, e.g. gfx908:sramecc+:xnack-. +static std::optional<llvm::StringRef> +parseTargetIDWithFormatCheckingOnly(llvm::StringRef TargetID, + llvm::StringMap<bool> *FeatureMap) { + llvm::StringRef Processor; + + if (TargetID.empty()) + return llvm::StringRef(); + + auto Split = TargetID.split(':'); + Processor = Split.first; + if (Processor.empty()) + return std::nullopt; + + auto Features = Split.second; + if (Features.empty()) + return Processor; + + llvm::StringMap<bool> LocalFeatureMap; + if (!FeatureMap) + FeatureMap = &LocalFeatureMap; + + while (!Features.empty()) { + auto Splits = Features.split(':'); + if (Splits.first.empty()) + return std::nullopt; + auto Sign = Splits.first.back(); + auto Feature = Splits.first.drop_back(); + if (Sign != '+' && Sign != '-') + return std::nullopt; + bool IsOn = Sign == '+'; + // Each feature can only show up at most once in target ID. + if (!FeatureMap->try_emplace(Feature, IsOn).second) + return std::nullopt; + Features = Splits.second; + } + return Processor; +} + +std::optional<llvm::StringRef> +parseTargetID(const llvm::Triple &T, llvm::StringRef TargetID, + llvm::StringMap<bool> *FeatureMap) { + auto OptionalProcessor = + parseTargetIDWithFormatCheckingOnly(TargetID, FeatureMap); + + if (!OptionalProcessor) + return std::nullopt; + + llvm::StringRef Processor = getCanonicalProcessorName(T, *OptionalProcessor); + if (Processor.empty()) + return std::nullopt; + + llvm::SmallSet<llvm::StringRef, 4> AllFeatures( + llvm::from_range, getAllPossibleTargetIDFeatures(T, Processor)); + + for (auto &&F : *FeatureMap) + if (!AllFeatures.count(F.first())) + return std::nullopt; + + return Processor; +} + +// A canonical target ID is a target ID containing a canonical processor name +// and features in alphabetical order. +std::string getCanonicalTargetID(llvm::StringRef Processor, + const llvm::StringMap<bool> &Features) { + std::string TargetID = Processor.str(); + std::map<const llvm::StringRef, bool> OrderedMap; + for (const auto &F : Features) + OrderedMap[F.first()] = F.second; + for (const auto &F : OrderedMap) + TargetID = TargetID + ':' + F.first.str() + (F.second ? "+" : "-"); + return TargetID; +} + // For a specific processor, a feature either shows up in all target IDs, or -// does not show up in any target IDs. Otherwise the target ID combination is -// invalid. +// does not show up in any target IDs. Otherwise the target ID combination +// is invalid. std::optional<std::pair<llvm::StringRef, llvm::StringRef>> -getConflictTargetIDCombination(llvm::ArrayRef<TargetIDEntry> Entries) { +getConflictTargetIDCombination(const std::set<llvm::StringRef> &TargetIDs) { struct Info { llvm::StringRef TargetID; - bool HasXnack; - bool HasSramEcc; + llvm::StringMap<bool> Features; + Info(llvm::StringRef TargetID, const llvm::StringMap<bool> &Features) + : TargetID(TargetID), Features(Features) {} }; - - llvm::SmallDenseMap<llvm::AMDGPU::GPUKind, Info> Seen; - for (const auto &[T, ID] : Entries) { - std::optional<llvm::AMDGPU::TargetID> Parsed = - llvm::AMDGPU::TargetID::parse(T, ID); - if (!Parsed) - continue; - - // A feature is present in a target ID only when an explicit '+'/'-' - // modifier is given, not when it is left unspecified. - Info Cur{ID, Parsed->isXnackOnOrOff(), Parsed->isSramEccOnOrOff()}; - auto [Loc, Inserted] = Seen.try_emplace(Parsed->getGPUKind(), Cur); - if (Inserted) - continue; - - const Info &Prev = Loc->second; - if (Cur.HasXnack != Prev.HasXnack || Cur.HasSramEcc != Prev.HasSramEcc) - return std::make_pair(Prev.TargetID, ID); + llvm::StringMap<Info> FeatureMap; + for (auto &&ID : TargetIDs) { + llvm::StringMap<bool> Features; + llvm::StringRef Proc = *parseTargetIDWithFormatCheckingOnly(ID, &Features); + auto [Loc, Inserted] = FeatureMap.try_emplace(Proc, ID, Features); + if (!Inserted) { + auto &ExistingFeatures = Loc->second.Features; + if (llvm::any_of(Features, [&](auto &F) { + return ExistingFeatures.count(F.first()) == 0; + })) + return std::make_pair(Loc->second.TargetID, ID); + } } return std::nullopt; } +bool isCompatibleTargetID(llvm::StringRef Provided, llvm::StringRef Requested) { + llvm::StringMap<bool> ProvidedFeatures, RequestedFeatures; + llvm::StringRef ProvidedProc = + *parseTargetIDWithFormatCheckingOnly(Provided, &ProvidedFeatures); + llvm::StringRef RequestedProc = + *parseTargetIDWithFormatCheckingOnly(Requested, &RequestedFeatures); + if (ProvidedProc != RequestedProc) + return false; + for (const auto &F : ProvidedFeatures) { + auto Loc = RequestedFeatures.find(F.first()); + // The default (unspecified) value of a feature is 'All', which can match + // either 'On' or 'Off'. + if (Loc == RequestedFeatures.end()) + return false; + // If a feature is specified, it must have exact match. + if (Loc->second != F.second) + return false; + } + return true; +} + std::string sanitizeTargetIDInFileName(llvm::StringRef TargetID) { std::string FileName = TargetID.str(); if (llvm::sys::path::is_style_windows(llvm::sys::path::Style::native)) diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp index a32fdaf528a4b0..080d3f4629679d 100644 --- a/clang/lib/Basic/Targets/AMDGPU.cpp +++ b/clang/lib/Basic/Targets/AMDGPU.cpp @@ -243,17 +243,13 @@ AMDGPUTargetInfo::AMDGPUTargetInfo(const llvm::Triple &Triple, HalfArgsAndReturns = true; if (Opts.AMDGPUXnackState != TargetOptions::AMDGPUFeatureState::Any) { - XnackSetting = - Opts.AMDGPUXnackState == TargetOptions::AMDGPUFeatureState::Enabled - ? llvm::AMDGPU::TargetIDSetting::On - : llvm::AMDGPU::TargetIDSetting::Off; + OffloadArchFeatures["xnack"] = + Opts.AMDGPUXnackState == TargetOptions::AMDGPUFeatureState::Enabled; } if (Opts.AMDGPUSramEccState != TargetOptions::AMDGPUFeatureState::Any) { - SramEccSetting = - Opts.AMDGPUSramEccState == TargetOptions::AMDGPUFeatureState::Enabled - ? llvm::AMDGPU::TargetIDSetting::On - : llvm::AMDGPU::TargetIDSetting::Off; + OffloadArchFeatures["sramecc"] = + Opts.AMDGPUSramEccState == TargetOptions::AMDGPUFeatureState::Enabled; } } @@ -318,25 +314,22 @@ void AMDGPUTargetInfo::getTargetDefines(const LangOptions &Opts, Twine("__")); Builder.defineMacro("__amdgcn_processor__", Twine("\"") + Twine(CanonName) + Twine("\"")); - llvm::AMDGPU::TargetID TargetID(GPUKind, getTriple(), XnackSetting, - SramEccSetting); - Builder.defineMacro("__amdgcn_target_id__", - Twine("\"") + - Twine(TargetID.getCanonicalTargetIDString()) + - Twine("\"")); - auto DefineFeatureMacro = [&](StringRef Feature, - llvm::AMDGPU::TargetIDSetting Setting) { - if (Setting == llvm::AMDGPU::TargetIDSetting::On || - Setting == llvm::AMDGPU::TargetIDSetting::Off) { - std::string NewF = Feature.str(); + Builder.defineMacro( + "__amdgcn_target_id__", + Twine("\"") + + Twine(getCanonicalTargetID(getArchNameAMDGCN(GPUKind), + OffloadArchFeatures)) + + Twine("\"")); + for (auto F : getAllPossibleTargetIDFeatures(getTriple(), CanonName)) { + auto Loc = OffloadArchFeatures.find(F); + if (Loc != OffloadArchFeatures.end()) { + std::string NewF = F.str(); llvm::replace(NewF, '-', '_'); - Builder.defineMacro( - Twine("__amdgcn_feature_") + Twine(NewF) + Twine("__"), - Setting == llvm::AMDGPU::TargetIDSetting::On ? "1" : "0"); + Builder.defineMacro(Twine("__amdgcn_feature_") + Twine(NewF) + + Twine("__"), + Loc->second ? "1" : "0"); } - }; - DefineFeatureMacro("xnack", XnackSetting); - DefineFeatureMacro("sramecc", SramEccSetting); + } } if (Opts.AtomicIgnoreDenormalMode) diff --git a/clang/lib/Basic/Targets/AMDGPU.h b/clang/lib/Basic/Targets/AMDGPU.h index 6c09ed5f6456b9..bf4ad80b291ec2 100644 --- a/clang/lib/Basic/Targets/AMDGPU.h +++ b/clang/lib/Basic/Targets/AMDGPU.h @@ -41,13 +41,13 @@ class LLVM_LIBRARY_VISIBILITY AMDGPUTargetInfo final : public TargetInfo { /// Whether having image instructions. bool HasImage = false; - /// Explicit xnack/sramecc target-id feature settings from the command line, - /// e.g. gfx908:xnack+:sramecc-. "Unsupported" means the feature was not - /// specified (or is not a valid target-id modifier for the processor). - llvm::AMDGPU::TargetIDSetting XnackSetting = - llvm::AMDGPU::TargetIDSetting::Unsupported; - llvm::AMDGPU::TargetIDSetting SramEccSetting = - llvm::AMDGPU::TargetIDSetting::Unsupported; + /// Target ID is device name followed by optional feature name postfixed + /// by plus or minus sign delimitted by colon, e.g. gfx908:xnack+:sramecc-. + /// If the target ID contains feature+, map it to true. + /// If the target ID contains feature-, map it to false. + /// If the target ID does not contain a feature (default), do not map it. + llvm::StringMap<bool> OffloadArchFeatures; + std::string TargetID; bool hasFP64() const { return getTriple().isAMDGCN(); } @@ -461,8 +461,8 @@ class LLVM_LIBRARY_VISIBILITY AMDGPUTargetInfo final : public TargetInfo { bool handleTargetFeatures(std::vector<std::string> &Features, DiagnosticsEngine &Diags) override { HasFullBFloat16 = true; - const llvm::AMDGPU::AMDGPUFeatureBitset &ArchFeatures = - llvm::AMDGPU::getFeatureBitset(GPUKind); + auto TargetIDFeatures = + getAllPossibleTargetIDFeatures(getTriple(), getArchNameAMDGCN(GPUKind)); for (const auto &F : Features) { assert(F.front() == '+' || F.front() == '-'); if (F == "+wavefrontsize64") @@ -473,18 +473,12 @@ class LLVM_LIBRARY_VISIBILITY AMDGPUTargetInfo final : public TargetInfo { CUMode = false; else if (F == "+image-insts") HasImage = true; - llvm::AMDGPU::TargetIDSetting Setting = - F.front() == '+' ? llvm::AMDGPU::TargetIDSetting::On - : llvm::AMDGPU::TargetIDSetting::Off; + bool IsOn = F.front() == '+'; StringRef Name = StringRef(F).drop_front(); - // xnack is a valid target-id modifier only when the processor supports - // on/off modes; sramecc when the processor supports sramecc. - if (Name == "xnack" && - ArchFeatures.test(llvm::AMDGPU::FEAT_XNACK_ON_OFF_MODES)) - XnackSetting = Setting; - else if (Name == "sramecc" && - ArchFeatures.test(llvm::AMDGPU::FEAT_SRAMECC_SUPPORT)) - SramEccSetting = Setting; + if (!llvm::is_contained(TargetIDFeatures, Name)) + continue; + assert(!OffloadArchFeatures.contains(Name)); + OffloadArchFeatures[Name] = IsOn; } return true; } diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 012194c3d36638..7a742e404bf5ca 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -108,7 +108,6 @@ #include "llvm/Support/TarWriter.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/TargetParser/AMDGPUTargetParser.h" #include "llvm/TargetParser/Host.h" #include "llvm/TargetParser/RISCVISAInfo.h" #include <cstdlib> // ::getenv @@ -3961,21 +3960,14 @@ static StringRef getCanonicalArchString(Compilation &C, if (Arch.isNVPTX()) return Args.MakeArgStringRef(OffloadArchToString(Arch)); - // AMDGCN target IDs carry a processor and xnack/sramecc modifiers to - // canonicalize. Other AMD offload arches (e.g. the amdgcnspirv pseudo-arch on - // a SPIR-V triple) have no target-id features and pass through unchanged. - if (Arch.isAMDGPU() && Triple.isAMDGCN()) { - StringRef TargetIDStr = ArchStr; - if (llvm::Triple::parseSubArch(ArchStr) != llvm::Triple::NoSubArch) - TargetIDStr = getProcessorFromTargetID(Triple, ArchStr); - - std::optional<llvm::AMDGPU::TargetID> ID = - llvm::AMDGPU::TargetID::parse(Triple, TargetIDStr); - if (!ID) { + if (Arch.isAMDGPU() || Arch.isAMDGCNSPIRV()) { + llvm::StringMap<bool> Features; + std::optional<StringRef> Arch = parseTargetID(Triple, ArchStr, &Features); + if (!Arch) { C.getDriver().Diag(clang::diag::err_drv_bad_target_id) << ArchStr; return StringRef(); } - return Args.MakeArgStringRef(ID->getCanonicalTargetIDString()); + return Args.MakeArgStringRef(getCanonicalTargetID(*Arch, Features)); } // If the input isn't CUDA or HIP just return the architecture. @@ -3986,18 +3978,13 @@ static StringRef getCanonicalArchString(Compilation &C, /// incompatible pair if a conflict occurs. static std::optional<std::pair<llvm::StringRef, llvm::StringRef>> getConflictOffloadArchCombination(const llvm::DenseSet<StringRef> &Archs, - const llvm::Triple &Triple) { + llvm::Triple Triple) { if (!Triple.isAMDGPU()) return std::nullopt; - // Sort for a deterministic conflicting pair in the diagnostic. - llvm::SmallVector<StringRef> ArchList(Archs.begin(), Archs.end()); - llvm::sort(ArchList); - - llvm::SmallVector<clang::TargetIDEntry> Entries; - for (StringRef Arch : ArchList) - Entries.emplace_back(Triple, Arch); - return getConflictTargetIDCombination(Entries); + std::set<StringRef> ArchSet; + llvm::copy(Archs, std::inserter(ArchSet, ArchSet.begin())); + return getConflictTargetIDCombination(ArchSet); } llvm::SmallVector<BoundArch> diff --git a/clang/lib/Driver/OffloadBundler.cpp b/clang/lib/Driver/OffloadBundler.cpp index 473fe940d9970d..2d0c326ab6f406 100644 --- a/clang/lib/Driver/OffloadBundler.cpp +++ b/clang/lib/Driver/OffloadBundler.cpp @@ -48,7 +48,6 @@ #include "llvm/Support/Timer.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/TargetParser/AMDGPUTargetParser.h" #include "llvm/TargetParser/Host.h" #include "llvm/TargetParser/Triple.h" #include <algorithm> @@ -1117,15 +1116,15 @@ bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, } // Incompatible if Processors mismatch. - std::optional<llvm::AMDGPU::TargetID> CodeObjectID = - llvm::AMDGPU::TargetID::parse(CodeObjectInfo.Triple, - CodeObjectInfo.TargetID); - std::optional<llvm::AMDGPU::TargetID> TargetID = - llvm::AMDGPU::TargetID::parse(TargetInfo.Triple, TargetInfo.TargetID); - - // Both target IDs must be valid and name the same processor. - if (!CodeObjectID || !TargetID || - CodeObjectID->getGPUKind() != TargetID->getGPUKind()) { + llvm::StringMap<bool> CodeObjectFeatureMap, TargetFeatureMap; + std::optional<StringRef> CodeObjectProc = clang::parseTargetID( + CodeObjectInfo.Triple, CodeObjectInfo.TargetID, &CodeObjectFeatureMap); + std::optional<StringRef> TargetProc = clang::parseTargetID( + TargetInfo.Triple, TargetInfo.TargetID, &TargetFeatureMap); + + // Both TargetProc and CodeObjectProc can't be empty here. + if (!TargetProc || !CodeObjectProc || + CodeObjectProc.value() != TargetProc.value()) { DEBUG_WITH_TYPE("CodeObjectCompatibility", dbgs() << "Incompatible: Processor mismatch \t[CodeObject: " << CodeObjectInfo.str() @@ -1133,30 +1132,44 @@ bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, return false; } - // A feature (xnack/sramecc) is compatible if the code object leaves it - // unspecified ("Any"), or specifies it with the same value the target does. - // A feature the code object specifies but the target leaves unspecified is - // incompatible, as is a differing explicit value. - auto FeatureCompatible = [&](llvm::AMDGPU::TargetIDSetting CodeObject, - llvm::AMDGPU::TargetIDSetting Target) { - bool CodeObjectExplicit = CodeObject == llvm::AMDGPU::TargetIDSetting::On || - CodeObject == llvm::AMDGPU::TargetIDSetting::Off; - if (!CodeObjectExplicit) - return true; - return CodeObject == Target; - }; - - if (!FeatureCompatible(CodeObjectID->getXnackSetting(), - TargetID->getXnackSetting()) || - !FeatureCompatible(CodeObjectID->getSramEccSetting(), - TargetID->getSramEccSetting())) { + // Incompatible if CodeObject has more features than Target, irrespective of + // type or sign of features. + if (CodeObjectFeatureMap.getNumItems() > TargetFeatureMap.getNumItems()) { DEBUG_WITH_TYPE("CodeObjectCompatibility", - dbgs() << "Incompatible: Feature mismatch \t[CodeObject: " + dbgs() << "Incompatible: CodeObject has more features " + "than target \t[CodeObject: " << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); return false; } + // Compatible if each target feature specified by target is compatible with + // target feature of code object. The target feature is compatible if the + // code object does not specify it (meaning Any), or if it specifies it + // with the same value (meaning On or Off). + for (const auto &CodeObjectFeature : CodeObjectFeatureMap) { + auto TargetFeature = TargetFeatureMap.find(CodeObjectFeature.getKey()); + if (TargetFeature == TargetFeatureMap.end()) { + DEBUG_WITH_TYPE( + "CodeObjectCompatibility", + dbgs() + << "Incompatible: Value of CodeObject's non-ANY feature is " + "not matching with Target feature's ANY value \t[CodeObject: " + << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() + << "]\n"); + return false; + } else if (TargetFeature->getValue() != CodeObjectFeature.getValue()) { + DEBUG_WITH_TYPE( + "CodeObjectCompatibility", + dbgs() << "Incompatible: Value of CodeObject's non-ANY feature is " + "not matching with Target feature's non-ANY value " + "\t[CodeObject: " + << CodeObjectInfo.str() + << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); + return false; + } + } + // CodeObject is compatible if all features of Target are: // - either, present in the Code Object's features map with the same sign, // - or, the feature is missing from CodeObjects's features map i.e. it is @@ -1525,18 +1538,8 @@ CheckHeterogeneousArchive(StringRef ArchiveName, if (CodeObjectFileError) return CodeObjectFileError; - // A single bundle may contain several triples. Pair each target ID with its - // own triple; the conflict check groups by resolved processor, which is - // spelling-independent. - llvm::SmallVector<OffloadTargetInfo> Infos; - for (StringRef BundleId : BundleIds) - Infos.emplace_back(BundleId, BundlerConfig); - llvm::SmallVector<clang::TargetIDEntry> Entries; - for (const OffloadTargetInfo &Info : Infos) - Entries.emplace_back(Info.Triple, Info.TargetID); - - if (auto &&ConflictingArchs = - clang::getConflictTargetIDCombination(Entries)) { + auto &&ConflictingArchs = clang::getConflictTargetIDCombination(BundleIds); + if (ConflictingArchs) { std::string ErrMsg = Twine("conflicting TargetIDs [" + ConflictingArchs.value().first + ", " + ConflictingArchs.value().second + "] found in " + diff --git a/clang/lib/Driver/ToolChains/AMDGPU.cpp b/clang/lib/Driver/ToolChains/AMDGPU.cpp index d7ea012898d18b..d6dacb4ccba839 100644 --- a/clang/lib/Driver/ToolChains/AMDGPU.cpp +++ b/clang/lib/Driver/ToolChains/AMDGPU.cpp @@ -802,24 +802,26 @@ AMDGPUToolChain::TranslateArgs(const DerivedArgList &Args, BoundArch BA, } if (!getTriple().isSPIRV()) { - std::optional<llvm::AMDGPU::TargetID> PTID = checkTargetID(*DAL); - - // Synthesize feature flags for explicit target ID modifiers (xnack, - // sramecc). - if (PTID) { - using llvm::AMDGPU::TargetIDSetting; - if (PTID->isXnackOnOrOff()) - DAL->AddFlagArg(nullptr, Opts.getOption(PTID->getXnackSetting() == - TargetIDSetting::On + AMDGPUToolChain::ParsedTargetIDType PTID = checkTargetID(*DAL); + + // Synthesize feature flags for target ID modifiers (xnack, sramecc). + if (PTID.OptionalFeatureMap) { + const llvm::StringMap<bool> &FeatureMap = *PTID.OptionalFeatureMap; + + auto XnackIt = FeatureMap.find("xnack"); + if (XnackIt != FeatureMap.end()) { + DAL->AddFlagArg(nullptr, Opts.getOption(XnackIt->second ? options::OPT_mxnack : options::OPT_mno_xnack)); + } - if (PTID->isSramEccOnOrOff()) - DAL->AddFlagArg( - nullptr, - Opts.getOption(PTID->getSramEccSetting() == TargetIDSetting::On - ? options::OPT_msramecc - : options::OPT_mno_sramecc)); + auto SrameccIt = FeatureMap.find("sramecc"); + if (SrameccIt != FeatureMap.end()) { + DAL->AddFlagArg(nullptr, + Opts.getOption(SrameccIt->second + ? options::OPT_msramecc + : options::OPT_mno_sramecc)); + } } } @@ -1037,48 +1039,43 @@ AMDGPUToolChain::getGPUArch(const llvm::opt::ArgList &DriverArgs) const { getTriple(), DriverArgs.getLastArgValue(options::OPT_mcpu_EQ)); } -StringRef -AMDGPUToolChain::getTargetIDArg(const llvm::opt::ArgList &DriverArgs) const { - // Target IDs are only meaningful for AMDGCN targets. - if (!getTriple().isAMDGCN()) - return StringRef(); - return DriverArgs.getLastArgValue(options::OPT_mcpu_EQ); -} - -std::optional<llvm::AMDGPU::TargetID> +AMDGPUToolChain::ParsedTargetIDType AMDGPUToolChain::getParsedTargetID(const llvm::opt::ArgList &DriverArgs) const { - StringRef TargetID = getTargetIDArg(DriverArgs); + StringRef TargetID = DriverArgs.getLastArgValue(options::OPT_mcpu_EQ); if (TargetID.empty()) - return std::nullopt; + return {}; - return llvm::AMDGPU::TargetID::parse(getTriple(), TargetID); + llvm::StringMap<bool> FeatureMap; + auto OptionalGpuArch = parseTargetID(getTriple(), TargetID, &FeatureMap); + if (!OptionalGpuArch) + return {TargetID.str(), std::nullopt, std::nullopt}; + + return {TargetID.str(), OptionalGpuArch->str(), FeatureMap}; } -std::optional<llvm::AMDGPU::TargetID> +AMDGPUToolChain::ParsedTargetIDType AMDGPUToolChain::checkTargetID(const llvm::opt::ArgList &DriverArgs) const { - std::optional<llvm::AMDGPU::TargetID> ID = getParsedTargetID(DriverArgs); - if (ID) - return ID; - - StringRef TargetID = getTargetIDArg(DriverArgs); - if (TargetID.empty()) - return ID; + auto PTID = getParsedTargetID(DriverArgs); + if (PTID.OptionalTargetID && !PTID.OptionalGPUArch) { + getDriver().Diag(clang::diag::err_drv_bad_target_id) + << *PTID.OptionalTargetID; + return PTID; + } - // Parsing failed. If the processor names a recognized GPU that is simply - // incompatible with the triple's subarch, prefer the more specific - // subarch-mismatch diagnostic over the generic invalid-target-id one. - StringRef Processor = getProcessorFromTargetID(getTriple(), TargetID); - llvm::AMDGPU::GPUKind Kind = llvm::AMDGPU::parseArchAMDGCN(Processor); if (getTriple().getSubArch() != llvm::Triple::NoSubArch && - Kind != llvm::AMDGPU::GK_NONE && - !llvm::AMDGPU::isCPUValidForSubArch(getTriple().getSubArch(), Kind)) { - getDriver().Diag(clang::diag::err_target_unsupported_arch) - << Processor << getTriple().getArchName(); - return ID; + PTID.OptionalGPUArch) { + llvm::AMDGPU::GPUKind Kind = + llvm::AMDGPU::parseArchAMDGCN(*PTID.OptionalGPUArch); + llvm::Triple::SubArchType KindSubArch = + static_cast<llvm::Triple::SubArchType>(llvm::AMDGPU::getSubArch(Kind)); + if (getTriple().getSubArch() != KindSubArch && + getTriple().getSubArch() != + llvm::AMDGPU::getMajorSubArch(KindSubArch)) { + getDriver().Diag(clang::diag::err_target_unsupported_arch) + << *PTID.OptionalGPUArch << getTriple().getArchName(); + } } - - getDriver().Diag(clang::diag::err_drv_bad_target_id) << TargetID; - return ID; + return PTID; } Expected<SmallVector<std::string>> @@ -1362,21 +1359,27 @@ LTOKind AMDGPUToolChain::getLTOMode(const ArgList &Args, } static bool isXnackAvailable(const llvm::Triple &TT, llvm::StringRef TargetID) { - std::optional<llvm::AMDGPU::TargetID> ID = - llvm::AMDGPU::TargetID::parse(TT, TargetID); - if (!ID) + // Arch-specific check - only report as supported if arch has xnack+ + if (!TT.isAMDGCN()) return false; + llvm::StringRef Processor = getProcessorFromTargetID(TT, TargetID); + llvm::AMDGPU::GPUKind ProcKind = llvm::AMDGPU::parseArchAMDGCN(Processor); const llvm::AMDGPU::AMDGPUFeatureBitset &Features = - llvm::AMDGPU::getFeatureBitset(ID->getGPUKind()); + llvm::AMDGPU::getFeatureBitset(ProcKind); - // If the processor has xnack but doesn't support on/off modes, xnack is - // always on. - if (Features.test(llvm::AMDGPU::FEAT_XNACK_SUPPORT) && - !Features.test(llvm::AMDGPU::FEAT_XNACK_ON_OFF_MODES)) + // If processor has xnack but doesn't support on/off modes, xnack is always on + bool XnackAlwaysOn = Features.test(llvm::AMDGPU::FEAT_XNACK_SUPPORT) && + !Features.test(llvm::AMDGPU::FEAT_XNACK_ON_OFF_MODES); + if (XnackAlwaysOn) return true; - // Otherwise, it is available only if the target ID explicitly enables it. - return ID->getXnackSetting() == llvm::AMDGPU::TargetIDSetting::On; + // Otherwise, check if xnack+ is explicitly enabled in the target ID + llvm::StringMap<bool> FeatureMap; + auto OptionalGpuArch = parseTargetID(TT, TargetID, &FeatureMap); + if (!OptionalGpuArch) + return false; + auto Loc = FeatureMap.find("xnack"); + return (Loc != FeatureMap.end() && Loc->second); } SanitizerMask AMDGPUToolChain::getSupportedSanitizers( diff --git a/clang/lib/Driver/ToolChains/AMDGPU.h b/clang/lib/Driver/ToolChains/AMDGPU.h index fd71b53064d3e1..027a6e3b47dca6 100644 --- a/clang/lib/Driver/ToolChains/AMDGPU.h +++ b/clang/lib/Driver/ToolChains/AMDGPU.h @@ -161,20 +161,23 @@ class LLVM_LIBRARY_VISIBILITY AMDGPUToolChain : public Generic_ELF { Action::OffloadKind DeviceOffloadingKind) const; protected: - /// Check and diagnose an invalid target ID specified by -mcpu. Returns the - /// parsed target ID, or std::nullopt if -mcpu is absent or invalid - virtual std::optional<llvm::AMDGPU::TargetID> + /// The struct type returned by getParsedTargetID. + struct ParsedTargetIDType { + std::optional<std::string> OptionalTargetID; + std::optional<std::string> OptionalGPUArch; + std::optional<llvm::StringMap<bool>> OptionalFeatureMap; + }; + + /// Check and diagnose invalid target ID specified by -mcpu. + /// Returns the parsed target ID. + virtual ParsedTargetIDType checkTargetID(const llvm::opt::ArgList &DriverArgs) const; - /// Parse the target ID specified by -mcpu. Returns the parsed target ID, or - /// std::nullopt if -mcpu is absent or invalid. - std::optional<llvm::AMDGPU::TargetID> + /// Get target ID, GPU arch, and target ID features if the target ID is + /// specified and valid. + ParsedTargetIDType getParsedTargetID(const llvm::opt::ArgList &DriverArgs) const; - /// Get the raw target ID string from -mcpu, or an empty string if -mcpu is - /// absent or the target is not AMDGCN. - StringRef getTargetIDArg(const llvm::opt::ArgList &DriverArgs) const; - /// Get GPU arch from -mcpu without checking. StringRef getGPUArch(const llvm::opt::ArgList &DriverArgs) const; diff --git a/clang/test/OffloadTools/clang-offload-bundler/basic.c b/clang/test/OffloadTools/clang-offload-bundler/basic.c index bd2ca595c4a7ea..b10c9cde089216 100644 --- a/clang/test/OffloadTools/clang-offload-bundler/basic.c +++ b/clang/test/OffloadTools/clang-offload-bundler/basic.c @@ -535,17 +535,6 @@ // RUN: not clang-offload-bundler -type=o -targets=host-x86_64-unknown-linux-gnu,openmp-amdgpu9.06-amd-amdhsa--gfx906,openmp-amdgpu9.06-amd-amdhsa--gfx906:sramecc+ -input=%t.o -input=%t.tgt1 -input=%t.tgt2 -output=%t.bad.bundle 2>&1 | FileCheck %s -check-prefix=BADTARGETS // BADTARGETS: error: Cannot bundle inputs with conflicting targets: 'openmp-amdgpu9.06-amd-amdhsa--gfx906' and 'openmp-amdgpu9.06-amd-amdhsa--gfx906:sramecc+' -// Check the per-member TargetID conflict detection performed by -// -check-input-archive. The bundle-time conflict check groups by offload kind -// and triple, so "gfx906" and "gfx906:xnack+" placed under different offload -// kinds (hip vs hipv4) bundle successfully. The archive check instead groups by -// resolved processor and must flag them as conflicting for the same gfx906. - -// RUN: clang-offload-bundler -type=o -targets=host-x86_64-unknown-linux-gnu,hip-amdgcn-amd-amdhsa--gfx906,hipv4-amdgcn-amd-amdhsa--gfx906:xnack+ -input=%t.o -input=%t.tgt1 -input=%t.tgt2 -output=%t.conflict.bundle -// RUN: llvm-ar cr %t.conflict-archive.a %t.conflict.bundle -// RUN: not clang-offload-bundler -unbundle -type=a -check-input-archive -targets=hip-amdgcn-amd-amdhsa--gfx906 -input=%t.conflict-archive.a -output=%t.conflict-out.a 2>&1 | FileCheck %s -check-prefix=CONFLICTARCHIVE -// CONFLICTARCHIVE: error: conflicting TargetIDs [gfx906, gfx906:xnack+] found in {{.*}}conflict.bundle of {{.*}}conflict-archive.a - // Check for error if no compatible code object is found in the heterogeneous archive library // RUN: not clang-offload-bundler -unbundle -type=a -targets=openmp-amdgpu8.03-amd-amdhsa--gfx803 -input=%t.input-archive.a -output=%t-archive-gfx803-incompatible.a 2>&1 | FileCheck %s -check-prefix=INCOMPATIBLEARCHIVE // INCOMPATIBLEARCHIVE: error: no compatible code object found for the target 'openmp-amdgpu8.03-amd-amdhsa--gfx803' in heterogeneous archive library diff --git a/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp b/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp index 72ead7c0b34db5..40d77abe2ef7c3 100644 --- a/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp +++ b/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp @@ -349,8 +349,8 @@ int main(int argc, const char **argv) { unsigned HostTargetNum = 0u; bool HIPOnly = true; llvm::DenseSet<StringRef> ParsedTargets; - // Map {offload-kind}-{triple} to its device triple and target IDs. - std::map<std::string, std::pair<llvm::Triple, std::set<StringRef>>> TargetIDs; + // Map {offload-kind}-{triple} to target IDs. + std::map<std::string, std::set<StringRef>> TargetIDs; // Standardize target names to include env field std::vector<std::string> StandardizedTargetNames; for (StringRef Target : TargetNames) { @@ -385,10 +385,8 @@ int main(int argc, const char **argv) { return reportError(createStringError(errc::invalid_argument, Msg.str())); } - auto &Entry = TargetIDs[OffloadInfo.OffloadKind.str() + "-" + - OffloadInfo.Triple.str()]; - Entry.first = OffloadInfo.Triple; - Entry.second.insert(OffloadInfo.TargetID); + TargetIDs[OffloadInfo.OffloadKind.str() + "-" + OffloadInfo.Triple.str()] + .insert(OffloadInfo.TargetID); if (KindIsValid && OffloadInfo.hasHostKind()) { ++HostTargetNum; // Save the index of the input that refers to the host. @@ -404,17 +402,14 @@ int main(int argc, const char **argv) { BundlerConfig.TargetNames.assign(StandardizedTargetNames.begin(), StandardizedTargetNames.end()); - for (const auto &[Key, TripleAndIDs] : TargetIDs) { - const auto &[Triple, IDs] = TripleAndIDs; - llvm::SmallVector<clang::TargetIDEntry> Entries; - for (StringRef ID : IDs) - Entries.emplace_back(Triple, ID); - if (auto ConflictingTID = clang::getConflictTargetIDCombination(Entries)) { + for (const auto &TargetID : TargetIDs) { + if (auto ConflictingTID = + clang::getConflictTargetIDCombination(TargetID.second)) { SmallVector<char, 128u> Buf; raw_svector_ostream Msg(Buf); Msg << "Cannot bundle inputs with conflicting targets: '" - << Key + "-" + ConflictingTID->first << "' and '" - << Key + "-" + ConflictingTID->second << "'"; + << TargetID.first + "-" + ConflictingTID->first << "' and '" + << TargetID.first + "-" + ConflictingTID->second << "'"; return reportError(createStringError(errc::invalid_argument, Msg.str())); } } diff --git a/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h b/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h index 8ae937f59c28c4..4b0fa417c6b100 100644 --- a/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h +++ b/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h @@ -399,7 +399,7 @@ class LLVM_ABI TargetID { /// \returns the canonical processor name followed by any explicit xnack and /// sramecc feature modifiers order (e.g. "gfx908:sramecc-:xnack+"), without /// the triple prefix. - std::string getCanonicalTargetIDString() const; + std::string getCanonicalFeatureString() const; bool operator==(const TargetID &Other) const; bool operator!=(const TargetID &Other) const { return !(*this == Other); } diff --git a/llvm/lib/TargetParser/AMDGPUTargetParser.cpp b/llvm/lib/TargetParser/AMDGPUTargetParser.cpp index 3e1d26f2c10fde..fc027c48ca354f 100644 --- a/llvm/lib/TargetParser/AMDGPUTargetParser.cpp +++ b/llvm/lib/TargetParser/AMDGPUTargetParser.cpp @@ -828,11 +828,8 @@ std::optional<TargetID> TargetID::parse(const Triple &TT, if (!TT.isAMDGCN()) return std::nullopt; - // Filter out unrecognized subarch suffixes. The bare arch may be spelled - // either "amdgcn" (legacy) or "amdgpu" (new subarch triples); anything else - // with no recognized subarch is a stray suffix. - if (TT.getSubArch() == Triple::NoSubArch && TT.getArchName() != "amdgcn" && - TT.getArchName() != "amdgpu") + // Filter out unrecognized subarch suffixes. + if (TT.getSubArch() == Triple::NoSubArch && TT.getArchName() != "amdgcn") return std::nullopt; // A named processor (i.e. not the empty/generic wildcard, which is resolved @@ -917,7 +914,7 @@ void TargetID::printCanonicalTargetIDString(raw_ostream &OS) const { isXnackHardwiredOn(Arch)); } -std::string TargetID::getCanonicalTargetIDString() const { +std::string TargetID::getCanonicalFeatureString() const { std::string Str; raw_string_ostream OS(Str); printCanonicalTargetIDString(OS); diff --git a/llvm/unittests/TargetParser/TargetParserTest.cpp b/llvm/unittests/TargetParser/TargetParserTest.cpp index d2ed408f74a2c0..3cf07ca5b02027 100644 --- a/llvm/unittests/TargetParser/TargetParserTest.cpp +++ b/llvm/unittests/TargetParser/TargetParserTest.cpp @@ -3474,13 +3474,13 @@ TEST(TargetParserTest, testAMDGPUParseTargetIDString) { EXPECT_FALSE( TargetID::parseTargetIDString("amdgpu11-amd-amdhsa-unknown-gfx1200")); - // A subarchless "amdgpu" is the canonical spelling of the offload triple and - // is accepted, but an unrecognized "amdgpu<x>" suffix is rejected. - EXPECT_TRUE( + // A subarchless "amdgpu" or an unrecognized "amdgpu<x>" arch is rejected, + // even with an otherwise valid processor. + EXPECT_FALSE( TargetID::parseTargetIDString("amdgpu-amd-amdhsa-unknown-gfx900")); EXPECT_FALSE( TargetID::parseTargetIDString("amdgpufoo-amd-amdhsa-unknown-gfx900")); - EXPECT_TRUE(TargetID::parseTargetIDString("amdgpu-amd-amdhsa-unknown-")); + EXPECT_FALSE(TargetID::parseTargetIDString("amdgpu-amd-amdhsa-unknown-")); EXPECT_FALSE(TargetID::parseTargetIDString("amdgpufoo-amd-amdhsa-unknown")); // Constructing directly from a triple and processor+features string must @@ -3499,12 +3499,12 @@ TEST(TargetParserTest, testAMDGPUParseTargetIDString) { } EXPECT_EQ(TargetID::parse(AMDHSA, "gfx908:xnack+:sramecc-") - ->getCanonicalTargetIDString(), + ->getCanonicalFeatureString(), "gfx908:sramecc-:xnack+"); - EXPECT_EQ(TargetID::parse(AMDHSA, "gfx908")->getCanonicalTargetIDString(), + EXPECT_EQ(TargetID::parse(AMDHSA, "gfx908")->getCanonicalFeatureString(), "gfx908"); EXPECT_EQ(TargetID::parse(Triple("amdgcn-amd-amdpal"), "gfx908:xnack-") - ->getCanonicalTargetIDString(), + ->getCanonicalFeatureString(), "gfx908:xnack-"); EXPECT_TRUE(TargetID::parse(AMDHSA, "").has_value()); EXPECT_FALSE(TargetID::parse(AMDHSA, "gfxbogus").has_value()); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
