llvm-project/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
Alexandros Lamprineas 8e65940161
[FMV][AArch64] Simplify version selection according to ACLE. (#121921)
Currently, the more features a version has, the higher its priority is.
We are changing ACLE https://github.com/ARM-software/acle/pull/370 as
follows:

"Among any two versions, the higher priority version is determined by
 identifying the highest priority feature that is specified in exactly
 one of the versions, and selecting that version."
2025-01-08 18:59:07 +00:00

363 lines
14 KiB
C++

//===- 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.");