[llvm-branch-commits] [llvm] [utils][TableGen] Handle versions on clause/directive spellings (PR #141766)
Krzysztof Parzyszek via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed May 28 12:28:32 PDT 2025
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.Parzyszek at amd.com>
Date: Tue, 27 May 2025 07:55:04 -0500
Subject: [PATCH 1/3] [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.Parzyszek at amd.com>
Date: Wed, 28 May 2025 12:09:32 -0500
Subject: [PATCH 2/3] 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.Parzyszek at amd.com>
Date: Wed, 28 May 2025 14:25:14 -0500
Subject: [PATCH 3/3] 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;
};
More information about the llvm-branch-commits
mailing list