| //===-- 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 <cctype> | |
| #include <vector> | |
| #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<CpuInfo> Cpu = parseCpu(CPU); | |
| if (!Cpu) | |
| return nullptr; | |
| return &Cpu->Arch; | |
| } | |
| std::optional<AArch64::ArchInfo> AArch64::ArchInfo::findBySubArch(StringRef SubArch) { | |
| for (const auto *A : AArch64::ArchInfos) | |
| if (A->getSubArch() == SubArch) | |
| return *A; | |
| return {}; | |
| } | |
| std::optional<AArch64::FMVInfo> lookupFMVByID(AArch64::ArchExtKind ExtID) { | |
| for (const AArch64::FMVInfo &Info : AArch64::getFMVInfo()) | |
| if (Info.ID == ExtID) | |
| return Info; | |
| return {}; | |
| } | |
| std::optional<AArch64::FMVInfo> getFMVInfoFrom(StringRef Feature) { | |
| std::optional<AArch64::FMVInfo> FMV = AArch64::parseFMVExtension(Feature); | |
| if (!FMV && Feature.starts_with('+')) | |
| if (std::optional<AArch64::ExtensionInfo> Ext = | |
| AArch64::targetFeatureToExtension(Feature)) | |
| FMV = lookupFMVByID(Ext->ID); | |
| return FMV; | |
| } | |
| APInt AArch64::getFMVPriority(ArrayRef<StringRef> Features) { | |
| // Transitively enable the Arch Extensions which correspond to each feature. | |
| ExtensionSet FeatureBits; | |
| APInt PriorityMask = APInt::getZero(128); | |
| for (const StringRef Feature : Features) { | |
| if (std::optional<FMVInfo> FMV = getFMVInfoFrom(Feature)) { | |
| // FMV feature without a corresponding Arch Extension may affect priority | |
| if (FMV->ID) | |
| FeatureBits.enable(*FMV->ID); | |
| else | |
| PriorityMask.setBit(FMV->PriorityBit); | |
| } | |
| } | |
| // Construct a bitmask for all the transitively enabled Arch Extensions. | |
| for (const FMVInfo &Info : getFMVInfo()) | |
| if (Info.ID && FeatureBits.Enabled.test(*Info.ID)) | |
| PriorityMask.setBit(Info.PriorityBit); | |
| return PriorityMask; | |
| } | |
| APInt AArch64::getCpuSupportsMask(ArrayRef<StringRef> Features) { | |
| // Transitively enable the Arch Extensions which correspond to each feature. | |
| ExtensionSet FeatureBits; | |
| for (const StringRef Feature : Features) | |
| if (std::optional<FMVInfo> FMV = getFMVInfoFrom(Feature)) | |
| if (FMV->ID) | |
| FeatureBits.enable(*FMV->ID); | |
| // Construct a bitmask for all the transitively enabled Arch Extensions. | |
| APInt FeaturesMask = APInt::getZero(128); | |
| for (const FMVInfo &Info : getFMVInfo()) | |
| if (Info.ID && FeatureBits.Enabled.test(*Info.ID)) | |
| FeaturesMask.setBit(*Info.FeatureBit); | |
| return FeaturesMask; | |
| } | |
| bool AArch64::getExtensionFeatures( | |
| const AArch64::ExtensionBitset &InputExts, | |
| std::vector<StringRef> &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<StringRef> &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::ExtensionInfo> | |
| 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::FMVInfo> 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::ExtensionInfo> | |
| AArch64::targetFeatureToExtension(StringRef TargetFeature) { | |
| for (const auto &E : Extensions) | |
| if (TargetFeature == E.PosTargetFeature || | |
| TargetFeature == E.NegTargetFeature) | |
| return E; | |
| return {}; | |
| } | |
| std::optional<AArch64::CpuInfo> 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<StringRef> &EnabledFeatureNames) { | |
| outs() << "Extensions enabled for the given AArch64 target\n\n" | |
| << " " << left_justify("Architecture Feature(s)", 55) | |
| << "Description\n"; | |
| std::vector<ExtensionInfo> 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); | |
| } | |
| // sve2-aes was historically associated with both FEAT_SVE2 and FEAT_SVE_AES, | |
| // the latter is now associated with sve-aes and sve2-aes has become shorthand | |
| // for +sve2+sve-aes. For backwards compatibility, when we disable sve2-aes we | |
| // must also disable sve-aes. | |
| if (E == AEK_SVE2AES) | |
| disable(AEK_SVEAES); | |
| // sve2-sm4 was historically associated with both FEAT_SVE2 and | |
| // FEAT_SVE_SM4, the latter is now associated with sve-sm4 and sve2-sm4 has | |
| // become shorthand for +sve2+sve-sm4. For backwards compatibility, when we | |
| // disable sve2-sm4 we must also disable sve-sm4. | |
| if (E == AEK_SVE2SM4) | |
| disable(AEK_SVESM4); | |
| // sve2-sha3 was historically associated with both FEAT_SVE2 and | |
| // FEAT_SVE_SHA3, the latter is now associated with sve-sha3 and sve2-sha3 has | |
| // become shorthand for +sve2+sve-sha3. For backwards compatibility, when we | |
| // disable sve2-sha3 we must also disable sve-sha3. | |
| if (E == AEK_SVE2SHA3) | |
| disable(AEK_SVESHA3); | |
| if (E == AEK_SVE2BITPERM){ | |
| disable(AEK_SVEBITPERM); | |
| disable(AEK_SVE2); | |
| } | |
| 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<std::string> &Features, | |
| std::vector<std::string> &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<StringRef> Features; | |
| toLLVMFeatureList(Features); | |
| for (StringRef F : Features) | |
| llvm::outs() << F << " "; | |
| llvm::outs() << "\n"; | |
| } | |
| const AArch64::ExtensionInfo & | |
| AArch64::getExtensionByID(AArch64::ArchExtKind ExtID) { | |
| return lookupExtensionByID(ExtID); | |
| } |