[mlir][ods] Add description to Attr and ensure doc generation
authorAlex Zinenko <zinenko@google.com>
Mon, 4 Apr 2022 12:24:54 +0000 (14:24 +0200)
committerAlex Zinenko <zinenko@google.com>
Tue, 5 Apr 2022 08:40:15 +0000 (10:40 +0200)
Add the description textual field to the Attr ODS class to mirror an
identical field in the Type ODS class. Add support for generating
documentation for attribute constraints defined using this field. This
ensures mlir-tblgen produces at least some documentation for dialects
that only define attribute constraints, such as DLTI.

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D123024

mlir/include/mlir/IR/OpBase.td
mlir/include/mlir/TableGen/Attribute.h
mlir/lib/TableGen/Attribute.cpp
mlir/test/mlir-tblgen/gen-dialect-doc.td
mlir/tools/mlir-tblgen/OpDocGen.cpp

index a5b3ca2..416d05d 100644 (file)
@@ -958,8 +958,8 @@ def SignlessIntegerOrFloatLike : TypeConstraint<Or<[
 // Base attribute definition
 
 // Base class for all attributes.
-class Attr<Pred condition, string descr = ""> :
-    AttrConstraint<condition, descr> {
+class Attr<Pred condition, string summary = ""> :
+    AttrConstraint<condition, summary> {
   code storageType = ?; // The backing mlir::Attribute type
   code returnType = ?;  // The underlying C++ value type
 
@@ -1007,11 +1007,14 @@ class Attr<Pred condition, string descr = ""> :
 
   // The fully-qualified C++ namespace where the generated class lives.
   string cppNamespace = "";
+
+  // The full description of this attribute.
+  string description = "";
 }
 
 // An attribute of a specific dialect.
-class DialectAttr<Dialect d, Pred condition, string descr = ""> :
-    Attr<condition, descr> {
+class DialectAttr<Dialect d, Pred condition, string summary = ""> :
+    Attr<condition, summary> {
   Dialect dialect = d;
   let cppNamespace = d.cppNamespace;
 }
index 043a355..9e6165a 100644 (file)
@@ -113,6 +113,9 @@ public:
 
   // Returns the dialect for the attribute if defined.
   Dialect getDialect() const;
+
+  // Returns the description of the attribute.
+  StringRef getDescription() const;
 };
 
 // Wrapper class providing helper methods for accessing MLIR constant attribute
index ac62220..ae9183f 100644 (file)
@@ -132,6 +132,10 @@ Dialect Attribute::getDialect() const {
   return Dialect(nullptr);
 }
 
+StringRef Attribute::getDescription() const {
+  return def->getValueAsString("description");
+}
+
 ConstantAttr::ConstantAttr(const DefInit *init) : def(init->getDef()) {
   assert(def->isSubClassOf("ConstantAttr") &&
          "must be subclass of TableGen 'ConstantAttr' class");
index 1edb423..1eda916 100644 (file)
@@ -14,9 +14,28 @@ def Test_Dialect : Dialect {
 }
 def AOp : Op<Test_Dialect, "a", [NoSideEffect, SingleBlockImplicitTerminator<"YieldOp">]>;
 
+def TestAttr : DialectAttr<Test_Dialect, CPred<"true">> {
+  let summary = "attribute summary";
+  let description = "attribute description";
+}
+
+def TestType : DialectType<Test_Dialect, CPred<"true">> {
+  let summary = "type summary";
+  let description = "type description";
+}
+
 // CHECK: Dialect without a [TOC] here.
 // CHECK: TOC added by tool.
 // CHECK: [TOC]
+
+// CHECK: ## Attribute constraint definition
+// CHECK: ### attribute summary
+// CHECK: attribute description
+
+// CHECK: ## Type constraint definition
+// CHECK: ### type summary
+// CHECK: type description
+
 // CHECK-NOT: [TOC]
 // CHECK: Traits: SingleBlockImplicitTerminator<YieldOp>
 // CHECK: Interfaces: NoSideEffect (MemoryEffectOpInterface)
index 9b9c648..83229cd 100644 (file)
@@ -211,13 +211,23 @@ static void emitOpDoc(const RecordKeeper &recordKeeper, raw_ostream &os) {
 }
 
 //===----------------------------------------------------------------------===//
+// Attribute Documentation
+//===----------------------------------------------------------------------===//
+
+static void emitAttrDoc(const Attribute &attr, raw_ostream &os) {
+  os << "### " << attr.getSummary() << "\n\n";
+  emitDescription(attr.getDescription(), os);
+  os << "\n\n";
+}
+
+//===----------------------------------------------------------------------===//
 // Type Documentation
 //===----------------------------------------------------------------------===//
 
 static void emitTypeDoc(const Type &type, raw_ostream &os) {
-  os << "### " << type.getSummary() << "\n";
+  os << "### " << type.getSummary() << "\n\n";
   emitDescription(type.getDescription(), os);
-  os << "\n";
+  os << "\n\n";
 }
 
 //===----------------------------------------------------------------------===//
@@ -292,9 +302,11 @@ static void emitAttrOrTypeDefDoc(const RecordKeeper &recordKeeper,
 // Dialect Documentation
 //===----------------------------------------------------------------------===//
 
-static void emitDialectDoc(const Dialect &dialect, ArrayRef<AttrDef> attrDefs,
-                           ArrayRef<Operator> ops, ArrayRef<Type> types,
-                           ArrayRef<TypeDef> typeDefs, raw_ostream &os) {
+static void emitDialectDoc(const Dialect &dialect,
+                           ArrayRef<Attribute> attributes,
+                           ArrayRef<AttrDef> attrDefs, ArrayRef<Operator> ops,
+                           ArrayRef<Type> types, ArrayRef<TypeDef> typeDefs,
+                           raw_ostream &os) {
   if (selectedDialect.getNumOccurrences() &&
       dialect.getName() != selectedDialect)
     return;
@@ -307,6 +319,12 @@ static void emitDialectDoc(const Dialect &dialect, ArrayRef<AttrDef> attrDefs,
   if (!r.match(dialect.getDescription()))
     os << "[TOC]\n\n";
 
+  if (!attributes.empty()) {
+    os << "## Attribute constraint definition\n\n";
+    for (const Attribute &attr : attributes)
+      emitAttrDoc(attr, os);
+  }
+
   if (!attrDefs.empty()) {
     os << "## Attribute definition\n\n";
     for (const AttrDef &def : attrDefs)
@@ -335,6 +353,8 @@ static void emitDialectDoc(const Dialect &dialect, ArrayRef<AttrDef> attrDefs,
 
 static void emitDialectDoc(const RecordKeeper &recordKeeper, raw_ostream &os) {
   std::vector<Record *> opDefs = getRequestedOpDefinitions(recordKeeper);
+  std::vector<Record *> attrDefs =
+      recordKeeper.getAllDerivedDefinitionsIfDefined("DialectAttr");
   std::vector<Record *> typeDefs =
       recordKeeper.getAllDerivedDefinitionsIfDefined("DialectType");
   std::vector<Record *> typeDefDefs =
@@ -344,26 +364,36 @@ static void emitDialectDoc(const RecordKeeper &recordKeeper, raw_ostream &os) {
 
   std::set<Dialect> dialectsWithDocs;
 
+  llvm::StringMap<std::vector<Attribute>> dialectAttrs;
   llvm::StringMap<std::vector<AttrDef>> dialectAttrDefs;
   llvm::StringMap<std::vector<Operator>> dialectOps;
   llvm::StringMap<std::vector<Type>> dialectTypes;
   llvm::StringMap<std::vector<TypeDef>> dialectTypeDefs;
-  for (auto *attrDef : attrDefDefs) {
+  for (Record *attrDef : attrDefs) {
+    Attribute attr(attrDef);
+    if (const Dialect &dialect = attr.getDialect()) {
+      dialectAttrs[dialect.getName()].push_back(attr);
+      dialectsWithDocs.insert(dialect);
+    }
+  }
+  for (Record *attrDef : attrDefDefs) {
     AttrDef attr(attrDef);
     dialectAttrDefs[attr.getDialect().getName()].push_back(attr);
     dialectsWithDocs.insert(attr.getDialect());
   }
-  for (auto *opDef : opDefs) {
+  for (Record *opDef : opDefs) {
     Operator op(opDef);
     dialectOps[op.getDialect().getName()].push_back(op);
     dialectsWithDocs.insert(op.getDialect());
   }
-  for (auto *typeDef : typeDefs) {
+  for (Record *typeDef : typeDefs) {
     Type type(typeDef);
-    if (auto dialect = type.getDialect())
+    if (const Dialect &dialect = type.getDialect()) {
       dialectTypes[dialect.getName()].push_back(type);
+      dialectsWithDocs.insert(dialect);
+    }
   }
-  for (auto *typeDef : typeDefDefs) {
+  for (Record *typeDef : typeDefDefs) {
     TypeDef type(typeDef);
     dialectTypeDefs[type.getDialect().getName()].push_back(type);
     dialectsWithDocs.insert(type.getDialect());
@@ -372,9 +402,9 @@ static void emitDialectDoc(const RecordKeeper &recordKeeper, raw_ostream &os) {
   os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
   for (const Dialect &dialect : dialectsWithDocs) {
     StringRef dialectName = dialect.getName();
-    emitDialectDoc(dialect, dialectAttrDefs[dialectName],
-                   dialectOps[dialectName], dialectTypes[dialectName],
-                   dialectTypeDefs[dialectName], os);
+    emitDialectDoc(dialect, dialectAttrs[dialectName],
+                   dialectAttrDefs[dialectName], dialectOps[dialectName],
+                   dialectTypes[dialectName], dialectTypeDefs[dialectName], os);
   }
 }