[MLIR] Generate cpp comments for TableGen summary and description (#139606)

This commit takes the `summary` and `description` of TableGen files and
generate a cpp comments on top of the declarations of generated cpp
classes. 

The main motivation is to improve the developer experience. When people
work on compilers from an IDE, they will be able to hover over the
symbols (e.g. `"ADialect::BOp"`) in their cpp code and see the summary
and descriptions without having to referring to the `.td` files.
This commit is contained in:
Peng Chen 2025-05-14 13:09:48 -07:00 committed by GitHub
parent 5ee67ee6bc
commit 3138f6cc8c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 238 additions and 5 deletions

View File

@ -0,0 +1,139 @@
// RUN: mlir-tblgen -gen-dialect-decls -I %S/../../include %s | FileCheck %s --check-prefix=DIALECT
// RUN: mlir-tblgen -gen-op-decls -I %S/../../include %s | FileCheck %s --check-prefix=OP
// RUN: mlir-tblgen -gen-typedef-decls -I %S/../../include %s | FileCheck %s --check-prefix=TYPE
// RUN: mlir-tblgen -gen-attrdef-decls -I %S/../../include %s | FileCheck %s --check-prefix=ATTR
// RUN: mlir-tblgen -gen-attr-interface-decls -I %S/../../include %s | FileCheck %s --check-prefix=ATTR-INTERFACE
// RUN: mlir-tblgen -gen-op-interface-decls -I %S/../../include %s | FileCheck %s --check-prefix=OP-INTERFACE
// RUN: mlir-tblgen -gen-type-interface-decls -I %S/../../include %s | FileCheck %s --check-prefix=TYPE-INTERFACE
// RUN: mlir-tblgen -gen-enum-decls -I %S/../../include %s | FileCheck %s --check-prefix=ENUM
include "mlir/IR/AttrTypeBase.td"
include "mlir/IR/EnumAttr.td"
include "mlir/IR/OpBase.td"
// check dialect with summary and description
def A_Dialect : Dialect {
let name = "a";
let cppNamespace = "";
let summary = "This is a summary";
let description = [{
This is a description, needs trimming
}];
// DIALECT: /// This is a summary
// DIALECT-NEXT: /// This is a description, needs trimming
// DIALECT-NEXT: class ADialect : public ::mlir::Dialect {
}
def A_SomeOp1 : Op<A_Dialect, "some_op1", []>{
let summary = "Some Op1 summary line1 \nsummary line2";
let description = [{
Some Op1 description
}];
let cppNamespace = "OP1";
// OP: namespace OP1
// OP-NEXT: /// Some Op1 summary line1
// OP-NEXT: /// summary line2
// OP-NEXT: /// Some Op1 description
// OP-NEXT: class SomeOp1;
}
// test weird characters in description
def A_SomeOp2 : Op<A_Dialect, "some_op2", []>{
let summary = "";
let description = [{
$ptr (`,` $mask^)? (`,` $other^)?
oilist(
`a` `=` $1 | `b` `=` $2
)
}];
// OP: /// $ptr (`,` $mask^)? (`,` $other^)?
// OP-NEXT: /// oilist(
// OP-NEXT: /// `a` `=` $1 | `b` `=` $2
// OP-NEXT: /// )
// OP-NEXT: class SomeOp2;
}
def A_TensorType : TypeDef<A_Dialect,"Tensor"> {
let typeName = "a.simple_a_tensor";
let summary = "Tensor Type A summary";
let description = [{
Tensor Type A description
}];
let extraClassDeclaration = [{
void getSignlessBlockType() const {
}
}];
// TYPE: /// Tensor Type A summary
// TYPE-NEXT: /// Tensor Type A description
// TYPE-NEXT: class TensorType;
}
def A_SimpleAttr : AttrDef<A_Dialect,"SimpleA"> {
let attrName = "a.simple_attr";
let summary = "Simple Attr A summary";
let description = [{
Simple Attr A description
}];
// ATTR: /// Simple Attr A summary
// ATTR-NEXT: /// Simple Attr A description
// ATTR-NEXT: class SimpleAAttr;
}
def EncodingTrait : AttrInterface<"EncodingTrait"> {
let cppNamespace = "mlir::a::traits";
let description = [{
Common trait for all layouts.
}];
let methods = [
];
// ATTR-INTERFACE: namespace mlir
// ATTR-INTERFACE-NEXT: namespace a
// ATTR-INTERFACE-NEXT: namespace traits
// ATTR-INTERFACE-NEXT: /// Common trait for all layouts.
// ATTR-INTERFACE-NEXT: class EncodingTrait;
}
def SimpleEncodingTrait : AttrInterface<"SimpleEncodingTrait"> {
let cppNamespace = "a::traits";
// ATTR-INTERFACE: namespace a {
// ATTR-INTERFACE-NEXT: namespace traits {
// ATTR-INTERFACE-NEXT: class SimpleEncodingTrait;
}
def SimpleOpInterface : OpInterface<"SimpleOpInterface"> {
let cppNamespace = "a::traits";
let description = [{
Simple Op Interface description
}];
// OP-INTERFACE: namespace a {
// OP-INTERFACE-NEXT: namespace traits {
// OP-INTERFACE-NEXT: /// Simple Op Interface description
// OP-INTERFACE-NEXT: class SimpleOpInterface;
}
def SimpleTypeInterface : TypeInterface<"SimpleTypeInterface"> {
let description = [{
Simple Type Interface description
}];
// TYPE-INTERFACE: /// Simple Type Interface description
// TYPE-INTERFACE-NEXT: class SimpleTypeInterface;
}
def MyBitEnum: I32BitEnumAttr<"MyBitEnum", "An example bit enum",
[I32BitEnumCaseBit<"Bit0", 0, "tagged">,
I32BitEnumCaseBit<"Bit1", 1>]> {
let genSpecializedAttr = 0;
// ENUM: // An example bit enum
// ENUM-NEXT: enum class MyBitEnum
}

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "AttrOrTypeFormatGen.h"
#include "CppGenUtilities.h"
#include "mlir/TableGen/AttrOrTypeDef.h"
#include "mlir/TableGen/Class.h"
#include "mlir/TableGen/CodeGenHelpers.h"
@ -813,8 +814,14 @@ bool DefGenerator::emitDecls(StringRef selectedDialect) {
NamespaceEmitter nsEmitter(os, defs.front().getDialect());
// Declare all the def classes first (in case they reference each other).
for (const AttrOrTypeDef &def : defs)
for (const AttrOrTypeDef &def : defs) {
std::string comments = tblgen::emitSummaryAndDescComments(
def.getSummary(), def.getDescription());
if (!comments.empty()) {
os << comments << "\n";
}
os << "class " << def.getCppClassName() << ";\n";
}
// Emit the declarations.
for (const AttrOrTypeDef &def : defs)

View File

@ -33,6 +33,7 @@ add_tablegen(mlir-tblgen MLIR
RewriterGen.cpp
SPIRVUtilsGen.cpp
TosaUtilsGen.cpp
CppGenUtilities.cpp
)
target_link_libraries(mlir-tblgen

View File

@ -0,0 +1,39 @@
//===- CppGenUtilities.cpp - MLIR cpp gen utilities --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Defines common utilities for generating cpp files from tablegen
// structures.
//
//===----------------------------------------------------------------------===//
#include "CppGenUtilities.h"
#include "mlir/Support/IndentedOstream.h"
std::string
mlir::tblgen::emitSummaryAndDescComments(llvm::StringRef summary,
llvm::StringRef description) {
std::string comments = "";
StringRef trimmedSummary = summary.trim();
StringRef trimmedDesc = description.trim();
llvm::raw_string_ostream os(comments);
raw_indented_ostream ros(os);
if (!trimmedSummary.empty()) {
ros.printReindented(trimmedSummary, "/// ");
}
if (!trimmedDesc.empty()) {
if (!trimmedSummary.empty()) {
// If there is a summary, add a newline after it.
ros << "\n";
}
ros.printReindented(trimmedDesc, "/// ");
}
return comments;
}

View File

@ -0,0 +1,29 @@
//===- CppGenUtilities.h - MLIR cpp gen utilities ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines common utilities for generating cpp files from tablegen
// structures.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_TOOLS_MLIRTBLGEN_CPPGENUTILITIES_H_
#define MLIR_TOOLS_MLIRTBLGEN_CPPGENUTILITIES_H_
#include "llvm/ADT/StringRef.h"
namespace mlir {
namespace tblgen {
// Emit the summary and description as a C++ comment, perperly aligned placed
// adjacent to the class declaration of generated classes.
std::string emitSummaryAndDescComments(llvm::StringRef summary,
llvm::StringRef description);
} // namespace tblgen
} // namespace mlir
#endif // MLIR_TOOLS_MLIRTBLGEN_CPPGENUTILITIES_H_

View File

@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
#include "CppGenUtilities.h"
#include "DialectGenUtilities.h"
#include "mlir/TableGen/Class.h"
#include "mlir/TableGen/CodeGenHelpers.h"
@ -108,7 +109,9 @@ tblgen::findDialectToGenerate(ArrayRef<Dialect> dialects) {
/// {0}: The name of the dialect class.
/// {1}: The dialect namespace.
/// {2}: The dialect parent class.
/// {3}: The summary and description comments.
static const char *const dialectDeclBeginStr = R"(
{3}
class {0} : public ::mlir::{2} {
explicit {0}(::mlir::MLIRContext *context);
@ -245,8 +248,11 @@ static void emitDialectDecl(Dialect &dialect, raw_ostream &os) {
std::string cppName = dialect.getCppClassName();
StringRef superClassName =
dialect.isExtensible() ? "ExtensibleDialect" : "Dialect";
std::string comments = tblgen::emitSummaryAndDescComments(
dialect.getSummary(), dialect.getDescription());
os << llvm::formatv(dialectDeclBeginStr, cppName, dialect.getName(),
superClassName);
superClassName, comments);
// If the dialect requested the default attribute printer and parser, emit
// the declarations for the hooks.

View File

@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
#include "CppGenUtilities.h"
#include "OpClass.h"
#include "OpFormatGen.h"
#include "OpGenHelpers.h"
@ -2640,8 +2641,7 @@ void OpEmitter::genSeparateArgParamBuilder() {
// Avoid emitting "resultTypes.size() >= 0u" which is always true.
if (!hasVariadicResult || numNonVariadicResults != 0)
body << " "
<< "assert(resultTypes.size() "
body << " " << "assert(resultTypes.size() "
<< (hasVariadicResult ? ">=" : "==") << " "
<< numNonVariadicResults
<< "u && \"mismatched number of results\");\n";
@ -4749,6 +4749,11 @@ static void emitOpClassDecls(const RecordKeeper &records,
for (auto *def : defs) {
Operator op(*def);
NamespaceEmitter emitter(os, op.getCppNamespace());
std::string comments = tblgen::emitSummaryAndDescComments(
op.getSummary(), op.getDescription());
if (!comments.empty()) {
os << comments << "\n";
}
os << "class " << op.getCppClassName() << ";\n";
}

View File

@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
#include "CppGenUtilities.h"
#include "DocGenUtilities.h"
#include "mlir/TableGen/Format.h"
#include "mlir/TableGen/GenInfo.h"
@ -527,6 +528,11 @@ void InterfaceGenerator::emitInterfaceDecl(const Interface &interface) {
// Emit a forward declaration of the interface class so that it becomes usable
// in the signature of its methods.
std::string comments = tblgen::emitSummaryAndDescComments(
"", interface.getDescription().value_or(""));
if (!comments.empty()) {
os << comments << "\n";
}
os << "class " << interfaceName << ";\n";
// Emit the traits struct containing the concept and model declarations.
@ -589,7 +595,8 @@ void InterfaceGenerator::emitInterfaceDecl(const Interface &interface) {
<< " auto* interface = getInterfaceFor(base);\n"
<< " if (!interface)\n"
" return false;\n"
" " << interfaceName << " odsInterfaceInstance(base, interface);\n"
" "
<< interfaceName << " odsInterfaceInstance(base, interface);\n"
<< " " << tblgen::tgfmt(extraClassOf->trim(), &extraClassOfFmt)
<< "\n }\n";
}