github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/tools/mlir-tblgen/OpDocGen.cpp (about)

     1  //===- OpDocGen.cpp - MLIR operation documentation generator --------------===//
     2  //
     3  // Copyright 2019 The MLIR Authors.
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //   http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  // =============================================================================
    17  //
    18  // OpDocGen uses the description of operations to generate documentation for the
    19  // operations.
    20  //
    21  //===----------------------------------------------------------------------===//
    22  
    23  #include "mlir/TableGen/GenInfo.h"
    24  #include "mlir/TableGen/Operator.h"
    25  #include "llvm/ADT/StringExtras.h"
    26  #include "llvm/Support/FormatVariadic.h"
    27  #include "llvm/Support/Signals.h"
    28  #include "llvm/TableGen/Error.h"
    29  #include "llvm/TableGen/Record.h"
    30  #include "llvm/TableGen/TableGenBackend.h"
    31  
    32  using namespace llvm;
    33  using namespace mlir;
    34  
    35  using mlir::tblgen::Operator;
    36  
    37  // Emit the description by aligning the text to the left per line (e.g.,
    38  // removing the minimum indentation across the block).
    39  //
    40  // This expects that the description in the tablegen file is already formatted
    41  // in a way the user wanted but has some additional indenting due to being
    42  // nested in the op definition.
    43  static void emitDescription(StringRef description, raw_ostream &os) {
    44    // Determine the minimum number of spaces in a line.
    45    size_t min_indent = -1;
    46    StringRef remaining = description;
    47    while (!remaining.empty()) {
    48      auto split = remaining.split('\n');
    49      size_t indent = split.first.find_first_not_of(" \t");
    50      if (indent != StringRef::npos)
    51        min_indent = std::min(indent, min_indent);
    52      remaining = split.second;
    53    }
    54  
    55    // Print out the description indented.
    56    os << "\n";
    57    remaining = description;
    58    bool printed = false;
    59    while (!remaining.empty()) {
    60      auto split = remaining.split('\n');
    61      if (split.second.empty()) {
    62        // Skip last line with just spaces.
    63        if (split.first.ltrim().empty())
    64          break;
    65      }
    66      // Print empty new line without spaces if line only has spaces, unless no
    67      // text has been emitted before.
    68      if (split.first.ltrim().empty()) {
    69        if (printed)
    70          os << "\n";
    71      } else {
    72        os << split.first.substr(min_indent) << "\n";
    73        printed = true;
    74      }
    75      remaining = split.second;
    76    }
    77  }
    78  
    79  static void emitOpDoc(const RecordKeeper &recordKeeper, raw_ostream &os) {
    80    const auto &defs = recordKeeper.getAllDerivedDefinitions("Op");
    81    os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
    82  
    83    // TODO: Group by dialect.
    84    // TODO: Add docs for types used (maybe dialect specific ones?) and link
    85    // between use and def.
    86    os << "# Operation definition\n";
    87    for (auto *def : defs) {
    88      Operator op(def);
    89      os << "## " << op.getOperationName() << " (" << op.getQualCppClassName()
    90         << ")";
    91  
    92      // Emit summary & description of operator.
    93      if (op.hasSummary())
    94        os << "\n" << op.getSummary() << "\n";
    95      os << "\n### Description:\n";
    96      if (op.hasDescription())
    97        emitDescription(op.getDescription(), os);
    98  
    99      // Emit operands & type of operand. All operands are numbered, some may be
   100      // named too.
   101      os << "\n### Operands:\n";
   102      for (const auto &operand : op.getOperands()) {
   103        os << "1. ";
   104        if (!operand.name.empty())
   105          os << "`" << operand.name << "`: ";
   106        else
   107          os << "&laquo;unnamed&raquo;: ";
   108        os << operand.constraint.getDescription() << "\n";
   109      }
   110  
   111      // Emit attributes.
   112      // TODO: Attributes are only documented by TableGen name, with no further
   113      // info. This should be improved.
   114      os << "\n### Attributes:\n";
   115      if (op.getNumAttributes() > 0) {
   116        os << "| Attribute | MLIR Type | Description |\n"
   117           << "| :-------: | :-------: | ----------- |\n";
   118      }
   119      for (auto namedAttr : op.getAttributes()) {
   120        os << "| `" << namedAttr.name << "` | `"
   121           << namedAttr.attr.getStorageType() << "` | "
   122           << namedAttr.attr.getDescription() << " attribute |\n";
   123      }
   124  
   125      // Emit results.
   126      os << "\n### Results:\n";
   127      for (unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
   128        os << "1. ";
   129        auto name = op.getResultName(i);
   130        if (name.empty())
   131          os << "&laquo;unnamed&raquo;: ";
   132        else
   133          os << "`" << name << "`: ";
   134        os << op.getResultTypeConstraint(i).getDescription() << "\n";
   135      }
   136  
   137      os << "\n";
   138    }
   139  }
   140  
   141  static mlir::GenRegistration
   142      genRegister("gen-op-doc", "Generate operation documentation",
   143                  [](const RecordKeeper &records, raw_ostream &os) {
   144                    emitOpDoc(records, os);
   145                    return false;
   146                  });