//===-- AArch64TargetParser - Parser for AArch64 features -------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements a target parser to recognise AArch64 hardware features // such as FPU/CPU/ARCH and extension names. // //===----------------------------------------------------------------------===// #include "llvm/TargetParser/AArch64TargetParser.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/ARMTargetParserCommon.h" #include "llvm/TargetParser/Triple.h" #include #include #define DEBUG_TYPE "target-parser" using namespace llvm; #define EMIT_FMV_INFO #include "llvm/TargetParser/AArch64TargetParserDef.inc" static unsigned checkArchVersion(llvm::StringRef Arch) { if (Arch.size() >= 2 && Arch[0] == 'v' && std::isdigit(Arch[1])) return (Arch[1] - 48); return 0; } const AArch64::ArchInfo *AArch64::getArchForCpu(StringRef CPU) { // Note: this now takes cpu aliases into account std::optional Cpu = parseCpu(CPU); if (!Cpu) return nullptr; return &Cpu->Arch; } std::optional AArch64::ArchInfo::findBySubArch(StringRef SubArch) { for (const auto *A : AArch64::ArchInfos) if (A->getSubArch() == SubArch) return *A; return {}; } uint64_t AArch64::getCpuSupportsMask(ArrayRef FeatureStrs) { uint64_t FeaturesMask = 0; for (const StringRef &FeatureStr : FeatureStrs) { if (auto Ext = parseFMVExtension(FeatureStr)) FeaturesMask |= (1ULL << Ext->Bit); } return FeaturesMask; } bool AArch64::getExtensionFeatures( const AArch64::ExtensionBitset &InputExts, std::vector &Features) { for (const auto &E : Extensions) /* INVALID and NONE have no feature name. */ if (InputExts.test(E.ID) && !E.PosTargetFeature.empty()) Features.push_back(E.PosTargetFeature); return true; } StringRef AArch64::resolveCPUAlias(StringRef Name) { for (const auto &A : CpuAliases) if (A.AltName == Name) return A.Name; return Name; } StringRef AArch64::getArchExtFeature(StringRef ArchExt) { bool IsNegated = ArchExt.starts_with("no"); StringRef ArchExtBase = IsNegated ? ArchExt.drop_front(2) : ArchExt; if (auto AE = parseArchExtension(ArchExtBase)) { assert(!(AE.has_value() && AE->NegTargetFeature.empty())); return IsNegated ? AE->NegTargetFeature : AE->PosTargetFeature; } return StringRef(); } void AArch64::fillValidCPUArchList(SmallVectorImpl &Values) { for (const auto &C : CpuInfos) Values.push_back(C.Name); for (const auto &Alias : CpuAliases) // The apple-latest alias is backend only, do not expose it to clang's -mcpu. if (Alias.AltName != "apple-latest") Values.push_back(Alias.AltName); llvm::sort(Values); } bool AArch64::isX18ReservedByDefault(const Triple &TT) { return TT.isAndroid() || TT.isOSDarwin() || TT.isOSFuchsia() || TT.isOSWindows() || TT.isOHOSFamily(); } // Allows partial match, ex. "v8a" matches "armv8a". const AArch64::ArchInfo *AArch64::parseArch(StringRef Arch) { Arch = llvm::ARM::getCanonicalArchName(Arch); if (checkArchVersion(Arch) < 8) return {}; StringRef Syn = llvm::ARM::getArchSynonym(Arch); for (const auto *A : ArchInfos) { if (A->Name.ends_with(Syn)) return A; } return {}; } std::optional AArch64::parseArchExtension(StringRef ArchExt) { if (ArchExt.empty()) return {}; for (const auto &A : Extensions) { if (ArchExt == A.UserVisibleName || ArchExt == A.Alias) return A; } return {}; } std::optional AArch64::parseFMVExtension(StringRef FMVExt) { // FIXME introduce general alias functionality, or remove this exception. if (FMVExt == "rdma") FMVExt = "rdm"; for (const auto &I : getFMVInfo()) { if (FMVExt == I.Name) return I; } return {}; } std::optional AArch64::targetFeatureToExtension(StringRef TargetFeature) { for (const auto &E : Extensions) if (TargetFeature == E.PosTargetFeature) return E; return {}; } std::optional AArch64::parseCpu(StringRef Name) { // Resolve aliases first. Name = resolveCPUAlias(Name); // Then find the CPU name. for (const auto &C : CpuInfos) if (Name == C.Name) return C; return {}; } void AArch64::PrintSupportedExtensions() { outs() << "All available -march extensions for AArch64\n\n" << " " << left_justify("Name", 20) << left_justify("Architecture Feature(s)", 55) << "Description\n"; for (const auto &Ext : Extensions) { // Extensions without a feature cannot be used with -march. if (!Ext.UserVisibleName.empty() && !Ext.PosTargetFeature.empty()) { outs() << " " << format(Ext.Description.empty() ? "%-20s%s\n" : "%-20s%-55s%s\n", Ext.UserVisibleName.str().c_str(), Ext.ArchFeatureName.str().c_str(), Ext.Description.str().c_str()); } } } void AArch64::printEnabledExtensions(const std::set &EnabledFeatureNames) { outs() << "Extensions enabled for the given AArch64 target\n\n" << " " << left_justify("Architecture Feature(s)", 55) << "Description\n"; std::vector EnabledExtensionsInfo; for (const auto &FeatureName : EnabledFeatureNames) { std::string PosFeatureName = '+' + FeatureName.str(); if (auto ExtInfo = targetFeatureToExtension(PosFeatureName)) EnabledExtensionsInfo.push_back(*ExtInfo); } std::sort(EnabledExtensionsInfo.begin(), EnabledExtensionsInfo.end(), [](const ExtensionInfo &Lhs, const ExtensionInfo &Rhs) { return Lhs.ArchFeatureName < Rhs.ArchFeatureName; }); for (const auto &Ext : EnabledExtensionsInfo) { outs() << " " << format("%-55s%s\n", Ext.ArchFeatureName.str().c_str(), Ext.Description.str().c_str()); } } const llvm::AArch64::ExtensionInfo & lookupExtensionByID(llvm::AArch64::ArchExtKind ExtID) { for (const auto &E : llvm::AArch64::Extensions) if (E.ID == ExtID) return E; llvm_unreachable("Invalid extension ID"); } void AArch64::ExtensionSet::enable(ArchExtKind E) { if (Enabled.test(E)) return; LLVM_DEBUG(llvm::dbgs() << "Enable " << lookupExtensionByID(E).UserVisibleName << "\n"); Touched.set(E); Enabled.set(E); // Recursively enable all features that this one depends on. This handles all // of the simple cases, where the behaviour doesn't depend on the base // architecture version. for (auto Dep : ExtensionDependencies) if (E == Dep.Later) enable(Dep.Earlier); // Special cases for dependencies which vary depending on the base // architecture version. if (BaseArch) { // +fp16 implies +fp16fml for v8.4A+, but not v9.0-A+ if (E == AEK_FP16 && BaseArch->is_superset(ARMV8_4A) && !BaseArch->is_superset(ARMV9A)) enable(AEK_FP16FML); // For v8.4A+ and v9.0A+, +crypto also enables +sha3 and +sm4. if (E == AEK_CRYPTO && BaseArch->is_superset(ARMV8_4A)) { enable(AEK_SHA3); enable(AEK_SM4); } } } void AArch64::ExtensionSet::disable(ArchExtKind E) { // -crypto always disables aes, sha2, sha3 and sm4, even for architectures // where the latter two would not be enabled by +crypto. if (E == AEK_CRYPTO) { disable(AEK_AES); disable(AEK_SHA2); disable(AEK_SHA3); disable(AEK_SM4); } if (!Enabled.test(E)) return; LLVM_DEBUG(llvm::dbgs() << "Disable " << lookupExtensionByID(E).UserVisibleName << "\n"); Touched.set(E); Enabled.reset(E); // Recursively disable all features that depends on this one. for (auto Dep : ExtensionDependencies) if (E == Dep.Earlier) disable(Dep.Later); } void AArch64::ExtensionSet::addCPUDefaults(const CpuInfo &CPU) { LLVM_DEBUG(llvm::dbgs() << "addCPUDefaults(" << CPU.Name << ")\n"); BaseArch = &CPU.Arch; AArch64::ExtensionBitset CPUExtensions = CPU.getImpliedExtensions(); for (const auto &E : Extensions) if (CPUExtensions.test(E.ID)) enable(E.ID); } void AArch64::ExtensionSet::addArchDefaults(const ArchInfo &Arch) { LLVM_DEBUG(llvm::dbgs() << "addArchDefaults(" << Arch.Name << ")\n"); BaseArch = &Arch; for (const auto &E : Extensions) if (Arch.DefaultExts.test(E.ID)) enable(E.ID); } bool AArch64::ExtensionSet::parseModifier(StringRef Modifier, const bool AllowNoDashForm) { LLVM_DEBUG(llvm::dbgs() << "parseModifier(" << Modifier << ")\n"); size_t NChars = 0; // The "no-feat" form is allowed in the target attribute but nowhere else. if (AllowNoDashForm && Modifier.starts_with("no-")) NChars = 3; else if (Modifier.starts_with("no")) NChars = 2; bool IsNegated = NChars != 0; StringRef ArchExt = Modifier.drop_front(NChars); if (auto AE = parseArchExtension(ArchExt)) { if (AE->PosTargetFeature.empty() || AE->NegTargetFeature.empty()) return false; if (IsNegated) disable(AE->ID); else enable(AE->ID); return true; } return false; } void AArch64::ExtensionSet::reconstructFromParsedFeatures( const std::vector &Features, std::vector &NonExtensions) { assert(Touched.none() && "Bitset already initialized"); for (auto &F : Features) { bool IsNegated = F[0] == '-'; if (auto AE = targetFeatureToExtension(F)) { Touched.set(AE->ID); if (IsNegated) Enabled.reset(AE->ID); else Enabled.set(AE->ID); continue; } NonExtensions.push_back(F); } } void AArch64::ExtensionSet::dump() const { std::vector Features; toLLVMFeatureList(Features); for (StringRef F : Features) llvm::outs() << F << " "; llvm::outs() << "\n"; } const AArch64::ExtensionInfo & AArch64::getExtensionByID(AArch64::ArchExtKind ExtID) { return lookupExtensionByID(ExtID); }