[llvm] [TableGen] Add GenericEnumClass that generates scoped enums (PR #170903)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Dec 5 10:50:18 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-tablegen
Author: Mirko BrkuĊĦanin (mbrkusanin)
<details>
<summary>Changes</summary>
Add a way of generating scoped enums with c++ `enum class` when a new name scope is preferable.
---
Full diff: https://github.com/llvm/llvm-project/pull/170903.diff
4 Files Affected:
- (modified) llvm/docs/TableGen/BackEnds.rst (+30)
- (modified) llvm/include/llvm/TableGen/SearchableTable.td (+7)
- (modified) llvm/test/TableGen/generic-tables.td (+98)
- (modified) llvm/utils/TableGen/SearchableTableEmitter.cpp (+21-3)
``````````diff
diff --git a/llvm/docs/TableGen/BackEnds.rst b/llvm/docs/TableGen/BackEnds.rst
index 1e3cb8783df16..166292773b1e6 100644
--- a/llvm/docs/TableGen/BackEnds.rst
+++ b/llvm/docs/TableGen/BackEnds.rst
@@ -629,6 +629,12 @@ using the ``let`` statement.
field, it will be assigned an integer value. Values are assigned in
alphabetical order starting with 0.
+* ``bit Scoped``. When set to 1, the generated C++ declaration will use
+ ``enum class`` instead of an unscoped ``enum``. This defaults to 0.
+
+To make it easier to request scoped enums, the ``GenericEnumClass`` helper
+derives from ``GenericEnum`` and sets ``Scoped = 1``.
+
Here is an example where the values of the elements are specified
explicitly, as a template argument to the ``BEntry`` class. The resulting
C++ code is shown.
@@ -688,6 +694,30 @@ by element name.
};
#endif
+Here is an example of a ``GenericEnumClass`` that generates scoped enumerated
+values by emitting an ``enum class`` declaration.
+
+.. code-block:: text
+
+ class FEntry;
+
+ def FApple : FEntry;
+ def FBanana : FEntry;
+ def FCherry : FEntry;
+
+ def FEnum : GenericEnumClass {
+ let FilterClass = "FEntry";
+ }
+
+.. code-block:: text
+
+ #ifdef GET_FEnum_DECL
+ enum class FEnum {
+ FApple = 0,
+ FBanana = 1,
+ FCherry = 2,
+ }
+ #endif
Generic Tables
~~~~~~~~~~~~~~
diff --git a/llvm/include/llvm/TableGen/SearchableTable.td b/llvm/include/llvm/TableGen/SearchableTable.td
index f10e1597d8da7..6834134a20f2d 100644
--- a/llvm/include/llvm/TableGen/SearchableTable.td
+++ b/llvm/include/llvm/TableGen/SearchableTable.td
@@ -47,6 +47,13 @@ class GenericEnum {
// If ValueField is not set, enum values will be assigned automatically,
// starting at 0, according to a lexicographical sort of the entry names.
string ValueField;
+
+ // If true, emit the enum as a scoped C++ enum class.
+ bit Scoped = 0;
+}
+
+class GenericEnumClass : GenericEnum {
+ let Scoped = 1;
}
// Define a record derived from this class to generate a generic table. This
diff --git a/llvm/test/TableGen/generic-tables.td b/llvm/test/TableGen/generic-tables.td
index 8638740a8e565..c8a7ff75e61c2 100644
--- a/llvm/test/TableGen/generic-tables.td
+++ b/llvm/test/TableGen/generic-tables.td
@@ -19,6 +19,13 @@ include "llvm/TableGen/SearchableTable.td"
// CHECK: CFoo
// CHECK: }
+// CHECK-LABEL: GET_FEnum_DECL
+// CHECK: enum class FEnum {
+// CHECK: FApple = 0,
+// CHECK: FBanana = 1,
+// CHECK: FCherry = 2,
+// CHECK: }
+
// CHECK-LABEL: GET_ATable_DECL
// CHECK: const AEntry *lookupATableByValues(uint8_t Val1, uint16_t Val2);
@@ -237,3 +244,94 @@ def EEntryOddTable : GenericTable {
let PrimaryKey = ["Value"];
let PrimaryKeyName = "lookupEEntryOddTableByValue";
}
+
+// CHECK-LABEL: GET_FTable_DECL
+// CHECK: const FTableEntry *lookupFTableByEnum(FEnum Enum);
+// CHECK: const FTableEntry *lookupFTableByEnumAndValue(FEnum Enum, uint32_t Value);
+
+// CHECK-LABEL: GET_FTable_IMPL
+// CHECK: constexpr FTableEntry FTable[] = {
+// CHECK: { FEnum::FApple, 0x1 },
+// CHECK: { FEnum::FBanana, 0x2 },
+// CHECK: { FEnum::FCherry, 0x3 },
+// CHECK: };
+// CHECK: const FTableEntry *lookupFTableByEnum(FEnum Enum) {
+// CHECK: if ((FEnum)Enum != std::clamp((FEnum)Enum, (FEnum)FEnum::FApple, (FEnum)FEnum::FCherry))
+// CHECK: return nullptr;
+// CHECK: const FTableEntry *lookupFTableByEnumAndValue(FEnum Enum, uint32_t Value) {
+// CHECK: struct IndexType {
+// CHECK: FEnum Enum;
+// CHECK: uint32_t Value;
+// CHECK: unsigned _index;
+// CHECK: };
+// CHECK: static const struct IndexType Index[] = {
+// CHECK: { FEnum::FApple, 0x1, 0 },
+// CHECK: { FEnum::FBanana, 0x2, 1 },
+// CHECK: { FEnum::FCherry, 0x3, 2 },
+// CHECK: };
+// CHECK: struct KeyType {
+// CHECK: FEnum Enum;
+// CHECK: uint32_t Value;
+// CHECK: };
+// CHECK: return &FTable[Idx->_index];
+
+// CHECK-LABEL: GET_FTableContiguous_DECL
+// CHECK: const FTableEntry *lookupFTableContiguousByEnum(FEnum Enum);
+
+// CHECK-LABEL: GET_FTableContiguous_IMPL
+// CHECK: constexpr FTableEntry FTableContiguous[] = {
+// CHECK: { FEnum::FApple, 0x1 },
+// CHECK: { FEnum::FBanana, 0x2 },
+// CHECK: { FEnum::FCherry, 0x3 },
+// CHECK: };
+// CHECK: const FTableEntry *lookupFTableContiguousByEnum(FEnum Enum) {
+// CHECK: if ((FEnum)Enum != std::clamp((FEnum)Enum, (FEnum)FEnum::FApple, (FEnum)FEnum::FCherry))
+// CHECK: return nullptr;
+// CHECK: auto Table = ArrayRef(FTableContiguous);
+// CHECK: size_t Idx = (unsigned)Enum - (unsigned)FEnum::FApple;
+// CHECK: return &Table[Idx];
+
+class FEntry;
+
+def FApple : FEntry;
+def FBanana : FEntry;
+def FCherry : FEntry;
+
+def FEnum : GenericEnumClass {
+ let FilterClass = "FEntry";
+}
+
+class FTableEntry<FEntry EnumValue, int val> {
+ FEntry Enum = EnumValue;
+ bits<32> Value = val;
+}
+
+def FAppleEntry : FTableEntry<FApple, 1>;
+def FBananaEntry : FTableEntry<FBanana, 2>;
+def FCherryEntry : FTableEntry<FCherry, 3>;
+
+def FTable : GenericTable {
+ let FilterClass = "FTableEntry";
+ let Fields = ["Enum", "Value"];
+
+ string TypeOf_Enum = "FEnum";
+
+ let PrimaryKey = ["Enum"];
+ let PrimaryKeyName = "lookupFTableByEnum";
+ let PrimaryKeyEarlyOut = 1;
+}
+
+def lookupFTableByEnumAndValue : SearchIndex {
+ let Table = FTable;
+ let Key = ["Enum", "Value"];
+}
+
+def FTableContiguous : GenericTable {
+ let FilterClass = "FTableEntry";
+ let Fields = ["Enum", "Value"];
+
+ string TypeOf_Enum = "FEnum";
+
+ let PrimaryKey = ["Enum"];
+ let PrimaryKeyName = "lookupFTableContiguousByEnum";
+}
diff --git a/llvm/utils/TableGen/SearchableTableEmitter.cpp b/llvm/utils/TableGen/SearchableTableEmitter.cpp
index 0dc8c92a5a37a..7514ca33c706f 100644
--- a/llvm/utils/TableGen/SearchableTableEmitter.cpp
+++ b/llvm/utils/TableGen/SearchableTableEmitter.cpp
@@ -55,6 +55,7 @@ struct GenericEnum {
const Record *Class = nullptr;
std::string PreprocessorGuard;
MapVector<const Record *, Entry> Entries;
+ bool Scoped = false;
const Entry *getEntry(const Record *Def) const {
auto II = Entries.find(Def);
@@ -146,6 +147,12 @@ class SearchableTableEmitter {
if (!Entry)
PrintFatalError(Loc,
Twine("Entry for field '") + Field.Name + "' is null");
+ if (Field.Enum->Scoped) {
+ std::string QualifiedName = Field.Enum->Name;
+ QualifiedName += "::";
+ QualifiedName += Entry->Name;
+ return QualifiedName;
+ }
return Entry->Name.str();
}
PrintFatalError(Loc, Twine("invalid field type for field '") + Field.Name +
@@ -193,7 +200,12 @@ class SearchableTableEmitter {
}
if (isa<BitRecTy>(Field.RecType))
return "bool";
- if (Field.Enum || Field.IsIntrinsic || Field.IsInstruction)
+ if (Field.Enum) {
+ if (Field.Enum->Scoped)
+ return Field.Enum->Name;
+ return "unsigned";
+ }
+ if (Field.IsIntrinsic || Field.IsInstruction)
return "unsigned";
PrintFatalError(Index.Loc,
Twine("In table '") + Table.Name + "' lookup method '" +
@@ -336,7 +348,7 @@ void SearchableTableEmitter::emitGenericEnum(const GenericEnum &Enum,
raw_ostream &OS) {
emitIfdef((Twine("GET_") + Enum.PreprocessorGuard + "_DECL").str(), OS);
- OS << "enum " << Enum.Name << " {\n";
+ OS << "enum " << (Enum.Scoped ? "class " : "") << Enum.Name << " {\n";
for (const auto &[Name, Value] :
make_second_range(Enum.Entries.getArrayRef()))
OS << " " << Name << " = " << Value << ",\n";
@@ -438,7 +450,12 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
if (IsContiguous && !Index.EarlyOut) {
OS << " auto Table = ArrayRef(" << IndexName << ");\n";
- OS << " size_t Idx = " << Field.Name << " - " << FirstRepr << ";\n";
+ std::string NumericField =
+ Field.Enum ? "(unsigned)" + Field.Name : Field.Name;
+ std::string NumericFirstRepr =
+ Field.Enum ? "(unsigned)" + FirstRepr : FirstRepr;
+ OS << " size_t Idx = " << NumericField << " - " << NumericFirstRepr
+ << ";\n";
OS << " return ";
if (IsPrimary)
OS << "&Table[Idx]";
@@ -756,6 +773,7 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
auto Enum = std::make_unique<GenericEnum>();
Enum->Name = EnumRec->getName().str();
Enum->PreprocessorGuard = EnumRec->getName().str();
+ Enum->Scoped = EnumRec->getValueAsBit("Scoped");
StringRef FilterClass = EnumRec->getValueAsString("FilterClass");
Enum->Class = Records.getClass(FilterClass);
``````````
</details>
https://github.com/llvm/llvm-project/pull/170903
More information about the llvm-commits
mailing list