llvm-project/llvm/utils/TableGen/ARMTargetDefEmitter.cpp
Tomas Matheson 72a895336b
[AArch64][TargetParser] move ArchInfo into tablegen [NFC] (#92037)
This moves the architecture version, profile and extension information
into tablegen, and generates the TargetParser ArchInfo objects from
this data.

There are two lists of "dependencies" defined for each architecture: the
SubtargetFeature::Implies which controls which features are
automatically enabled in the backend when the corresponding architecture
SubtargetFeature is enabled; and the list of Extensions which are
enabled by default for this architecture. As far as I can tell, the idea
here is that the SubtargetFeature models the mandatory dependencies (although
they can still be disabled if desired) while the default extensions models
the typical use case for that architecture.
2024-05-16 09:45:14 +01:00

185 lines
7.2 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/StringSet.h"
#include "llvm/Support/Format.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 MarchA = A->getValueAsString("MArchName");
const auto MarchB = B->getValueAsString("MArchName");
return MarchA.compare(MarchB) < 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";
// Emit the ArchExtKind enum
OS << "#ifdef EMIT_ARCHEXTKIND_ENUM\n"
<< "enum ArchExtKind : unsigned {\n"
<< " AEK_NONE = 1,\n";
for (const Record *Rec : SortedExtensions) {
auto AEK = Rec->getValueAsString("ArchExtKindSpelling").upper();
if (AEK != "AEK_NONE")
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("MArchName") << "\"";
if (auto Alias = Rec->getValueAsString("MArchAlias"); Alias.empty())
OS << ", {}";
else
OS << ", \"" << Alias << "\"";
OS << ", AArch64::" << AEK;
if (AEK == "AEK_NONE") {
// HACK: don't emit posfeat/negfeat strings for FMVOnlyExtensions.
OS << ", {}, {}";
} else {
OS << ", \"+" << Rec->getValueAsString("Name") << "\""; // posfeature
OS << ", \"-" << Rec->getValueAsString("Name") << "\""; // negfeature
}
OS << ", " << Rec->getValueAsString("FMVBit");
OS << ", \"" << Rec->getValueAsString("FMVDependencies") << "\"";
OS << ", " << (uint64_t)Rec->getValueAsInt("FMVPriority");
OS << "},\n";
};
OS << " {\"none\", {}, AArch64::AEK_NONE, {}, {}, FEAT_INIT, \"\", "
"ExtensionInfo::MaxFMVPriority},\n";
OS << "};\n"
<< "#undef EMIT_EXTENSIONS\n"
<< "#endif // EMIT_EXTENSIONS\n"
<< "\n";
// Emit architecture information
OS << "#ifdef EMIT_ARCHITECTURES\n";
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 =
Minor == 0 ? "ARMV" + std::to_string(Major) + ProfileUpper.c_str()
: "ARMV" + std::to_string(Major) + "_" +
std::to_string(Minor) + ProfileUpper.c_str();
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")) {
// Only process subclasses of Extension
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 (auto CppSpelling : CppSpellings)
OS << " &" << CppSpelling << ",\n";
OS << "};\n";
OS << "#undef EMIT_ARCHITECTURES\n"
<< "#endif // EMIT_ARCHITECTURES\n"
<< "\n";
}
static TableGen::Emitter::Opt
X("gen-arm-target-def", EmitARMTargetDef,
"Generate the ARM or AArch64 Architecture information header.");