//===- 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 #include #include using namespace llvm; /// Collect the full set of implied features for a SubtargetFeature. static void CollectImpliedFeatures(std::set &SeenFeats, Record *Rec) { assert(Rec->isSubClassOf("SubtargetFeature") && "Rec is not a SubtargetFeature"); SeenFeats.insert(Rec); for (Record *Implied : Rec->getValueAsListOfDefs("Implies")) CollectImpliedFeatures(SeenFeats, Implied); } static void CheckFeatureTree(Record *Root) { std::set 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 DefaultExts{DefaultExtsVec.begin(), DefaultExtsVec.end()}; for (auto *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(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 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::getFMVInfo() {\n" << " static std::vector 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 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 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); CheckFeatureTree(Arch); 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.");