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 | std::optional<AArch64::FMVInfo> lookupFMVByID(AArch64::ArchExtKind ExtID) { |
52 | for (const AArch64::FMVInfo &Info : AArch64::getFMVInfo()) |
53 | if (Info.ID == ExtID) |
54 | return Info; |
55 | return {}; |
56 | } |
57 | |
58 | uint64_t AArch64::getFMVPriority(ArrayRef<StringRef> Features) { |
59 | // Transitively enable the Arch Extensions which correspond to each feature. |
60 | ExtensionSet FeatureBits; |
61 | for (const StringRef Feature : Features) { |
62 | std::optional<FMVInfo> FMV = parseFMVExtension(Extension: Feature); |
63 | if (!FMV && Feature.starts_with(Prefix: '+')) { |
64 | if (std::optional<ExtensionInfo> Info = targetFeatureToExtension(TargetFeature: Feature)) |
65 | FMV = lookupFMVByID(ExtID: Info->ID); |
66 | } |
67 | if (FMV && FMV->ID) |
68 | FeatureBits.enable(E: *FMV->ID); |
69 | } |
70 | |
71 | // Construct a bitmask for all the transitively enabled Arch Extensions. |
72 | uint64_t PriorityMask = 0; |
73 | for (const FMVInfo &Info : getFMVInfo()) |
74 | if (Info.ID && FeatureBits.Enabled.test(I: *Info.ID)) |
75 | PriorityMask |= (1ULL << Info.PriorityBit); |
76 | |
77 | return PriorityMask; |
78 | } |
79 | |
80 | uint64_t AArch64::getCpuSupportsMask(ArrayRef<StringRef> Features) { |
81 | // Transitively enable the Arch Extensions which correspond to each feature. |
82 | ExtensionSet FeatureBits; |
83 | for (const StringRef Feature : Features) |
84 | if (std::optional<FMVInfo> Info = parseFMVExtension(Extension: Feature)) |
85 | if (Info->ID) |
86 | FeatureBits.enable(E: *Info->ID); |
87 | |
88 | // Construct a bitmask for all the transitively enabled Arch Extensions. |
89 | uint64_t FeaturesMask = 0; |
90 | for (const FMVInfo &Info : getFMVInfo()) |
91 | if (Info.ID && FeatureBits.Enabled.test(I: *Info.ID)) |
92 | FeaturesMask |= (1ULL << Info.FeatureBit); |
93 | |
94 | return FeaturesMask; |
95 | } |
96 | |
97 | bool AArch64::getExtensionFeatures( |
98 | const AArch64::ExtensionBitset &InputExts, |
99 | std::vector<StringRef> &Features) { |
100 | for (const auto &E : Extensions) |
101 | /* INVALID and NONE have no feature name. */ |
102 | if (InputExts.test(I: E.ID) && !E.PosTargetFeature.empty()) |
103 | Features.push_back(x: E.PosTargetFeature); |
104 | |
105 | return true; |
106 | } |
107 | |
108 | StringRef AArch64::resolveCPUAlias(StringRef Name) { |
109 | for (const auto &A : CpuAliases) |
110 | if (A.AltName == Name) |
111 | return A.Name; |
112 | return Name; |
113 | } |
114 | |
115 | StringRef AArch64::getArchExtFeature(StringRef ArchExt) { |
116 | bool IsNegated = ArchExt.starts_with(Prefix: "no" ); |
117 | StringRef ArchExtBase = IsNegated ? ArchExt.drop_front(N: 2) : ArchExt; |
118 | |
119 | if (auto AE = parseArchExtension(Extension: ArchExtBase)) { |
120 | assert(!(AE.has_value() && AE->NegTargetFeature.empty())); |
121 | return IsNegated ? AE->NegTargetFeature : AE->PosTargetFeature; |
122 | } |
123 | |
124 | return StringRef(); |
125 | } |
126 | |
127 | void AArch64::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values) { |
128 | for (const auto &C : CpuInfos) |
129 | Values.push_back(Elt: C.Name); |
130 | |
131 | for (const auto &Alias : CpuAliases) |
132 | // The apple-latest alias is backend only, do not expose it to clang's -mcpu. |
133 | if (Alias.AltName != "apple-latest" ) |
134 | Values.push_back(Elt: Alias.AltName); |
135 | |
136 | llvm::sort(C&: Values); |
137 | } |
138 | |
139 | bool AArch64::isX18ReservedByDefault(const Triple &TT) { |
140 | return TT.isAndroid() || TT.isOSDarwin() || TT.isOSFuchsia() || |
141 | TT.isOSWindows() || TT.isOHOSFamily(); |
142 | } |
143 | |
144 | // Allows partial match, ex. "v8a" matches "armv8a". |
145 | const AArch64::ArchInfo *AArch64::parseArch(StringRef Arch) { |
146 | Arch = llvm::ARM::getCanonicalArchName(Arch); |
147 | if (checkArchVersion(Arch) < 8) |
148 | return {}; |
149 | |
150 | StringRef Syn = llvm::ARM::getArchSynonym(Arch); |
151 | for (const auto *A : ArchInfos) { |
152 | if (A->Name.ends_with(Suffix: Syn)) |
153 | return A; |
154 | } |
155 | return {}; |
156 | } |
157 | |
158 | std::optional<AArch64::ExtensionInfo> |
159 | AArch64::parseArchExtension(StringRef ArchExt) { |
160 | if (ArchExt.empty()) |
161 | return {}; |
162 | for (const auto &A : Extensions) { |
163 | if (ArchExt == A.UserVisibleName || ArchExt == A.Alias) |
164 | return A; |
165 | } |
166 | return {}; |
167 | } |
168 | |
169 | std::optional<AArch64::FMVInfo> AArch64::parseFMVExtension(StringRef FMVExt) { |
170 | // FIXME introduce general alias functionality, or remove this exception. |
171 | if (FMVExt == "rdma" ) |
172 | FMVExt = "rdm" ; |
173 | |
174 | for (const auto &I : getFMVInfo()) { |
175 | if (FMVExt == I.Name) |
176 | return I; |
177 | } |
178 | return {}; |
179 | } |
180 | |
181 | std::optional<AArch64::ExtensionInfo> |
182 | AArch64::targetFeatureToExtension(StringRef TargetFeature) { |
183 | for (const auto &E : Extensions) |
184 | if (TargetFeature == E.PosTargetFeature || |
185 | TargetFeature == E.NegTargetFeature) |
186 | return E; |
187 | return {}; |
188 | } |
189 | |
190 | std::optional<AArch64::CpuInfo> AArch64::parseCpu(StringRef Name) { |
191 | // Resolve aliases first. |
192 | Name = resolveCPUAlias(Name); |
193 | |
194 | // Then find the CPU name. |
195 | for (const auto &C : CpuInfos) |
196 | if (Name == C.Name) |
197 | return C; |
198 | |
199 | return {}; |
200 | } |
201 | |
202 | void AArch64::PrintSupportedExtensions() { |
203 | outs() << "All available -march extensions for AArch64\n\n" |
204 | << " " << left_justify(Str: "Name" , Width: 20) |
205 | << left_justify(Str: "Architecture Feature(s)" , Width: 55) |
206 | << "Description\n" ; |
207 | for (const auto &Ext : Extensions) { |
208 | // Extensions without a feature cannot be used with -march. |
209 | if (!Ext.UserVisibleName.empty() && !Ext.PosTargetFeature.empty()) { |
210 | outs() << " " |
211 | << format(Fmt: Ext.Description.empty() ? "%-20s%s\n" : "%-20s%-55s%s\n" , |
212 | Vals: Ext.UserVisibleName.str().c_str(), |
213 | Vals: Ext.ArchFeatureName.str().c_str(), |
214 | Vals: Ext.Description.str().c_str()); |
215 | } |
216 | } |
217 | } |
218 | |
219 | void |
220 | AArch64::printEnabledExtensions(const std::set<StringRef> &EnabledFeatureNames) { |
221 | outs() << "Extensions enabled for the given AArch64 target\n\n" |
222 | << " " << left_justify(Str: "Architecture Feature(s)" , Width: 55) |
223 | << "Description\n" ; |
224 | std::vector<ExtensionInfo> EnabledExtensionsInfo; |
225 | for (const auto &FeatureName : EnabledFeatureNames) { |
226 | std::string PosFeatureName = '+' + FeatureName.str(); |
227 | if (auto ExtInfo = targetFeatureToExtension(TargetFeature: PosFeatureName)) |
228 | EnabledExtensionsInfo.push_back(x: *ExtInfo); |
229 | } |
230 | |
231 | std::sort(first: EnabledExtensionsInfo.begin(), last: EnabledExtensionsInfo.end(), |
232 | comp: [](const ExtensionInfo &Lhs, const ExtensionInfo &Rhs) { |
233 | return Lhs.ArchFeatureName < Rhs.ArchFeatureName; |
234 | }); |
235 | |
236 | for (const auto &Ext : EnabledExtensionsInfo) { |
237 | outs() << " " |
238 | << format(Fmt: "%-55s%s\n" , |
239 | Vals: Ext.ArchFeatureName.str().c_str(), |
240 | Vals: Ext.Description.str().c_str()); |
241 | } |
242 | } |
243 | |
244 | const llvm::AArch64::ExtensionInfo & |
245 | lookupExtensionByID(llvm::AArch64::ArchExtKind ExtID) { |
246 | for (const auto &E : llvm::AArch64::Extensions) |
247 | if (E.ID == ExtID) |
248 | return E; |
249 | llvm_unreachable("Invalid extension ID" ); |
250 | } |
251 | |
252 | void AArch64::ExtensionSet::enable(ArchExtKind E) { |
253 | if (Enabled.test(I: E)) |
254 | return; |
255 | |
256 | LLVM_DEBUG(llvm::dbgs() << "Enable " << lookupExtensionByID(E).UserVisibleName << "\n" ); |
257 | |
258 | Touched.set(E); |
259 | Enabled.set(E); |
260 | |
261 | // Recursively enable all features that this one depends on. This handles all |
262 | // of the simple cases, where the behaviour doesn't depend on the base |
263 | // architecture version. |
264 | for (auto Dep : ExtensionDependencies) |
265 | if (E == Dep.Later) |
266 | enable(E: Dep.Earlier); |
267 | |
268 | // Special cases for dependencies which vary depending on the base |
269 | // architecture version. |
270 | if (BaseArch) { |
271 | // +fp16 implies +fp16fml for v8.4A+, but not v9.0-A+ |
272 | if (E == AEK_FP16 && BaseArch->is_superset(Other: ARMV8_4A) && |
273 | !BaseArch->is_superset(Other: ARMV9A)) |
274 | enable(E: AEK_FP16FML); |
275 | |
276 | // For v8.4A+ and v9.0A+, +crypto also enables +sha3 and +sm4. |
277 | if (E == AEK_CRYPTO && BaseArch->is_superset(Other: ARMV8_4A)) { |
278 | enable(E: AEK_SHA3); |
279 | enable(E: AEK_SM4); |
280 | } |
281 | } |
282 | } |
283 | |
284 | void AArch64::ExtensionSet::disable(ArchExtKind E) { |
285 | // -crypto always disables aes, sha2, sha3 and sm4, even for architectures |
286 | // where the latter two would not be enabled by +crypto. |
287 | if (E == AEK_CRYPTO) { |
288 | disable(E: AEK_AES); |
289 | disable(E: AEK_SHA2); |
290 | disable(E: AEK_SHA3); |
291 | disable(E: AEK_SM4); |
292 | } |
293 | |
294 | // sve2-aes was historically associated with both FEAT_SVE2 and FEAT_SVE_AES, |
295 | // the latter is now associated with sve-aes and sve2-aes has become shorthand |
296 | // for +sve2+sve-aes. For backwards compatibility, when we disable sve2-aes we |
297 | // must also disable sve-aes. |
298 | if (E == AEK_SVE2AES) |
299 | disable(E: AEK_SVEAES); |
300 | |
301 | // sve2-sha3 was historically associated with both FEAT_SVE2 and |
302 | // FEAT_SVE_SHA3, the latter is now associated with sve-sha3 and sve2-sha3 has |
303 | // become shorthand for +sve2+sve-sha3. For backwards compatibility, when we |
304 | // disable sve2-sha3 we must also disable sve-sha3. |
305 | if (E == AEK_SVE2SHA3) |
306 | disable(E: AEK_SVESHA3); |
307 | |
308 | if (E == AEK_SVE2BITPERM){ |
309 | disable(E: AEK_SVEBITPERM); |
310 | disable(E: AEK_SVE2); |
311 | } |
312 | |
313 | if (!Enabled.test(I: E)) |
314 | return; |
315 | |
316 | LLVM_DEBUG(llvm::dbgs() << "Disable " << lookupExtensionByID(E).UserVisibleName << "\n" ); |
317 | |
318 | Touched.set(E); |
319 | Enabled.reset(I: E); |
320 | |
321 | // Recursively disable all features that depends on this one. |
322 | for (auto Dep : ExtensionDependencies) |
323 | if (E == Dep.Earlier) |
324 | disable(E: Dep.Later); |
325 | } |
326 | |
327 | void AArch64::ExtensionSet::addCPUDefaults(const CpuInfo &CPU) { |
328 | LLVM_DEBUG(llvm::dbgs() << "addCPUDefaults(" << CPU.Name << ")\n" ); |
329 | BaseArch = &CPU.Arch; |
330 | |
331 | AArch64::ExtensionBitset CPUExtensions = CPU.getImpliedExtensions(); |
332 | for (const auto &E : Extensions) |
333 | if (CPUExtensions.test(I: E.ID)) |
334 | enable(E: E.ID); |
335 | } |
336 | |
337 | void AArch64::ExtensionSet::addArchDefaults(const ArchInfo &Arch) { |
338 | LLVM_DEBUG(llvm::dbgs() << "addArchDefaults(" << Arch.Name << ")\n" ); |
339 | BaseArch = &Arch; |
340 | |
341 | for (const auto &E : Extensions) |
342 | if (Arch.DefaultExts.test(I: E.ID)) |
343 | enable(E: E.ID); |
344 | } |
345 | |
346 | bool AArch64::ExtensionSet::parseModifier(StringRef Modifier, |
347 | const bool AllowNoDashForm) { |
348 | LLVM_DEBUG(llvm::dbgs() << "parseModifier(" << Modifier << ")\n" ); |
349 | |
350 | size_t NChars = 0; |
351 | // The "no-feat" form is allowed in the target attribute but nowhere else. |
352 | if (AllowNoDashForm && Modifier.starts_with(Prefix: "no-" )) |
353 | NChars = 3; |
354 | else if (Modifier.starts_with(Prefix: "no" )) |
355 | NChars = 2; |
356 | bool IsNegated = NChars != 0; |
357 | StringRef ArchExt = Modifier.drop_front(N: NChars); |
358 | |
359 | if (auto AE = parseArchExtension(ArchExt)) { |
360 | if (AE->PosTargetFeature.empty() || AE->NegTargetFeature.empty()) |
361 | return false; |
362 | if (IsNegated) |
363 | disable(E: AE->ID); |
364 | else |
365 | enable(E: AE->ID); |
366 | return true; |
367 | } |
368 | return false; |
369 | } |
370 | |
371 | void AArch64::ExtensionSet::reconstructFromParsedFeatures( |
372 | const std::vector<std::string> &Features, |
373 | std::vector<std::string> &NonExtensions) { |
374 | assert(Touched.none() && "Bitset already initialized" ); |
375 | for (auto &F : Features) { |
376 | bool IsNegated = F[0] == '-'; |
377 | if (auto AE = targetFeatureToExtension(TargetFeature: F)) { |
378 | Touched.set(AE->ID); |
379 | if (IsNegated) |
380 | Enabled.reset(I: AE->ID); |
381 | else |
382 | Enabled.set(AE->ID); |
383 | continue; |
384 | } |
385 | NonExtensions.push_back(x: F); |
386 | } |
387 | } |
388 | |
389 | void AArch64::ExtensionSet::dump() const { |
390 | std::vector<StringRef> Features; |
391 | toLLVMFeatureList(Features); |
392 | for (StringRef F : Features) |
393 | llvm::outs() << F << " " ; |
394 | llvm::outs() << "\n" ; |
395 | } |
396 | |
397 | const AArch64::ExtensionInfo & |
398 | AArch64::getExtensionByID(AArch64::ArchExtKind ExtID) { |
399 | return lookupExtensionByID(ExtID); |
400 | } |
401 | |