[Mlir-commits] [mlir] a8f2e80 - [MLIR][tblgen] Honor `-dialect` in `-gen-{attrdef, op, typedef, enum}-doc` (#182183)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Feb 19 11:36:11 PST 2026


Author: Fabian Schuiki
Date: 2026-02-19T11:36:05-08:00
New Revision: a8f2e80d5fe3e713330eb34ad878beeb71e43e0c

URL: https://github.com/llvm/llvm-project/commit/a8f2e80d5fe3e713330eb34ad878beeb71e43e0c
DIFF: https://github.com/llvm/llvm-project/commit/a8f2e80d5fe3e713330eb34ad878beeb71e43e0c.diff

LOG: [MLIR][tblgen] Honor `-dialect` in `-gen-{attrdef,op,typedef,enum}-doc` (#182183)

Make all dialect documentation generators use the same set of records as
`-gen-dialect-doc`, which honors the `-dialect` tblgen option to filter
records by dialect. Add a `-keep-op-source-order` option to allow
`-gen-op-doc` to continue producing unsorted op lists if needed.

This commit factors the record collection, filtering, and sorting
performed in `emitDialectDoc` out into a separate `collectRecords`
function, returning a `DialectRecords` with the results. The emit
functions now all accept a `DialectRecords` argument instead of
collecting records themselves. Most changes are mechanical renamings and
moving code around.

This fixes a confusing issue where `gen-dialect-doc` would produce the
entire documentation for a dialect, but individual calls to
`gen-attrdef-doc` and the like would seemingly operate on a different
set of records, potentially covering multiple dialects. This all produce
the overall documentation now.

Added: 
    mlir/test/mlir-tblgen/gen-op-doc.td

Modified: 
    mlir/test/mlir-tblgen/openmp-ops.td
    mlir/tools/mlir-tblgen/OpDocGen.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/test/mlir-tblgen/gen-op-doc.td b/mlir/test/mlir-tblgen/gen-op-doc.td
new file mode 100644
index 0000000000000..61b62b9bf8362
--- /dev/null
+++ b/mlir/test/mlir-tblgen/gen-op-doc.td
@@ -0,0 +1,20 @@
+// RUN: mlir-tblgen -gen-op-doc -I %S/../../include -dialect=foo %s | FileCheck %s --check-prefix=CHECK-FOO
+// RUN: mlir-tblgen -gen-op-doc -I %S/../../include -dialect=bar %s | FileCheck %s --check-prefix=CHECK-BAR
+
+include "mlir/IR/OpBase.td"
+
+def Foo_Dialect : Dialect {
+  let name = "foo";
+}
+
+def Bar_Dialect : Dialect {
+  let name = "bar";
+}
+
+// CHECK-FOO: foo.a
+// CHECK-BAR-NOT: foo.a
+def AOp : Op<Foo_Dialect, "a">;
+
+// CHECK-BAR: bar.b
+// CHECK-FOO-NOT: bar.b
+def BOp : Op<Bar_Dialect, "b">;

diff  --git a/mlir/test/mlir-tblgen/openmp-ops.td b/mlir/test/mlir-tblgen/openmp-ops.td
index 5da8957b51f44..1e841a15ebfc0 100644
--- a/mlir/test/mlir-tblgen/openmp-ops.td
+++ b/mlir/test/mlir-tblgen/openmp-ops.td
@@ -10,7 +10,7 @@
 // RUN:   -I %S/../../../llvm/include > %t/mlir/Dialect/OpenMP/OmpCommon.td
 
 // RUN: mlir-tblgen -gen-op-decls -I %S/../../include -I %t %s | FileCheck %s --check-prefix=DECL
-// RUN: mlir-tblgen -gen-op-doc -I %S/../../include -I %t %s | FileCheck %s --check-prefix=DOC
+// RUN: mlir-tblgen -gen-op-doc -I %S/../../include -I %t %s -keep-op-source-order | FileCheck %s --check-prefix=DOC
 
 include "mlir/Dialect/OpenMP/OpenMPOpBase.td"
 

diff  --git a/mlir/tools/mlir-tblgen/OpDocGen.cpp b/mlir/tools/mlir-tblgen/OpDocGen.cpp
index f2b269e3a4542..5e3cf302ed3ea 100644
--- a/mlir/tools/mlir-tblgen/OpDocGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpDocGen.cpp
@@ -53,6 +53,10 @@ static cl::opt<bool> allowHugoSpecificFeatures(
     "allow-hugo-specific-features",
     cl::desc("Allows using features specific to Hugo"), cl::init(false),
     cl::cat(docCat));
+static cl::opt<bool>
+    keepOpSourceOrder("keep-op-source-order",
+                      cl::desc("Do not sort ops alphabetically"),
+                      cl::init(false), cl::cat(docCat));
 
 void mlir::tblgen::emitSummary(StringRef summary, raw_ostream &os) {
   if (summary.empty())
@@ -102,6 +106,41 @@ static void emitNamedConstraint(const T &it, raw_ostream &os) {
   os << " | " << it.constraint.getSummary() << " |\n";
 }
 
+//===----------------------------------------------------------------------===//
+// Records
+//===----------------------------------------------------------------------===//
+
+namespace {
+struct OpDocGroup {
+  const Dialect &getDialect() const { return ops.front().getDialect(); }
+
+  /// Summary description of the section.
+  std::string summary = "";
+
+  /// Description of the section.
+  StringRef description = "";
+
+  /// Instances inside the section.
+  std::vector<Operator> ops;
+};
+
+/// Holds all records collected from a dialect relevant for documentation
+/// generation.
+struct DialectRecords {
+  DialectRecords(Dialect dialect, StringRef inputFilename)
+      : dialect(dialect), inputFilename(inputFilename) {}
+
+  Dialect dialect;
+  StringRef inputFilename;
+  std::vector<Attribute> attributes;
+  std::vector<AttrDef> attrDefs;
+  std::vector<OpDocGroup> ops;
+  std::vector<Type> types;
+  std::vector<TypeDef> typeDefs;
+  std::vector<EnumInfo> enums;
+};
+} // namespace
+
 //===----------------------------------------------------------------------===//
 // Operation Documentation
 //===----------------------------------------------------------------------===//
@@ -284,13 +323,41 @@ static void emitSourceLink(StringRef inputFilename, raw_ostream &os) {
      << inputFromMlirInclude << ")\n";
 }
 
-static void emitOpDoc(const RecordKeeper &records, raw_ostream &os) {
-  auto opDefs = getRequestedOpDefinitions(records);
+static void maybeNest(bool nest, llvm::function_ref<void(raw_ostream &os)> fn,
+                      raw_ostream &os) {
+  std::string str;
+  raw_string_ostream ss(str);
+  fn(ss);
+  for (StringRef x : llvm::split(str, "\n")) {
+    if (nest && x.starts_with("#"))
+      os << "#";
+    os << x << "\n";
+  }
+}
+
+static void emitOpDocGroup(const OpDocGroup &grouping, raw_ostream &os) {
+  bool nested = !grouping.summary.empty();
+  maybeNest(
+      nested,
+      [&](raw_ostream &os) {
+        if (nested) {
+          os << "\n## " << StringRef(grouping.summary).trim() << "\n";
+          emitDescription(grouping.description, os);
+          os << "\n";
+        }
+        for (const Operator &op : grouping.ops) {
+          emitOpDoc(op, os);
+        }
+      },
+      os);
+}
 
+static bool emitOpDoc(const DialectRecords &records, raw_ostream &os) {
   os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
-  emitSourceLink(records.getInputFilename(), os);
-  for (const Record *opDef : opDefs)
-    emitOpDoc(Operator(opDef), os);
+  emitSourceLink(records.inputFilename, os);
+  for (const OpDocGroup &grouping : records.ops)
+    emitOpDocGroup(grouping, os);
+  return false;
 }
 
 //===----------------------------------------------------------------------===//
@@ -372,13 +439,18 @@ static void emitAttrOrTypeDefDoc(const AttrOrTypeDef &def, raw_ostream &os) {
   os << "\n";
 }
 
-static void emitAttrOrTypeDefDoc(const RecordKeeper &records, raw_ostream &os,
-                                 StringRef recordTypeName) {
-  auto defs = records.getAllDerivedDefinitions(recordTypeName);
+static bool emitAttrDefDoc(const DialectRecords &records, raw_ostream &os) {
+  os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
+  for (const AttrDef &def : records.attrDefs)
+    emitAttrOrTypeDefDoc(def, os);
+  return false;
+}
 
+static bool emitTypeDefDoc(const DialectRecords &records, raw_ostream &os) {
   os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
-  for (const Record *def : defs)
-    emitAttrOrTypeDefDoc(AttrOrTypeDef(def), os);
+  for (const TypeDef &def : records.typeDefs)
+    emitAttrOrTypeDefDoc(def, os);
+  return false;
 }
 
 //===----------------------------------------------------------------------===//
@@ -404,122 +476,86 @@ static void emitEnumDoc(const EnumInfo &def, raw_ostream &os) {
   os << "\n";
 }
 
-static void emitEnumDoc(const RecordKeeper &records, raw_ostream &os) {
+static bool emitEnumDoc(const DialectRecords &records, raw_ostream &os) {
   os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
-  for (const Record *def : records.getAllDerivedDefinitions("EnumInfo"))
-    emitEnumDoc(EnumInfo(def), os);
+  for (const EnumInfo &def : records.enums)
+    emitEnumDoc(def, os);
+  return false;
 }
 
 //===----------------------------------------------------------------------===//
 // Dialect Documentation
 //===----------------------------------------------------------------------===//
 
-struct OpDocGroup {
-  const Dialect &getDialect() const { return ops.front().getDialect(); }
-
-  // Returns the summary description of the section.
-  std::string summary = "";
-
-  // Returns the description of the section.
-  StringRef description = "";
-
-  // Instances inside the section.
-  std::vector<Operator> ops;
-};
-
-static void maybeNest(bool nest, llvm::function_ref<void(raw_ostream &os)> fn,
-                      raw_ostream &os) {
-  std::string str;
-  raw_string_ostream ss(str);
-  fn(ss);
-  for (StringRef x : llvm::split(str, "\n")) {
-    if (nest && x.starts_with("#"))
-      os << "#";
-    os << x << "\n";
-  }
-}
-
-static void emitBlock(ArrayRef<Attribute> attributes, StringRef inputFilename,
-                      ArrayRef<AttrDef> attrDefs, ArrayRef<OpDocGroup> ops,
-                      ArrayRef<Type> types, ArrayRef<TypeDef> typeDefs,
-                      ArrayRef<EnumInfo> enums, raw_ostream &os) {
-  if (!ops.empty()) {
+static void emitBlock(const DialectRecords &records, raw_ostream &os) {
+  if (!records.ops.empty()) {
     os << "\n## Operations\n";
-    emitSourceLink(inputFilename, os);
-    for (const OpDocGroup &grouping : ops) {
-      bool nested = !grouping.summary.empty();
-      maybeNest(
-          nested,
-          [&](raw_ostream &os) {
-            if (nested) {
-              os << "\n## " << StringRef(grouping.summary).trim() << "\n";
-              emitDescription(grouping.description, os);
-              os << "\n";
-            }
-            for (const Operator &op : grouping.ops) {
-              emitOpDoc(op, os);
-            }
-          },
-          os);
-    }
+    emitSourceLink(records.inputFilename, os);
+    for (const OpDocGroup &grouping : records.ops)
+      emitOpDocGroup(grouping, os);
   }
 
-  if (!attributes.empty()) {
+  if (!records.attributes.empty()) {
     os << "\n## Attribute constraints\n";
-    for (const Attribute &attr : attributes)
+    for (const Attribute &attr : records.attributes)
       emitAttrDoc(attr, os);
   }
 
-  if (!attrDefs.empty()) {
+  if (!records.attrDefs.empty()) {
     os << "\n## Attributes\n";
-    for (const AttrDef &def : attrDefs)
+    for (const AttrDef &def : records.attrDefs)
       emitAttrOrTypeDefDoc(def, os);
   }
 
   // TODO: Add link between use and def for types
-  if (!types.empty()) {
+  if (!records.types.empty()) {
     os << "\n## Type constraints\n";
-    for (const Type &type : types)
+    for (const Type &type : records.types)
       emitTypeDoc(type, os);
   }
 
-  if (!typeDefs.empty()) {
+  if (!records.typeDefs.empty()) {
     os << "\n## Types\n";
-    for (const TypeDef &def : typeDefs)
+    for (const TypeDef &def : records.typeDefs)
       emitAttrOrTypeDefDoc(def, os);
   }
 
-  if (!enums.empty()) {
+  if (!records.enums.empty()) {
     os << "\n## Enums\n";
-    for (const EnumInfo &def : enums)
+    for (const EnumInfo &def : records.enums)
       emitEnumDoc(def, os);
   }
 }
 
-static void emitDialectDoc(const Dialect &dialect, StringRef inputFilename,
-                           ArrayRef<Attribute> attributes,
-                           ArrayRef<AttrDef> attrDefs, ArrayRef<OpDocGroup> ops,
-                           ArrayRef<Type> types, ArrayRef<TypeDef> typeDefs,
-                           ArrayRef<EnumInfo> enums, raw_ostream &os) {
-  os << "\n# '" << dialect.getName() << "' Dialect\n";
-  emitSummary(dialect.getSummary(), os);
-  emitDescription(dialect.getDescription(), os);
+static bool emitDialectDoc(const DialectRecords &records, raw_ostream &os) {
+  os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
+  os << "\n# '" << records.dialect.getName() << "' Dialect\n";
+  emitSummary(records.dialect.getSummary(), os);
+  emitDescription(records.dialect.getDescription(), os);
 
   // Generate a TOC marker except if description already contains one.
   Regex r("^[[:space:]]*\\[TOC\\]$", Regex::RegexFlags::Newline);
-  if (!r.match(dialect.getDescription()))
+  if (!r.match(records.dialect.getDescription()))
     os << "\n[TOC]\n";
 
-  emitBlock(attributes, inputFilename, attrDefs, ops, types, typeDefs, enums,
-            os);
+  emitBlock(records, os);
+  return false;
 }
 
-static bool emitDialectDoc(const RecordKeeper &records, raw_ostream &os) {
+//===----------------------------------------------------------------------===//
+// Record Collection
+//===----------------------------------------------------------------------===//
+
+/// Collect, filter, and organize all records relevant for dialect documentation
+/// generation. Returns none if no single dialect could be determined. See
+/// `mlir::tblgen::findDialectToGenerate`.
+static std::optional<DialectRecords>
+collectRecords(const RecordKeeper &records) {
   auto dialectDefs = records.getAllDerivedDefinitionsIfDefined("Dialect");
   SmallVector<Dialect> dialects(dialectDefs.begin(), dialectDefs.end());
   std::optional<Dialect> dialect = findDialectToGenerate(dialects);
   if (!dialect)
-    return true;
+    return std::nullopt;
 
   std::vector<const Record *> opDefs = getRequestedOpDefinitions(records);
   auto attrDefs = records.getAllDerivedDefinitionsIfDefined("DialectAttr");
@@ -528,13 +564,7 @@ static bool emitDialectDoc(const RecordKeeper &records, raw_ostream &os) {
   auto attrDefDefs = records.getAllDerivedDefinitionsIfDefined("AttrDef");
   auto enumDefs = records.getAllDerivedDefinitionsIfDefined("EnumInfo");
 
-  std::vector<Attribute> dialectAttrs;
-  std::vector<AttrDef> dialectAttrDefs;
-  std::vector<OpDocGroup> dialectOps;
-  std::vector<Type> dialectTypes;
-  std::vector<TypeDef> dialectTypeDefs;
-  std::vector<EnumInfo> dialectEnums;
-
+  DialectRecords result(*dialect, records.getInputFilename());
   SmallDenseSet<const Record *> seen;
   auto addIfNotSeen = [&](const Record *record, const auto &def, auto &vec) {
     if (seen.insert(record).second) {
@@ -550,9 +580,9 @@ static bool emitDialectDoc(const RecordKeeper &records, raw_ostream &os) {
   SmallDenseMap<const Record *, OpDocGroup> opDocGroup;
 
   for (const Record *def : attrDefDefs)
-    addIfInDialect(def, AttrDef(def), dialectAttrDefs);
+    addIfInDialect(def, AttrDef(def), result.attrDefs);
   for (const Record *def : attrDefs)
-    addIfInDialect(def, Attribute(def), dialectAttrs);
+    addIfInDialect(def, Attribute(def), result.attributes);
   for (const Record *def : opDefs) {
     if (const Record *group = def->getValueAsOptionalDef("opDocGroup")) {
       OpDocGroup &op = opDocGroup[group];
@@ -560,7 +590,7 @@ static bool emitDialectDoc(const RecordKeeper &records, raw_ostream &os) {
     } else {
       OpDocGroup op;
       op.ops.emplace_back(def);
-      addIfInDialect(def, op, dialectOps);
+      addIfInDialect(def, op, result.ops);
     }
   }
   for (const Record *rec :
@@ -569,21 +599,23 @@ static bool emitDialectDoc(const RecordKeeper &records, raw_ostream &os) {
       continue;
     opDocGroup[rec].summary = rec->getValueAsString("summary");
     opDocGroup[rec].description = rec->getValueAsString("description");
-    dialectOps.push_back(opDocGroup[rec]);
+    result.ops.push_back(opDocGroup[rec]);
   }
   for (const Record *def : typeDefDefs)
-    addIfInDialect(def, TypeDef(def), dialectTypeDefs);
+    addIfInDialect(def, TypeDef(def), result.typeDefs);
   for (const Record *def : typeDefs)
-    addIfInDialect(def, Type(def), dialectTypes);
-  dialectEnums.reserve(enumDefs.size());
+    addIfInDialect(def, Type(def), result.types);
+  result.enums.reserve(enumDefs.size());
   for (const Record *def : enumDefs)
-    addIfNotSeen(def, EnumInfo(def), dialectEnums);
+    addIfNotSeen(def, EnumInfo(def), result.enums);
 
   // Sort alphabetically ignorning dialect for ops and section name for
   // sections.
   // TODO: The sorting order could be revised, currently attempting to sort of
   // keep in alphabetical order.
-  llvm::sort(dialectOps, [](const OpDocGroup &lhs, const OpDocGroup &rhs) {
+  if (keepOpSourceOrder)
+    return result;
+  llvm::sort(result.ops, [](const OpDocGroup &lhs, const OpDocGroup &rhs) {
     auto getDesc = [](const OpDocGroup &arg) -> StringRef {
       if (!arg.summary.empty())
         return arg.summary;
@@ -592,11 +624,7 @@ static bool emitDialectDoc(const RecordKeeper &records, raw_ostream &os) {
     return getDesc(lhs).compare_insensitive(getDesc(rhs)) < 0;
   });
 
-  os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
-  emitDialectDoc(*dialect, records.getInputFilename(), dialectAttrs,
-                 dialectAttrDefs, dialectOps, dialectTypes, dialectTypeDefs,
-                 dialectEnums, os);
-  return false;
+  return result;
 }
 
 //===----------------------------------------------------------------------===//
@@ -607,33 +635,39 @@ static mlir::GenRegistration
     genAttrRegister("gen-attrdef-doc",
                     "Generate dialect attribute documentation",
                     [](const RecordKeeper &records, raw_ostream &os) {
-                      emitAttrOrTypeDefDoc(records, os, "AttrDef");
-                      return false;
+                      if (auto filtered = collectRecords(records))
+                        return emitAttrDefDoc(*filtered, os);
+                      return true;
                     });
 
 static mlir::GenRegistration
     genOpRegister("gen-op-doc", "Generate dialect documentation",
                   [](const RecordKeeper &records, raw_ostream &os) {
-                    emitOpDoc(records, os);
-                    return false;
+                    if (auto filtered = collectRecords(records))
+                      return emitOpDoc(*filtered, os);
+                    return true;
                   });
 
 static mlir::GenRegistration
     genTypeRegister("gen-typedef-doc", "Generate dialect type documentation",
                     [](const RecordKeeper &records, raw_ostream &os) {
-                      emitAttrOrTypeDefDoc(records, os, "TypeDef");
-                      return false;
+                      if (auto filtered = collectRecords(records))
+                        return emitTypeDefDoc(*filtered, os);
+                      return true;
                     });
 
 static mlir::GenRegistration
     genEnumRegister("gen-enum-doc", "Generate dialect enum documentation",
                     [](const RecordKeeper &records, raw_ostream &os) {
-                      emitEnumDoc(records, os);
-                      return false;
+                      if (auto filtered = collectRecords(records))
+                        return emitEnumDoc(*filtered, os);
+                      return true;
                     });
 
 static mlir::GenRegistration
     genRegister("gen-dialect-doc", "Generate dialect documentation",
                 [](const RecordKeeper &records, raw_ostream &os) {
-                  return emitDialectDoc(records, os);
+                  if (auto filtered = collectRecords(records))
+                    return emitDialectDoc(*filtered, os);
+                  return true;
                 });


        


More information about the Mlir-commits mailing list