aboutsummaryrefslogtreecommitdiff
path: root/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp')
-rw-r--r--llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp362
1 files changed, 362 insertions, 0 deletions
diff --git a/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp b/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
new file mode 100644
index 0000000..4dea89e
--- /dev/null
+++ b/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
@@ -0,0 +1,362 @@
+//===- 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/TableGenBackend.h"
+#include <cstdint>
+#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;
+
+ // 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 << "{\"" << Rec->getValueAsString("UserVisibleName") << "\"";
+ if (auto Alias = Rec->getValueAsString("UserVisibleAlias"); Alias.empty())
+ OS << ", {}";
+ else
+ OS << ", \"" << Alias << "\"";
+ OS << ", AArch64::" << AEK;
+ OS << ", \"" << Rec->getValueAsString("ArchFeatureName") << "\"";
+ OS << ", \"" << Rec->getValueAsString("Desc") << "\"";
+ OS << ", \"+" << Rec->getValueAsString("Name") << "\""; // posfeature
+ OS << ", \"-" << Rec->getValueAsString("Name") << "\""; // negfeature
+ OS << "},\n";
+ };
+ OS << "};\n"
+ << "#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) {
+ OS << " I.emplace_back(";
+ OS << "\"" << Rec->getValueAsString("Name") << "\"";
+ OS << ", " << Rec->getValueAsString("FeatureBit");
+ OS << ", " << Rec->getValueAsString("PriorityBit");
+ auto FeatName = Rec->getValueAsString("BackendFeature");
+ const Record *FeatRec = ExtensionMap[FeatName];
+ 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");
+ 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++
+ const std::string CppSpelling = ArchInfoName(Major, Minor, ProfileUpper);
+ OS << "inline constexpr ArchInfo " << CppSpelling << " = {\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.
+ if (Minor == 0)
+ OS << llvm::format(" \"armv%d-%s\",\n", Major, ProfileLower.c_str());
+ else
+ OS << llvm::format(" \"armv%d.%d-%s\",\n", Major, Minor,
+ ProfileLower.c_str());
+
+ // SubtargetFeature::Name, used for -target-feature. Here the "+" is added.
+ const auto TargetFeatureName = Rec->getValueAsString("Name");
+ OS << " \"+" << 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"
+ << "/// The set of all architectures\n"
+ << "static constexpr std::array<const ArchInfo *, " << CppSpellings.size()
+ << "> ArchInfos = {\n";
+ for (StringRef CppSpelling : CppSpellings)
+ OS << " &" << CppSpelling << ",\n";
+ OS << "};\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 << llvm::formatv(R"( { "{0}", "{1}" },)", Name, 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);
+
+ checkFeatureTree(Arch);
+
+ OS << " {\n"
+ << " \"" << Name << "\",\n"
+ << " " << 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";
+}
+
+static TableGen::Emitter::Opt
+ X("gen-arm-target-def", emitARMTargetDef,
+ "Generate the ARM or AArch64 Architecture information header.");