[utils][TableGen] Handle versions on clause/directive spellings (#143021)

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
This commit is contained in:
Krzysztof Parzyszek
2025-06-06 17:07:25 -05:00
committed by GitHub
parent 428afa62b0
commit 7b2aa02a33
11 changed files with 208 additions and 99 deletions

View File

@@ -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;
}

View File

@@ -0,0 +1,40 @@
//===-------------------------------------------------------------- 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>
#include <tuple>
namespace llvm::directive {
struct VersionRange {
static constexpr int MaxValue = std::numeric_limits<int>::max();
// The default "Version" value in get<Lang><Enum>Name() is 0, include that
// in the maximum range.
int Min = 0;
int Max = MaxValue;
bool operator<(const VersionRange &R) const {
return std::tie(Min, Max) < std::tie(R.Min, R.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

View File

@@ -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 @@ private:
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 = 0x7fffffff; \
}
STRUCT_VERSION_RANGE;
class Spelling : public Versioned {
public:
using Value = std::pair<StringRef, VersionRange>;
using Value = 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,9 +168,9 @@ public:
// 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

View File

@@ -1,4 +1,5 @@
add_subdirectory(Atomic)
add_subdirectory(Directive)
add_subdirectory(Driver)
add_subdirectory(HLSL)
add_subdirectory(OpenACC)

View File

@@ -0,0 +1,6 @@
add_llvm_component_library(LLVMFrontendDirective
Spelling.cpp
LINK_COMPONENTS
Support
)

View File

@@ -0,0 +1,38 @@
//===-------------------------------------------------------------- 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/StringRef.h"
#include "llvm/Support/MathExtras.h"
#include <cassert>
using namespace llvm;
static bool Contains(directive::VersionRange V, int P) {
return V.Min <= P && P <= V.Max;
}
llvm::StringRef llvm::directive::FindName(
llvm::iterator_range<const directive::Spelling *> Range, unsigned Version) {
assert(llvm::isInt<8 * sizeof(int)>(Version) && "Version value out of range");
int V = Version;
// 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();
}

View File

@@ -9,5 +9,5 @@ add_llvm_component_library(LLVMFrontendOpenACC
acc_gen
)
target_link_libraries(LLVMFrontendOpenACC LLVMSupport)
target_link_libraries(LLVMFrontendOpenACC LLVMSupport LLVMFrontendDirective)

View File

@@ -23,4 +23,5 @@ add_llvm_component_library(LLVMFrontendOpenMP
BitReader
FrontendOffloading
FrontendAtomic
FrontendDirective
)

View File

@@ -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 constexpr llvm::directive::Spelling TDLC_clausec_spellings[] = {
// IMPL-NEXT: {"clausec", {0, 2147483647}},
// IMPL-NEXT: {"ccccccc", {0, 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: }

View File

@@ -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";

View File

@@ -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) {
BaseRecord Rec(R);
llvm::transform(Rec.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,20 @@ 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());
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 +402,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 << ":";
std::vector<Spelling::Value> 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 constexpr 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 +453,28 @@ 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 +1187,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 +1362,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";