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/6] [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/6] 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/6] 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/6] 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/6] 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/6] Restart build


_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to