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(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(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(E.ID) && !E.PosTargetFeature.empty()) 66 Features.push_back(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("no"); 80 StringRef ArchExtBase = IsNegated ? ArchExt.drop_front(2) : ArchExt; 81 82 if (auto AE = parseArchExtension(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(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(Alias.AltName); 98 99 llvm::sort(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(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("Name", 20) 167 << left_justify("Architecture Feature(s)", 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(Ext.Description.empty() ? "%-20s%s\n" : "%-20s%-55s%s\n", 174 Ext.UserVisibleName.str().c_str(), 175 Ext.ArchFeatureName.str().c_str(), 176 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("Architecture Feature(s)", 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(PosFeatureName)) 190 EnabledExtensionsInfo.push_back(*ExtInfo); 191 } 192 193 std::sort(EnabledExtensionsInfo.begin(), EnabledExtensionsInfo.end(), 194 [](const ExtensionInfo &Lhs, const ExtensionInfo &Rhs) { 195 return Lhs.ArchFeatureName < Rhs.ArchFeatureName; 196 }); 197 198 for (const auto &Ext : EnabledExtensionsInfo) { 199 outs() << " " 200 << format("%-55s%s\n", 201 Ext.ArchFeatureName.str().c_str(), 202 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(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(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(ARMV8_4A) && 235 !BaseArch->is_superset(ARMV9A)) 236 enable(AEK_FP16FML); 237 238 // For v8.4A+ and v9.0A+, +crypto also enables +sha3 and +sm4. 239 if (E == AEK_CRYPTO && BaseArch->is_superset(ARMV8_4A)) { 240 enable(AEK_SHA3); 241 enable(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(AEK_AES); 251 disable(AEK_SHA2); 252 disable(AEK_SHA3); 253 disable(AEK_SM4); 254 } 255 256 if (!Enabled.test(E)) 257 return; 258 259 LLVM_DEBUG(llvm::dbgs() << "Disable " << lookupExtensionByID(E).UserVisibleName << "\n"); 260 261 Touched.set(E); 262 Enabled.reset(E); 263 264 // Recursively disable all features that depends on this one. 265 for (auto Dep : ExtensionDependencies) 266 if (E == Dep.Earlier) 267 disable(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(E.ID)) 277 enable(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(E.ID)) 286 enable(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("no-")) 296 NChars = 3; 297 else if (Modifier.starts_with("no")) 298 NChars = 2; 299 bool IsNegated = NChars != 0; 300 StringRef ArchExt = Modifier.drop_front(NChars); 301 302 if (auto AE = parseArchExtension(ArchExt)) { 303 if (AE->PosTargetFeature.empty() || AE->NegTargetFeature.empty()) 304 return false; 305 if (IsNegated) 306 disable(AE->ID); 307 else 308 enable(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(F)) { 321 Touched.set(AE->ID); 322 if (IsNegated) 323 Enabled.reset(AE->ID); 324 else 325 Enabled.set(AE->ID); 326 continue; 327 } 328 NonExtensions.push_back(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