[llvm] [mlir] [utils][TableGen] Implement clause aliases as alternative spellings (PR #141765)

Krzysztof Parzyszek via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 5 07:26:11 PDT 2025


https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/141765

>From 809e5129acf2fc1c49f355c3a5c858aeab5f9b5f Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 21 May 2025 09:50:14 -0500
Subject: [PATCH 1/7] [utils][TableGen] Clarify usage of ClauseVal, rename to
 EnumVal

The class "ClauseVal" actually represents a definition of an enumeration
value, and in itself it is not bound to any clause. Rename it to EnumVal
and add a comment clarifying how it's translated into an actual enum
definition in the generated source code.

There is no change in functionality.
---
 .../llvm/Frontend/Directive/DirectiveBase.td  | 22 +++++--
 llvm/include/llvm/Frontend/OpenACC/ACC.td     |  4 +-
 llvm/include/llvm/Frontend/OpenMP/OMP.td      | 64 +++++++++----------
 llvm/include/llvm/TableGen/DirectiveEmitter.h |  4 +-
 llvm/test/TableGen/directive1.td              |  6 +-
 .../utils/TableGen/Basic/DirectiveEmitter.cpp | 37 +++++------
 mlir/test/mlir-tblgen/directive-common.td     |  6 +-
 mlir/tools/mlir-tblgen/DirectiveCommonGen.cpp |  8 +--
 8 files changed, 83 insertions(+), 68 deletions(-)

diff --git a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
index 3e2744dea8d14..582da20083aee 100644
--- a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
+++ b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
@@ -51,9 +51,23 @@ class DirectiveLanguage {
   string flangClauseBaseClass = "";
 }
 
-// Information about values accepted by enum-like clauses
-class ClauseVal<string n, int v, bit uv> {
-  // Name of the clause value.
+// Some clauses take an argument from a predefined list of allowed keyword
+// values. For example, assume a clause "someclause" with an argument from
+// the list "foo", "bar", "baz". In the user source code this would look
+// like "someclause(foo)", whereas in the compiler the values would be
+// represented as
+// enum someclause.enumClauseValue {
+//   Xyz_foo = v_foo,
+//   Xyz_bar = v_bar,
+//   Xyz_baz = v_baz,
+// }
+// The "Xyz_..." are the _record_ names of EnumVal's:
+// def Xyz_foo = EnumVal<"foo", v_foo>;
+// def Xyz_bar = EnumVal<"bar", v_bar>;
+// def Xyz_baz = EnumVal<"baz", v_baz>;
+//
+class EnumVal<string n, int v, bit uv> {
+  // Spelling of the value.
   string name = n;
 
   // Integer value of the clause.
@@ -90,7 +104,7 @@ class Clause<string c> {
   string enumClauseValue = "";
 
   // List of allowed clause values
-  list<ClauseVal> allowedClauseValues = [];
+  list<EnumVal> allowedClauseValues = [];
 
   // If set to true, value class is part of a list. Single class by default.
   bit isValueList = false;
diff --git a/llvm/include/llvm/Frontend/OpenACC/ACC.td b/llvm/include/llvm/Frontend/OpenACC/ACC.td
index 46cba9f2400e1..b74cd6e5642ec 100644
--- a/llvm/include/llvm/Frontend/OpenACC/ACC.td
+++ b/llvm/include/llvm/Frontend/OpenACC/ACC.td
@@ -86,8 +86,8 @@ def ACCC_Create : Clause<"create"> {
 }
 
 // 2.5.16
-def ACC_Default_none : ClauseVal<"none", 1, 1> { let isDefault = 1; }
-def ACC_Default_present : ClauseVal<"present", 0, 1> {}
+def ACC_Default_none : EnumVal<"none", 1, 1> { let isDefault = 1; }
+def ACC_Default_present : EnumVal<"present", 0, 1> {}
 
 def ACCC_Default : Clause<"default"> {
   let flangClass = "AccDefaultClause";
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 0af4b436649a3..cc9e038dc533c 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -77,9 +77,9 @@ def OMPC_AtomicDefaultMemOrder : Clause<"atomic_default_mem_order"> {
   let flangClass = "OmpAtomicDefaultMemOrderClause";
 }
 
-def OMP_BIND_parallel : ClauseVal<"parallel",1,1> {}
-def OMP_BIND_teams : ClauseVal<"teams",2,1> {}
-def OMP_BIND_thread : ClauseVal<"thread",3,1> { let isDefault = true; }
+def OMP_BIND_parallel : EnumVal<"parallel",1,1> {}
+def OMP_BIND_teams : EnumVal<"teams",2,1> {}
+def OMP_BIND_thread : EnumVal<"thread",3,1> { let isDefault = true; }
 def OMPC_Bind : Clause<"bind"> {
   let clangClass = "OMPBindClause";
   let flangClass = "OmpBindClause";
@@ -91,11 +91,11 @@ def OMPC_Bind : Clause<"bind"> {
   ];
 }
 
-def OMP_CANCELLATION_CONSTRUCT_Parallel : ClauseVal<"parallel", 1, 1> {}
-def OMP_CANCELLATION_CONSTRUCT_Loop : ClauseVal<"loop", 2, 1> {}
-def OMP_CANCELLATION_CONSTRUCT_Sections : ClauseVal<"sections", 3, 1> {}
-def OMP_CANCELLATION_CONSTRUCT_Taskgroup : ClauseVal<"taskgroup", 4, 1> {}
-def OMP_CANCELLATION_CONSTRUCT_None : ClauseVal<"none", 5, 0> {
+def OMP_CANCELLATION_CONSTRUCT_Parallel : EnumVal<"parallel", 1, 1> {}
+def OMP_CANCELLATION_CONSTRUCT_Loop : EnumVal<"loop", 2, 1> {}
+def OMP_CANCELLATION_CONSTRUCT_Sections : EnumVal<"sections", 3, 1> {}
+def OMP_CANCELLATION_CONSTRUCT_Taskgroup : EnumVal<"taskgroup", 4, 1> {}
+def OMP_CANCELLATION_CONSTRUCT_None : EnumVal<"none", 5, 0> {
   let isDefault = 1;
 }
 def OMPC_CancellationConstructType : Clause<"cancellation_construct_type"> {
@@ -210,8 +210,8 @@ def OMPC_From : Clause<"from"> {
 def OMPC_Full: Clause<"full"> {
   let clangClass = "OMPFullClause";
 }
-def OMP_GRAINSIZE_Strict : ClauseVal<"strict", 1, 1> {}
-def OMP_GRAINSIZE_Unknown : ClauseVal<"unknown", 2, 0> { let isDefault = 1; }
+def OMP_GRAINSIZE_Strict : EnumVal<"strict", 1, 1> {}
+def OMP_GRAINSIZE_Unknown : EnumVal<"unknown", 2, 0> { let isDefault = 1; }
 def OMPC_GrainSize : Clause<"grainsize"> {
   let clangClass = "OMPGrainsizeClause";
   let flangClass = "OmpGrainsizeClause";
@@ -278,12 +278,12 @@ def OMPC_Map : Clause<"map"> {
 def OMPC_Match : Clause<"match"> {
   let flangClass = "OmpMatchClause";
 }
-def OMP_MEMORY_ORDER_SeqCst : ClauseVal<"seq_cst", 1, 1> {}
-def OMP_MEMORY_ORDER_AcqRel : ClauseVal<"acq_rel", 2, 1> {}
-def OMP_MEMORY_ORDER_Acquire : ClauseVal<"acquire", 3, 1> {}
-def OMP_MEMORY_ORDER_Release : ClauseVal<"release", 4, 1> {}
-def OMP_MEMORY_ORDER_Relaxed : ClauseVal<"relaxed", 5, 1> {}
-def OMP_MEMORY_ORDER_Default : ClauseVal<"default", 6, 0> {
+def OMP_MEMORY_ORDER_SeqCst : EnumVal<"seq_cst", 1, 1> {}
+def OMP_MEMORY_ORDER_AcqRel : EnumVal<"acq_rel", 2, 1> {}
+def OMP_MEMORY_ORDER_Acquire : EnumVal<"acquire", 3, 1> {}
+def OMP_MEMORY_ORDER_Release : EnumVal<"release", 4, 1> {}
+def OMP_MEMORY_ORDER_Relaxed : EnumVal<"relaxed", 5, 1> {}
+def OMP_MEMORY_ORDER_Default : EnumVal<"default", 6, 0> {
   let isDefault = 1;
 }
 def OMPC_MemoryOrder : Clause<"memory_order"> {
@@ -337,8 +337,8 @@ def OMPC_Novariants : Clause<"novariants"> {
 def OMPC_NoWait : Clause<"nowait"> {
   let clangClass = "OMPNowaitClause";
 }
-def OMP_NUMTASKS_Strict : ClauseVal<"strict", 1, 1> {}
-def OMP_NUMTASKS_Unknown : ClauseVal<"unknown", 2, 0> { let isDefault = 1; }
+def OMP_NUMTASKS_Strict : EnumVal<"strict", 1, 1> {}
+def OMP_NUMTASKS_Unknown : EnumVal<"unknown", 2, 0> { let isDefault = 1; }
 def OMPC_NumTasks : Clause<"num_tasks"> {
   let clangClass = "OMPNumTasksClause";
   let flangClass = "OmpNumTasksClause";
@@ -366,8 +366,8 @@ def OMPC_OMPX_DynCGroupMem : Clause<"ompx_dyn_cgroup_mem"> {
   let clangClass = "OMPXDynCGroupMemClause";
   let flangClass = "ScalarIntExpr";
 }
-def OMP_ORDER_concurrent : ClauseVal<"concurrent",1,1> {}
-def OMP_ORDER_unknown : ClauseVal<"unknown",2,0> { let isDefault = 1; }
+def OMP_ORDER_concurrent : EnumVal<"concurrent",1,1> {}
+def OMP_ORDER_unknown : EnumVal<"unknown",2,0> { let isDefault = 1; }
 def OMPC_Order : Clause<"order"> {
   let clangClass = "OMPOrderClause";
   let flangClass = "OmpOrderClause";
@@ -404,12 +404,12 @@ def OMPC_Private : Clause<"private"> {
   let clangClass = "OMPPrivateClause";
   let flangClass = "OmpObjectList";
 }
-def OMP_PROC_BIND_master : ClauseVal<"master",2,1> {}
-def OMP_PROC_BIND_close : ClauseVal<"close",3,1> {}
-def OMP_PROC_BIND_spread : ClauseVal<"spread",4,1> {}
-def OMP_PROC_BIND_primary : ClauseVal<"primary",5,1> {}
-def OMP_PROC_BIND_default : ClauseVal<"default",6,0> {}
-def OMP_PROC_BIND_unknown : ClauseVal<"unknown",7,0> { let isDefault = true; }
+def OMP_PROC_BIND_master : EnumVal<"master",2,1> {}
+def OMP_PROC_BIND_close : EnumVal<"close",3,1> {}
+def OMP_PROC_BIND_spread : EnumVal<"spread",4,1> {}
+def OMP_PROC_BIND_primary : EnumVal<"primary",5,1> {}
+def OMP_PROC_BIND_default : EnumVal<"default",6,0> {}
+def OMP_PROC_BIND_unknown : EnumVal<"unknown",7,0> { let isDefault = true; }
 def OMPC_ProcBind : Clause<"proc_bind"> {
   let clangClass = "OMPProcBindClause";
   let flangClass = "OmpProcBindClause";
@@ -443,12 +443,12 @@ def OMPC_SafeLen : Clause<"safelen"> {
   let clangClass = "OMPSafelenClause";
   let flangClass = "ScalarIntConstantExpr";
 }
-def OMP_SCHEDULE_Static : ClauseVal<"static", 2, 1> {}
-def OMP_SCHEDULE_Dynamic : ClauseVal<"dynamic", 3, 1> {}
-def OMP_SCHEDULE_Guided : ClauseVal<"guided", 4, 1> {}
-def OMP_SCHEDULE_Auto : ClauseVal<"auto", 5, 1> {}
-def OMP_SCHEDULE_Runtime : ClauseVal<"runtime", 6, 1> {}
-def OMP_SCHEDULE_Default : ClauseVal<"default", 7, 0> { let isDefault = 1; }
+def OMP_SCHEDULE_Static : EnumVal<"static", 2, 1> {}
+def OMP_SCHEDULE_Dynamic : EnumVal<"dynamic", 3, 1> {}
+def OMP_SCHEDULE_Guided : EnumVal<"guided", 4, 1> {}
+def OMP_SCHEDULE_Auto : EnumVal<"auto", 5, 1> {}
+def OMP_SCHEDULE_Runtime : EnumVal<"runtime", 6, 1> {}
+def OMP_SCHEDULE_Default : EnumVal<"default", 7, 0> { let isDefault = 1; }
 def OMPC_Schedule : Clause<"schedule"> {
   let clangClass = "OMPScheduleClause";
   let flangClass = "OmpScheduleClause";
diff --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h b/llvm/include/llvm/TableGen/DirectiveEmitter.h
index 6defc8722d810..8615442ebff9f 100644
--- a/llvm/include/llvm/TableGen/DirectiveEmitter.h
+++ b/llvm/include/llvm/TableGen/DirectiveEmitter.h
@@ -308,9 +308,9 @@ class VersionedClause {
   const Record *Def;
 };
 
-class ClauseVal : public BaseRecord {
+class EnumVal : public BaseRecord {
 public:
-  ClauseVal(const Record *Def) : BaseRecord(Def) {}
+  EnumVal(const Record *Def) : BaseRecord(Def) {}
 
   int getValue() const { return Def->getValueAsInt("value"); }
 
diff --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td
index 3b2b4ca1b7031..74091edfa2a66 100644
--- a/llvm/test/TableGen/directive1.td
+++ b/llvm/test/TableGen/directive1.td
@@ -14,9 +14,9 @@ def TestDirectiveLanguage : DirectiveLanguage {
   let flangClauseBaseClass = "TdlClause";
 }
 
-def TDLCV_vala : ClauseVal<"vala",1,1> {}
-def TDLCV_valb : ClauseVal<"valb",2,1> {}
-def TDLCV_valc : ClauseVal<"valc",3,0> { let isDefault = 1; }
+def TDLCV_vala : EnumVal<"vala",1,1> {}
+def TDLCV_valb : EnumVal<"valb",2,1> {}
+def TDLCV_valc : EnumVal<"valc",3,0> { let isDefault = 1; }
 
 def TDLC_ClauseA : Clause<"clausea"> {
   let enumClauseValue = "AKind";
diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
index 3d1795c5a6ff5..f459e7c98ebc1 100644
--- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
@@ -147,7 +147,7 @@ static void generateEnumBitmask(ArrayRef<const Record *> Records,
 
 // Generate enums for values that clauses can take.
 // Also generate function declarations for get<Enum>Name(StringRef Str).
-static void generateEnumClauseVal(ArrayRef<const Record *> Records,
+static void generateClauseEnumVal(ArrayRef<const Record *> Records,
                                   raw_ostream &OS,
                                   const DirectiveLanguage &DirLang,
                                   std::string &EnumHelperFuncs) {
@@ -166,8 +166,8 @@ static void generateEnumClauseVal(ArrayRef<const Record *> Records,
 
     OS << "\n";
     OS << "enum class " << Enum << " {\n";
-    for (const ClauseVal CVal : ClauseVals)
-      OS << "  " << CVal.getRecordName() << "=" << CVal.getValue() << ",\n";
+    for (const EnumVal Val : ClauseVals)
+      OS << "  " << Val.getRecordName() << "=" << Val.getValue() << ",\n";
     OS << "};\n";
 
     if (DirLang.hasMakeEnumAvailableInNamespace()) {
@@ -306,9 +306,9 @@ static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) {
                     DirLang.getClausePrefix(),
                     DirLang.hasMakeEnumAvailableInNamespace());
 
-  // Emit ClauseVal enumeration
+  // Emit ClauseVals enumeration
   std::string EnumHelperFuncs;
-  generateEnumClauseVal(DirLang.getClauses(), OS, DirLang, EnumHelperFuncs);
+  generateClauseEnumVal(DirLang.getClauses(), OS, DirLang, EnumHelperFuncs);
 
   // Generic function signatures
   OS << "\n";
@@ -412,9 +412,11 @@ static void generateGetKind(ArrayRef<const Record *> Records, raw_ostream &OS,
   OS << "}\n";
 }
 
-// Generate function implementation for get<ClauseVal>Kind(StringRef Str)
-static void generateGetKindClauseVal(const DirectiveLanguage &DirLang,
-                                     raw_ostream &OS) {
+// Generate function implementations for
+//   <enumClauseValue> get<enumClauseValue>(StringRef Str) and
+//   StringRef get<enumClauseValue>Name(<enumClauseValue>)
+static void generateGetClauseVal(const DirectiveLanguage &DirLang,
+                                 raw_ostream &OS) {
   StringRef Lang = DirLang.getName();
   std::string Qual = getQualifier(DirLang);
 
@@ -445,10 +447,9 @@ static void generateGetKindClauseVal(const DirectiveLanguage &DirLang,
     OS << Qual << Enum << " " << Qual << "get" << Enum
        << "(llvm::StringRef Str) {\n";
     OS << "  return StringSwitch<" << Enum << ">(Str)\n";
-    for (const auto &CV : ClauseVals) {
-      ClauseVal CVal(CV);
-      OS << "    .Case(\"" << CVal.getFormattedName() << "\"," << CV->getName()
-         << ")\n";
+    for (const EnumVal Val : ClauseVals) {
+      OS << "    .Case(\"" << Val.getFormattedName() << "\","
+         << Val.getRecordName() << ")\n";
     }
     OS << "    .Default(" << DefaultName << ");\n";
     OS << "}\n";
@@ -457,10 +458,9 @@ static void generateGetKindClauseVal(const DirectiveLanguage &DirLang,
     OS << "llvm::StringRef " << Qual << "get" << Lang << Enum << "Name(" << Qual
        << Enum << " x) {\n";
     OS << "  switch (x) {\n";
-    for (const auto &CV : ClauseVals) {
-      ClauseVal CVal(CV);
-      OS << "    case " << CV->getName() << ":\n";
-      OS << "      return \"" << CVal.getFormattedName() << "\";\n";
+    for (const EnumVal Val : ClauseVals) {
+      OS << "    case " << Val.getRecordName() << ":\n";
+      OS << "      return \"" << Val.getFormattedName() << "\";\n";
     }
     OS << "  }\n"; // switch
     OS << "  llvm_unreachable(\"Invalid " << Lang << " " << Enum
@@ -1318,8 +1318,9 @@ void emitDirectivesBasicImpl(const DirectiveLanguage &DirLang,
   // getClauseName(Clause Kind)
   generateGetName(DirLang.getClauses(), OS, "Clause", DirLang, CPrefix);
 
-  // get<ClauseVal>Kind(StringRef Str)
-  generateGetKindClauseVal(DirLang, OS);
+  // <enumClauseValue> get<enumClauseValue>(StringRef Str) ; string -> value
+  // StringRef get<enumClauseValue>Name(<enumClauseValue>) ; value -> string
+  generateGetClauseVal(DirLang, OS);
 
   // isAllowedClauseForDirective(Directive D, Clause C, unsigned Version)
   generateIsAllowedClause(DirLang, OS);
diff --git a/mlir/test/mlir-tblgen/directive-common.td b/mlir/test/mlir-tblgen/directive-common.td
index 9429238a03f07..54e0d14ca83dd 100644
--- a/mlir/test/mlir-tblgen/directive-common.td
+++ b/mlir/test/mlir-tblgen/directive-common.td
@@ -7,9 +7,9 @@ def TestDirectiveLanguage : DirectiveLanguage {
   let cppNamespace = "tdl";
 }
 
-def TDLCV_vala : ClauseVal<"vala",1,1> {}
-def TDLCV_valb : ClauseVal<"valb",2,1> {}
-def TDLCV_valc : ClauseVal<"valc",3,0> { let isDefault = 1; }
+def TDLCV_vala : EnumVal<"vala",1,1> {}
+def TDLCV_valb : EnumVal<"valb",2,1> {}
+def TDLCV_valc : EnumVal<"valc",3,0> { let isDefault = 1; }
 
 def TDLC_ClauseA : Clause<"clausea"> {
   let flangClass = "TdlClauseA";
diff --git a/mlir/tools/mlir-tblgen/DirectiveCommonGen.cpp b/mlir/tools/mlir-tblgen/DirectiveCommonGen.cpp
index 602b3df3094af..09e1bdafe7bc9 100644
--- a/mlir/tools/mlir-tblgen/DirectiveCommonGen.cpp
+++ b/mlir/tools/mlir-tblgen/DirectiveCommonGen.cpp
@@ -21,7 +21,7 @@
 #include "llvm/TableGen/Record.h"
 
 using llvm::Clause;
-using llvm::ClauseVal;
+using llvm::EnumVal;
 using llvm::raw_ostream;
 using llvm::RecordKeeper;
 
@@ -63,11 +63,11 @@ static bool emitDecls(const RecordKeeper &records, llvm::StringRef dialect,
 
     std::vector<std::string> cvDefs;
     for (const auto &it : llvm::enumerate(clauseVals)) {
-      const ClauseVal cval{it.value()};
-      if (!cval.isUserVisible())
+      const EnumVal val{it.value()};
+      if (!val.isUserVisible())
         continue;
 
-      std::string name = cval.getFormattedName();
+      std::string name = val.getFormattedName();
       std::string enumValName(name.length(), ' ');
       llvm::transform(name, enumValName.begin(), llvm::toLower);
       enumValName[0] = llvm::toUpper(enumValName[0]);

>From 78d1f1b2344ab48902b44afd7fb84649b46d6749 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 21 May 2025 11:26:33 -0500
Subject: [PATCH 2/7] [utils][TableGen] Unify converting names to upper-camel
 case

There were 3 different functions in DirectiveEmitter.cpp doing essentially
the same thing: taking a name separated with _ or whitepace, and converting
it to the upper-camel case. Extract that into a single function that can
handle different sets of separators.
---
 llvm/include/llvm/TableGen/DirectiveEmitter.h | 76 ++++++++-----------
 .../utils/TableGen/Basic/DirectiveEmitter.cpp |  2 +-
 2 files changed, 33 insertions(+), 45 deletions(-)

diff --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h b/llvm/include/llvm/TableGen/DirectiveEmitter.h
index 8615442ebff9f..48e18de0904c0 100644
--- a/llvm/include/llvm/TableGen/DirectiveEmitter.h
+++ b/llvm/include/llvm/TableGen/DirectiveEmitter.h
@@ -113,14 +113,39 @@ class BaseRecord {
 
   // Returns the name of the directive formatted for output. Whitespace are
   // replaced with underscores.
-  static std::string formatName(StringRef Name) {
+  static std::string getSnakeName(StringRef Name) {
     std::string N = Name.str();
     llvm::replace(N, ' ', '_');
     return N;
   }
 
+  static std::string getUpperCamelName(StringRef Name, StringRef Sep) {
+    std::string Camel = Name.str();
+    // Convert to uppercase
+    bool Cap = true;
+    llvm::transform(Camel, Camel.begin(), [&](unsigned char C) {
+      if (Sep.contains(C)) {
+        assert(!Cap && "No initial or repeated separators");
+        Cap = true;
+      } else if (Cap) {
+        C = llvm::toUpper(C);
+        Cap = false;
+      }
+      return C;
+    });
+    size_t Out = 0;
+    // Remove separators
+    for (size_t In = 0, End = Camel.size(); In != End; ++In) {
+      unsigned char C = Camel[In];
+      if (!Sep.contains(C))
+        Camel[Out++] = C;
+    }
+    Camel.resize(Out);
+    return Camel;
+  }
+
   std::string getFormattedName() const {
-    return formatName(Def->getValueAsString("name"));
+    return getSnakeName(Def->getValueAsString("name"));
   }
 
   bool isDefault() const { return Def->getValueAsBit("isDefault"); }
@@ -172,26 +197,13 @@ class Directive : public BaseRecord {
 
   // Clang uses a different format for names of its directives enum.
   std::string getClangAccSpelling() const {
-    std::string Name = Def->getValueAsString("name").str();
+    StringRef Name = Def->getValueAsString("name");
 
     // Clang calls the 'unknown' value 'invalid'.
     if (Name == "unknown")
       return "Invalid";
 
-    // Clang entries all start with a capital letter, so apply that.
-    Name[0] = std::toupper(Name[0]);
-    // Additionally, spaces/underscores are handled by capitalizing the next
-    // letter of the name and removing the space/underscore.
-    for (unsigned I = 0; I < Name.size(); ++I) {
-      if (Name[I] == ' ' || Name[I] == '_') {
-        Name.erase(I, 1);
-        assert(Name[I] != ' ' && Name[I] != '_' &&
-               "No double spaces/underscores");
-        Name[I] = std::toupper(Name[I]);
-      }
-    }
-
-    return Name;
+    return BaseRecord::getUpperCamelName(Name, " _");
   }
 };
 
@@ -218,19 +230,7 @@ class Clause : public BaseRecord {
   //     num_threads -> NumThreads
   std::string getFormattedParserClassName() const {
     StringRef Name = Def->getValueAsString("name");
-    std::string N = Name.str();
-    bool Cap = true;
-    llvm::transform(N, N.begin(), [&Cap](unsigned char C) {
-      if (Cap == true) {
-        C = toUpper(C);
-        Cap = false;
-      } else if (C == '_') {
-        Cap = true;
-      }
-      return C;
-    });
-    erase(N, '_');
-    return N;
+    return BaseRecord::getUpperCamelName(Name, "_");
   }
 
   // Clang uses a different format for names of its clause enum, which can be
@@ -241,20 +241,8 @@ class Clause : public BaseRecord {
         !ClangSpelling.empty())
       return ClangSpelling.str();
 
-    std::string Name = Def->getValueAsString("name").str();
-    // Clang entries all start with a capital letter, so apply that.
-    Name[0] = std::toupper(Name[0]);
-    // Additionally, underscores are handled by capitalizing the next letter of
-    // the name and removing the underscore.
-    for (unsigned I = 0; I < Name.size(); ++I) {
-      if (Name[I] == '_') {
-        Name.erase(I, 1);
-        assert(Name[I] != '_' && "No double underscores");
-        Name[I] = std::toupper(Name[I]);
-      }
-    }
-
-    return Name;
+    StringRef Name = Def->getValueAsString("name");
+    return BaseRecord::getUpperCamelName(Name, "_");
   }
 
   // Optional field.
diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
index f459e7c98ebc1..9e79a83ed6e18 100644
--- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
@@ -839,7 +839,7 @@ static void generateGetDirectiveLanguages(const DirectiveLanguage &DirLang,
         D.getSourceLanguages(), OS,
         [&](const Record *L) {
           StringRef N = L->getValueAsString("name");
-          OS << "SourceLanguage::" << BaseRecord::formatName(N);
+          OS << "SourceLanguage::" << BaseRecord::getSnakeName(N);
         },
         " | ");
     OS << ";\n";

>From e7d2e0b40eae0bf37f76d0aa8a59520b529c760c Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 21 May 2025 14:23:38 -0500
Subject: [PATCH 3/7] [utils][TableGen] Treat clause aliases equally with names

The code in DirectiveEmitter that generates clause parsers sorted
clause names to ensure that longer names were tried before shorter
ones, in cases where a shorter name may be a prefix of a longer one.
This matters in the strict Fortran source format, since whitespace
is ignored there.

This sorting did not take into account clause aliases, which are
just alternative names. These extra names were not protected in the
same way, and were just appended immediately after the primary name.

This patch generates a list of pairs Record+Name, where a given
record can appear multiple times with different names. Sort that
list and use it to generate parsers for each record.
What used to be
```
  ("fred" || "f") >> construct<SomeClause>{} ||
  "foo" << construct<OtherClause>{}
```
is now
```
  "fred" >> construct<SomeClause>{} ||
  "foo" >> construct<OtherClause>{} ||
  "f" >> construct<SomeClause>{}
```
---
 llvm/test/TableGen/directive1.td              |  4 +-
 .../utils/TableGen/Basic/DirectiveEmitter.cpp | 75 ++++++++++---------
 2 files changed, 42 insertions(+), 37 deletions(-)

diff --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td
index 74091edfa2a66..f756f54c03bfb 100644
--- a/llvm/test/TableGen/directive1.td
+++ b/llvm/test/TableGen/directive1.td
@@ -34,6 +34,7 @@ def TDLC_ClauseB : Clause<"clauseb"> {
 }
 
 def TDLC_ClauseC : Clause<"clausec"> {
+  let aliases = ["ccccccc"];
   let flangClass = "IntExpr";
   let isValueList = 1;
 }
@@ -260,7 +261,8 @@ def TDL_DirA : Directive<"dira"> {
 // IMPL-NEXT:  TYPE_PARSER(
 // IMPL-NEXT:    "clausec" >> construct<TdlClause>(construct<TdlClause::Clausec>(parenthesized(nonemptyList(Parser<IntExpr>{})))) || 
 // IMPL-NEXT:    "clauseb" >> construct<TdlClause>(construct<TdlClause::Clauseb>(maybe(parenthesized(Parser<IntExpr>{})))) ||
-// IMPL-NEXT:    "clausea" >> construct<TdlClause>(construct<TdlClause::Clausea>())
+// IMPL-NEXT:    "clausea" >> construct<TdlClause>(construct<TdlClause::Clausea>()) ||
+// IMPL-NEXT:    "ccccccc" >> construct<TdlClause>(construct<TdlClause::Clausec>(parenthesized(nonemptyList(Parser<IntExpr>{}))))
 // IMPL-NEXT:  )
 // IMPL-EMPTY:
 // IMPL-NEXT:  #endif // GEN_FLANG_CLAUSES_PARSER
diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
index 9e79a83ed6e18..bd6c543e1741a 100644
--- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
@@ -608,7 +608,7 @@ static void emitLeafTable(const DirectiveLanguage &DirLang, raw_ostream &OS,
   std::vector<int> Ordering(Directives.size());
   std::iota(Ordering.begin(), Ordering.end(), 0);
 
-  sort(Ordering, [&](int A, int B) {
+  llvm::sort(Ordering, [&](int A, int B) {
     auto &LeavesA = LeafTable[A];
     auto &LeavesB = LeafTable[B];
     int DirA = LeavesA[0], DirB = LeavesB[0];
@@ -1113,59 +1113,63 @@ static void generateFlangClauseParserKindMap(const DirectiveLanguage &DirLang,
      << " Parser clause\");\n";
 }
 
-static bool compareClauseName(const Record *R1, const Record *R2) {
-  Clause C1(R1);
-  Clause C2(R2);
-  return (C1.getName() > C2.getName());
+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);
+    List.push_back(std::make_pair(R, C.getName()));
+    llvm::transform(C.getAliases(), std::back_inserter(List),
+                    [R](StringRef S) { return std::make_pair(R, S); });
+  }
+  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 reverse alphabetical order so with clauses with same
-  // beginning, the longer option is tried before.
-  sort(Clauses, compareClauseName);
+  // 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);
   IfDefScope Scope("GEN_FLANG_CLAUSES_PARSER", OS);
   StringRef Base = DirLang.getFlangClauseBaseClass();
 
+  unsigned LastIndex = Names.size() - 1;
   OS << "\n";
-  unsigned Index = 0;
-  unsigned LastClauseIndex = Clauses.size() - 1;
   OS << "TYPE_PARSER(\n";
-  for (const Clause Clause : Clauses) {
-    const std::vector<StringRef> &Aliases = Clause.getAliases();
-    if (Aliases.empty()) {
-      OS << "  \"" << Clause.getName() << "\"";
-    } else {
-      OS << "  ("
-         << "\"" << Clause.getName() << "\"_tok";
-      for (StringRef Alias : Aliases) {
-        OS << " || \"" << Alias << "\"_tok";
-      }
-      OS << ")";
-    }
+  for (auto [Index, RecTxt] : llvm::enumerate(Names)) {
+    auto [R, N] = RecTxt;
+    Clause C(R);
 
-    StringRef FlangClass = Clause.getFlangClass();
-    OS << " >> construct<" << Base << ">(construct<" << Base
-       << "::" << Clause.getFormattedParserClassName() << ">(";
+    StringRef FlangClass = C.getFlangClass();
+    OS << "  \"" << N << "\" >> construct<" << Base << ">(construct<" << Base
+       << "::" << C.getFormattedParserClassName() << ">(";
     if (FlangClass.empty()) {
       OS << "))";
-      if (Index != LastClauseIndex)
+      if (Index != LastIndex)
         OS << " ||";
       OS << "\n";
-      ++Index;
       continue;
     }
 
-    if (Clause.isValueOptional())
+    if (C.isValueOptional())
       OS << "maybe(";
     OS << "parenthesized(";
-    if (Clause.isValueList())
+    if (C.isValueList())
       OS << "nonemptyList(";
 
-    if (!Clause.getPrefix().empty())
-      OS << "\"" << Clause.getPrefix() << ":\" >> ";
+    if (!C.getPrefix().empty())
+      OS << "\"" << C.getPrefix() << ":\" >> ";
 
     // The common Flang parser are used directly. Their name is identical to
     // the Flang class with first letter as lowercase. If the Flang class is
@@ -1181,19 +1185,18 @@ static void generateFlangClausesParser(const DirectiveLanguage &DirLang,
             .Case("ScalarLogicalExpr", "scalarLogicalExpr")
             .Default(("Parser<" + FlangClass + ">{}").toStringRef(Scratch));
     OS << Parser;
-    if (!Clause.getPrefix().empty() && Clause.isPrefixOptional())
+    if (!C.getPrefix().empty() && C.isPrefixOptional())
       OS << " || " << Parser;
-    if (Clause.isValueList()) // close nonemptyList(.
+    if (C.isValueList()) // close nonemptyList(.
       OS << ")";
     OS << ")"; // close parenthesized(.
 
-    if (Clause.isValueOptional()) // close maybe(.
+    if (C.isValueOptional()) // close maybe(.
       OS << ")";
     OS << "))";
-    if (Index != LastClauseIndex)
+    if (Index != LastIndex)
       OS << " ||";
     OS << "\n";
-    ++Index;
   }
   OS << ")\n";
 }

>From ba3c9184105aa18126024291dbe6401d691c3d14 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Tue, 20 May 2025 14:07:47 -0500
Subject: [PATCH 4/7] [utils][TableGen] Implement clause aliases as alternative
 spellings

Use the spellings in the generated clause parser. The functions
`get<lang>ClauseKind` and `get<lang>ClauseName` are not yet updated.

The definitions of both clauses and directives now take a list of
"Spelling"s instead of a single string. For example
```
def ACCC_Copyin : Clause<[Spelling<"copyin">,
                          Spelling<"present_or_copyin">,
                          Spelling<"pcopyin">]> { ... }
```

A "Spelling" is a versioned string, defaulting to "all versions".

For background information see
https://discourse.llvm.org/t/rfc-alternative-spellings-of-openmp-directives/85507
---
 .../llvm/Frontend/Directive/DirectiveBase.td  |  41 +-
 llvm/include/llvm/Frontend/OpenACC/ACC.td     | 146 +++---
 llvm/include/llvm/Frontend/OpenMP/OMP.td      | 496 +++++++++---------
 llvm/include/llvm/TableGen/DirectiveEmitter.h |  85 ++-
 llvm/test/TableGen/directive1.td              |  60 ++-
 llvm/test/TableGen/directive2.td              |  63 ++-
 llvm/test/TableGen/directive3.td              |  10 +-
 .../utils/TableGen/Basic/DirectiveEmitter.cpp | 115 ++--
 mlir/test/mlir-tblgen/directive-common.td     |   2 +-
 9 files changed, 582 insertions(+), 436 deletions(-)

diff --git a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
index 582da20083aee..142ba0423f251 100644
--- a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
+++ b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
@@ -51,6 +51,20 @@ class DirectiveLanguage {
   string flangClauseBaseClass = "";
 }
 
+// Base class for versioned entities.
+class Versioned<int min = 1, int max = 0x7FFFFFFF> {
+  // Mininum version number where this object is valid.
+  int minVersion = min;
+
+  // Maximum version number where this object is valid.
+  int maxVersion = max;
+}
+
+class Spelling<string s, int min = 1, int max = 0x7FFFFFFF>
+    : Versioned<min, max> {
+  string spelling = s;
+}
+
 // Some clauses take an argument from a predefined list of allowed keyword
 // values. For example, assume a clause "someclause" with an argument from
 // the list "foo", "bar", "baz". In the user source code this would look
@@ -81,12 +95,9 @@ class EnumVal<string n, int v, bit uv> {
 }
 
 // Information about a specific clause.
-class Clause<string c> {
-  // Name of the clause.
-  string name = c;
-
-  // Define aliases used in the parser.
-  list<string> aliases = [];
+class Clause<list<Spelling> ss> {
+  // Spellings of the clause.
+  list<Spelling> spellings = ss;
 
   // Optional class holding value of the clause in clang AST.
   string clangClass = "";
@@ -134,15 +145,9 @@ class Clause<string c> {
 }
 
 // Hold information about clause validity by version.
-class VersionedClause<Clause c, int min = 1, int max = 0x7FFFFFFF> {
-  // Actual clause.
+class VersionedClause<Clause c, int min = 1, int max = 0x7FFFFFFF>
+    : Versioned<min, max> {
   Clause clause = c;
-
-  // Mininum version number where this clause is valid.
-  int minVersion = min;
-
-  // Maximum version number where this clause is valid.
-  int maxVersion = max;
 }
 
 // Kinds of directive associations.
@@ -190,15 +195,15 @@ class SourceLanguage<string n> {
   string name = n;  // Name of the enum value in enum class Association.
 }
 
-// The C languages also implies C++ until there is a reason to add C++
+// The C language also implies C++ until there is a reason to add C++
 // separately.
 def L_C : SourceLanguage<"C"> {}
 def L_Fortran : SourceLanguage<"Fortran"> {}
 
 // Information about a specific directive.
-class Directive<string d> {
-  // Name of the directive. Can be composite directive sepearted by whitespace.
-  string name = d;
+class Directive<list<Spelling> ss> {
+  // Spellings of the directive.
+  list<Spelling> spellings = ss;
 
   // Clauses cannot appear twice in the three allowed lists below. Also, since
   // required implies allowed, the same clause cannot appear in both the
diff --git a/llvm/include/llvm/Frontend/OpenACC/ACC.td b/llvm/include/llvm/Frontend/OpenACC/ACC.td
index b74cd6e5642ec..65751839ceb09 100644
--- a/llvm/include/llvm/Frontend/OpenACC/ACC.td
+++ b/llvm/include/llvm/Frontend/OpenACC/ACC.td
@@ -32,64 +32,65 @@ def OpenACC : DirectiveLanguage {
 //===----------------------------------------------------------------------===//
 
 // 2.16.1
-def ACCC_Async : Clause<"async"> {
+def ACCC_Async : Clause<[Spelling<"async">]> {
   let flangClass = "ScalarIntExpr";
   let isValueOptional = true;
 }
 
 // 2.9.7
-def ACCC_Auto : Clause<"auto"> {}
+def ACCC_Auto : Clause<[Spelling<"auto">]> {}
 
 // 2.7.12
-def ACCC_Attach : Clause<"attach"> {
+def ACCC_Attach : Clause<[Spelling<"attach">]> {
   let flangClass = "AccObjectList";
 }
 
 // 2.15.1
-def ACCC_Bind : Clause<"bind"> {
+def ACCC_Bind : Clause<[Spelling<"bind">]> {
   let flangClass = "AccBindClause";
 }
 
 // 2.12
-def ACCC_Capture : Clause<"capture"> {
+def ACCC_Capture : Clause<[Spelling<"capture">]> {
 }
 
 // 2.9.1
-def ACCC_Collapse : Clause<"collapse"> {
+def ACCC_Collapse : Clause<[Spelling<"collapse">]> {
   let flangClass = "AccCollapseArg";
 }
 
 // 2.7.6
-def ACCC_Copy : Clause<"copy"> {
+def ACCC_Copy
+    : Clause<[Spelling<"copy">, Spelling<"present_or_copy">,
+              Spelling<"pcopy">]> {
   let flangClass = "AccObjectList";
-  let aliases = ["present_or_copy", "pcopy"];
 }
 
 // 2.7.7
-def ACCC_Copyin : Clause<"copyin"> {
+def ACCC_Copyin : Clause<[Spelling<"copyin">, Spelling<"present_or_copyin">,
+                          Spelling<"pcopyin">]> {
   let flangClass = "AccObjectListWithModifier";
   let clangAccSpelling = "CopyIn";
-  let aliases = ["present_or_copyin", "pcopyin"];
 }
 
 // 2.7.8
-def ACCC_Copyout : Clause<"copyout"> {
+def ACCC_Copyout : Clause<[Spelling<"copyout">, Spelling<"present_or_copyout">,
+                           Spelling<"pcopyout">]> {
   let flangClass = "AccObjectListWithModifier";
   let clangAccSpelling = "CopyOut";
-  let aliases = ["present_or_copyout", "pcopyout"];
 }
 
 // 2.7.9
-def ACCC_Create : Clause<"create"> {
+def ACCC_Create : Clause<[Spelling<"create">, Spelling<"present_or_create">,
+                          Spelling<"pcreate">]> {
   let flangClass = "AccObjectListWithModifier";
-  let aliases = ["present_or_create", "pcreate"];
 }
 
 // 2.5.16
 def ACC_Default_none : EnumVal<"none", 1, 1> { let isDefault = 1; }
 def ACC_Default_present : EnumVal<"present", 0, 1> {}
 
-def ACCC_Default : Clause<"default"> {
+def ACCC_Default : Clause<[Spelling<"default">]> {
   let flangClass = "AccDefaultClause";
   let enumClauseValue = "DefaultValue";
   let allowedClauseValues = [
@@ -99,174 +100,173 @@ def ACCC_Default : Clause<"default"> {
 }
 
 // 2.14.3
-def ACCC_DefaultAsync : Clause<"default_async"> {
+def ACCC_DefaultAsync : Clause<[Spelling<"default_async">]> {
   let flangClass = "ScalarIntExpr";
 }
 
 // 2.7.11
-def ACCC_Delete : Clause<"delete"> {
+def ACCC_Delete : Clause<[Spelling<"delete">]> {
   let flangClass = "AccObjectList";
 }
 
 // 2.7.13
-def ACCC_Detach : Clause<"detach"> {
+def ACCC_Detach : Clause<[Spelling<"detach">]> {
   let flangClass = "AccObjectList";
 }
 
 // 2.14.4
-def ACCC_Device : Clause<"device"> {
+def ACCC_Device : Clause<[Spelling<"device">]> {
   let flangClass = "AccObjectList";
 }
 
 // 2.14.1 - 2.14.2
-def ACCC_DeviceNum : Clause<"device_num">  {
+def ACCC_DeviceNum : Clause<[Spelling<"device_num">]>  {
   let flangClass = "ScalarIntExpr";
 }
 
 // 2.7.4
-def ACCC_DevicePtr : Clause<"deviceptr"> {
+def ACCC_DevicePtr : Clause<[Spelling<"deviceptr">]> {
   let flangClass = "AccObjectList";
   let clangAccSpelling = "DevicePtr";
 }
 
 // 2.13.1
-def ACCC_DeviceResident : Clause<"device_resident"> {
+def ACCC_DeviceResident : Clause<[Spelling<"device_resident">]> {
   let flangClass = "AccObjectList";
 }
 
 // 2.4
-def ACCC_DeviceType : Clause<"device_type"> {
+def ACCC_DeviceType : Clause<[Spelling<"device_type">, Spelling<"dtype">]> {
   let flangClass = "AccDeviceTypeExprList";
   let defaultValue = "*";
-  let aliases = ["dtype"];
 }
 
 // 2.6.6
-def ACCC_Finalize : Clause<"finalize"> {}
+def ACCC_Finalize : Clause<[Spelling<"finalize">]> {}
 
 // 2.5.14
-def ACCC_FirstPrivate : Clause<"firstprivate"> {
+def ACCC_FirstPrivate : Clause<[Spelling<"firstprivate">]> {
   let flangClass = "AccObjectList";
   let clangAccSpelling = "FirstPrivate";
 }
 
 // 2.9.2
-def ACCC_Gang : Clause<"gang"> {
+def ACCC_Gang : Clause<[Spelling<"gang">]> {
   let flangClass = "AccGangArgList";
   let isValueOptional = true;
 }
 
 // 2.14.4
-def ACCC_Host : Clause<"host"> {
+def ACCC_Host : Clause<[Spelling<"host">]> {
   let flangClass = "AccObjectList";
 }
 
 // 2.5.6
-def ACCC_If : Clause <"if"> {
+def ACCC_If : Clause<[Spelling<"if">]> {
   let flangClass = "ScalarExpr";
 }
 
 // 2.14.4
-def ACCC_IfPresent : Clause<"if_present"> {}
+def ACCC_IfPresent : Clause<[Spelling<"if_present">]> {}
 
 // 2.9.6
-def ACCC_Independent : Clause<"independent"> {}
+def ACCC_Independent : Clause<[Spelling<"independent">]> {}
 
 // 2.13.3
-def ACCC_Link : Clause<"link"> {
+def ACCC_Link : Clause<[Spelling<"link">]> {
   let flangClass = "AccObjectList";
 }
 
 // 2.7.10
-def ACCC_NoCreate : Clause<"no_create"> {
+def ACCC_NoCreate : Clause<[Spelling<"no_create">]> {
   let flangClass = "AccObjectList";
 }
 
 // 2.15.1
-def ACCC_NoHost : Clause<"nohost"> { 
-  let clangAccSpelling = "NoHost"; 
+def ACCC_NoHost : Clause<[Spelling<"nohost">]> {
+  let clangAccSpelling = "NoHost";
 }
 
 // 2.5.10
-def ACCC_NumGangs : Clause<"num_gangs"> {
+def ACCC_NumGangs : Clause<[Spelling<"num_gangs">]> {
   let flangClass = "ScalarIntExpr";
   let isValueList = 1;
 }
 
 // 2.5.11
-def ACCC_NumWorkers : Clause<"num_workers"> {
+def ACCC_NumWorkers : Clause<[Spelling<"num_workers">]> {
   let flangClass = "ScalarIntExpr";
 }
 
 // 2.7.5
-def ACCC_Present : Clause<"present"> {
+def ACCC_Present : Clause<[Spelling<"present">]> {
   let flangClass = "AccObjectList";
 }
 
 // 2.5.13
-def ACCC_Private : Clause<"private"> {
+def ACCC_Private : Clause<[Spelling<"private">]> {
   let flangClass = "AccObjectList";
 }
 
 // 2.9.8
-def ACCC_Tile : Clause <"tile"> {
+def ACCC_Tile : Clause<[Spelling<"tile">]> {
   let flangClass = "AccTileExprList";
 }
 
 // 2.8.1
-def ACCC_UseDevice : Clause <"use_device"> {
+def ACCC_UseDevice : Clause<[Spelling<"use_device">]> {
   let flangClass = "AccObjectList";
 }
 
 // 2.12
-def ACCC_Read : Clause<"read"> {}
+def ACCC_Read : Clause<[Spelling<"read">]> {}
 
 // 2.5.15
-def ACCC_Reduction : Clause<"reduction"> {
+def ACCC_Reduction : Clause<[Spelling<"reduction">]> {
   let flangClass = "AccObjectListWithReduction";
 }
 
 // 2.5.7
-def ACCC_Self : Clause<"self"> {
+def ACCC_Self : Clause<[Spelling<"self">]> {
   let flangClass = "AccSelfClause";
   let isValueOptional = true;
 }
 
 // 2.9.5
-def ACCC_Seq : Clause<"seq"> {}
+def ACCC_Seq : Clause<[Spelling<"seq">]> {}
 
 // Non-standard extension
-def ACCC_ShortLoop : Clause<"shortloop"> {}
+def ACCC_ShortLoop : Clause<[Spelling<"shortloop">]> {}
 
 // 2.9.4
-def ACCC_Vector : Clause<"vector"> {
+def ACCC_Vector : Clause<[Spelling<"vector">]> {
   let flangClass = "ScalarIntExpr";
   let isValueOptional = true;
   let prefix = "length";
 }
 
 // 2.5.12
-def ACCC_VectorLength : Clause<"vector_length"> {
+def ACCC_VectorLength : Clause<[Spelling<"vector_length">]> {
   let flangClass = "ScalarIntExpr";
 }
 
 // 2.16.2
-def ACCC_Wait : Clause<"wait"> {
+def ACCC_Wait : Clause<[Spelling<"wait">]> {
   let flangClass = "AccWaitArgument";
   let isValueOptional = true;
 }
 
 // 2.9.3
-def ACCC_Worker: Clause<"worker"> {
+def ACCC_Worker: Clause<[Spelling<"worker">]> {
   let flangClass = "ScalarIntExpr";
   let isValueOptional = true;
   let prefix = "num";
 }
 
 // 2.12
-def ACCC_Write : Clause<"write"> {}
+def ACCC_Write : Clause<[Spelling<"write">]> {}
 
-def ACCC_Unknown : Clause<"unknown"> {
+def ACCC_Unknown : Clause<[Spelling<"unknown">]> {
   let isDefault = true;
 }
 
@@ -275,14 +275,14 @@ def ACCC_Unknown : Clause<"unknown"> {
 //===----------------------------------------------------------------------===//
 
 // 2.12
-def ACC_Atomic : Directive<"atomic"> {
+def ACC_Atomic : Directive<[Spelling<"atomic">]> {
   let allowedOnceClauses = [VersionedClause<ACCC_If, 34>];
   let association = AS_Block;
   let category = CA_Executable;
 }
 
 // 2.6.5
-def ACC_Data : Directive<"data"> {
+def ACC_Data : Directive<[Spelling<"data">]> {
   let allowedOnceClauses = [
     VersionedClause<ACCC_If>,
     VersionedClause<ACCC_Default>
@@ -308,7 +308,7 @@ def ACC_Data : Directive<"data"> {
 }
 
 // 2.13
-def ACC_Declare : Directive<"declare"> {
+def ACC_Declare : Directive<[Spelling<"declare">]> {
   let allowedClauses = [
     VersionedClause<ACCC_Copy>,
     VersionedClause<ACCC_Copyin>,
@@ -324,7 +324,7 @@ def ACC_Declare : Directive<"declare"> {
 }
 
 // 2.5.3
-def ACC_Kernels : Directive<"kernels"> {
+def ACC_Kernels : Directive<[Spelling<"kernels">]> {
   let allowedClauses = [VersionedClause<ACCC_Async>,
                         VersionedClause<ACCC_Attach>,
                         VersionedClause<ACCC_Copy>,
@@ -347,7 +347,7 @@ def ACC_Kernels : Directive<"kernels"> {
 }
 
 // 2.5.1
-def ACC_Parallel : Directive<"parallel"> {
+def ACC_Parallel : Directive<[Spelling<"parallel">]> {
   let allowedClauses = [
     VersionedClause<ACCC_Attach>,
     VersionedClause<ACCC_Async>,
@@ -377,7 +377,7 @@ def ACC_Parallel : Directive<"parallel"> {
 }
 
 // 2.5.2
-def ACC_Serial : Directive<"serial"> {
+def ACC_Serial : Directive<[Spelling<"serial">]> {
   // Spec line 950-951: clause is as for the parallel construct except that the
   // num_gangs, num_workers, and vector_length clauses are not permitted.
   let allowedClauses = [VersionedClause<ACCC_Async>,
@@ -402,7 +402,7 @@ def ACC_Serial : Directive<"serial"> {
 }
 
 // 2.9
-def ACC_Loop : Directive<"loop"> {
+def ACC_Loop : Directive<[Spelling<"loop">]> {
   let allowedClauses = [
     VersionedClause<ACCC_DeviceType>,
     VersionedClause<ACCC_Private>,
@@ -424,13 +424,13 @@ def ACC_Loop : Directive<"loop"> {
 }
 
 // 2.10
-def ACC_Cache : Directive<"cache"> {
+def ACC_Cache : Directive<[Spelling<"cache">]> {
   let association = AS_None;
   let category = CA_Executable;
 }
 
 // 2.14.1
-def ACC_Init : Directive<"init"> {
+def ACC_Init : Directive<[Spelling<"init">]> {
   let allowedOnceClauses = [VersionedClause<ACCC_DeviceNum>,
                             VersionedClause<ACCC_If>];
   let allowedClauses = [VersionedClause<ACCC_DeviceType>];
@@ -439,7 +439,7 @@ def ACC_Init : Directive<"init"> {
 }
 
 // 2.15.1
-def ACC_Routine : Directive<"routine"> {
+def ACC_Routine : Directive<[Spelling<"routine">]> {
   let allowedClauses = [
     VersionedClause<ACCC_Bind>,
     VersionedClause<ACCC_DeviceType>,
@@ -456,7 +456,7 @@ def ACC_Routine : Directive<"routine"> {
 }
 
 // 2.14.3
-def ACC_Set : Directive<"set"> {
+def ACC_Set : Directive<[Spelling<"set">]> {
   let allowedOnceClauses = [
     VersionedClause<ACCC_DefaultAsync>,
     VersionedClause<ACCC_DeviceNum>,
@@ -476,7 +476,7 @@ def ACC_Set : Directive<"set"> {
 }
 
 // 2.14.2
-def ACC_Shutdown : Directive<"shutdown"> {
+def ACC_Shutdown : Directive<[Spelling<"shutdown">]> {
   let allowedOnceClauses = [VersionedClause<ACCC_DeviceNum>,
                             VersionedClause<ACCC_If>];
   let allowedClauses = [VersionedClause<ACCC_DeviceType>];
@@ -485,7 +485,7 @@ def ACC_Shutdown : Directive<"shutdown"> {
 }
 
 // 2.14.4
-def ACC_Update : Directive<"update"> {
+def ACC_Update : Directive<[Spelling<"update">]> {
   let allowedClauses = [VersionedClause<ACCC_DeviceType>,
                         VersionedClause<ACCC_IfPresent>,
                         VersionedClause<ACCC_Wait>];
@@ -501,7 +501,7 @@ def ACC_Update : Directive<"update"> {
 }
 
 // 2.16.3
-def ACC_Wait : Directive<"wait"> {
+def ACC_Wait : Directive<[Spelling<"wait">]> {
   let allowedOnceClauses = [
     VersionedClause<ACCC_Async>,
     VersionedClause<ACCC_If>
@@ -511,7 +511,7 @@ def ACC_Wait : Directive<"wait"> {
 }
 
 // 2.14.6
-def ACC_EnterData : Directive<"enter data"> {
+def ACC_EnterData : Directive<[Spelling<"enter data">]> {
   let allowedClauses = [
     VersionedClause<ACCC_Wait>
   ];
@@ -529,7 +529,7 @@ def ACC_EnterData : Directive<"enter data"> {
 }
 
 // 2.14.7
-def ACC_ExitData : Directive<"exit data"> {
+def ACC_ExitData : Directive<[Spelling<"exit data">]> {
   let allowedClauses = [VersionedClause<ACCC_Finalize>,
                         VersionedClause<ACCC_Wait>];
   let allowedOnceClauses = [VersionedClause<ACCC_Async>,
@@ -544,7 +544,7 @@ def ACC_ExitData : Directive<"exit data"> {
 }
 
 // 2.8
-def ACC_HostData : Directive<"host_data"> {
+def ACC_HostData : Directive<[Spelling<"host_data">]> {
   let allowedClauses = [VersionedClause<ACCC_IfPresent>];
   let allowedOnceClauses = [VersionedClause<ACCC_If>];
   let requiredClauses = [
@@ -555,7 +555,7 @@ def ACC_HostData : Directive<"host_data"> {
 }
 
 // 2.11
-def ACC_KernelsLoop : Directive<"kernels loop"> {
+def ACC_KernelsLoop : Directive<[Spelling<"kernels loop">]> {
   let allowedClauses = [VersionedClause<ACCC_Async>,
                         VersionedClause<ACCC_Attach>,
                         VersionedClause<ACCC_Collapse>,
@@ -591,7 +591,7 @@ def ACC_KernelsLoop : Directive<"kernels loop"> {
 }
 
 // 2.11
-def ACC_ParallelLoop : Directive<"parallel loop"> {
+def ACC_ParallelLoop : Directive<[Spelling<"parallel loop">]> {
   let allowedClauses = [VersionedClause<ACCC_Async>,
                         VersionedClause<ACCC_Attach>,
                         VersionedClause<ACCC_Collapse>,
@@ -628,7 +628,7 @@ def ACC_ParallelLoop : Directive<"parallel loop"> {
 }
 
 // 2.11
-def ACC_SerialLoop : Directive<"serial loop"> {
+def ACC_SerialLoop : Directive<[Spelling<"serial loop">]> {
   let allowedClauses = [VersionedClause<ACCC_Async>,
                         VersionedClause<ACCC_Attach>,
                         VersionedClause<ACCC_Collapse>,
@@ -661,7 +661,7 @@ def ACC_SerialLoop : Directive<"serial loop"> {
   let category = CA_Executable;
 }
 
-def ACC_Unknown : Directive<"unknown"> {
+def ACC_Unknown : Directive<[Spelling<"unknown">]> {
   let isDefault = true;
   let association = AS_None;
   let category = CA_Utility;
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index cc9e038dc533c..027692275b63b 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -32,47 +32,48 @@ def OpenMP : DirectiveLanguage {
 // Sorted alphabetically wrt clause spelling.
 //===----------------------------------------------------------------------===//
 
-def OMPC_Absent : Clause<"absent"> {
+def OMPC_Absent : Clause<[Spelling<"absent">]> {
   let clangClass = "OMPAbsentClause";
   let flangClass = "OmpAbsentClause";
 }
-def OMPC_Acquire : Clause<"acquire"> {
+def OMPC_Acquire : Clause<[Spelling<"acquire">]> {
   let clangClass = "OMPAcquireClause";
 }
-def OMPC_AcqRel : Clause<"acq_rel"> {
+def OMPC_AcqRel : Clause<[Spelling<"acq_rel">]> {
   let clangClass = "OMPAcqRelClause";
 }
-def OMPC_AdjustArgs : Clause<"adjust_args"> {
+def OMPC_AdjustArgs : Clause<[Spelling<"adjust_args">]> {
   let flangClass = "OmpAdjustArgsClause";
 }
-def OMPC_Affinity : Clause<"affinity"> {
+def OMPC_Affinity : Clause<[Spelling<"affinity">]> {
   let clangClass = "OMPAffinityClause";
   let flangClass = "OmpAffinityClause";
 }
-def OMPC_Align : Clause<"align"> {
+def OMPC_Align : Clause<[Spelling<"align">]> {
   let clangClass = "OMPAlignClause";
   let flangClass = "OmpAlignClause";
 }
-def OMPC_Aligned : Clause<"aligned"> {
+def OMPC_Aligned : Clause<[Spelling<"aligned">]> {
   let clangClass = "OMPAlignedClause";
   let flangClass = "OmpAlignedClause";
 }
-def OMPC_Allocate : Clause<"allocate"> {
+def OMPC_Allocate : Clause<[Spelling<"allocate">]> {
   let clangClass = "OMPAllocateClause";
   let flangClass = "OmpAllocateClause";
 }
-def OMPC_Allocator : Clause<"allocator"> {
+def OMPC_Allocator : Clause<[Spelling<"allocator">]> {
   let clangClass = "OMPAllocatorClause";
   let flangClass = "ScalarIntExpr";
 }
-def OMPC_AppendArgs : Clause<"append_args"> {
+def OMPC_AppendArgs : Clause<[Spelling<"append_args">]> {
   let flangClass = "OmpAppendArgsClause";
 }
-def OMPC_At : Clause<"at"> {
+def OMPC_At : Clause<[Spelling<"at">]> {
   let clangClass = "OMPAtClause";
   let flangClass = "OmpAtClause";
 }
-def OMPC_AtomicDefaultMemOrder : Clause<"atomic_default_mem_order"> {
+def OMPC_AtomicDefaultMemOrder
+    : Clause<[Spelling<"atomic_default_mem_order">]> {
   let clangClass = "OMPAtomicDefaultMemOrderClause";
   let flangClass = "OmpAtomicDefaultMemOrderClause";
 }
@@ -80,7 +81,7 @@ def OMPC_AtomicDefaultMemOrder : Clause<"atomic_default_mem_order"> {
 def OMP_BIND_parallel : EnumVal<"parallel",1,1> {}
 def OMP_BIND_teams : EnumVal<"teams",2,1> {}
 def OMP_BIND_thread : EnumVal<"thread",3,1> { let isDefault = true; }
-def OMPC_Bind : Clause<"bind"> {
+def OMPC_Bind : Clause<[Spelling<"bind">]> {
   let clangClass = "OMPBindClause";
   let flangClass = "OmpBindClause";
   let enumClauseValue = "BindKind";
@@ -98,7 +99,8 @@ def OMP_CANCELLATION_CONSTRUCT_Taskgroup : EnumVal<"taskgroup", 4, 1> {}
 def OMP_CANCELLATION_CONSTRUCT_None : EnumVal<"none", 5, 0> {
   let isDefault = 1;
 }
-def OMPC_CancellationConstructType : Clause<"cancellation_construct_type"> {
+def OMPC_CancellationConstructType
+    : Clause<[Spelling<"cancellation_construct_type">]> {
   let enumClauseValue = "CancellationConstructType";
   let allowedClauseValues = [
     OMP_CANCELLATION_CONSTRUCT_Parallel,
@@ -110,109 +112,109 @@ def OMPC_CancellationConstructType : Clause<"cancellation_construct_type"> {
   let flangClass = "OmpCancellationConstructTypeClause";
   let skipFlangUnparser = true;
 }
-def OMPC_Contains : Clause<"contains"> {
+def OMPC_Contains : Clause<[Spelling<"contains">]> {
   let clangClass = "OMPContainsClause";
   let flangClass = "OmpContainsClause";
 }
-def OMPC_Capture : Clause<"capture"> {
+def OMPC_Capture : Clause<[Spelling<"capture">]> {
   let clangClass = "OMPCaptureClause";
 }
-def OMPC_Collapse : Clause<"collapse"> {
+def OMPC_Collapse : Clause<[Spelling<"collapse">]> {
   let clangClass = "OMPCollapseClause";
   let flangClass = "ScalarIntConstantExpr";
 }
-def OMPC_Compare : Clause<"compare"> {
+def OMPC_Compare : Clause<[Spelling<"compare">]> {
   let clangClass = "OMPCompareClause";
 }
-def OMPC_Copyin : Clause<"copyin"> {
+def OMPC_Copyin : Clause<[Spelling<"copyin">]> {
   let clangClass = "OMPCopyinClause";
   let flangClass = "OmpObjectList";
 }
-def OMPC_CopyPrivate : Clause<"copyprivate"> {
+def OMPC_CopyPrivate : Clause<[Spelling<"copyprivate">]> {
   let clangClass = "OMPCopyprivateClause";
   let flangClass = "OmpObjectList";
 }
-def OMPC_Default : Clause<"default"> {
+def OMPC_Default : Clause<[Spelling<"default">]> {
   let clangClass = "OMPDefaultClause";
   let flangClass = "OmpDefaultClause";
 }
-def OMPC_DefaultMap : Clause<"defaultmap"> {
+def OMPC_DefaultMap : Clause<[Spelling<"defaultmap">]> {
   let clangClass = "OMPDefaultmapClause";
   let flangClass = "OmpDefaultmapClause";
 }
-def OMPC_Depend : Clause<"depend"> {
+def OMPC_Depend : Clause<[Spelling<"depend">]> {
   let clangClass = "OMPDependClause";
   let flangClass = "OmpDependClause";
 }
-def OMPC_Depobj : Clause<"depobj"> {
+def OMPC_Depobj : Clause<[Spelling<"depobj">]> {
   let clangClass = "OMPDepobjClause";
   let isImplicit = true;
 }
-def OMPC_Destroy : Clause<"destroy"> {
+def OMPC_Destroy : Clause<[Spelling<"destroy">]> {
   let clangClass = "OMPDestroyClause";
   let flangClass = "OmpDestroyClause";
   let isValueOptional = true;
 }
-def OMPC_Detach : Clause<"detach"> {
+def OMPC_Detach : Clause<[Spelling<"detach">]> {
   let clangClass = "OMPDetachClause";
   let flangClass = "OmpDetachClause";
 }
-def OMPC_Device : Clause<"device"> {
+def OMPC_Device : Clause<[Spelling<"device">]> {
   let clangClass = "OMPDeviceClause";
   let flangClass = "OmpDeviceClause";
 }
-def OMPC_DeviceType : Clause<"device_type"> {
+def OMPC_DeviceType : Clause<[Spelling<"device_type">]> {
   let flangClass = "OmpDeviceTypeClause";
 }
-def OMPC_DistSchedule : Clause<"dist_schedule"> {
+def OMPC_DistSchedule : Clause<[Spelling<"dist_schedule">]> {
   let clangClass = "OMPDistScheduleClause";
   let flangClass = "ScalarIntExpr";
   let isValueOptional = true;
 }
-def OMPC_Doacross : Clause<"doacross"> {
+def OMPC_Doacross : Clause<[Spelling<"doacross">]> {
   let clangClass = "OMPDoacrossClause";
   let flangClass = "OmpDoacrossClause";
 }
-def OMPC_DynamicAllocators : Clause<"dynamic_allocators"> {
+def OMPC_DynamicAllocators : Clause<[Spelling<"dynamic_allocators">]> {
   let clangClass = "OMPDynamicAllocatorsClause";
 }
-def OMPC_Enter : Clause<"enter"> {
+def OMPC_Enter : Clause<[Spelling<"enter">]> {
   let flangClass = "OmpObjectList";
 }
-def OMPC_Exclusive : Clause<"exclusive"> {
+def OMPC_Exclusive : Clause<[Spelling<"exclusive">]> {
   let clangClass = "OMPExclusiveClause";
   let flangClass = "OmpObjectList";
 }
-def OMPC_Fail : Clause<"fail"> {
+def OMPC_Fail : Clause<[Spelling<"fail">]> {
   let clangClass = "OMPFailClause";
   let flangClass = "OmpFailClause";
 }
-def OMPC_Filter : Clause<"filter"> {
+def OMPC_Filter : Clause<[Spelling<"filter">]> {
   let clangClass = "OMPFilterClause";
   let flangClass = "ScalarIntExpr";
 }
-def OMPC_Final : Clause<"final"> {
+def OMPC_Final : Clause<[Spelling<"final">]> {
   let clangClass = "OMPFinalClause";
   let flangClass = "ScalarLogicalExpr";
 }
-def OMPC_FirstPrivate : Clause<"firstprivate"> {
+def OMPC_FirstPrivate : Clause<[Spelling<"firstprivate">]> {
   let clangClass = "OMPFirstprivateClause";
   let flangClass = "OmpObjectList";
 }
-def OMPC_Flush : Clause<"flush"> {
+def OMPC_Flush : Clause<[Spelling<"flush">]> {
   let clangClass = "OMPFlushClause";
   let isImplicit = true;
 }
-def OMPC_From : Clause<"from"> {
+def OMPC_From : Clause<[Spelling<"from">]> {
   let clangClass = "OMPFromClause";
   let flangClass = "OmpFromClause";
 }
-def OMPC_Full: Clause<"full"> {
+def OMPC_Full: Clause<[Spelling<"full">]> {
   let clangClass = "OMPFullClause";
 }
 def OMP_GRAINSIZE_Strict : EnumVal<"strict", 1, 1> {}
 def OMP_GRAINSIZE_Unknown : EnumVal<"unknown", 2, 0> { let isDefault = 1; }
-def OMPC_GrainSize : Clause<"grainsize"> {
+def OMPC_GrainSize : Clause<[Spelling<"grainsize">]> {
   let clangClass = "OMPGrainsizeClause";
   let flangClass = "OmpGrainsizeClause";
   let enumClauseValue = "GrainsizeType";
@@ -221,61 +223,61 @@ def OMPC_GrainSize : Clause<"grainsize"> {
     OMP_GRAINSIZE_Unknown
   ];
 }
-def OMPC_HasDeviceAddr : Clause<"has_device_addr"> {
+def OMPC_HasDeviceAddr : Clause<[Spelling<"has_device_addr">]> {
   let clangClass = "OMPHasDeviceAddrClause";
   let flangClass = "OmpObjectList";
 }
-def OMPC_Hint : Clause<"hint"> {
+def OMPC_Hint : Clause<[Spelling<"hint">]> {
   let clangClass = "OMPHintClause";
   let flangClass = "OmpHintClause";
 }
-def OMPC_Holds : Clause<"holds"> {
+def OMPC_Holds : Clause<[Spelling<"holds">]> {
   let clangClass = "OMPHoldsClause";
   let flangClass = "OmpHoldsClause";
 }
-def OMPC_If : Clause<"if"> {
+def OMPC_If : Clause<[Spelling<"if">]> {
   let clangClass = "OMPIfClause";
   let flangClass = "OmpIfClause";
 }
-def OMPC_Inbranch : Clause<"inbranch"> {
+def OMPC_Inbranch : Clause<[Spelling<"inbranch">]> {
 }
-def OMPC_Inclusive : Clause<"inclusive"> {
+def OMPC_Inclusive : Clause<[Spelling<"inclusive">]> {
   let clangClass = "OMPInclusiveClause";
   let flangClass = "OmpObjectList";
 }
-def OMPC_Indirect : Clause<"indirect"> {
+def OMPC_Indirect : Clause<[Spelling<"indirect">]> {
 }
-def OMPC_Init : Clause<"init"> {
+def OMPC_Init : Clause<[Spelling<"init">]> {
   let clangClass = "OMPInitClause";
   let flangClass = "OmpInitClause";
 }
-def OMPC_Initializer : Clause<"initializer"> {
+def OMPC_Initializer : Clause<[Spelling<"initializer">]> {
   let flangClass = "OmpInitializerClause";
 }
-def OMPC_InReduction : Clause<"in_reduction"> {
+def OMPC_InReduction : Clause<[Spelling<"in_reduction">]> {
   let clangClass = "OMPInReductionClause";
   let flangClass = "OmpInReductionClause";
 }
-def OMPC_IsDevicePtr : Clause<"is_device_ptr"> {
+def OMPC_IsDevicePtr : Clause<[Spelling<"is_device_ptr">]> {
   let clangClass = "OMPIsDevicePtrClause";
   let flangClass = "OmpObjectList";
 }
-def OMPC_LastPrivate : Clause<"lastprivate"> {
+def OMPC_LastPrivate : Clause<[Spelling<"lastprivate">]> {
   let clangClass = "OMPLastprivateClause";
   let flangClass = "OmpLastprivateClause";
 }
-def OMPC_Linear : Clause<"linear"> {
+def OMPC_Linear : Clause<[Spelling<"linear">]> {
   let clangClass = "OMPLinearClause";
   let flangClass = "OmpLinearClause";
 }
-def OMPC_Link : Clause<"link"> {
+def OMPC_Link : Clause<[Spelling<"link">]> {
   let flangClass = "OmpObjectList";
 }
-def OMPC_Map : Clause<"map"> {
+def OMPC_Map : Clause<[Spelling<"map">]> {
   let clangClass = "OMPMapClause";
   let flangClass = "OmpMapClause";
 }
-def OMPC_Match : Clause<"match"> {
+def OMPC_Match : Clause<[Spelling<"match">]> {
   let flangClass = "OmpMatchClause";
 }
 def OMP_MEMORY_ORDER_SeqCst : EnumVal<"seq_cst", 1, 1> {}
@@ -286,7 +288,7 @@ def OMP_MEMORY_ORDER_Relaxed : EnumVal<"relaxed", 5, 1> {}
 def OMP_MEMORY_ORDER_Default : EnumVal<"default", 6, 0> {
   let isDefault = 1;
 }
-def OMPC_MemoryOrder : Clause<"memory_order"> {
+def OMPC_MemoryOrder : Clause<[Spelling<"memory_order">]> {
   let enumClauseValue = "MemoryOrderKind";
   let allowedClauseValues = [
     OMP_MEMORY_ORDER_SeqCst,
@@ -297,49 +299,49 @@ def OMPC_MemoryOrder : Clause<"memory_order"> {
     OMP_MEMORY_ORDER_Default
   ];
 }
-def OMPC_Mergeable : Clause<"mergeable"> {
+def OMPC_Mergeable : Clause<[Spelling<"mergeable">]> {
   let clangClass = "OMPMergeableClause";
 }
-def OMPC_Message : Clause<"message"> {
+def OMPC_Message : Clause<[Spelling<"message">]> {
   let clangClass = "OMPMessageClause";
   let flangClass = "OmpMessageClause";
 }
-def OMPC_NoOpenMP : Clause<"no_openmp"> {
+def OMPC_NoOpenMP : Clause<[Spelling<"no_openmp">]> {
   let clangClass = "OMPNoOpenMPClause";
 }
-def OMPC_NoOpenMPRoutines : Clause<"no_openmp_routines"> {
+def OMPC_NoOpenMPRoutines : Clause<[Spelling<"no_openmp_routines">]> {
   let clangClass = "OMPNoOpenMPRoutinesClause";
 }
-def OMPC_NoOpenMPConstructs : Clause<"no_openmp_constructs"> {
+def OMPC_NoOpenMPConstructs : Clause<[Spelling<"no_openmp_constructs">]> {
   let clangClass = "OMPNoOpenMPConstructsClause";
 }
-def OMPC_NoParallelism : Clause<"no_parallelism"> {
+def OMPC_NoParallelism : Clause<[Spelling<"no_parallelism">]> {
   let clangClass = "OMPNoParallelismClause";
 }
-def OMPC_Nocontext : Clause<"nocontext"> {
+def OMPC_Nocontext : Clause<[Spelling<"nocontext">]> {
   let clangClass = "OMPNocontextClause";
   let flangClass = "ScalarLogicalExpr";
 }
-def OMPC_NoGroup : Clause<"nogroup"> {
+def OMPC_NoGroup : Clause<[Spelling<"nogroup">]> {
   let clangClass = "OMPNogroupClause";
 }
-def OMPC_NonTemporal : Clause<"nontemporal"> {
+def OMPC_NonTemporal : Clause<[Spelling<"nontemporal">]> {
   let clangClass = "OMPNontemporalClause";
   let flangClass = "Name";
   let isValueList = true;
 }
-def OMPC_Notinbranch : Clause<"notinbranch"> {
+def OMPC_Notinbranch : Clause<[Spelling<"notinbranch">]> {
 }
-def OMPC_Novariants : Clause<"novariants"> {
+def OMPC_Novariants : Clause<[Spelling<"novariants">]> {
   let clangClass = "OMPNovariantsClause";
   let flangClass = "ScalarLogicalExpr";
 }
-def OMPC_NoWait : Clause<"nowait"> {
+def OMPC_NoWait : Clause<[Spelling<"nowait">]> {
   let clangClass = "OMPNowaitClause";
 }
 def OMP_NUMTASKS_Strict : EnumVal<"strict", 1, 1> {}
 def OMP_NUMTASKS_Unknown : EnumVal<"unknown", 2, 0> { let isDefault = 1; }
-def OMPC_NumTasks : Clause<"num_tasks"> {
+def OMPC_NumTasks : Clause<[Spelling<"num_tasks">]> {
   let clangClass = "OMPNumTasksClause";
   let flangClass = "OmpNumTasksClause";
   let enumClauseValue = "NumTasksType";
@@ -348,27 +350,27 @@ def OMPC_NumTasks : Clause<"num_tasks"> {
     OMP_NUMTASKS_Unknown
   ];
 }
-def OMPC_NumTeams : Clause<"num_teams"> {
+def OMPC_NumTeams : Clause<[Spelling<"num_teams">]> {
   let clangClass = "OMPNumTeamsClause";
   let flangClass = "ScalarIntExpr";
 }
-def OMPC_NumThreads : Clause<"num_threads"> {
+def OMPC_NumThreads : Clause<[Spelling<"num_threads">]> {
   let clangClass = "OMPNumThreadsClause";
   let flangClass = "ScalarIntExpr";
 }
-def OMPC_OMPX_Attribute : Clause<"ompx_attribute"> {
+def OMPC_OMPX_Attribute : Clause<[Spelling<"ompx_attribute">]> {
   let clangClass = "OMPXAttributeClause";
 }
-def OMPC_OMPX_Bare : Clause<"ompx_bare"> {
+def OMPC_OMPX_Bare : Clause<[Spelling<"ompx_bare">]> {
   let clangClass = "OMPXBareClause";
 }
-def OMPC_OMPX_DynCGroupMem : Clause<"ompx_dyn_cgroup_mem"> {
+def OMPC_OMPX_DynCGroupMem : Clause<[Spelling<"ompx_dyn_cgroup_mem">]> {
   let clangClass = "OMPXDynCGroupMemClause";
   let flangClass = "ScalarIntExpr";
 }
 def OMP_ORDER_concurrent : EnumVal<"concurrent",1,1> {}
 def OMP_ORDER_unknown : EnumVal<"unknown",2,0> { let isDefault = 1; }
-def OMPC_Order : Clause<"order"> {
+def OMPC_Order : Clause<[Spelling<"order">]> {
   let clangClass = "OMPOrderClause";
   let flangClass = "OmpOrderClause";
   let enumClauseValue = "OrderKind";
@@ -377,30 +379,30 @@ def OMPC_Order : Clause<"order"> {
     OMP_ORDER_concurrent
   ];
 }
-def OMPC_Ordered : Clause<"ordered"> {
+def OMPC_Ordered : Clause<[Spelling<"ordered">]> {
   let clangClass = "OMPOrderedClause";
   let flangClass = "ScalarIntConstantExpr";
   let isValueOptional = true;
 }
-def OMPC_Otherwise : Clause<"otherwise"> {
+def OMPC_Otherwise : Clause<[Spelling<"otherwise">]> {
   let flangClass = "OmpOtherwiseClause";
   let isValueOptional = true;
 }
-def OMPC_Partial: Clause<"partial"> {
+def OMPC_Partial: Clause<[Spelling<"partial">]> {
   let clangClass = "OMPPartialClause";
   let flangClass = "ScalarIntConstantExpr";
   let isValueOptional = true;
 }
-def OMPC_Permutation: Clause<"permutation"> {
+def OMPC_Permutation: Clause<[Spelling<"permutation">]> {
   let clangClass = "OMPPermutationClause";
   let flangClass = "ScalarIntExpr";
   let isValueList = true;
 }
-def OMPC_Priority : Clause<"priority"> {
+def OMPC_Priority : Clause<[Spelling<"priority">]> {
   let clangClass = "OMPPriorityClause";
   let flangClass = "ScalarIntExpr";
 }
-def OMPC_Private : Clause<"private"> {
+def OMPC_Private : Clause<[Spelling<"private">]> {
   let clangClass = "OMPPrivateClause";
   let flangClass = "OmpObjectList";
 }
@@ -410,7 +412,7 @@ def OMP_PROC_BIND_spread : EnumVal<"spread",4,1> {}
 def OMP_PROC_BIND_primary : EnumVal<"primary",5,1> {}
 def OMP_PROC_BIND_default : EnumVal<"default",6,0> {}
 def OMP_PROC_BIND_unknown : EnumVal<"unknown",7,0> { let isDefault = true; }
-def OMPC_ProcBind : Clause<"proc_bind"> {
+def OMPC_ProcBind : Clause<[Spelling<"proc_bind">]> {
   let clangClass = "OMPProcBindClause";
   let flangClass = "OmpProcBindClause";
   let enumClauseValue = "ProcBindKind";
@@ -423,23 +425,23 @@ def OMPC_ProcBind : Clause<"proc_bind"> {
     OMP_PROC_BIND_unknown
   ];
 }
-def OMPC_Read : Clause<"read"> {
+def OMPC_Read : Clause<[Spelling<"read">]> {
   let clangClass = "OMPReadClause";
 }
-def OMPC_Reduction : Clause<"reduction"> {
+def OMPC_Reduction : Clause<[Spelling<"reduction">]> {
   let clangClass = "OMPReductionClause";
   let flangClass = "OmpReductionClause";
 }
-def OMPC_Relaxed : Clause<"relaxed"> {
+def OMPC_Relaxed : Clause<[Spelling<"relaxed">]> {
   let clangClass = "OMPRelaxedClause";
 }
-def OMPC_Release : Clause<"release"> {
+def OMPC_Release : Clause<[Spelling<"release">]> {
   let clangClass = "OMPReleaseClause";
 }
-def OMPC_ReverseOffload : Clause<"reverse_offload"> {
+def OMPC_ReverseOffload : Clause<[Spelling<"reverse_offload">]> {
   let clangClass = "OMPReverseOffloadClause";
 }
-def OMPC_SafeLen : Clause<"safelen"> {
+def OMPC_SafeLen : Clause<[Spelling<"safelen">]> {
   let clangClass = "OMPSafelenClause";
   let flangClass = "ScalarIntConstantExpr";
 }
@@ -449,7 +451,7 @@ def OMP_SCHEDULE_Guided : EnumVal<"guided", 4, 1> {}
 def OMP_SCHEDULE_Auto : EnumVal<"auto", 5, 1> {}
 def OMP_SCHEDULE_Runtime : EnumVal<"runtime", 6, 1> {}
 def OMP_SCHEDULE_Default : EnumVal<"default", 7, 0> { let isDefault = 1; }
-def OMPC_Schedule : Clause<"schedule"> {
+def OMPC_Schedule : Clause<[Spelling<"schedule">]> {
   let clangClass = "OMPScheduleClause";
   let flangClass = "OmpScheduleClause";
   let enumClauseValue = "ScheduleKind";
@@ -462,94 +464,94 @@ def OMPC_Schedule : Clause<"schedule"> {
     OMP_SCHEDULE_Default
   ];
 }
-def OMPC_SeqCst : Clause<"seq_cst"> {
+def OMPC_SeqCst : Clause<[Spelling<"seq_cst">]> {
   let clangClass = "OMPSeqCstClause";
 }
-def OMPC_Severity : Clause<"severity"> {
+def OMPC_Severity : Clause<[Spelling<"severity">]> {
   let clangClass = "OMPSeverityClause";
   let flangClass = "OmpSeverityClause";
 }
-def OMPC_Shared : Clause<"shared"> {
+def OMPC_Shared : Clause<[Spelling<"shared">]> {
   let clangClass = "OMPSharedClause";
   let flangClass = "OmpObjectList";
 }
-def OMPC_Simd : Clause<"simd"> {
+def OMPC_Simd : Clause<[Spelling<"simd">]> {
   let clangClass = "OMPSIMDClause";
 }
-def OMPC_SimdLen : Clause<"simdlen"> {
+def OMPC_SimdLen : Clause<[Spelling<"simdlen">]> {
   let clangClass = "OMPSimdlenClause";
   let flangClass = "ScalarIntConstantExpr";
 }
-def OMPC_Sizes: Clause<"sizes"> {
+def OMPC_Sizes: Clause<[Spelling<"sizes">]> {
   let clangClass = "OMPSizesClause";
   let flangClass = "ScalarIntExpr";
   let isValueList = true;
 }
-def OMPC_TaskReduction : Clause<"task_reduction"> {
+def OMPC_TaskReduction : Clause<[Spelling<"task_reduction">]> {
   let clangClass = "OMPTaskReductionClause";
   let flangClass = "OmpTaskReductionClause";
 }
-def OMPC_ThreadLimit : Clause<"thread_limit"> {
+def OMPC_ThreadLimit : Clause<[Spelling<"thread_limit">]> {
   let clangClass = "OMPThreadLimitClause";
   let flangClass = "ScalarIntExpr";
 }
-def OMPC_ThreadPrivate : Clause<"threadprivate"> {
+def OMPC_ThreadPrivate : Clause<[Spelling<"threadprivate">]> {
   let isImplicit = true;
 }
-def OMPC_Threads : Clause<"threads"> {
+def OMPC_Threads : Clause<[Spelling<"threads">]> {
   let clangClass = "OMPThreadsClause";
 }
-def OMPC_To : Clause<"to"> {
+def OMPC_To : Clause<[Spelling<"to">]> {
   let clangClass = "OMPToClause";
   let flangClass = "OmpToClause";
 }
-def OMPC_UnifiedAddress : Clause<"unified_address"> {
+def OMPC_UnifiedAddress : Clause<[Spelling<"unified_address">]> {
   let clangClass = "OMPUnifiedAddressClause";
 }
-def OMPC_UnifiedSharedMemory : Clause<"unified_shared_memory"> {
+def OMPC_UnifiedSharedMemory : Clause<[Spelling<"unified_shared_memory">]> {
   let clangClass = "OMPUnifiedSharedMemoryClause";
 }
-def OMPC_SelfMaps : Clause<"self_maps"> {
+def OMPC_SelfMaps : Clause<[Spelling<"self_maps">]> {
   let clangClass = "OMPSelfMapsClause";
 }
-def OMPC_Uniform : Clause<"uniform"> {
+def OMPC_Uniform : Clause<[Spelling<"uniform">]> {
   let flangClass = "Name";
   let isValueList = true;
 }
-def OMPC_Unknown : Clause<"unknown"> {
+def OMPC_Unknown : Clause<[Spelling<"unknown">]> {
   let isImplicit = true;
   let isDefault = true;
 }
-def OMPC_Untied : Clause<"untied"> {
+def OMPC_Untied : Clause<[Spelling<"untied">]> {
   let clangClass = "OMPUntiedClause";
 }
-def OMPC_Update : Clause<"update"> {
+def OMPC_Update : Clause<[Spelling<"update">]> {
   let clangClass = "OMPUpdateClause";
   let flangClass = "OmpUpdateClause";
   let isValueOptional = true;
 }
-def OMPC_Use : Clause<"use"> {
+def OMPC_Use : Clause<[Spelling<"use">]> {
   let clangClass = "OMPUseClause";
   let flangClass = "OmpUseClause";
 }
-def OMPC_UsesAllocators : Clause<"uses_allocators"> {
+def OMPC_UsesAllocators : Clause<[Spelling<"uses_allocators">]> {
   let clangClass = "OMPUsesAllocatorsClause";
 }
-def OMPC_UseDeviceAddr : Clause<"use_device_addr"> {
+def OMPC_UseDeviceAddr : Clause<[Spelling<"use_device_addr">]> {
   let clangClass = "OMPUseDeviceAddrClause";
   let flangClass = "OmpObjectList";
 }
-def OMPC_UseDevicePtr : Clause<"use_device_ptr"> {
+def OMPC_UseDevicePtr : Clause<[Spelling<"use_device_ptr">]> {
   let clangClass = "OMPUseDevicePtrClause";
   let flangClass = "OmpObjectList";
 }
-def OMPC_Weak : Clause<"weak"> {
+def OMPC_Weak : Clause<[Spelling<"weak">]> {
   let clangClass = "OMPWeakClause";
 }
-def OMPC_When: Clause<"when"> {
+def OMPC_When: Clause<[Spelling<"when">]> {
   let flangClass = "OmpWhenClause";
 }
-def OMPC_Write : Clause<"write"> {
+def OMPC_Write : Clause<[Spelling<"write">]> {
   let clangClass = "OMPWriteClause";
 }
 
@@ -559,7 +561,7 @@ def OMPC_Write : Clause<"write"> {
 // follows "xyz".
 //===----------------------------------------------------------------------===//
 
-def OMP_Allocate : Directive<"allocate"> {
+def OMP_Allocate : Directive<[Spelling<"allocate">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Align, 51>,
     VersionedClause<OMPC_Allocator>,
@@ -567,7 +569,7 @@ def OMP_Allocate : Directive<"allocate"> {
   let association = AS_None;
   let category = CA_Declarative;
 }
-def OMP_Allocators : Directive<"allocators"> {
+def OMP_Allocators : Directive<[Spelling<"allocators">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
   ];
@@ -575,7 +577,7 @@ def OMP_Allocators : Directive<"allocators"> {
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_Assumes : Directive<"assumes"> {
+def OMP_Assumes : Directive<[Spelling<"assumes">]> {
   let association = AS_None;
   let category = CA_Informational;
   let allowedOnceClauses = [
@@ -587,7 +589,7 @@ def OMP_Assumes : Directive<"assumes"> {
     VersionedClause<OMPC_NoParallelism, 51>,
   ];
 }
-def OMP_Assume : Directive<"assume"> {
+def OMP_Assume : Directive<[Spelling<"assume">]> {
   let association = AS_Block;
   let category = CA_Informational;
   let allowedOnceClauses = [
@@ -600,7 +602,7 @@ def OMP_Assume : Directive<"assume"> {
     VersionedClause<OMPC_NoOpenMPConstructs, 60>,
   ];
 }
-def OMP_Atomic : Directive<"atomic"> {
+def OMP_Atomic : Directive<[Spelling<"atomic">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_AcqRel, 50>,
     VersionedClause<OMPC_Acquire, 50>,
@@ -619,11 +621,11 @@ def OMP_Atomic : Directive<"atomic"> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_Barrier : Directive<"barrier"> {
+def OMP_Barrier : Directive<[Spelling<"barrier">]> {
   let association = AS_None;
   let category = CA_Executable;
 }
-def OMP_BeginAssumes : Directive<"begin assumes"> {
+def OMP_BeginAssumes : Directive<[Spelling<"begin assumes">]> {
   let association = AS_Delimited;
   let category = CA_Informational;
   let allowedOnceClauses = [
@@ -636,12 +638,12 @@ def OMP_BeginAssumes : Directive<"begin assumes"> {
   ];
   let languages = [L_C];
 }
-def OMP_EndAssumes : Directive<"end assumes"> {
+def OMP_EndAssumes : Directive<[Spelling<"end assumes">]> {
   let association = AS_Delimited;
   let category = OMP_BeginAssumes.category;
   let languages = OMP_BeginAssumes.languages;
 }
-def OMP_BeginDeclareTarget : Directive<"begin declare target"> {
+def OMP_BeginDeclareTarget : Directive<[Spelling<"begin declare target">]> {
   let allowedClauses = [
     VersionedClause<OMPC_DeviceType>,
     VersionedClause<OMPC_Indirect>,
@@ -652,22 +654,22 @@ def OMP_BeginDeclareTarget : Directive<"begin declare target"> {
   let category = CA_Declarative;
   let languages = [L_C];
 }
-def OMP_EndDeclareTarget : Directive<"end declare target"> {
+def OMP_EndDeclareTarget : Directive<[Spelling<"end declare target">]> {
   let association = AS_Delimited;
   let category = OMP_BeginDeclareTarget.category;
   let languages = OMP_BeginDeclareTarget.languages;
 }
-def OMP_BeginDeclareVariant : Directive<"begin declare variant"> {
+def OMP_BeginDeclareVariant : Directive<[Spelling<"begin declare variant">]> {
   let association = AS_Delimited;
   let category = CA_Declarative;
   let languages = [L_C];
 }
-def OMP_EndDeclareVariant : Directive<"end declare variant"> {
+def OMP_EndDeclareVariant : Directive<[Spelling<"end declare variant">]> {
   let association = AS_Delimited;
   let category = OMP_BeginDeclareVariant.category;
   let languages = OMP_BeginDeclareVariant.languages;
 }
-def OMP_Cancel : Directive<"cancel"> {
+def OMP_Cancel : Directive<[Spelling<"cancel">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_CancellationConstructType>,
     VersionedClause<OMPC_If>,
@@ -675,35 +677,35 @@ def OMP_Cancel : Directive<"cancel"> {
   let association = AS_None;
   let category = CA_Executable;
 }
-def OMP_CancellationPoint : Directive<"cancellation point"> {
+def OMP_CancellationPoint : Directive<[Spelling<"cancellation point">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_CancellationConstructType>,
   ];
   let association = AS_None;
   let category = CA_Executable;
 }
-def OMP_Critical : Directive<"critical"> {
+def OMP_Critical : Directive<[Spelling<"critical">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Hint>,
   ];
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_DeclareMapper : Directive<"declare mapper"> {
+def OMP_DeclareMapper : Directive<[Spelling<"declare mapper">]> {
   let requiredClauses = [
     VersionedClause<OMPC_Map, 45>,
   ];
   let association = AS_None;
   let category = CA_Declarative;
 }
-def OMP_DeclareReduction : Directive<"declare reduction"> {
+def OMP_DeclareReduction : Directive<[Spelling<"declare reduction">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Initializer>,
   ];
   let association = AS_None;
   let category = CA_Declarative;
 }
-def OMP_DeclareSimd : Directive<"declare simd"> {
+def OMP_DeclareSimd : Directive<[Spelling<"declare simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Linear>,
@@ -719,7 +721,7 @@ def OMP_DeclareSimd : Directive<"declare simd"> {
   let association = AS_Declaration;
   let category = CA_Declarative;
 }
-def OMP_DeclareTarget : Directive<"declare target"> {
+def OMP_DeclareTarget : Directive<[Spelling<"declare target">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Enter, 52>,
     VersionedClause<OMPC_Indirect>,
@@ -732,7 +734,7 @@ def OMP_DeclareTarget : Directive<"declare target"> {
   let association = AS_None;
   let category = CA_Declarative;
 }
-def OMP_DeclareVariant : Directive<"declare variant"> {
+def OMP_DeclareVariant : Directive<[Spelling<"declare variant">]> {
   let allowedClauses = [
     VersionedClause<OMPC_AdjustArgs, 51>,
   ];
@@ -744,7 +746,7 @@ def OMP_DeclareVariant : Directive<"declare variant"> {
   let category = CA_Declarative;
   let languages = [L_C];
 }
-def OMP_Depobj : Directive<"depobj"> {
+def OMP_Depobj : Directive<[Spelling<"depobj">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Depend, 50>,
     // TODO This should ne `none` instead. Comment carried over from
@@ -756,7 +758,7 @@ def OMP_Depobj : Directive<"depobj"> {
   let association = AS_None;
   let category = CA_Executable;
 }
-def OMP_dispatch : Directive<"dispatch"> {
+def OMP_dispatch : Directive<[Spelling<"dispatch">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Depend>,
     VersionedClause<OMPC_Device>,
@@ -769,7 +771,7 @@ def OMP_dispatch : Directive<"dispatch"> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_Distribute : Directive<"distribute"> {
+def OMP_Distribute : Directive<[Spelling<"distribute">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_FirstPrivate>,
@@ -784,7 +786,7 @@ def OMP_Distribute : Directive<"distribute"> {
   let association = AS_Loop;
   let category = CA_Executable;
 }
-def OMP_Do : Directive<"do"> {
+def OMP_Do : Directive<[Spelling<"do">]> {
   let allowedClauses = [
     VersionedClause<OMPC_FirstPrivate>,
     VersionedClause<OMPC_LastPrivate>,
@@ -803,7 +805,7 @@ def OMP_Do : Directive<"do"> {
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_EndDo : Directive<"end do"> {
+def OMP_EndDo : Directive<[Spelling<"end do">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_NoWait>,
   ];
@@ -812,7 +814,7 @@ def OMP_EndDo : Directive<"end do"> {
   let category = OMP_Do.category;
   let languages = OMP_Do.languages;
 }
-def OMP_Error : Directive<"error"> {
+def OMP_Error : Directive<[Spelling<"error">]> {
   let allowedClauses = [
     VersionedClause<OMPC_At, 51>,
     VersionedClause<OMPC_Message, 51>,
@@ -821,7 +823,7 @@ def OMP_Error : Directive<"error"> {
   let association = AS_None;
   let category = CA_Utility;
 }
-def OMP_Flush : Directive<"flush"> {
+def OMP_Flush : Directive<[Spelling<"flush">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_AcqRel, 50>,
     VersionedClause<OMPC_Acquire, 50>,
@@ -834,7 +836,7 @@ def OMP_Flush : Directive<"flush"> {
   let association = AS_None;
   let category = CA_Executable;
 }
-def OMP_For : Directive<"for"> {
+def OMP_For : Directive<[Spelling<"for">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Collapse>,
@@ -852,14 +854,14 @@ def OMP_For : Directive<"for"> {
   let category = CA_Executable;
   let languages = [L_C];
 }
-def OMP_Interchange : Directive<"interchange"> {
+def OMP_Interchange : Directive<[Spelling<"interchange">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Permutation>,
   ];
   let association = AS_Loop;
   let category = CA_Executable;
 }
-def OMP_interop : Directive<"interop"> {
+def OMP_interop : Directive<[Spelling<"interop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Depend>,
     VersionedClause<OMPC_Destroy>,
@@ -871,7 +873,7 @@ def OMP_interop : Directive<"interop"> {
   let association = AS_None;
   let category = CA_Executable;
 }
-def OMP_loop : Directive<"loop"> {
+def OMP_loop : Directive<[Spelling<"loop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_LastPrivate>,
     VersionedClause<OMPC_Private>,
@@ -885,18 +887,18 @@ def OMP_loop : Directive<"loop"> {
   let association = AS_Loop;
   let category = CA_Executable;
 }
-def OMP_masked : Directive<"masked"> {
+def OMP_masked : Directive<[Spelling<"masked">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Filter>,
   ];
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_Master : Directive<"master"> {
+def OMP_Master : Directive<[Spelling<"master">]> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_Metadirective : Directive<"metadirective"> {
+def OMP_Metadirective : Directive<[Spelling<"metadirective">]> {
   let allowedClauses = [
     VersionedClause<OMPC_When>,
   ];
@@ -907,11 +909,11 @@ def OMP_Metadirective : Directive<"metadirective"> {
   let association = AS_None;
   let category = CA_Meta;
 }
-def OMP_Nothing : Directive<"nothing"> {
+def OMP_Nothing : Directive<[Spelling<"nothing">]> {
   let association = AS_None;
   let category = CA_Utility;
 }
-def OMP_Ordered : Directive<"ordered"> {
+def OMP_Ordered : Directive<[Spelling<"ordered">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Depend>,
     VersionedClause<OMPC_Doacross, 52>,
@@ -924,7 +926,7 @@ def OMP_Ordered : Directive<"ordered"> {
   // There is also a block-associated "ordered" directive.
   let category = CA_Executable;
 }
-def OMP_Parallel : Directive<"parallel"> {
+def OMP_Parallel : Directive<[Spelling<"parallel">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Copyin>,
@@ -943,7 +945,7 @@ def OMP_Parallel : Directive<"parallel"> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_Requires : Directive<"requires"> {
+def OMP_Requires : Directive<[Spelling<"requires">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_UnifiedAddress>,
     VersionedClause<OMPC_UnifiedSharedMemory>,
@@ -962,11 +964,11 @@ def OMP_Requires : Directive<"requires"> {
   let association = AS_None;
   let category = CA_Informational;
 }
-def OMP_Reverse : Directive<"reverse"> {
+def OMP_Reverse : Directive<[Spelling<"reverse">]> {
   let association = AS_Loop;
   let category = CA_Executable;
 }
-def OMP_Scan : Directive<"scan"> {
+def OMP_Scan : Directive<[Spelling<"scan">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Exclusive>,
     VersionedClause<OMPC_Inclusive>,
@@ -974,7 +976,7 @@ def OMP_Scan : Directive<"scan"> {
   let association = AS_Separating;
   let category = CA_Subsidiary;
 }
-def OMP_Scope : Directive<"scope"> {
+def OMP_Scope : Directive<[Spelling<"scope">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Private, 51>,
     VersionedClause<OMPC_Reduction, 51>,
@@ -987,7 +989,7 @@ def OMP_Scope : Directive<"scope"> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_EndScope : Directive<"end scope"> {
+def OMP_EndScope : Directive<[Spelling<"end scope">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_NoWait>,
   ];
@@ -996,11 +998,11 @@ def OMP_EndScope : Directive<"end scope"> {
   let category = OMP_Scope.category;
   let languages = [L_Fortran];
 }
-def OMP_Section : Directive<"section"> {
+def OMP_Section : Directive<[Spelling<"section">]> {
   let association = AS_Separating;
   let category = CA_Subsidiary;
 }
-def OMP_Sections : Directive<"sections"> {
+def OMP_Sections : Directive<[Spelling<"sections">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_FirstPrivate>,
@@ -1012,7 +1014,7 @@ def OMP_Sections : Directive<"sections"> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_EndSections : Directive<"end sections"> {
+def OMP_EndSections : Directive<[Spelling<"end sections">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_NoWait>,
   ];
@@ -1021,7 +1023,7 @@ def OMP_EndSections : Directive<"end sections"> {
   let category = OMP_Sections.category;
   let languages = [L_Fortran];
 }
-def OMP_Simd : Directive<"simd"> {
+def OMP_Simd : Directive<[Spelling<"simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -1041,7 +1043,7 @@ def OMP_Simd : Directive<"simd"> {
   let association = AS_Loop;
   let category = CA_Executable;
 }
-def OMP_Single : Directive<"single"> {
+def OMP_Single : Directive<[Spelling<"single">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_CopyPrivate>,
@@ -1054,7 +1056,7 @@ def OMP_Single : Directive<"single"> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_EndSingle : Directive<"end single"> {
+def OMP_EndSingle : Directive<[Spelling<"end single">]> {
   let allowedClauses = [
     VersionedClause<OMPC_CopyPrivate>,
   ];
@@ -1066,7 +1068,7 @@ def OMP_EndSingle : Directive<"end single"> {
   let category = OMP_Single.category;
   let languages = [L_Fortran];
 }
-def OMP_Target : Directive<"target"> {
+def OMP_Target : Directive<[Spelling<"target">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Depend>,
@@ -1091,7 +1093,7 @@ def OMP_Target : Directive<"target"> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_TargetData : Directive<"target data"> {
+def OMP_TargetData : Directive<[Spelling<"target data">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Device>,
     VersionedClause<OMPC_If>,
@@ -1104,7 +1106,7 @@ def OMP_TargetData : Directive<"target data"> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_TargetEnterData : Directive<"target enter data"> {
+def OMP_TargetEnterData : Directive<[Spelling<"target enter data">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Depend>,
   ];
@@ -1119,7 +1121,7 @@ def OMP_TargetEnterData : Directive<"target enter data"> {
   let association = AS_None;
   let category = CA_Executable;
 }
-def OMP_TargetExitData : Directive<"target exit data"> {
+def OMP_TargetExitData : Directive<[Spelling<"target exit data">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Depend>,
   ];
@@ -1134,7 +1136,7 @@ def OMP_TargetExitData : Directive<"target exit data"> {
   let association = AS_None;
   let category = CA_Executable;
 }
-def OMP_TargetUpdate : Directive<"target update"> {
+def OMP_TargetUpdate : Directive<[Spelling<"target update">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Depend>,
     VersionedClause<OMPC_From>,
@@ -1148,7 +1150,7 @@ def OMP_TargetUpdate : Directive<"target update"> {
   let association = AS_None;
   let category = CA_Executable;
 }
-def OMP_Task : Directive<"task"> {
+def OMP_Task : Directive<[Spelling<"task">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Affinity, 50>,
     VersionedClause<OMPC_Allocate>,
@@ -1170,7 +1172,7 @@ def OMP_Task : Directive<"task"> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_TaskGroup : Directive<"taskgroup"> {
+def OMP_TaskGroup : Directive<[Spelling<"taskgroup">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate, 50>,
     VersionedClause<OMPC_TaskReduction, 50>,
@@ -1178,7 +1180,7 @@ def OMP_TaskGroup : Directive<"taskgroup"> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_TaskLoop : Directive<"taskloop"> {
+def OMP_TaskLoop : Directive<[Spelling<"taskloop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_FirstPrivate>,
@@ -1205,7 +1207,7 @@ def OMP_TaskLoop : Directive<"taskloop"> {
   let association = AS_Loop;
   let category = CA_Executable;
 }
-def OMP_TaskWait : Directive<"taskwait"> {
+def OMP_TaskWait : Directive<[Spelling<"taskwait">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Depend, 50>,
     VersionedClause<OMPC_NoWait, 51>,
@@ -1213,11 +1215,11 @@ def OMP_TaskWait : Directive<"taskwait"> {
   let association = AS_None;
   let category = CA_Executable;
 }
-def OMP_TaskYield : Directive<"taskyield"> {
+def OMP_TaskYield : Directive<[Spelling<"taskyield">]> {
   let association = AS_None;
   let category = CA_Executable;
 }
-def OMP_Teams : Directive<"teams"> {
+def OMP_Teams : Directive<[Spelling<"teams">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_FirstPrivate>,
@@ -1235,30 +1237,30 @@ def OMP_Teams : Directive<"teams"> {
   let association = AS_Block;
   let category = CA_Executable;
 }
-def OMP_ThreadPrivate : Directive<"threadprivate"> {
+def OMP_ThreadPrivate : Directive<[Spelling<"threadprivate">]> {
   let association = AS_None;
   let category = CA_Declarative;
 }
-def OMP_Tile : Directive<"tile"> {
+def OMP_Tile : Directive<[Spelling<"tile">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Sizes, 51>,
   ];
   let association = AS_Loop;
   let category = CA_Executable;
 }
-def OMP_Stripe : Directive<"stripe"> {
+def OMP_Stripe : Directive<[Spelling<"stripe">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Sizes, 60>,
   ];
   let association = AS_Loop;
   let category = CA_Executable;
 }
-def OMP_Unknown : Directive<"unknown"> {
+def OMP_Unknown : Directive<[Spelling<"unknown">]> {
   let isDefault = true;
   let association = AS_None;
   let category = CA_Utility;
 }
-def OMP_Unroll : Directive<"unroll"> {
+def OMP_Unroll : Directive<[Spelling<"unroll">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_Full, 51>,
     VersionedClause<OMPC_Partial, 51>,
@@ -1266,7 +1268,7 @@ def OMP_Unroll : Directive<"unroll"> {
   let association = AS_Loop;
   let category = CA_Executable;
 }
-def OMP_Workshare : Directive<"workshare"> {
+def OMP_Workshare : Directive<[Spelling<"workshare">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_NoWait>,
   ];
@@ -1274,7 +1276,7 @@ def OMP_Workshare : Directive<"workshare"> {
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_EndWorkshare : Directive<"end workshare"> {
+def OMP_EndWorkshare : Directive<[Spelling<"end workshare">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_NoWait>,
   ];
@@ -1290,7 +1292,7 @@ def OMP_EndWorkshare : Directive<"end workshare"> {
 // follows "xyz".
 //===----------------------------------------------------------------------===//
 
-def OMP_DistributeParallelDo : Directive<"distribute parallel do"> {
+def OMP_DistributeParallelDo : Directive<[Spelling<"distribute parallel do">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Copyin>,
@@ -1315,7 +1317,8 @@ def OMP_DistributeParallelDo : Directive<"distribute parallel do"> {
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_DistributeParallelDoSimd : Directive<"distribute parallel do simd"> {
+def OMP_DistributeParallelDoSimd
+    : Directive<[Spelling<"distribute parallel do simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -1342,7 +1345,8 @@ def OMP_DistributeParallelDoSimd : Directive<"distribute parallel do simd"> {
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_DistributeParallelFor : Directive<"distribute parallel for"> {
+def OMP_DistributeParallelFor
+    : Directive<[Spelling<"distribute parallel for">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Collapse>,
@@ -1365,7 +1369,8 @@ def OMP_DistributeParallelFor : Directive<"distribute parallel for"> {
   let category = CA_Executable;
   let languages = [L_C];
 }
-def OMP_DistributeParallelForSimd : Directive<"distribute parallel for simd"> {
+def OMP_DistributeParallelForSimd
+    : Directive<[Spelling<"distribute parallel for simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -1393,7 +1398,7 @@ def OMP_DistributeParallelForSimd : Directive<"distribute parallel for simd"> {
   let category = CA_Executable;
   let languages = [L_C];
 }
-def OMP_DistributeSimd : Directive<"distribute simd"> {
+def OMP_DistributeSimd : Directive<[Spelling<"distribute simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -1420,7 +1425,7 @@ def OMP_DistributeSimd : Directive<"distribute simd"> {
   let leafConstructs = [OMP_Distribute, OMP_Simd];
   let category = CA_Executable;
 }
-def OMP_DoSimd : Directive<"do simd"> {
+def OMP_DoSimd : Directive<[Spelling<"do simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_FirstPrivate>,
@@ -1443,7 +1448,7 @@ def OMP_DoSimd : Directive<"do simd"> {
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_EndDoSimd : Directive<"end do simd"> {
+def OMP_EndDoSimd : Directive<[Spelling<"end do simd">]> {
   let allowedOnceClauses = [
     VersionedClause<OMPC_NoWait>,
   ];
@@ -1452,7 +1457,7 @@ def OMP_EndDoSimd : Directive<"end do simd"> {
   let category = OMP_DoSimd.category;
   let languages = [L_Fortran];
 }
-def OMP_ForSimd : Directive<"for simd"> {
+def OMP_ForSimd : Directive<[Spelling<"for simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -1474,7 +1479,7 @@ def OMP_ForSimd : Directive<"for simd"> {
   let leafConstructs = [OMP_For, OMP_Simd];
   let category = CA_Executable;
 }
-def OMP_target_loop : Directive<"target loop"> {
+def OMP_target_loop : Directive<[Spelling<"target loop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Depend>,
@@ -1504,7 +1509,7 @@ def OMP_target_loop : Directive<"target loop"> {
   let leafConstructs = [OMP_Target, OMP_loop];
   let category = CA_Executable;
 }
-def OMP_MaskedTaskloop : Directive<"masked taskloop"> {
+def OMP_MaskedTaskloop : Directive<[Spelling<"masked taskloop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Collapse>,
@@ -1528,7 +1533,7 @@ def OMP_MaskedTaskloop : Directive<"masked taskloop"> {
   let leafConstructs = [OMP_masked, OMP_TaskLoop];
   let category = CA_Executable;
 }
-def OMP_MaskedTaskloopSimd : Directive<"masked taskloop simd"> {
+def OMP_MaskedTaskloopSimd : Directive<[Spelling<"masked taskloop simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -1558,7 +1563,7 @@ def OMP_MaskedTaskloopSimd : Directive<"masked taskloop simd"> {
   let leafConstructs = [OMP_masked, OMP_TaskLoop, OMP_Simd];
   let category = CA_Executable;
 }
-def OMP_MasterTaskloop : Directive<"master taskloop"> {
+def OMP_MasterTaskloop : Directive<[Spelling<"master taskloop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Collapse>,
@@ -1581,7 +1586,7 @@ def OMP_MasterTaskloop : Directive<"master taskloop"> {
   let leafConstructs = [OMP_Master, OMP_TaskLoop];
   let category = CA_Executable;
 }
-def OMP_MasterTaskloopSimd : Directive<"master taskloop simd"> {
+def OMP_MasterTaskloopSimd : Directive<[Spelling<"master taskloop simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -1610,7 +1615,7 @@ def OMP_MasterTaskloopSimd : Directive<"master taskloop simd"> {
   let leafConstructs = [OMP_Master, OMP_TaskLoop, OMP_Simd];
   let category = CA_Executable;
 }
-def OMP_ParallelDo : Directive<"parallel do"> {
+def OMP_ParallelDo : Directive<[Spelling<"parallel do">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Copyin>,
     VersionedClause<OMPC_Default>,
@@ -1634,7 +1639,7 @@ def OMP_ParallelDo : Directive<"parallel do"> {
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_ParallelDoSimd : Directive<"parallel do simd"> {
+def OMP_ParallelDoSimd : Directive<[Spelling<"parallel do simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -1663,7 +1668,7 @@ def OMP_ParallelDoSimd : Directive<"parallel do simd"> {
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_ParallelFor : Directive<"parallel for"> {
+def OMP_ParallelFor : Directive<[Spelling<"parallel for">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Collapse>,
@@ -1687,7 +1692,7 @@ def OMP_ParallelFor : Directive<"parallel for"> {
   let category = CA_Executable;
   let languages = [L_C];
 }
-def OMP_ParallelForSimd : Directive<"parallel for simd"> {
+def OMP_ParallelForSimd : Directive<[Spelling<"parallel for simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -1715,7 +1720,7 @@ def OMP_ParallelForSimd : Directive<"parallel for simd"> {
   let category = CA_Executable;
   let languages = [L_C];
 }
-def OMP_parallel_loop : Directive<"parallel loop"> {
+def OMP_parallel_loop : Directive<[Spelling<"parallel loop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Copyin>,
@@ -1738,7 +1743,7 @@ def OMP_parallel_loop : Directive<"parallel loop"> {
   let leafConstructs = [OMP_Parallel, OMP_loop];
   let category = CA_Executable;
 }
-def OMP_ParallelMasked : Directive<"parallel masked"> {
+def OMP_ParallelMasked : Directive<[Spelling<"parallel masked">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Copyin>,
@@ -1756,8 +1761,8 @@ def OMP_ParallelMasked : Directive<"parallel masked"> {
   let leafConstructs = [OMP_Parallel, OMP_masked];
   let category = CA_Executable;
 }
-def OMP_ParallelMaskedTaskloop :
-    Directive<"parallel masked taskloop"> {
+def OMP_ParallelMaskedTaskloop
+    : Directive<[Spelling<"parallel masked taskloop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Collapse>,
@@ -1784,8 +1789,8 @@ def OMP_ParallelMaskedTaskloop :
   let leafConstructs = [OMP_Parallel, OMP_masked, OMP_TaskLoop];
   let category = CA_Executable;
 }
-def OMP_ParallelMaskedTaskloopSimd :
-    Directive<"parallel masked taskloop simd"> {
+def OMP_ParallelMaskedTaskloopSimd
+    : Directive<[Spelling<"parallel masked taskloop simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -1818,7 +1823,7 @@ def OMP_ParallelMaskedTaskloopSimd :
   let leafConstructs = [OMP_Parallel, OMP_masked, OMP_TaskLoop, OMP_Simd];
   let category = CA_Executable;
 }
-def OMP_ParallelMaster : Directive<"parallel master"> {
+def OMP_ParallelMaster : Directive<[Spelling<"parallel master">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Copyin>,
@@ -1835,8 +1840,8 @@ def OMP_ParallelMaster : Directive<"parallel master"> {
   let leafConstructs = [OMP_Parallel, OMP_Master];
   let category = CA_Executable;
 }
-def OMP_ParallelMasterTaskloop :
-    Directive<"parallel master taskloop"> {
+def OMP_ParallelMasterTaskloop
+    : Directive<[Spelling<"parallel master taskloop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Collapse>,
@@ -1862,8 +1867,8 @@ def OMP_ParallelMasterTaskloop :
   let leafConstructs = [OMP_Parallel, OMP_Master, OMP_TaskLoop];
   let category = CA_Executable;
 }
-def OMP_ParallelMasterTaskloopSimd :
-    Directive<"parallel master taskloop simd"> {
+def OMP_ParallelMasterTaskloopSimd
+    : Directive<[Spelling<"parallel master taskloop simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -1895,7 +1900,7 @@ def OMP_ParallelMasterTaskloopSimd :
   let leafConstructs = [OMP_Parallel, OMP_Master, OMP_TaskLoop, OMP_Simd];
   let category = CA_Executable;
 }
-def OMP_ParallelSections : Directive<"parallel sections"> {
+def OMP_ParallelSections : Directive<[Spelling<"parallel sections">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Copyin>,
@@ -1915,7 +1920,7 @@ def OMP_ParallelSections : Directive<"parallel sections"> {
   let leafConstructs = [OMP_Parallel, OMP_Sections];
   let category = CA_Executable;
 }
-def OMP_ParallelWorkshare : Directive<"parallel workshare"> {
+def OMP_ParallelWorkshare : Directive<[Spelling<"parallel workshare">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Copyin>,
@@ -1934,7 +1939,7 @@ def OMP_ParallelWorkshare : Directive<"parallel workshare"> {
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_TargetParallel : Directive<"target parallel"> {
+def OMP_TargetParallel : Directive<[Spelling<"target parallel">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Default>,
@@ -1962,7 +1967,7 @@ def OMP_TargetParallel : Directive<"target parallel"> {
   let leafConstructs = [OMP_Target, OMP_Parallel];
   let category = CA_Executable;
 }
-def OMP_TargetParallelDo : Directive<"target parallel do"> {
+def OMP_TargetParallelDo : Directive<[Spelling<"target parallel do">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocator>,
     VersionedClause<OMPC_Default>,
@@ -1994,7 +1999,8 @@ def OMP_TargetParallelDo : Directive<"target parallel do"> {
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_TargetParallelDoSimd : Directive<"target parallel do simd"> {
+def OMP_TargetParallelDoSimd
+    : Directive<[Spelling<"target parallel do simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -2028,7 +2034,7 @@ def OMP_TargetParallelDoSimd : Directive<"target parallel do simd"> {
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_TargetParallelFor : Directive<"target parallel for"> {
+def OMP_TargetParallelFor : Directive<[Spelling<"target parallel for">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Collapse>,
@@ -2063,7 +2069,8 @@ def OMP_TargetParallelFor : Directive<"target parallel for"> {
   let category = CA_Executable;
   let languages = [L_C];
 }
-def OMP_TargetParallelForSimd : Directive<"target parallel for simd"> {
+def OMP_TargetParallelForSimd
+    : Directive<[Spelling<"target parallel for simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -2102,7 +2109,7 @@ def OMP_TargetParallelForSimd : Directive<"target parallel for simd"> {
   let category = CA_Executable;
   let languages = [L_C];
 }
-def OMP_target_parallel_loop : Directive<"target parallel loop"> {
+def OMP_target_parallel_loop : Directive<[Spelling<"target parallel loop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Depend>,
@@ -2134,7 +2141,7 @@ def OMP_target_parallel_loop : Directive<"target parallel loop"> {
   let leafConstructs = [OMP_Target, OMP_Parallel, OMP_loop];
   let category = CA_Executable;
 }
-def OMP_TargetSimd : Directive<"target simd"> {
+def OMP_TargetSimd : Directive<[Spelling<"target simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -2170,7 +2177,7 @@ def OMP_TargetSimd : Directive<"target simd"> {
   let leafConstructs = [OMP_Target, OMP_Simd];
   let category = CA_Executable;
 }
-def OMP_TargetTeams : Directive<"target teams"> {
+def OMP_TargetTeams : Directive<[Spelling<"target teams">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Depend>,
@@ -2198,7 +2205,8 @@ def OMP_TargetTeams : Directive<"target teams"> {
   let leafConstructs = [OMP_Target, OMP_Teams];
   let category = CA_Executable;
 }
-def OMP_TargetTeamsDistribute : Directive<"target teams distribute"> {
+def OMP_TargetTeamsDistribute
+    : Directive<[Spelling<"target teams distribute">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Depend>,
@@ -2229,8 +2237,8 @@ def OMP_TargetTeamsDistribute : Directive<"target teams distribute"> {
   let leafConstructs = [OMP_Target, OMP_Teams, OMP_Distribute];
   let category = CA_Executable;
 }
-def OMP_TargetTeamsDistributeParallelDo :
-    Directive<"target teams distribute parallel do"> {
+def OMP_TargetTeamsDistributeParallelDo
+    : Directive<[Spelling<"target teams distribute parallel do">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Depend>,
@@ -2265,8 +2273,8 @@ def OMP_TargetTeamsDistributeParallelDo :
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_TargetTeamsDistributeParallelDoSimd :
-    Directive<"target teams distribute parallel do simd"> {
+def OMP_TargetTeamsDistributeParallelDoSimd
+    : Directive<[Spelling<"target teams distribute parallel do simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -2305,8 +2313,8 @@ def OMP_TargetTeamsDistributeParallelDoSimd :
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_TargetTeamsDistributeParallelFor :
-    Directive<"target teams distribute parallel for"> {
+def OMP_TargetTeamsDistributeParallelFor
+    : Directive<[Spelling<"target teams distribute parallel for">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Collapse>,
@@ -2342,8 +2350,8 @@ def OMP_TargetTeamsDistributeParallelFor :
   let category = CA_Executable;
   let languages = [L_C];
 }
-def OMP_TargetTeamsDistributeParallelForSimd :
-    Directive<"target teams distribute parallel for simd"> {
+def OMP_TargetTeamsDistributeParallelForSimd
+    : Directive<[Spelling<"target teams distribute parallel for simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -2384,8 +2392,8 @@ def OMP_TargetTeamsDistributeParallelForSimd :
   let category = CA_Executable;
   let languages = [L_C];
 }
-def OMP_TargetTeamsDistributeSimd :
-    Directive<"target teams distribute simd"> {
+def OMP_TargetTeamsDistributeSimd
+    : Directive<[Spelling<"target teams distribute simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -2420,7 +2428,7 @@ def OMP_TargetTeamsDistributeSimd :
   let leafConstructs = [OMP_Target, OMP_Teams, OMP_Distribute, OMP_Simd];
   let category = CA_Executable;
 }
-def OMP_target_teams_loop : Directive<"target teams loop"> {
+def OMP_target_teams_loop : Directive<[Spelling<"target teams loop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_DefaultMap>,
@@ -2451,7 +2459,7 @@ def OMP_target_teams_loop : Directive<"target teams loop"> {
   let leafConstructs = [OMP_Target, OMP_Teams, OMP_loop];
   let category = CA_Executable;
 }
-def OMP_TaskLoopSimd : Directive<"taskloop simd"> {
+def OMP_TaskLoopSimd : Directive<[Spelling<"taskloop simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -2484,7 +2492,7 @@ def OMP_TaskLoopSimd : Directive<"taskloop simd"> {
   let leafConstructs = [OMP_TaskLoop, OMP_Simd];
   let category = CA_Executable;
 }
-def OMP_TeamsDistribute : Directive<"teams distribute"> {
+def OMP_TeamsDistribute : Directive<[Spelling<"teams distribute">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Collapse>,
@@ -2506,8 +2514,8 @@ def OMP_TeamsDistribute : Directive<"teams distribute"> {
   let leafConstructs = [OMP_Teams, OMP_Distribute];
   let category = CA_Executable;
 }
-def OMP_TeamsDistributeParallelDo :
-    Directive<"teams distribute parallel do"> {
+def OMP_TeamsDistributeParallelDo
+    : Directive<[Spelling<"teams distribute parallel do">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Copyin>,
@@ -2534,8 +2542,8 @@ def OMP_TeamsDistributeParallelDo :
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_TeamsDistributeParallelDoSimd :
-    Directive<"teams distribute parallel do simd"> {
+def OMP_TeamsDistributeParallelDoSimd
+    : Directive<[Spelling<"teams distribute parallel do simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -2566,8 +2574,8 @@ def OMP_TeamsDistributeParallelDoSimd :
   let category = CA_Executable;
   let languages = [L_Fortran];
 }
-def OMP_TeamsDistributeParallelFor :
-    Directive<"teams distribute parallel for"> {
+def OMP_TeamsDistributeParallelFor
+    : Directive<[Spelling<"teams distribute parallel for">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_Collapse>,
@@ -2592,8 +2600,8 @@ def OMP_TeamsDistributeParallelFor :
   let category = CA_Executable;
   let languages = [L_C];
 }
-def OMP_TeamsDistributeParallelForSimd :
-    Directive<"teams distribute parallel for simd"> {
+def OMP_TeamsDistributeParallelForSimd
+    : Directive<[Spelling<"teams distribute parallel for simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -2623,7 +2631,7 @@ def OMP_TeamsDistributeParallelForSimd :
   let category = CA_Executable;
   let languages = [L_C];
 }
-def OMP_TeamsDistributeSimd : Directive<"teams distribute simd"> {
+def OMP_TeamsDistributeSimd : Directive<[Spelling<"teams distribute simd">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Aligned>,
     VersionedClause<OMPC_Allocate>,
@@ -2650,7 +2658,7 @@ def OMP_TeamsDistributeSimd : Directive<"teams distribute simd"> {
   let leafConstructs = [OMP_Teams, OMP_Distribute, OMP_Simd];
   let category = CA_Executable;
 }
-def OMP_teams_loop : Directive<"teams loop"> {
+def OMP_teams_loop : Directive<[Spelling<"teams loop">]> {
   let allowedClauses = [
     VersionedClause<OMPC_Allocate>,
     VersionedClause<OMPC_FirstPrivate>,
diff --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h b/llvm/include/llvm/TableGen/DirectiveEmitter.h
index 48e18de0904c0..1235b7638e761 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/Support/MathExtras.h"
 #include "llvm/TableGen/Record.h"
 #include <algorithm>
 #include <string>
@@ -94,6 +95,52 @@ class DirectiveLanguage {
   }
 };
 
+class Versioned {
+public:
+  int getMinVersion(const Record *R) const {
+    int64_t Min = R->getValueAsInt("minVersion");
+    assert(llvm::isInt<IntWidth>(Min) && "Value out of range of 'int'");
+    return Min;
+  }
+
+  int getMaxVersion(const Record *R) const {
+    int64_t Max = R->getValueAsInt("maxVersion");
+    assert(llvm::isInt<IntWidth>(Max) && "Value out of range of 'int'");
+    return Max;
+  }
+
+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 = INT_MAX;                                                         \
+  }
+
+STRUCT_VERSION_RANGE;
+
+class Spelling : public Versioned {
+public:
+  using Value = std::pair<StringRef, VersionRange>;
+
+  Spelling(const Record *Def) : Def(Def) {}
+
+  StringRef getText() const { return Def->getValueAsString("spelling"); }
+  VersionRange getVersions() const {
+    return VersionRange{getMinVersion(Def), getMaxVersion(Def)};
+  }
+
+  Value get() const { return std::make_pair(getText(), getVersions()); }
+
+private:
+  const Record *Def;
+};
+
 // Note: In all the classes below, allow implicit construction from Record *,
 // to allow writing code like:
 //  for (const Directive D : getDirectives()) {
@@ -109,7 +156,33 @@ class BaseRecord {
 public:
   BaseRecord(const Record *Def) : Def(Def) {}
 
-  StringRef getName() const { return Def->getValueAsString("name"); }
+  std::vector<Spelling::Value> getSpellings() const {
+    std::vector<Spelling::Value> List;
+    llvm::transform(
+        Def->getValueAsListOfDefs("spellings"), std::back_inserter(List),
+        [](const Record *R) { return Spelling(R).get(); });
+    return List;
+  }
+
+  StringRef getSpellingForIdentifier() const {
+    // From all spellings, pick the first one with the minimum version
+    // (i.e. pick the first from all the oldest ones). This guarantees
+    // that given several equivalent (in terms of versions) names, the
+    // first one is used, e.g. given
+    //   Clause<[Spelling<"foo">, Spelling<"bar">]> ...
+    // "foo" will be the selected spelling.
+    //
+    // This is a suitable spelling for generating an identifier name,
+    // since it will remain unchanged when any potential new spellings
+    // are added.
+    Spelling::Value Oldest{"not found", {/*Min=*/INT_MAX, 0}};
+    for (auto V : getSpellings()) {
+      if (V.second.Min < Oldest.second.Min) {
+        Oldest = V;
+      }
+    }
+    return Oldest.first;
+  }
 
   // Returns the name of the directive formatted for output. Whitespace are
   // replaced with underscores.
@@ -145,7 +218,9 @@ class BaseRecord {
   }
 
   std::string getFormattedName() const {
-    return getSnakeName(Def->getValueAsString("name"));
+    if (auto maybeName = Def->getValueAsOptionalString("name"))
+      return getSnakeName(*maybeName);
+    return getSnakeName(getSpellingForIdentifier());
   }
 
   bool isDefault() const { return Def->getValueAsBit("isDefault"); }
@@ -197,7 +272,7 @@ class Directive : public BaseRecord {
 
   // Clang uses a different format for names of its directives enum.
   std::string getClangAccSpelling() const {
-    StringRef Name = Def->getValueAsString("name");
+    StringRef Name = getSpellingForIdentifier();
 
     // Clang calls the 'unknown' value 'invalid'.
     if (Name == "unknown")
@@ -229,7 +304,7 @@ class Clause : public BaseRecord {
   // ex: async -> Async
   //     num_threads -> NumThreads
   std::string getFormattedParserClassName() const {
-    StringRef Name = Def->getValueAsString("name");
+    StringRef Name = getSpellingForIdentifier();
     return BaseRecord::getUpperCamelName(Name, "_");
   }
 
@@ -241,7 +316,7 @@ class Clause : public BaseRecord {
         !ClangSpelling.empty())
       return ClangSpelling.str();
 
-    StringRef Name = Def->getValueAsString("name");
+    StringRef Name = getSpellingForIdentifier();
     return BaseRecord::getUpperCamelName(Name, "_");
   }
 
diff --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td
index f756f54c03bfb..8196a30d03df4 100644
--- a/llvm/test/TableGen/directive1.td
+++ b/llvm/test/TableGen/directive1.td
@@ -18,7 +18,7 @@ def TDLCV_vala : EnumVal<"vala",1,1> {}
 def TDLCV_valb : EnumVal<"valb",2,1> {}
 def TDLCV_valc : EnumVal<"valc",3,0> { let isDefault = 1; }
 
-def TDLC_ClauseA : Clause<"clausea"> {
+def TDLC_ClauseA : Clause<[Spelling<"clausea">]> {
   let enumClauseValue = "AKind";
   let allowedClauseValues = [
     TDLCV_vala,
@@ -27,19 +27,18 @@ def TDLC_ClauseA : Clause<"clausea"> {
   ];
 }
 
-def TDLC_ClauseB : Clause<"clauseb"> {
+def TDLC_ClauseB : Clause<[Spelling<"clauseb">]> {
   let flangClass = "IntExpr";
   let isValueOptional = 1;
   let isDefault = 1;
 }
 
-def TDLC_ClauseC : Clause<"clausec"> {
-  let aliases = ["ccccccc"];
+def TDLC_ClauseC : Clause<[Spelling<"clausec">, Spelling<"ccccccc">]> {
   let flangClass = "IntExpr";
   let isValueList = 1;
 }
 
-def TDL_DirA : Directive<"dira"> {
+def TDL_DirA : Directive<[Spelling<"dira">]> {
   let allowedClauses = [
     VersionedClause<TDLC_ClauseA>,
     VersionedClause<TDLC_ClauseB>
@@ -54,15 +53,18 @@ def TDL_DirA : Directive<"dira"> {
 // CHECK-EMPTY:
 // 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/Support/Compiler.h"
 // CHECK-NEXT:  #include <cstddef>
+// CHECK-NEXT:  #include <utility>
 // CHECK-EMPTY:
 // CHECK-NEXT:  namespace llvm {
-// CHECK-NEXT:  class StringRef;
 // CHECK-NEXT:  namespace tdl {
 // 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,
@@ -124,13 +126,20 @@ def TDL_DirA : Directive<"dira"> {
 // CHECK-NEXT:  constexpr auto TDLCV_valc = AKind::TDLCV_valc;
 // CHECK-EMPTY:
 // CHECK-NEXT:  // Enumeration helper functions
-// CHECK-NEXT:  LLVM_ABI Directive getTdlDirectiveKind(StringRef Str);
+// CHECK-NEXT:  LLVM_ABI std::pair<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 StringRef getTdlDirectiveName(Directive D);
+// CHECK-NEXT:  LLVM_ABI std::pair<Clause, VersionRange> getTdlClauseKindAndVersions(StringRef Str);
 // CHECK-EMPTY:
-// CHECK-NEXT:  LLVM_ABI Clause getTdlClauseKind(StringRef Str);
+// CHECK-NEXT:  inline Clause getTdlClauseKind(StringRef Str) {
+// CHECK-NEXT:    return getTdlClauseKindAndVersions(Str).first;
+// CHECK-NEXT:  }
 // CHECK-EMPTY:
-// CHECK-NEXT:  LLVM_ABI StringRef getTdlClauseName(Clause C);
+// CHECK-NEXT:  LLVM_ABI StringRef getTdlClauseName(Clause C, unsigned Ver = 0);
 // CHECK-EMPTY:
 // CHECK-NEXT:  /// Return true if \p C is a valid clause for \p D in version \p Version.
 // CHECK-NEXT:  LLVM_ABI bool isAllowedClauseForDirective(Directive D, Clause C, unsigned Version);
@@ -312,14 +321,16 @@ def TDL_DirA : Directive<"dira"> {
 // IMPL-NEXT:  #undef GEN_DIRECTIVES_IMPL
 // IMPL-EMPTY:
 // IMPL-NEXT:  #include "llvm/Support/ErrorHandling.h"
+// IMPL-NEXT:  #include <utility>
 // IMPL-EMPTY:
-// IMPL-NEXT:  llvm::tdl::Directive llvm::tdl::getTdlDirectiveKind(llvm::StringRef Str) {
-// IMPL-NEXT:    return StringSwitch<Directive>(Str)
-// IMPL-NEXT:      .Case("dira",TDLD_dira)
-// IMPL-NEXT:      .Default(TDLD_dira);
-// IMPL-NEXT:  }
+// 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:     .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) {
+// IMPL-NEXT:  llvm::StringRef llvm::tdl::getTdlDirectiveName(llvm::tdl::Directive Kind, unsigned) {
 // IMPL-NEXT:    switch (Kind) {
 // IMPL-NEXT:      case TDLD_dira:
 // IMPL-NEXT:        return "dira";
@@ -327,15 +338,16 @@ def TDL_DirA : Directive<"dira"> {
 // IMPL-NEXT:    llvm_unreachable("Invalid Tdl Directive kind");
 // IMPL-NEXT:  }
 // IMPL-EMPTY:
-// IMPL-NEXT:  llvm::tdl::Clause llvm::tdl::getTdlClauseKind(llvm::StringRef Str) {
-// IMPL-NEXT:    return StringSwitch<Clause>(Str)
-// IMPL-NEXT:      .Case("clausea",TDLC_clausea)
-// IMPL-NEXT:      .Case("clauseb",TDLC_clauseb)
-// IMPL-NEXT:      .Case("clausec",TDLC_clausec)
-// IMPL-NEXT:      .Default(TDLC_clauseb);
-// IMPL-NEXT:  }
+// 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:     .Case("clausea", {TDLC_clausea, All})
+// IMPL-NEXT:     .Case("clauseb", {TDLC_clauseb, All})
+// IMPL-NEXT:     .Case("clausec", {TDLC_clausec, All})
+// IMPL-NEXT:     .Default({TDLC_clauseb, All});
+// IMPL-NEXT: }
 // IMPL-EMPTY:
-// IMPL-NEXT:  llvm::StringRef llvm::tdl::getTdlClauseName(llvm::tdl::Clause Kind) {
+// IMPL-NEXT:  llvm::StringRef llvm::tdl::getTdlClauseName(llvm::tdl::Clause Kind, unsigned) {
 // IMPL-NEXT:    switch (Kind) {
 // IMPL-NEXT:      case TDLC_clausea:
 // IMPL-NEXT:        return "clausea";
diff --git a/llvm/test/TableGen/directive2.td b/llvm/test/TableGen/directive2.td
index 3de66cfea94b9..ead6aa2637b76 100644
--- a/llvm/test/TableGen/directive2.td
+++ b/llvm/test/TableGen/directive2.td
@@ -13,26 +13,26 @@ def TestDirectiveLanguage : DirectiveLanguage {
   let flangClauseBaseClass = "TdlClause";
 }
 
-def TDLC_ClauseA : Clause<"clausea"> {
+def TDLC_ClauseA : Clause<[Spelling<"clausea">]> {
   let isImplicit = 1;
 }
-def TDLC_ClauseB : Clause<"clauseb"> {
+def TDLC_ClauseB : Clause<[Spelling<"clauseb">]> {
   let isDefault = 1;
   let flangClass = "IntExpr";
   let isValueList = 1;
 }
-def TDLC_ClauseC : Clause<"clausec"> {
+def TDLC_ClauseC : Clause<[Spelling<"clausec">]> {
   let clangClass = "ClauseC";
   let flangClass = "Name";
   let defaultValue = "*";
   let isValueOptional = 1;
 }
-def TDLC_ClauseD : Clause<"claused"> {
+def TDLC_ClauseD : Clause<[Spelling<"claused">]> {
   let clangClass = "ClauseD";
   let isImplicit = 1;
 }
 
-def TDL_DirA : Directive<"dira"> {
+def TDL_DirA : Directive<[Spelling<"dira">]> {
   let allowedClauses = [
     VersionedClause<TDLC_ClauseA, 2, 4>,
     VersionedClause<TDLC_ClauseB, 2>
@@ -46,13 +46,16 @@ def TDL_DirA : Directive<"dira"> {
 // CHECK-NEXT:  #define LLVM_Tdl_INC
 // CHECK-EMPTY:
 // CHECK-NEXT:  #include "llvm/ADT/ArrayRef.h"
+// CHECK-NEXT:  #include "llvm/ADT/StringRef.h"
 // CHECK-NEXT:  #include "llvm/Support/Compiler.h"
 // CHECK-NEXT:  #include <cstddef>
+// CHECK-NEXT:  #include <utility>
 // CHECK-EMPTY:
 // CHECK-NEXT:  namespace llvm {
-// CHECK-NEXT:  class StringRef;
 // 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,
@@ -99,13 +102,20 @@ def TDL_DirA : Directive<"dira"> {
 // CHECK-NEXT:  static constexpr std::size_t Clause_enumSize = 4;
 // CHECK-EMPTY:
 // CHECK-NEXT:  // Enumeration helper functions
-// CHECK-NEXT:  LLVM_ABI Directive getTdlDirectiveKind(StringRef Str);
+// CHECK-NEXT:  LLVM_ABI std::pair<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 StringRef getTdlDirectiveName(Directive D);
+// CHECK-NEXT:  LLVM_ABI std::pair<Clause, VersionRange> getTdlClauseKindAndVersions(StringRef Str);
 // CHECK-EMPTY:
-// CHECK-NEXT:  LLVM_ABI Clause getTdlClauseKind(StringRef Str);
+// CHECK-NEXT:  inline Clause getTdlClauseKind(StringRef Str) {
+// CHECK-NEXT:    return getTdlClauseKindAndVersions(Str).first;
+// CHECK-NEXT:  }
 // CHECK-EMPTY:
-// CHECK-NEXT:  LLVM_ABI StringRef getTdlClauseName(Clause C);
+// CHECK-NEXT:  LLVM_ABI StringRef getTdlClauseName(Clause C, unsigned Ver = 0);
 // CHECK-EMPTY:
 // CHECK-NEXT:  /// Return true if \p C is a valid clause for \p D in version \p Version.
 // CHECK-NEXT:  LLVM_ABI bool isAllowedClauseForDirective(Directive D, Clause C, unsigned Version);
@@ -258,14 +268,16 @@ def TDL_DirA : Directive<"dira"> {
 // IMPL-NEXT:  #undef GEN_DIRECTIVES_IMPL
 // IMPL-EMPTY:
 // IMPL-NEXT:  #include "llvm/Support/ErrorHandling.h"
+// IMPL-NEXT:  #include <utility>
 // IMPL-EMPTY:
-// IMPL-NEXT:  llvm::tdl::Directive llvm::tdl::getTdlDirectiveKind(llvm::StringRef Str) {
-// IMPL-NEXT:    return StringSwitch<Directive>(Str)
-// IMPL-NEXT:      .Case("dira",TDLD_dira)
-// IMPL-NEXT:      .Default(TDLD_dira);
-// IMPL-NEXT:  }
+// 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:     .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) {
+// IMPL-NEXT:  llvm::StringRef llvm::tdl::getTdlDirectiveName(llvm::tdl::Directive Kind, unsigned) {
 // IMPL-NEXT:    switch (Kind) {
 // IMPL-NEXT:      case TDLD_dira:
 // IMPL-NEXT:        return "dira";
@@ -273,16 +285,17 @@ def TDL_DirA : Directive<"dira"> {
 // IMPL-NEXT:    llvm_unreachable("Invalid Tdl Directive kind");
 // IMPL-NEXT:  }
 // IMPL-EMPTY:
-// IMPL-NEXT:  llvm::tdl::Clause llvm::tdl::getTdlClauseKind(llvm::StringRef Str) {
-// IMPL-NEXT:    return StringSwitch<Clause>(Str)
-// IMPL-NEXT:      .Case("clausea",TDLC_clauseb)
-// IMPL-NEXT:      .Case("clauseb",TDLC_clauseb)
-// IMPL-NEXT:      .Case("clausec",TDLC_clausec)
-// IMPL-NEXT:      .Case("claused",TDLC_clauseb)
-// IMPL-NEXT:      .Default(TDLC_clauseb);
-// IMPL-NEXT:  }
+// 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:     .Case("clausea", {TDLC_clauseb, All})
+// IMPL-NEXT:     .Case("clauseb", {TDLC_clauseb, All})
+// IMPL-NEXT:     .Case("clausec", {TDLC_clausec, All})
+// IMPL-NEXT:     .Case("claused", {TDLC_clauseb, All})
+// IMPL-NEXT:     .Default({TDLC_clauseb, All});
+// IMPL-NEXT: }
 // IMPL-EMPTY:
-// IMPL-NEXT:  llvm::StringRef llvm::tdl::getTdlClauseName(llvm::tdl::Clause Kind) {
+// IMPL-NEXT:  llvm::StringRef llvm::tdl::getTdlClauseName(llvm::tdl::Clause Kind, unsigned) {
 // IMPL-NEXT:    switch (Kind) {
 // IMPL-NEXT:      case TDLC_clausea:
 // IMPL-NEXT:        return "clausea";
diff --git a/llvm/test/TableGen/directive3.td b/llvm/test/TableGen/directive3.td
index 2a82b5ebcb493..a9e1f04d4dec6 100644
--- a/llvm/test/TableGen/directive3.td
+++ b/llvm/test/TableGen/directive3.td
@@ -7,20 +7,20 @@ def TestDirectiveLanguage : DirectiveLanguage {
   let name = "TdlError";
 }
 
-def TDLC_ClauseA : Clause<"clausea"> {
+def TDLC_ClauseA : Clause<[Spelling<"clausea">]> {
   let isDefault = 1;
 }
 
-def TDLC_ClauseB : Clause<"clauseb"> {
+def TDLC_ClauseB : Clause<[Spelling<"clauseb">]> {
 }
 
-def TDLC_ClauseC : Clause<"clausec"> {
+def TDLC_ClauseC : Clause<[Spelling<"clausec">]> {
 }
 
-def TDLC_ClauseD : Clause<"claused"> {
+def TDLC_ClauseD : Clause<[Spelling<"claused">]> {
 }
 
-def TDL_DirA : Directive<"dira"> {
+def TDL_DirA : Directive<[Spelling<"dira">]> {
   let allowedClauses = [
     VersionedClause<TDLC_ClauseA>,
     VersionedClause<TDLC_ClauseB>,
diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
index bd6c543e1741a..e1e41b3ecb584 100644
--- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
@@ -24,6 +24,7 @@
 #include "llvm/TableGen/TableGenBackend.h"
 
 #include <numeric>
+#include <string>
 #include <vector>
 
 using namespace llvm;
@@ -193,10 +194,11 @@ static bool hasDuplicateClauses(ArrayRef<const Record *> Clauses,
                                 StringSet<> &CrtClauses) {
   bool HasError = false;
   for (const VersionedClause VerClause : Clauses) {
-    const auto InsRes = CrtClauses.insert(VerClause.getClause().getName());
+    StringRef Name = VerClause.getClause().getRecordName();
+    const auto InsRes = CrtClauses.insert(Name);
     if (!InsRes.second) {
-      PrintError("Clause " + VerClause.getClause().getRecordName() +
-                 " already defined on directive " + Directive.getRecordName());
+      PrintError("Clause " + Name + " already defined on directive " +
+                 Directive.getRecordName());
       HasError = true;
     }
   }
@@ -267,11 +269,12 @@ static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) {
   if (DirLang.hasEnableBitmaskEnumInNamespace())
     OS << "#include \"llvm/ADT/BitmaskEnum.h\"\n";
 
+  OS << "#include \"llvm/ADT/StringRef.h\"\n";
   OS << "#include \"llvm/Support/Compiler.h\"\n";
   OS << "#include <cstddef>\n"; // for size_t
+  OS << "#include <utility>\n"; // for std::pair
   OS << "\n";
   OS << "namespace llvm {\n";
-  OS << "class StringRef;\n";
 
   // Open namespaces defined in the directive language
   SmallVector<StringRef, 2> Namespaces;
@@ -282,6 +285,13 @@ 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),
@@ -313,22 +323,32 @@ static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) {
   // Generic function signatures
   OS << "\n";
   OS << "// Enumeration helper functions\n";
-  OS << "LLVM_ABI Directive get" << Lang << "DirectiveKind(StringRef Str);\n";
+
+  OS << "LLVM_ABI std::pair<Directive, VersionRange> get" << Lang
+     << "DirectiveKindAndVersions(StringRef Str);\n";
+
+  OS << "inline Directive get" << Lang << "DirectiveKind(StringRef Str) {\n";
+  OS << "  return get" << Lang << "DirectiveKindAndVersions(Str).first;\n";
+  OS << "}\n";
   OS << "\n";
 
-  // For OpenMP the signature is
-  //   getOpenMPDirectiveName(Directive D, unsigned V)
-  OS << "LLVM_ABI StringRef get" << DirLang.getName()
-     << "DirectiveName(Directive D";
-  if (DirLang.getCppNamespace() == "omp")
-    OS << ", unsigned = 0";
-  OS << ");\n";
+  OS << "LLVM_ABI StringRef get" << Lang
+     << "DirectiveName(Directive D, unsigned Ver = 0);\n";
   OS << "\n";
 
-  OS << "LLVM_ABI Clause get" << Lang << "ClauseKind(StringRef Str);\n";
+  OS << "LLVM_ABI std::pair<Clause, VersionRange> get" << Lang
+     << "ClauseKindAndVersions(StringRef Str);\n";
   OS << "\n";
-  OS << "LLVM_ABI StringRef get" << Lang << "ClauseName(Clause C);\n";
+
+  OS << "inline Clause get" << Lang << "ClauseKind(StringRef Str) {\n";
+  OS << "  return get" << Lang << "ClauseKindAndVersions(Str).first;\n";
+  OS << "}\n";
   OS << "\n";
+
+  OS << "LLVM_ABI StringRef get" << Lang
+     << "ClauseName(Clause C, unsigned Ver = 0);\n";
+  OS << "\n";
+
   OS << "/// Return true if \\p C is a valid clause for \\p D in version \\p "
      << "Version.\n";
   OS << "LLVM_ABI bool isAllowedClauseForDirective(Directive D, "
@@ -359,25 +379,23 @@ static void generateGetName(ArrayRef<const Record *> Records, raw_ostream &OS,
                             StringRef Prefix) {
   StringRef Lang = DirLang.getName();
   std::string Qual = getQualifier(DirLang);
-  // For OpenMP the "Directive" signature is
-  //   getOpenMPDirectiveName(Directive D, unsigned V)
   OS << "\n";
   OS << "llvm::StringRef " << Qual << "get" << Lang << Enum << "Name(" << Qual
-     << Enum << " Kind";
-  if (DirLang.getCppNamespace() == "omp" && Enum == "Directive")
-    OS << ", unsigned";
-  OS << ") {\n";
+     << Enum << " Kind, unsigned) {\n";
   OS << "  switch (Kind) {\n";
   for (const Record *R : Records) {
     OS << "    case " << getIdentifierName(R, Prefix) << ":\n";
-    OS << "      return \"" << BaseRecord(R).getName() << "\";\n";
+    // FIXME: This will need to recognize different spellings for different
+    // versions.
+    OS << "      return \"" << BaseRecord(R).getSpellingForIdentifier()
+       << "\";\n";
   }
   OS << "  }\n"; // switch
   OS << "  llvm_unreachable(\"Invalid " << Lang << " " << Enum << " kind\");\n";
   OS << "}\n";
 }
 
-// Generate function implementation for get<Enum>Kind(StringRef Str)
+// Generate function implementation for get<Enum>KindAndVersions(StringRef Str)
 static void generateGetKind(ArrayRef<const Record *> Records, raw_ostream &OS,
                             StringRef Enum, const DirectiveLanguage &DirLang,
                             StringRef Prefix, bool ImplicitAsUnknown) {
@@ -394,21 +412,29 @@ static void generateGetKind(ArrayRef<const Record *> Records, raw_ostream &OS,
   std::string Qual = getQualifier(DirLang);
   std::string DefaultName = getIdentifierName(*DefaultIt, Prefix);
 
+  // std::pair<<Enum>, VersionRange>
+  // get<DirLang><Enum>KindAndVersions(StringRef Str);
   OS << "\n";
-  OS << Qual << Enum << " " << Qual << "get" << DirLang.getName() << Enum
-     << "Kind(llvm::StringRef Str) {\n";
-  OS << "  return StringSwitch<" << Enum << ">(Str)\n";
+  OS << "std::pair<" << Qual << Enum << ", " << Qual << "VersionRange> " << Qual
+     << "get" << DirLang.getName() << Enum
+     << "KindAndVersions(llvm::StringRef Str) {\n";
+  OS << "  VersionRange All{}; // Default-initialized to \"all-versions\"\n";
+  OS << "  return StringSwitch<std::pair<" << Enum << ", "
+     << "VersionRange>>(Str)\n";
 
   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(\"" << Rec.getName() << "\"," << DefaultName << ")\n";
+      OS << "    .Case(\"" << Name << "\", {" << DefaultName << ", All})\n";
     } else {
-      OS << "    .Case(\"" << Rec.getName() << "\","
-         << getIdentifierName(R, Prefix) << ")\n";
+      OS << "    .Case(\"" << Name << "\", {"
+         << getIdentifierName(R, Prefix) << ", All})\n";
     }
   }
-  OS << "    .Default(" << DefaultName << ");\n";
+  OS << "    .Default({" << DefaultName << ", All});\n";
   OS << "}\n";
 }
 
@@ -430,7 +456,7 @@ static void generateGetClauseVal(const DirectiveLanguage &DirLang,
     });
 
     if (DefaultIt == ClauseVals.end()) {
-      PrintError("At least one val in Clause " + C.getFormattedName() +
+      PrintError("At least one val in Clause " + C.getRecordName() +
                  " must be defined as default.");
       return;
     }
@@ -438,8 +464,8 @@ static void generateGetClauseVal(const DirectiveLanguage &DirLang,
 
     StringRef Enum = C.getEnumName();
     if (Enum.empty()) {
-      PrintError("enumClauseValue field not set in Clause" +
-                 C.getFormattedName() + ".");
+      PrintError("enumClauseValue field not set in Clause" + C.getRecordName() +
+                 ".");
       return;
     }
 
@@ -599,7 +625,10 @@ static void emitLeafTable(const DirectiveLanguage &DirLang, raw_ostream &OS,
   // (such as "end declare target").
   DenseSet<int> EndDirectives;
   for (auto [Rec, Id] : DirId) {
-    if (Directive(Rec).getName().starts_with_insensitive("end "))
+    // FIXME: This will need to recognize different spellings for different
+    // versions.
+    StringRef Name = Directive(Rec).getSpellingForIdentifier();
+    if (Name.starts_with_insensitive("end "))
       EndDirectives.insert(Id);
   }
 
@@ -710,7 +739,7 @@ static void generateGetDirectiveAssociation(const DirectiveLanguage &DirLang,
   };
 
   auto ErrorPrefixFor = [&](Directive D) -> std::string {
-    return (Twine("Directive '") + D.getName() + "' in namespace '" +
+    return (Twine("Directive '") + D.getRecordName() + "' in namespace '" +
             DirLang.getCppNamespace() + "' ")
         .str();
   };
@@ -752,7 +781,6 @@ static void generateGetDirectiveAssociation(const DirectiveLanguage &DirLang,
     // Compute the association from leaf constructs.
     std::vector<const Record *> Leaves = D.getLeafConstructs();
     if (Leaves.empty()) {
-      errs() << D.getName() << '\n';
       PrintFatalError(ErrorPrefixFor(D) +
                       "requests association to be computed from leaves, "
                       "but it has no leaves");
@@ -899,7 +927,7 @@ static void generateDirectiveClauseSets(const DirectiveLanguage &DirLang,
 
   for (const Directive Dir : DirLang.getDirectives()) {
     OS << "\n";
-    OS << "// Sets for " << Dir.getName() << "\n";
+    OS << "// Sets for " << Dir.getSpellingForIdentifier() << "\n";
 
     generateClauseSet(Dir.getAllowedClauses(), OS, "allowedClauses_", Dir,
                       DirLang, FE);
@@ -1034,8 +1062,11 @@ static void generateFlangClauseUnparse(const DirectiveLanguage &DirLang,
   for (const Clause Clause : DirLang.getClauses()) {
     if (Clause.skipFlangUnparser())
       continue;
+    // The unparser doesn't know the effective version, so just pick some
+    // spelling.
+    StringRef SomeSpelling = Clause.getSpellingForIdentifier();
     std::string Parser = Clause.getFormattedParserClassName();
-    std::string Upper = Clause.getName().upper();
+    std::string Upper = SomeSpelling.upper();
 
     if (!Clause.getFlangClass().empty()) {
       if (Clause.isValueOptional() && Clause.getDefaultValue().empty()) {
@@ -1125,9 +1156,9 @@ getSpellingTexts(ArrayRef<const Record *> Records) {
   std::vector<RecordWithText> List;
   for (const Record *R : Records) {
     Clause C(R);
-    List.push_back(std::make_pair(R, C.getName()));
-    llvm::transform(C.getAliases(), std::back_inserter(List),
-                    [R](StringRef S) { return std::make_pair(R, S); });
+    llvm::transform(
+        C.getSpellings(), std::back_inserter(List),
+        [R](Spelling::Value V) { return std::make_pair(R, V.first); });
   }
   return List;
 }
@@ -1305,7 +1336,9 @@ void emitDirectivesBasicImpl(const DirectiveLanguage &DirLang,
   StringRef DPrefix = DirLang.getDirectivePrefix();
   StringRef CPrefix = DirLang.getClausePrefix();
 
-  OS << "\n#include \"llvm/Support/ErrorHandling.h\"\n";
+  OS << "\n";
+  OS << "#include \"llvm/Support/ErrorHandling.h\"\n";
+  OS << "#include <utility>\n";
 
   // getDirectiveKind(StringRef Str)
   generateGetKind(DirLang.getDirectives(), OS, "Directive", DirLang, DPrefix,
diff --git a/mlir/test/mlir-tblgen/directive-common.td b/mlir/test/mlir-tblgen/directive-common.td
index 54e0d14ca83dd..78747b82f1f4a 100644
--- a/mlir/test/mlir-tblgen/directive-common.td
+++ b/mlir/test/mlir-tblgen/directive-common.td
@@ -11,7 +11,7 @@ def TDLCV_vala : EnumVal<"vala",1,1> {}
 def TDLCV_valb : EnumVal<"valb",2,1> {}
 def TDLCV_valc : EnumVal<"valc",3,0> { let isDefault = 1; }
 
-def TDLC_ClauseA : Clause<"clausea"> {
+def TDLC_ClauseA : Clause<[Spelling<"clausea">]> {
   let flangClass = "TdlClauseA";
   let enumClauseValue = "AKind";
   let allowedClauseValues = [

>From 5b50dee31caecd63a132a92cd624d47c78b67e8e Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 28 May 2025 08:38:14 -0500
Subject: [PATCH 5/7] format

---
 llvm/include/llvm/TableGen/DirectiveEmitter.h  | 6 +++---
 llvm/utils/TableGen/Basic/DirectiveEmitter.cpp | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h b/llvm/include/llvm/TableGen/DirectiveEmitter.h
index 1235b7638e761..03777adf4426d 100644
--- a/llvm/include/llvm/TableGen/DirectiveEmitter.h
+++ b/llvm/include/llvm/TableGen/DirectiveEmitter.h
@@ -158,9 +158,9 @@ class BaseRecord {
 
   std::vector<Spelling::Value> getSpellings() const {
     std::vector<Spelling::Value> List;
-    llvm::transform(
-        Def->getValueAsListOfDefs("spellings"), std::back_inserter(List),
-        [](const Record *R) { return Spelling(R).get(); });
+    llvm::transform(Def->getValueAsListOfDefs("spellings"),
+                    std::back_inserter(List),
+                    [](const Record *R) { return Spelling(R).get(); });
     return List;
   }
 
diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
index e1e41b3ecb584..75f796abb7ce6 100644
--- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
@@ -430,8 +430,8 @@ static void generateGetKind(ArrayRef<const Record *> Records, raw_ostream &OS,
     if (ImplicitAsUnknown && R->getValueAsBit("isImplicit")) {
       OS << "    .Case(\"" << Name << "\", {" << DefaultName << ", All})\n";
     } else {
-      OS << "    .Case(\"" << Name << "\", {"
-         << getIdentifierName(R, Prefix) << ", All})\n";
+      OS << "    .Case(\"" << Name << "\", {" << getIdentifierName(R, Prefix)
+         << ", All})\n";
     }
   }
   OS << "    .Default({" << DefaultName << ", All});\n";

>From 8422cce8867256ee038e0add9427daceb2bcc56e Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 28 May 2025 09:55:58 -0500
Subject: [PATCH 6/7] Use literal 0x7fffffff instead of macro INT_MAX

The macro can expand to different strings depending on the compiler,
i.e. 0x7fffffff or 2147483647.
---
 llvm/include/llvm/TableGen/DirectiveEmitter.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h b/llvm/include/llvm/TableGen/DirectiveEmitter.h
index 03777adf4426d..3d936eb7b8e7f 100644
--- a/llvm/include/llvm/TableGen/DirectiveEmitter.h
+++ b/llvm/include/llvm/TableGen/DirectiveEmitter.h
@@ -119,7 +119,7 @@ class Versioned {
 #define STRUCT_VERSION_RANGE                                                   \
   struct VersionRange {                                                        \
     int Min = 1;                                                               \
-    int Max = INT_MAX;                                                         \
+    int Max = 0x7fffffff;                                                      \
   }
 
 STRUCT_VERSION_RANGE;

>From a23cc001c4e40b8ef9c65e646ec4d4596a1cdd95 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 5 Jun 2025 09:23:32 -0500
Subject: [PATCH 7/7] Remove unnecessary {}

---
 llvm/include/llvm/TableGen/DirectiveEmitter.h | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h b/llvm/include/llvm/TableGen/DirectiveEmitter.h
index 1b844ead5981d..4a5b3a24de595 100644
--- a/llvm/include/llvm/TableGen/DirectiveEmitter.h
+++ b/llvm/include/llvm/TableGen/DirectiveEmitter.h
@@ -177,9 +177,8 @@ 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.second.Min < Oldest.second.Min)
         Oldest = V;
-      }
     }
     return Oldest.first;
   }



More information about the llvm-commits mailing list