| //===- ARMTargetDefEmitter.cpp - Generate data about ARM Architectures ----===// | |
| // | |
| // 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 tablegen backend exports information about CPUs, FPUs, architectures, | |
| // and features into a common format that can be used by both TargetParser and | |
| // the ARM and AArch64 backends. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ADT/DenseMap.h" | |
| #include "llvm/ADT/StringSet.h" | |
| #include "llvm/Support/Format.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| #include "llvm/TableGen/Error.h" | |
| #include "llvm/TableGen/Record.h" | |
| #include "llvm/TableGen/StringToOffsetTable.h" | |
| #include "llvm/TableGen/TableGenBackend.h" | |
| #include <set> | |
| #include <string> | |
| using namespace llvm; | |
| /// Collect the full set of implied features for a SubtargetFeature. | |
| static void collectImpliedFeatures(std::set<const Record *> &SeenFeats, | |
| const Record *Rec) { | |
| assert(Rec->isSubClassOf("SubtargetFeature") && | |
| "Rec is not a SubtargetFeature"); | |
| SeenFeats.insert(Rec); | |
| for (const Record *Implied : Rec->getValueAsListOfDefs("Implies")) | |
| collectImpliedFeatures(SeenFeats, Implied); | |
| } | |
| static void checkFeatureTree(const Record *Root) { | |
| std::set<const Record *> SeenFeats; | |
| collectImpliedFeatures(SeenFeats, Root); | |
| // Check that each of the mandatory (implied) features which is an | |
| // ExtensionWithMArch is also enabled by default. | |
| auto DefaultExtsVec = Root->getValueAsListOfDefs("DefaultExts"); | |
| std::set<const Record *> DefaultExts{DefaultExtsVec.begin(), | |
| DefaultExtsVec.end()}; | |
| for (const Record *Feat : SeenFeats) { | |
| if (Feat->isSubClassOf("ExtensionWithMArch") && !DefaultExts.count(Feat)) | |
| PrintFatalError(Root->getLoc(), | |
| "ExtensionWithMArch " + Feat->getName() + | |
| " is implied (mandatory) as a SubtargetFeature, but " | |
| "is not present in DefaultExts"); | |
| } | |
| } | |
| static void emitARMTargetDef(const RecordKeeper &RK, raw_ostream &OS) { | |
| OS << "// Autogenerated by ARMTargetDefEmitter.cpp\n\n"; | |
| // Look through all SubtargetFeature defs with the given FieldName, and | |
| // collect the set of all Values that that FieldName is set to. | |
| auto GatherSubtargetFeatureFieldValues = [&RK](StringRef FieldName) { | |
| llvm::StringSet<> Set; | |
| for (const Record *Rec : RK.getAllDerivedDefinitions("SubtargetFeature")) { | |
| if (Rec->getValueAsString("FieldName") == FieldName) { | |
| Set.insert(Rec->getValueAsString("Value")); | |
| } | |
| } | |
| return Set; | |
| }; | |
| // Sort the extensions alphabetically, so they don't appear in tablegen order. | |
| std::vector<const Record *> SortedExtensions = | |
| RK.getAllDerivedDefinitions("Extension"); | |
| auto Alphabetical = [](const Record *A, const Record *B) -> bool { | |
| const auto NameA = A->getValueAsString("Name"); | |
| const auto NameB = B->getValueAsString("Name"); | |
| return NameA.compare(NameB) < 0; // A lexographically less than B | |
| }; | |
| sort(SortedExtensions, Alphabetical); | |
| // Cache Extension records for quick lookup. | |
| DenseMap<StringRef, const Record *> ExtensionMap; | |
| for (const Record *Rec : SortedExtensions) { | |
| auto Name = Rec->getValueAsString("UserVisibleName"); | |
| if (Name.empty()) | |
| Name = Rec->getValueAsString("Name"); | |
| ExtensionMap[Name] = Rec; | |
| } | |
| // The ARMProcFamilyEnum values are initialised by SubtargetFeature defs | |
| // which set the ARMProcFamily field. We can generate the enum from these defs | |
| // which look like this: | |
| // | |
| // def ProcA5 : SubtargetFeature<"a5", "ARMProcFamily", "CortexA5", | |
| // "Cortex-A5 ARM processors", []>; | |
| OS << "#ifndef ARM_PROCESSOR_FAMILY\n" | |
| << "#define ARM_PROCESSOR_FAMILY(ENUM)\n" | |
| << "#endif\n\n"; | |
| const StringSet<> ARMProcFamilyVals = | |
| GatherSubtargetFeatureFieldValues("ARMProcFamily"); | |
| for (const StringRef &Family : ARMProcFamilyVals.keys()) | |
| OS << "ARM_PROCESSOR_FAMILY(" << Family << ")\n"; | |
| OS << "\n#undef ARM_PROCESSOR_FAMILY\n\n"; | |
| OS << "#ifndef ARM_ARCHITECTURE\n" | |
| << "#define ARM_ARCHITECTURE(ENUM)\n" | |
| << "#endif\n\n"; | |
| // This should correspond to instances of the Architecture tablegen class. | |
| const StringSet<> ARMArchVals = GatherSubtargetFeatureFieldValues("ARMArch"); | |
| for (const StringRef &Arch : ARMArchVals.keys()) | |
| OS << "ARM_ARCHITECTURE(" << Arch << ")\n"; | |
| OS << "\n#undef ARM_ARCHITECTURE\n\n"; | |
| // Currently only AArch64 (not ARM) is handled beyond this point. | |
| if (!RK.getClass("Architecture64")) | |
| return; | |
| StringToOffsetTable StrTab; | |
| // Emit the ArchExtKind enum | |
| OS << "#ifdef EMIT_ARCHEXTKIND_ENUM\n" | |
| << "enum ArchExtKind : unsigned {\n"; | |
| for (const Record *Rec : SortedExtensions) { | |
| auto AEK = Rec->getValueAsString("ArchExtKindSpelling").upper(); | |
| OS << " " << AEK << ",\n"; | |
| } | |
| OS << " AEK_NUM_EXTENSIONS\n" | |
| << "};\n" | |
| << "#undef EMIT_ARCHEXTKIND_ENUM\n" | |
| << "#endif // EMIT_ARCHEXTKIND_ENUM\n"; | |
| // Emit information for each defined Extension; used to build ArmExtKind. | |
| OS << "#ifdef EMIT_EXTENSIONS\n" | |
| << "inline constexpr ExtensionInfo Extensions[] = {\n"; | |
| for (const Record *Rec : SortedExtensions) { | |
| auto AEK = Rec->getValueAsString("ArchExtKindSpelling").upper(); | |
| OS << " "; | |
| OS << "{" | |
| << StrTab.GetOrAddStringOffset(Rec->getValueAsString("UserVisibleName")); | |
| // Empty string implies no alias. | |
| OS << ", " | |
| << StrTab.GetOrAddStringOffset( | |
| Rec->getValueAsString("UserVisibleAlias")); | |
| OS << ", AArch64::" << AEK; | |
| OS << ", " | |
| << StrTab.GetOrAddStringOffset(Rec->getValueAsString("ArchFeatureName")); | |
| OS << ", " << StrTab.GetOrAddStringOffset(Rec->getValueAsString("Desc")); | |
| OS << ", " | |
| << StrTab.GetOrAddStringOffset( | |
| std::string("+") + | |
| Rec->getValueAsString("Name").str()); // posfeature | |
| OS << ", " | |
| << StrTab.GetOrAddStringOffset( | |
| std::string("-") + | |
| Rec->getValueAsString("Name").str()); // negfeature | |
| OS << "},\n"; | |
| }; | |
| OS << "};\n"; | |
| OS << "#undef EMIT_EXTENSIONS\n" | |
| << "#endif // EMIT_EXTENSIONS\n" | |
| << "\n"; | |
| // Emit FMV information | |
| auto FMVExts = RK.getAllDerivedDefinitionsIfDefined("FMVExtension"); | |
| OS << "#ifdef EMIT_FMV_INFO\n" | |
| << "const std::vector<llvm::AArch64::FMVInfo>& " | |
| "llvm::AArch64::getFMVInfo() {\n" | |
| << " static std::vector<FMVInfo> I;\n" | |
| << " if(I.size()) return I;\n" | |
| << " I.reserve(" << FMVExts.size() << ");\n"; | |
| for (const Record *Rec : FMVExts) { | |
| auto FeatName = Rec->getValueAsString("BackendFeature"); | |
| const Record *FeatRec = ExtensionMap[FeatName]; | |
| OS << " I.emplace_back("; | |
| OS << "\"" << Rec->getValueAsString("Name") << "\""; | |
| if (FeatRec) | |
| OS << ", " << Rec->getValueAsString("FeatureBit"); | |
| else | |
| OS << ", std::nullopt"; | |
| OS << ", " << Rec->getValueAsString("PriorityBit"); | |
| if (FeatRec) | |
| OS << ", " << FeatRec->getValueAsString("ArchExtKindSpelling").upper(); | |
| else | |
| OS << ", std::nullopt"; | |
| OS << ");\n"; | |
| }; | |
| OS << " return I;\n" | |
| << "}\n" | |
| << "#undef EMIT_FMV_INFO\n" | |
| << "#endif // EMIT_FMV_INFO\n" | |
| << "\n"; | |
| // Emit extension dependencies | |
| OS << "#ifdef EMIT_EXTENSION_DEPENDENCIES\n" | |
| << "inline constexpr ExtensionDependency ExtensionDependencies[] = {\n"; | |
| for (const Record *Rec : SortedExtensions) { | |
| auto LaterAEK = Rec->getValueAsString("ArchExtKindSpelling").upper(); | |
| for (const Record *I : Rec->getValueAsListOfDefs("Implies")) | |
| if (auto EarlierAEK = I->getValueAsOptionalString("ArchExtKindSpelling")) | |
| OS << " {" << EarlierAEK->upper() << ", " << LaterAEK << "},\n"; | |
| } | |
| // FIXME: Tablegen has the Subtarget Feature FeatureRCPC_IMMO which is implied | |
| // by FeatureRCPC3 and in turn implies FeatureRCPC. The proper fix is to make | |
| // FeatureRCPC_IMMO an Extension but that will expose it to the command line. | |
| OS << " {AEK_RCPC, AEK_RCPC3},\n"; | |
| OS << "};\n" | |
| << "#undef EMIT_EXTENSION_DEPENDENCIES\n" | |
| << "#endif // EMIT_EXTENSION_DEPENDENCIES\n" | |
| << "\n"; | |
| // Emit architecture information | |
| OS << "#ifdef EMIT_ARCHITECTURES\n"; | |
| // Return the C++ name of the of an ArchInfo object | |
| auto ArchInfoName = [](int Major, int Minor, | |
| StringRef Profile) -> std::string { | |
| return Minor == 0 ? "ARMV" + std::to_string(Major) + Profile.upper() | |
| : "ARMV" + std::to_string(Major) + "_" + | |
| std::to_string(Minor) + Profile.upper(); | |
| }; | |
| auto Architectures = RK.getAllDerivedDefinitionsIfDefined("Architecture64"); | |
| OS << "\n" | |
| << "/// The set of all architectures\n" | |
| << "inline constexpr std::array<ArchInfo, " << Architectures.size() | |
| << "> ArchInfos = {{\n"; | |
| std::vector<std::string> CppSpellings; | |
| for (const Record *Rec : Architectures) { | |
| const int Major = Rec->getValueAsInt("Major"); | |
| const int Minor = Rec->getValueAsInt("Minor"); | |
| const std::string ProfileLower = Rec->getValueAsString("Profile").str(); | |
| const std::string ProfileUpper = Rec->getValueAsString("Profile").upper(); | |
| if (ProfileLower != "a" && ProfileLower != "r") | |
| PrintFatalError(Rec->getLoc(), | |
| "error: Profile must be one of 'a' or 'r', got '" + | |
| ProfileLower + "'"); | |
| // Name of the object in C++ | |
| std::string CppSpelling = ArchInfoName(Major, Minor, ProfileUpper); | |
| OS << " {\n"; | |
| CppSpellings.push_back(std::move(CppSpelling)); | |
| OS << llvm::format(" VersionTuple{%d, %d},\n", Major, Minor); | |
| OS << llvm::format(" %sProfile,\n", ProfileUpper.c_str()); | |
| // Name as spelled for -march. | |
| std::string ArchStr; | |
| llvm::raw_string_ostream AS(ArchStr); | |
| if (Minor == 0) | |
| AS << llvm::format("armv%d-%s", Major, ProfileLower.c_str()); | |
| else | |
| AS << llvm::format("armv%d.%d-%s", Major, Minor, ProfileLower.c_str()); | |
| OS << " " << StrTab.GetOrAddStringOffset(ArchStr) << ",\n"; | |
| // SubtargetFeature::Name, used for -target-feature. Here the "+" is added. | |
| std::string TargetFeatureName = | |
| (Twine("+") + Rec->getValueAsString("Name")).str(); | |
| OS << " " << StrTab.GetOrAddStringOffset(TargetFeatureName) << ",\n"; | |
| // Construct the list of default extensions | |
| OS << " (AArch64::ExtensionBitset({"; | |
| for (auto *E : Rec->getValueAsListOfDefs("DefaultExts")) { | |
| OS << "AArch64::" << E->getValueAsString("ArchExtKindSpelling").upper() | |
| << ", "; | |
| } | |
| OS << "}))\n"; | |
| OS << " },\n"; | |
| } | |
| OS << "}};\n"; | |
| for (auto [Idx, CppSpelling] : enumerate(CppSpellings)) | |
| OS << "static constexpr const ArchInfo &" << CppSpelling << " = ArchInfos[" | |
| << Idx << "];\n"; | |
| OS << "#undef EMIT_ARCHITECTURES\n" | |
| << "#endif // EMIT_ARCHITECTURES\n" | |
| << "\n"; | |
| // Emit CPU Aliases | |
| OS << "#ifdef EMIT_CPU_ALIAS\n" | |
| << "inline constexpr Alias CpuAliases[] = {\n"; | |
| llvm::StringSet<> Processors; | |
| for (const Record *Rec : RK.getAllDerivedDefinitions("ProcessorModel")) | |
| Processors.insert(Rec->getValueAsString("Name")); | |
| llvm::StringSet<> Aliases; | |
| for (const Record *Rec : RK.getAllDerivedDefinitions("ProcessorAlias")) { | |
| auto Name = Rec->getValueAsString("Name"); | |
| auto Alias = Rec->getValueAsString("Alias"); | |
| if (!Processors.contains(Alias)) | |
| PrintFatalError( | |
| Rec, "Alias '" + Name + "' references a non-existent ProcessorModel '" + Alias + "'"); | |
| if (Processors.contains(Name)) | |
| PrintFatalError( | |
| Rec, "Alias '" + Name + "' duplicates an existing ProcessorModel"); | |
| if (!Aliases.insert(Name).second) | |
| PrintFatalError( | |
| Rec, "Alias '" + Name + "' duplicates an existing ProcessorAlias"); | |
| OS << " {" << StrTab.GetOrAddStringOffset(Name) << ", " | |
| << StrTab.GetOrAddStringOffset(Alias) << "},\n"; | |
| } | |
| OS << "};\n" | |
| << "#undef EMIT_CPU_ALIAS\n" | |
| << "#endif // EMIT_CPU_ALIAS\n" | |
| << "\n"; | |
| // Emit CPU information | |
| OS << "#ifdef EMIT_CPU_INFO\n" | |
| << "inline constexpr CpuInfo CpuInfos[] = {\n"; | |
| for (const Record *Rec : RK.getAllDerivedDefinitions("ProcessorModel")) { | |
| auto Name = Rec->getValueAsString("Name"); | |
| auto Features = Rec->getValueAsListOfDefs("Features"); | |
| // "apple-latest" is backend-only, should not be accepted by TargetParser. | |
| if (Name == "apple-latest") | |
| continue; | |
| const Record *Arch; | |
| if (Name == "generic") { | |
| // "generic" is an exception. It does not have an architecture, and there | |
| // are tests that depend on e.g. -mattr=-v8.4a meaning HasV8_0aOps==false. | |
| // However, in TargetParser CPUInfo, it is written as 8.0-A. | |
| Arch = RK.getDef("HasV8_0aOps"); | |
| } else { | |
| // Search for an Architecture64 in the list of features. | |
| auto IsArch = [](const Record *F) { | |
| return F->isSubClassOf("Architecture64"); | |
| }; | |
| auto ArchIter = llvm::find_if(Features, IsArch); | |
| if (ArchIter == Features.end()) | |
| PrintFatalError(Rec, "Features must include an Architecture64."); | |
| Arch = *ArchIter; | |
| // Check there is only one Architecture in the list. | |
| if (llvm::count_if(Features, IsArch) > 1) | |
| PrintFatalError(Rec, "Features has multiple Architecture64 entries"); | |
| } | |
| auto Major = Arch->getValueAsInt("Major"); | |
| auto Minor = Arch->getValueAsInt("Minor"); | |
| auto Profile = Arch->getValueAsString("Profile"); | |
| auto ArchInfo = ArchInfoName(Major, Minor, Profile); | |
| unsigned ArchIdx = | |
| llvm::find(CppSpellings, ArchInfo) - CppSpellings.begin(); | |
| checkFeatureTree(Arch); | |
| OS << " {\n" | |
| << " " << StrTab.GetOrAddStringOffset(Name) << ",\n" | |
| << " " << ArchIdx << " /* " << ArchInfo << " */,\n" | |
| << " AArch64::ExtensionBitset({\n"; | |
| // Keep track of extensions we have seen | |
| StringSet<> SeenExts; | |
| for (const Record *E : Rec->getValueAsListOfDefs("Features")) | |
| // Only process subclasses of Extension | |
| if (E->isSubClassOf("Extension")) { | |
| const auto AEK = E->getValueAsString("ArchExtKindSpelling").upper(); | |
| if (!SeenExts.insert(AEK).second) | |
| PrintFatalError(Rec, "feature already added: " + E->getName()); | |
| OS << " AArch64::" << AEK << ",\n"; | |
| } | |
| OS << " })\n" | |
| << " },\n"; | |
| } | |
| OS << "};\n"; | |
| OS << "#undef EMIT_CPU_INFO\n" | |
| << "#endif // EMIT_CPU_INFO\n" | |
| << "\n"; | |
| // Emit string table. | |
| OS << "#ifdef EMIT_STRTAB\n"; | |
| StrTab.EmitStringTableDef(OS, "StrTab"); | |
| OS << "#undef EMIT_STRTAB\n" | |
| << "#endif // EMIT_STRTAB\n" | |
| << "\n"; | |
| } | |
| static TableGen::Emitter::Opt | |
| X("gen-arm-target-def", emitARMTargetDef, | |
| "Generate the ARM or AArch64 Architecture information header."); |