[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