From 40137ff0d81be80e4900c17c57b1f66c53ddf2f9 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Mon, 22 Apr 2024 14:41:11 -0500 Subject: [PATCH] [Frontend][OpenMP] Refactor getLeafConstructs, add getCompoundConstruct (#87247) Emit a special leaf construct table in DirectiveEmitter.cpp, which will allow both decomposition of a construct into leafs, and composition of constituent constructs into a single compound construct (if possible). The function `getLeafConstructs` is no longer auto-generated, but implemented in OMP.cpp. The table contains a row for each directive, and each row has the following format `dir_id, num_leafs, leaf1, leaf2, ..., leafN, -1, ...` The rows are sorted lexicographically with respect to the leaf constructs. This allows a binary search for the row corresponding to the given list of leafs. There is an auxiliary table that for each directive contains the index of the row corresponding to that directive. Looking up leaf constructs for a directive `dir_id` is of constant time, and and consists of two lookups: `LeafTable[Auxiliary[dir_id]]`. Finding a compound directive given the set of leafs is of time O(logn), and is roughly represented by `row = binary_search(LeafTable); return row[0]`. The functions `getLeafConstructs` and `getCompoundConstruct` use these lookup methods internally. --- llvm/include/llvm/Frontend/OpenMP/OMP.h | 7 + llvm/lib/Frontend/OpenMP/OMP.cpp | 70 +++++- llvm/test/TableGen/directive1.td | 21 +- llvm/test/TableGen/directive2.td | 21 +- llvm/unittests/Frontend/CMakeLists.txt | 1 + llvm/unittests/Frontend/OpenMPComposeTest.cpp | 41 ++++ llvm/utils/TableGen/DirectiveEmitter.cpp | 226 ++++++++++++------ 7 files changed, 301 insertions(+), 86 deletions(-) create mode 100644 llvm/unittests/Frontend/OpenMPComposeTest.cpp diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.h b/llvm/include/llvm/Frontend/OpenMP/OMP.h index a85cd9d344c6d..4ed47f15dfe59 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMP.h +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.h @@ -15,4 +15,11 @@ #include "llvm/Frontend/OpenMP/OMP.h.inc" +#include "llvm/ADT/ArrayRef.h" + +namespace llvm::omp { +ArrayRef getLeafConstructs(Directive D); +Directive getCompoundConstruct(ArrayRef Parts); +} // namespace llvm::omp + #endif // LLVM_FRONTEND_OPENMP_OMP_H diff --git a/llvm/lib/Frontend/OpenMP/OMP.cpp b/llvm/lib/Frontend/OpenMP/OMP.cpp index 4f2f95392648b..e958bced3a422 100644 --- a/llvm/lib/Frontend/OpenMP/OMP.cpp +++ b/llvm/lib/Frontend/OpenMP/OMP.cpp @@ -8,12 +8,80 @@ #include "llvm/Frontend/OpenMP/OMP.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" +#include +#include +#include + using namespace llvm; -using namespace omp; +using namespace llvm::omp; #define GEN_DIRECTIVES_IMPL #include "llvm/Frontend/OpenMP/OMP.inc" + +namespace llvm::omp { +ArrayRef getLeafConstructs(Directive D) { + auto Idx = static_cast(D); + if (Idx >= Directive_enumSize) + std::nullopt; + const auto *Row = LeafConstructTable[LeafConstructTableOrdering[Idx]]; + return ArrayRef(&Row[2], static_cast(Row[1])); +} + +Directive getCompoundConstruct(ArrayRef Parts) { + if (Parts.empty()) + return OMPD_unknown; + + // Parts don't have to be leafs, so expand them into leafs first. + // Store the expanded leafs in the same format as rows in the leaf + // table (generated by tablegen). + SmallVector RawLeafs(2); + for (Directive P : Parts) { + ArrayRef Ls = getLeafConstructs(P); + if (!Ls.empty()) + RawLeafs.append(Ls.begin(), Ls.end()); + else + RawLeafs.push_back(P); + } + + // RawLeafs will be used as key in the binary search. The search doesn't + // guarantee that the exact same entry will be found (since RawLeafs may + // not correspond to any compound directive). Because of that, we will + // need to compare the search result with the given set of leafs. + // Also, if there is only one leaf in the list, it corresponds to itself, + // no search is necessary. + auto GivenLeafs{ArrayRef(RawLeafs).drop_front(2)}; + if (GivenLeafs.size() == 1) + return GivenLeafs.front(); + RawLeafs[1] = static_cast(GivenLeafs.size()); + + auto Iter = std::lower_bound( + LeafConstructTable, LeafConstructTableEndDirective, + static_cast>(RawLeafs.data()), + [](const llvm::omp::Directive *RowA, const llvm::omp::Directive *RowB) { + const auto *BeginA = &RowA[2]; + const auto *EndA = BeginA + static_cast(RowA[1]); + const auto *BeginB = &RowB[2]; + const auto *EndB = BeginB + static_cast(RowB[1]); + if (BeginA == EndA && BeginB == EndB) + return static_cast(RowA[0]) < static_cast(RowB[0]); + return std::lexicographical_compare(BeginA, EndA, BeginB, EndB); + }); + + if (Iter == std::end(LeafConstructTable)) + return OMPD_unknown; + + // Verify that we got a match. + Directive Found = (*Iter)[0]; + ArrayRef FoundLeafs = getLeafConstructs(Found); + if (FoundLeafs == GivenLeafs) + return Found; + return OMPD_unknown; +} +} // namespace llvm::omp diff --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td index 3184f625ead92..526dcb3c3bf0a 100644 --- a/llvm/test/TableGen/directive1.td +++ b/llvm/test/TableGen/directive1.td @@ -52,6 +52,7 @@ def TDL_DirA : Directive<"dira"> { // CHECK-EMPTY: // CHECK-NEXT: #include "llvm/ADT/ArrayRef.h" // CHECK-NEXT: #include "llvm/ADT/BitmaskEnum.h" +// CHECK-NEXT: #include // CHECK-EMPTY: // CHECK-NEXT: namespace llvm { // CHECK-NEXT: class StringRef; @@ -112,7 +113,7 @@ def TDL_DirA : Directive<"dira"> { // CHECK-NEXT: /// Return true if \p C is a valid clause for \p D in version \p Version. // CHECK-NEXT: bool isAllowedClauseForDirective(Directive D, Clause C, unsigned Version); // CHECK-EMPTY: -// CHECK-NEXT: llvm::ArrayRef getLeafConstructs(Directive D); +// CHECK-NEXT: constexpr std::size_t getMaxLeafCount() { return 0; } // CHECK-NEXT: Association getDirectiveAssociation(Directive D); // CHECK-NEXT: AKind getAKind(StringRef); // CHECK-NEXT: llvm::StringRef getTdlAKindName(AKind); @@ -359,13 +360,6 @@ def TDL_DirA : Directive<"dira"> { // IMPL-NEXT: llvm_unreachable("Invalid Tdl Directive kind"); // IMPL-NEXT: } // IMPL-EMPTY: -// IMPL-NEXT: llvm::ArrayRef llvm::tdl::getLeafConstructs(llvm::tdl::Directive Dir) { -// IMPL-NEXT: switch (Dir) { -// IMPL-NEXT: default: -// IMPL-NEXT: return ArrayRef{}; -// IMPL-NEXT: } // switch (Dir) -// IMPL-NEXT: } -// IMPL-EMPTY: // IMPL-NEXT: llvm::tdl::Association llvm::tdl::getDirectiveAssociation(llvm::tdl::Directive Dir) { // IMPL-NEXT: switch (Dir) { // IMPL-NEXT: case llvm::tdl::Directive::TDLD_dira: @@ -374,4 +368,15 @@ def TDL_DirA : Directive<"dira"> { // IMPL-NEXT: llvm_unreachable("Unexpected directive"); // IMPL-NEXT: } // IMPL-EMPTY: +// IMPL-NEXT: static_assert(sizeof(llvm::tdl::Directive) == sizeof(int)); +// IMPL-NEXT: {{.*}} static const llvm::tdl::Directive LeafConstructTable[][2] = { +// IMPL-NEXT: llvm::tdl::TDLD_dira, static_cast(0), +// IMPL-NEXT: }; +// IMPL-EMPTY: +// IMPL-NEXT: {{.*}} static auto LeafConstructTableEndDirective = LeafConstructTable + 1; +// IMPL-EMPTY: +// IMPL-NEXT: {{.*}} static const int LeafConstructTableOrdering[] = { +// IMPL-NEXT: 0, +// IMPL-NEXT: }; +// IMPL-EMPTY: // IMPL-NEXT: #endif // GEN_DIRECTIVES_IMPL diff --git a/llvm/test/TableGen/directive2.td b/llvm/test/TableGen/directive2.td index d6fa4835c8dfd..9df8a06d3e517 100644 --- a/llvm/test/TableGen/directive2.td +++ b/llvm/test/TableGen/directive2.td @@ -45,6 +45,7 @@ def TDL_DirA : Directive<"dira"> { // CHECK-NEXT: #define LLVM_Tdl_INC // CHECK-EMPTY: // CHECK-NEXT: #include "llvm/ADT/ArrayRef.h" +// CHECK-NEXT: #include // CHECK-EMPTY: // CHECK-NEXT: namespace llvm { // CHECK-NEXT: class StringRef; @@ -88,7 +89,7 @@ def TDL_DirA : Directive<"dira"> { // CHECK-NEXT: /// Return true if \p C is a valid clause for \p D in version \p Version. // CHECK-NEXT: bool isAllowedClauseForDirective(Directive D, Clause C, unsigned Version); // CHECK-EMPTY: -// CHECK-NEXT: llvm::ArrayRef getLeafConstructs(Directive D); +// CHECK-NEXT: constexpr std::size_t getMaxLeafCount() { return 0; } // CHECK-NEXT: Association getDirectiveAssociation(Directive D); // CHECK-NEXT: } // namespace tdl // CHECK-NEXT: } // namespace llvm @@ -290,13 +291,6 @@ def TDL_DirA : Directive<"dira"> { // IMPL-NEXT: llvm_unreachable("Invalid Tdl Directive kind"); // IMPL-NEXT: } // IMPL-EMPTY: -// IMPL-NEXT: llvm::ArrayRef llvm::tdl::getLeafConstructs(llvm::tdl::Directive Dir) { -// IMPL-NEXT: switch (Dir) { -// IMPL-NEXT: default: -// IMPL-NEXT: return ArrayRef{}; -// IMPL-NEXT: } // switch (Dir) -// IMPL-NEXT: } -// IMPL-EMPTY: // IMPL-NEXT: llvm::tdl::Association llvm::tdl::getDirectiveAssociation(llvm::tdl::Directive Dir) { // IMPL-NEXT: switch (Dir) { // IMPL-NEXT: case llvm::tdl::Directive::TDLD_dira: @@ -305,4 +299,15 @@ def TDL_DirA : Directive<"dira"> { // IMPL-NEXT: llvm_unreachable("Unexpected directive"); // IMPL-NEXT: } // IMPL-EMPTY: +// IMPL-NEXT: static_assert(sizeof(llvm::tdl::Directive) == sizeof(int)); +// IMPL-NEXT: {{.*}} static const llvm::tdl::Directive LeafConstructTable[][2] = { +// IMPL-NEXT: llvm::tdl::TDLD_dira, static_cast(0), +// IMPL-NEXT: }; +// IMPL-EMPTY: +// IMPL-NEXT: {{.*}} static auto LeafConstructTableEndDirective = LeafConstructTable + 1; +// IMPL-EMPTY: +// IMPL-NEXT: {{.*}} static const int LeafConstructTableOrdering[] = { +// IMPL-NEXT: 0, +// IMPL-NEXT: }; +// IMPL-EMPTY: // IMPL-NEXT: #endif // GEN_DIRECTIVES_IMPL diff --git a/llvm/unittests/Frontend/CMakeLists.txt b/llvm/unittests/Frontend/CMakeLists.txt index c6f60142d6276..ddb6a16cbb984 100644 --- a/llvm/unittests/Frontend/CMakeLists.txt +++ b/llvm/unittests/Frontend/CMakeLists.txt @@ -14,6 +14,7 @@ add_llvm_unittest(LLVMFrontendTests OpenMPContextTest.cpp OpenMPIRBuilderTest.cpp OpenMPParsingTest.cpp + OpenMPComposeTest.cpp DEPENDS acc_gen diff --git a/llvm/unittests/Frontend/OpenMPComposeTest.cpp b/llvm/unittests/Frontend/OpenMPComposeTest.cpp new file mode 100644 index 0000000000000..c5fbe6ec6adfe --- /dev/null +++ b/llvm/unittests/Frontend/OpenMPComposeTest.cpp @@ -0,0 +1,41 @@ +//===- llvm/unittests/Frontend/OpenMPComposeTest.cpp ----------------------===// +// +// 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/ArrayRef.h" +#include "llvm/Frontend/OpenMP/OMP.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::omp; + +TEST(Composition, GetLeafConstructs) { + ArrayRef L1 = getLeafConstructs(OMPD_loop); + ASSERT_EQ(L1, (ArrayRef{})); + ArrayRef L2 = getLeafConstructs(OMPD_parallel_for); + ASSERT_EQ(L2, (ArrayRef{OMPD_parallel, OMPD_for})); + ArrayRef L3 = getLeafConstructs(OMPD_parallel_for_simd); + ASSERT_EQ(L3, (ArrayRef{OMPD_parallel, OMPD_for, OMPD_simd})); +} + +TEST(Composition, GetCompoundConstruct) { + Directive C1 = + getCompoundConstruct({OMPD_target, OMPD_teams, OMPD_distribute}); + ASSERT_EQ(C1, OMPD_target_teams_distribute); + Directive C2 = getCompoundConstruct({OMPD_target}); + ASSERT_EQ(C2, OMPD_target); + Directive C3 = getCompoundConstruct({OMPD_target, OMPD_masked}); + ASSERT_EQ(C3, OMPD_unknown); + Directive C4 = getCompoundConstruct({OMPD_target, OMPD_teams_distribute}); + ASSERT_EQ(C4, OMPD_target_teams_distribute); + Directive C5 = getCompoundConstruct({}); + ASSERT_EQ(C5, OMPD_unknown); + Directive C6 = getCompoundConstruct({OMPD_parallel_for, OMPD_simd}); + ASSERT_EQ(C6, OMPD_parallel_for_simd); + Directive C7 = getCompoundConstruct({OMPD_do, OMPD_simd}); + ASSERT_EQ(C7, OMPD_do_simd); // Make sure it's not OMPD_end_do_simd +} diff --git a/llvm/utils/TableGen/DirectiveEmitter.cpp b/llvm/utils/TableGen/DirectiveEmitter.cpp index e0edf1720f8ac..69d9c5e8325ab 100644 --- a/llvm/utils/TableGen/DirectiveEmitter.cpp +++ b/llvm/utils/TableGen/DirectiveEmitter.cpp @@ -12,6 +12,8 @@ //===----------------------------------------------------------------------===// #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" @@ -20,6 +22,9 @@ #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" +#include +#include + using namespace llvm; namespace { @@ -39,7 +44,8 @@ class IfDefScope { }; } // namespace -// Generate enum class +// Generate enum class. Entries are emitted in the order in which they appear +// in the `Records` vector. static void GenerateEnumClass(const std::vector &Records, raw_ostream &OS, StringRef Enum, StringRef Prefix, const DirectiveLanguage &DirLang, @@ -175,6 +181,16 @@ bool DirectiveLanguage::HasValidityErrors() const { return HasDuplicateClausesInDirectives(getDirectives()); } +// Count the maximum number of leaf constituents per construct. +static size_t GetMaxLeafCount(const DirectiveLanguage &DirLang) { + size_t MaxCount = 0; + for (Record *R : DirLang.getDirectives()) { + size_t Count = Directive{R}.getLeafConstructs().size(); + MaxCount = std::max(MaxCount, Count); + } + return MaxCount; +} + // Generate the declaration section for the enumeration in the directive // language static void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) { @@ -189,6 +205,7 @@ static void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) { if (DirLang.hasEnableBitmaskEnumInNamespace()) OS << "#include \"llvm/ADT/BitmaskEnum.h\"\n"; + OS << "#include \n"; // for size_t OS << "\n"; OS << "namespace llvm {\n"; OS << "class StringRef;\n"; @@ -244,7 +261,8 @@ static void EmitDirectivesDecl(RecordKeeper &Records, raw_ostream &OS) { OS << "bool isAllowedClauseForDirective(Directive D, " << "Clause C, unsigned Version);\n"; OS << "\n"; - OS << "llvm::ArrayRef getLeafConstructs(Directive D);\n"; + OS << "constexpr std::size_t getMaxLeafCount() { return " + << GetMaxLeafCount(DirLang) << "; }\n"; OS << "Association getDirectiveAssociation(Directive D);\n"; if (EnumHelperFuncs.length() > 0) { OS << EnumHelperFuncs; @@ -396,6 +414,19 @@ GenerateCaseForVersionedClauses(const std::vector &Clauses, } } +static std::string GetDirectiveName(const DirectiveLanguage &DirLang, + const Record *Rec) { + Directive Dir{Rec}; + return (llvm::Twine("llvm::") + DirLang.getCppNamespace() + + "::" + DirLang.getDirectivePrefix() + Dir.getFormattedName()) + .str(); +} + +static std::string GetDirectiveType(const DirectiveLanguage &DirLang) { + return (llvm::Twine("llvm::") + DirLang.getCppNamespace() + "::Directive") + .str(); +} + // Generate the isAllowedClauseForDirective function implementation. static void GenerateIsAllowedClause(const DirectiveLanguage &DirLang, raw_ostream &OS) { @@ -450,77 +481,134 @@ static void GenerateIsAllowedClause(const DirectiveLanguage &DirLang, OS << "}\n"; // End of function isAllowedClauseForDirective } -// Generate the getLeafConstructs function implementation. -static void GenerateGetLeafConstructs(const DirectiveLanguage &DirLang, - raw_ostream &OS) { - auto getQualifiedName = [&](StringRef Formatted) -> std::string { - return (llvm::Twine("llvm::") + DirLang.getCppNamespace() + - "::Directive::" + DirLang.getDirectivePrefix() + Formatted) - .str(); - }; - - // For each list of leaves, generate a static local object, then - // return a reference to that object for a given directive, e.g. +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). // - // static ListTy leafConstructs_A_B = { A, B }; - // static ListTy leafConstructs_C_D_E = { C, D, E }; - // switch (Dir) { - // case A_B: - // return leafConstructs_A_B; - // case C_D_E: - // return leafConstructs_C_D_E; - // } - - // Map from a record that defines a directive to the name of the - // local object with the list of its leaves. - DenseMap ListNames; - - std::string DirectiveTypeName = - std::string("llvm::") + DirLang.getCppNamespace().str() + "::Directive"; - - OS << '\n'; - - // ArrayRef<...> llvm::::GetLeafConstructs(llvm::::Directive Dir) - OS << "llvm::ArrayRef<" << DirectiveTypeName - << "> llvm::" << DirLang.getCppNamespace() << "::getLeafConstructs(" - << DirectiveTypeName << " Dir) "; - OS << "{\n"; + // 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]]. + + std::vector Directives = DirLang.getDirectives(); + DenseMap DirId; // Record * -> llvm::omp::Directive + + for (auto [Idx, Rec] : llvm::enumerate(Directives)) + DirId.insert(std::make_pair(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] : llvm::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; + } - // Generate the locals. - for (Record *R : DirLang.getDirectives()) { - Directive Dir{R}; + // 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) { + if (Directive{Rec}.getName().starts_with_insensitive("end ")) + EndDirectives.insert(Id); + } - std::vector LeafConstructs = Dir.getLeafConstructs(); - if (LeafConstructs.empty()) - continue; + // 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. + 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]); + }); - std::string ListName = "leafConstructs_" + Dir.getFormattedName(); - OS << " static const " << DirectiveTypeName << ' ' << ListName - << "[] = {\n"; - for (Record *L : LeafConstructs) { - Directive LeafDir{L}; - OS << " " << getQualifiedName(LeafDir.getFormattedName()) << ",\n"; + // 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"; - ListNames.insert(std::make_pair(R, std::move(ListName))); - } - - if (!ListNames.empty()) OS << '\n'; - OS << " switch (Dir) {\n"; - for (Record *R : DirLang.getDirectives()) { - auto F = ListNames.find(R); - if (F == ListNames.end()) - continue; - - Directive Dir{R}; - OS << " case " << getQualifiedName(Dir.getFormattedName()) << ":\n"; - OS << " return " << F->second << ";\n"; } - OS << " default:\n"; - OS << " return ArrayRef<" << DirectiveTypeName << ">{};\n"; - OS << " } // switch (Dir)\n"; - OS << "}\n"; + OS << "};\n\n"; + + // Emit a marker where the first "end directive" is. + auto FirstE = llvm::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 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, @@ -1105,11 +1193,11 @@ void EmitDirectivesBasicImpl(const DirectiveLanguage &DirLang, // isAllowedClauseForDirective(Directive D, Clause C, unsigned Version) GenerateIsAllowedClause(DirLang, OS); - // getLeafConstructs(Directive D) - GenerateGetLeafConstructs(DirLang, OS); - // getDirectiveAssociation(Directive D) GenerateGetDirectiveAssociation(DirLang, OS); + + // Leaf table for getLeafConstructs, etc. + EmitLeafTable(DirLang, OS, "LeafConstructTable"); } // Generate the implemenation section for the enumeration in the directive