xref: /llvm-project/mlir/tools/mlir-tblgen/OpDocGen.cpp (revision e5521fae944c1f3f6905ce5902819a5c9be7f802)
1a280e399SJacques Pienaar //===- OpDocGen.cpp - MLIR operation documentation generator --------------===//
2a280e399SJacques Pienaar //
330857107SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
456222a06SMehdi Amini // See https://llvm.org/LICENSE.txt for license information.
556222a06SMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a280e399SJacques Pienaar //
756222a06SMehdi Amini //===----------------------------------------------------------------------===//
8a280e399SJacques Pienaar //
9a280e399SJacques Pienaar // OpDocGen uses the description of operations to generate documentation for the
10a280e399SJacques Pienaar // operations.
11a280e399SJacques Pienaar //
12a280e399SJacques Pienaar //===----------------------------------------------------------------------===//
13a280e399SJacques Pienaar 
14e0c3b94cSRiver Riddle #include "DialectGenUtilities.h"
15635544fcSRiver Riddle #include "DocGenUtilities.h"
1696caf381SJacques Pienaar #include "OpGenHelpers.h"
179b851527SJacques Pienaar #include "mlir/Support/IndentedOstream.h"
1883ef862fSRiver Riddle #include "mlir/TableGen/AttrOrTypeDef.h"
19f0918485STom Natan #include "mlir/TableGen/Attribute.h"
20a280e399SJacques Pienaar #include "mlir/TableGen/GenInfo.h"
21a280e399SJacques Pienaar #include "mlir/TableGen/Operator.h"
2277672c97SJacques Pienaar #include "llvm/ADT/DenseMap.h"
23e0c3b94cSRiver Riddle #include "llvm/ADT/SetVector.h"
24a280e399SJacques Pienaar #include "llvm/ADT/StringExtras.h"
250118a3f6SJacques Pienaar #include "llvm/ADT/StringRef.h"
2690ae4d90SMehdi Amini #include "llvm/Support/CommandLine.h"
27a280e399SJacques Pienaar #include "llvm/Support/FormatVariadic.h"
28a232a48dSJacques Pienaar #include "llvm/Support/Regex.h"
29a280e399SJacques Pienaar #include "llvm/Support/Signals.h"
30a280e399SJacques Pienaar #include "llvm/TableGen/Error.h"
31a280e399SJacques Pienaar #include "llvm/TableGen/Record.h"
32a280e399SJacques Pienaar #include "llvm/TableGen/TableGenBackend.h"
33a280e399SJacques Pienaar 
345fe53c41SJohn Demme #include <set>
350118a3f6SJacques Pienaar #include <string>
360118a3f6SJacques Pienaar 
37a280e399SJacques Pienaar using namespace llvm;
38a280e399SJacques Pienaar using namespace mlir;
3977672c97SJacques Pienaar using namespace mlir::tblgen;
40a280e399SJacques Pienaar using mlir::tblgen::Operator;
41a280e399SJacques Pienaar 
42bccd37f6SRahul Joshi //===----------------------------------------------------------------------===//
43bccd37f6SRahul Joshi // Commandline Options
44bccd37f6SRahul Joshi //===----------------------------------------------------------------------===//
45bccd37f6SRahul Joshi static cl::OptionCategory
46bccd37f6SRahul Joshi     docCat("Options for -gen-(attrdef|typedef|enum|op|dialect)-doc");
47bccd37f6SRahul Joshi cl::opt<std::string>
48bccd37f6SRahul Joshi     stripPrefix("strip-prefix",
49bccd37f6SRahul Joshi                 cl::desc("Strip prefix of the fully qualified names"),
50bccd37f6SRahul Joshi                 cl::init("::mlir::"), cl::cat(docCat));
51bccd37f6SRahul Joshi cl::opt<bool> allowHugoSpecificFeatures(
52bccd37f6SRahul Joshi     "allow-hugo-specific-features",
53bccd37f6SRahul Joshi     cl::desc("Allows using features specific to Hugo"), cl::init(false),
54bccd37f6SRahul Joshi     cl::cat(docCat));
55bccd37f6SRahul Joshi 
564acbd444Srikhuijzer void mlir::tblgen::emitSummary(StringRef summary, raw_ostream &os) {
574acbd444Srikhuijzer   if (!summary.empty()) {
58bccd37f6SRahul Joshi     StringRef trimmed = summary.trim();
599a8ff346SRik Huijzer     char first = std::toupper(trimmed.front());
60bccd37f6SRahul Joshi     StringRef rest = trimmed.drop_front();
614acbd444Srikhuijzer     os << "\n_" << first << rest << "_\n\n";
624acbd444Srikhuijzer   }
634acbd444Srikhuijzer }
644acbd444Srikhuijzer 
65a759cf31SLei Zhang // Emit the description by aligning the text to the left per line (e.g.,
66a759cf31SLei Zhang // removing the minimum indentation across the block).
67a759cf31SLei Zhang //
68a759cf31SLei Zhang // This expects that the description in the tablegen file is already formatted
69a759cf31SLei Zhang // in a way the user wanted but has some additional indenting due to being
70a759cf31SLei Zhang // nested in the op definition.
71635544fcSRiver Riddle void mlir::tblgen::emitDescription(StringRef description, raw_ostream &os) {
729b851527SJacques Pienaar   raw_indented_ostream ros(os);
730a6def62SLongsheng Mou   StringRef trimmed = description.rtrim(" \t");
740a6def62SLongsheng Mou   ros.printReindented(trimmed);
750a6def62SLongsheng Mou   if (!trimmed.ends_with("\n"))
760a6def62SLongsheng Mou     ros << "\n";
77f20ec77bSJacques Pienaar }
78f20ec77bSJacques Pienaar 
792e2ad539SAlex Zinenko void mlir::tblgen::emitDescriptionComment(StringRef description,
802e2ad539SAlex Zinenko                                           raw_ostream &os, StringRef prefix) {
812e2ad539SAlex Zinenko   if (description.empty())
822e2ad539SAlex Zinenko     return;
832e2ad539SAlex Zinenko   raw_indented_ostream ros(os);
842e2ad539SAlex Zinenko   StringRef trimmed = description.rtrim(" \t");
852e2ad539SAlex Zinenko   ros.printReindented(trimmed, (Twine(prefix) + "/// ").str());
8688d319a2SKazu Hirata   if (!trimmed.ends_with("\n"))
872e2ad539SAlex Zinenko     ros << "\n";
882e2ad539SAlex Zinenko }
892e2ad539SAlex Zinenko 
9077672c97SJacques Pienaar // Emits `str` with trailing newline if not empty.
9177672c97SJacques Pienaar static void emitIfNotEmpty(StringRef str, raw_ostream &os) {
9277672c97SJacques Pienaar   if (!str.empty()) {
9377672c97SJacques Pienaar     emitDescription(str, os);
9477672c97SJacques Pienaar     os << "\n";
9577672c97SJacques Pienaar   }
9677672c97SJacques Pienaar }
97a280e399SJacques Pienaar 
981a083f02SRiver Riddle /// Emit the given named constraint.
991a083f02SRiver Riddle template <typename T>
1001a083f02SRiver Riddle static void emitNamedConstraint(const T &it, raw_ostream &os) {
1011a083f02SRiver Riddle   if (!it.name.empty())
102ec0b53d4SJacques Pienaar     os << "| `" << it.name << "`";
1031a083f02SRiver Riddle   else
1041a083f02SRiver Riddle     os << "&laquo;unnamed&raquo;";
105e0a93e4bSRiver Riddle   os << " | " << it.constraint.getSummary() << "\n";
1061a083f02SRiver Riddle }
1071a083f02SRiver Riddle 
1081a083f02SRiver Riddle //===----------------------------------------------------------------------===//
1091a083f02SRiver Riddle // Operation Documentation
1101a083f02SRiver Riddle //===----------------------------------------------------------------------===//
1111a083f02SRiver Riddle 
1121a083f02SRiver Riddle /// Emit the assembly format of an operation.
1131a083f02SRiver Riddle static void emitAssemblyFormat(StringRef opName, StringRef format,
11477672c97SJacques Pienaar                                raw_ostream &os) {
1151a083f02SRiver Riddle   os << "\nSyntax:\n\n```\noperation ::= `" << opName << "` ";
11677672c97SJacques Pienaar 
1171a083f02SRiver Riddle   // Print the assembly format aligned.
1181a083f02SRiver Riddle   unsigned indent = strlen("operation ::= ");
1191a083f02SRiver Riddle   std::pair<StringRef, StringRef> split = format.split('\n');
1201a083f02SRiver Riddle   os << split.first.trim() << "\n";
1211a083f02SRiver Riddle   do {
1221a083f02SRiver Riddle     split = split.second.split('\n');
1231a083f02SRiver Riddle     StringRef formatChunk = split.first.trim();
1241a083f02SRiver Riddle     if (!formatChunk.empty())
1251a083f02SRiver Riddle       os.indent(indent) << formatChunk << "\n";
1261a083f02SRiver Riddle   } while (!split.second.empty());
1271a083f02SRiver Riddle   os << "```\n\n";
1281a083f02SRiver Riddle }
1295e932afdSMLIR Team 
130061e4f24SRik Huijzer /// Place `text` between backticks so that the Markdown processor renders it as
131061e4f24SRik Huijzer /// inline code.
132061e4f24SRik Huijzer static std::string backticks(const std::string &text) {
133061e4f24SRik Huijzer   return '`' + text + '`';
134061e4f24SRik Huijzer }
135061e4f24SRik Huijzer 
1361fc096afSMehdi Amini static void emitOpTraitsDoc(const Operator &op, raw_ostream &os) {
137ec0b53d4SJacques Pienaar   // TODO: We should link to the trait/documentation of it. That also means we
138ec0b53d4SJacques Pienaar   // should add descriptions to traits that can be queried.
139ec0b53d4SJacques Pienaar   // Collect using set to sort effects, interfaces & traits.
140ec0b53d4SJacques Pienaar   std::set<std::string> effects, interfaces, traits;
141ec0b53d4SJacques Pienaar   for (auto &trait : op.getTraits()) {
142468581f1SJacques Pienaar     if (isa<PredTrait>(&trait))
143ec0b53d4SJacques Pienaar       continue;
144ec0b53d4SJacques Pienaar 
145ec0b53d4SJacques Pienaar     std::string name = trait.getDef().getName().str();
146ec0b53d4SJacques Pienaar     StringRef ref = name;
147ec0b53d4SJacques Pienaar     StringRef traitName = trait.getDef().getValueAsString("trait");
148ec0b53d4SJacques Pienaar     traitName.consume_back("::Trait");
149ec0b53d4SJacques Pienaar     traitName.consume_back("::Impl");
15088d319a2SKazu Hirata     if (ref.starts_with("anonymous_"))
151ec0b53d4SJacques Pienaar       name = traitName.str();
152468581f1SJacques Pienaar     if (isa<InterfaceTrait>(&trait)) {
153ec0b53d4SJacques Pienaar       if (trait.getDef().isSubClassOf("SideEffectsTraitBase")) {
154ec0b53d4SJacques Pienaar         auto effectName = trait.getDef().getValueAsString("baseEffectName");
155ec0b53d4SJacques Pienaar         effectName.consume_front("::");
156ec0b53d4SJacques Pienaar         effectName.consume_front("mlir::");
157ec0b53d4SJacques Pienaar         std::string effectStr;
158bccd37f6SRahul Joshi         raw_string_ostream os(effectStr);
159ec0b53d4SJacques Pienaar         os << effectName << "{";
160ec0b53d4SJacques Pienaar         auto list = trait.getDef().getValueAsListOfDefs("effects");
161bccd37f6SRahul Joshi         interleaveComma(list, os, [&](const Record *rec) {
162ec0b53d4SJacques Pienaar           StringRef effect = rec->getValueAsString("effect");
163ec0b53d4SJacques Pienaar           effect.consume_front("::");
164ec0b53d4SJacques Pienaar           effect.consume_front("mlir::");
165ec0b53d4SJacques Pienaar           os << effect << " on " << rec->getValueAsString("resource");
166ec0b53d4SJacques Pienaar         });
167ec0b53d4SJacques Pienaar         os << "}";
168ffc80de8SJOE1994         effects.insert(backticks(effectStr));
169bccd37f6SRahul Joshi         name.append(formatv(" ({0})", traitName).str());
170ec0b53d4SJacques Pienaar       }
171061e4f24SRik Huijzer       interfaces.insert(backticks(name));
172ec0b53d4SJacques Pienaar       continue;
173ec0b53d4SJacques Pienaar     }
174ec0b53d4SJacques Pienaar 
175061e4f24SRik Huijzer     traits.insert(backticks(name));
176ec0b53d4SJacques Pienaar   }
177ec0b53d4SJacques Pienaar   if (!traits.empty()) {
178bccd37f6SRahul Joshi     interleaveComma(traits, os << "\nTraits: ");
179ec0b53d4SJacques Pienaar     os << "\n";
180ec0b53d4SJacques Pienaar   }
181ec0b53d4SJacques Pienaar   if (!interfaces.empty()) {
182bccd37f6SRahul Joshi     interleaveComma(interfaces, os << "\nInterfaces: ");
183ec0b53d4SJacques Pienaar     os << "\n";
184ec0b53d4SJacques Pienaar   }
185ec0b53d4SJacques Pienaar   if (!effects.empty()) {
186bccd37f6SRahul Joshi     interleaveComma(effects, os << "\nEffects: ");
187ec0b53d4SJacques Pienaar     os << "\n";
188ec0b53d4SJacques Pienaar   }
189ec0b53d4SJacques Pienaar }
190ec0b53d4SJacques Pienaar 
191d339d8feSBenjamin Maxwell static StringRef resolveAttrDescription(const Attribute &attr) {
192d339d8feSBenjamin Maxwell   StringRef description = attr.getDescription();
193d339d8feSBenjamin Maxwell   if (description.empty())
194d339d8feSBenjamin Maxwell     return attr.getBaseAttr().getDescription();
195d339d8feSBenjamin Maxwell   return description;
196d339d8feSBenjamin Maxwell }
197d339d8feSBenjamin Maxwell 
19878fdbdbfSMehdi Amini static void emitOpDoc(const Operator &op, raw_ostream &os) {
1990118a3f6SJacques Pienaar   std::string classNameStr = op.getQualCppClassName();
2000118a3f6SJacques Pienaar   StringRef className = classNameStr;
2010118a3f6SJacques Pienaar   (void)className.consume_front(stripPrefix);
202bccd37f6SRahul Joshi   os << formatv("### `{0}` ({1})\n", op.getOperationName(), className);
2031a083f02SRiver Riddle 
2041a083f02SRiver Riddle   // Emit the summary, syntax, and description if present.
2051a083f02SRiver Riddle   if (op.hasSummary())
2064acbd444Srikhuijzer     emitSummary(op.getSummary(), os);
2071a083f02SRiver Riddle   if (op.hasAssemblyFormat())
2081a083f02SRiver Riddle     emitAssemblyFormat(op.getOperationName(), op.getAssemblyFormat().trim(),
2091a083f02SRiver Riddle                        os);
2101a083f02SRiver Riddle   if (op.hasDescription())
2111a083f02SRiver Riddle     mlir::tblgen::emitDescription(op.getDescription(), os);
2121a083f02SRiver Riddle 
213ec0b53d4SJacques Pienaar   emitOpTraitsDoc(op, os);
214ec0b53d4SJacques Pienaar 
2151a083f02SRiver Riddle   // Emit attributes.
2161a083f02SRiver Riddle   if (op.getNumAttributes() != 0) {
2171a083f02SRiver Riddle     os << "\n#### Attributes:\n\n";
218d339d8feSBenjamin Maxwell     // Note: This table is HTML rather than markdown so the attribute's
219d339d8feSBenjamin Maxwell     // description can appear in an expandable region. The description may be
220d339d8feSBenjamin Maxwell     // multiple lines, which is not supported in a markdown table cell.
221d339d8feSBenjamin Maxwell     os << "<table>\n";
222d339d8feSBenjamin Maxwell     // Header.
223d339d8feSBenjamin Maxwell     os << "<tr><th>Attribute</th><th>MLIR Type</th><th>Description</th></tr>\n";
2241a083f02SRiver Riddle     for (const auto &it : op.getAttributes()) {
2251a083f02SRiver Riddle       StringRef storageType = it.attr.getStorageType();
226d339d8feSBenjamin Maxwell       // Name and storage type.
227d339d8feSBenjamin Maxwell       os << "<tr>";
228d339d8feSBenjamin Maxwell       os << "<td><code>" << it.name << "</code></td><td>" << storageType
229d339d8feSBenjamin Maxwell          << "</td><td>";
230d339d8feSBenjamin Maxwell       StringRef description = resolveAttrDescription(it.attr);
2312754f88aSBenjamin Maxwell       if (allowHugoSpecificFeatures && !description.empty()) {
232d339d8feSBenjamin Maxwell         // Expandable description.
233d339d8feSBenjamin Maxwell         // This appears as just the summary, but when clicked shows the full
234d339d8feSBenjamin Maxwell         // description.
235f0918485STom Natan         os << "<details>" << "<summary>" << it.attr.getSummary() << "</summary>"
236d339d8feSBenjamin Maxwell            << "{{% markdown %}}" << description << "{{% /markdown %}}"
237d339d8feSBenjamin Maxwell            << "</details>";
238d339d8feSBenjamin Maxwell       } else {
239d339d8feSBenjamin Maxwell         // Fallback: Single-line summary.
240d339d8feSBenjamin Maxwell         os << it.attr.getSummary();
2411a083f02SRiver Riddle       }
242d339d8feSBenjamin Maxwell       os << "</td></tr>\n";
243d339d8feSBenjamin Maxwell     }
2442754f88aSBenjamin Maxwell     os << "</table>\n";
2451a083f02SRiver Riddle   }
2461a083f02SRiver Riddle 
2471a083f02SRiver Riddle   // Emit each of the operands.
2481a083f02SRiver Riddle   if (op.getNumOperands() != 0) {
2491a083f02SRiver Riddle     os << "\n#### Operands:\n\n";
2501a083f02SRiver Riddle     os << "| Operand | Description |\n"
2511a083f02SRiver Riddle        << "| :-----: | ----------- |\n";
2521a083f02SRiver Riddle     for (const auto &it : op.getOperands())
2531a083f02SRiver Riddle       emitNamedConstraint(it, os);
2541a083f02SRiver Riddle   }
2551a083f02SRiver Riddle 
2561a083f02SRiver Riddle   // Emit results.
2571a083f02SRiver Riddle   if (op.getNumResults() != 0) {
2581a083f02SRiver Riddle     os << "\n#### Results:\n\n";
2591a083f02SRiver Riddle     os << "| Result | Description |\n"
2601a083f02SRiver Riddle        << "| :----: | ----------- |\n";
2611a083f02SRiver Riddle     for (const auto &it : op.getResults())
2621a083f02SRiver Riddle       emitNamedConstraint(it, os);
2631a083f02SRiver Riddle   }
2641a083f02SRiver Riddle 
2651a083f02SRiver Riddle   // Emit successors.
2661a083f02SRiver Riddle   if (op.getNumSuccessors() != 0) {
2671a083f02SRiver Riddle     os << "\n#### Successors:\n\n";
2681a083f02SRiver Riddle     os << "| Successor | Description |\n"
2691a083f02SRiver Riddle        << "| :-------: | ----------- |\n";
2701a083f02SRiver Riddle     for (const auto &it : op.getSuccessors())
2711a083f02SRiver Riddle       emitNamedConstraint(it, os);
2721a083f02SRiver Riddle   }
2731a083f02SRiver Riddle 
2741a083f02SRiver Riddle   os << "\n";
2751a083f02SRiver Riddle }
2761a083f02SRiver Riddle 
277e9869b57SRik Huijzer static void emitSourceLink(StringRef inputFilename, raw_ostream &os) {
278e9869b57SRik Huijzer   size_t pathBegin = inputFilename.find("mlir/include/mlir/");
279e9869b57SRik Huijzer   if (pathBegin == StringRef::npos)
280e9869b57SRik Huijzer     return;
281e9869b57SRik Huijzer 
282e9869b57SRik Huijzer   StringRef inputFromMlirInclude = inputFilename.substr(pathBegin);
283e9869b57SRik Huijzer 
284e9869b57SRik Huijzer   os << "[source](https://github.com/llvm/llvm-project/blob/main/"
285e9869b57SRik Huijzer      << inputFromMlirInclude << ")\n\n";
286e9869b57SRik Huijzer }
287e9869b57SRik Huijzer 
288e8137503SRahul Joshi static void emitOpDoc(const RecordKeeper &records, raw_ostream &os) {
289e8137503SRahul Joshi   auto opDefs = getRequestedOpDefinitions(records);
2901a083f02SRiver Riddle 
2911a083f02SRiver Riddle   os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
292e8137503SRahul Joshi   emitSourceLink(records.getInputFilename(), os);
293bccd37f6SRahul Joshi   for (const Record *opDef : opDefs)
2941a083f02SRiver Riddle     emitOpDoc(Operator(opDef), os);
2951a083f02SRiver Riddle }
2961a083f02SRiver Riddle 
2971a083f02SRiver Riddle //===----------------------------------------------------------------------===//
2980be38d4fSAlex Zinenko // Attribute Documentation
2990be38d4fSAlex Zinenko //===----------------------------------------------------------------------===//
3000be38d4fSAlex Zinenko 
3010be38d4fSAlex Zinenko static void emitAttrDoc(const Attribute &attr, raw_ostream &os) {
3020be38d4fSAlex Zinenko   os << "### " << attr.getSummary() << "\n\n";
3030be38d4fSAlex Zinenko   emitDescription(attr.getDescription(), os);
3040be38d4fSAlex Zinenko   os << "\n\n";
3050be38d4fSAlex Zinenko }
3060be38d4fSAlex Zinenko 
3070be38d4fSAlex Zinenko //===----------------------------------------------------------------------===//
3081a083f02SRiver Riddle // Type Documentation
3091a083f02SRiver Riddle //===----------------------------------------------------------------------===//
3101a083f02SRiver Riddle 
3111a083f02SRiver Riddle static void emitTypeDoc(const Type &type, raw_ostream &os) {
3120be38d4fSAlex Zinenko   os << "### " << type.getSummary() << "\n\n";
313e0a93e4bSRiver Riddle   emitDescription(type.getDescription(), os);
3140be38d4fSAlex Zinenko   os << "\n\n";
31527e8efedSJacques Pienaar }
31677672c97SJacques Pienaar 
3171a083f02SRiver Riddle //===----------------------------------------------------------------------===//
3185fe53c41SJohn Demme // TypeDef Documentation
3195fe53c41SJohn Demme //===----------------------------------------------------------------------===//
3205fe53c41SJohn Demme 
321caddfbd2SRiver Riddle static void emitAttrOrTypeDefAssemblyFormat(const AttrOrTypeDef &def,
322caddfbd2SRiver Riddle                                             raw_ostream &os) {
323ca6bd9cdSMogball   ArrayRef<AttrOrTypeParameter> parameters = def.getParameters();
32483685287SMarkus Böck   char prefix = isa<AttrDef>(def) ? '#' : '!';
325caddfbd2SRiver Riddle   if (parameters.empty()) {
32683685287SMarkus Böck     os << "\nSyntax: `" << prefix << def.getDialect().getName() << "."
327caddfbd2SRiver Riddle        << def.getMnemonic() << "`\n";
3285fe53c41SJohn Demme     return;
3295fe53c41SJohn Demme   }
3305fe53c41SJohn Demme 
33183685287SMarkus Böck   os << "\nSyntax:\n\n```\n"
332791c3cb7SJacques Pienaar      << prefix << def.getDialect().getName() << "." << def.getMnemonic()
333791c3cb7SJacques Pienaar      << "<\n";
33489de9cc8SMehdi Amini   for (const auto &it : llvm::enumerate(parameters)) {
335caddfbd2SRiver Riddle     const AttrOrTypeParameter &param = it.value();
336caddfbd2SRiver Riddle     os << "  " << param.getSyntax();
337caddfbd2SRiver Riddle     if (it.index() < (parameters.size() - 1))
3385fe53c41SJohn Demme       os << ",";
339caddfbd2SRiver Riddle     os << "   # " << param.getName() << "\n";
3405fe53c41SJohn Demme   }
3415fe53c41SJohn Demme   os << ">\n```\n";
3425fe53c41SJohn Demme }
3435fe53c41SJohn Demme 
344caddfbd2SRiver Riddle static void emitAttrOrTypeDefDoc(const AttrOrTypeDef &def, raw_ostream &os) {
345bccd37f6SRahul Joshi   os << formatv("### {0}\n", def.getCppClassName());
3465fe53c41SJohn Demme 
347caddfbd2SRiver Riddle   // Emit the summary if present.
348caddfbd2SRiver Riddle   if (def.hasSummary())
349caddfbd2SRiver Riddle     os << "\n" << def.getSummary() << "\n";
350caddfbd2SRiver Riddle 
351caddfbd2SRiver Riddle   // Emit the syntax if present.
35223e3cbe2SRiver Riddle   if (def.getMnemonic() && !def.hasCustomAssemblyFormat())
353caddfbd2SRiver Riddle     emitAttrOrTypeDefAssemblyFormat(def, os);
354caddfbd2SRiver Riddle 
355caddfbd2SRiver Riddle   // Emit the description if present.
356caddfbd2SRiver Riddle   if (def.hasDescription()) {
35769904112SMarius Brehler     os << "\n";
358caddfbd2SRiver Riddle     mlir::tblgen::emitDescription(def.getDescription(), os);
35969904112SMarius Brehler   }
3605fe53c41SJohn Demme 
361caddfbd2SRiver Riddle   // Emit parameter documentation.
362ca6bd9cdSMogball   ArrayRef<AttrOrTypeParameter> parameters = def.getParameters();
363e0a93e4bSRiver Riddle   if (!parameters.empty()) {
364caddfbd2SRiver Riddle     os << "\n#### Parameters:\n\n";
3655fe53c41SJohn Demme     os << "| Parameter | C++ type | Description |\n"
3665fe53c41SJohn Demme        << "| :-------: | :-------: | ----------- |\n";
3675fe53c41SJohn Demme     for (const auto &it : parameters) {
368e0a93e4bSRiver Riddle       auto desc = it.getSummary();
36969904112SMarius Brehler       os << "| " << it.getName() << " | `" << it.getCppType() << "` | "
3705fe53c41SJohn Demme          << (desc ? *desc : "") << " |\n";
3715fe53c41SJohn Demme     }
3725fe53c41SJohn Demme   }
3735fe53c41SJohn Demme 
3745fe53c41SJohn Demme   os << "\n";
3755fe53c41SJohn Demme }
3765fe53c41SJohn Demme 
377e8137503SRahul Joshi static void emitAttrOrTypeDefDoc(const RecordKeeper &records, raw_ostream &os,
378e8137503SRahul Joshi                                  StringRef recordTypeName) {
379e8137503SRahul Joshi   auto defs = records.getAllDerivedDefinitions(recordTypeName);
380caddfbd2SRiver Riddle 
381caddfbd2SRiver Riddle   os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
382bccd37f6SRahul Joshi   for (const Record *def : defs)
383caddfbd2SRiver Riddle     emitAttrOrTypeDefDoc(AttrOrTypeDef(def), os);
384caddfbd2SRiver Riddle }
385caddfbd2SRiver Riddle 
3865fe53c41SJohn Demme //===----------------------------------------------------------------------===//
387f0918485STom Natan // Enum Documentation
388f0918485STom Natan //===----------------------------------------------------------------------===//
389f0918485STom Natan 
390f0918485STom Natan static void emitEnumDoc(const EnumAttr &def, raw_ostream &os) {
391bccd37f6SRahul Joshi   os << formatv("### {0}\n", def.getEnumClassName());
392f0918485STom Natan 
393f0918485STom Natan   // Emit the summary if present.
394f0918485STom Natan   if (!def.getSummary().empty())
395f0918485STom Natan     os << "\n" << def.getSummary() << "\n";
396f0918485STom Natan 
397f0918485STom Natan   // Emit case documentation.
398f0918485STom Natan   std::vector<EnumAttrCase> cases = def.getAllCases();
399f0918485STom Natan   os << "\n#### Cases:\n\n";
400f0918485STom Natan   os << "| Symbol | Value | String |\n"
401f0918485STom Natan      << "| :----: | :---: | ------ |\n";
402f0918485STom Natan   for (const auto &it : cases) {
403f0918485STom Natan     os << "| " << it.getSymbol() << " | `" << it.getValue() << "` | "
404f0918485STom Natan        << it.getStr() << " |\n";
405f0918485STom Natan   }
406f0918485STom Natan 
407f0918485STom Natan   os << "\n";
408f0918485STom Natan }
409f0918485STom Natan 
410e8137503SRahul Joshi static void emitEnumDoc(const RecordKeeper &records, raw_ostream &os) {
411f0918485STom Natan   os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
412*e5521faeSMalte Dehling   for (const Record *def : records.getAllDerivedDefinitions("EnumAttrInfo"))
413f0918485STom Natan     emitEnumDoc(EnumAttr(def), os);
414f0918485STom Natan }
415f0918485STom Natan 
416f0918485STom Natan //===----------------------------------------------------------------------===//
4171a083f02SRiver Riddle // Dialect Documentation
4181a083f02SRiver Riddle //===----------------------------------------------------------------------===//
4191a083f02SRiver Riddle 
420791c3cb7SJacques Pienaar struct OpDocGroup {
421791c3cb7SJacques Pienaar   const Dialect &getDialect() const { return ops.front().getDialect(); }
422791c3cb7SJacques Pienaar 
423791c3cb7SJacques Pienaar   // Returns the summary description of the section.
424791c3cb7SJacques Pienaar   std::string summary = "";
425791c3cb7SJacques Pienaar 
426791c3cb7SJacques Pienaar   // Returns the description of the section.
427791c3cb7SJacques Pienaar   StringRef description = "";
428791c3cb7SJacques Pienaar 
429791c3cb7SJacques Pienaar   // Instances inside the section.
430791c3cb7SJacques Pienaar   std::vector<Operator> ops;
431791c3cb7SJacques Pienaar };
432791c3cb7SJacques Pienaar 
433791c3cb7SJacques Pienaar static void maybeNest(bool nest, llvm::function_ref<void(raw_ostream &os)> fn,
434791c3cb7SJacques Pienaar                       raw_ostream &os) {
435791c3cb7SJacques Pienaar   std::string str;
436bccd37f6SRahul Joshi   raw_string_ostream ss(str);
437791c3cb7SJacques Pienaar   fn(ss);
438ffc80de8SJOE1994   for (StringRef x : llvm::split(str, "\n")) {
439791c3cb7SJacques Pienaar     if (nest && x.starts_with("#"))
440791c3cb7SJacques Pienaar       os << "#";
441791c3cb7SJacques Pienaar     os << x << "\n";
442791c3cb7SJacques Pienaar   }
443791c3cb7SJacques Pienaar }
444791c3cb7SJacques Pienaar 
445e9869b57SRik Huijzer static void emitBlock(ArrayRef<Attribute> attributes, StringRef inputFilename,
446791c3cb7SJacques Pienaar                       ArrayRef<AttrDef> attrDefs, ArrayRef<OpDocGroup> ops,
4470be38d4fSAlex Zinenko                       ArrayRef<Type> types, ArrayRef<TypeDef> typeDefs,
448f0918485STom Natan                       ArrayRef<EnumAttr> enums, raw_ostream &os) {
449287ade41SJacques Pienaar   if (!ops.empty()) {
450e9869b57SRik Huijzer     os << "## Operations\n\n";
451e9869b57SRik Huijzer     emitSourceLink(inputFilename, os);
452791c3cb7SJacques Pienaar     for (const OpDocGroup &grouping : ops) {
453fbb5f733SJacques Pienaar       bool nested = !grouping.summary.empty();
454791c3cb7SJacques Pienaar       maybeNest(
455791c3cb7SJacques Pienaar           nested,
456791c3cb7SJacques Pienaar           [&](raw_ostream &os) {
457791c3cb7SJacques Pienaar             if (nested) {
458791c3cb7SJacques Pienaar               os << "## " << StringRef(grouping.summary).trim() << "\n\n";
459791c3cb7SJacques Pienaar               emitDescription(grouping.description, os);
460791c3cb7SJacques Pienaar               os << "\n\n";
461791c3cb7SJacques Pienaar             }
462791c3cb7SJacques Pienaar             for (const Operator &op : grouping.ops) {
463287ade41SJacques Pienaar               emitOpDoc(op, os);
464287ade41SJacques Pienaar             }
465791c3cb7SJacques Pienaar           },
466791c3cb7SJacques Pienaar           os);
467791c3cb7SJacques Pienaar     }
468791c3cb7SJacques Pienaar   }
469287ade41SJacques Pienaar 
4700be38d4fSAlex Zinenko   if (!attributes.empty()) {
471e9869b57SRik Huijzer     os << "## Attribute constraints\n\n";
4720be38d4fSAlex Zinenko     for (const Attribute &attr : attributes)
4730be38d4fSAlex Zinenko       emitAttrDoc(attr, os);
4740be38d4fSAlex Zinenko   }
4750be38d4fSAlex Zinenko 
476caddfbd2SRiver Riddle   if (!attrDefs.empty()) {
477e9869b57SRik Huijzer     os << "## Attributes\n\n";
478caddfbd2SRiver Riddle     for (const AttrDef &def : attrDefs)
479caddfbd2SRiver Riddle       emitAttrOrTypeDefDoc(def, os);
480caddfbd2SRiver Riddle   }
481caddfbd2SRiver Riddle 
4829db53a18SRiver Riddle   // TODO: Add link between use and def for types
4831a083f02SRiver Riddle   if (!types.empty()) {
484e9869b57SRik Huijzer     os << "## Type constraints\n\n";
4851a083f02SRiver Riddle     for (const Type &type : types)
4861a083f02SRiver Riddle       emitTypeDoc(type, os);
4871a083f02SRiver Riddle   }
4881a083f02SRiver Riddle 
4895fe53c41SJohn Demme   if (!typeDefs.empty()) {
490e9869b57SRik Huijzer     os << "## Types\n\n";
491caddfbd2SRiver Riddle     for (const TypeDef &def : typeDefs)
492caddfbd2SRiver Riddle       emitAttrOrTypeDefDoc(def, os);
4935fe53c41SJohn Demme   }
494f0918485STom Natan 
495f0918485STom Natan   if (!enums.empty()) {
496f0918485STom Natan     os << "## Enums\n\n";
497f0918485STom Natan     for (const EnumAttr &def : enums)
498f0918485STom Natan       emitEnumDoc(def, os);
499f0918485STom Natan   }
500a280e399SJacques Pienaar }
501a280e399SJacques Pienaar 
502e9869b57SRik Huijzer static void emitDialectDoc(const Dialect &dialect, StringRef inputFilename,
503791c3cb7SJacques Pienaar                            ArrayRef<Attribute> attributes,
504791c3cb7SJacques Pienaar                            ArrayRef<AttrDef> attrDefs, ArrayRef<OpDocGroup> ops,
505791c3cb7SJacques Pienaar                            ArrayRef<Type> types, ArrayRef<TypeDef> typeDefs,
506f0918485STom Natan                            ArrayRef<EnumAttr> enums, raw_ostream &os) {
507791c3cb7SJacques Pienaar   os << "# '" << dialect.getName() << "' Dialect\n\n";
508791c3cb7SJacques Pienaar   emitIfNotEmpty(dialect.getSummary(), os);
509791c3cb7SJacques Pienaar   emitIfNotEmpty(dialect.getDescription(), os);
510791c3cb7SJacques Pienaar 
511791c3cb7SJacques Pienaar   // Generate a TOC marker except if description already contains one.
512bccd37f6SRahul Joshi   Regex r("^[[:space:]]*\\[TOC\\]$", Regex::RegexFlags::Newline);
513791c3cb7SJacques Pienaar   if (!r.match(dialect.getDescription()))
514791c3cb7SJacques Pienaar     os << "[TOC]\n\n";
515791c3cb7SJacques Pienaar 
516f0918485STom Natan   emitBlock(attributes, inputFilename, attrDefs, ops, types, typeDefs, enums,
517f0918485STom Natan             os);
518791c3cb7SJacques Pienaar }
519791c3cb7SJacques Pienaar 
520e8137503SRahul Joshi static bool emitDialectDoc(const RecordKeeper &records, raw_ostream &os) {
521e8137503SRahul Joshi   auto dialectDefs = records.getAllDerivedDefinitionsIfDefined("Dialect");
522832955f6SRiver Riddle   SmallVector<Dialect> dialects(dialectDefs.begin(), dialectDefs.end());
5233cfe412eSFangrui Song   std::optional<Dialect> dialect = findDialectToGenerate(dialects);
524832955f6SRiver Riddle   if (!dialect)
525832955f6SRiver Riddle     return true;
526832955f6SRiver Riddle 
527e8137503SRahul Joshi   std::vector<const Record *> opDefs = getRequestedOpDefinitions(records);
528e8137503SRahul Joshi   auto attrDefs = records.getAllDerivedDefinitionsIfDefined("DialectAttr");
529e8137503SRahul Joshi   auto typeDefs = records.getAllDerivedDefinitionsIfDefined("DialectType");
530e8137503SRahul Joshi   auto typeDefDefs = records.getAllDerivedDefinitionsIfDefined("TypeDef");
531e8137503SRahul Joshi   auto attrDefDefs = records.getAllDerivedDefinitionsIfDefined("AttrDef");
532e8137503SRahul Joshi   auto enumDefs = records.getAllDerivedDefinitionsIfDefined("EnumAttrInfo");
53377672c97SJacques Pienaar 
534832955f6SRiver Riddle   std::vector<Attribute> dialectAttrs;
535832955f6SRiver Riddle   std::vector<AttrDef> dialectAttrDefs;
536791c3cb7SJacques Pienaar   std::vector<OpDocGroup> dialectOps;
537832955f6SRiver Riddle   std::vector<Type> dialectTypes;
538832955f6SRiver Riddle   std::vector<TypeDef> dialectTypeDefs;
539f0918485STom Natan   std::vector<EnumAttr> dialectEnums;
540791c3cb7SJacques Pienaar 
541bccd37f6SRahul Joshi   SmallDenseSet<const Record *> seen;
542bccd37f6SRahul Joshi   auto addIfNotSeen = [&](const Record *record, const auto &def, auto &vec) {
543f0918485STom Natan     if (seen.insert(record).second) {
544832955f6SRiver Riddle       vec.push_back(def);
545791c3cb7SJacques Pienaar       return true;
546791c3cb7SJacques Pienaar     }
547791c3cb7SJacques Pienaar     return false;
548832955f6SRiver Riddle   };
549bccd37f6SRahul Joshi   auto addIfInDialect = [&](const Record *record, const auto &def, auto &vec) {
550f0918485STom Natan     return def.getDialect() == *dialect && addIfNotSeen(record, def, vec);
551f0918485STom Natan   };
55227e8efedSJacques Pienaar 
553b60c6cbcSRahul Joshi   SmallDenseMap<const Record *, OpDocGroup> opDocGroup;
554791c3cb7SJacques Pienaar 
555b60c6cbcSRahul Joshi   for (const Record *def : attrDefDefs)
556832955f6SRiver Riddle     addIfInDialect(def, AttrDef(def), dialectAttrDefs);
557b60c6cbcSRahul Joshi   for (const Record *def : attrDefs)
558832955f6SRiver Riddle     addIfInDialect(def, Attribute(def), dialectAttrs);
559b60c6cbcSRahul Joshi   for (const Record *def : opDefs) {
560b60c6cbcSRahul Joshi     if (const Record *group = def->getValueAsOptionalDef("opDocGroup")) {
561791c3cb7SJacques Pienaar       OpDocGroup &op = opDocGroup[group];
562791c3cb7SJacques Pienaar       addIfInDialect(def, Operator(def), op.ops);
563791c3cb7SJacques Pienaar     } else {
564791c3cb7SJacques Pienaar       OpDocGroup op;
565791c3cb7SJacques Pienaar       op.ops.emplace_back(def);
566791c3cb7SJacques Pienaar       addIfInDialect(def, op, dialectOps);
567791c3cb7SJacques Pienaar     }
568791c3cb7SJacques Pienaar   }
569b60c6cbcSRahul Joshi   for (const Record *rec :
570e8137503SRahul Joshi        records.getAllDerivedDefinitionsIfDefined("OpDocGroup")) {
571791c3cb7SJacques Pienaar     if (opDocGroup[rec].ops.empty())
572791c3cb7SJacques Pienaar       continue;
573791c3cb7SJacques Pienaar     opDocGroup[rec].summary = rec->getValueAsString("summary");
574791c3cb7SJacques Pienaar     opDocGroup[rec].description = rec->getValueAsString("description");
575791c3cb7SJacques Pienaar     dialectOps.push_back(opDocGroup[rec]);
576791c3cb7SJacques Pienaar   }
577b60c6cbcSRahul Joshi   for (const Record *def : typeDefDefs)
578832955f6SRiver Riddle     addIfInDialect(def, TypeDef(def), dialectTypeDefs);
579b60c6cbcSRahul Joshi   for (const Record *def : typeDefs)
580832955f6SRiver Riddle     addIfInDialect(def, Type(def), dialectTypes);
581f0918485STom Natan   dialectEnums.reserve(enumDefs.size());
582b60c6cbcSRahul Joshi   for (const Record *def : enumDefs)
583f0918485STom Natan     addIfNotSeen(def, EnumAttr(def), dialectEnums);
584e0c3b94cSRiver Riddle 
585791c3cb7SJacques Pienaar   // Sort alphabetically ignorning dialect for ops and section name for
586791c3cb7SJacques Pienaar   // sections.
587791c3cb7SJacques Pienaar   // TODO: The sorting order could be revised, currently attempting to sort of
588791c3cb7SJacques Pienaar   // keep in alphabetical order.
589791c3cb7SJacques Pienaar   std::sort(dialectOps.begin(), dialectOps.end(),
590791c3cb7SJacques Pienaar             [](const OpDocGroup &lhs, const OpDocGroup &rhs) {
591791c3cb7SJacques Pienaar               auto getDesc = [](const OpDocGroup &arg) -> StringRef {
592791c3cb7SJacques Pienaar                 if (!arg.summary.empty())
593791c3cb7SJacques Pienaar                   return arg.summary;
594791c3cb7SJacques Pienaar                 return arg.ops.front().getDef().getValueAsString("opName");
595791c3cb7SJacques Pienaar               };
596791c3cb7SJacques Pienaar               return getDesc(lhs).compare_insensitive(getDesc(rhs)) < 0;
597791c3cb7SJacques Pienaar             });
598791c3cb7SJacques Pienaar 
59927e8efedSJacques Pienaar   os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
600e8137503SRahul Joshi   emitDialectDoc(*dialect, records.getInputFilename(), dialectAttrs,
601e9869b57SRik Huijzer                  dialectAttrDefs, dialectOps, dialectTypes, dialectTypeDefs,
602f0918485STom Natan                  dialectEnums, os);
603e0c3b94cSRiver Riddle   return false;
60477672c97SJacques Pienaar }
60577672c97SJacques Pienaar 
6061a083f02SRiver Riddle //===----------------------------------------------------------------------===//
6071a083f02SRiver Riddle // Gen Registration
6081a083f02SRiver Riddle //===----------------------------------------------------------------------===//
6091a083f02SRiver Riddle 
610a280e399SJacques Pienaar static mlir::GenRegistration
611caddfbd2SRiver Riddle     genAttrRegister("gen-attrdef-doc",
612caddfbd2SRiver Riddle                     "Generate dialect attribute documentation",
613b60c6cbcSRahul Joshi                     [](const RecordKeeper &records, raw_ostream &os) {
614caddfbd2SRiver Riddle                       emitAttrOrTypeDefDoc(records, os, "AttrDef");
615caddfbd2SRiver Riddle                       return false;
616caddfbd2SRiver Riddle                     });
617caddfbd2SRiver Riddle 
618caddfbd2SRiver Riddle static mlir::GenRegistration
6191a083f02SRiver Riddle     genOpRegister("gen-op-doc", "Generate dialect documentation",
620b60c6cbcSRahul Joshi                   [](const RecordKeeper &records, raw_ostream &os) {
621a280e399SJacques Pienaar                     emitOpDoc(records, os);
622a280e399SJacques Pienaar                     return false;
623a280e399SJacques Pienaar                   });
6241a083f02SRiver Riddle 
6251a083f02SRiver Riddle static mlir::GenRegistration
626caddfbd2SRiver Riddle     genTypeRegister("gen-typedef-doc", "Generate dialect type documentation",
627b60c6cbcSRahul Joshi                     [](const RecordKeeper &records, raw_ostream &os) {
628caddfbd2SRiver Riddle                       emitAttrOrTypeDefDoc(records, os, "TypeDef");
629caddfbd2SRiver Riddle                       return false;
630caddfbd2SRiver Riddle                     });
631caddfbd2SRiver Riddle 
632caddfbd2SRiver Riddle static mlir::GenRegistration
633f0918485STom Natan     genEnumRegister("gen-enum-doc", "Generate dialect enum documentation",
634b60c6cbcSRahul Joshi                     [](const RecordKeeper &records, raw_ostream &os) {
635f0918485STom Natan                       emitEnumDoc(records, os);
636f0918485STom Natan                       return false;
637f0918485STom Natan                     });
638f0918485STom Natan 
639f0918485STom Natan static mlir::GenRegistration
6401a083f02SRiver Riddle     genRegister("gen-dialect-doc", "Generate dialect documentation",
641b60c6cbcSRahul Joshi                 [](const RecordKeeper &records, raw_ostream &os) {
642e0c3b94cSRiver Riddle                   return emitDialectDoc(records, os);
6431a083f02SRiver Riddle                 });
644