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 << "«unnamed»"; 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 ¶m = 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