Enable emitting dialect summary & description during op generation

Sort ops per dialect and emit summary & description (if provided) of each dialect before emitting the ops of the dialect.

PiperOrigin-RevId: 273077138
This commit is contained in:
Jacques Pienaar 2019-10-05 12:21:07 -07:00 committed by A. Unique TensorFlower
parent 18db4ce493
commit 77672c9777
4 changed files with 82 additions and 14 deletions

View File

@ -41,6 +41,20 @@ public:
// Returns the C++ namespaces that ops of this dialect should be placed into.
StringRef getCppNamespace() const;
// Returns the summary description of the dialect. Returns empty string if
// none.
StringRef getSummary() const;
// Returns the description of the dialect. Returns empty string if none.
StringRef getDescription() const;
// Returns whether two dialects are equal by checking the equality of the
// underlying record.
bool operator==(const Dialect &other) const;
// Compares two dialects by comparing the names of the dialects.
bool operator<(const Dialect &other) const;
private:
const llvm::Record &def;
};

View File

@ -161,6 +161,9 @@ public:
// not provide enough methods.
const llvm::Record &getDef() const;
// Returns the dialect of the op.
const Dialect &getDialect() const { return dialect; }
private:
// Populates the vectors containing operands, attributes, results and traits.
void populateOpStructure();

View File

@ -33,5 +33,31 @@ StringRef tblgen::Dialect::getCppNamespace() const {
return def.getValueAsString("cppNamespace");
}
static StringRef getAsStringOrEmpty(const llvm::Record &record,
StringRef fieldName) {
if (auto valueInit = record.getValueInit(fieldName)) {
if (llvm::isa<llvm::CodeInit>(valueInit) ||
llvm::isa<llvm::StringInit>(valueInit))
return record.getValueAsString(fieldName);
}
return "";
}
StringRef tblgen::Dialect::getSummary() const {
return getAsStringOrEmpty(def, "summary");
}
StringRef tblgen::Dialect::getDescription() const {
return getAsStringOrEmpty(def, "description");
}
bool Dialect::operator==(const Dialect &other) const {
return &def == &other.def;
}
bool Dialect::operator<(const Dialect &other) const {
return getName() < other.getName();
}
} // end namespace tblgen
} // end namespace mlir

View File

@ -23,6 +23,7 @@
#include "DocGenUtilities.h"
#include "mlir/TableGen/GenInfo.h"
#include "mlir/TableGen/Operator.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Signals.h"
@ -32,6 +33,7 @@
using namespace llvm;
using namespace mlir;
using namespace mlir::tblgen;
using mlir::tblgen::Operator;
@ -77,29 +79,39 @@ void mlir::tblgen::emitDescription(StringRef description, raw_ostream &os) {
}
}
static void emitOpDoc(const RecordKeeper &recordKeeper, raw_ostream &os) {
const auto &defs = recordKeeper.getAllDerivedDefinitions("Op");
os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
// Emits `str` with trailing newline if not empty.
static void emitIfNotEmpty(StringRef str, raw_ostream &os) {
if (!str.empty()) {
emitDescription(str, os);
os << "\n";
}
}
// TODO: Group by dialect.
// TODO: Add docs for types used (maybe dialect specific ones?) and link
// between use and def.
os << "# Operation definition\n";
for (auto *def : defs) {
Operator op(def);
os << "## " << op.getOperationName() << " (" << op.getQualCppClassName()
static void emitOpDocForDialect(const Dialect &dialect,
const std::vector<Operator> &ops,
raw_ostream &os) {
os << "# Dialect '" << dialect.getName() << "' definition\n";
emitIfNotEmpty(dialect.getSummary(), os);
emitIfNotEmpty(dialect.getDescription(), os);
// TODO(antiagainst): Add docs for types used (maybe dialect specific ones?)
// and link between use and def.
os << "## Operation definition\n";
for (auto op : ops) {
os << "### " << op.getOperationName() << " (" << op.getQualCppClassName()
<< ")";
// Emit summary & description of operator.
if (op.hasSummary())
os << "\n" << op.getSummary() << "\n";
os << "\n### Description:\n";
os << "\n#### Description:\n";
if (op.hasDescription())
mlir::tblgen::emitDescription(op.getDescription(), os);
// Emit operands & type of operand. All operands are numbered, some may be
// named too.
os << "\n### Operands:\n";
os << "\n#### Operands:\n";
for (const auto &operand : op.getOperands()) {
os << "1. ";
if (!operand.name.empty())
@ -112,7 +124,7 @@ static void emitOpDoc(const RecordKeeper &recordKeeper, raw_ostream &os) {
// Emit attributes.
// TODO: Attributes are only documented by TableGen name, with no further
// info. This should be improved.
os << "\n### Attributes:\n";
os << "\n#### Attributes:\n";
if (op.getNumAttributes() > 0) {
os << "| Attribute | MLIR Type | Description |\n"
<< "| :-------: | :-------: | ----------- |\n";
@ -124,7 +136,7 @@ static void emitOpDoc(const RecordKeeper &recordKeeper, raw_ostream &os) {
}
// Emit results.
os << "\n### Results:\n";
os << "\n#### Results:\n";
for (unsigned i = 0, e = op.getNumResults(); i < e; ++i) {
os << "1. ";
auto name = op.getResultName(i);
@ -139,6 +151,19 @@ static void emitOpDoc(const RecordKeeper &recordKeeper, raw_ostream &os) {
}
}
static void emitOpDoc(const RecordKeeper &recordKeeper, raw_ostream &os) {
const auto &defs = recordKeeper.getAllDerivedDefinitions("Op");
os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
std::map<Dialect, std::vector<Operator>> dialectOps;
for (auto *def : defs) {
Operator op(def);
dialectOps[op.getDialect()].push_back(op);
}
for (auto dialectWithOps : dialectOps)
emitOpDocForDialect(dialectWithOps.first, dialectWithOps.second, os);
}
static mlir::GenRegistration
genRegister("gen-op-doc", "Generate operation documentation",
[](const RecordKeeper &records, raw_ostream &os) {