[Mlir-commits] [mlir] [mlir-tblgen] Render enum keyword alternatives in generated attr/type docs (PR #203677)

Maksim Levental llvmlistbot at llvm.org
Fri Jun 12 23:40:44 PDT 2026


https://github.com/makslevental updated https://github.com/llvm/llvm-project/pull/203677

>From bd6c6fa2345f5976d8f495a0eb051a8949cb8bd5 Mon Sep 17 00:00:00 2001
From: Maksim Levental <maksim.levental at gmail.com>
Date: Fri, 12 Jun 2026 23:27:02 -0700
Subject: [PATCH] [mlir-tblgen] Render enum keyword alternatives in generated
 attr/type docs

When mlir-tblgen generates documentation for AttrDefs/TypeDefs that have
EnumParameter fields, it previously rendered the raw C++ type (e.g.
`::mlir::ns::MyEnum`) in the syntax block. This was unhelpful for
users who need to know the valid keyword values.

This patch:
1. Adds an `EnumInfo enum = enumInfo;` field to the `EnumParameter`
   TableGen class, persisting the enum record for tooling to inspect.
2. Modifies `emitAttrOrTypeDefAssemblyFormat` in OpDocGen.cpp to detect
   EnumParameter fields and render their cases as backtick-quoted
   alternatives (e.g. `` `read` | `read_write` ``).
3. Adds a test case to gen-dialect-doc.td verifying the new behavior.

Before:
  #my_dialect.my_attr<
    int32_t,   # index
    ::mlir::ns::MyEnum,   # access
  >

After:
  #my_dialect.my_attr<
    int32_t,   # index
    `read` | `read_write`,   # access
  >

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply at anthropic.com>
---
 mlir/include/mlir/IR/EnumAttr.td         |  3 +++
 mlir/test/mlir-tblgen/gen-dialect-doc.td | 25 ++++++++++++++++++++++++
 mlir/tools/mlir-tblgen/OpDocGen.cpp      | 24 ++++++++++++++++++++++-
 3 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/mlir/include/mlir/IR/EnumAttr.td b/mlir/include/mlir/IR/EnumAttr.td
index 6eef5075fe18a..5f3384e95ba79 100644
--- a/mlir/include/mlir/IR/EnumAttr.td
+++ b/mlir/include/mlir/IR/EnumAttr.td
@@ -503,6 +503,9 @@ class I64BitEnumAttr<string name, string summary,
 class EnumParameter<EnumInfo enumInfo>
     : AttrParameter<enumInfo.cppNamespace # "::" # enumInfo.className,
                     "an enum of type " # enumInfo.className> {
+  // Store the enum info so that tooling (e.g. doc generation) can inspect the
+  // enum cases without re-parsing the C++ type string.
+  EnumInfo enum = enumInfo;
   let parser = !if(!isa<EnumAttrInfo>(enumInfo),
     !cast<EnumAttrInfo>(enumInfo).parameterParser, ?);
   let printer = !if(!isa<EnumAttrInfo>(enumInfo),
diff --git a/mlir/test/mlir-tblgen/gen-dialect-doc.td b/mlir/test/mlir-tblgen/gen-dialect-doc.td
index 72916704369ee..c373f3fb6801b 100644
--- a/mlir/test/mlir-tblgen/gen-dialect-doc.td
+++ b/mlir/test/mlir-tblgen/gen-dialect-doc.td
@@ -67,6 +67,24 @@ def TestAttrDefParams : AttrDef<Test_Dialect, "TestAttrDefParams"> {
   let assemblyFormat = "`<` $value `>`";
 }
 
+def TestEnumForParam :
+    I32EnumAttr<"TestEnumForParam",
+        "enum for param test", [
+        I32EnumAttrCase<"Alpha", 0, "alpha">,
+        I32EnumAttrCase<"Beta", 1, "beta">]> {
+  let genSpecializedAttr = 0;
+  let cppNamespace = "NS";
+}
+
+def TestAttrWithEnum : AttrDef<Test_Dialect, "TestAttrWithEnum"> {
+  let mnemonic = "with_enum";
+  let parameters = (ins
+    "int":$value,
+    EnumParameter<TestEnumForParam>:$mode
+  );
+  let assemblyFormat = "`<` $value `,` $mode `>`";
+}
+
 def TestTypeDef : TypeDef<Test_Dialect, "TestTypeDef"> {
   let mnemonic = "test_type_def";
 }
@@ -140,6 +158,13 @@ def TestEnum :
 // CHECK: Syntax:
 // CHECK: #test.test_attr_def_params
 
+// CHECK: TestAttrWithEnumAttr
+// CHECK: Syntax:
+// CHECK:      #test.with_enum<
+// CHECK-NEXT:   int,   # value
+// CHECK-NEXT:   `alpha` | `beta`   # mode
+// CHECK-NEXT: >
+
 // CHECK: ## Type constraints
 // CHECK: ### type summary
 // CHECK: type description
diff --git a/mlir/tools/mlir-tblgen/OpDocGen.cpp b/mlir/tools/mlir-tblgen/OpDocGen.cpp
index 5e3cf302ed3ea..7c050dbb08a3b 100644
--- a/mlir/tools/mlir-tblgen/OpDocGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpDocGen.cpp
@@ -384,6 +384,25 @@ static void emitTypeDoc(const Type &type, raw_ostream &os) {
 // TypeDef Documentation
 //===----------------------------------------------------------------------===//
 
+/// If \p param is an EnumParameter, return a string listing the enum's keyword
+/// alternatives (e.g. "`read` | `read_write`"). Otherwise return std::nullopt.
+static std::optional<std::string>
+getEnumParameterDocSyntax(const AttrOrTypeParameter &param) {
+  const auto *paramDef = dyn_cast<DefInit>(param.getDef());
+  if (!paramDef || !paramDef->getDef()->isSubClassOf("EnumParameter"))
+    return std::nullopt;
+  const Record *enumRec = paramDef->getDef()->getValueAsDef("enum");
+  EnumInfo enumInfo(enumRec);
+  std::vector<EnumCase> cases = enumInfo.getAllCases();
+  std::string result;
+  for (const auto &caseIt : llvm::enumerate(cases)) {
+    if (caseIt.index() > 0)
+      result += " | ";
+    result += (llvm::Twine("`") + caseIt.value().getStr() + "`").str();
+  }
+  return result;
+}
+
 static void emitAttrOrTypeDefAssemblyFormat(const AttrOrTypeDef &def,
                                             raw_ostream &os) {
   ArrayRef<AttrOrTypeParameter> parameters = def.getParameters();
@@ -399,7 +418,10 @@ static void emitAttrOrTypeDefAssemblyFormat(const AttrOrTypeDef &def,
      << "<\n";
   for (const auto &it : llvm::enumerate(parameters)) {
     const AttrOrTypeParameter &param = it.value();
-    os << "  " << param.getSyntax();
+    if (auto enumSyntax = getEnumParameterDocSyntax(param))
+      os << "  " << *enumSyntax;
+    else
+      os << "  " << param.getSyntax();
     if (it.index() < (parameters.size() - 1))
       os << ",";
     os << "   # " << param.getName() << "\n";



More information about the Mlir-commits mailing list