Path: blob/main/contrib/llvm-project/llvm/utils/TableGen/DirectiveEmitter.cpp
35258 views
//===- DirectiveEmitter.cpp - Directive Language Emitter ------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// DirectiveEmitter uses the descriptions of directives and clauses to construct9// common code declarations to be used in Frontends.10//11//===----------------------------------------------------------------------===//1213#include "llvm/TableGen/DirectiveEmitter.h"14#include "llvm/ADT/DenseMap.h"15#include "llvm/ADT/DenseSet.h"16#include "llvm/ADT/STLExtras.h"17#include "llvm/ADT/SmallVector.h"18#include "llvm/ADT/StringSet.h"19#include "llvm/ADT/StringSwitch.h"20#include "llvm/TableGen/Error.h"21#include "llvm/TableGen/Record.h"22#include "llvm/TableGen/TableGenBackend.h"2324#include <numeric>25#include <vector>2627using namespace llvm;2829namespace {30// Simple RAII helper for defining ifdef-undef-endif scopes.31class IfDefScope {32public:33IfDefScope(StringRef Name, raw_ostream &OS) : Name(Name), OS(OS) {34OS << "#ifdef " << Name << "\n"35<< "#undef " << Name << "\n";36}3738~IfDefScope() { OS << "\n#endif // " << Name << "\n\n"; }3940private:41StringRef Name;42raw_ostream &OS;43};44} // namespace4546// Generate enum class. Entries are emitted in the order in which they appear47// in the `Records` vector.48static void GenerateEnumClass(const std::vector<Record *> &Records,49raw_ostream &OS, StringRef Enum, StringRef Prefix,50const DirectiveLanguage &DirLang,51bool ExportEnums) {52OS << "\n";53OS << "enum class " << Enum << " {\n";54for (const auto &R : Records) {55BaseRecord Rec{R};56OS << " " << Prefix << Rec.getFormattedName() << ",\n";57}58OS << "};\n";59OS << "\n";60OS << "static constexpr std::size_t " << Enum61<< "_enumSize = " << Records.size() << ";\n";6263// Make the enum values available in the defined namespace. This allows us to64// write something like Enum_X if we have a `using namespace <CppNamespace>`.65// At the same time we do not loose the strong type guarantees of the enum66// class, that is we cannot pass an unsigned as Directive without an explicit67// cast.68if (ExportEnums) {69OS << "\n";70for (const auto &R : Records) {71BaseRecord Rec{R};72OS << "constexpr auto " << Prefix << Rec.getFormattedName() << " = "73<< "llvm::" << DirLang.getCppNamespace() << "::" << Enum74<< "::" << Prefix << Rec.getFormattedName() << ";\n";75}76}77}7879// Generate enums for values that clauses can take.80// Also generate function declarations for get<Enum>Name(StringRef Str).81static void GenerateEnumClauseVal(const std::vector<Record *> &Records,82raw_ostream &OS,83const DirectiveLanguage &DirLang,84std::string &EnumHelperFuncs) {85for (const auto &R : Records) {86Clause C{R};87const auto &ClauseVals = C.getClauseVals();88if (ClauseVals.size() <= 0)89continue;9091const auto &EnumName = C.getEnumName();92if (EnumName.size() == 0) {93PrintError("enumClauseValue field not set in Clause" +94C.getFormattedName() + ".");95return;96}9798OS << "\n";99OS << "enum class " << EnumName << " {\n";100for (const auto &CV : ClauseVals) {101ClauseVal CVal{CV};102OS << " " << CV->getName() << "=" << CVal.getValue() << ",\n";103}104OS << "};\n";105106if (DirLang.hasMakeEnumAvailableInNamespace()) {107OS << "\n";108for (const auto &CV : ClauseVals) {109OS << "constexpr auto " << CV->getName() << " = "110<< "llvm::" << DirLang.getCppNamespace() << "::" << EnumName111<< "::" << CV->getName() << ";\n";112}113EnumHelperFuncs += (llvm::Twine(EnumName) + llvm::Twine(" get") +114llvm::Twine(EnumName) + llvm::Twine("(StringRef);\n"))115.str();116117EnumHelperFuncs +=118(llvm::Twine("llvm::StringRef get") + llvm::Twine(DirLang.getName()) +119llvm::Twine(EnumName) + llvm::Twine("Name(") +120llvm::Twine(EnumName) + llvm::Twine(");\n"))121.str();122}123}124}125126static bool HasDuplicateClauses(const std::vector<Record *> &Clauses,127const Directive &Directive,128llvm::StringSet<> &CrtClauses) {129bool HasError = false;130for (const auto &C : Clauses) {131VersionedClause VerClause{C};132const auto insRes = CrtClauses.insert(VerClause.getClause().getName());133if (!insRes.second) {134PrintError("Clause " + VerClause.getClause().getRecordName() +135" already defined on directive " + Directive.getRecordName());136HasError = true;137}138}139return HasError;140}141142// Check for duplicate clauses in lists. Clauses cannot appear twice in the143// three allowed list. Also, since required implies allowed, clauses cannot144// appear in both the allowedClauses and requiredClauses lists.145static bool146HasDuplicateClausesInDirectives(const std::vector<Record *> &Directives) {147bool HasDuplicate = false;148for (const auto &D : Directives) {149Directive Dir{D};150llvm::StringSet<> Clauses;151// Check for duplicates in the three allowed lists.152if (HasDuplicateClauses(Dir.getAllowedClauses(), Dir, Clauses) ||153HasDuplicateClauses(Dir.getAllowedOnceClauses(), Dir, Clauses) ||154HasDuplicateClauses(Dir.getAllowedExclusiveClauses(), Dir, Clauses)) {155HasDuplicate = true;156}157// Check for duplicate between allowedClauses and required158Clauses.clear();159if (HasDuplicateClauses(Dir.getAllowedClauses(), Dir, Clauses) ||160HasDuplicateClauses(Dir.getRequiredClauses(), Dir, Clauses)) {161HasDuplicate = true;162}163if (HasDuplicate)164PrintFatalError("One or more clauses are defined multiple times on"165" directive " +166Dir.getRecordName());167}168169return HasDuplicate;170}171172// Check consitency of records. Return true if an error has been detected.173// Return false if the records are valid.174bool DirectiveLanguage::HasValidityErrors() const {175if (getDirectiveLanguages().size() != 1) {176PrintFatalError("A single definition of DirectiveLanguage is needed.");177return true;178}179180return HasDuplicateClausesInDirectives(getDirectives());181}182183// Count the maximum number of leaf constituents per construct.184static size_t GetMaxLeafCount(const DirectiveLanguage &DirLang) {185size_t MaxCount = 0;186for (Record *R : DirLang.getDirectives()) {187size_t Count = Directive{R}.getLeafConstructs().size();188MaxCount = std::max(MaxCount, Count);189}190return MaxCount;191}192193// Generate the declaration section for the enumeration in the directive194// language195static void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) {196const auto DirLang = DirectiveLanguage{Records};197if (DirLang.HasValidityErrors())198return;199200OS << "#ifndef LLVM_" << DirLang.getName() << "_INC\n";201OS << "#define LLVM_" << DirLang.getName() << "_INC\n";202OS << "\n#include \"llvm/ADT/ArrayRef.h\"\n";203204if (DirLang.hasEnableBitmaskEnumInNamespace())205OS << "#include \"llvm/ADT/BitmaskEnum.h\"\n";206207OS << "#include <cstddef>\n"; // for size_t208OS << "\n";209OS << "namespace llvm {\n";210OS << "class StringRef;\n";211212// Open namespaces defined in the directive language213llvm::SmallVector<StringRef, 2> Namespaces;214llvm::SplitString(DirLang.getCppNamespace(), Namespaces, "::");215for (auto Ns : Namespaces)216OS << "namespace " << Ns << " {\n";217218if (DirLang.hasEnableBitmaskEnumInNamespace())219OS << "\nLLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();\n";220221// Emit Directive associations222std::vector<Record *> associations;223llvm::copy_if(224DirLang.getAssociations(), std::back_inserter(associations),225// Skip the "special" value226[](const Record *Def) { return Def->getName() != "AS_FromLeaves"; });227GenerateEnumClass(associations, OS, "Association",228/*Prefix=*/"", DirLang, /*ExportEnums=*/false);229230GenerateEnumClass(DirLang.getCategories(), OS, "Category", /*Prefix=*/"",231DirLang, /*ExportEnums=*/false);232233// Emit Directive enumeration234GenerateEnumClass(DirLang.getDirectives(), OS, "Directive",235DirLang.getDirectivePrefix(), DirLang,236DirLang.hasMakeEnumAvailableInNamespace());237238// Emit Clause enumeration239GenerateEnumClass(DirLang.getClauses(), OS, "Clause",240DirLang.getClausePrefix(), DirLang,241DirLang.hasMakeEnumAvailableInNamespace());242243// Emit ClauseVal enumeration244std::string EnumHelperFuncs;245GenerateEnumClauseVal(DirLang.getClauses(), OS, DirLang, EnumHelperFuncs);246247// Generic function signatures248OS << "\n";249OS << "// Enumeration helper functions\n";250OS << "Directive get" << DirLang.getName()251<< "DirectiveKind(llvm::StringRef Str);\n";252OS << "\n";253OS << "llvm::StringRef get" << DirLang.getName()254<< "DirectiveName(Directive D);\n";255OS << "\n";256OS << "Clause get" << DirLang.getName()257<< "ClauseKind(llvm::StringRef Str);\n";258OS << "\n";259OS << "llvm::StringRef get" << DirLang.getName() << "ClauseName(Clause C);\n";260OS << "\n";261OS << "/// Return true if \\p C is a valid clause for \\p D in version \\p "262<< "Version.\n";263OS << "bool isAllowedClauseForDirective(Directive D, "264<< "Clause C, unsigned Version);\n";265OS << "\n";266OS << "constexpr std::size_t getMaxLeafCount() { return "267<< GetMaxLeafCount(DirLang) << "; }\n";268OS << "Association getDirectiveAssociation(Directive D);\n";269OS << "Category getDirectiveCategory(Directive D);\n";270if (EnumHelperFuncs.length() > 0) {271OS << EnumHelperFuncs;272OS << "\n";273}274275// Closing namespaces276for (auto Ns : llvm::reverse(Namespaces))277OS << "} // namespace " << Ns << "\n";278279OS << "} // namespace llvm\n";280281OS << "#endif // LLVM_" << DirLang.getName() << "_INC\n";282}283284// Generate function implementation for get<Enum>Name(StringRef Str)285static void GenerateGetName(const std::vector<Record *> &Records,286raw_ostream &OS, StringRef Enum,287const DirectiveLanguage &DirLang,288StringRef Prefix) {289OS << "\n";290OS << "llvm::StringRef llvm::" << DirLang.getCppNamespace() << "::get"291<< DirLang.getName() << Enum << "Name(" << Enum << " Kind) {\n";292OS << " switch (Kind) {\n";293for (const auto &R : Records) {294BaseRecord Rec{R};295OS << " case " << Prefix << Rec.getFormattedName() << ":\n";296OS << " return \"";297if (Rec.getAlternativeName().empty())298OS << Rec.getName();299else300OS << Rec.getAlternativeName();301OS << "\";\n";302}303OS << " }\n"; // switch304OS << " llvm_unreachable(\"Invalid " << DirLang.getName() << " " << Enum305<< " kind\");\n";306OS << "}\n";307}308309// Generate function implementation for get<Enum>Kind(StringRef Str)310static void GenerateGetKind(const std::vector<Record *> &Records,311raw_ostream &OS, StringRef Enum,312const DirectiveLanguage &DirLang, StringRef Prefix,313bool ImplicitAsUnknown) {314315auto DefaultIt = llvm::find_if(316Records, [](Record *R) { return R->getValueAsBit("isDefault") == true; });317318if (DefaultIt == Records.end()) {319PrintError("At least one " + Enum + " must be defined as default.");320return;321}322323BaseRecord DefaultRec{(*DefaultIt)};324325OS << "\n";326OS << Enum << " llvm::" << DirLang.getCppNamespace() << "::get"327<< DirLang.getName() << Enum << "Kind(llvm::StringRef Str) {\n";328OS << " return llvm::StringSwitch<" << Enum << ">(Str)\n";329330for (const auto &R : Records) {331BaseRecord Rec{R};332if (ImplicitAsUnknown && R->getValueAsBit("isImplicit")) {333OS << " .Case(\"" << Rec.getName() << "\"," << Prefix334<< DefaultRec.getFormattedName() << ")\n";335} else {336OS << " .Case(\"" << Rec.getName() << "\"," << Prefix337<< Rec.getFormattedName() << ")\n";338}339}340OS << " .Default(" << Prefix << DefaultRec.getFormattedName() << ");\n";341OS << "}\n";342}343344// Generate function implementation for get<ClauseVal>Kind(StringRef Str)345static void GenerateGetKindClauseVal(const DirectiveLanguage &DirLang,346raw_ostream &OS) {347for (const auto &R : DirLang.getClauses()) {348Clause C{R};349const auto &ClauseVals = C.getClauseVals();350if (ClauseVals.size() <= 0)351continue;352353auto DefaultIt = llvm::find_if(ClauseVals, [](Record *CV) {354return CV->getValueAsBit("isDefault") == true;355});356357if (DefaultIt == ClauseVals.end()) {358PrintError("At least one val in Clause " + C.getFormattedName() +359" must be defined as default.");360return;361}362const auto DefaultName = (*DefaultIt)->getName();363364const auto &EnumName = C.getEnumName();365if (EnumName.size() == 0) {366PrintError("enumClauseValue field not set in Clause" +367C.getFormattedName() + ".");368return;369}370371OS << "\n";372OS << EnumName << " llvm::" << DirLang.getCppNamespace() << "::get"373<< EnumName << "(llvm::StringRef Str) {\n";374OS << " return llvm::StringSwitch<" << EnumName << ">(Str)\n";375for (const auto &CV : ClauseVals) {376ClauseVal CVal{CV};377OS << " .Case(\"" << CVal.getFormattedName() << "\"," << CV->getName()378<< ")\n";379}380OS << " .Default(" << DefaultName << ");\n";381OS << "}\n";382383OS << "\n";384OS << "llvm::StringRef llvm::" << DirLang.getCppNamespace() << "::get"385<< DirLang.getName() << EnumName386<< "Name(llvm::" << DirLang.getCppNamespace() << "::" << EnumName387<< " x) {\n";388OS << " switch (x) {\n";389for (const auto &CV : ClauseVals) {390ClauseVal CVal{CV};391OS << " case " << CV->getName() << ":\n";392OS << " return \"" << CVal.getFormattedName() << "\";\n";393}394OS << " }\n"; // switch395OS << " llvm_unreachable(\"Invalid " << DirLang.getName() << " "396<< EnumName << " kind\");\n";397OS << "}\n";398}399}400401static void402GenerateCaseForVersionedClauses(const std::vector<Record *> &Clauses,403raw_ostream &OS, StringRef DirectiveName,404const DirectiveLanguage &DirLang,405llvm::StringSet<> &Cases) {406for (const auto &C : Clauses) {407VersionedClause VerClause{C};408409const auto ClauseFormattedName = VerClause.getClause().getFormattedName();410411if (Cases.insert(ClauseFormattedName).second) {412OS << " case " << DirLang.getClausePrefix() << ClauseFormattedName413<< ":\n";414OS << " return " << VerClause.getMinVersion()415<< " <= Version && " << VerClause.getMaxVersion() << " >= Version;\n";416}417}418}419420static std::string GetDirectiveName(const DirectiveLanguage &DirLang,421const Record *Rec) {422Directive Dir{Rec};423return (llvm::Twine("llvm::") + DirLang.getCppNamespace() +424"::" + DirLang.getDirectivePrefix() + Dir.getFormattedName())425.str();426}427428static std::string GetDirectiveType(const DirectiveLanguage &DirLang) {429return (llvm::Twine("llvm::") + DirLang.getCppNamespace() + "::Directive")430.str();431}432433// Generate the isAllowedClauseForDirective function implementation.434static void GenerateIsAllowedClause(const DirectiveLanguage &DirLang,435raw_ostream &OS) {436OS << "\n";437OS << "bool llvm::" << DirLang.getCppNamespace()438<< "::isAllowedClauseForDirective("439<< "Directive D, Clause C, unsigned Version) {\n";440OS << " assert(unsigned(D) <= llvm::" << DirLang.getCppNamespace()441<< "::Directive_enumSize);\n";442OS << " assert(unsigned(C) <= llvm::" << DirLang.getCppNamespace()443<< "::Clause_enumSize);\n";444445OS << " switch (D) {\n";446447for (const auto &D : DirLang.getDirectives()) {448Directive Dir{D};449450OS << " case " << DirLang.getDirectivePrefix() << Dir.getFormattedName()451<< ":\n";452if (Dir.getAllowedClauses().size() == 0 &&453Dir.getAllowedOnceClauses().size() == 0 &&454Dir.getAllowedExclusiveClauses().size() == 0 &&455Dir.getRequiredClauses().size() == 0) {456OS << " return false;\n";457} else {458OS << " switch (C) {\n";459460llvm::StringSet<> Cases;461462GenerateCaseForVersionedClauses(Dir.getAllowedClauses(), OS,463Dir.getName(), DirLang, Cases);464465GenerateCaseForVersionedClauses(Dir.getAllowedOnceClauses(), OS,466Dir.getName(), DirLang, Cases);467468GenerateCaseForVersionedClauses(Dir.getAllowedExclusiveClauses(), OS,469Dir.getName(), DirLang, Cases);470471GenerateCaseForVersionedClauses(Dir.getRequiredClauses(), OS,472Dir.getName(), DirLang, Cases);473474OS << " default:\n";475OS << " return false;\n";476OS << " }\n"; // End of clauses switch477}478OS << " break;\n";479}480481OS << " }\n"; // End of directives switch482OS << " llvm_unreachable(\"Invalid " << DirLang.getName()483<< " Directive kind\");\n";484OS << "}\n"; // End of function isAllowedClauseForDirective485}486487static void EmitLeafTable(const DirectiveLanguage &DirLang, raw_ostream &OS,488StringRef TableName) {489// The leaf constructs are emitted in a form of a 2D table, where each490// row corresponds to a directive (and there is a row for each directive).491//492// Each row consists of493// - the id of the directive itself,494// - number of leaf constructs that will follow (0 for leafs),495// - ids of the leaf constructs (none if the directive is itself a leaf).496// The total number of these entries is at most MaxLeafCount+2. If this497// number is less than that, it is padded to occupy exactly MaxLeafCount+2498// entries in memory.499//500// The rows are stored in the table in the lexicographical order. This501// is intended to enable binary search when mapping a sequence of leafs502// back to the compound directive.503// The consequence of that is that in order to find a row corresponding504// to the given directive, we'd need to scan the first element of each505// row. To avoid this, an auxiliary ordering table is created, such that506// row for Dir_A = table[auxiliary[Dir_A]].507508std::vector<Record *> Directives = DirLang.getDirectives();509DenseMap<Record *, int> DirId; // Record * -> llvm::omp::Directive510511for (auto [Idx, Rec] : llvm::enumerate(Directives))512DirId.insert(std::make_pair(Rec, Idx));513514using LeafList = std::vector<int>;515int MaxLeafCount = GetMaxLeafCount(DirLang);516517// The initial leaf table, rows order is same as directive order.518std::vector<LeafList> LeafTable(Directives.size());519for (auto [Idx, Rec] : llvm::enumerate(Directives)) {520Directive Dir{Rec};521std::vector<Record *> Leaves = Dir.getLeafConstructs();522523auto &List = LeafTable[Idx];524List.resize(MaxLeafCount + 2);525List[0] = Idx; // The id of the directive itself.526List[1] = Leaves.size(); // The number of leaves to follow.527528for (int I = 0; I != MaxLeafCount; ++I)529List[I + 2] =530static_cast<size_t>(I) < Leaves.size() ? DirId.at(Leaves[I]) : -1;531}532533// Some Fortran directives are delimited, i.e. they have the form of534// "directive"---"end directive". If "directive" is a compound construct,535// then the set of leaf constituents will be nonempty and the same for536// both directives. Given this set of leafs, looking up the corresponding537// compound directive should return "directive", and not "end directive".538// To avoid this problem, gather all "end directives" at the end of the539// leaf table, and only do the search on the initial segment of the table540// that excludes the "end directives".541// It's safe to find all directives whose names begin with "end ". The542// problem only exists for compound directives, like "end do simd".543// All existing directives with names starting with "end " are either544// "end directives" for an existing "directive", or leaf directives545// (such as "end declare target").546DenseSet<int> EndDirectives;547for (auto [Rec, Id] : DirId) {548if (Directive{Rec}.getName().starts_with_insensitive("end "))549EndDirectives.insert(Id);550}551552// Avoid sorting the vector<vector> array, instead sort an index array.553// It will also be useful later to create the auxiliary indexing array.554std::vector<int> Ordering(Directives.size());555std::iota(Ordering.begin(), Ordering.end(), 0);556557llvm::sort(Ordering, [&](int A, int B) {558auto &LeavesA = LeafTable[A];559auto &LeavesB = LeafTable[B];560int DirA = LeavesA[0], DirB = LeavesB[0];561// First of all, end directives compare greater than non-end directives.562int IsEndA = EndDirectives.count(DirA), IsEndB = EndDirectives.count(DirB);563if (IsEndA != IsEndB)564return IsEndA < IsEndB;565if (LeavesA[1] == 0 && LeavesB[1] == 0)566return DirA < DirB;567return std::lexicographical_compare(&LeavesA[2], &LeavesA[2] + LeavesA[1],568&LeavesB[2], &LeavesB[2] + LeavesB[1]);569});570571// Emit the table572573// The directives are emitted into a scoped enum, for which the underlying574// type is `int` (by default). The code above uses `int` to store directive575// ids, so make sure that we catch it when something changes in the576// underlying type.577std::string DirectiveType = GetDirectiveType(DirLang);578OS << "\nstatic_assert(sizeof(" << DirectiveType << ") == sizeof(int));\n";579580OS << "[[maybe_unused]] static const " << DirectiveType << ' ' << TableName581<< "[][" << MaxLeafCount + 2 << "] = {\n";582for (size_t I = 0, E = Directives.size(); I != E; ++I) {583auto &Leaves = LeafTable[Ordering[I]];584OS << " {" << GetDirectiveName(DirLang, Directives[Leaves[0]]);585OS << ", static_cast<" << DirectiveType << ">(" << Leaves[1] << "),";586for (size_t I = 2, E = Leaves.size(); I != E; ++I) {587int Idx = Leaves[I];588if (Idx >= 0)589OS << ' ' << GetDirectiveName(DirLang, Directives[Leaves[I]]) << ',';590else591OS << " static_cast<" << DirectiveType << ">(-1),";592}593OS << "},\n";594}595OS << "};\n\n";596597// Emit a marker where the first "end directive" is.598auto FirstE = llvm::find_if(Ordering, [&](int RowIdx) {599return EndDirectives.count(LeafTable[RowIdx][0]);600});601OS << "[[maybe_unused]] static auto " << TableName602<< "EndDirective = " << TableName << " + "603<< std::distance(Ordering.begin(), FirstE) << ";\n\n";604605// Emit the auxiliary index table: it's the inverse of the `Ordering`606// table above.607OS << "[[maybe_unused]] static const int " << TableName << "Ordering[] = {\n";608OS << " ";609std::vector<int> Reverse(Ordering.size());610for (int I = 0, E = Ordering.size(); I != E; ++I)611Reverse[Ordering[I]] = I;612for (int Idx : Reverse)613OS << ' ' << Idx << ',';614OS << "\n};\n";615}616617static void GenerateGetDirectiveAssociation(const DirectiveLanguage &DirLang,618raw_ostream &OS) {619enum struct Association {620None = 0, // None should be the smallest value.621Block, // The values of the rest don't matter.622Declaration,623Delimited,624Loop,625Separating,626FromLeaves,627Invalid,628};629630std::vector<Record *> associations = DirLang.getAssociations();631632auto getAssocValue = [](StringRef name) -> Association {633return StringSwitch<Association>(name)634.Case("AS_Block", Association::Block)635.Case("AS_Declaration", Association::Declaration)636.Case("AS_Delimited", Association::Delimited)637.Case("AS_Loop", Association::Loop)638.Case("AS_None", Association::None)639.Case("AS_Separating", Association::Separating)640.Case("AS_FromLeaves", Association::FromLeaves)641.Default(Association::Invalid);642};643644auto getAssocName = [&](Association A) -> StringRef {645if (A != Association::Invalid && A != Association::FromLeaves) {646auto F = llvm::find_if(associations, [&](const Record *R) {647return getAssocValue(R->getName()) == A;648});649if (F != associations.end())650return (*F)->getValueAsString("name"); // enum name651}652llvm_unreachable("Unexpected association value");653};654655auto errorPrefixFor = [&](Directive D) -> std::string {656return (Twine("Directive '") + D.getName() + "' in namespace '" +657DirLang.getCppNamespace() + "' ")658.str();659};660661auto reduce = [&](Association A, Association B) -> Association {662if (A > B)663std::swap(A, B);664665// Calculate the result using the following rules:666// x + x = x667// AS_None + x = x668// AS_Block + AS_Loop = AS_Loop669if (A == Association::None || A == B)670return B;671if (A == Association::Block && B == Association::Loop)672return B;673if (A == Association::Loop && B == Association::Block)674return A;675return Association::Invalid;676};677678llvm::DenseMap<const Record *, Association> AsMap;679680auto compAssocImpl = [&](const Record *R, auto &&Self) -> Association {681if (auto F = AsMap.find(R); F != AsMap.end())682return F->second;683684Directive D{R};685Association AS = getAssocValue(D.getAssociation()->getName());686if (AS == Association::Invalid) {687PrintFatalError(errorPrefixFor(D) +688"has an unrecognized value for association: '" +689D.getAssociation()->getName() + "'");690}691if (AS != Association::FromLeaves) {692AsMap.insert(std::make_pair(R, AS));693return AS;694}695// Compute the association from leaf constructs.696std::vector<Record *> leaves = D.getLeafConstructs();697if (leaves.empty()) {698llvm::errs() << D.getName() << '\n';699PrintFatalError(errorPrefixFor(D) +700"requests association to be computed from leaves, "701"but it has no leaves");702}703704Association Result = Self(leaves[0], Self);705for (int I = 1, E = leaves.size(); I < E; ++I) {706Association A = Self(leaves[I], Self);707Association R = reduce(Result, A);708if (R == Association::Invalid) {709PrintFatalError(errorPrefixFor(D) +710"has leaves with incompatible association values: " +711getAssocName(A) + " and " + getAssocName(R));712}713Result = R;714}715716assert(Result != Association::Invalid);717assert(Result != Association::FromLeaves);718AsMap.insert(std::make_pair(R, Result));719return Result;720};721722for (Record *R : DirLang.getDirectives())723compAssocImpl(R, compAssocImpl); // Updates AsMap.724725OS << '\n';726727auto getQualifiedName = [&](StringRef Formatted) -> std::string {728return (llvm::Twine("llvm::") + DirLang.getCppNamespace() +729"::Directive::" + DirLang.getDirectivePrefix() + Formatted)730.str();731};732733std::string DirectiveTypeName =734std::string("llvm::") + DirLang.getCppNamespace().str() + "::Directive";735std::string AssociationTypeName =736std::string("llvm::") + DirLang.getCppNamespace().str() + "::Association";737738OS << AssociationTypeName << " llvm::" << DirLang.getCppNamespace()739<< "::getDirectiveAssociation(" << DirectiveTypeName << " Dir) {\n";740OS << " switch (Dir) {\n";741for (Record *R : DirLang.getDirectives()) {742if (auto F = AsMap.find(R); F != AsMap.end()) {743Directive Dir{R};744OS << " case " << getQualifiedName(Dir.getFormattedName()) << ":\n";745OS << " return " << AssociationTypeName746<< "::" << getAssocName(F->second) << ";\n";747}748}749OS << " } // switch (Dir)\n";750OS << " llvm_unreachable(\"Unexpected directive\");\n";751OS << "}\n";752}753754static void GenerateGetDirectiveCategory(const DirectiveLanguage &DirLang,755raw_ostream &OS) {756std::string LangNamespace = "llvm::" + DirLang.getCppNamespace().str();757std::string CategoryTypeName = LangNamespace + "::Category";758std::string CategoryNamespace = CategoryTypeName + "::";759760OS << '\n';761OS << CategoryTypeName << ' ' << LangNamespace << "::getDirectiveCategory("762<< GetDirectiveType(DirLang) << " Dir) {\n";763OS << " switch (Dir) {\n";764765for (Record *R : DirLang.getDirectives()) {766Directive D{R};767OS << " case " << GetDirectiveName(DirLang, R) << ":\n";768OS << " return " << CategoryNamespace769<< D.getCategory()->getValueAsString("name") << ";\n";770}771OS << " } // switch (Dir)\n";772OS << " llvm_unreachable(\"Unexpected directive\");\n";773OS << "}\n";774}775776// Generate a simple enum set with the give clauses.777static void GenerateClauseSet(const std::vector<Record *> &Clauses,778raw_ostream &OS, StringRef ClauseSetPrefix,779Directive &Dir,780const DirectiveLanguage &DirLang) {781782OS << "\n";783OS << " static " << DirLang.getClauseEnumSetClass() << " " << ClauseSetPrefix784<< DirLang.getDirectivePrefix() << Dir.getFormattedName() << " {\n";785786for (const auto &C : Clauses) {787VersionedClause VerClause{C};788OS << " llvm::" << DirLang.getCppNamespace()789<< "::Clause::" << DirLang.getClausePrefix()790<< VerClause.getClause().getFormattedName() << ",\n";791}792OS << " };\n";793}794795// Generate an enum set for the 4 kinds of clauses linked to a directive.796static void GenerateDirectiveClauseSets(const DirectiveLanguage &DirLang,797raw_ostream &OS) {798799IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_SETS", OS);800801OS << "\n";802OS << "namespace llvm {\n";803804// Open namespaces defined in the directive language.805llvm::SmallVector<StringRef, 2> Namespaces;806llvm::SplitString(DirLang.getCppNamespace(), Namespaces, "::");807for (auto Ns : Namespaces)808OS << "namespace " << Ns << " {\n";809810for (const auto &D : DirLang.getDirectives()) {811Directive Dir{D};812813OS << "\n";814OS << " // Sets for " << Dir.getName() << "\n";815816GenerateClauseSet(Dir.getAllowedClauses(), OS, "allowedClauses_", Dir,817DirLang);818GenerateClauseSet(Dir.getAllowedOnceClauses(), OS, "allowedOnceClauses_",819Dir, DirLang);820GenerateClauseSet(Dir.getAllowedExclusiveClauses(), OS,821"allowedExclusiveClauses_", Dir, DirLang);822GenerateClauseSet(Dir.getRequiredClauses(), OS, "requiredClauses_", Dir,823DirLang);824}825826// Closing namespaces827for (auto Ns : llvm::reverse(Namespaces))828OS << "} // namespace " << Ns << "\n";829830OS << "} // namespace llvm\n";831}832833// Generate a map of directive (key) with DirectiveClauses struct as values.834// The struct holds the 4 sets of enumeration for the 4 kinds of clauses835// allowances (allowed, allowed once, allowed exclusive and required).836static void GenerateDirectiveClauseMap(const DirectiveLanguage &DirLang,837raw_ostream &OS) {838839IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_MAP", OS);840841OS << "\n";842OS << "{\n";843844for (const auto &D : DirLang.getDirectives()) {845Directive Dir{D};846OS << " {llvm::" << DirLang.getCppNamespace()847<< "::Directive::" << DirLang.getDirectivePrefix()848<< Dir.getFormattedName() << ",\n";849OS << " {\n";850OS << " llvm::" << DirLang.getCppNamespace() << "::allowedClauses_"851<< DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n";852OS << " llvm::" << DirLang.getCppNamespace() << "::allowedOnceClauses_"853<< DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n";854OS << " llvm::" << DirLang.getCppNamespace()855<< "::allowedExclusiveClauses_" << DirLang.getDirectivePrefix()856<< Dir.getFormattedName() << ",\n";857OS << " llvm::" << DirLang.getCppNamespace() << "::requiredClauses_"858<< DirLang.getDirectivePrefix() << Dir.getFormattedName() << ",\n";859OS << " }\n";860OS << " },\n";861}862863OS << "}\n";864}865866// Generate classes entry for Flang clauses in the Flang parse-tree867// If the clause as a non-generic class, no entry is generated.868// If the clause does not hold a value, an EMPTY_CLASS is used.869// If the clause class is generic then a WRAPPER_CLASS is used. When the value870// is optional, the value class is wrapped into a std::optional.871static void GenerateFlangClauseParserClass(const DirectiveLanguage &DirLang,872raw_ostream &OS) {873874IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_CLASSES", OS);875876OS << "\n";877878for (const auto &C : DirLang.getClauses()) {879Clause Clause{C};880if (!Clause.getFlangClass().empty()) {881OS << "WRAPPER_CLASS(" << Clause.getFormattedParserClassName() << ", ";882if (Clause.isValueOptional() && Clause.isValueList()) {883OS << "std::optional<std::list<" << Clause.getFlangClass() << ">>";884} else if (Clause.isValueOptional()) {885OS << "std::optional<" << Clause.getFlangClass() << ">";886} else if (Clause.isValueList()) {887OS << "std::list<" << Clause.getFlangClass() << ">";888} else {889OS << Clause.getFlangClass();890}891} else {892OS << "EMPTY_CLASS(" << Clause.getFormattedParserClassName();893}894OS << ");\n";895}896}897898// Generate a list of the different clause classes for Flang.899static void GenerateFlangClauseParserClassList(const DirectiveLanguage &DirLang,900raw_ostream &OS) {901902IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_CLASSES_LIST", OS);903904OS << "\n";905llvm::interleaveComma(DirLang.getClauses(), OS, [&](Record *C) {906Clause Clause{C};907OS << Clause.getFormattedParserClassName() << "\n";908});909}910911// Generate dump node list for the clauses holding a generic class name.912static void GenerateFlangClauseDump(const DirectiveLanguage &DirLang,913raw_ostream &OS) {914915IfDefScope Scope("GEN_FLANG_DUMP_PARSE_TREE_CLAUSES", OS);916917OS << "\n";918for (const auto &C : DirLang.getClauses()) {919Clause Clause{C};920OS << "NODE(" << DirLang.getFlangClauseBaseClass() << ", "921<< Clause.getFormattedParserClassName() << ")\n";922}923}924925// Generate Unparse functions for clauses classes in the Flang parse-tree926// If the clause is a non-generic class, no entry is generated.927static void GenerateFlangClauseUnparse(const DirectiveLanguage &DirLang,928raw_ostream &OS) {929930IfDefScope Scope("GEN_FLANG_CLAUSE_UNPARSE", OS);931932OS << "\n";933934for (const auto &C : DirLang.getClauses()) {935Clause Clause{C};936if (!Clause.getFlangClass().empty()) {937if (Clause.isValueOptional() && Clause.getDefaultValue().empty()) {938OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass()939<< "::" << Clause.getFormattedParserClassName() << " &x) {\n";940OS << " Word(\"" << Clause.getName().upper() << "\");\n";941942OS << " Walk(\"(\", x.v, \")\");\n";943OS << "}\n";944} else if (Clause.isValueOptional()) {945OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass()946<< "::" << Clause.getFormattedParserClassName() << " &x) {\n";947OS << " Word(\"" << Clause.getName().upper() << "\");\n";948OS << " Put(\"(\");\n";949OS << " if (x.v.has_value())\n";950if (Clause.isValueList())951OS << " Walk(x.v, \",\");\n";952else953OS << " Walk(x.v);\n";954OS << " else\n";955OS << " Put(\"" << Clause.getDefaultValue() << "\");\n";956OS << " Put(\")\");\n";957OS << "}\n";958} else {959OS << "void Unparse(const " << DirLang.getFlangClauseBaseClass()960<< "::" << Clause.getFormattedParserClassName() << " &x) {\n";961OS << " Word(\"" << Clause.getName().upper() << "\");\n";962OS << " Put(\"(\");\n";963if (Clause.isValueList())964OS << " Walk(x.v, \",\");\n";965else966OS << " Walk(x.v);\n";967OS << " Put(\")\");\n";968OS << "}\n";969}970} else {971OS << "void Before(const " << DirLang.getFlangClauseBaseClass()972<< "::" << Clause.getFormattedParserClassName() << " &) { Word(\""973<< Clause.getName().upper() << "\"); }\n";974}975}976}977978// Generate check in the Enter functions for clauses classes.979static void GenerateFlangClauseCheckPrototypes(const DirectiveLanguage &DirLang,980raw_ostream &OS) {981982IfDefScope Scope("GEN_FLANG_CLAUSE_CHECK_ENTER", OS);983984OS << "\n";985for (const auto &C : DirLang.getClauses()) {986Clause Clause{C};987OS << "void Enter(const parser::" << DirLang.getFlangClauseBaseClass()988<< "::" << Clause.getFormattedParserClassName() << " &);\n";989}990}991992// Generate the mapping for clauses between the parser class and the993// corresponding clause Kind994static void GenerateFlangClauseParserKindMap(const DirectiveLanguage &DirLang,995raw_ostream &OS) {996997IfDefScope Scope("GEN_FLANG_CLAUSE_PARSER_KIND_MAP", OS);998999OS << "\n";1000for (const auto &C : DirLang.getClauses()) {1001Clause Clause{C};1002OS << "if constexpr (std::is_same_v<A, parser::"1003<< DirLang.getFlangClauseBaseClass()1004<< "::" << Clause.getFormattedParserClassName();1005OS << ">)\n";1006OS << " return llvm::" << DirLang.getCppNamespace()1007<< "::Clause::" << DirLang.getClausePrefix() << Clause.getFormattedName()1008<< ";\n";1009}10101011OS << "llvm_unreachable(\"Invalid " << DirLang.getName()1012<< " Parser clause\");\n";1013}10141015static bool compareClauseName(Record *R1, Record *R2) {1016Clause C1{R1};1017Clause C2{R2};1018return (C1.getName() > C2.getName());1019}10201021// Generate the parser for the clauses.1022static void GenerateFlangClausesParser(const DirectiveLanguage &DirLang,1023raw_ostream &OS) {1024std::vector<Record *> Clauses = DirLang.getClauses();1025// Sort clauses in reverse alphabetical order so with clauses with same1026// beginning, the longer option is tried before.1027llvm::sort(Clauses, compareClauseName);1028IfDefScope Scope("GEN_FLANG_CLAUSES_PARSER", OS);1029OS << "\n";1030unsigned index = 0;1031unsigned lastClauseIndex = DirLang.getClauses().size() - 1;1032OS << "TYPE_PARSER(\n";1033for (const auto &C : Clauses) {1034Clause Clause{C};1035if (Clause.getAliases().empty()) {1036OS << " \"" << Clause.getName() << "\"";1037} else {1038OS << " ("1039<< "\"" << Clause.getName() << "\"_tok";1040for (StringRef alias : Clause.getAliases()) {1041OS << " || \"" << alias << "\"_tok";1042}1043OS << ")";1044}10451046OS << " >> construct<" << DirLang.getFlangClauseBaseClass()1047<< ">(construct<" << DirLang.getFlangClauseBaseClass()1048<< "::" << Clause.getFormattedParserClassName() << ">(";1049if (Clause.getFlangClass().empty()) {1050OS << "))";1051if (index != lastClauseIndex)1052OS << " ||";1053OS << "\n";1054++index;1055continue;1056}10571058if (Clause.isValueOptional())1059OS << "maybe(";1060OS << "parenthesized(";1061if (Clause.isValueList())1062OS << "nonemptyList(";10631064if (!Clause.getPrefix().empty())1065OS << "\"" << Clause.getPrefix() << ":\" >> ";10661067// The common Flang parser are used directly. Their name is identical to1068// the Flang class with first letter as lowercase. If the Flang class is1069// not a common class, we assume there is a specific Parser<>{} with the1070// Flang class name provided.1071llvm::SmallString<128> Scratch;1072StringRef Parser =1073llvm::StringSwitch<StringRef>(Clause.getFlangClass())1074.Case("Name", "name")1075.Case("ScalarIntConstantExpr", "scalarIntConstantExpr")1076.Case("ScalarIntExpr", "scalarIntExpr")1077.Case("ScalarExpr", "scalarExpr")1078.Case("ScalarLogicalExpr", "scalarLogicalExpr")1079.Default(("Parser<" + Clause.getFlangClass() + ">{}")1080.toStringRef(Scratch));1081OS << Parser;1082if (!Clause.getPrefix().empty() && Clause.isPrefixOptional())1083OS << " || " << Parser;1084if (Clause.isValueList()) // close nonemptyList(.1085OS << ")";1086OS << ")"; // close parenthesized(.10871088if (Clause.isValueOptional()) // close maybe(.1089OS << ")";1090OS << "))";1091if (index != lastClauseIndex)1092OS << " ||";1093OS << "\n";1094++index;1095}1096OS << ")\n";1097}10981099// Generate the implementation section for the enumeration in the directive1100// language1101static void EmitDirectivesFlangImpl(const DirectiveLanguage &DirLang,1102raw_ostream &OS) {11031104GenerateDirectiveClauseSets(DirLang, OS);11051106GenerateDirectiveClauseMap(DirLang, OS);11071108GenerateFlangClauseParserClass(DirLang, OS);11091110GenerateFlangClauseParserClassList(DirLang, OS);11111112GenerateFlangClauseDump(DirLang, OS);11131114GenerateFlangClauseUnparse(DirLang, OS);11151116GenerateFlangClauseCheckPrototypes(DirLang, OS);11171118GenerateFlangClauseParserKindMap(DirLang, OS);11191120GenerateFlangClausesParser(DirLang, OS);1121}11221123static void GenerateClauseClassMacro(const DirectiveLanguage &DirLang,1124raw_ostream &OS) {1125// Generate macros style information for legacy code in clang1126IfDefScope Scope("GEN_CLANG_CLAUSE_CLASS", OS);11271128OS << "\n";11291130OS << "#ifndef CLAUSE\n";1131OS << "#define CLAUSE(Enum, Str, Implicit)\n";1132OS << "#endif\n";1133OS << "#ifndef CLAUSE_CLASS\n";1134OS << "#define CLAUSE_CLASS(Enum, Str, Class)\n";1135OS << "#endif\n";1136OS << "#ifndef CLAUSE_NO_CLASS\n";1137OS << "#define CLAUSE_NO_CLASS(Enum, Str)\n";1138OS << "#endif\n";1139OS << "\n";1140OS << "#define __CLAUSE(Name, Class) \\\n";1141OS << " CLAUSE(" << DirLang.getClausePrefix()1142<< "##Name, #Name, /* Implicit */ false) \\\n";1143OS << " CLAUSE_CLASS(" << DirLang.getClausePrefix()1144<< "##Name, #Name, Class)\n";1145OS << "#define __CLAUSE_NO_CLASS(Name) \\\n";1146OS << " CLAUSE(" << DirLang.getClausePrefix()1147<< "##Name, #Name, /* Implicit */ false) \\\n";1148OS << " CLAUSE_NO_CLASS(" << DirLang.getClausePrefix() << "##Name, #Name)\n";1149OS << "#define __IMPLICIT_CLAUSE_CLASS(Name, Str, Class) \\\n";1150OS << " CLAUSE(" << DirLang.getClausePrefix()1151<< "##Name, Str, /* Implicit */ true) \\\n";1152OS << " CLAUSE_CLASS(" << DirLang.getClausePrefix()1153<< "##Name, Str, Class)\n";1154OS << "#define __IMPLICIT_CLAUSE_NO_CLASS(Name, Str) \\\n";1155OS << " CLAUSE(" << DirLang.getClausePrefix()1156<< "##Name, Str, /* Implicit */ true) \\\n";1157OS << " CLAUSE_NO_CLASS(" << DirLang.getClausePrefix() << "##Name, Str)\n";1158OS << "\n";11591160for (const auto &R : DirLang.getClauses()) {1161Clause C{R};1162if (C.getClangClass().empty()) { // NO_CLASS1163if (C.isImplicit()) {1164OS << "__IMPLICIT_CLAUSE_NO_CLASS(" << C.getFormattedName() << ", \""1165<< C.getFormattedName() << "\")\n";1166} else {1167OS << "__CLAUSE_NO_CLASS(" << C.getFormattedName() << ")\n";1168}1169} else { // CLASS1170if (C.isImplicit()) {1171OS << "__IMPLICIT_CLAUSE_CLASS(" << C.getFormattedName() << ", \""1172<< C.getFormattedName() << "\", " << C.getClangClass() << ")\n";1173} else {1174OS << "__CLAUSE(" << C.getFormattedName() << ", " << C.getClangClass()1175<< ")\n";1176}1177}1178}11791180OS << "\n";1181OS << "#undef __IMPLICIT_CLAUSE_NO_CLASS\n";1182OS << "#undef __IMPLICIT_CLAUSE_CLASS\n";1183OS << "#undef __CLAUSE_NO_CLASS\n";1184OS << "#undef __CLAUSE\n";1185OS << "#undef CLAUSE_NO_CLASS\n";1186OS << "#undef CLAUSE_CLASS\n";1187OS << "#undef CLAUSE\n";1188}11891190// Generate the implemenation for the enumeration in the directive1191// language. This code can be included in library.1192void EmitDirectivesBasicImpl(const DirectiveLanguage &DirLang,1193raw_ostream &OS) {1194IfDefScope Scope("GEN_DIRECTIVES_IMPL", OS);11951196OS << "\n#include \"llvm/Support/ErrorHandling.h\"\n";11971198// getDirectiveKind(StringRef Str)1199GenerateGetKind(DirLang.getDirectives(), OS, "Directive", DirLang,1200DirLang.getDirectivePrefix(), /*ImplicitAsUnknown=*/false);12011202// getDirectiveName(Directive Kind)1203GenerateGetName(DirLang.getDirectives(), OS, "Directive", DirLang,1204DirLang.getDirectivePrefix());12051206// getClauseKind(StringRef Str)1207GenerateGetKind(DirLang.getClauses(), OS, "Clause", DirLang,1208DirLang.getClausePrefix(),1209/*ImplicitAsUnknown=*/true);12101211// getClauseName(Clause Kind)1212GenerateGetName(DirLang.getClauses(), OS, "Clause", DirLang,1213DirLang.getClausePrefix());12141215// get<ClauseVal>Kind(StringRef Str)1216GenerateGetKindClauseVal(DirLang, OS);12171218// isAllowedClauseForDirective(Directive D, Clause C, unsigned Version)1219GenerateIsAllowedClause(DirLang, OS);12201221// getDirectiveAssociation(Directive D)1222GenerateGetDirectiveAssociation(DirLang, OS);12231224// getDirectiveCategory(Directive D)1225GenerateGetDirectiveCategory(DirLang, OS);12261227// Leaf table for getLeafConstructs, etc.1228EmitLeafTable(DirLang, OS, "LeafConstructTable");1229}12301231// Generate the implemenation section for the enumeration in the directive1232// language.1233static void EmitDirectivesImpl(RecordKeeper &Records, raw_ostream &OS) {1234const auto DirLang = DirectiveLanguage{Records};1235if (DirLang.HasValidityErrors())1236return;12371238EmitDirectivesFlangImpl(DirLang, OS);12391240GenerateClauseClassMacro(DirLang, OS);12411242EmitDirectivesBasicImpl(DirLang, OS);1243}12441245static TableGen::Emitter::Opt1246X("gen-directive-decl", EmitDirectivesDecl,1247"Generate directive related declaration code (header file)");12481249static TableGen::Emitter::Opt1250Y("gen-directive-impl", EmitDirectivesImpl,1251"Generate directive related implementation code");125212531254