| //===- 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/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 <string> |
| |
| using namespace llvm; |
| |
| static void EmitARMTargetDef(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<Record *> SortedExtensions = |
| RK.getAllDerivedDefinitions("Extension"); |
| auto Alphabetical = [](Record *A, 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 |
| }; |
| std::sort(SortedExtensions.begin(), SortedExtensions.end(), Alphabetical); |
| |
| // 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("Bit"); |
| OS << ", \"" << Rec->getValueAsString("BackendFeatures") << "\""; |
| OS << ", " << (uint64_t)Rec->getValueAsInt("Priority"); |
| 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(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; |
| |
| 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 = [](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); |
| |
| // The apple-latest alias is backend only, do not expose it to -mcpu. |
| if (Name == "apple-latest") |
| continue; |
| |
| OS << " {\n" |
| << " \"" << Name << "\",\n" |
| << " " << ArchInfo << ",\n" |
| << " AArch64::ExtensionBitset({\n"; |
| |
| // Keep track of extensions we have seen |
| StringSet<> SeenExts; |
| for (auto *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."); |