1bdd1243dSDimitry Andric //===-- AArch64TargetParser - Parser for AArch64 features -------*- C++ -*-===// 2bdd1243dSDimitry Andric // 3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bdd1243dSDimitry Andric // 7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8bdd1243dSDimitry Andric // 9bdd1243dSDimitry Andric // This file implements a target parser to recognise AArch64 hardware features 10bdd1243dSDimitry Andric // such as FPU/CPU/ARCH and extension names. 11bdd1243dSDimitry Andric // 12bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 13bdd1243dSDimitry Andric 14bdd1243dSDimitry Andric #include "llvm/TargetParser/AArch64TargetParser.h" 157a6dacacSDimitry Andric #include "llvm/Support/Debug.h" 165f757f3fSDimitry Andric #include "llvm/Support/Format.h" 175f757f3fSDimitry Andric #include "llvm/Support/raw_ostream.h" 18bdd1243dSDimitry Andric #include "llvm/TargetParser/ARMTargetParserCommon.h" 19bdd1243dSDimitry Andric #include "llvm/TargetParser/Triple.h" 20bdd1243dSDimitry Andric #include <cctype> 21*0fca6ea1SDimitry Andric #include <vector> 22bdd1243dSDimitry Andric 237a6dacacSDimitry Andric #define DEBUG_TYPE "target-parser" 247a6dacacSDimitry Andric 25bdd1243dSDimitry Andric using namespace llvm; 26bdd1243dSDimitry Andric 27*0fca6ea1SDimitry Andric #define EMIT_FMV_INFO 28*0fca6ea1SDimitry Andric #include "llvm/TargetParser/AArch64TargetParserDef.inc" 29*0fca6ea1SDimitry Andric 30bdd1243dSDimitry Andric static unsigned checkArchVersion(llvm::StringRef Arch) { 31bdd1243dSDimitry Andric if (Arch.size() >= 2 && Arch[0] == 'v' && std::isdigit(Arch[1])) 32bdd1243dSDimitry Andric return (Arch[1] - 48); 33bdd1243dSDimitry Andric return 0; 34bdd1243dSDimitry Andric } 35bdd1243dSDimitry Andric 367a6dacacSDimitry Andric const AArch64::ArchInfo *AArch64::getArchForCpu(StringRef CPU) { 37bdd1243dSDimitry Andric // Note: this now takes cpu aliases into account 3806c3fb27SDimitry Andric std::optional<CpuInfo> Cpu = parseCpu(CPU); 3906c3fb27SDimitry Andric if (!Cpu) 407a6dacacSDimitry Andric return nullptr; 417a6dacacSDimitry Andric return &Cpu->Arch; 42bdd1243dSDimitry Andric } 43bdd1243dSDimitry Andric 4406c3fb27SDimitry Andric std::optional<AArch64::ArchInfo> AArch64::ArchInfo::findBySubArch(StringRef SubArch) { 45bdd1243dSDimitry Andric for (const auto *A : AArch64::ArchInfos) 46bdd1243dSDimitry Andric if (A->getSubArch() == SubArch) 47bdd1243dSDimitry Andric return *A; 4806c3fb27SDimitry Andric return {}; 49bdd1243dSDimitry Andric } 50bdd1243dSDimitry Andric 51bdd1243dSDimitry Andric uint64_t AArch64::getCpuSupportsMask(ArrayRef<StringRef> FeatureStrs) { 52bdd1243dSDimitry Andric uint64_t FeaturesMask = 0; 53bdd1243dSDimitry Andric for (const StringRef &FeatureStr : FeatureStrs) { 54*0fca6ea1SDimitry Andric if (auto Ext = parseFMVExtension(FeatureStr)) 55*0fca6ea1SDimitry Andric FeaturesMask |= (1ULL << Ext->Bit); 56bdd1243dSDimitry Andric } 57bdd1243dSDimitry Andric return FeaturesMask; 58bdd1243dSDimitry Andric } 59bdd1243dSDimitry Andric 605f757f3fSDimitry Andric bool AArch64::getExtensionFeatures( 615f757f3fSDimitry Andric const AArch64::ExtensionBitset &InputExts, 62bdd1243dSDimitry Andric std::vector<StringRef> &Features) { 63bdd1243dSDimitry Andric for (const auto &E : Extensions) 64bdd1243dSDimitry Andric /* INVALID and NONE have no feature name. */ 65*0fca6ea1SDimitry Andric if (InputExts.test(E.ID) && !E.PosTargetFeature.empty()) 66*0fca6ea1SDimitry Andric Features.push_back(E.PosTargetFeature); 67bdd1243dSDimitry Andric 68bdd1243dSDimitry Andric return true; 69bdd1243dSDimitry Andric } 70bdd1243dSDimitry Andric 71bdd1243dSDimitry Andric StringRef AArch64::resolveCPUAlias(StringRef Name) { 72bdd1243dSDimitry Andric for (const auto &A : CpuAliases) 73*0fca6ea1SDimitry Andric if (A.AltName == Name) 74bdd1243dSDimitry Andric return A.Name; 75bdd1243dSDimitry Andric return Name; 76bdd1243dSDimitry Andric } 77bdd1243dSDimitry Andric 78bdd1243dSDimitry Andric StringRef AArch64::getArchExtFeature(StringRef ArchExt) { 797a6dacacSDimitry Andric bool IsNegated = ArchExt.starts_with("no"); 807a6dacacSDimitry Andric StringRef ArchExtBase = IsNegated ? ArchExt.drop_front(2) : ArchExt; 817a6dacacSDimitry Andric 827a6dacacSDimitry Andric if (auto AE = parseArchExtension(ArchExtBase)) { 83*0fca6ea1SDimitry Andric assert(!(AE.has_value() && AE->NegTargetFeature.empty())); 84*0fca6ea1SDimitry Andric return IsNegated ? AE->NegTargetFeature : AE->PosTargetFeature; 85bdd1243dSDimitry Andric } 86bdd1243dSDimitry Andric 87bdd1243dSDimitry Andric return StringRef(); 88bdd1243dSDimitry Andric } 89bdd1243dSDimitry Andric 90bdd1243dSDimitry Andric void AArch64::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values) { 91bdd1243dSDimitry Andric for (const auto &C : CpuInfos) 92bdd1243dSDimitry Andric Values.push_back(C.Name); 93bdd1243dSDimitry Andric 94bdd1243dSDimitry Andric for (const auto &Alias : CpuAliases) 95*0fca6ea1SDimitry Andric // The apple-latest alias is backend only, do not expose it to clang's -mcpu. 96*0fca6ea1SDimitry Andric if (Alias.AltName != "apple-latest") 97*0fca6ea1SDimitry Andric Values.push_back(Alias.AltName); 98*0fca6ea1SDimitry Andric 99*0fca6ea1SDimitry Andric llvm::sort(Values); 100bdd1243dSDimitry Andric } 101bdd1243dSDimitry Andric 102bdd1243dSDimitry Andric bool AArch64::isX18ReservedByDefault(const Triple &TT) { 103bdd1243dSDimitry Andric return TT.isAndroid() || TT.isOSDarwin() || TT.isOSFuchsia() || 10406c3fb27SDimitry Andric TT.isOSWindows() || TT.isOHOSFamily(); 105bdd1243dSDimitry Andric } 106bdd1243dSDimitry Andric 107bdd1243dSDimitry Andric // Allows partial match, ex. "v8a" matches "armv8a". 1087a6dacacSDimitry Andric const AArch64::ArchInfo *AArch64::parseArch(StringRef Arch) { 109bdd1243dSDimitry Andric Arch = llvm::ARM::getCanonicalArchName(Arch); 110bdd1243dSDimitry Andric if (checkArchVersion(Arch) < 8) 11106c3fb27SDimitry Andric return {}; 112bdd1243dSDimitry Andric 113bdd1243dSDimitry Andric StringRef Syn = llvm::ARM::getArchSynonym(Arch); 114bdd1243dSDimitry Andric for (const auto *A : ArchInfos) { 1155f757f3fSDimitry Andric if (A->Name.ends_with(Syn)) 1167a6dacacSDimitry Andric return A; 117bdd1243dSDimitry Andric } 11806c3fb27SDimitry Andric return {}; 119bdd1243dSDimitry Andric } 120bdd1243dSDimitry Andric 121*0fca6ea1SDimitry Andric std::optional<AArch64::ExtensionInfo> 122*0fca6ea1SDimitry Andric AArch64::parseArchExtension(StringRef ArchExt) { 123*0fca6ea1SDimitry Andric if (ArchExt.empty()) 124*0fca6ea1SDimitry Andric return {}; 125bdd1243dSDimitry Andric for (const auto &A : Extensions) { 126*0fca6ea1SDimitry Andric if (ArchExt == A.UserVisibleName || ArchExt == A.Alias) 12706c3fb27SDimitry Andric return A; 128bdd1243dSDimitry Andric } 12906c3fb27SDimitry Andric return {}; 130bdd1243dSDimitry Andric } 131bdd1243dSDimitry Andric 132*0fca6ea1SDimitry Andric std::optional<AArch64::FMVInfo> AArch64::parseFMVExtension(StringRef FMVExt) { 133*0fca6ea1SDimitry Andric // FIXME introduce general alias functionality, or remove this exception. 134*0fca6ea1SDimitry Andric if (FMVExt == "rdma") 135*0fca6ea1SDimitry Andric FMVExt = "rdm"; 136*0fca6ea1SDimitry Andric 137*0fca6ea1SDimitry Andric for (const auto &I : getFMVInfo()) { 138*0fca6ea1SDimitry Andric if (FMVExt == I.Name) 139*0fca6ea1SDimitry Andric return I; 140*0fca6ea1SDimitry Andric } 141*0fca6ea1SDimitry Andric return {}; 142*0fca6ea1SDimitry Andric } 143*0fca6ea1SDimitry Andric 144*0fca6ea1SDimitry Andric std::optional<AArch64::ExtensionInfo> 145*0fca6ea1SDimitry Andric AArch64::targetFeatureToExtension(StringRef TargetFeature) { 146*0fca6ea1SDimitry Andric for (const auto &E : Extensions) 147*0fca6ea1SDimitry Andric if (TargetFeature == E.PosTargetFeature) 148*0fca6ea1SDimitry Andric return E; 149*0fca6ea1SDimitry Andric return {}; 150*0fca6ea1SDimitry Andric } 151*0fca6ea1SDimitry Andric 15206c3fb27SDimitry Andric std::optional<AArch64::CpuInfo> AArch64::parseCpu(StringRef Name) { 153bdd1243dSDimitry Andric // Resolve aliases first. 154bdd1243dSDimitry Andric Name = resolveCPUAlias(Name); 155bdd1243dSDimitry Andric 156bdd1243dSDimitry Andric // Then find the CPU name. 157bdd1243dSDimitry Andric for (const auto &C : CpuInfos) 158bdd1243dSDimitry Andric if (Name == C.Name) 159bdd1243dSDimitry Andric return C; 160bdd1243dSDimitry Andric 16106c3fb27SDimitry Andric return {}; 162bdd1243dSDimitry Andric } 1635f757f3fSDimitry Andric 164*0fca6ea1SDimitry Andric void AArch64::PrintSupportedExtensions() { 1655f757f3fSDimitry Andric outs() << "All available -march extensions for AArch64\n\n" 1665f757f3fSDimitry Andric << " " << left_justify("Name", 20) 167*0fca6ea1SDimitry Andric << left_justify("Architecture Feature(s)", 55) 168*0fca6ea1SDimitry Andric << "Description\n"; 1695f757f3fSDimitry Andric for (const auto &Ext : Extensions) { 1705f757f3fSDimitry Andric // Extensions without a feature cannot be used with -march. 171*0fca6ea1SDimitry Andric if (!Ext.UserVisibleName.empty() && !Ext.PosTargetFeature.empty()) { 1725f757f3fSDimitry Andric outs() << " " 173*0fca6ea1SDimitry Andric << format(Ext.Description.empty() ? "%-20s%s\n" : "%-20s%-55s%s\n", 174*0fca6ea1SDimitry Andric Ext.UserVisibleName.str().c_str(), 175*0fca6ea1SDimitry Andric Ext.ArchFeatureName.str().c_str(), 176*0fca6ea1SDimitry Andric Ext.Description.str().c_str()); 1775f757f3fSDimitry Andric } 1785f757f3fSDimitry Andric } 1795f757f3fSDimitry Andric } 1807a6dacacSDimitry Andric 181*0fca6ea1SDimitry Andric void 182*0fca6ea1SDimitry Andric AArch64::printEnabledExtensions(const std::set<StringRef> &EnabledFeatureNames) { 183*0fca6ea1SDimitry Andric outs() << "Extensions enabled for the given AArch64 target\n\n" 184*0fca6ea1SDimitry Andric << " " << left_justify("Architecture Feature(s)", 55) 185*0fca6ea1SDimitry Andric << "Description\n"; 186*0fca6ea1SDimitry Andric std::vector<ExtensionInfo> EnabledExtensionsInfo; 187*0fca6ea1SDimitry Andric for (const auto &FeatureName : EnabledFeatureNames) { 188*0fca6ea1SDimitry Andric std::string PosFeatureName = '+' + FeatureName.str(); 189*0fca6ea1SDimitry Andric if (auto ExtInfo = targetFeatureToExtension(PosFeatureName)) 190*0fca6ea1SDimitry Andric EnabledExtensionsInfo.push_back(*ExtInfo); 191*0fca6ea1SDimitry Andric } 192*0fca6ea1SDimitry Andric 193*0fca6ea1SDimitry Andric std::sort(EnabledExtensionsInfo.begin(), EnabledExtensionsInfo.end(), 194*0fca6ea1SDimitry Andric [](const ExtensionInfo &Lhs, const ExtensionInfo &Rhs) { 195*0fca6ea1SDimitry Andric return Lhs.ArchFeatureName < Rhs.ArchFeatureName; 196*0fca6ea1SDimitry Andric }); 197*0fca6ea1SDimitry Andric 198*0fca6ea1SDimitry Andric for (const auto &Ext : EnabledExtensionsInfo) { 199*0fca6ea1SDimitry Andric outs() << " " 200*0fca6ea1SDimitry Andric << format("%-55s%s\n", 201*0fca6ea1SDimitry Andric Ext.ArchFeatureName.str().c_str(), 202*0fca6ea1SDimitry Andric Ext.Description.str().c_str()); 203*0fca6ea1SDimitry Andric } 204*0fca6ea1SDimitry Andric } 205*0fca6ea1SDimitry Andric 2067a6dacacSDimitry Andric const llvm::AArch64::ExtensionInfo & 2077a6dacacSDimitry Andric lookupExtensionByID(llvm::AArch64::ArchExtKind ExtID) { 2087a6dacacSDimitry Andric for (const auto &E : llvm::AArch64::Extensions) 2097a6dacacSDimitry Andric if (E.ID == ExtID) 2107a6dacacSDimitry Andric return E; 2117a6dacacSDimitry Andric llvm_unreachable("Invalid extension ID"); 2127a6dacacSDimitry Andric } 2137a6dacacSDimitry Andric 2147a6dacacSDimitry Andric void AArch64::ExtensionSet::enable(ArchExtKind E) { 2157a6dacacSDimitry Andric if (Enabled.test(E)) 2167a6dacacSDimitry Andric return; 2177a6dacacSDimitry Andric 218*0fca6ea1SDimitry Andric LLVM_DEBUG(llvm::dbgs() << "Enable " << lookupExtensionByID(E).UserVisibleName << "\n"); 2197a6dacacSDimitry Andric 2207a6dacacSDimitry Andric Touched.set(E); 2217a6dacacSDimitry Andric Enabled.set(E); 2227a6dacacSDimitry Andric 2237a6dacacSDimitry Andric // Recursively enable all features that this one depends on. This handles all 2247a6dacacSDimitry Andric // of the simple cases, where the behaviour doesn't depend on the base 2257a6dacacSDimitry Andric // architecture version. 2267a6dacacSDimitry Andric for (auto Dep : ExtensionDependencies) 2277a6dacacSDimitry Andric if (E == Dep.Later) 2287a6dacacSDimitry Andric enable(Dep.Earlier); 2297a6dacacSDimitry Andric 2307a6dacacSDimitry Andric // Special cases for dependencies which vary depending on the base 2317a6dacacSDimitry Andric // architecture version. 2327a6dacacSDimitry Andric if (BaseArch) { 2337a6dacacSDimitry Andric // +fp16 implies +fp16fml for v8.4A+, but not v9.0-A+ 2347a6dacacSDimitry Andric if (E == AEK_FP16 && BaseArch->is_superset(ARMV8_4A) && 2357a6dacacSDimitry Andric !BaseArch->is_superset(ARMV9A)) 2367a6dacacSDimitry Andric enable(AEK_FP16FML); 2377a6dacacSDimitry Andric 2387a6dacacSDimitry Andric // For v8.4A+ and v9.0A+, +crypto also enables +sha3 and +sm4. 2397a6dacacSDimitry Andric if (E == AEK_CRYPTO && BaseArch->is_superset(ARMV8_4A)) { 2407a6dacacSDimitry Andric enable(AEK_SHA3); 2417a6dacacSDimitry Andric enable(AEK_SM4); 2427a6dacacSDimitry Andric } 2437a6dacacSDimitry Andric } 2447a6dacacSDimitry Andric } 2457a6dacacSDimitry Andric 2467a6dacacSDimitry Andric void AArch64::ExtensionSet::disable(ArchExtKind E) { 2477a6dacacSDimitry Andric // -crypto always disables aes, sha2, sha3 and sm4, even for architectures 2487a6dacacSDimitry Andric // where the latter two would not be enabled by +crypto. 2497a6dacacSDimitry Andric if (E == AEK_CRYPTO) { 2507a6dacacSDimitry Andric disable(AEK_AES); 2517a6dacacSDimitry Andric disable(AEK_SHA2); 2527a6dacacSDimitry Andric disable(AEK_SHA3); 2537a6dacacSDimitry Andric disable(AEK_SM4); 2547a6dacacSDimitry Andric } 2557a6dacacSDimitry Andric 2567a6dacacSDimitry Andric if (!Enabled.test(E)) 2577a6dacacSDimitry Andric return; 2587a6dacacSDimitry Andric 259*0fca6ea1SDimitry Andric LLVM_DEBUG(llvm::dbgs() << "Disable " << lookupExtensionByID(E).UserVisibleName << "\n"); 2607a6dacacSDimitry Andric 2617a6dacacSDimitry Andric Touched.set(E); 2627a6dacacSDimitry Andric Enabled.reset(E); 2637a6dacacSDimitry Andric 2647a6dacacSDimitry Andric // Recursively disable all features that depends on this one. 2657a6dacacSDimitry Andric for (auto Dep : ExtensionDependencies) 2667a6dacacSDimitry Andric if (E == Dep.Earlier) 2677a6dacacSDimitry Andric disable(Dep.Later); 2687a6dacacSDimitry Andric } 2697a6dacacSDimitry Andric 2707a6dacacSDimitry Andric void AArch64::ExtensionSet::addCPUDefaults(const CpuInfo &CPU) { 2717a6dacacSDimitry Andric LLVM_DEBUG(llvm::dbgs() << "addCPUDefaults(" << CPU.Name << ")\n"); 2727a6dacacSDimitry Andric BaseArch = &CPU.Arch; 2737a6dacacSDimitry Andric 2747a6dacacSDimitry Andric AArch64::ExtensionBitset CPUExtensions = CPU.getImpliedExtensions(); 2757a6dacacSDimitry Andric for (const auto &E : Extensions) 2767a6dacacSDimitry Andric if (CPUExtensions.test(E.ID)) 2777a6dacacSDimitry Andric enable(E.ID); 2787a6dacacSDimitry Andric } 2797a6dacacSDimitry Andric 2807a6dacacSDimitry Andric void AArch64::ExtensionSet::addArchDefaults(const ArchInfo &Arch) { 2817a6dacacSDimitry Andric LLVM_DEBUG(llvm::dbgs() << "addArchDefaults(" << Arch.Name << ")\n"); 2827a6dacacSDimitry Andric BaseArch = &Arch; 2837a6dacacSDimitry Andric 2847a6dacacSDimitry Andric for (const auto &E : Extensions) 2857a6dacacSDimitry Andric if (Arch.DefaultExts.test(E.ID)) 2867a6dacacSDimitry Andric enable(E.ID); 2877a6dacacSDimitry Andric } 2887a6dacacSDimitry Andric 289*0fca6ea1SDimitry Andric bool AArch64::ExtensionSet::parseModifier(StringRef Modifier, 290*0fca6ea1SDimitry Andric const bool AllowNoDashForm) { 2917a6dacacSDimitry Andric LLVM_DEBUG(llvm::dbgs() << "parseModifier(" << Modifier << ")\n"); 2927a6dacacSDimitry Andric 293*0fca6ea1SDimitry Andric size_t NChars = 0; 294*0fca6ea1SDimitry Andric // The "no-feat" form is allowed in the target attribute but nowhere else. 295*0fca6ea1SDimitry Andric if (AllowNoDashForm && Modifier.starts_with("no-")) 296*0fca6ea1SDimitry Andric NChars = 3; 297*0fca6ea1SDimitry Andric else if (Modifier.starts_with("no")) 298*0fca6ea1SDimitry Andric NChars = 2; 299*0fca6ea1SDimitry Andric bool IsNegated = NChars != 0; 300*0fca6ea1SDimitry Andric StringRef ArchExt = Modifier.drop_front(NChars); 3017a6dacacSDimitry Andric 302*0fca6ea1SDimitry Andric if (auto AE = parseArchExtension(ArchExt)) { 303*0fca6ea1SDimitry Andric if (AE->PosTargetFeature.empty() || AE->NegTargetFeature.empty()) 3047a6dacacSDimitry Andric return false; 305*0fca6ea1SDimitry Andric if (IsNegated) 306*0fca6ea1SDimitry Andric disable(AE->ID); 307*0fca6ea1SDimitry Andric else 308*0fca6ea1SDimitry Andric enable(AE->ID); 309*0fca6ea1SDimitry Andric return true; 310*0fca6ea1SDimitry Andric } 311*0fca6ea1SDimitry Andric return false; 312*0fca6ea1SDimitry Andric } 313*0fca6ea1SDimitry Andric 314*0fca6ea1SDimitry Andric void AArch64::ExtensionSet::reconstructFromParsedFeatures( 315*0fca6ea1SDimitry Andric const std::vector<std::string> &Features, 316*0fca6ea1SDimitry Andric std::vector<std::string> &NonExtensions) { 317*0fca6ea1SDimitry Andric assert(Touched.none() && "Bitset already initialized"); 318*0fca6ea1SDimitry Andric for (auto &F : Features) { 319*0fca6ea1SDimitry Andric bool IsNegated = F[0] == '-'; 320*0fca6ea1SDimitry Andric if (auto AE = targetFeatureToExtension(F)) { 321*0fca6ea1SDimitry Andric Touched.set(AE->ID); 322*0fca6ea1SDimitry Andric if (IsNegated) 323*0fca6ea1SDimitry Andric Enabled.reset(AE->ID); 324*0fca6ea1SDimitry Andric else 325*0fca6ea1SDimitry Andric Enabled.set(AE->ID); 326*0fca6ea1SDimitry Andric continue; 327*0fca6ea1SDimitry Andric } 328*0fca6ea1SDimitry Andric NonExtensions.push_back(F); 329*0fca6ea1SDimitry Andric } 330*0fca6ea1SDimitry Andric } 331*0fca6ea1SDimitry Andric 332*0fca6ea1SDimitry Andric void AArch64::ExtensionSet::dump() const { 333*0fca6ea1SDimitry Andric std::vector<StringRef> Features; 334*0fca6ea1SDimitry Andric toLLVMFeatureList(Features); 335*0fca6ea1SDimitry Andric for (StringRef F : Features) 336*0fca6ea1SDimitry Andric llvm::outs() << F << " "; 337*0fca6ea1SDimitry Andric llvm::outs() << "\n"; 338*0fca6ea1SDimitry Andric } 339*0fca6ea1SDimitry Andric 340*0fca6ea1SDimitry Andric const AArch64::ExtensionInfo & 341*0fca6ea1SDimitry Andric AArch64::getExtensionByID(AArch64::ArchExtKind ExtID) { 342*0fca6ea1SDimitry Andric return lookupExtensionByID(ExtID); 3437a6dacacSDimitry Andric } 344