| 1 | //===--- TargetID.cpp - Utilities for parsing target ID -------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "clang/Basic/TargetID.h" |
| 10 | #include "llvm/ADT/STLExtras.h" |
| 11 | #include "llvm/ADT/SmallSet.h" |
| 12 | #include "llvm/TargetParser/TargetParser.h" |
| 13 | #include "llvm/TargetParser/Triple.h" |
| 14 | #include <map> |
| 15 | #include <optional> |
| 16 | |
| 17 | namespace clang { |
| 18 | |
| 19 | static llvm::SmallVector<llvm::StringRef, 4> |
| 20 | getAllPossibleAMDGPUTargetIDFeatures(const llvm::Triple &T, |
| 21 | llvm::StringRef Proc) { |
| 22 | // Entries in returned vector should be in alphabetical order. |
| 23 | llvm::SmallVector<llvm::StringRef, 4> Ret; |
| 24 | auto ProcKind = T.isAMDGCN() ? llvm::AMDGPU::parseArchAMDGCN(CPU: Proc) |
| 25 | : llvm::AMDGPU::parseArchR600(CPU: Proc); |
| 26 | if (ProcKind == llvm::AMDGPU::GK_NONE) |
| 27 | return Ret; |
| 28 | auto Features = T.isAMDGCN() ? llvm::AMDGPU::getArchAttrAMDGCN(AK: ProcKind) |
| 29 | : llvm::AMDGPU::getArchAttrR600(AK: ProcKind); |
| 30 | if (Features & llvm::AMDGPU::FEATURE_SRAMECC) |
| 31 | Ret.push_back(Elt: "sramecc" ); |
| 32 | if (Features & llvm::AMDGPU::FEATURE_XNACK) |
| 33 | Ret.push_back(Elt: "xnack" ); |
| 34 | return Ret; |
| 35 | } |
| 36 | |
| 37 | llvm::SmallVector<llvm::StringRef, 4> |
| 38 | getAllPossibleTargetIDFeatures(const llvm::Triple &T, |
| 39 | llvm::StringRef Processor) { |
| 40 | llvm::SmallVector<llvm::StringRef, 4> Ret; |
| 41 | if (T.isAMDGPU()) |
| 42 | return getAllPossibleAMDGPUTargetIDFeatures(T, Proc: Processor); |
| 43 | return Ret; |
| 44 | } |
| 45 | |
| 46 | /// Returns canonical processor name or empty string if \p Processor is invalid. |
| 47 | static llvm::StringRef getCanonicalProcessorName(const llvm::Triple &T, |
| 48 | llvm::StringRef Processor) { |
| 49 | if (T.isAMDGPU()) |
| 50 | return llvm::AMDGPU::getCanonicalArchName(T, Arch: Processor); |
| 51 | return Processor; |
| 52 | } |
| 53 | |
| 54 | llvm::StringRef getProcessorFromTargetID(const llvm::Triple &T, |
| 55 | llvm::StringRef TargetID) { |
| 56 | auto Split = TargetID.split(Separator: ':'); |
| 57 | return getCanonicalProcessorName(T, Processor: Split.first); |
| 58 | } |
| 59 | |
| 60 | // Parse a target ID with format checking only. Do not check whether processor |
| 61 | // name or features are valid for the processor. |
| 62 | // |
| 63 | // A target ID is a processor name followed by a list of target features |
| 64 | // delimited by colon. Each target feature is a string post-fixed by a plus |
| 65 | // or minus sign, e.g. gfx908:sramecc+:xnack-. |
| 66 | static std::optional<llvm::StringRef> |
| 67 | parseTargetIDWithFormatCheckingOnly(llvm::StringRef TargetID, |
| 68 | llvm::StringMap<bool> *FeatureMap) { |
| 69 | llvm::StringRef Processor; |
| 70 | |
| 71 | if (TargetID.empty()) |
| 72 | return llvm::StringRef(); |
| 73 | |
| 74 | auto Split = TargetID.split(Separator: ':'); |
| 75 | Processor = Split.first; |
| 76 | if (Processor.empty()) |
| 77 | return std::nullopt; |
| 78 | |
| 79 | auto Features = Split.second; |
| 80 | if (Features.empty()) |
| 81 | return Processor; |
| 82 | |
| 83 | llvm::StringMap<bool> LocalFeatureMap; |
| 84 | if (!FeatureMap) |
| 85 | FeatureMap = &LocalFeatureMap; |
| 86 | |
| 87 | while (!Features.empty()) { |
| 88 | auto Splits = Features.split(Separator: ':'); |
| 89 | auto Sign = Splits.first.back(); |
| 90 | auto Feature = Splits.first.drop_back(); |
| 91 | if (Sign != '+' && Sign != '-') |
| 92 | return std::nullopt; |
| 93 | bool IsOn = Sign == '+'; |
| 94 | // Each feature can only show up at most once in target ID. |
| 95 | if (!FeatureMap->try_emplace(Key: Feature, Args&: IsOn).second) |
| 96 | return std::nullopt; |
| 97 | Features = Splits.second; |
| 98 | } |
| 99 | return Processor; |
| 100 | } |
| 101 | |
| 102 | std::optional<llvm::StringRef> |
| 103 | parseTargetID(const llvm::Triple &T, llvm::StringRef TargetID, |
| 104 | llvm::StringMap<bool> *FeatureMap) { |
| 105 | auto OptionalProcessor = |
| 106 | parseTargetIDWithFormatCheckingOnly(TargetID, FeatureMap); |
| 107 | |
| 108 | if (!OptionalProcessor) |
| 109 | return std::nullopt; |
| 110 | |
| 111 | llvm::StringRef Processor = getCanonicalProcessorName(T, Processor: *OptionalProcessor); |
| 112 | if (Processor.empty()) |
| 113 | return std::nullopt; |
| 114 | |
| 115 | llvm::SmallSet<llvm::StringRef, 4> AllFeatures( |
| 116 | llvm::from_range, getAllPossibleTargetIDFeatures(T, Processor)); |
| 117 | |
| 118 | for (auto &&F : *FeatureMap) |
| 119 | if (!AllFeatures.count(V: F.first())) |
| 120 | return std::nullopt; |
| 121 | |
| 122 | return Processor; |
| 123 | } |
| 124 | |
| 125 | // A canonical target ID is a target ID containing a canonical processor name |
| 126 | // and features in alphabetical order. |
| 127 | std::string getCanonicalTargetID(llvm::StringRef Processor, |
| 128 | const llvm::StringMap<bool> &Features) { |
| 129 | std::string TargetID = Processor.str(); |
| 130 | std::map<const llvm::StringRef, bool> OrderedMap; |
| 131 | for (const auto &F : Features) |
| 132 | OrderedMap[F.first()] = F.second; |
| 133 | for (const auto &F : OrderedMap) |
| 134 | TargetID = TargetID + ':' + F.first.str() + (F.second ? "+" : "-" ); |
| 135 | return TargetID; |
| 136 | } |
| 137 | |
| 138 | // For a specific processor, a feature either shows up in all target IDs, or |
| 139 | // does not show up in any target IDs. Otherwise the target ID combination |
| 140 | // is invalid. |
| 141 | std::optional<std::pair<llvm::StringRef, llvm::StringRef>> |
| 142 | getConflictTargetIDCombination(const std::set<llvm::StringRef> &TargetIDs) { |
| 143 | struct Info { |
| 144 | llvm::StringRef TargetID; |
| 145 | llvm::StringMap<bool> Features; |
| 146 | Info(llvm::StringRef TargetID, const llvm::StringMap<bool> &Features) |
| 147 | : TargetID(TargetID), Features(Features) {} |
| 148 | }; |
| 149 | llvm::StringMap<Info> FeatureMap; |
| 150 | for (auto &&ID : TargetIDs) { |
| 151 | llvm::StringMap<bool> Features; |
| 152 | llvm::StringRef Proc = *parseTargetIDWithFormatCheckingOnly(TargetID: ID, FeatureMap: &Features); |
| 153 | auto [Loc, Inserted] = FeatureMap.try_emplace(Key: Proc, Args: ID, Args&: Features); |
| 154 | if (!Inserted) { |
| 155 | auto &ExistingFeatures = Loc->second.Features; |
| 156 | if (llvm::any_of(Range&: Features, P: [&](auto &F) { |
| 157 | return ExistingFeatures.count(F.first()) == 0; |
| 158 | })) |
| 159 | return std::make_pair(x&: Loc->second.TargetID, y: ID); |
| 160 | } |
| 161 | } |
| 162 | return std::nullopt; |
| 163 | } |
| 164 | |
| 165 | bool isCompatibleTargetID(llvm::StringRef Provided, llvm::StringRef Requested) { |
| 166 | llvm::StringMap<bool> ProvidedFeatures, RequestedFeatures; |
| 167 | llvm::StringRef ProvidedProc = |
| 168 | *parseTargetIDWithFormatCheckingOnly(TargetID: Provided, FeatureMap: &ProvidedFeatures); |
| 169 | llvm::StringRef RequestedProc = |
| 170 | *parseTargetIDWithFormatCheckingOnly(TargetID: Requested, FeatureMap: &RequestedFeatures); |
| 171 | if (ProvidedProc != RequestedProc) |
| 172 | return false; |
| 173 | for (const auto &F : ProvidedFeatures) { |
| 174 | auto Loc = RequestedFeatures.find(Key: F.first()); |
| 175 | // The default (unspecified) value of a feature is 'All', which can match |
| 176 | // either 'On' or 'Off'. |
| 177 | if (Loc == RequestedFeatures.end()) |
| 178 | return false; |
| 179 | // If a feature is specified, it must have exact match. |
| 180 | if (Loc->second != F.second) |
| 181 | return false; |
| 182 | } |
| 183 | return true; |
| 184 | } |
| 185 | |
| 186 | } // namespace clang |
| 187 | |