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/SmallSet.h" |
11 | #include "llvm/Support/raw_ostream.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 | auto Loc = FeatureMap->find(Key: Feature); |
95 | // Each feature can only show up at most once in target ID. |
96 | if (Loc != FeatureMap->end()) |
97 | return std::nullopt; |
98 | (*FeatureMap)[Feature] = IsOn; |
99 | Features = Splits.second; |
100 | } |
101 | return Processor; |
102 | } |
103 | |
104 | std::optional<llvm::StringRef> |
105 | parseTargetID(const llvm::Triple &T, llvm::StringRef TargetID, |
106 | llvm::StringMap<bool> *FeatureMap) { |
107 | auto OptionalProcessor = |
108 | parseTargetIDWithFormatCheckingOnly(TargetID, FeatureMap); |
109 | |
110 | if (!OptionalProcessor) |
111 | return std::nullopt; |
112 | |
113 | llvm::StringRef Processor = getCanonicalProcessorName(T, Processor: *OptionalProcessor); |
114 | if (Processor.empty()) |
115 | return std::nullopt; |
116 | |
117 | llvm::SmallSet<llvm::StringRef, 4> AllFeatures; |
118 | for (auto &&F : getAllPossibleTargetIDFeatures(T, Processor)) |
119 | AllFeatures.insert(V: F); |
120 | |
121 | for (auto &&F : *FeatureMap) |
122 | if (!AllFeatures.count(V: F.first())) |
123 | return std::nullopt; |
124 | |
125 | return Processor; |
126 | } |
127 | |
128 | // A canonical target ID is a target ID containing a canonical processor name |
129 | // and features in alphabetical order. |
130 | std::string getCanonicalTargetID(llvm::StringRef Processor, |
131 | const llvm::StringMap<bool> &Features) { |
132 | std::string TargetID = Processor.str(); |
133 | std::map<const llvm::StringRef, bool> OrderedMap; |
134 | for (const auto &F : Features) |
135 | OrderedMap[F.first()] = F.second; |
136 | for (const auto &F : OrderedMap) |
137 | TargetID = TargetID + ':' + F.first.str() + (F.second ? "+" : "-" ); |
138 | return TargetID; |
139 | } |
140 | |
141 | // For a specific processor, a feature either shows up in all target IDs, or |
142 | // does not show up in any target IDs. Otherwise the target ID combination |
143 | // is invalid. |
144 | std::optional<std::pair<llvm::StringRef, llvm::StringRef>> |
145 | getConflictTargetIDCombination(const std::set<llvm::StringRef> &TargetIDs) { |
146 | struct Info { |
147 | llvm::StringRef TargetID; |
148 | llvm::StringMap<bool> Features; |
149 | }; |
150 | llvm::StringMap<Info> FeatureMap; |
151 | for (auto &&ID : TargetIDs) { |
152 | llvm::StringMap<bool> Features; |
153 | llvm::StringRef Proc = *parseTargetIDWithFormatCheckingOnly(TargetID: ID, FeatureMap: &Features); |
154 | auto Loc = FeatureMap.find(Key: Proc); |
155 | if (Loc == FeatureMap.end()) |
156 | FeatureMap[Proc] = Info{.TargetID: ID, .Features: Features}; |
157 | else { |
158 | auto &ExistingFeatures = Loc->second.Features; |
159 | if (llvm::any_of(Range&: Features, P: [&](auto &F) { |
160 | return ExistingFeatures.count(F.first()) == 0; |
161 | })) |
162 | return std::make_pair(x&: Loc->second.TargetID, y: ID); |
163 | } |
164 | } |
165 | return std::nullopt; |
166 | } |
167 | |
168 | bool isCompatibleTargetID(llvm::StringRef Provided, llvm::StringRef Requested) { |
169 | llvm::StringMap<bool> ProvidedFeatures, RequestedFeatures; |
170 | llvm::StringRef ProvidedProc = |
171 | *parseTargetIDWithFormatCheckingOnly(TargetID: Provided, FeatureMap: &ProvidedFeatures); |
172 | llvm::StringRef RequestedProc = |
173 | *parseTargetIDWithFormatCheckingOnly(TargetID: Requested, FeatureMap: &RequestedFeatures); |
174 | if (ProvidedProc != RequestedProc) |
175 | return false; |
176 | for (const auto &F : ProvidedFeatures) { |
177 | auto Loc = RequestedFeatures.find(Key: F.first()); |
178 | // The default (unspecified) value of a feature is 'All', which can match |
179 | // either 'On' or 'Off'. |
180 | if (Loc == RequestedFeatures.end()) |
181 | return false; |
182 | // If a feature is specified, it must have exact match. |
183 | if (Loc->second != F.second) |
184 | return false; |
185 | } |
186 | return true; |
187 | } |
188 | |
189 | } // namespace clang |
190 | |