https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/141766
>From 2ef30aacee4d80c0e4a925aa5ba9416423d10b1b Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Tue, 27 May 2025 07:55:04 -0500 Subject: [PATCH 1/7] [utils][TableGen] Handle versions on clause/directive spellings In "get<lang>DirectiveName(Kind, Version)", return the spelling that corresponds to Version, and in "get<lang>DirectiveKindAndVersions(Name)" return the pair {Kind, VersionRange}, where VersionRange contains the minimum and the maximum versions that allow "Name" as a spelling. This applies to clauses as well. In general it applies to classes that have spellings (defined via TableGen class "Spelling"). Given a Kind and a Version, getting the corresponding spelling requires a runtime search (which can fail in a general case). To avoid generating the search function inline, a small additional component of llvm/Frontent was added: LLVMFrontendDirective. The corresponding header file also defines C++ classes "Spelling" and "VersionRange", which are used in TableGen/DirectiveEmitter as well. For background information see https://discourse.llvm.org/t/rfc-alternative-spellings-of-openmp-directives/85507 --- .../llvm/Frontend/Directive/Spelling.h | 39 +++++ llvm/include/llvm/TableGen/DirectiveEmitter.h | 25 +-- llvm/lib/Frontend/CMakeLists.txt | 1 + llvm/lib/Frontend/Directive/CMakeLists.txt | 6 + llvm/lib/Frontend/Directive/Spelling.cpp | 31 ++++ llvm/lib/Frontend/OpenACC/CMakeLists.txt | 2 +- llvm/lib/Frontend/OpenMP/CMakeLists.txt | 1 + llvm/test/TableGen/directive1.td | 34 ++-- llvm/test/TableGen/directive2.td | 24 +-- .../utils/TableGen/Basic/DirectiveEmitter.cpp | 146 +++++++++++------- 10 files changed, 212 insertions(+), 97 deletions(-) create mode 100644 llvm/include/llvm/Frontend/Directive/Spelling.h create mode 100644 llvm/lib/Frontend/Directive/CMakeLists.txt create mode 100644 llvm/lib/Frontend/Directive/Spelling.cpp diff --git a/llvm/include/llvm/Frontend/Directive/Spelling.h b/llvm/include/llvm/Frontend/Directive/Spelling.h new file mode 100644 index 0000000000000..3ba0ae2296535 --- /dev/null +++ b/llvm/include/llvm/Frontend/Directive/Spelling.h @@ -0,0 +1,39 @@ +//===-- Spelling.h ------------------------------------------------ C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_FRONTEND_DIRECTIVE_SPELLING_H +#define LLVM_FRONTEND_DIRECTIVE_SPELLING_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" + +#include <limits> + +namespace llvm::directive { + +struct VersionRange { + static constexpr int MaxValue = std::numeric_limits<int>::max(); + int Min = 1; + int Max = MaxValue; +}; + +inline bool operator<(const VersionRange &A, const VersionRange &B) { + if (A.Min != B.Min) + return A.Min < B.Min; + return A.Max < B.Max; +} + +struct Spelling { + StringRef Name; + VersionRange Versions; +}; + +StringRef FindName(llvm::iterator_range<const Spelling *>, unsigned Version); + +} // namespace llvm::directive + +#endif // LLVM_FRONTEND_DIRECTIVE_SPELLING_H diff --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h b/llvm/include/llvm/TableGen/DirectiveEmitter.h index 1235b7638e761..c7d7460087723 100644 --- a/llvm/include/llvm/TableGen/DirectiveEmitter.h +++ b/llvm/include/llvm/TableGen/DirectiveEmitter.h @@ -17,6 +17,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Frontend/Directive/Spelling.h" #include "llvm/Support/MathExtras.h" #include "llvm/TableGen/Record.h" #include <algorithm> @@ -113,29 +114,19 @@ class Versioned { constexpr static int IntWidth = 8 * sizeof(int); }; -// Range of specification versions: [Min, Max] -// Default value: all possible versions. -// This is the same structure as the one emitted into the generated sources. -#define STRUCT_VERSION_RANGE \ - struct VersionRange { \ - int Min = 1; \ - int Max = INT_MAX; \ - } - -STRUCT_VERSION_RANGE; - class Spelling : public Versioned { public: - using Value = std::pair<StringRef, VersionRange>; + using Value = llvm::directive::Spelling; Spelling(const Record *Def) : Def(Def) {} StringRef getText() const { return Def->getValueAsString("spelling"); } - VersionRange getVersions() const { - return VersionRange{getMinVersion(Def), getMaxVersion(Def)}; + llvm::directive::VersionRange getVersions() const { + return llvm::directive::VersionRange{getMinVersion(Def), + getMaxVersion(Def)}; } - Value get() const { return std::make_pair(getText(), getVersions()); } + Value get() const { return Value{getText(), getVersions()}; } private: const Record *Def; @@ -177,11 +168,11 @@ class BaseRecord { // are added. Spelling::Value Oldest{"not found", {/*Min=*/INT_MAX, 0}}; for (auto V : getSpellings()) { - if (V.second.Min < Oldest.second.Min) { + if (V.Versions.Min < Oldest.Versions.Min) { Oldest = V; } } - return Oldest.first; + return Oldest.Name; } // Returns the name of the directive formatted for output. Whitespace are diff --git a/llvm/lib/Frontend/CMakeLists.txt b/llvm/lib/Frontend/CMakeLists.txt index b305ce7d771ce..3b31e6f8dec96 100644 --- a/llvm/lib/Frontend/CMakeLists.txt +++ b/llvm/lib/Frontend/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(Atomic) +add_subdirectory(Directive) add_subdirectory(Driver) add_subdirectory(HLSL) add_subdirectory(OpenACC) diff --git a/llvm/lib/Frontend/Directive/CMakeLists.txt b/llvm/lib/Frontend/Directive/CMakeLists.txt new file mode 100644 index 0000000000000..a567e1affb171 --- /dev/null +++ b/llvm/lib/Frontend/Directive/CMakeLists.txt @@ -0,0 +1,6 @@ +add_llvm_component_library(LLVMFrontendDirective + Spelling.cpp + + LINK_COMPONENTS + Support +) diff --git a/llvm/lib/Frontend/Directive/Spelling.cpp b/llvm/lib/Frontend/Directive/Spelling.cpp new file mode 100644 index 0000000000000..c808e625ee4fd --- /dev/null +++ b/llvm/lib/Frontend/Directive/Spelling.cpp @@ -0,0 +1,31 @@ +//===-- Spelling.cpp ---------------------------------------------- C++ -*-===// +// +// 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/Frontend/Directive/Spelling.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/MathExtras.h" + +#include <cassert> + +llvm::StringRef llvm::directive::FindName( + llvm::iterator_range<const llvm::directive::Spelling *> Range, + unsigned Version) { + assert(llvm::isInt<8 * sizeof(int)>(Version) && "Version value out of range"); + + int V = Version; + Spelling Tmp{StringRef(), {V, V}}; + auto F = + llvm::lower_bound(Range, Tmp, [](const Spelling &A, const Spelling &B) { + return A.Versions < B.Versions; + }); + if (F != Range.end()) + return F->Name; + return StringRef(); +} diff --git a/llvm/lib/Frontend/OpenACC/CMakeLists.txt b/llvm/lib/Frontend/OpenACC/CMakeLists.txt index f352014978690..4664b71407c48 100644 --- a/llvm/lib/Frontend/OpenACC/CMakeLists.txt +++ b/llvm/lib/Frontend/OpenACC/CMakeLists.txt @@ -9,5 +9,5 @@ add_llvm_component_library(LLVMFrontendOpenACC acc_gen ) -target_link_libraries(LLVMFrontendOpenACC LLVMSupport) +target_link_libraries(LLVMFrontendOpenACC LLVMSupport LLVMFrontendDirective) diff --git a/llvm/lib/Frontend/OpenMP/CMakeLists.txt b/llvm/lib/Frontend/OpenMP/CMakeLists.txt index 35c607866a94e..5bf15ca3a8991 100644 --- a/llvm/lib/Frontend/OpenMP/CMakeLists.txt +++ b/llvm/lib/Frontend/OpenMP/CMakeLists.txt @@ -23,4 +23,5 @@ add_llvm_component_library(LLVMFrontendOpenMP BitReader FrontendOffloading FrontendAtomic + FrontendDirective ) diff --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td index 8196a30d03df4..1c8da26f50f4b 100644 --- a/llvm/test/TableGen/directive1.td +++ b/llvm/test/TableGen/directive1.td @@ -54,6 +54,7 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // CHECK-NEXT: #include "llvm/ADT/ArrayRef.h" // CHECK-NEXT: #include "llvm/ADT/BitmaskEnum.h" // CHECK-NEXT: #include "llvm/ADT/StringRef.h" +// CHECK-NEXT: #include "llvm/Frontend/Directive/Spelling.h" // CHECK-NEXT: #include "llvm/Support/Compiler.h" // CHECK-NEXT: #include <cstddef> // CHECK-NEXT: #include <utility> @@ -63,8 +64,6 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // CHECK-EMPTY: // CHECK-NEXT: LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); // CHECK-EMPTY: -// CHECK-NEXT: struct VersionRange { int Min = 1; int Max = 0x7fffffff; }; -// CHECK-EMPTY: // CHECK-NEXT: enum class Association { // CHECK-NEXT: Block, // CHECK-NEXT: Declaration, @@ -126,14 +125,14 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // CHECK-NEXT: constexpr auto TDLCV_valc = AKind::TDLCV_valc; // CHECK-EMPTY: // CHECK-NEXT: // Enumeration helper functions -// CHECK-NEXT: LLVM_ABI std::pair<Directive, VersionRange> getTdlDirectiveKindAndVersions(StringRef Str); +// CHECK-NEXT: LLVM_ABI std::pair<Directive, directive::VersionRange> getTdlDirectiveKindAndVersions(StringRef Str); // CHECK-NEXT: inline Directive getTdlDirectiveKind(StringRef Str) { // CHECK-NEXT: return getTdlDirectiveKindAndVersions(Str).first; // CHECK-NEXT: } // CHECK-EMPTY: // CHECK-NEXT: LLVM_ABI StringRef getTdlDirectiveName(Directive D, unsigned Ver = 0); // CHECK-EMPTY: -// CHECK-NEXT: LLVM_ABI std::pair<Clause, VersionRange> getTdlClauseKindAndVersions(StringRef Str); +// CHECK-NEXT: LLVM_ABI std::pair<Clause, directive::VersionRange> getTdlClauseKindAndVersions(StringRef Str); // CHECK-EMPTY: // CHECK-NEXT: inline Clause getTdlClauseKind(StringRef Str) { // CHECK-NEXT: return getTdlClauseKindAndVersions(Str).first; @@ -320,17 +319,18 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // IMPL: #ifdef GEN_DIRECTIVES_IMPL // IMPL-NEXT: #undef GEN_DIRECTIVES_IMPL // IMPL-EMPTY: +// IMPL-NEXT: #include "llvm/Frontend/Directive/Spelling.h" // IMPL-NEXT: #include "llvm/Support/ErrorHandling.h" // IMPL-NEXT: #include <utility> // IMPL-EMPTY: -// IMPL-NEXT: std::pair<llvm::tdl::Directive, llvm::tdl::VersionRange> llvm::tdl::getTdlDirectiveKindAndVersions(llvm::StringRef Str) { -// IMPL-NEXT: VersionRange All{}; // Default-initialized to "all-versions" -// IMPL-NEXT: return StringSwitch<std::pair<Directive, VersionRange>>(Str) +// IMPL-NEXT: std::pair<llvm::tdl::Directive, llvm::directive::VersionRange> llvm::tdl::getTdlDirectiveKindAndVersions(llvm::StringRef Str) { +// IMPL-NEXT: directive::VersionRange All; // Default-initialized to "all versions" +// IMPL-NEXT: return StringSwitch<std::pair<Directive, directive::VersionRange>>(Str) // IMPL-NEXT: .Case("dira", {TDLD_dira, All}) // IMPL-NEXT: .Default({TDLD_dira, All}); // IMPL-NEXT: } // IMPL-EMPTY: -// IMPL-NEXT: llvm::StringRef llvm::tdl::getTdlDirectiveName(llvm::tdl::Directive Kind, unsigned) { +// IMPL-NEXT: llvm::StringRef llvm::tdl::getTdlDirectiveName(llvm::tdl::Directive Kind, unsigned Version) { // IMPL-NEXT: switch (Kind) { // IMPL-NEXT: case TDLD_dira: // IMPL-NEXT: return "dira"; @@ -338,23 +338,29 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // IMPL-NEXT: llvm_unreachable("Invalid Tdl Directive kind"); // IMPL-NEXT: } // IMPL-EMPTY: -// IMPL-NEXT: std::pair<llvm::tdl::Clause, llvm::tdl::VersionRange> llvm::tdl::getTdlClauseKindAndVersions(llvm::StringRef Str) { -// IMPL-NEXT: VersionRange All{}; // Default-initialized to "all-versions" -// IMPL-NEXT: return StringSwitch<std::pair<Clause, VersionRange>>(Str) +// IMPL-NEXT: std::pair<llvm::tdl::Clause, llvm::directive::VersionRange> llvm::tdl::getTdlClauseKindAndVersions(llvm::StringRef Str) { +// IMPL-NEXT: directive::VersionRange All; // Default-initialized to "all versions" +// IMPL-NEXT: return StringSwitch<std::pair<Clause, directive::VersionRange>>(Str) // IMPL-NEXT: .Case("clausea", {TDLC_clausea, All}) // IMPL-NEXT: .Case("clauseb", {TDLC_clauseb, All}) // IMPL-NEXT: .Case("clausec", {TDLC_clausec, All}) +// IMPL-NEXT: .Case("ccccccc", {TDLC_clausec, All}) // IMPL-NEXT: .Default({TDLC_clauseb, All}); // IMPL-NEXT: } // IMPL-EMPTY: -// IMPL-NEXT: llvm::StringRef llvm::tdl::getTdlClauseName(llvm::tdl::Clause Kind, unsigned) { +// IMPL-NEXT: llvm::StringRef llvm::tdl::getTdlClauseName(llvm::tdl::Clause Kind, unsigned Version) { // IMPL-NEXT: switch (Kind) { // IMPL-NEXT: case TDLC_clausea: // IMPL-NEXT: return "clausea"; // IMPL-NEXT: case TDLC_clauseb: // IMPL-NEXT: return "clauseb"; -// IMPL-NEXT: case TDLC_clausec: -// IMPL-NEXT: return "clausec"; +// IMPL-NEXT: case TDLC_clausec: { +// IMPL-NEXT: static const llvm::directive::Spelling TDLC_clausec_spellings[] = { +// IMPL-NEXT: {"clausec", {1, 2147483647}}, +// IMPL-NEXT: {"ccccccc", {1, 2147483647}}, +// IMPL-NEXT: }; +// IMPL-NEXT: return llvm::directive::FindName(TDLC_clausec_spellings, Version); +// IMPL-NEXT: } // IMPL-NEXT: } // IMPL-NEXT: llvm_unreachable("Invalid Tdl Clause kind"); // IMPL-NEXT: } diff --git a/llvm/test/TableGen/directive2.td b/llvm/test/TableGen/directive2.td index ead6aa2637b76..3a64bb3900a31 100644 --- a/llvm/test/TableGen/directive2.td +++ b/llvm/test/TableGen/directive2.td @@ -47,6 +47,7 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // CHECK-EMPTY: // CHECK-NEXT: #include "llvm/ADT/ArrayRef.h" // CHECK-NEXT: #include "llvm/ADT/StringRef.h" +// CHECK-NEXT: #include "llvm/Frontend/Directive/Spelling.h" // CHECK-NEXT: #include "llvm/Support/Compiler.h" // CHECK-NEXT: #include <cstddef> // CHECK-NEXT: #include <utility> @@ -54,8 +55,6 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // CHECK-NEXT: namespace llvm { // CHECK-NEXT: namespace tdl { // CHECK-EMPTY: -// CHECK-NEXT: struct VersionRange { int Min = 1; int Max = 0x7fffffff; }; -// CHECK-EMPTY: // CHECK-NEXT: enum class Association { // CHECK-NEXT: Block, // CHECK-NEXT: Declaration, @@ -102,14 +101,14 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // CHECK-NEXT: static constexpr std::size_t Clause_enumSize = 4; // CHECK-EMPTY: // CHECK-NEXT: // Enumeration helper functions -// CHECK-NEXT: LLVM_ABI std::pair<Directive, VersionRange> getTdlDirectiveKindAndVersions(StringRef Str); +// CHECK-NEXT: LLVM_ABI std::pair<Directive, directive::VersionRange> getTdlDirectiveKindAndVersions(StringRef Str); // CHECK-NEXT: inline Directive getTdlDirectiveKind(StringRef Str) { // CHECK-NEXT: return getTdlDirectiveKindAndVersions(Str).first; // CHECK-NEXT: } // CHECK-EMPTY: // CHECK-NEXT: LLVM_ABI StringRef getTdlDirectiveName(Directive D, unsigned Ver = 0); // CHECK-EMPTY: -// CHECK-NEXT: LLVM_ABI std::pair<Clause, VersionRange> getTdlClauseKindAndVersions(StringRef Str); +// CHECK-NEXT: LLVM_ABI std::pair<Clause, directive::VersionRange> getTdlClauseKindAndVersions(StringRef Str); // CHECK-EMPTY: // CHECK-NEXT: inline Clause getTdlClauseKind(StringRef Str) { // CHECK-NEXT: return getTdlClauseKindAndVersions(Str).first; @@ -267,17 +266,18 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // IMPL: #ifdef GEN_DIRECTIVES_IMPL // IMPL-NEXT: #undef GEN_DIRECTIVES_IMPL // IMPL-EMPTY: +// IMPL-NEXT: #include "llvm/Frontend/Directive/Spelling.h" // IMPL-NEXT: #include "llvm/Support/ErrorHandling.h" // IMPL-NEXT: #include <utility> // IMPL-EMPTY: -// IMPL-NEXT: std::pair<llvm::tdl::Directive, llvm::tdl::VersionRange> llvm::tdl::getTdlDirectiveKindAndVersions(llvm::StringRef Str) { -// IMPL-NEXT: VersionRange All{}; // Default-initialized to "all-versions" -// IMPL-NEXT: return StringSwitch<std::pair<Directive, VersionRange>>(Str) +// IMPL-NEXT: std::pair<llvm::tdl::Directive, llvm::directive::VersionRange> llvm::tdl::getTdlDirectiveKindAndVersions(llvm::StringRef Str) { +// IMPL-NEXT: directive::VersionRange All; // Default-initialized to "all versions" +// IMPL-NEXT: return StringSwitch<std::pair<Directive, directive::VersionRange>>(Str) // IMPL-NEXT: .Case("dira", {TDLD_dira, All}) // IMPL-NEXT: .Default({TDLD_dira, All}); // IMPL-NEXT: } // IMPL-EMPTY: -// IMPL-NEXT: llvm::StringRef llvm::tdl::getTdlDirectiveName(llvm::tdl::Directive Kind, unsigned) { +// IMPL-NEXT: llvm::StringRef llvm::tdl::getTdlDirectiveName(llvm::tdl::Directive Kind, unsigned Version) { // IMPL-NEXT: switch (Kind) { // IMPL-NEXT: case TDLD_dira: // IMPL-NEXT: return "dira"; @@ -285,9 +285,9 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // IMPL-NEXT: llvm_unreachable("Invalid Tdl Directive kind"); // IMPL-NEXT: } // IMPL-EMPTY: -// IMPL-NEXT: std::pair<llvm::tdl::Clause, llvm::tdl::VersionRange> llvm::tdl::getTdlClauseKindAndVersions(llvm::StringRef Str) { -// IMPL-NEXT: VersionRange All{}; // Default-initialized to "all-versions" -// IMPL-NEXT: return StringSwitch<std::pair<Clause, VersionRange>>(Str) +// IMPL-NEXT: std::pair<llvm::tdl::Clause, llvm::directive::VersionRange> llvm::tdl::getTdlClauseKindAndVersions(llvm::StringRef Str) { +// IMPL-NEXT: directive::VersionRange All; // Default-initialized to "all versions" +// IMPL-NEXT: return StringSwitch<std::pair<Clause, directive::VersionRange>>(Str) // IMPL-NEXT: .Case("clausea", {TDLC_clauseb, All}) // IMPL-NEXT: .Case("clauseb", {TDLC_clauseb, All}) // IMPL-NEXT: .Case("clausec", {TDLC_clausec, All}) @@ -295,7 +295,7 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // IMPL-NEXT: .Default({TDLC_clauseb, All}); // IMPL-NEXT: } // IMPL-EMPTY: -// IMPL-NEXT: llvm::StringRef llvm::tdl::getTdlClauseName(llvm::tdl::Clause Kind, unsigned) { +// IMPL-NEXT: llvm::StringRef llvm::tdl::getTdlClauseName(llvm::tdl::Clause Kind, unsigned Version) { // IMPL-NEXT: switch (Kind) { // IMPL-NEXT: case TDLC_clausea: // IMPL-NEXT: return "clausea"; diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp index e1e41b3ecb584..84438de067dce 100644 --- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp +++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp @@ -77,6 +77,19 @@ static std::string getIdentifierName(const Record *Rec, StringRef Prefix) { return Prefix.str() + BaseRecord(Rec).getFormattedName(); } +using RecordWithSpelling = std::pair<const Record *, Spelling::Value>; + +static std::vector<RecordWithSpelling> +getSpellings(ArrayRef<const Record *> Records) { + std::vector<RecordWithSpelling> List; + for (const Record *R : Records) { + Clause C(R); + llvm::transform(C.getSpellings(), std::back_inserter(List), + [R](Spelling::Value V) { return std::make_pair(R, V); }); + } + return List; +} + static void generateEnumExports(ArrayRef<const Record *> Records, raw_ostream &OS, StringRef Enum, StringRef Prefix) { @@ -270,6 +283,7 @@ static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) { OS << "#include \"llvm/ADT/BitmaskEnum.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 <cstddef>\n"; // for size_t OS << "#include <utility>\n"; // for std::pair @@ -285,13 +299,6 @@ static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) { if (DirLang.hasEnableBitmaskEnumInNamespace()) OS << "\nLLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();\n"; -#define AS_STRING_HELPER_TO_GET_THE_ARGUMENT_MACRO_EXPANDED(x) #x -#define AS_STRING(x) AS_STRING_HELPER_TO_GET_THE_ARGUMENT_MACRO_EXPANDED(x) - OS << "\n"; - OS << AS_STRING(STRUCT_VERSION_RANGE) << ";\n"; -#undef AS_STRING -#undef AS_STRING_HELPER_TO_GET_THE_ARGUMENT_MACRO_EXPANDED - // Emit Directive associations std::vector<const Record *> Associations; copy_if(DirLang.getAssociations(), std::back_inserter(Associations), @@ -324,7 +331,7 @@ static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) { OS << "\n"; OS << "// Enumeration helper functions\n"; - OS << "LLVM_ABI std::pair<Directive, VersionRange> get" << Lang + OS << "LLVM_ABI std::pair<Directive, directive::VersionRange> get" << Lang << "DirectiveKindAndVersions(StringRef Str);\n"; OS << "inline Directive get" << Lang << "DirectiveKind(StringRef Str) {\n"; @@ -336,7 +343,7 @@ static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) { << "DirectiveName(Directive D, unsigned Ver = 0);\n"; OS << "\n"; - OS << "LLVM_ABI std::pair<Clause, VersionRange> get" << Lang + OS << "LLVM_ABI std::pair<Clause, directive::VersionRange> get" << Lang << "ClauseKindAndVersions(StringRef Str);\n"; OS << "\n"; @@ -373,6 +380,33 @@ static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) { 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<Spelling::Value> +orderSpellings(ArrayRef<Spelling::Value> Spellings) { + std::vector<Spelling::Value> List(Spellings.begin(), Spellings.end()); + + // There are two intertwined orderings: (1) the order between spellings + // (used here), and (2) the order between a spelling and a version (used + // at runtime). + // Define order (2) as such that the first A that is not less than V + // will be the selected spelling given V. Specifically, + // V <(2) A <=> V < A.Min + // A <(2) V <=> A.Max < V + // + // The orders have to be compatible, i.e. + // A <(2) V and !(V <(2) B) => A <(1) B, and + // !(A <(2) v) and V <(2) B => A <(1) B + // In other words, the transitive closure of (2) must contain (1). + llvm::stable_sort(List, + [](const Spelling::Value &A, const Spelling::Value &B) { + return A.Versions < B.Versions; + }); + + return List; +} + // Generate function implementation for get<Enum>Name(StringRef Str) static void generateGetName(ArrayRef<const Record *> Records, raw_ostream &OS, StringRef Enum, const DirectiveLanguage &DirLang, @@ -381,14 +415,31 @@ static void generateGetName(ArrayRef<const Record *> Records, raw_ostream &OS, std::string Qual = getQualifier(DirLang); OS << "\n"; OS << "llvm::StringRef " << Qual << "get" << Lang << Enum << "Name(" << Qual - << Enum << " Kind, unsigned) {\n"; + << Enum << " Kind, unsigned Version) {\n"; OS << " switch (Kind) {\n"; for (const Record *R : Records) { - OS << " case " << getIdentifierName(R, Prefix) << ":\n"; - // FIXME: This will need to recognize different spellings for different - // versions. - OS << " return \"" << BaseRecord(R).getSpellingForIdentifier() - << "\";\n"; + BaseRecord Rec(R); + std::string Ident = getIdentifierName(R, Prefix); + OS << " case " << Ident << ":"; + auto 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 const 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"; @@ -415,23 +466,29 @@ static void generateGetKind(ArrayRef<const Record *> Records, raw_ostream &OS, // std::pair<<Enum>, VersionRange> // get<DirLang><Enum>KindAndVersions(StringRef Str); OS << "\n"; - OS << "std::pair<" << Qual << Enum << ", " << Qual << "VersionRange> " << Qual - << "get" << DirLang.getName() << Enum + OS << "std::pair<" << Qual << Enum << ", llvm::directive::VersionRange> " + << Qual << "get" << DirLang.getName() << Enum << "KindAndVersions(llvm::StringRef Str) {\n"; - OS << " VersionRange All{}; // Default-initialized to \"all-versions\"\n"; + OS << " directive::VersionRange All; // Default-initialized to \"all " + "versions\"\n"; OS << " return StringSwitch<std::pair<" << Enum << ", " - << "VersionRange>>(Str)\n"; + << "directive::VersionRange>>(Str)\n"; + + directive::VersionRange All; for (const Record *R : Records) { BaseRecord Rec(R); - // FIXME: This will need to recognize different spellings for different - // versions. - StringRef Name = Rec.getSpellingForIdentifier(); - if (ImplicitAsUnknown && R->getValueAsBit("isImplicit")) { - OS << " .Case(\"" << Name << "\", {" << DefaultName << ", All})\n"; - } else { - OS << " .Case(\"" << Name << "\", {" - << getIdentifierName(R, Prefix) << ", All})\n"; + 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"; @@ -1144,47 +1201,29 @@ static void generateFlangClauseParserKindMap(const DirectiveLanguage &DirLang, << " Parser clause\");\n"; } -using RecordWithText = std::pair<const Record *, StringRef>; - -static bool compareRecordText(const RecordWithText &A, - const RecordWithText &B) { - return A.second > B.second; -} - -static std::vector<RecordWithText> -getSpellingTexts(ArrayRef<const Record *> Records) { - std::vector<RecordWithText> List; - for (const Record *R : Records) { - Clause C(R); - llvm::transform( - C.getSpellings(), std::back_inserter(List), - [R](Spelling::Value V) { return std::make_pair(R, V.first); }); - } - return List; -} - // Generate the parser for the clauses. static void generateFlangClausesParser(const DirectiveLanguage &DirLang, raw_ostream &OS) { std::vector<const Record *> Clauses = DirLang.getClauses(); // Sort clauses in the reverse alphabetical order with respect to their // names and aliases, so that longer names are tried before shorter ones. - std::vector<std::pair<const Record *, StringRef>> Names = - getSpellingTexts(Clauses); - llvm::sort(Names, compareRecordText); + std::vector<RecordWithSpelling> 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, RecTxt] : llvm::enumerate(Names)) { - auto [R, N] = RecTxt; + for (auto [Index, RecSp] : llvm::enumerate(Names)) { + auto [R, S] = RecSp; Clause C(R); StringRef FlangClass = C.getFlangClass(); - OS << " \"" << N << "\" >> construct<" << Base << ">(construct<" << Base - << "::" << C.getFormattedParserClassName() << ">("; + OS << " \"" << S.Name << "\" >> construct<" << Base << ">(construct<" + << Base << "::" << C.getFormattedParserClassName() << ">("; if (FlangClass.empty()) { OS << "))"; if (Index != LastIndex) @@ -1337,6 +1376,7 @@ void emitDirectivesBasicImpl(const DirectiveLanguage &DirLang, StringRef CPrefix = DirLang.getClausePrefix(); OS << "\n"; + OS << "#include \"llvm/Frontend/Directive/Spelling.h\"\n"; OS << "#include \"llvm/Support/ErrorHandling.h\"\n"; OS << "#include <utility>\n"; >From a40f63fcac3ef4edc60ec3a17a8d9f9e9d223a20 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Wed, 28 May 2025 12:09:32 -0500 Subject: [PATCH 2/7] Use linear search instead of std::lower_bound --- llvm/lib/Frontend/Directive/Spelling.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Frontend/Directive/Spelling.cpp b/llvm/lib/Frontend/Directive/Spelling.cpp index c808e625ee4fd..ad870f1b366c3 100644 --- a/llvm/lib/Frontend/Directive/Spelling.cpp +++ b/llvm/lib/Frontend/Directive/Spelling.cpp @@ -8,24 +8,30 @@ #include "llvm/Frontend/Directive/Spelling.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/MathExtras.h" #include <cassert> +static bool Contains(llvm::directive::VersionRange V, int P) { + return V.Min <= P && P <= V.Max; +} + llvm::StringRef llvm::directive::FindName( llvm::iterator_range<const llvm::directive::Spelling *> Range, unsigned Version) { assert(llvm::isInt<8 * sizeof(int)>(Version) && "Version value out of range"); int V = Version; - Spelling Tmp{StringRef(), {V, V}}; - auto F = - llvm::lower_bound(Range, Tmp, [](const Spelling &A, const Spelling &B) { - return A.Versions < B.Versions; - }); - if (F != Range.end()) - return F->Name; + // Do a linear search to find the first Spelling that contains Version. + // The condition "contains(S, Version)" does not partition the list of + // spellings, so std::[lower|upper]_bound cannot be used. + // In practice the list of spellings is expected to be very short, so + // linear search seems appropriate. In general, an interval tree may be + // a better choice, but in this case it may be an overkill. + for (auto &S : Range) { + if (Contains(S.Versions, V)) + return S.Name; + } return StringRef(); } >From 8a486f77fba8b88d9e64ea00c58509798e210c3d Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Wed, 28 May 2025 14:25:14 -0500 Subject: [PATCH 3/7] Set the minimum possible version to 0 instead of 1 The default value of the Version parameter in get<Lang><Enum>Name() was 0, which was not in the range [1, 0x7fffffff]. To fix this, it was either to change the default value to 1, or to lower the minimum version to 0. The latter seemed like a better choice, since 0 is a natural choice for a lower bound on version numbers. --- llvm/include/llvm/Frontend/Directive/DirectiveBase.td | 4 ++-- llvm/include/llvm/Frontend/Directive/Spelling.h | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td index 142ba0423f251..301a7cce59627 100644 --- a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td +++ b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td @@ -52,7 +52,7 @@ class DirectiveLanguage { } // Base class for versioned entities. -class Versioned<int min = 1, int max = 0x7FFFFFFF> { +class Versioned<int min = 0, int max = 0x7FFFFFFF> { // Mininum version number where this object is valid. int minVersion = min; @@ -60,7 +60,7 @@ class Versioned<int min = 1, int max = 0x7FFFFFFF> { int maxVersion = max; } -class Spelling<string s, int min = 1, int max = 0x7FFFFFFF> +class Spelling<string s, int min = 0, int max = 0x7FFFFFFF> : Versioned<min, max> { string spelling = s; } diff --git a/llvm/include/llvm/Frontend/Directive/Spelling.h b/llvm/include/llvm/Frontend/Directive/Spelling.h index 3ba0ae2296535..5ac7b27aa2737 100644 --- a/llvm/include/llvm/Frontend/Directive/Spelling.h +++ b/llvm/include/llvm/Frontend/Directive/Spelling.h @@ -17,7 +17,9 @@ namespace llvm::directive { struct VersionRange { static constexpr int MaxValue = std::numeric_limits<int>::max(); - int Min = 1; + // The default "Version" value in get<Lang><Enum>Name() is 0, include that + // in the maximum range. + int Min = 0; int Max = MaxValue; }; >From 596ccd6b1327fa3443f7ce5addc98a9a63972940 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Wed, 28 May 2025 15:37:10 -0500 Subject: [PATCH 4/7] Fix testcase --- llvm/test/TableGen/directive1.td | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td index 1c8da26f50f4b..862c5a056bcfc 100644 --- a/llvm/test/TableGen/directive1.td +++ b/llvm/test/TableGen/directive1.td @@ -356,8 +356,8 @@ def TDL_DirA : Directive<[Spelling<"dira">]> { // IMPL-NEXT: return "clauseb"; // IMPL-NEXT: case TDLC_clausec: { // IMPL-NEXT: static const llvm::directive::Spelling TDLC_clausec_spellings[] = { -// IMPL-NEXT: {"clausec", {1, 2147483647}}, -// IMPL-NEXT: {"ccccccc", {1, 2147483647}}, +// IMPL-NEXT: {"clausec", {0, 2147483647}}, +// IMPL-NEXT: {"ccccccc", {0, 2147483647}}, // IMPL-NEXT: }; // IMPL-NEXT: return llvm::directive::FindName(TDLC_clausec_spellings, Version); // IMPL-NEXT: } >From ad099a312e553b261e91f36fffd3ca30b684c3a0 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Thu, 29 May 2025 08:03:32 -0500 Subject: [PATCH 5/7] Restart build >From 01d3e102047ffd14d1c48751ff712ea737d4e675 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Thu, 29 May 2025 08:44:14 -0500 Subject: [PATCH 6/7] Restart build >From fda5ad0fa81c4527f8f39721eb0ef116a1b93d83 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <krzysztof.parzys...@amd.com> Date: Thu, 29 May 2025 09:34:37 -0500 Subject: [PATCH 7/7] Use BaseRecord instead of Clause for getSpellings() --- llvm/utils/TableGen/Basic/DirectiveEmitter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp index 84438de067dce..0a137ac9ab6cd 100644 --- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp +++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp @@ -83,8 +83,8 @@ static std::vector<RecordWithSpelling> getSpellings(ArrayRef<const Record *> Records) { std::vector<RecordWithSpelling> List; for (const Record *R : Records) { - Clause C(R); - llvm::transform(C.getSpellings(), std::back_inserter(List), + BaseRecord Rec(R); + llvm::transform(Rec.getSpellings(), std::back_inserter(List), [R](Spelling::Value V) { return std::make_pair(R, V); }); } return List; _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits