diff options
Diffstat (limited to 'llvm/utils/TableGen/Basic')
-rw-r--r-- | llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp | 362 | ||||
-rw-r--r-- | llvm/utils/TableGen/Basic/Attributes.cpp | 148 | ||||
-rw-r--r-- | llvm/utils/TableGen/Basic/CMakeLists.txt | 7 | ||||
-rw-r--r-- | llvm/utils/TableGen/Basic/DirectiveEmitter.cpp | 1225 | ||||
-rw-r--r-- | llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp | 867 | ||||
-rw-r--r-- | llvm/utils/TableGen/Basic/RISCVTargetDefEmitter.cpp | 266 | ||||
-rw-r--r-- | llvm/utils/TableGen/Basic/TableGen.cpp | 99 | ||||
-rw-r--r-- | llvm/utils/TableGen/Basic/TableGen.h | 13 | ||||
-rw-r--r-- | llvm/utils/TableGen/Basic/VTEmitter.cpp | 216 |
9 files changed, 3203 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."); diff --git a/llvm/utils/TableGen/Basic/Attributes.cpp b/llvm/utils/TableGen/Basic/Attributes.cpp new file mode 100644 index 0000000..66ba25c --- /dev/null +++ b/llvm/utils/TableGen/Basic/Attributes.cpp @@ -0,0 +1,148 @@ +//===- Attributes.cpp - Generate attributes -------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +using namespace llvm; + +#define DEBUG_TYPE "attr-enum" + +namespace { + +class Attributes { +public: + Attributes(const RecordKeeper &R) : Records(R) {} + void run(raw_ostream &OS); + +private: + void emitTargetIndependentNames(raw_ostream &OS); + void emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr); + void emitAttributeProperties(raw_ostream &OF); + + const RecordKeeper &Records; +}; + +} // End anonymous namespace. + +void Attributes::emitTargetIndependentNames(raw_ostream &OS) { + OS << "#ifdef GET_ATTR_NAMES\n"; + OS << "#undef GET_ATTR_NAMES\n"; + + OS << "#ifndef ATTRIBUTE_ALL\n"; + OS << "#define ATTRIBUTE_ALL(FIRST, SECOND)\n"; + OS << "#endif\n\n"; + + auto Emit = [&](ArrayRef<StringRef> KindNames, StringRef MacroName) { + OS << "#ifndef " << MacroName << "\n"; + OS << "#define " << MacroName + << "(FIRST, SECOND) ATTRIBUTE_ALL(FIRST, SECOND)\n"; + OS << "#endif\n\n"; + for (StringRef KindName : KindNames) { + for (auto *A : Records.getAllDerivedDefinitions(KindName)) { + OS << MacroName << "(" << A->getName() << "," + << A->getValueAsString("AttrString") << ")\n"; + } + } + OS << "#undef " << MacroName << "\n\n"; + }; + + // Emit attribute enums in the same order llvm::Attribute::operator< expects. + Emit({"EnumAttr", "TypeAttr", "IntAttr", "ConstantRangeAttr", + "ConstantRangeListAttr"}, + "ATTRIBUTE_ENUM"); + Emit({"StrBoolAttr"}, "ATTRIBUTE_STRBOOL"); + Emit({"ComplexStrAttr"}, "ATTRIBUTE_COMPLEXSTR"); + + OS << "#undef ATTRIBUTE_ALL\n"; + OS << "#endif\n\n"; + + OS << "#ifdef GET_ATTR_ENUM\n"; + OS << "#undef GET_ATTR_ENUM\n"; + unsigned Value = 1; // Leave zero for AttrKind::None. + for (StringRef KindName : {"EnumAttr", "TypeAttr", "IntAttr", + "ConstantRangeAttr", "ConstantRangeListAttr"}) { + OS << "First" << KindName << " = " << Value << ",\n"; + for (auto *A : Records.getAllDerivedDefinitions(KindName)) { + OS << A->getName() << " = " << Value << ",\n"; + Value++; + } + OS << "Last" << KindName << " = " << (Value - 1) << ",\n"; + } + OS << "#endif\n\n"; +} + +void Attributes::emitFnAttrCompatCheck(raw_ostream &OS, bool IsStringAttr) { + OS << "#ifdef GET_ATTR_COMPAT_FUNC\n"; + OS << "#undef GET_ATTR_COMPAT_FUNC\n"; + + OS << "static inline bool hasCompatibleFnAttrs(const Function &Caller,\n" + << " const Function &Callee) {\n"; + OS << " bool Ret = true;\n\n"; + + for (const Record *Rule : Records.getAllDerivedDefinitions("CompatRule")) { + StringRef FuncName = Rule->getValueAsString("CompatFunc"); + OS << " Ret &= " << FuncName << "(Caller, Callee"; + StringRef AttrName = Rule->getValueAsString("AttrName"); + if (!AttrName.empty()) + OS << ", \"" << AttrName << "\""; + OS << ");\n"; + } + + OS << "\n"; + OS << " return Ret;\n"; + OS << "}\n\n"; + + OS << "static inline void mergeFnAttrs(Function &Caller,\n" + << " const Function &Callee) {\n"; + + for (const Record *Rule : Records.getAllDerivedDefinitions("MergeRule")) { + StringRef FuncName = Rule->getValueAsString("MergeFunc"); + OS << " " << FuncName << "(Caller, Callee);\n"; + } + + OS << "}\n\n"; + + OS << "#endif\n"; +} + +void Attributes::emitAttributeProperties(raw_ostream &OS) { + OS << "#ifdef GET_ATTR_PROP_TABLE\n"; + OS << "#undef GET_ATTR_PROP_TABLE\n"; + OS << "static const uint8_t AttrPropTable[] = {\n"; + for (StringRef KindName : {"EnumAttr", "TypeAttr", "IntAttr", + "ConstantRangeAttr", "ConstantRangeListAttr"}) { + bool AllowIntersectAnd = KindName == "EnumAttr"; + bool AllowIntersectMin = KindName == "IntAttr"; + for (auto *A : Records.getAllDerivedDefinitions(KindName)) { + OS << "0"; + for (const Init *P : *A->getValueAsListInit("Properties")) { + if (!AllowIntersectAnd && + cast<DefInit>(P)->getDef()->getName() == "IntersectAnd") + PrintFatalError("'IntersectAnd' only compatible with 'EnumAttr'"); + if (!AllowIntersectMin && + cast<DefInit>(P)->getDef()->getName() == "IntersectMin") + PrintFatalError("'IntersectMin' only compatible with 'IntAttr'"); + + OS << " | AttributeProperty::" << cast<DefInit>(P)->getDef()->getName(); + } + OS << ",\n"; + } + } + OS << "};\n"; + OS << "#endif\n"; +} + +void Attributes::run(raw_ostream &OS) { + emitTargetIndependentNames(OS); + emitFnAttrCompatCheck(OS, false); + emitAttributeProperties(OS); +} + +static TableGen::Emitter::OptClass<Attributes> X("gen-attrs", + "Generate attributes"); diff --git a/llvm/utils/TableGen/Basic/CMakeLists.txt b/llvm/utils/TableGen/Basic/CMakeLists.txt index 41d737e..b058fba 100644 --- a/llvm/utils/TableGen/Basic/CMakeLists.txt +++ b/llvm/utils/TableGen/Basic/CMakeLists.txt @@ -9,8 +9,15 @@ set(LLVM_LINK_COMPONENTS ) add_llvm_library(LLVMTableGenBasic OBJECT EXCLUDE_FROM_ALL DISABLE_LLVM_LINK_LLVM_DYLIB + ARMTargetDefEmitter.cpp + Attributes.cpp CodeGenIntrinsics.cpp + DirectiveEmitter.cpp + IntrinsicEmitter.cpp + RISCVTargetDefEmitter.cpp SDNodeProperties.cpp + TableGen.cpp + VTEmitter.cpp ) # Users may include its headers as "Basic/*.h" diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp new file mode 100644 index 0000000..fd815f4 --- /dev/null +++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp @@ -0,0 +1,1225 @@ +//===- DirectiveEmitter.cpp - Directive Language Emitter ------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// DirectiveEmitter uses the descriptions of directives and clauses to construct +// common code declarations to be used in Frontends. +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/DirectiveEmitter.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" + +#include <numeric> +#include <vector> + +using namespace llvm; + +namespace { +// Simple RAII helper for defining ifdef-undef-endif scopes. +class IfDefScope { +public: + IfDefScope(StringRef Name, raw_ostream &OS) : Name(Name), OS(OS) { + OS << "#ifdef " << Name << "\n" + << "#undef " << Name << "\n"; + } + + ~IfDefScope() { OS << "\n#endif // " << Name << "\n\n"; } + +private: + StringRef Name; + raw_ostream &OS; +}; +} // namespace + +// Generate enum class. Entries are emitted in the order in which they appear +// in the `Records` vector. +static void generateEnumClass(ArrayRef<const Record *> Records, raw_ostream &OS, + StringRef Enum, StringRef Prefix, + const DirectiveLanguage &DirLang, + bool ExportEnums) { + OS << "\n"; + OS << "enum class " << Enum << " {\n"; + for (const auto &R : Records) { + BaseRecord Rec(R); + OS << " " << Prefix << Rec.getFormattedName() << ",\n"; + } + OS << "};\n"; + OS << "\n"; + OS << "static constexpr std::size_t " << Enum + << "_enumSize = " << Records.size() << ";\n"; + + // Make the enum values available in the defined namespace. This allows us to + // write something like Enum_X if we have a `using namespace <CppNamespace>`. + // At the same time we do not loose the strong type guarantees of the enum + // class, that is we cannot pass an unsigned as Directive without an explicit + // cast. + if (ExportEnums) { + OS << "\n"; + for (const auto &R : Records) { + BaseRecord Rec(R); + OS << "constexpr auto " << Prefix << Rec.getFormattedName() << " = " + << "llvm::" << DirLang.getCppNamespace() << "::" << Enum + << "::" << Prefix << Rec.getFormattedName() << ";\n"; + } + } +} + +// Generate enums for values that clauses can take. +// Also generate function declarations for get<Enum>Name(StringRef Str). +static void generateEnumClauseVal(ArrayRef<const Record *> Records, + raw_ostream &OS, + const DirectiveLanguage &DirLang, + std::string &EnumHelperFuncs) { + for (const auto &R : Records) { + Clause C(R); + const auto &ClauseVals = C.getClauseVals(); + if (ClauseVals.size() <= 0) + continue; + + const auto &EnumName = C.getEnumName(); + if (EnumName.empty()) { + PrintError("enumClauseValue field not set in Clause" + + C.getFormattedName() + "."); + return; + } + + OS << "\n"; + OS << "enum class " << EnumName << " {\n"; + for (const ClauseVal CVal : ClauseVals) + OS << " " << CVal.getRecordName() << "=" << CVal.getValue() << ",\n"; + OS << "};\n"; + + if (DirLang.hasMakeEnumAvailableInNamespace()) { + OS << "\n"; + for (const auto &CV : ClauseVals) { + OS << "constexpr auto " << CV->getName() << " = " + << "llvm::" << DirLang.getCppNamespace() << "::" << EnumName + << "::" << CV->getName() << ";\n"; + } + EnumHelperFuncs += (Twine("LLVM_ABI ") + Twine(EnumName) + Twine(" get") + + Twine(EnumName) + Twine("(StringRef);\n")) + .str(); + + EnumHelperFuncs += + (Twine("LLVM_ABI llvm::StringRef get") + Twine(DirLang.getName()) + + Twine(EnumName) + Twine("Name(") + Twine(EnumName) + Twine(");\n")) + .str(); + } + } +} + +static bool hasDuplicateClauses(ArrayRef<const Record *> Clauses, + const Directive &Directive, + StringSet<> &CrtClauses) { + bool HasError = false; + for (const VersionedClause VerClause : Clauses) { + const auto InsRes = CrtClauses.insert(VerClause.getClause().getName()); + if (!InsRes.second) { + PrintError("Clause " + VerClause.getClause().getRecordName() + + " already defined on directive " + Directive.getRecordName()); + HasError = true; + } + } + return HasError; +} + +// Check for duplicate clauses in lists. Clauses cannot appear twice in the +// three allowed list. Also, since required implies allowed, clauses cannot +// appear in both the allowedClauses and requiredClauses lists. +static bool +hasDuplicateClausesInDirectives(ArrayRef<const Record *> Directives) { + bool HasDuplicate = false; + for (const Directive Dir : Directives) { + StringSet<> Clauses; + // Check for duplicates in the three allowed lists. + if (hasDuplicateClauses(Dir.getAllowedClauses(), Dir, Clauses) || + hasDuplicateClauses(Dir.getAllowedOnceClauses(), Dir, Clauses) || + hasDuplicateClauses(Dir.getAllowedExclusiveClauses(), Dir, Clauses)) { + HasDuplicate = true; + } + // Check for duplicate between allowedClauses and required + Clauses.clear(); + if (hasDuplicateClauses(Dir.getAllowedClauses(), Dir, Clauses) || + hasDuplicateClauses(Dir.getRequiredClauses(), Dir, Clauses)) { + HasDuplicate = true; + } + if (HasDuplicate) + PrintFatalError("One or more clauses are defined multiple times on" + " directive " + + Dir.getRecordName()); + } + + return HasDuplicate; +} + +// Check consitency of records. Return true if an error has been detected. +// Return false if the records are valid. +bool DirectiveLanguage::HasValidityErrors() const { + if (getDirectiveLanguages().size() != 1) { + PrintFatalError("A single definition of DirectiveLanguage is needed."); + return true; + } + + return hasDuplicateClausesInDirectives(getDirectives()); +} + +// Count the maximum number of leaf constituents per construct. +static size_t getMaxLeafCount(const DirectiveLanguage &DirLang) { + size_t MaxCount = 0; + for (const Directive D : DirLang.getDirectives()) + MaxCount = std::max(MaxCount, D.getLeafConstructs().size()); + return MaxCount; +} + +// Generate the declaration section for the enumeration in the directive +// language. +static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) { + const auto DirLang = DirectiveLanguage(Records); + if (DirLang.HasValidityErrors()) + return; + + OS << "#ifndef LLVM_" << DirLang.getName() << "_INC\n"; + OS << "#define LLVM_" << DirLang.getName() << "_INC\n"; + OS << "\n#include \"llvm/ADT/ArrayRef.h\"\n"; + + if (DirLang.hasEnableBitmaskEnumInNamespace()) + OS << "#include \"llvm/ADT/BitmaskEnum.h\"\n"; + + OS << "#include \"llvm/Support/Compiler.h\"\n"; + OS << "#include <cstddef>\n"; // for size_t + OS << "\n"; + OS << "namespace llvm {\n"; + OS << "class StringRef;\n"; + + // Open namespaces defined in the directive language + SmallVector<StringRef, 2> Namespaces; + SplitString(DirLang.getCppNamespace(), Namespaces, "::"); + for (auto Ns : Namespaces) + OS << "namespace " << Ns << " {\n"; + + if (DirLang.hasEnableBitmaskEnumInNamespace()) + OS << "\nLLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();\n"; + + // Emit Directive associations + std::vector<const Record *> Associations; + copy_if(DirLang.getAssociations(), std::back_inserter(Associations), + // Skip the "special" value + [](const Record *Def) { return Def->getName() != "AS_FromLeaves"; }); + generateEnumClass(Associations, OS, "Association", + /*Prefix=*/"", DirLang, /*ExportEnums=*/false); + + generateEnumClass(DirLang.getCategories(), OS, "Category", /*Prefix=*/"", + DirLang, /*ExportEnums=*/false); + + // Emit Directive enumeration + generateEnumClass(DirLang.getDirectives(), OS, "Directive", + DirLang.getDirectivePrefix(), DirLang, + DirLang.hasMakeEnumAvailableInNamespace()); + + // Emit Clause enumeration + generateEnumClass(DirLang.getClauses(), OS, "Clause", + DirLang.getClausePrefix(), DirLang, + DirLang.hasMakeEnumAvailableInNamespace()); + + // Emit ClauseVal enumeration + std::string EnumHelperFuncs; + generateEnumClauseVal(DirLang.getClauses(), OS, DirLang, EnumHelperFuncs); + + // Generic function signatures + OS << "\n"; + OS << "// Enumeration helper functions\n"; + OS << "LLVM_ABI Directive get" << DirLang.getName() + << "DirectiveKind(llvm::StringRef Str);\n"; + OS << "\n"; + OS << "LLVM_ABI llvm::StringRef get" << DirLang.getName() + << "DirectiveName(Directive D);\n"; + OS << "\n"; + OS << "LLVM_ABI Clause get" << DirLang.getName() + << "ClauseKind(llvm::StringRef Str);\n"; + OS << "\n"; + OS << "LLVM_ABI llvm::StringRef get" << DirLang.getName() + << "ClauseName(Clause C);\n"; + OS << "\n"; + OS << "/// Return true if \\p C is a valid clause for \\p D in version \\p " + << "Version.\n"; + OS << "LLVM_ABI bool isAllowedClauseForDirective(Directive D, " + << "Clause C, unsigned Version);\n"; + OS << "\n"; + OS << "constexpr std::size_t getMaxLeafCount() { return " + << getMaxLeafCount(DirLang) << "; }\n"; + OS << "LLVM_ABI Association getDirectiveAssociation(Directive D);\n"; + OS << "LLVM_ABI Category getDirectiveCategory(Directive D);\n"; + if (EnumHelperFuncs.length() > 0) { + OS << EnumHelperFuncs; + OS << "\n"; + } + + // Closing namespaces + for (auto Ns : reverse(Namespaces)) + OS << "} // namespace " << Ns << "\n"; + + OS << "} // namespace llvm\n"; + + OS << "#endif // LLVM_" << DirLang.getName() << "_INC\n"; +} + +// Generate function implementation for get<Enum>Name(StringRef Str) +static void generateGetName(ArrayRef<const Record *> Records, raw_ostream &OS, + StringRef Enum, const DirectiveLanguage &DirLang, + StringRef Prefix) { + OS << "\n"; + OS << "llvm::StringRef llvm::" << DirLang.getCppNamespace() << "::get" + << DirLang.getName() << Enum << "Name(" << Enum << " Kind) {\n"; + OS << " switch (Kind) {\n"; + for (const BaseRecord Rec : Records) { + OS << " case " << Prefix << Rec.getFormattedName() << ":\n"; + OS << " return \""; + if (Rec.getAlternativeName().empty()) + OS << Rec.getName(); + else + OS << Rec.getAlternativeName(); + OS << "\";\n"; + } + OS << " }\n"; // switch + OS << " llvm_unreachable(\"Invalid " << DirLang.getName() << " " << Enum + << " kind\");\n"; + OS << "}\n"; +} + +// Generate function implementation for get<Enum>Kind(StringRef Str) +static void generateGetKind(ArrayRef<const Record *> Records, raw_ostream &OS, + StringRef Enum, const DirectiveLanguage &DirLang, + StringRef Prefix, bool ImplicitAsUnknown) { + + const auto *DefaultIt = find_if( + Records, [](const Record *R) { return R->getValueAsBit("isDefault"); }); + + if (DefaultIt == Records.end()) { + PrintError("At least one " + Enum + " must be defined as default."); + return; + } + + BaseRecord DefaultRec(*DefaultIt); + + OS << "\n"; + OS << Enum << " llvm::" << DirLang.getCppNamespace() << "::get" + << DirLang.getName() << Enum << "Kind(llvm::StringRef Str) {\n"; + OS << " return llvm::StringSwitch<" << Enum << ">(Str)\n"; + + for (const auto &R : Records) { + BaseRecord Rec(R); + if (ImplicitAsUnknown && R->getValueAsBit("isImplicit")) { + OS << " .Case(\"" << Rec.getName() << "\"," << Prefix + << DefaultRec.getFormattedName() << ")\n"; + } else { + OS << " .Case(\"" << Rec.getName() << "\"," << Prefix + << Rec.getFormattedName() << ")\n"; + } + } + OS << " .Default(" << Prefix << DefaultRec.getFormattedName() << ");\n"; + OS << "}\n"; +} + +// Generate function implementation for get<ClauseVal>Kind(StringRef Str) +static void generateGetKindClauseVal(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + for (const Clause C : DirLang.getClauses()) { + const auto &ClauseVals = C.getClauseVals(); + if (ClauseVals.size() <= 0) + continue; + + auto DefaultIt = find_if(ClauseVals, [](const Record *CV) { + return CV->getValueAsBit("isDefault"); + }); + + if (DefaultIt == ClauseVals.end()) { + PrintError("At least one val in Clause " + C.getFormattedName() + + " must be defined as default."); + return; + } + const auto DefaultName = (*DefaultIt)->getName(); + + const auto &EnumName = C.getEnumName(); + if (EnumName.empty()) { + PrintError("enumClauseValue field not set in Clause" + + C.getFormattedName() + "."); + return; + } + + OS << "\n"; + OS << EnumName << " llvm::" << DirLang.getCppNamespace() << "::get" + << EnumName << "(llvm::StringRef Str) {\n"; + OS << " return llvm::StringSwitch<" << EnumName << ">(Str)\n"; + for (const auto &CV : ClauseVals) { + ClauseVal CVal(CV); + OS << " .Case(\"" << CVal.getFormattedName() << "\"," << CV->getName() + << ")\n"; + } + OS << " .Default(" << DefaultName << ");\n"; + OS << "}\n"; + + OS << "\n"; + OS << "llvm::StringRef llvm::" << DirLang.getCppNamespace() << "::get" + << DirLang.getName() << EnumName + << "Name(llvm::" << DirLang.getCppNamespace() << "::" << EnumName + << " x) {\n"; + OS << " switch (x) {\n"; + for (const auto &CV : ClauseVals) { + ClauseVal CVal(CV); + OS << " case " << CV->getName() << ":\n"; + OS << " return \"" << CVal.getFormattedName() << "\";\n"; + } + OS << " }\n"; // switch + OS << " llvm_unreachable(\"Invalid " << DirLang.getName() << " " + << EnumName << " kind\");\n"; + OS << "}\n"; + } +} + +static void generateCaseForVersionedClauses(ArrayRef<const Record *> Clauses, + raw_ostream &OS, + StringRef DirectiveName, + const DirectiveLanguage &DirLang, + StringSet<> &Cases) { + for (const VersionedClause VerClause : Clauses) { + const auto ClauseFormattedName = VerClause.getClause().getFormattedName(); + + if (Cases.insert(ClauseFormattedName).second) { + OS << " case " << DirLang.getClausePrefix() << ClauseFormattedName + << ":\n"; + OS << " return " << VerClause.getMinVersion() + << " <= Version && " << VerClause.getMaxVersion() << " >= Version;\n"; + } + } +} + +static std::string getDirectiveName(const DirectiveLanguage &DirLang, + const Record *Rec) { + Directive Dir(Rec); + return (Twine("llvm::") + DirLang.getCppNamespace() + + "::" + DirLang.getDirectivePrefix() + Dir.getFormattedName()) + .str(); +} + +static std::string getDirectiveType(const DirectiveLanguage &DirLang) { + return (Twine("llvm::") + DirLang.getCppNamespace() + "::Directive").str(); +} + +// Generate the isAllowedClauseForDirective function implementation. +static void generateIsAllowedClause(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + OS << "\n"; + OS << "bool llvm::" << DirLang.getCppNamespace() + << "::isAllowedClauseForDirective(" + << "Directive D, Clause C, unsigned Version) {\n"; + OS << " assert(unsigned(D) <= llvm::" << DirLang.getCppNamespace() + << "::Directive_enumSize);\n"; + OS << " assert(unsigned(C) <= llvm::" << DirLang.getCppNamespace() + << "::Clause_enumSize);\n"; + + OS << " switch (D) {\n"; + + for (const Directive Dir : DirLang.getDirectives()) { + OS << " case " << DirLang.getDirectivePrefix() << Dir.getFormattedName() + << ":\n"; + if (Dir.getAllowedClauses().empty() && + Dir.getAllowedOnceClauses().empty() && + Dir.getAllowedExclusiveClauses().empty() && + Dir.getRequiredClauses().empty()) { + OS << " return false;\n"; + } else { + OS << " switch (C) {\n"; + + StringSet<> Cases; + + generateCaseForVersionedClauses(Dir.getAllowedClauses(), OS, + Dir.getName(), DirLang, Cases); + + generateCaseForVersionedClauses(Dir.getAllowedOnceClauses(), OS, + Dir.getName(), DirLang, Cases); + + generateCaseForVersionedClauses(Dir.getAllowedExclusiveClauses(), OS, + Dir.getName(), DirLang, Cases); + + generateCaseForVersionedClauses(Dir.getRequiredClauses(), OS, + Dir.getName(), DirLang, Cases); + + OS << " default:\n"; + OS << " return false;\n"; + OS << " }\n"; // End of clauses switch + } + OS << " break;\n"; + } + + OS << " }\n"; // End of directives switch + OS << " llvm_unreachable(\"Invalid " << DirLang.getName() + << " Directive kind\");\n"; + OS << "}\n"; // End of function isAllowedClauseForDirective +} + +static void emitLeafTable(const DirectiveLanguage &DirLang, raw_ostream &OS, + StringRef TableName) { + // The leaf constructs are emitted in a form of a 2D table, where each + // row corresponds to a directive (and there is a row for each directive). + // + // Each row consists of + // - the id of the directive itself, + // - number of leaf constructs that will follow (0 for leafs), + // - ids of the leaf constructs (none if the directive is itself a leaf). + // The total number of these entries is at most MaxLeafCount+2. If this + // number is less than that, it is padded to occupy exactly MaxLeafCount+2 + // entries in memory. + // + // The rows are stored in the table in the lexicographical order. This + // is intended to enable binary search when mapping a sequence of leafs + // back to the compound directive. + // The consequence of that is that in order to find a row corresponding + // to the given directive, we'd need to scan the first element of each + // row. To avoid this, an auxiliary ordering table is created, such that + // row for Dir_A = table[auxiliary[Dir_A]]. + + ArrayRef<const Record *> Directives = DirLang.getDirectives(); + DenseMap<const Record *, int> DirId; // Record * -> llvm::omp::Directive + + for (auto [Idx, Rec] : enumerate(Directives)) + DirId.insert(std::make_pair(Rec, Idx)); + + using LeafList = std::vector<int>; + int MaxLeafCount = getMaxLeafCount(DirLang); + + // The initial leaf table, rows order is same as directive order. + std::vector<LeafList> LeafTable(Directives.size()); + for (auto [Idx, Rec] : enumerate(Directives)) { + Directive Dir(Rec); + std::vector<const Record *> Leaves = Dir.getLeafConstructs(); + + auto &List = LeafTable[Idx]; + List.resize(MaxLeafCount + 2); + List[0] = Idx; // The id of the directive itself. + List[1] = Leaves.size(); // The number of leaves to follow. + + for (int I = 0; I != MaxLeafCount; ++I) + List[I + 2] = + static_cast<size_t>(I) < Leaves.size() ? DirId.at(Leaves[I]) : -1; + } + + // Some Fortran directives are delimited, i.e. they have the form of + // "directive"---"end directive". If "directive" is a compound construct, + // then the set of leaf constituents will be nonempty and the same for + // both directives. Given this set of leafs, looking up the corresponding + // compound directive should return "directive", and not "end directive". + // To avoid this problem, gather all "end directives" at the end of the + // leaf table, and only do the search on the initial segment of the table + // that excludes the "end directives". + // It's safe to find all directives whose names begin with "end ". The + // problem only exists for compound directives, like "end do simd". + // All existing directives with names starting with "end " are either + // "end directives" for an existing "directive", or leaf directives + // (such as "end declare target"). + DenseSet<int> EndDirectives; + for (auto [Rec, Id] : DirId) { + if (Directive(Rec).getName().starts_with_insensitive("end ")) + EndDirectives.insert(Id); + } + + // Avoid sorting the vector<vector> array, instead sort an index array. + // It will also be useful later to create the auxiliary indexing array. + std::vector<int> Ordering(Directives.size()); + std::iota(Ordering.begin(), Ordering.end(), 0); + + sort(Ordering, [&](int A, int B) { + auto &LeavesA = LeafTable[A]; + auto &LeavesB = LeafTable[B]; + int DirA = LeavesA[0], DirB = LeavesB[0]; + // First of all, end directives compare greater than non-end directives. + int IsEndA = EndDirectives.count(DirA), IsEndB = EndDirectives.count(DirB); + if (IsEndA != IsEndB) + return IsEndA < IsEndB; + if (LeavesA[1] == 0 && LeavesB[1] == 0) + return DirA < DirB; + return std::lexicographical_compare(&LeavesA[2], &LeavesA[2] + LeavesA[1], + &LeavesB[2], &LeavesB[2] + LeavesB[1]); + }); + + // Emit the table + + // The directives are emitted into a scoped enum, for which the underlying + // type is `int` (by default). The code above uses `int` to store directive + // ids, so make sure that we catch it when something changes in the + // underlying type. + std::string DirectiveType = getDirectiveType(DirLang); + OS << "\nstatic_assert(sizeof(" << DirectiveType << ") == sizeof(int));\n"; + + OS << "[[maybe_unused]] static const " << DirectiveType << ' ' << TableName + << "[][" << MaxLeafCount + 2 << "] = {\n"; + for (size_t I = 0, E = Directives.size(); I != E; ++I) { + auto &Leaves = LeafTable[Ordering[I]]; + OS << " {" << getDirectiveName(DirLang, Directives[Leaves[0]]); + OS << ", static_cast<" << DirectiveType << ">(" << Leaves[1] << "),"; + for (size_t I = 2, E = Leaves.size(); I != E; ++I) { + int Idx = Leaves[I]; + if (Idx >= 0) + OS << ' ' << getDirectiveName(DirLang, Directives[Leaves[I]]) << ','; + else + OS << " static_cast<" << DirectiveType << ">(-1),"; + } + OS << "},\n"; + } + OS << "};\n\n"; + + // Emit a marker where the first "end directive" is. + auto FirstE = find_if(Ordering, [&](int RowIdx) { + return EndDirectives.count(LeafTable[RowIdx][0]); + }); + OS << "[[maybe_unused]] static auto " << TableName + << "EndDirective = " << TableName << " + " + << std::distance(Ordering.begin(), FirstE) << ";\n\n"; + + // Emit the auxiliary index table: it's the inverse of the `Ordering` + // table above. + OS << "[[maybe_unused]] static const int " << TableName << "Ordering[] = {\n"; + OS << " "; + std::vector<int> Reverse(Ordering.size()); + for (int I = 0, E = Ordering.size(); I != E; ++I) + Reverse[Ordering[I]] = I; + for (int Idx : Reverse) + OS << ' ' << Idx << ','; + OS << "\n};\n"; +} + +static void generateGetDirectiveAssociation(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + enum struct Association { + None = 0, // None should be the smallest value. + Block, // The values of the rest don't matter. + Declaration, + Delimited, + Loop, + Separating, + FromLeaves, + Invalid, + }; + + ArrayRef<const Record *> Associations = DirLang.getAssociations(); + + auto GetAssocValue = [](StringRef Name) -> Association { + return StringSwitch<Association>(Name) + .Case("AS_Block", Association::Block) + .Case("AS_Declaration", Association::Declaration) + .Case("AS_Delimited", Association::Delimited) + .Case("AS_Loop", Association::Loop) + .Case("AS_None", Association::None) + .Case("AS_Separating", Association::Separating) + .Case("AS_FromLeaves", Association::FromLeaves) + .Default(Association::Invalid); + }; + + auto GetAssocName = [&](Association A) -> StringRef { + if (A != Association::Invalid && A != Association::FromLeaves) { + const auto *F = find_if(Associations, [&](const Record *R) { + return GetAssocValue(R->getName()) == A; + }); + if (F != Associations.end()) + return (*F)->getValueAsString("name"); // enum name + } + llvm_unreachable("Unexpected association value"); + }; + + auto ErrorPrefixFor = [&](Directive D) -> std::string { + return (Twine("Directive '") + D.getName() + "' in namespace '" + + DirLang.getCppNamespace() + "' ") + .str(); + }; + + auto Reduce = [&](Association A, Association B) -> Association { + if (A > B) + std::swap(A, B); + + // Calculate the result using the following rules: + // x + x = x + // AS_None + x = x + // AS_Block + AS_Loop = AS_Loop + if (A == Association::None || A == B) + return B; + if (A == Association::Block && B == Association::Loop) + return B; + if (A == Association::Loop && B == Association::Block) + return A; + return Association::Invalid; + }; + + DenseMap<const Record *, Association> AsMap; + + auto CompAssocImpl = [&](const Record *R, auto &&Self) -> Association { + if (auto F = AsMap.find(R); F != AsMap.end()) + return F->second; + + Directive D(R); + Association AS = GetAssocValue(D.getAssociation()->getName()); + if (AS == Association::Invalid) { + PrintFatalError(ErrorPrefixFor(D) + + "has an unrecognized value for association: '" + + D.getAssociation()->getName() + "'"); + } + if (AS != Association::FromLeaves) { + AsMap.insert(std::make_pair(R, AS)); + return AS; + } + // Compute the association from leaf constructs. + std::vector<const Record *> Leaves = D.getLeafConstructs(); + if (Leaves.empty()) { + errs() << D.getName() << '\n'; + PrintFatalError(ErrorPrefixFor(D) + + "requests association to be computed from leaves, " + "but it has no leaves"); + } + + Association Result = Self(Leaves[0], Self); + for (int I = 1, E = Leaves.size(); I < E; ++I) { + Association A = Self(Leaves[I], Self); + Association R = Reduce(Result, A); + if (R == Association::Invalid) { + PrintFatalError(ErrorPrefixFor(D) + + "has leaves with incompatible association values: " + + GetAssocName(A) + " and " + GetAssocName(R)); + } + Result = R; + } + + assert(Result != Association::Invalid); + assert(Result != Association::FromLeaves); + AsMap.insert(std::make_pair(R, Result)); + return Result; + }; + + for (const Record *R : DirLang.getDirectives()) + CompAssocImpl(R, CompAssocImpl); // Updates AsMap. + + OS << '\n'; + + auto GetQualifiedName = [&](StringRef Formatted) -> std::string { + return (Twine("llvm::") + DirLang.getCppNamespace() + + "::Directive::" + DirLang.getDirectivePrefix() + Formatted) + .str(); + }; + + std::string DirectiveTypeName = + "llvm::" + DirLang.getCppNamespace().str() + "::Directive"; + std::string AssociationTypeName = + "llvm::" + DirLang.getCppNamespace().str() + "::Association"; + + OS << AssociationTypeName << " llvm::" << DirLang.getCppNamespace() + << "::getDirectiveAssociation(" << DirectiveTypeName << " Dir) {\n"; + OS << " switch (Dir) {\n"; + for (const Record *R : DirLang.getDirectives()) { + if (auto F = AsMap.find(R); F != AsMap.end()) { + Directive Dir(R); + OS << " case " << GetQualifiedName(Dir.getFormattedName()) << ":\n"; + OS << " return " << AssociationTypeName + << "::" << GetAssocName(F->second) << ";\n"; + } + } + OS << " } // switch (Dir)\n"; + OS << " llvm_unreachable(\"Unexpected directive\");\n"; + OS << "}\n"; +} + +static void generateGetDirectiveCategory(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + std::string LangNamespace = "llvm::" + DirLang.getCppNamespace().str(); + std::string CategoryTypeName = LangNamespace + "::Category"; + std::string CategoryNamespace = CategoryTypeName + "::"; + + OS << '\n'; + OS << CategoryTypeName << ' ' << LangNamespace << "::getDirectiveCategory(" + << getDirectiveType(DirLang) << " Dir) {\n"; + OS << " switch (Dir) {\n"; + + for (const Record *R : DirLang.getDirectives()) { + Directive D(R); + OS << " case " << getDirectiveName(DirLang, R) << ":\n"; + OS << " return " << CategoryNamespace + << D.getCategory()->getValueAsString("name") << ";\n"; + } + OS << " } // switch (Dir)\n"; + OS << " llvm_unreachable(\"Unexpected directive\");\n"; + OS << "}\n"; +} + +// Generate a simple enum set with the give clauses. +static void generateClauseSet(ArrayRef<const Record *> Clauses, raw_ostream &OS, + StringRef ClauseSetPrefix, const Directive &Dir, + const DirectiveLanguage &DirLang) { + + OS << "\n"; + OS << " static " << DirLang.getClauseEnumSetClass() << " " << ClauseSetPrefix + << DirLang.getDirectivePrefix() << Dir.getFormattedName() << " {\n"; + + for (const auto &C : Clauses) { + VersionedClause VerClause(C); + OS << " llvm::" << DirLang.getCppNamespace() + << "::Clause::" << DirLang.getClausePrefix() + << VerClause.getClause().getFormattedName() << ",\n"; + } + OS << " };\n"; +} + +// Generate an enum set for the 4 kinds of clauses linked to a directive. +static void generateDirectiveClauseSets(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + + IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_SETS", OS); + + OS << "\n"; + OS << "namespace llvm {\n"; + + // Open namespaces defined in the directive language. + SmallVector<StringRef, 2> Namespaces; + SplitString(DirLang.getCppNamespace(), Namespaces, "::"); + for (auto Ns : Namespaces) + OS << "namespace " << Ns << " {\n"; + + for (const Directive Dir : DirLang.getDirectives()) { + OS << "\n"; + OS << " // Sets for " << Dir.getName() << "\n"; + + generateClauseSet(Dir.getAllowedClauses(), OS, "allowedClauses_", Dir, + DirLang); + generateClauseSet(Dir.getAllowedOnceClauses(), OS, "allowedOnceClauses_", + Dir, DirLang); + generateClauseSet(Dir.getAllowedExclusiveClauses(), OS, + "allowedExclusiveClauses_", Dir, DirLang); + generateClauseSet(Dir.getRequiredClauses(), OS, "requiredClauses_", Dir, + DirLang); + } + + // Closing namespaces + for (auto Ns : reverse(Namespaces)) + OS << "} // namespace " << Ns << "\n"; + + OS << "} // namespace llvm\n"; +} + +// Generate a map of directive (key) with DirectiveClauses struct as values. +// The struct holds the 4 sets of enumeration for the 4 kinds of clauses +// allowances (allowed, allowed once, allowed exclusive and required). +static void generateDirectiveClauseMap(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + + IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_MAP", OS); + + OS << "\n"; + OS << "{\n"; + + for (const Directive Dir : DirLang.getDirectives()) { + OS << " {llvm::" << DirLang.getCppNamespace() + << "::Directive::" << DirLang.getDirectivePrefix() + << Dir.getFormattedName() << ",\n"; + OS << " {\n"; + OS << " llvm::" << DirLang.getCppNamespace() << "::allowedClauses_" + << DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n"; + OS << " llvm::" << DirLang.getCppNamespace() << "::allowedOnceClauses_" + << DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n"; + OS << " llvm::" << DirLang.getCppNamespace() + << "::allowedExclusiveClauses_" << DirLang.getDirectivePrefix() + << Dir.getFormattedName() << ",\n"; + OS << " llvm::" << DirLang.getCppNamespace() << "::requiredClauses_" + << DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n"; + OS << " }\n"; + OS << " },\n"; + } + + OS << "}\n"; +} + +// Generate classes entry for Flang clauses in the Flang parse-tree +// If the clause as a non-generic class, no entry is generated. +// If the clause does not hold a value, an EMPTY_CLASS is used. +// If the clause class is generic then a WRAPPER_CLASS is used. When the value +// is optional, the value class is wrapped into a std::optional. +static void generateFlangClauseParserClass(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + + IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_CLASSES", OS); + + OS << "\n"; + + for (const Clause Clause : DirLang.getClauses()) { + if (!Clause.getFlangClass().empty()) { + OS << "WRAPPER_CLASS(" << Clause.getFormattedParserClassName() << ", "; + if (Clause.isValueOptional() && Clause.isValueList()) { + OS << "std::optional<std::list<" << Clause.getFlangClass() << ">>"; + } else if (Clause.isValueOptional()) { + OS << "std::optional<" << Clause.getFlangClass() << ">"; + } else if (Clause.isValueList()) { + OS << "std::list<" << Clause.getFlangClass() << ">"; + } else { + OS << Clause.getFlangClass(); + } + } else { + OS << "EMPTY_CLASS(" << Clause.getFormattedParserClassName(); + } + OS << ");\n"; + } +} + +// Generate a list of the different clause classes for Flang. +static void generateFlangClauseParserClassList(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + + IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_CLASSES_LIST", OS); + + OS << "\n"; + interleaveComma(DirLang.getClauses(), OS, [&](const Record *C) { + Clause Clause(C); + OS << Clause.getFormattedParserClassName() << "\n"; + }); +} + +// Generate dump node list for the clauses holding a generic class name. +static void generateFlangClauseDump(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + + IfDefScope Scope("GEN_FLANG_DUMP_PARSE_TREE_CLAUSES", OS); + + OS << "\n"; + for (const Clause Clause : DirLang.getClauses()) { + OS << "NODE(" << DirLang.getFlangClauseBaseClass() << ", " + << Clause.getFormattedParserClassName() << ")\n"; + } +} + +// Generate Unparse functions for clauses classes in the Flang parse-tree +// If the clause is a non-generic class, no entry is generated. +static void generateFlangClauseUnparse(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + + IfDefScope Scope("GEN_FLANG_CLAUSE_UNPARSE", OS); + + OS << "\n"; + + for (const Clause Clause : DirLang.getClauses()) { + if (!Clause.getFlangClass().empty()) { + if (Clause.isValueOptional() && Clause.getDefaultValue().empty()) { + OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass() + << "::" << Clause.getFormattedParserClassName() << " &x) {\n"; + OS << " Word(\"" << Clause.getName().upper() << "\");\n"; + + OS << " Walk(\"(\", x.v, \")\");\n"; + OS << "}\n"; + } else if (Clause.isValueOptional()) { + OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass() + << "::" << Clause.getFormattedParserClassName() << " &x) {\n"; + OS << " Word(\"" << Clause.getName().upper() << "\");\n"; + OS << " Put(\"(\");\n"; + OS << " if (x.v.has_value())\n"; + if (Clause.isValueList()) + OS << " Walk(x.v, \",\");\n"; + else + OS << " Walk(x.v);\n"; + OS << " else\n"; + OS << " Put(\"" << Clause.getDefaultValue() << "\");\n"; + OS << " Put(\")\");\n"; + OS << "}\n"; + } else { + OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass() + << "::" << Clause.getFormattedParserClassName() << " &x) {\n"; + OS << " Word(\"" << Clause.getName().upper() << "\");\n"; + OS << " Put(\"(\");\n"; + if (Clause.isValueList()) + OS << " Walk(x.v, \",\");\n"; + else + OS << " Walk(x.v);\n"; + OS << " Put(\")\");\n"; + OS << "}\n"; + } + } else { + OS << "void Before(const " << DirLang.getFlangClauseBaseClass() + << "::" << Clause.getFormattedParserClassName() << " &) { Word(\"" + << Clause.getName().upper() << "\"); }\n"; + } + } +} + +// Generate check in the Enter functions for clauses classes. +static void generateFlangClauseCheckPrototypes(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + + IfDefScope Scope("GEN_FLANG_CLAUSE_CHECK_ENTER", OS); + + OS << "\n"; + for (const Clause Clause : DirLang.getClauses()) { + OS << "void Enter(const parser::" << DirLang.getFlangClauseBaseClass() + << "::" << Clause.getFormattedParserClassName() << " &);\n"; + } +} + +// Generate the mapping for clauses between the parser class and the +// corresponding clause Kind +static void generateFlangClauseParserKindMap(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + + IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_KIND_MAP", OS); + + OS << "\n"; + for (const Clause Clause : DirLang.getClauses()) { + OS << "if constexpr (std::is_same_v<A, parser::" + << DirLang.getFlangClauseBaseClass() + << "::" << Clause.getFormattedParserClassName(); + OS << ">)\n"; + OS << " return llvm::" << DirLang.getCppNamespace() + << "::Clause::" << DirLang.getClausePrefix() << Clause.getFormattedName() + << ";\n"; + } + + OS << "llvm_unreachable(\"Invalid " << DirLang.getName() + << " Parser clause\");\n"; +} + +static bool compareClauseName(const Record *R1, const Record *R2) { + Clause C1(R1); + Clause C2(R2); + return (C1.getName() > C2.getName()); +} + +// Generate the parser for the clauses. +static void generateFlangClausesParser(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + std::vector<const Record *> Clauses = DirLang.getClauses(); + // Sort clauses in reverse alphabetical order so with clauses with same + // beginning, the longer option is tried before. + sort(Clauses, compareClauseName); + IfDefScope Scope("GEN_FLANG_CLAUSES_PARSER", OS); + OS << "\n"; + unsigned Index = 0; + unsigned LastClauseIndex = Clauses.size() - 1; + OS << "TYPE_PARSER(\n"; + for (const Clause Clause : Clauses) { + if (Clause.getAliases().empty()) { + OS << " \"" << Clause.getName() << "\""; + } else { + OS << " (" + << "\"" << Clause.getName() << "\"_tok"; + for (StringRef Alias : Clause.getAliases()) { + OS << " || \"" << Alias << "\"_tok"; + } + OS << ")"; + } + + OS << " >> construct<" << DirLang.getFlangClauseBaseClass() + << ">(construct<" << DirLang.getFlangClauseBaseClass() + << "::" << Clause.getFormattedParserClassName() << ">("; + if (Clause.getFlangClass().empty()) { + OS << "))"; + if (Index != LastClauseIndex) + OS << " ||"; + OS << "\n"; + ++Index; + continue; + } + + if (Clause.isValueOptional()) + OS << "maybe("; + OS << "parenthesized("; + if (Clause.isValueList()) + OS << "nonemptyList("; + + if (!Clause.getPrefix().empty()) + OS << "\"" << Clause.getPrefix() << ":\" >> "; + + // The common Flang parser are used directly. Their name is identical to + // the Flang class with first letter as lowercase. If the Flang class is + // not a common class, we assume there is a specific Parser<>{} with the + // Flang class name provided. + SmallString<128> Scratch; + StringRef Parser = + StringSwitch<StringRef>(Clause.getFlangClass()) + .Case("Name", "name") + .Case("ScalarIntConstantExpr", "scalarIntConstantExpr") + .Case("ScalarIntExpr", "scalarIntExpr") + .Case("ScalarExpr", "scalarExpr") + .Case("ScalarLogicalExpr", "scalarLogicalExpr") + .Default(("Parser<" + Clause.getFlangClass() + ">{}") + .toStringRef(Scratch)); + OS << Parser; + if (!Clause.getPrefix().empty() && Clause.isPrefixOptional()) + OS << " || " << Parser; + if (Clause.isValueList()) // close nonemptyList(. + OS << ")"; + OS << ")"; // close parenthesized(. + + if (Clause.isValueOptional()) // close maybe(. + OS << ")"; + OS << "))"; + if (Index != LastClauseIndex) + OS << " ||"; + OS << "\n"; + ++Index; + } + OS << ")\n"; +} + +// Generate the implementation section for the enumeration in the directive +// language +static void emitDirectivesFlangImpl(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + generateDirectiveClauseSets(DirLang, OS); + + generateDirectiveClauseMap(DirLang, OS); + + generateFlangClauseParserClass(DirLang, OS); + + generateFlangClauseParserClassList(DirLang, OS); + + generateFlangClauseDump(DirLang, OS); + + generateFlangClauseUnparse(DirLang, OS); + + generateFlangClauseCheckPrototypes(DirLang, OS); + + generateFlangClauseParserKindMap(DirLang, OS); + + generateFlangClausesParser(DirLang, OS); +} + +static void generateClauseClassMacro(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + // Generate macros style information for legacy code in clang + IfDefScope Scope("GEN_CLANG_CLAUSE_CLASS", OS); + + OS << "\n"; + + OS << "#ifndef CLAUSE\n"; + OS << "#define CLAUSE(Enum, Str, Implicit)\n"; + OS << "#endif\n"; + OS << "#ifndef CLAUSE_CLASS\n"; + OS << "#define CLAUSE_CLASS(Enum, Str, Class)\n"; + OS << "#endif\n"; + OS << "#ifndef CLAUSE_NO_CLASS\n"; + OS << "#define CLAUSE_NO_CLASS(Enum, Str)\n"; + OS << "#endif\n"; + OS << "\n"; + OS << "#define __CLAUSE(Name, Class) \\\n"; + OS << " CLAUSE(" << DirLang.getClausePrefix() + << "##Name, #Name, /* Implicit */ false) \\\n"; + OS << " CLAUSE_CLASS(" << DirLang.getClausePrefix() + << "##Name, #Name, Class)\n"; + OS << "#define __CLAUSE_NO_CLASS(Name) \\\n"; + OS << " CLAUSE(" << DirLang.getClausePrefix() + << "##Name, #Name, /* Implicit */ false) \\\n"; + OS << " CLAUSE_NO_CLASS(" << DirLang.getClausePrefix() << "##Name, #Name)\n"; + OS << "#define __IMPLICIT_CLAUSE_CLASS(Name, Str, Class) \\\n"; + OS << " CLAUSE(" << DirLang.getClausePrefix() + << "##Name, Str, /* Implicit */ true) \\\n"; + OS << " CLAUSE_CLASS(" << DirLang.getClausePrefix() + << "##Name, Str, Class)\n"; + OS << "#define __IMPLICIT_CLAUSE_NO_CLASS(Name, Str) \\\n"; + OS << " CLAUSE(" << DirLang.getClausePrefix() + << "##Name, Str, /* Implicit */ true) \\\n"; + OS << " CLAUSE_NO_CLASS(" << DirLang.getClausePrefix() << "##Name, Str)\n"; + OS << "\n"; + + for (const Clause C : DirLang.getClauses()) { + if (C.getClangClass().empty()) { // NO_CLASS + if (C.isImplicit()) { + OS << "__IMPLICIT_CLAUSE_NO_CLASS(" << C.getFormattedName() << ", \"" + << C.getFormattedName() << "\")\n"; + } else { + OS << "__CLAUSE_NO_CLASS(" << C.getFormattedName() << ")\n"; + } + } else { // CLASS + if (C.isImplicit()) { + OS << "__IMPLICIT_CLAUSE_CLASS(" << C.getFormattedName() << ", \"" + << C.getFormattedName() << "\", " << C.getClangClass() << ")\n"; + } else { + OS << "__CLAUSE(" << C.getFormattedName() << ", " << C.getClangClass() + << ")\n"; + } + } + } + + OS << "\n"; + OS << "#undef __IMPLICIT_CLAUSE_NO_CLASS\n"; + OS << "#undef __IMPLICIT_CLAUSE_CLASS\n"; + OS << "#undef __CLAUSE_NO_CLASS\n"; + OS << "#undef __CLAUSE\n"; + OS << "#undef CLAUSE_NO_CLASS\n"; + OS << "#undef CLAUSE_CLASS\n"; + OS << "#undef CLAUSE\n"; +} + +// Generate the implemenation for the enumeration in the directive +// language. This code can be included in library. +void emitDirectivesBasicImpl(const DirectiveLanguage &DirLang, + raw_ostream &OS) { + IfDefScope Scope("GEN_DIRECTIVES_IMPL", OS); + + OS << "\n#include \"llvm/Support/ErrorHandling.h\"\n"; + + // getDirectiveKind(StringRef Str) + generateGetKind(DirLang.getDirectives(), OS, "Directive", DirLang, + DirLang.getDirectivePrefix(), /*ImplicitAsUnknown=*/false); + + // getDirectiveName(Directive Kind) + generateGetName(DirLang.getDirectives(), OS, "Directive", DirLang, + DirLang.getDirectivePrefix()); + + // getClauseKind(StringRef Str) + generateGetKind(DirLang.getClauses(), OS, "Clause", DirLang, + DirLang.getClausePrefix(), + /*ImplicitAsUnknown=*/true); + + // getClauseName(Clause Kind) + generateGetName(DirLang.getClauses(), OS, "Clause", DirLang, + DirLang.getClausePrefix()); + + // get<ClauseVal>Kind(StringRef Str) + generateGetKindClauseVal(DirLang, OS); + + // isAllowedClauseForDirective(Directive D, Clause C, unsigned Version) + generateIsAllowedClause(DirLang, OS); + + // getDirectiveAssociation(Directive D) + generateGetDirectiveAssociation(DirLang, OS); + + // getDirectiveCategory(Directive D) + generateGetDirectiveCategory(DirLang, OS); + + // Leaf table for getLeafConstructs, etc. + emitLeafTable(DirLang, OS, "LeafConstructTable"); +} + +// Generate the implemenation section for the enumeration in the directive +// language. +static void emitDirectivesImpl(const RecordKeeper &Records, raw_ostream &OS) { + const auto DirLang = DirectiveLanguage(Records); + if (DirLang.HasValidityErrors()) + return; + + emitDirectivesFlangImpl(DirLang, OS); + + generateClauseClassMacro(DirLang, OS); + + emitDirectivesBasicImpl(DirLang, OS); +} + +static TableGen::Emitter::Opt + X("gen-directive-decl", emitDirectivesDecl, + "Generate directive related declaration code (header file)"); + +static TableGen::Emitter::Opt + Y("gen-directive-impl", emitDirectivesImpl, + "Generate directive related implementation code"); diff --git a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp new file mode 100644 index 0000000..fc2b890 --- /dev/null +++ b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp @@ -0,0 +1,867 @@ +//===- IntrinsicEmitter.cpp - Generate intrinsic information --------------===// +// +// 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 emits information about intrinsic functions. +// +//===----------------------------------------------------------------------===// + +#include "CodeGenIntrinsics.h" +#include "SequenceToOffsetTable.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/ModRef.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringToOffsetTable.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <algorithm> +#include <array> +#include <cassert> +#include <cctype> +#include <map> +#include <optional> +#include <string> +#include <utility> +#include <vector> +using namespace llvm; + +static cl::OptionCategory GenIntrinsicCat("Options for -gen-intrinsic-enums"); +static cl::opt<std::string> + IntrinsicPrefix("intrinsic-prefix", + cl::desc("Generate intrinsics with this target prefix"), + cl::value_desc("target prefix"), cl::cat(GenIntrinsicCat)); + +namespace { +class IntrinsicEmitter { + const RecordKeeper &Records; + +public: + IntrinsicEmitter(const RecordKeeper &R) : Records(R) {} + + void run(raw_ostream &OS, bool Enums); + + void EmitEnumInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); + void EmitArgKind(raw_ostream &OS); + void EmitIITInfo(raw_ostream &OS); + void EmitTargetInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); + void EmitIntrinsicToNameTable(const CodeGenIntrinsicTable &Ints, + raw_ostream &OS); + void EmitIntrinsicToOverloadTable(const CodeGenIntrinsicTable &Ints, + raw_ostream &OS); + void EmitGenerator(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); + void EmitAttributes(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); + void EmitIntrinsicToBuiltinMap(const CodeGenIntrinsicTable &Ints, + bool IsClang, raw_ostream &OS); +}; + +// Helper class to use with `TableGen::Emitter::OptClass`. +template <bool Enums> class IntrinsicEmitterOpt : public IntrinsicEmitter { +public: + IntrinsicEmitterOpt(const RecordKeeper &R) : IntrinsicEmitter(R) {} + void run(raw_ostream &OS) { IntrinsicEmitter::run(OS, Enums); } +}; + +} // End anonymous namespace + +//===----------------------------------------------------------------------===// +// IntrinsicEmitter Implementation +//===----------------------------------------------------------------------===// + +void IntrinsicEmitter::run(raw_ostream &OS, bool Enums) { + emitSourceFileHeader("Intrinsic Function Source Fragment", OS); + + CodeGenIntrinsicTable Ints(Records); + + if (Enums) { + // Emit the enum information. + EmitEnumInfo(Ints, OS); + + // Emit ArgKind for Intrinsics.h. + EmitArgKind(OS); + } else { + // Emit IIT_Info constants. + EmitIITInfo(OS); + + // Emit the target metadata. + EmitTargetInfo(Ints, OS); + + // Emit the intrinsic ID -> name table. + EmitIntrinsicToNameTable(Ints, OS); + + // Emit the intrinsic ID -> overload table. + EmitIntrinsicToOverloadTable(Ints, OS); + + // Emit the intrinsic declaration generator. + EmitGenerator(Ints, OS); + + // Emit the intrinsic parameter attributes. + EmitAttributes(Ints, OS); + + // Emit code to translate Clang builtins into LLVM intrinsics. + EmitIntrinsicToBuiltinMap(Ints, true, OS); + + // Emit code to translate MS builtins into LLVM intrinsics. + EmitIntrinsicToBuiltinMap(Ints, false, OS); + } +} + +void IntrinsicEmitter::EmitEnumInfo(const CodeGenIntrinsicTable &Ints, + raw_ostream &OS) { + // Find the TargetSet for which to generate enums. There will be an initial + // set with an empty target prefix which will include target independent + // intrinsics like dbg.value. + using TargetSet = CodeGenIntrinsicTable::TargetSet; + const TargetSet *Set = nullptr; + for (const auto &Target : Ints.getTargets()) { + if (Target.Name == IntrinsicPrefix) { + Set = &Target; + break; + } + } + if (!Set) { + // The first entry is for target independent intrinsics, so drop it. + auto KnowTargets = Ints.getTargets().drop_front(); + PrintFatalError([KnowTargets](raw_ostream &OS) { + OS << "tried to generate intrinsics for unknown target " + << IntrinsicPrefix << "\nKnown targets are: "; + interleaveComma(KnowTargets, OS, + [&OS](const TargetSet &Target) { OS << Target.Name; }); + OS << '\n'; + }); + } + + // Generate a complete header for target specific intrinsics. + if (IntrinsicPrefix.empty()) { + OS << "#ifdef GET_INTRINSIC_ENUM_VALUES\n"; + } else { + std::string UpperPrefix = StringRef(IntrinsicPrefix).upper(); + OS << formatv("#ifndef LLVM_IR_INTRINSIC_{}_ENUMS_H\n", UpperPrefix); + OS << formatv("#define LLVM_IR_INTRINSIC_{}_ENUMS_H\n", UpperPrefix); + OS << "namespace llvm::Intrinsic {\n"; + OS << formatv("enum {}Intrinsics : unsigned {{\n", UpperPrefix); + } + + OS << "// Enum values for intrinsics.\n"; + bool First = true; + for (const auto &Int : Ints[*Set]) { + OS << " " << Int.EnumName; + + // Assign a value to the first intrinsic in this target set so that all + // intrinsic ids are distinct. + if (First) { + OS << " = " << Set->Offset + 1; + First = false; + } + + OS << ", "; + if (Int.EnumName.size() < 40) + OS.indent(40 - Int.EnumName.size()); + OS << formatv(" // {}\n", Int.Name); + } + + // Emit num_intrinsics into the target neutral enum. + if (IntrinsicPrefix.empty()) { + OS << formatv(" num_intrinsics = {}\n", Ints.size() + 1); + OS << "#endif\n\n"; + } else { + OS << R"(}; // enum +} // namespace llvm::Intrinsic +#endif + +)"; + } +} + +void IntrinsicEmitter::EmitArgKind(raw_ostream &OS) { + if (!IntrinsicPrefix.empty()) + return; + OS << "// llvm::Intrinsic::IITDescriptor::ArgKind.\n"; + OS << "#ifdef GET_INTRINSIC_ARGKIND\n"; + if (const auto RecArgKind = Records.getDef("ArgKind")) { + for (const auto &RV : RecArgKind->getValues()) + OS << " AK_" << RV.getName() << " = " << *RV.getValue() << ",\n"; + } else { + OS << "#error \"ArgKind is not defined\"\n"; + } + OS << "#endif\n\n"; +} + +void IntrinsicEmitter::EmitIITInfo(raw_ostream &OS) { + OS << "#ifdef GET_INTRINSIC_IITINFO\n"; + std::array<StringRef, 256> RecsByNumber; + auto IIT_Base = Records.getAllDerivedDefinitionsIfDefined("IIT_Base"); + for (const Record *Rec : IIT_Base) { + auto Number = Rec->getValueAsInt("Number"); + assert(0 <= Number && Number < (int)RecsByNumber.size() && + "IIT_Info.Number should be uint8_t"); + assert(RecsByNumber[Number].empty() && "Duplicate IIT_Info.Number"); + RecsByNumber[Number] = Rec->getName(); + } + if (IIT_Base.size() > 0) { + for (unsigned I = 0, E = RecsByNumber.size(); I < E; ++I) + if (!RecsByNumber[I].empty()) + OS << " " << RecsByNumber[I] << " = " << I << ",\n"; + } else { + OS << "#error \"class IIT_Base is not defined\"\n"; + } + OS << "#endif\n\n"; +} + +void IntrinsicEmitter::EmitTargetInfo(const CodeGenIntrinsicTable &Ints, + raw_ostream &OS) { + OS << R"(// Target mapping. +#ifdef GET_INTRINSIC_TARGET_DATA +struct IntrinsicTargetInfo { + StringLiteral Name; + size_t Offset; + size_t Count; +}; +static constexpr IntrinsicTargetInfo TargetInfos[] = { +)"; + for (const auto [Name, Offset, Count] : Ints.getTargets()) + OS << formatv(" {{\"{}\", {}, {}},\n", Name, Offset, Count); + OS << R"(}; +#endif + +)"; +} + +void IntrinsicEmitter::EmitIntrinsicToNameTable( + const CodeGenIntrinsicTable &Ints, raw_ostream &OS) { + // Built up a table of the intrinsic names. + constexpr StringLiteral NotIntrinsic = "not_intrinsic"; + StringToOffsetTable Table; + Table.GetOrAddStringOffset(NotIntrinsic); + for (const auto &Int : Ints) + Table.GetOrAddStringOffset(Int.Name); + + OS << R"(// Intrinsic ID to name table. +#ifdef GET_INTRINSIC_NAME_TABLE +// Note that entry #0 is the invalid intrinsic! + +)"; + + Table.EmitStringLiteralDef(OS, "static constexpr char IntrinsicNameTable[]", + /*Indent=*/""); + + OS << R"( +static constexpr unsigned IntrinsicNameOffsetTable[] = { +)"; + + OS << formatv(" {}, // {}\n", Table.GetStringOffset(NotIntrinsic), + NotIntrinsic); + for (const auto &Int : Ints) + OS << formatv(" {}, // {}\n", Table.GetStringOffset(Int.Name), Int.Name); + + OS << R"( +}; // IntrinsicNameOffsetTable + +#endif + +)"; +} + +void IntrinsicEmitter::EmitIntrinsicToOverloadTable( + const CodeGenIntrinsicTable &Ints, raw_ostream &OS) { + OS << R"(// Intrinsic ID to overload bitset. +#ifdef GET_INTRINSIC_OVERLOAD_TABLE +static constexpr uint8_t OTable[] = { + 0 + )"; + for (auto [I, Int] : enumerate(Ints)) { + // Add one to the index so we emit a null bit for the invalid #0 intrinsic. + size_t Idx = I + 1; + + if (Idx % 8 == 0) + OS << ",\n 0"; + if (Int.isOverloaded) + OS << " | (1<<" << Idx % 8 << ')'; + } + OS << "\n};\n\n"; + // OTable contains a true bit at the position if the intrinsic is overloaded. + OS << "return (OTable[id/8] & (1 << (id%8))) != 0;\n"; + OS << "#endif\n\n"; +} + +using TypeSigTy = SmallVector<unsigned char>; + +/// Computes type signature of the intrinsic \p Int. +static TypeSigTy ComputeTypeSignature(const CodeGenIntrinsic &Int) { + TypeSigTy TypeSig; + const Record *TypeInfo = Int.TheDef->getValueAsDef("TypeInfo"); + const ListInit *TypeList = TypeInfo->getValueAsListInit("TypeSig"); + + for (const auto *TypeListEntry : TypeList->getValues()) + TypeSig.emplace_back(cast<IntInit>(TypeListEntry)->getValue()); + return TypeSig; +} + +// Pack the type signature into 32-bit fixed encoding word. +static std::optional<uint32_t> encodePacked(const TypeSigTy &TypeSig) { + if (TypeSig.size() > 8) + return std::nullopt; + + uint32_t Result = 0; + for (unsigned char C : reverse(TypeSig)) { + if (C > 15) + return std::nullopt; + Result = (Result << 4) | C; + } + return Result; +} + +void IntrinsicEmitter::EmitGenerator(const CodeGenIntrinsicTable &Ints, + raw_ostream &OS) { + // Note: the code below can be switched to use 32-bit fixed encoding by + // flipping the flag below. + constexpr bool Use16BitFixedEncoding = true; + using FixedEncodingTy = + std::conditional_t<Use16BitFixedEncoding, uint16_t, uint32_t>; + constexpr unsigned FixedEncodingBits = sizeof(FixedEncodingTy) * CHAR_BIT; + // Mask with all bits 1 except the most significant bit. + const unsigned Mask = (1U << (FixedEncodingBits - 1)) - 1; + const unsigned MSBPostion = FixedEncodingBits - 1; + StringRef FixedEncodingTypeName = + Use16BitFixedEncoding ? "uint16_t" : "uint32_t"; + + // If we can compute a 16/32-bit fixed encoding for this intrinsic, do so and + // capture it in this vector, otherwise store a ~0U. + std::vector<FixedEncodingTy> FixedEncodings; + SequenceToOffsetTable<TypeSigTy> LongEncodingTable; + + FixedEncodings.reserve(Ints.size()); + + // Compute the unique argument type info. + for (const CodeGenIntrinsic &Int : Ints) { + // Get the signature for the intrinsic. + TypeSigTy TypeSig = ComputeTypeSignature(Int); + + // Check to see if we can encode it into a 16/32 bit word. + std::optional<uint32_t> Result = encodePacked(TypeSig); + if (Result && (*Result & Mask) == Result) { + FixedEncodings.push_back(static_cast<FixedEncodingTy>(*Result)); + continue; + } + + LongEncodingTable.add(TypeSig); + + // This is a placehold that we'll replace after the table is laid out. + FixedEncodings.push_back(static_cast<FixedEncodingTy>(~0U)); + } + + LongEncodingTable.layout(); + + OS << formatv(R"(// Global intrinsic function declaration type table. +#ifdef GET_INTRINSIC_GENERATOR_GLOBAL +static constexpr {} IIT_Table[] = {{ + )", + FixedEncodingTypeName); + + unsigned MaxOffset = 0; + for (auto [Idx, FixedEncoding, Int] : enumerate(FixedEncodings, Ints)) { + if ((Idx & 7) == 7) + OS << "\n "; + + // If the entry fit in the table, just emit it. + if ((FixedEncoding & Mask) == FixedEncoding) { + OS << "0x" << Twine::utohexstr(FixedEncoding) << ", "; + continue; + } + + TypeSigTy TypeSig = ComputeTypeSignature(Int); + unsigned Offset = LongEncodingTable.get(TypeSig); + MaxOffset = std::max(MaxOffset, Offset); + + // Otherwise, emit the offset into the long encoding table. We emit it this + // way so that it is easier to read the offset in the .def file. + OS << formatv("(1U<<{}) | {}, ", MSBPostion, Offset); + } + + OS << "0\n};\n\n"; + + // verify that all offsets will fit in 16/32 bits. + if ((MaxOffset & Mask) != MaxOffset) + PrintFatalError("Offset of long encoding table exceeds encoding bits"); + + // Emit the shared table of register lists. + OS << "static constexpr unsigned char IIT_LongEncodingTable[] = {\n"; + if (!LongEncodingTable.empty()) + LongEncodingTable.emit( + OS, [](raw_ostream &OS, unsigned char C) { OS << (unsigned)C; }); + OS << " 255\n};\n"; + OS << "#endif\n\n"; // End of GET_INTRINSIC_GENERATOR_GLOBAL +} + +/// Returns the effective MemoryEffects for intrinsic \p Int. +static MemoryEffects getEffectiveME(const CodeGenIntrinsic &Int) { + MemoryEffects ME = Int.ME; + // TODO: IntrHasSideEffects should affect not only readnone intrinsics. + if (ME.doesNotAccessMemory() && Int.hasSideEffects) + ME = MemoryEffects::unknown(); + return ME; +} + +static bool compareFnAttributes(const CodeGenIntrinsic *L, + const CodeGenIntrinsic *R) { + auto TieBoolAttributes = [](const CodeGenIntrinsic *I) -> auto { + // Sort throwing intrinsics after non-throwing intrinsics. + return std::tie(I->canThrow, I->isNoDuplicate, I->isNoMerge, I->isNoReturn, + I->isNoCallback, I->isNoSync, I->isNoFree, I->isWillReturn, + I->isCold, I->isConvergent, I->isSpeculatable, + I->hasSideEffects, I->isStrictFP); + }; + + auto TieL = TieBoolAttributes(L); + auto TieR = TieBoolAttributes(R); + + if (TieL != TieR) + return TieL < TieR; + + // Try to order by readonly/readnone attribute. + uint32_t LME = getEffectiveME(*L).toIntValue(); + uint32_t RME = getEffectiveME(*R).toIntValue(); + if (LME != RME) + return LME > RME; + + return false; +} + +/// Returns true if \p Int has a non-empty set of function attributes. Note that +/// NoUnwind = !canThrow, so we need to negate it's sense to test if the +// intrinsic has NoUnwind attribute. +static bool hasFnAttributes(const CodeGenIntrinsic &Int) { + return !Int.canThrow || Int.isNoReturn || Int.isNoCallback || Int.isNoSync || + Int.isNoFree || Int.isWillReturn || Int.isCold || Int.isNoDuplicate || + Int.isNoMerge || Int.isConvergent || Int.isSpeculatable || + Int.isStrictFP || getEffectiveME(Int) != MemoryEffects::unknown(); +} + +namespace { +struct FnAttributeComparator { + bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const { + return compareFnAttributes(L, R); + } +}; + +struct AttributeComparator { + bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const { + // Order all intrinsics with no functiona attributes before all intrinsics + // with function attributes. + bool HasFnAttrLHS = hasFnAttributes(*L); + bool HasFnAttrRHS = hasFnAttributes(*R); + + // Order by argument attributes if function `hasFnAttributes` is equal. + // This is reliable because each side is already sorted internally. + return std::tie(HasFnAttrLHS, L->ArgumentAttributes) < + std::tie(HasFnAttrRHS, R->ArgumentAttributes); + } +}; +} // End anonymous namespace + +/// Returns the name of the IR enum for argument attribute kind \p Kind. +static StringRef getArgAttrEnumName(CodeGenIntrinsic::ArgAttrKind Kind) { + switch (Kind) { + case CodeGenIntrinsic::NoCapture: + return "NoCapture"; + case CodeGenIntrinsic::NoAlias: + return "NoAlias"; + case CodeGenIntrinsic::NoUndef: + return "NoUndef"; + case CodeGenIntrinsic::NonNull: + return "NonNull"; + case CodeGenIntrinsic::Returned: + return "Returned"; + case CodeGenIntrinsic::ReadOnly: + return "ReadOnly"; + case CodeGenIntrinsic::WriteOnly: + return "WriteOnly"; + case CodeGenIntrinsic::ReadNone: + return "ReadNone"; + case CodeGenIntrinsic::ImmArg: + return "ImmArg"; + case CodeGenIntrinsic::Alignment: + return "Alignment"; + case CodeGenIntrinsic::Dereferenceable: + return "Dereferenceable"; + } + llvm_unreachable("Unknown CodeGenIntrinsic::ArgAttrKind enum"); +} + +/// EmitAttributes - This emits the Intrinsic::getAttributes method. +void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints, + raw_ostream &OS) { + OS << R"(// Add parameter attributes that are not common to all intrinsics. +#ifdef GET_INTRINSIC_ATTRIBUTES +static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID) { + switch (ID) { + default: llvm_unreachable("Invalid attribute set number");)"; + // Compute unique argument attribute sets. + std::map<SmallVector<CodeGenIntrinsic::ArgAttribute, 0>, unsigned> + UniqArgAttributes; + for (const CodeGenIntrinsic &Int : Ints) { + for (auto &Attrs : Int.ArgumentAttributes) { + if (Attrs.empty()) + continue; + + unsigned ID = UniqArgAttributes.size(); + if (!UniqArgAttributes.try_emplace(Attrs, ID).second) + continue; + + assert(is_sorted(Attrs) && "Argument attributes are not sorted"); + + OS << formatv(R"( + case {}: + return AttributeSet::get(C, {{ +)", + ID); + for (const CodeGenIntrinsic::ArgAttribute &Attr : Attrs) { + StringRef AttrName = getArgAttrEnumName(Attr.Kind); + if (Attr.Kind == CodeGenIntrinsic::Alignment || + Attr.Kind == CodeGenIntrinsic::Dereferenceable) + OS << formatv(" Attribute::get(C, Attribute::{}, {}),\n", + AttrName, Attr.Value); + else + OS << formatv(" Attribute::get(C, Attribute::{}),\n", AttrName); + } + OS << " });"; + } + } + OS << R"( + } +} // getIntrinsicArgAttributeSet +)"; + + // Compute unique function attribute sets. + std::map<const CodeGenIntrinsic *, unsigned, FnAttributeComparator> + UniqFnAttributes; + OS << R"( +static AttributeSet getIntrinsicFnAttributeSet(LLVMContext &C, unsigned ID) { + switch (ID) { + default: llvm_unreachable("Invalid attribute set number");)"; + + for (const CodeGenIntrinsic &Int : Ints) { + if (!hasFnAttributes(Int)) + continue; + unsigned ID = UniqFnAttributes.size(); + if (!UniqFnAttributes.try_emplace(&Int, ID).second) + continue; + OS << formatv(R"( + case {}: + return AttributeSet::get(C, {{ +)", + ID); + auto addAttribute = [&OS](StringRef Attr) { + OS << formatv(" Attribute::get(C, Attribute::{}),\n", Attr); + }; + if (!Int.canThrow) + addAttribute("NoUnwind"); + if (Int.isNoReturn) + addAttribute("NoReturn"); + if (Int.isNoCallback) + addAttribute("NoCallback"); + if (Int.isNoSync) + addAttribute("NoSync"); + if (Int.isNoFree) + addAttribute("NoFree"); + if (Int.isWillReturn) + addAttribute("WillReturn"); + if (Int.isCold) + addAttribute("Cold"); + if (Int.isNoDuplicate) + addAttribute("NoDuplicate"); + if (Int.isNoMerge) + addAttribute("NoMerge"); + if (Int.isConvergent) + addAttribute("Convergent"); + if (Int.isSpeculatable) + addAttribute("Speculatable"); + if (Int.isStrictFP) + addAttribute("StrictFP"); + + const MemoryEffects ME = getEffectiveME(Int); + if (ME != MemoryEffects::unknown()) { + OS << formatv(" // {}\n", ME); + OS << formatv(" Attribute::getWithMemoryEffects(C, " + "MemoryEffects::createFromIntValue({})),\n", + ME.toIntValue()); + } + OS << " });"; + } + OS << R"( + } +} // getIntrinsicFnAttributeSet + +AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id) { +)"; + + // Compute the maximum number of attribute arguments and the map. For function + // attributes, we only consider whether the intrinsics has any function + // arguments or not. + std::map<const CodeGenIntrinsic *, unsigned, AttributeComparator> + UniqAttributes; + for (const CodeGenIntrinsic &Int : Ints) { + unsigned ID = UniqAttributes.size(); + UniqAttributes.try_emplace(&Int, ID); + } + + // Assign a 16-bit packed ID for each intrinsic. The lower 8-bits will be its + // "argument attribute ID" (index in UniqAttributes) and upper 8 bits will be + // its "function attribute ID" (index in UniqFnAttributes). + if (UniqAttributes.size() > 256) + PrintFatalError("Too many unique argument attributes for table!"); + if (UniqFnAttributes.size() > 256) + PrintFatalError("Too many unique function attributes for table!"); + + // Emit an array of AttributeList. Most intrinsics will have at least one + // entry, for the function itself (index ~1), which is usually nounwind. + OS << " static constexpr uint16_t IntrinsicsToAttributesMap[] = {"; + for (const CodeGenIntrinsic &Int : Ints) { + uint16_t FnAttrIndex = hasFnAttributes(Int) ? UniqFnAttributes[&Int] : 0; + OS << formatv("\n {} << 8 | {}, // {}", FnAttrIndex, + UniqAttributes[&Int], Int.Name); + } + + OS << formatv(R"( + }; + if (id == 0) + return AttributeList(); + + uint16_t PackedID = IntrinsicsToAttributesMap[id - 1]; + uint8_t FnAttrID = PackedID >> 8; + switch(PackedID & 0xFF) {{ + default: llvm_unreachable("Invalid attribute number"); +)"); + + for (const auto [IntPtr, UniqueID] : UniqAttributes) { + OS << formatv(" case {}:\n", UniqueID); + const CodeGenIntrinsic &Int = *IntPtr; + + // Keep track of the number of attributes we're writing out. + unsigned NumAttrs = + llvm::count_if(Int.ArgumentAttributes, + [](const auto &Attrs) { return !Attrs.empty(); }); + NumAttrs += hasFnAttributes(Int); + if (NumAttrs == 0) { + OS << " return AttributeList();\n"; + continue; + } + + OS << " return AttributeList::get(C, {\n"; + ListSeparator LS(",\n"); + for (const auto &[AttrIdx, Attrs] : enumerate(Int.ArgumentAttributes)) { + if (Attrs.empty()) + continue; + + unsigned ArgAttrID = UniqArgAttributes.find(Attrs)->second; + OS << LS + << formatv(" {{{}, getIntrinsicArgAttributeSet(C, {})}", AttrIdx, + ArgAttrID); + } + + if (hasFnAttributes(Int)) { + OS << LS + << " {AttributeList::FunctionIndex, " + "getIntrinsicFnAttributeSet(C, FnAttrID)}"; + } + OS << "\n });\n"; + } + + OS << R"( } +} +#endif // GET_INTRINSIC_ATTRIBUTES + +)"; +} + +void IntrinsicEmitter::EmitIntrinsicToBuiltinMap( + const CodeGenIntrinsicTable &Ints, bool IsClang, raw_ostream &OS) { + StringRef CompilerName = IsClang ? "Clang" : "MS"; + StringRef UpperCompilerName = IsClang ? "CLANG" : "MS"; + + // map<TargetPrefix, pair<map<BuiltinName, EnumName>, CommonPrefix>. + // Note that we iterate over both the maps in the code below and both + // iterations need to iterate in sorted key order. For the inner map, entries + // need to be emitted in the sorted order of `BuiltinName` with `CommonPrefix` + // rempved, because we use std::lower_bound to search these entries. For the + // outer map as well, entries need to be emitted in sorter order of + // `TargetPrefix` as we use std::lower_bound to search these entries. + using BIMEntryTy = + std::pair<std::map<StringRef, StringRef>, std::optional<StringRef>>; + std::map<StringRef, BIMEntryTy> BuiltinMap; + + for (const CodeGenIntrinsic &Int : Ints) { + StringRef BuiltinName = IsClang ? Int.ClangBuiltinName : Int.MSBuiltinName; + if (BuiltinName.empty()) + continue; + // Get the map for this target prefix. + auto &[Map, CommonPrefix] = BuiltinMap[Int.TargetPrefix]; + + if (!Map.insert({BuiltinName, Int.EnumName}).second) + PrintFatalError(Int.TheDef->getLoc(), + "Intrinsic '" + Int.TheDef->getName() + "': duplicate " + + CompilerName + " builtin name!"); + + // Update common prefix. + if (!CommonPrefix) { + // For the first builtin for this target, initialize the common prefix. + CommonPrefix = BuiltinName; + continue; + } + + // Update the common prefix. Note that this assumes that `take_front` will + // never set the `Data` pointer in CommonPrefix to nullptr. + const char *Mismatch = mismatch(*CommonPrefix, BuiltinName).first; + *CommonPrefix = CommonPrefix->take_front(Mismatch - CommonPrefix->begin()); + } + + // Populate the string table with the names of all the builtins after + // removing this common prefix. + StringToOffsetTable Table; + for (const auto &[TargetPrefix, Entry] : BuiltinMap) { + auto &[Map, CommonPrefix] = Entry; + for (auto &[BuiltinName, EnumName] : Map) { + StringRef Suffix = BuiltinName.substr(CommonPrefix->size()); + Table.GetOrAddStringOffset(Suffix); + } + } + + OS << formatv(R"( +// Get the LLVM intrinsic that corresponds to a builtin. This is used by the +// C front-end. The builtin name is passed in as BuiltinName, and a target +// prefix (e.g. 'ppc') is passed in as TargetPrefix. +#ifdef GET_LLVM_INTRINSIC_FOR_{}_BUILTIN +Intrinsic::ID +Intrinsic::getIntrinsicFor{}Builtin(StringRef TargetPrefix, + StringRef BuiltinName) {{ + using namespace Intrinsic; +)", + UpperCompilerName, CompilerName); + + if (BuiltinMap.empty()) { + OS << formatv(R"( + return not_intrinsic; + } +#endif // GET_LLVM_INTRINSIC_FOR_{}_BUILTIN +)", + UpperCompilerName); + return; + } + + if (!Table.empty()) { + Table.EmitStringLiteralDef(OS, "static constexpr char BuiltinNames[]"); + + OS << R"( + struct BuiltinEntry { + ID IntrinsicID; + unsigned StrTabOffset; + const char *getName() const { return &BuiltinNames[StrTabOffset]; } + bool operator<(StringRef RHS) const { + return strncmp(getName(), RHS.data(), RHS.size()) < 0; + } + }; + +)"; + } + + // Emit a per target table of bultin names. + bool HasTargetIndependentBuiltins = false; + StringRef TargetIndepndentCommonPrefix; + for (const auto &[TargetPrefix, Entry] : BuiltinMap) { + const auto &[Map, CommonPrefix] = Entry; + if (!TargetPrefix.empty()) { + OS << formatv(" // Builtins for {0}.\n", TargetPrefix); + } else { + OS << " // Target independent builtins.\n"; + HasTargetIndependentBuiltins = true; + TargetIndepndentCommonPrefix = *CommonPrefix; + } + + // Emit the builtin table for this target prefix. + OS << formatv(" static constexpr BuiltinEntry {}Names[] = {{\n", + TargetPrefix); + for (const auto &[BuiltinName, EnumName] : Map) { + StringRef Suffix = BuiltinName.substr(CommonPrefix->size()); + OS << formatv(" {{{}, {}}, // {}\n", EnumName, + *Table.GetStringOffset(Suffix), BuiltinName); + } + OS << formatv(" }; // {}Names\n\n", TargetPrefix); + } + + // After emitting the builtin tables for all targets, emit a lookup table for + // all targets. We will use binary search, similar to the table for builtin + // names to lookup into this table. + OS << R"( + struct TargetEntry { + StringLiteral TargetPrefix; + ArrayRef<BuiltinEntry> Names; + StringLiteral CommonPrefix; + bool operator<(StringRef RHS) const { + return TargetPrefix < RHS; + }; + }; + static constexpr TargetEntry TargetTable[] = { +)"; + + for (const auto &[TargetPrefix, Entry] : BuiltinMap) { + const auto &[Map, CommonPrefix] = Entry; + if (TargetPrefix.empty()) + continue; + OS << formatv(R"( {{"{0}", {0}Names, "{1}"},)", TargetPrefix, + CommonPrefix) + << "\n"; + } + OS << " };\n"; + + // Now for the actual lookup, first check the target independent table if + // we emitted one. + if (HasTargetIndependentBuiltins) { + OS << formatv(R"( + // Check if it's a target independent builtin. + // Copy the builtin name so we can use it in consume_front without clobbering + // if for the lookup in the target specific table. + StringRef Suffix = BuiltinName; + if (Suffix.consume_front("{}")) {{ + auto II = lower_bound(Names, Suffix); + if (II != std::end(Names) && II->getName() == Suffix) + return II->IntrinsicID; + } +)", + TargetIndepndentCommonPrefix); + } + + // If a target independent builtin was not found, lookup the target specific. + OS << formatv(R"( + auto TI = lower_bound(TargetTable, TargetPrefix); + if (TI == std::end(TargetTable) || TI->TargetPrefix != TargetPrefix) + return not_intrinsic; + // This is the last use of BuiltinName, so no need to copy before using it in + // consume_front. + if (!BuiltinName.consume_front(TI->CommonPrefix)) + return not_intrinsic; + auto II = lower_bound(TI->Names, BuiltinName); + if (II == std::end(TI->Names) || II->getName() != BuiltinName) + return not_intrinsic; + return II->IntrinsicID; +} +#endif // GET_LLVM_INTRINSIC_FOR_{}_BUILTIN + +)", + UpperCompilerName); +} + +static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/true>> + X("gen-intrinsic-enums", "Generate intrinsic enums"); + +static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/false>> + Y("gen-intrinsic-impl", "Generate intrinsic implementation code"); diff --git a/llvm/utils/TableGen/Basic/RISCVTargetDefEmitter.cpp b/llvm/utils/TableGen/Basic/RISCVTargetDefEmitter.cpp new file mode 100644 index 0000000..723f1d7 --- /dev/null +++ b/llvm/utils/TableGen/Basic/RISCVTargetDefEmitter.cpp @@ -0,0 +1,266 @@ +//===- RISCVTargetDefEmitter.cpp - Generate lists of RISC-V CPUs ----------===// +// +// 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 emits the include file needed by RISCVTargetParser.cpp +// and RISCVISAInfo.cpp to parse the RISC-V CPUs and extensions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseSet.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/RISCVISAUtils.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" + +using namespace llvm; + +static StringRef getExtensionName(const Record *R) { + StringRef Name = R->getValueAsString("Name"); + Name.consume_front("experimental-"); + return Name; +} + +static void printExtensionTable(raw_ostream &OS, + ArrayRef<const Record *> Extensions, + bool Experimental) { + OS << "static const RISCVSupportedExtension Supported"; + if (Experimental) + OS << "Experimental"; + OS << "Extensions[] = {\n"; + + for (const Record *R : Extensions) { + if (R->getValueAsBit("Experimental") != Experimental) + continue; + + OS.indent(4) << "{\"" << getExtensionName(R) << "\", {" + << R->getValueAsInt("MajorVersion") << ", " + << R->getValueAsInt("MinorVersion") << "}},\n"; + } + + OS << "};\n\n"; +} + +static void emitRISCVExtensions(const RecordKeeper &Records, raw_ostream &OS) { + OS << "#ifdef GET_SUPPORTED_EXTENSIONS\n"; + OS << "#undef GET_SUPPORTED_EXTENSIONS\n\n"; + + std::vector<const Record *> Extensions = + Records.getAllDerivedDefinitionsIfDefined("RISCVExtension"); + llvm::sort(Extensions, [](const Record *Rec1, const Record *Rec2) { + return getExtensionName(Rec1) < getExtensionName(Rec2); + }); + + if (!Extensions.empty()) { + printExtensionTable(OS, Extensions, /*Experimental=*/false); + printExtensionTable(OS, Extensions, /*Experimental=*/true); + } + + OS << "#endif // GET_SUPPORTED_EXTENSIONS\n\n"; + + OS << "#ifdef GET_IMPLIED_EXTENSIONS\n"; + OS << "#undef GET_IMPLIED_EXTENSIONS\n\n"; + + if (!Extensions.empty()) { + OS << "\nstatic constexpr ImpliedExtsEntry ImpliedExts[] = {\n"; + for (const Record *Ext : Extensions) { + auto ImpliesList = Ext->getValueAsListOfDefs("Implies"); + if (ImpliesList.empty()) + continue; + + StringRef Name = getExtensionName(Ext); + + for (auto *ImpliedExt : ImpliesList) { + if (!ImpliedExt->isSubClassOf("RISCVExtension")) + continue; + + OS.indent(4) << "{ {\"" << Name << "\"}, \"" + << getExtensionName(ImpliedExt) << "\"},\n"; + } + } + + OS << "};\n\n"; + } + + OS << "#endif // GET_IMPLIED_EXTENSIONS\n\n"; +} + +// We can generate march string from target features as what has been described +// in RISC-V ISA specification (version 20191213) 'Chapter 27. ISA Extension +// Naming Conventions'. +// +// This is almost the same as RISCVFeatures::parseFeatureBits, except that we +// get feature name from feature records instead of feature bits. +static void printMArch(raw_ostream &OS, ArrayRef<const Record *> Features) { + RISCVISAUtils::OrderedExtensionMap Extensions; + unsigned XLen = 0; + + // Convert features to FeatureVector. + for (const Record *Feature : Features) { + StringRef FeatureName = getExtensionName(Feature); + if (Feature->isSubClassOf("RISCVExtension")) { + unsigned Major = Feature->getValueAsInt("MajorVersion"); + unsigned Minor = Feature->getValueAsInt("MinorVersion"); + Extensions[FeatureName.str()] = {Major, Minor}; + } else if (FeatureName == "64bit") { + assert(XLen == 0 && "Already determined XLen"); + XLen = 64; + } else if (FeatureName == "32bit") { + assert(XLen == 0 && "Already determined XLen"); + XLen = 32; + } + } + + assert(XLen != 0 && "Unable to determine XLen"); + + OS << "rv" << XLen; + + ListSeparator LS("_"); + for (auto const &Ext : Extensions) + OS << LS << Ext.first << Ext.second.Major << 'p' << Ext.second.Minor; +} + +static void printProfileTable(raw_ostream &OS, + ArrayRef<const Record *> Profiles, + bool Experimental) { + OS << "static constexpr RISCVProfile Supported"; + if (Experimental) + OS << "Experimental"; + OS << "Profiles[] = {\n"; + + for (const Record *Rec : Profiles) { + if (Rec->getValueAsBit("Experimental") != Experimental) + continue; + + StringRef Name = Rec->getValueAsString("Name"); + Name.consume_front("experimental-"); + OS.indent(4) << "{\"" << Name << "\",\""; + printMArch(OS, Rec->getValueAsListOfDefs("Implies")); + OS << "\"},\n"; + } + + OS << "};\n\n"; +} + +static void emitRISCVProfiles(const RecordKeeper &Records, raw_ostream &OS) { + OS << "#ifdef GET_SUPPORTED_PROFILES\n"; + OS << "#undef GET_SUPPORTED_PROFILES\n\n"; + + auto Profiles = Records.getAllDerivedDefinitionsIfDefined("RISCVProfile"); + + if (!Profiles.empty()) { + printProfileTable(OS, Profiles, /*Experimental=*/false); + bool HasExperimentalProfiles = any_of(Profiles, [&](auto &Rec) { + return Rec->getValueAsBit("Experimental"); + }); + if (HasExperimentalProfiles) + printProfileTable(OS, Profiles, /*Experimental=*/true); + } + + OS << "#endif // GET_SUPPORTED_PROFILES\n\n"; +} + +static void emitRISCVProcs(const RecordKeeper &RK, raw_ostream &OS) { + OS << "#ifndef PROC\n" + << "#define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN" + << ", FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID)\n" + << "#endif\n\n"; + + // Iterate on all definition records. + for (const Record *Rec : + RK.getAllDerivedDefinitionsIfDefined("RISCVProcessorModel")) { + const std::vector<const Record *> &Features = + Rec->getValueAsListOfDefs("Features"); + bool FastScalarUnalignedAccess = any_of(Features, [&](auto &Feature) { + return Feature->getValueAsString("Name") == "unaligned-scalar-mem"; + }); + + bool FastVectorUnalignedAccess = any_of(Features, [&](auto &Feature) { + return Feature->getValueAsString("Name") == "unaligned-vector-mem"; + }); + + OS << "PROC(" << Rec->getName() << ", {\"" << Rec->getValueAsString("Name") + << "\"}, {\""; + + StringRef MArch = Rec->getValueAsString("DefaultMarch"); + + // Compute MArch from features if we don't specify it. + if (MArch.empty()) + printMArch(OS, Features); + else + OS << MArch; + + uint32_t MVendorID = Rec->getValueAsInt("MVendorID"); + uint64_t MArchID = Rec->getValueAsInt("MArchID"); + uint64_t MImpID = Rec->getValueAsInt("MImpID"); + + OS << "\"}, " << FastScalarUnalignedAccess << ", " + << FastVectorUnalignedAccess; + OS << ", " << format_hex(MVendorID, 10); + OS << ", " << format_hex(MArchID, 18); + OS << ", " << format_hex(MImpID, 18); + OS << ")\n"; + } + OS << "\n#undef PROC\n"; + OS << "\n"; + OS << "#ifndef TUNE_PROC\n" + << "#define TUNE_PROC(ENUM, NAME)\n" + << "#endif\n\n"; + + for (const Record *Rec : + RK.getAllDerivedDefinitionsIfDefined("RISCVTuneProcessorModel")) { + OS << "TUNE_PROC(" << Rec->getName() << ", " + << "\"" << Rec->getValueAsString("Name") << "\")\n"; + } + + OS << "\n#undef TUNE_PROC\n"; +} + +static void emitRISCVExtensionBitmask(const RecordKeeper &RK, raw_ostream &OS) { + std::vector<const Record *> Extensions = + RK.getAllDerivedDefinitionsIfDefined("RISCVExtensionBitmask"); + llvm::sort(Extensions, [](const Record *Rec1, const Record *Rec2) { + return getExtensionName(Rec1) < getExtensionName(Rec2); + }); + +#ifndef NDEBUG + llvm::DenseSet<std::pair<uint64_t, uint64_t>> Seen; +#endif + + OS << "#ifdef GET_RISCVExtensionBitmaskTable_IMPL\n"; + OS << "static const RISCVExtensionBitmask ExtensionBitmask[]={\n"; + for (const Record *Rec : Extensions) { + unsigned GroupIDVal = Rec->getValueAsInt("GroupID"); + unsigned BitPosVal = Rec->getValueAsInt("BitPos"); + + StringRef ExtName = Rec->getValueAsString("Name"); + ExtName.consume_front("experimental-"); + +#ifndef NDEBUG + assert(Seen.insert(std::make_pair(GroupIDVal, BitPosVal)).second && + "duplicated bitmask"); +#endif + + OS.indent(4) << "{" + << "\"" << ExtName << "\"" + << ", " << GroupIDVal << ", " << BitPosVal << "ULL" + << "},\n"; + } + OS << "};\n"; + OS << "#endif\n"; +} + +static void emitRiscvTargetDef(const RecordKeeper &RK, raw_ostream &OS) { + emitRISCVExtensions(RK, OS); + emitRISCVProfiles(RK, OS); + emitRISCVProcs(RK, OS); + emitRISCVExtensionBitmask(RK, OS); +} + +static TableGen::Emitter::Opt X("gen-riscv-target-def", emitRiscvTargetDef, + "Generate the list of CPUs and extensions for " + "RISC-V"); diff --git a/llvm/utils/TableGen/Basic/TableGen.cpp b/llvm/utils/TableGen/Basic/TableGen.cpp new file mode 100644 index 0000000..80ac93f --- /dev/null +++ b/llvm/utils/TableGen/Basic/TableGen.cpp @@ -0,0 +1,99 @@ +//===- TableGen.cpp - Top-Level TableGen implementation for LLVM ----------===// +// +// 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 file contains the global defintions (mostly command line parameters) +// shared between llvm-tblgen and llvm-min-tblgen. +// +//===----------------------------------------------------------------------===// + +#include "TableGen.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Main.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/SetTheory.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <cassert> +#include <string> +#include <vector> + +using namespace llvm; + +namespace llvm { +cl::opt<bool> EmitLongStrLiterals( + "long-string-literals", + cl::desc("when emitting large string tables, prefer string literals over " + "comma-separated char literals. This can be a readability and " + "compile-time performance win, but upsets some compilers"), + cl::Hidden, cl::init(true)); +} // end namespace llvm + +static cl::OptionCategory PrintEnumsCat("Options for -print-enums"); +static cl::opt<std::string> Class("class", + cl::desc("Print Enum list for this class"), + cl::value_desc("class name"), + cl::cat(PrintEnumsCat)); + +static void printRecords(const RecordKeeper &Records, raw_ostream &OS) { + OS << Records; // No argument, dump all contents +} + +static void printEnums(const RecordKeeper &Records, raw_ostream &OS) { + for (const Record *Rec : Records.getAllDerivedDefinitions(Class)) + OS << Rec->getName() << ", "; + OS << "\n"; +} + +static void printSets(const RecordKeeper &Records, raw_ostream &OS) { + SetTheory Sets; + Sets.addFieldExpander("Set", "Elements"); + for (const Record *Rec : Records.getAllDerivedDefinitions("Set")) { + OS << Rec->getName() << " = ["; + const std::vector<const Record *> *Elts = Sets.expand(Rec); + assert(Elts && "Couldn't expand Set instance"); + for (const Record *Elt : *Elts) + OS << ' ' << Elt->getName(); + OS << " ]\n"; + } +} + +static TableGen::Emitter::Opt X[] = { + {"print-records", printRecords, "Print all records to stdout (default)", + true}, + {"print-detailed-records", EmitDetailedRecords, + "Print full details of all records to stdout"}, + {"null-backend", [](const RecordKeeper &Records, raw_ostream &OS) {}, + "Do nothing after parsing (useful for timing)"}, + {"dump-json", EmitJSON, "Dump all records as machine-readable JSON"}, + {"print-enums", printEnums, "Print enum values for a class"}, + {"print-sets", printSets, "Print expanded sets for testing DAG exprs"}, +}; + +int tblgen_main(int argc, char **argv) { + InitLLVM X(argc, argv); + cl::ParseCommandLineOptions(argc, argv); + + return TableGenMain(argv[0]); +} + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#if __has_feature(address_sanitizer) || \ + (defined(__SANITIZE_ADDRESS__) && defined(__GNUC__)) || \ + __has_feature(leak_sanitizer) + +#include <sanitizer/lsan_interface.h> +// Disable LeakSanitizer for this binary as it has too many leaks that are not +// very interesting to fix. See compiler-rt/include/sanitizer/lsan_interface.h . +LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; } + +#endif diff --git a/llvm/utils/TableGen/Basic/TableGen.h b/llvm/utils/TableGen/Basic/TableGen.h new file mode 100644 index 0000000..630aea6 --- /dev/null +++ b/llvm/utils/TableGen/Basic/TableGen.h @@ -0,0 +1,13 @@ +//===- TableGen.h ---------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Shared entry point for llvm-tblgen and llvm-min-tblgen. +// +//===----------------------------------------------------------------------===// + +int tblgen_main(int argc, char **argv); diff --git a/llvm/utils/TableGen/Basic/VTEmitter.cpp b/llvm/utils/TableGen/Basic/VTEmitter.cpp new file mode 100644 index 0000000..d02932d --- /dev/null +++ b/llvm/utils/TableGen/Basic/VTEmitter.cpp @@ -0,0 +1,216 @@ +//===- VTEmitter.cpp - Generate properties from ValueTypes.td -------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include <cassert> +#include <map> +using namespace llvm; + +namespace { + +class VTEmitter { +private: + const RecordKeeper &Records; + +public: + VTEmitter(const RecordKeeper &R) : Records(R) {} + + void run(raw_ostream &OS); +}; + +} // End anonymous namespace. + +static void vTtoGetLlvmTyString(raw_ostream &OS, const Record *VT) { + bool IsVector = VT->getValueAsBit("isVector"); + bool IsRISCVVecTuple = VT->getValueAsBit("isRISCVVecTuple"); + + if (IsRISCVVecTuple) { + unsigned NElem = VT->getValueAsInt("nElem"); + unsigned Sz = VT->getValueAsInt("Size"); + OS << "TargetExtType::get(Context, \"riscv.vector.tuple\", " + "ScalableVectorType::get(Type::getInt8Ty(Context), " + << (Sz / (NElem * 8)) << "), " << NElem << ")"; + return; + } + + if (IsVector) + OS << (VT->getValueAsBit("isScalable") ? "Scalable" : "Fixed") + << "VectorType::get("; + + auto OutputVT = IsVector ? VT->getValueAsDef("ElementType") : VT; + int64_t OutputVTSize = OutputVT->getValueAsInt("Size"); + + if (OutputVT->getValueAsBit("isFP")) { + StringRef FloatTy; + auto OutputVTName = OutputVT->getValueAsString("LLVMName"); + switch (OutputVTSize) { + default: + llvm_unreachable("Unhandled case"); + case 16: + FloatTy = (OutputVTName == "bf16") ? "BFloatTy" : "HalfTy"; + break; + case 32: + FloatTy = "FloatTy"; + break; + case 64: + FloatTy = "DoubleTy"; + break; + case 80: + FloatTy = "X86_FP80Ty"; + break; + case 128: + FloatTy = (OutputVTName == "ppcf128") ? "PPC_FP128Ty" : "FP128Ty"; + break; + } + OS << "Type::get" << FloatTy << "(Context)"; + } else if (OutputVT->getValueAsBit("isInteger")) { + // We only have Type::getInt1Ty, Int8, Int16, Int32, Int64, and Int128 + if ((isPowerOf2_64(OutputVTSize) && OutputVTSize >= 8 && + OutputVTSize <= 128) || + OutputVTSize == 1) + OS << "Type::getInt" << OutputVTSize << "Ty(Context)"; + else + OS << "Type::getIntNTy(Context, " << OutputVTSize << ")"; + } else + llvm_unreachable("Unhandled case"); + + if (IsVector) + OS << ", " << VT->getValueAsInt("nElem") << ")"; +} + +void VTEmitter::run(raw_ostream &OS) { + emitSourceFileHeader("ValueTypes Source Fragment", OS, Records); + + std::vector<const Record *> VTsByNumber{512}; + for (auto *VT : Records.getAllDerivedDefinitions("ValueType")) { + auto Number = VT->getValueAsInt("Value"); + assert(0 <= Number && Number < (int)VTsByNumber.size() && + "ValueType should be uint16_t"); + assert(!VTsByNumber[Number] && "Duplicate ValueType"); + VTsByNumber[Number] = VT; + } + + struct VTRange { + StringRef First; + StringRef Last; + bool Closed; + }; + + std::map<StringRef, VTRange> VTRanges; + + auto UpdateVTRange = [&VTRanges](const char *Key, StringRef Name, + bool Valid) { + if (Valid) { + if (!VTRanges.count(Key)) + VTRanges[Key].First = Name; + assert(!VTRanges[Key].Closed && "Gap detected!"); + VTRanges[Key].Last = Name; + } else if (VTRanges.count(Key)) { + VTRanges[Key].Closed = true; + } + }; + + OS << "#ifdef GET_VT_ATTR // (Ty, n, sz, Any, Int, FP, Vec, Sc, Tup, NF, " + "NElem, EltTy)\n"; + for (const auto *VT : VTsByNumber) { + if (!VT) + continue; + auto Name = VT->getValueAsString("LLVMName"); + auto Value = VT->getValueAsInt("Value"); + bool IsInteger = VT->getValueAsBit("isInteger"); + bool IsFP = VT->getValueAsBit("isFP"); + bool IsVector = VT->getValueAsBit("isVector"); + bool IsScalable = VT->getValueAsBit("isScalable"); + bool IsRISCVVecTuple = VT->getValueAsBit("isRISCVVecTuple"); + int64_t NF = VT->getValueAsInt("NF"); + bool IsNormalValueType = VT->getValueAsBit("isNormalValueType"); + int64_t NElem = IsVector ? VT->getValueAsInt("nElem") : 0; + StringRef EltName = IsVector ? VT->getValueAsDef("ElementType")->getName() + : "INVALID_SIMPLE_VALUE_TYPE"; + + UpdateVTRange("INTEGER_FIXEDLEN_VECTOR_VALUETYPE", Name, + IsInteger && IsVector && !IsScalable); + UpdateVTRange("INTEGER_SCALABLE_VECTOR_VALUETYPE", Name, + IsInteger && IsScalable); + UpdateVTRange("FP_FIXEDLEN_VECTOR_VALUETYPE", Name, + IsFP && IsVector && !IsScalable); + UpdateVTRange("FP_SCALABLE_VECTOR_VALUETYPE", Name, IsFP && IsScalable); + UpdateVTRange("FIXEDLEN_VECTOR_VALUETYPE", Name, IsVector && !IsScalable); + UpdateVTRange("SCALABLE_VECTOR_VALUETYPE", Name, IsScalable); + UpdateVTRange("RISCV_VECTOR_TUPLE_VALUETYPE", Name, IsRISCVVecTuple); + UpdateVTRange("VECTOR_VALUETYPE", Name, IsVector); + UpdateVTRange("INTEGER_VALUETYPE", Name, IsInteger && !IsVector); + UpdateVTRange("FP_VALUETYPE", Name, IsFP && !IsVector); + UpdateVTRange("VALUETYPE", Name, IsNormalValueType); + + // clang-format off + OS << " GET_VT_ATTR(" + << Name << ", " + << Value << ", " + << VT->getValueAsInt("Size") << ", " + << VT->getValueAsBit("isOverloaded") << ", " + << (IsInteger ? Name[0] == 'i' ? 3 : 1 : 0) << ", " + << (IsFP ? Name[0] == 'f' ? 3 : 1 : 0) << ", " + << IsVector << ", " + << IsScalable << ", " + << IsRISCVVecTuple << ", " + << NF << ", " + << NElem << ", " + << EltName << ")\n"; + // clang-format on + } + OS << "#endif\n\n"; + + OS << "#ifdef GET_VT_RANGES\n"; + for (const auto &KV : VTRanges) { + assert(KV.second.Closed); + OS << " FIRST_" << KV.first << " = " << KV.second.First << ",\n" + << " LAST_" << KV.first << " = " << KV.second.Last << ",\n"; + } + OS << "#endif\n\n"; + + OS << "#ifdef GET_VT_VECATTR // (Ty, Sc, Tup, nElem, ElTy)\n"; + for (const auto *VT : VTsByNumber) { + if (!VT || !VT->getValueAsBit("isVector")) + continue; + const auto *ElTy = VT->getValueAsDef("ElementType"); + assert(ElTy); + // clang-format off + OS << " GET_VT_VECATTR(" + << VT->getValueAsString("LLVMName") << ", " + << VT->getValueAsBit("isScalable") << ", " + << VT->getValueAsBit("isRISCVVecTuple") << ", " + << VT->getValueAsInt("nElem") << ", " + << ElTy->getName() << ")\n"; + // clang-format on + } + OS << "#endif\n\n"; + + OS << "#ifdef GET_VT_EVT\n"; + for (const auto *VT : VTsByNumber) { + if (!VT) + continue; + bool IsInteger = VT->getValueAsBit("isInteger"); + bool IsVector = VT->getValueAsBit("isVector"); + bool IsFP = VT->getValueAsBit("isFP"); + bool IsRISCVVecTuple = VT->getValueAsBit("isRISCVVecTuple"); + + if (!IsInteger && !IsVector && !IsFP && !IsRISCVVecTuple) + continue; + + OS << " GET_VT_EVT(" << VT->getValueAsString("LLVMName") << ", "; + vTtoGetLlvmTyString(OS, VT); + OS << ")\n"; + } + OS << "#endif\n\n"; +} + +static TableGen::Emitter::OptClass<VTEmitter> X("gen-vt", "Generate ValueType"); |