[llvm] [mlir] [utils][TableGen] Treat clause aliases equally with names (PR #141763)
Krzysztof Parzyszek via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 5 05:35:19 PDT 2025
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/141763
>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/3] [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/3] [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/3] [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";
}
More information about the llvm-commits
mailing list