[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