//===- 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 #include #include 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 namespace { enum class Frontend { LLVM, Flang, Clang }; StringRef getFESpelling(Frontend FE) { switch (FE) { case Frontend::LLVM: return "llvm"; case Frontend::Flang: return "flang"; case Frontend::Clang: return "clang"; } llvm_unreachable("unknown FE kind"); } } // namespace // Get the full namespace qualifier for the directive language. static std::string getQualifier(const DirectiveLanguage &DirLang, Frontend FE = Frontend::LLVM) { return (Twine(getFESpelling(FE)) + "::" + DirLang.getCppNamespace().str() + "::") .str(); } // Get prefixed formatted name, e.g. for "target data", get "OMPD_target_data". // This should work for any Record as long as BaseRecord::getFormattedName // works. static std::string getIdentifierName(const Record *Rec, StringRef Prefix) { return Prefix.str() + BaseRecord(Rec).getFormattedName(); } using RecordWithSpelling = std::pair; static std::vector getSpellings(ArrayRef Records) { std::vector List; for (const Record *R : Records) { BaseRecord Rec(R); llvm::transform(Rec.getSpellings(), std::back_inserter(List), [R](Spelling::Value V) { return std::make_pair(R, V); }); } return List; } static void generateEnumExports(ArrayRef Records, raw_ostream &OS, StringRef Enum, StringRef Prefix) { for (const Record *R : Records) { std::string N = getIdentifierName(R, Prefix); OS << "constexpr auto " << N << " = " << Enum << "::" << N << ";\n"; } } // Generate enum class. Entries are emitted in the order in which they appear // in the `Records` vector. static void generateEnumClass(ArrayRef Records, raw_ostream &OS, StringRef Enum, StringRef Prefix, bool ExportEnums) { OS << "\n"; OS << "enum class " << Enum << " {\n"; if (!Records.empty()) { std::string N; for (auto [I, R] : llvm::enumerate(Records)) { N = getIdentifierName(R, Prefix); OS << " " << N << ",\n"; // Make the sentinel names less likely to conflict with actual names... if (I == 0) OS << " First_ = " << N << ",\n"; } OS << " Last_ = " << N << ",\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 `. // 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"; generateEnumExports(Records, OS, Enum, Prefix); } } // Generate enum class with values corresponding to different bit positions. // Entries are emitted in the order in which they appear in the `Records` // vector. static void generateEnumBitmask(ArrayRef Records, raw_ostream &OS, StringRef Enum, StringRef Prefix, bool ExportEnums) { assert(Records.size() <= 64 && "Too many values for a bitmask"); StringRef Type = Records.size() <= 32 ? "uint32_t" : "uint64_t"; StringRef TypeSuffix = Records.size() <= 32 ? "U" : "ULL"; OS << "\n"; OS << "enum class " << Enum << " : " << Type << " {\n"; std::string LastName; for (auto [I, R] : llvm::enumerate(Records)) { LastName = getIdentifierName(R, Prefix); OS << " " << LastName << " = " << (1ull << I) << TypeSuffix << ",\n"; } OS << " LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/" << LastName << ")\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 `. // 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"; generateEnumExports(Records, OS, Enum, Prefix); } } // Generate enums for values that clauses can take. // Also generate function declarations for getName(StringRef Str). static void generateClauseEnumVal(ArrayRef Records, raw_ostream &OS, const DirectiveLanguage &DirLang, std::string &EnumHelperFuncs) { for (const Record *R : Records) { Clause C(R); const auto &ClauseVals = C.getClauseVals(); if (ClauseVals.size() <= 0) continue; StringRef Enum = C.getEnumName(); if (Enum.empty()) { PrintError("enumClauseValue field not set in Clause" + C.getFormattedName() + "."); return; } OS << "\n"; OS << "enum class " << Enum << " {\n"; for (const EnumVal Val : ClauseVals) OS << " " << Val.getRecordName() << "=" << Val.getValue() << ",\n"; OS << "};\n"; if (DirLang.hasMakeEnumAvailableInNamespace()) { OS << "\n"; for (const auto &CV : ClauseVals) { OS << "constexpr auto " << CV->getName() << " = " << Enum << "::" << CV->getName() << ";\n"; } EnumHelperFuncs += (Twine("LLVM_ABI ") + Twine(Enum) + Twine(" get") + Twine(Enum) + Twine("(StringRef Str);\n")) .str(); EnumHelperFuncs += (Twine("LLVM_ABI StringRef get") + Twine(DirLang.getName()) + Twine(Enum) + Twine("Name(") + Twine(Enum) + Twine(" x);\n")) .str(); } } } static bool hasDuplicateClauses(ArrayRef Clauses, const Directive &Directive, StringSet<> &CrtClauses) { bool HasError = false; for (const VersionedClause VerClause : Clauses) { StringRef Name = VerClause.getClause().getRecordName(); const auto InsRes = CrtClauses.insert(Name); if (!InsRes.second) { PrintError("Clause " + Name + " 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 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; StringRef Lang = DirLang.getName(); OS << "#ifndef LLVM_" << Lang << "_INC\n"; OS << "#define LLVM_" << Lang << "_INC\n"; OS << "\n#include \"llvm/ADT/ArrayRef.h\"\n"; if (DirLang.hasEnableBitmaskEnumInNamespace()) OS << "#include \"llvm/ADT/BitmaskEnum.h\"\n"; OS << "#include \"llvm/ADT/Sequence.h\"\n"; OS << "#include \"llvm/ADT/StringRef.h\"\n"; OS << "#include \"llvm/Frontend/Directive/Spelling.h\"\n"; OS << "#include \"llvm/Support/Compiler.h\"\n"; OS << "#include \n"; // for size_t OS << "#include \n"; // for std::pair OS << "\n"; OS << "namespace llvm {\n"; // Open namespaces defined in the directive language SmallVector 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 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=*/"", /*ExportEnums=*/false); generateEnumClass(DirLang.getCategories(), OS, "Category", /*Prefix=*/"", /*ExportEnums=*/false); generateEnumBitmask(DirLang.getSourceLanguages(), OS, "SourceLanguage", /*Prefix=*/"", /*ExportEnums=*/false); // Emit Directive enumeration generateEnumClass(DirLang.getDirectives(), OS, "Directive", DirLang.getDirectivePrefix(), DirLang.hasMakeEnumAvailableInNamespace()); // Emit Clause enumeration generateEnumClass(DirLang.getClauses(), OS, "Clause", DirLang.getClausePrefix(), DirLang.hasMakeEnumAvailableInNamespace()); // Emit ClauseVals enumeration std::string EnumHelperFuncs; generateClauseEnumVal(DirLang.getClauses(), OS, DirLang, EnumHelperFuncs); // Generic function signatures OS << "\n"; OS << "// Enumeration helper functions\n"; OS << "LLVM_ABI std::pair get" << Lang << "DirectiveKindAndVersions(StringRef Str);\n"; OS << "inline Directive get" << Lang << "DirectiveKind(StringRef Str) {\n"; OS << " return get" << Lang << "DirectiveKindAndVersions(Str).first;\n"; OS << "}\n"; OS << "\n"; OS << "LLVM_ABI StringRef get" << Lang << "DirectiveName(Directive D, unsigned Ver = 0);\n"; OS << "\n"; OS << "LLVM_ABI std::pair get" << Lang << "ClauseKindAndVersions(StringRef Str);\n"; OS << "\n"; OS << "inline Clause get" << Lang << "ClauseKind(StringRef Str) {\n"; OS << " return get" << Lang << "ClauseKindAndVersions(Str).first;\n"; OS << "}\n"; OS << "\n"; OS << "LLVM_ABI StringRef get" << Lang << "ClauseName(Clause C, unsigned Ver = 0);\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"; OS << "LLVM_ABI SourceLanguage getDirectiveLanguages(Directive D);\n"; if (EnumHelperFuncs.length() > 0) { OS << EnumHelperFuncs; OS << "\n"; } // Closing namespaces for (auto Ns : reverse(Namespaces)) OS << "} // namespace " << Ns << "\n"; // These specializations need to be in ::llvm. for (StringRef Enum : {"Association", "Category", "Directive", "Clause"}) { OS << "\n"; OS << "template <> struct enum_iteration_traits<" << DirLang.getCppNamespace() << "::" << Enum << "> {\n"; OS << " static constexpr bool is_iterable = true;\n"; OS << "};\n"; } OS << "} // namespace llvm\n"; OS << "#endif // LLVM_" << Lang << "_INC\n"; } // Given a list of spellings (for a given clause/directive), order them // in a way that allows the use of binary search to locate a spelling // for a specified version. static std::vector orderSpellings(ArrayRef Spellings) { std::vector List(Spellings.begin(), Spellings.end()); llvm::stable_sort(List, [](const Spelling::Value &A, const Spelling::Value &B) { return A.Versions < B.Versions; }); return List; } // Generate function implementation for getName(StringRef Str) static void generateGetName(ArrayRef Records, raw_ostream &OS, StringRef Enum, const DirectiveLanguage &DirLang, StringRef Prefix) { StringRef Lang = DirLang.getName(); std::string Qual = getQualifier(DirLang); OS << "\n"; OS << "llvm::StringRef " << Qual << "get" << Lang << Enum << "Name(" << Qual << Enum << " Kind, unsigned Version) {\n"; OS << " switch (Kind) {\n"; for (const Record *R : Records) { BaseRecord Rec(R); std::string Ident = getIdentifierName(R, Prefix); OS << " case " << Ident << ":"; std::vector Spellings(orderSpellings(Rec.getSpellings())); assert(Spellings.size() != 0 && "No spellings for this item"); if (Spellings.size() == 1) { OS << "\n"; OS << " return \"" << Spellings.front().Name << "\";\n"; } else { OS << " {\n"; std::string SpellingsName = Ident + "_spellings"; OS << " static constexpr llvm::directive::Spelling " << SpellingsName << "[] = {\n"; for (auto &S : Spellings) { OS << " {\"" << S.Name << "\", {" << S.Versions.Min << ", " << S.Versions.Max << "}},\n"; } OS << " };\n"; OS << " return llvm::directive::FindName(" << SpellingsName << ", Version);\n"; OS << " }\n"; } } OS << " }\n"; // switch OS << " llvm_unreachable(\"Invalid " << Lang << " " << Enum << " kind\");\n"; OS << "}\n"; } // Generate function implementation for getKindAndVersions(StringRef Str) static void generateGetKind(ArrayRef 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); std::string Qual = getQualifier(DirLang); std::string DefaultName = getIdentifierName(*DefaultIt, Prefix); // std::pair<, VersionRange> // getKindAndVersions(StringRef Str); OS << "\n"; OS << "std::pair<" << Qual << Enum << ", llvm::directive::VersionRange> " << Qual << "get" << DirLang.getName() << Enum << "KindAndVersions(llvm::StringRef Str) {\n"; OS << " directive::VersionRange All; // Default-initialized to \"all " "versions\"\n"; OS << " return StringSwitch>(Str)\n"; directive::VersionRange All; for (const Record *R : Records) { BaseRecord Rec(R); std::string Ident = ImplicitAsUnknown && R->getValueAsBit("isImplicit") ? DefaultName : getIdentifierName(R, Prefix); for (auto &[Name, Versions] : Rec.getSpellings()) { OS << " .Case(\"" << Name << "\", {" << Ident << ", "; if (Versions.Min == All.Min && Versions.Max == All.Max) OS << "All})\n"; else OS << "{" << Versions.Min << ", " << Versions.Max << "}})\n"; } } OS << " .Default({" << DefaultName << ", All});\n"; OS << "}\n"; } // Generate function implementations for // get(StringRef Str) and // StringRef getName() static void generateGetClauseVal(const DirectiveLanguage &DirLang, raw_ostream &OS) { StringRef Lang = DirLang.getName(); std::string Qual = getQualifier(DirLang); 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.getRecordName() + " must be defined as default."); return; } const auto DefaultName = (*DefaultIt)->getName(); StringRef Enum = C.getEnumName(); if (Enum.empty()) { PrintError("enumClauseValue field not set in Clause" + C.getRecordName() + "."); return; } OS << "\n"; OS << Qual << Enum << " " << Qual << "get" << Enum << "(llvm::StringRef Str) {\n"; OS << " return StringSwitch<" << Enum << ">(Str)\n"; for (const EnumVal Val : ClauseVals) { OS << " .Case(\"" << Val.getFormattedName() << "\"," << Val.getRecordName() << ")\n"; } OS << " .Default(" << DefaultName << ");\n"; OS << "}\n"; OS << "\n"; OS << "llvm::StringRef " << Qual << "get" << Lang << Enum << "Name(" << Qual << Enum << " x) {\n"; OS << " switch (x) {\n"; for (const EnumVal Val : ClauseVals) { OS << " case " << Val.getRecordName() << ":\n"; OS << " return \"" << Val.getFormattedName() << "\";\n"; } OS << " }\n"; // switch OS << " llvm_unreachable(\"Invalid " << Lang << " " << Enum << " kind\");\n"; OS << "}\n"; } } static void generateCaseForVersionedClauses(ArrayRef VerClauses, raw_ostream &OS, const DirectiveLanguage &DirLang, StringSet<> &Cases) { StringRef Prefix = DirLang.getClausePrefix(); for (const Record *R : VerClauses) { VersionedClause VerClause(R); std::string Name = getIdentifierName(VerClause.getClause().getRecord(), Prefix); if (Cases.insert(Name).second) { OS << " case " << Name << ":\n"; OS << " return " << VerClause.getMinVersion() << " <= Version && " << VerClause.getMaxVersion() << " >= Version;\n"; } } } // Generate the isAllowedClauseForDirective function implementation. static void generateIsAllowedClause(const DirectiveLanguage &DirLang, raw_ostream &OS) { std::string Qual = getQualifier(DirLang); OS << "\n"; OS << "bool " << Qual << "isAllowedClauseForDirective(" << Qual << "Directive D, " << Qual << "Clause C, unsigned Version) {\n"; OS << " assert(unsigned(D) <= Directive_enumSize);\n"; OS << " assert(unsigned(C) <= Clause_enumSize);\n"; OS << " switch (D) {\n"; StringRef Prefix = DirLang.getDirectivePrefix(); for (const Record *R : DirLang.getDirectives()) { Directive Dir(R); OS << " case " << getIdentifierName(R, Prefix) << ":\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, DirLang, Cases); generateCaseForVersionedClauses(Dir.getAllowedOnceClauses(), OS, DirLang, Cases); generateCaseForVersionedClauses(Dir.getAllowedExclusiveClauses(), OS, DirLang, Cases); generateCaseForVersionedClauses(Dir.getRequiredClauses(), OS, 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 Directives = DirLang.getDirectives(); DenseMap DirId; // Record * -> llvm::omp::Directive for (auto [Idx, Rec] : enumerate(Directives)) DirId.try_emplace(Rec, Idx); using LeafList = std::vector; int MaxLeafCount = getMaxLeafCount(DirLang); // The initial leaf table, rows order is same as directive order. std::vector LeafTable(Directives.size()); for (auto [Idx, Rec] : enumerate(Directives)) { Directive Dir(Rec); std::vector 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(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 EndDirectives; for (auto [Rec, Id] : DirId) { // FIXME: This will need to recognize different spellings for different // versions. StringRef Name = Directive(Rec).getSpellingForIdentifier(); if (Name.starts_with_insensitive("end ")) EndDirectives.insert(Id); } // Avoid sorting the vector array, instead sort an index array. // It will also be useful later to create the auxiliary indexing array. std::vector Ordering(Directives.size()); std::iota(Ordering.begin(), Ordering.end(), 0); llvm::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. bool IsEndA = EndDirectives.contains(DirA); bool IsEndB = EndDirectives.contains(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. StringRef Prefix = DirLang.getDirectivePrefix(); std::string Qual = getQualifier(DirLang); std::string DirectiveType = Qual + "Directive"; 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 << " {" << Qual << getIdentifierName(Directives[Leaves[0]], Prefix); 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 << ' ' << Qual << getIdentifierName(Directives[Leaves[I]], Prefix) << ','; 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.contains(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 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 Associations = DirLang.getAssociations(); auto GetAssocValue = [](StringRef Name) -> Association { return StringSwitch(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.getRecordName() + "' 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 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.try_emplace(R, AS); return AS; } // Compute the association from leaf constructs. std::vector Leaves = D.getLeafConstructs(); if (Leaves.empty()) { 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.try_emplace(R, Result); return Result; }; for (const Record *R : DirLang.getDirectives()) CompAssocImpl(R, CompAssocImpl); // Updates AsMap. OS << '\n'; StringRef Prefix = DirLang.getDirectivePrefix(); std::string Qual = getQualifier(DirLang); OS << Qual << "Association " << Qual << "getDirectiveAssociation(" << Qual << "Directive Dir) {\n"; OS << " switch (Dir) {\n"; for (const Record *R : DirLang.getDirectives()) { if (auto F = AsMap.find(R); F != AsMap.end()) { OS << " case " << getIdentifierName(R, Prefix) << ":\n"; OS << " return Association::" << 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 Qual = getQualifier(DirLang); OS << '\n'; OS << Qual << "Category " << Qual << "getDirectiveCategory(" << Qual << "Directive Dir) {\n"; OS << " switch (Dir) {\n"; StringRef Prefix = DirLang.getDirectivePrefix(); for (const Record *R : DirLang.getDirectives()) { Directive D(R); OS << " case " << getIdentifierName(R, Prefix) << ":\n"; OS << " return Category::" << D.getCategory()->getValueAsString("name") << ";\n"; } OS << " } // switch (Dir)\n"; OS << " llvm_unreachable(\"Unexpected directive\");\n"; OS << "}\n"; } static void generateGetDirectiveLanguages(const DirectiveLanguage &DirLang, raw_ostream &OS) { std::string Qual = getQualifier(DirLang); OS << '\n'; OS << Qual << "SourceLanguage " << Qual << "getDirectiveLanguages(" << Qual << "Directive D) {\n"; OS << " switch (D) {\n"; StringRef Prefix = DirLang.getDirectivePrefix(); for (const Record *R : DirLang.getDirectives()) { Directive D(R); OS << " case " << getIdentifierName(R, Prefix) << ":\n"; OS << " return "; llvm::interleave( D.getSourceLanguages(), OS, [&](const Record *L) { StringRef N = L->getValueAsString("name"); OS << "SourceLanguage::" << BaseRecord::getSnakeName(N); }, " | "); OS << ";\n"; } OS << " } // switch(D)\n"; OS << " llvm_unreachable(\"Unexpected directive\");\n"; OS << "}\n"; } // Generate a simple enum set with the give clauses. static void generateClauseSet(ArrayRef VerClauses, raw_ostream &OS, StringRef ClauseSetPrefix, const Directive &Dir, const DirectiveLanguage &DirLang, Frontend FE) { OS << "\n"; OS << "static " << DirLang.getClauseEnumSetClass() << " " << ClauseSetPrefix << DirLang.getDirectivePrefix() << Dir.getFormattedName() << " {\n"; StringRef Prefix = DirLang.getClausePrefix(); for (const VersionedClause VerClause : VerClauses) { Clause C = VerClause.getClause(); if (FE == Frontend::Flang) { OS << " Clause::" << getIdentifierName(C.getRecord(), Prefix) << ",\n"; } else { assert(FE == Frontend::Clang); assert(DirLang.getName() == "OpenACC"); OS << " OpenACCClauseKind::" << C.getClangAccSpelling() << ",\n"; } } OS << "};\n"; } // Generate an enum set for the 4 kinds of clauses linked to a directive. static void generateDirectiveClauseSets(const DirectiveLanguage &DirLang, Frontend FE, raw_ostream &OS) { std::string IfDefName{"GEN_"}; IfDefName += getFESpelling(FE).upper(); IfDefName += "_DIRECTIVE_CLAUSE_SETS"; IfDefScope Scope(IfDefName, OS); StringRef Namespace = getFESpelling(FE == Frontend::Flang ? Frontend::LLVM : FE); OS << "\n"; // The namespace has to be different for clang vs flang, as 2 structs with the // same name but different layout is UB. So just put the 'clang' on in the // clang namespace. OS << "namespace " << Namespace << " {\n"; // Open namespaces defined in the directive language. SmallVector 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.getSpellingForIdentifier() << "\n"; generateClauseSet(Dir.getAllowedClauses(), OS, "allowedClauses_", Dir, DirLang, FE); generateClauseSet(Dir.getAllowedOnceClauses(), OS, "allowedOnceClauses_", Dir, DirLang, FE); generateClauseSet(Dir.getAllowedExclusiveClauses(), OS, "allowedExclusiveClauses_", Dir, DirLang, FE); generateClauseSet(Dir.getRequiredClauses(), OS, "requiredClauses_", Dir, DirLang, FE); } // Closing namespaces for (auto Ns : reverse(Namespaces)) OS << "} // namespace " << Ns << "\n"; OS << "} // namespace " << Namespace << "\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, Frontend FE, raw_ostream &OS) { std::string IfDefName{"GEN_"}; IfDefName += getFESpelling(FE).upper(); IfDefName += "_DIRECTIVE_CLAUSE_MAP"; IfDefScope Scope(IfDefName, OS); OS << "\n"; OS << "{\n"; // The namespace has to be different for clang vs flang, as 2 structs with the // same name but different layout is UB. So just put the 'clang' on in the // clang namespace. std::string Qual = getQualifier(DirLang, FE == Frontend::Flang ? Frontend::LLVM : FE); StringRef Prefix = DirLang.getDirectivePrefix(); for (const Record *R : DirLang.getDirectives()) { Directive Dir(R); std::string Name = getIdentifierName(R, Prefix); OS << " {"; if (FE == Frontend::Flang) { OS << Qual << "Directive::" << Name << ",\n"; } else { assert(FE == Frontend::Clang); assert(DirLang.getName() == "OpenACC"); OS << "clang::OpenACCDirectiveKind::" << Dir.getClangAccSpelling() << ",\n"; } OS << " {\n"; OS << " " << Qual << "allowedClauses_" << Name << ",\n"; OS << " " << Qual << "allowedOnceClauses_" << Name << ",\n"; OS << " " << Qual << "allowedExclusiveClauses_" << Name << ",\n"; OS << " " << Qual << "requiredClauses_" << Name << ",\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>"; } 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); StringRef Base = DirLang.getFlangClauseBaseClass(); OS << "\n"; for (const Clause Clause : DirLang.getClauses()) { if (Clause.skipFlangUnparser()) continue; // The unparser doesn't know the effective version, so just pick some // spelling. StringRef SomeSpelling = Clause.getSpellingForIdentifier(); std::string Parser = Clause.getFormattedParserClassName(); std::string Upper = SomeSpelling.upper(); if (!Clause.getFlangClass().empty()) { if (Clause.isValueOptional() && Clause.getDefaultValue().empty()) { OS << "void Unparse(const " << Base << "::" << Parser << " &x) {\n"; OS << " Word(\"" << Upper << "\");\n"; OS << " Walk(\"(\", x.v, \")\");\n"; OS << "}\n"; } else if (Clause.isValueOptional()) { OS << "void Unparse(const " << Base << "::" << Parser << " &x) {\n"; OS << " Word(\"" << 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 " << Base << "::" << Parser << " &x) {\n"; OS << " Word(\"" << 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 " << Base << "::" << Parser << " &) { Word(\"" << 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); StringRef Prefix = DirLang.getClausePrefix(); std::string Qual = getQualifier(DirLang); OS << "\n"; for (const Record *R : DirLang.getClauses()) { Clause C(R); OS << "if constexpr (std::is_same_v)\n"; OS << " return " << Qual << "Clause::" << getIdentifierName(R, Prefix) << ";\n"; } OS << "llvm_unreachable(\"Invalid " << DirLang.getName() << " Parser clause\");\n"; } // Generate the parser for the clauses. static void generateFlangClausesParser(const DirectiveLanguage &DirLang, raw_ostream &OS) { std::vector Clauses = DirLang.getClauses(); // Sort clauses in the reverse alphabetical order with respect to their // names and aliases, so that longer names are tried before shorter ones. std::vector Names = getSpellings(Clauses); llvm::sort(Names, [](const auto &A, const auto &B) { return A.second.Name > B.second.Name; }); IfDefScope Scope("GEN_FLANG_CLAUSES_PARSER", OS); StringRef Base = DirLang.getFlangClauseBaseClass(); unsigned LastIndex = Names.size() - 1; OS << "\n"; OS << "TYPE_PARSER(\n"; for (auto [Index, RecSp] : llvm::enumerate(Names)) { auto [R, S] = RecSp; Clause C(R); StringRef FlangClass = C.getFlangClass(); OS << " \"" << S.Name << "\" >> construct<" << Base << ">(construct<" << Base << "::" << C.getFormattedParserClassName() << ">("; if (FlangClass.empty()) { OS << "))"; if (Index != LastIndex) OS << " ||"; OS << "\n"; continue; } if (C.isValueOptional()) OS << "maybe("; OS << "parenthesized("; if (C.isValueList()) OS << "nonemptyList("; if (!C.getPrefix().empty()) OS << "\"" << C.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(FlangClass) .Case("Name", "name") .Case("ScalarIntConstantExpr", "scalarIntConstantExpr") .Case("ScalarIntExpr", "scalarIntExpr") .Case("ScalarExpr", "scalarExpr") .Case("ScalarLogicalExpr", "scalarLogicalExpr") .Default(("Parser<" + FlangClass + ">{}").toStringRef(Scratch)); OS << Parser; if (!C.getPrefix().empty() && C.isPrefixOptional()) OS << " || " << Parser; if (C.isValueList()) // close nonemptyList(. OS << ")"; OS << ")"; // close parenthesized(. if (C.isValueOptional()) // close maybe(. OS << ")"; OS << "))"; if (Index != LastIndex) OS << " ||"; OS << "\n"; } OS << ")\n"; } // Generate the implementation section for the enumeration in the directive // language static void emitDirectivesClangImpl(const DirectiveLanguage &DirLang, raw_ostream &OS) { // Currently we only have work to do for OpenACC, so skip otherwise. if (DirLang.getName() != "OpenACC") return; generateDirectiveClauseSets(DirLang, Frontend::Clang, OS); generateDirectiveClauseMap(DirLang, Frontend::Clang, OS); } // Generate the implementation section for the enumeration in the directive // language static void emitDirectivesFlangImpl(const DirectiveLanguage &DirLang, raw_ostream &OS) { generateDirectiveClauseSets(DirLang, Frontend::Flang, OS); generateDirectiveClauseMap(DirLang, Frontend::Flang, 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); StringRef Prefix = DirLang.getClausePrefix(); 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(" << Prefix << "##Name, #Name, /* Implicit */ false) \\\n"; OS << " CLAUSE_CLASS(" << Prefix << "##Name, #Name, Class)\n"; OS << "#define __CLAUSE_NO_CLASS(Name) \\\n"; OS << " CLAUSE(" << Prefix << "##Name, #Name, /* Implicit */ false) \\\n"; OS << " CLAUSE_NO_CLASS(" << Prefix << "##Name, #Name)\n"; OS << "#define __IMPLICIT_CLAUSE_CLASS(Name, Str, Class) \\\n"; OS << " CLAUSE(" << Prefix << "##Name, Str, /* Implicit */ true) \\\n"; OS << " CLAUSE_CLASS(" << Prefix << "##Name, Str, Class)\n"; OS << "#define __IMPLICIT_CLAUSE_NO_CLASS(Name, Str) \\\n"; OS << " CLAUSE(" << Prefix << "##Name, Str, /* Implicit */ true) \\\n"; OS << " CLAUSE_NO_CLASS(" << Prefix << "##Name, Str)\n"; OS << "\n"; for (const Clause C : DirLang.getClauses()) { std::string Name = C.getFormattedName(); if (C.getClangClass().empty()) { // NO_CLASS if (C.isImplicit()) { OS << "__IMPLICIT_CLAUSE_NO_CLASS(" << Name << ", \"" << Name << "\")\n"; } else { OS << "__CLAUSE_NO_CLASS(" << Name << ")\n"; } } else { // CLASS if (C.isImplicit()) { OS << "__IMPLICIT_CLAUSE_CLASS(" << Name << ", \"" << Name << "\", " << C.getClangClass() << ")\n"; } else { OS << "__CLAUSE(" << Name << ", " << 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); StringRef DPrefix = DirLang.getDirectivePrefix(); StringRef CPrefix = DirLang.getClausePrefix(); OS << "\n"; OS << "#include \"llvm/Frontend/Directive/Spelling.h\"\n"; OS << "#include \"llvm/Support/ErrorHandling.h\"\n"; OS << "#include \n"; // getDirectiveKind(StringRef Str) generateGetKind(DirLang.getDirectives(), OS, "Directive", DirLang, DPrefix, /*ImplicitAsUnknown=*/false); // getDirectiveName(Directive Kind) generateGetName(DirLang.getDirectives(), OS, "Directive", DirLang, DPrefix); // getClauseKind(StringRef Str) generateGetKind(DirLang.getClauses(), OS, "Clause", DirLang, CPrefix, /*ImplicitAsUnknown=*/true); // getClauseName(Clause Kind) generateGetName(DirLang.getClauses(), OS, "Clause", DirLang, CPrefix); // get(StringRef Str) ; string -> value // StringRef getName() ; value -> string generateGetClauseVal(DirLang, OS); // isAllowedClauseForDirective(Directive D, Clause C, unsigned Version) generateIsAllowedClause(DirLang, OS); // getDirectiveAssociation(Directive D) generateGetDirectiveAssociation(DirLang, OS); // getDirectiveCategory(Directive D) generateGetDirectiveCategory(DirLang, OS); // getDirectiveLanguages(Directive D) generateGetDirectiveLanguages(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); emitDirectivesClangImpl(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");