1 | //===-- AArch64TargetParser - Parser for AArch64 features -------*- C++ -*-===// |
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 | // This file implements a target parser to recognise AArch64 hardware features |
10 | // such as FPU/CPU/ARCH and extension names. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/TargetParser/AArch64TargetParser.h" |
15 | #include "llvm/Support/Debug.h" |
16 | #include "llvm/Support/Format.h" |
17 | #include "llvm/Support/raw_ostream.h" |
18 | #include "llvm/TargetParser/ARMTargetParserCommon.h" |
19 | #include "llvm/TargetParser/Triple.h" |
20 | #include <cctype> |
21 | #include <vector> |
22 | |
23 | #define DEBUG_TYPE "target-parser" |
24 | |
25 | using namespace llvm; |
26 | |
27 | #define EMIT_FMV_INFO |
28 | #include "llvm/TargetParser/AArch64TargetParserDef.inc" |
29 | |
30 | static unsigned checkArchVersion(llvm::StringRef Arch) { |
31 | if (Arch.size() >= 2 && Arch[0] == 'v' && std::isdigit(Arch[1])) |
32 | return (Arch[1] - 48); |
33 | return 0; |
34 | } |
35 | |
36 | const AArch64::ArchInfo *AArch64::getArchForCpu(StringRef CPU) { |
37 | // Note: this now takes cpu aliases into account |
38 | std::optional<CpuInfo> Cpu = parseCpu(Name: CPU); |
39 | if (!Cpu) |
40 | return nullptr; |
41 | return &Cpu->Arch; |
42 | } |
43 | |
44 | std::optional<AArch64::ArchInfo> AArch64::ArchInfo::findBySubArch(StringRef SubArch) { |
45 | for (const auto *A : AArch64::ArchInfos) |
46 | if (A->getSubArch() == SubArch) |
47 | return *A; |
48 | return {}; |
49 | } |
50 | |
51 | uint64_t AArch64::getCpuSupportsMask(ArrayRef<StringRef> FeatureStrs) { |
52 | uint64_t FeaturesMask = 0; |
53 | for (const StringRef &FeatureStr : FeatureStrs) { |
54 | if (auto Ext = parseFMVExtension(Extension: FeatureStr)) |
55 | FeaturesMask |= (1ULL << Ext->Bit); |
56 | } |
57 | return FeaturesMask; |
58 | } |
59 | |
60 | bool AArch64::getExtensionFeatures( |
61 | const AArch64::ExtensionBitset &InputExts, |
62 | std::vector<StringRef> &Features) { |
63 | for (const auto &E : Extensions) |
64 | /* INVALID and NONE have no feature name. */ |
65 | if (InputExts.test(I: E.ID) && !E.PosTargetFeature.empty()) |
66 | Features.push_back(x: E.PosTargetFeature); |
67 | |
68 | return true; |
69 | } |
70 | |
71 | StringRef AArch64::resolveCPUAlias(StringRef Name) { |
72 | for (const auto &A : CpuAliases) |
73 | if (A.AltName == Name) |
74 | return A.Name; |
75 | return Name; |
76 | } |
77 | |
78 | StringRef AArch64::getArchExtFeature(StringRef ArchExt) { |
79 | bool IsNegated = ArchExt.starts_with(Prefix: "no" ); |
80 | StringRef ArchExtBase = IsNegated ? ArchExt.drop_front(N: 2) : ArchExt; |
81 | |
82 | if (auto AE = parseArchExtension(Extension: ArchExtBase)) { |
83 | assert(!(AE.has_value() && AE->NegTargetFeature.empty())); |
84 | return IsNegated ? AE->NegTargetFeature : AE->PosTargetFeature; |
85 | } |
86 | |
87 | return StringRef(); |
88 | } |
89 | |
90 | void AArch64::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values) { |
91 | for (const auto &C : CpuInfos) |
92 | Values.push_back(Elt: C.Name); |
93 | |
94 | for (const auto &Alias : CpuAliases) |
95 | // The apple-latest alias is backend only, do not expose it to clang's -mcpu. |
96 | if (Alias.AltName != "apple-latest" ) |
97 | Values.push_back(Elt: Alias.AltName); |
98 | |
99 | llvm::sort(C&: Values); |
100 | } |
101 | |
102 | bool AArch64::isX18ReservedByDefault(const Triple &TT) { |
103 | return TT.isAndroid() || TT.isOSDarwin() || TT.isOSFuchsia() || |
104 | TT.isOSWindows() || TT.isOHOSFamily(); |
105 | } |
106 | |
107 | // Allows partial match, ex. "v8a" matches "armv8a". |
108 | const AArch64::ArchInfo *AArch64::parseArch(StringRef Arch) { |
109 | Arch = llvm::ARM::getCanonicalArchName(Arch); |
110 | if (checkArchVersion(Arch) < 8) |
111 | return {}; |
112 | |
113 | StringRef Syn = llvm::ARM::getArchSynonym(Arch); |
114 | for (const auto *A : ArchInfos) { |
115 | if (A->Name.ends_with(Suffix: Syn)) |
116 | return A; |
117 | } |
118 | return {}; |
119 | } |
120 | |
121 | std::optional<AArch64::ExtensionInfo> |
122 | AArch64::parseArchExtension(StringRef ArchExt) { |
123 | if (ArchExt.empty()) |
124 | return {}; |
125 | for (const auto &A : Extensions) { |
126 | if (ArchExt == A.UserVisibleName || ArchExt == A.Alias) |
127 | return A; |
128 | } |
129 | return {}; |
130 | } |
131 | |
132 | std::optional<AArch64::FMVInfo> AArch64::parseFMVExtension(StringRef FMVExt) { |
133 | // FIXME introduce general alias functionality, or remove this exception. |
134 | if (FMVExt == "rdma" ) |
135 | FMVExt = "rdm" ; |
136 | |
137 | for (const auto &I : getFMVInfo()) { |
138 | if (FMVExt == I.Name) |
139 | return I; |
140 | } |
141 | return {}; |
142 | } |
143 | |
144 | std::optional<AArch64::ExtensionInfo> |
145 | AArch64::targetFeatureToExtension(StringRef TargetFeature) { |
146 | for (const auto &E : Extensions) |
147 | if (TargetFeature == E.PosTargetFeature) |
148 | return E; |
149 | return {}; |
150 | } |
151 | |
152 | std::optional<AArch64::CpuInfo> AArch64::parseCpu(StringRef Name) { |
153 | // Resolve aliases first. |
154 | Name = resolveCPUAlias(Name); |
155 | |
156 | // Then find the CPU name. |
157 | for (const auto &C : CpuInfos) |
158 | if (Name == C.Name) |
159 | return C; |
160 | |
161 | return {}; |
162 | } |
163 | |
164 | void AArch64::PrintSupportedExtensions() { |
165 | outs() << "All available -march extensions for AArch64\n\n" |
166 | << " " << left_justify(Str: "Name" , Width: 20) |
167 | << left_justify(Str: "Architecture Feature(s)" , Width: 55) |
168 | << "Description\n" ; |
169 | for (const auto &Ext : Extensions) { |
170 | // Extensions without a feature cannot be used with -march. |
171 | if (!Ext.UserVisibleName.empty() && !Ext.PosTargetFeature.empty()) { |
172 | outs() << " " |
173 | << format(Fmt: Ext.Description.empty() ? "%-20s%s\n" : "%-20s%-55s%s\n" , |
174 | Vals: Ext.UserVisibleName.str().c_str(), |
175 | Vals: Ext.ArchFeatureName.str().c_str(), |
176 | Vals: Ext.Description.str().c_str()); |
177 | } |
178 | } |
179 | } |
180 | |
181 | void |
182 | AArch64::printEnabledExtensions(const std::set<StringRef> &EnabledFeatureNames) { |
183 | outs() << "Extensions enabled for the given AArch64 target\n\n" |
184 | << " " << left_justify(Str: "Architecture Feature(s)" , Width: 55) |
185 | << "Description\n" ; |
186 | std::vector<ExtensionInfo> EnabledExtensionsInfo; |
187 | for (const auto &FeatureName : EnabledFeatureNames) { |
188 | std::string PosFeatureName = '+' + FeatureName.str(); |
189 | if (auto ExtInfo = targetFeatureToExtension(TargetFeature: PosFeatureName)) |
190 | EnabledExtensionsInfo.push_back(x: *ExtInfo); |
191 | } |
192 | |
193 | std::sort(first: EnabledExtensionsInfo.begin(), last: EnabledExtensionsInfo.end(), |
194 | comp: [](const ExtensionInfo &Lhs, const ExtensionInfo &Rhs) { |
195 | return Lhs.ArchFeatureName < Rhs.ArchFeatureName; |
196 | }); |
197 | |
198 | for (const auto &Ext : EnabledExtensionsInfo) { |
199 | outs() << " " |
200 | << format(Fmt: "%-55s%s\n" , |
201 | Vals: Ext.ArchFeatureName.str().c_str(), |
202 | Vals: Ext.Description.str().c_str()); |
203 | } |
204 | } |
205 | |
206 | const llvm::AArch64::ExtensionInfo & |
207 | lookupExtensionByID(llvm::AArch64::ArchExtKind ExtID) { |
208 | for (const auto &E : llvm::AArch64::Extensions) |
209 | if (E.ID == ExtID) |
210 | return E; |
211 | llvm_unreachable("Invalid extension ID" ); |
212 | } |
213 | |
214 | void AArch64::ExtensionSet::enable(ArchExtKind E) { |
215 | if (Enabled.test(I: E)) |
216 | return; |
217 | |
218 | LLVM_DEBUG(llvm::dbgs() << "Enable " << lookupExtensionByID(E).UserVisibleName << "\n" ); |
219 | |
220 | Touched.set(E); |
221 | Enabled.set(E); |
222 | |
223 | // Recursively enable all features that this one depends on. This handles all |
224 | // of the simple cases, where the behaviour doesn't depend on the base |
225 | // architecture version. |
226 | for (auto Dep : ExtensionDependencies) |
227 | if (E == Dep.Later) |
228 | enable(E: Dep.Earlier); |
229 | |
230 | // Special cases for dependencies which vary depending on the base |
231 | // architecture version. |
232 | if (BaseArch) { |
233 | // +fp16 implies +fp16fml for v8.4A+, but not v9.0-A+ |
234 | if (E == AEK_FP16 && BaseArch->is_superset(Other: ARMV8_4A) && |
235 | !BaseArch->is_superset(Other: ARMV9A)) |
236 | enable(E: AEK_FP16FML); |
237 | |
238 | // For v8.4A+ and v9.0A+, +crypto also enables +sha3 and +sm4. |
239 | if (E == AEK_CRYPTO && BaseArch->is_superset(Other: ARMV8_4A)) { |
240 | enable(E: AEK_SHA3); |
241 | enable(E: AEK_SM4); |
242 | } |
243 | } |
244 | } |
245 | |
246 | void AArch64::ExtensionSet::disable(ArchExtKind E) { |
247 | // -crypto always disables aes, sha2, sha3 and sm4, even for architectures |
248 | // where the latter two would not be enabled by +crypto. |
249 | if (E == AEK_CRYPTO) { |
250 | disable(E: AEK_AES); |
251 | disable(E: AEK_SHA2); |
252 | disable(E: AEK_SHA3); |
253 | disable(E: AEK_SM4); |
254 | } |
255 | |
256 | if (!Enabled.test(I: E)) |
257 | return; |
258 | |
259 | LLVM_DEBUG(llvm::dbgs() << "Disable " << lookupExtensionByID(E).UserVisibleName << "\n" ); |
260 | |
261 | Touched.set(E); |
262 | Enabled.reset(I: E); |
263 | |
264 | // Recursively disable all features that depends on this one. |
265 | for (auto Dep : ExtensionDependencies) |
266 | if (E == Dep.Earlier) |
267 | disable(E: Dep.Later); |
268 | } |
269 | |
270 | void AArch64::ExtensionSet::addCPUDefaults(const CpuInfo &CPU) { |
271 | LLVM_DEBUG(llvm::dbgs() << "addCPUDefaults(" << CPU.Name << ")\n" ); |
272 | BaseArch = &CPU.Arch; |
273 | |
274 | AArch64::ExtensionBitset CPUExtensions = CPU.getImpliedExtensions(); |
275 | for (const auto &E : Extensions) |
276 | if (CPUExtensions.test(I: E.ID)) |
277 | enable(E: E.ID); |
278 | } |
279 | |
280 | void AArch64::ExtensionSet::addArchDefaults(const ArchInfo &Arch) { |
281 | LLVM_DEBUG(llvm::dbgs() << "addArchDefaults(" << Arch.Name << ")\n" ); |
282 | BaseArch = &Arch; |
283 | |
284 | for (const auto &E : Extensions) |
285 | if (Arch.DefaultExts.test(I: E.ID)) |
286 | enable(E: E.ID); |
287 | } |
288 | |
289 | bool AArch64::ExtensionSet::parseModifier(StringRef Modifier, |
290 | const bool AllowNoDashForm) { |
291 | LLVM_DEBUG(llvm::dbgs() << "parseModifier(" << Modifier << ")\n" ); |
292 | |
293 | size_t NChars = 0; |
294 | // The "no-feat" form is allowed in the target attribute but nowhere else. |
295 | if (AllowNoDashForm && Modifier.starts_with(Prefix: "no-" )) |
296 | NChars = 3; |
297 | else if (Modifier.starts_with(Prefix: "no" )) |
298 | NChars = 2; |
299 | bool IsNegated = NChars != 0; |
300 | StringRef ArchExt = Modifier.drop_front(N: NChars); |
301 | |
302 | if (auto AE = parseArchExtension(ArchExt)) { |
303 | if (AE->PosTargetFeature.empty() || AE->NegTargetFeature.empty()) |
304 | return false; |
305 | if (IsNegated) |
306 | disable(E: AE->ID); |
307 | else |
308 | enable(E: AE->ID); |
309 | return true; |
310 | } |
311 | return false; |
312 | } |
313 | |
314 | void AArch64::ExtensionSet::reconstructFromParsedFeatures( |
315 | const std::vector<std::string> &Features, |
316 | std::vector<std::string> &NonExtensions) { |
317 | assert(Touched.none() && "Bitset already initialized" ); |
318 | for (auto &F : Features) { |
319 | bool IsNegated = F[0] == '-'; |
320 | if (auto AE = targetFeatureToExtension(TargetFeature: F)) { |
321 | Touched.set(AE->ID); |
322 | if (IsNegated) |
323 | Enabled.reset(I: AE->ID); |
324 | else |
325 | Enabled.set(AE->ID); |
326 | continue; |
327 | } |
328 | NonExtensions.push_back(x: F); |
329 | } |
330 | } |
331 | |
332 | void AArch64::ExtensionSet::dump() const { |
333 | std::vector<StringRef> Features; |
334 | toLLVMFeatureList(Features); |
335 | for (StringRef F : Features) |
336 | llvm::outs() << F << " " ; |
337 | llvm::outs() << "\n" ; |
338 | } |
339 | |
340 | const AArch64::ExtensionInfo & |
341 | AArch64::getExtensionByID(AArch64::ArchExtKind ExtID) { |
342 | return lookupExtensionByID(ExtID); |
343 | } |
344 | |