720 lines · cpp
1//===- OpInterfacesGen.cpp - MLIR op interface utility generator ----------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// OpInterfacesGen generates definitions for operation interfaces.10//11//===----------------------------------------------------------------------===//12 13#include "CppGenUtilities.h"14#include "DocGenUtilities.h"15#include "mlir/TableGen/Format.h"16#include "mlir/TableGen/GenInfo.h"17#include "mlir/TableGen/Interfaces.h"18#include "llvm/ADT/SmallVector.h"19#include "llvm/ADT/StringExtras.h"20#include "llvm/Support/FormatVariadic.h"21#include "llvm/Support/raw_ostream.h"22#include "llvm/TableGen/CodeGenHelpers.h"23#include "llvm/TableGen/Error.h"24#include "llvm/TableGen/Record.h"25#include "llvm/TableGen/TableGenBackend.h"26 27using namespace mlir;28using llvm::Record;29using llvm::RecordKeeper;30using mlir::tblgen::Interface;31using mlir::tblgen::InterfaceMethod;32using mlir::tblgen::OpInterface;33 34/// Emit a string corresponding to a C++ type, followed by a space if necessary.35static raw_ostream &emitCPPType(StringRef type, raw_ostream &os) {36 type = type.trim();37 os << type;38 if (type.back() != '&' && type.back() != '*')39 os << " ";40 return os;41}42 43/// Emit the method name and argument list for the given method. If 'addThisArg'44/// is true, then an argument is added to the beginning of the argument list for45/// the concrete value.46static void emitMethodNameAndArgs(const InterfaceMethod &method, StringRef name,47 raw_ostream &os, StringRef valueType,48 bool addThisArg, bool addConst) {49 os << name << '(';50 if (addThisArg) {51 if (addConst)52 os << "const ";53 os << "const Concept *impl, ";54 emitCPPType(valueType, os)55 << "tablegen_opaque_val" << (method.arg_empty() ? "" : ", ");56 }57 llvm::interleaveComma(method.getArguments(), os,58 [&](const InterfaceMethod::Argument &arg) {59 os << arg.type << " " << arg.name;60 });61 os << ')';62 if (addConst)63 os << " const";64}65 66/// Get an array of all OpInterface definitions but exclude those subclassing67/// "DeclareOpInterfaceMethods".68static std::vector<const Record *>69getAllInterfaceDefinitions(const RecordKeeper &records, StringRef name) {70 std::vector<const Record *> defs =71 records.getAllDerivedDefinitions((name + "Interface").str());72 73 std::string declareName = ("Declare" + name + "InterfaceMethods").str();74 llvm::erase_if(defs, [&](const Record *def) {75 // Ignore any "declare methods" interfaces.76 if (def->isSubClassOf(declareName))77 return true;78 // Ignore interfaces defined outside of the top-level file.79 return llvm::SrcMgr.FindBufferContainingLoc(def->getLoc()[0]) !=80 llvm::SrcMgr.getMainFileID();81 });82 return defs;83}84 85namespace {86/// This struct is the base generator used when processing tablegen interfaces.87class InterfaceGenerator {88public:89 bool emitInterfaceDefs();90 bool emitInterfaceDecls();91 bool emitInterfaceDocs();92 93protected:94 InterfaceGenerator(std::vector<const Record *> &&defs, raw_ostream &os)95 : defs(std::move(defs)), os(os) {}96 97 void emitConceptDecl(const Interface &interface);98 void emitModelDecl(const Interface &interface);99 void emitModelMethodsDef(const Interface &interface);100 void forwardDeclareInterface(const Interface &interface);101 void emitInterfaceDecl(const Interface &interface);102 void emitInterfaceTraitDecl(const Interface &interface);103 104 /// The set of interface records to emit.105 std::vector<const Record *> defs;106 // The stream to emit to.107 raw_ostream &os;108 /// The C++ value type of the interface, e.g. Operation*.109 StringRef valueType;110 /// The C++ base interface type.111 StringRef interfaceBaseType;112 /// The name of the typename for the value template.113 StringRef valueTemplate;114 /// The name of the substituion variable for the value.115 StringRef substVar;116 /// The format context to use for methods.117 tblgen::FmtContext nonStaticMethodFmt;118 tblgen::FmtContext traitMethodFmt;119 tblgen::FmtContext extraDeclsFmt;120};121 122/// A specialized generator for attribute interfaces.123struct AttrInterfaceGenerator : public InterfaceGenerator {124 AttrInterfaceGenerator(const RecordKeeper &records, raw_ostream &os)125 : InterfaceGenerator(getAllInterfaceDefinitions(records, "Attr"), os) {126 valueType = "::mlir::Attribute";127 interfaceBaseType = "AttributeInterface";128 valueTemplate = "ConcreteAttr";129 substVar = "_attr";130 StringRef castCode = "(::llvm::cast<ConcreteAttr>(tablegen_opaque_val))";131 nonStaticMethodFmt.addSubst(substVar, castCode).withSelf(castCode);132 traitMethodFmt.addSubst(substVar,133 "(*static_cast<const ConcreteAttr *>(this))");134 extraDeclsFmt.addSubst(substVar, "(*this)");135 }136};137/// A specialized generator for operation interfaces.138struct OpInterfaceGenerator : public InterfaceGenerator {139 OpInterfaceGenerator(const RecordKeeper &records, raw_ostream &os)140 : InterfaceGenerator(getAllInterfaceDefinitions(records, "Op"), os) {141 valueType = "::mlir::Operation *";142 interfaceBaseType = "OpInterface";143 valueTemplate = "ConcreteOp";144 substVar = "_op";145 StringRef castCode = "(llvm::cast<ConcreteOp>(tablegen_opaque_val))";146 nonStaticMethodFmt.addSubst("_this", "impl")147 .addSubst(substVar, castCode)148 .withSelf(castCode);149 traitMethodFmt.addSubst(substVar, "(*static_cast<ConcreteOp *>(this))");150 extraDeclsFmt.addSubst(substVar, "(*this)");151 }152};153/// A specialized generator for type interfaces.154struct TypeInterfaceGenerator : public InterfaceGenerator {155 TypeInterfaceGenerator(const RecordKeeper &records, raw_ostream &os)156 : InterfaceGenerator(getAllInterfaceDefinitions(records, "Type"), os) {157 valueType = "::mlir::Type";158 interfaceBaseType = "TypeInterface";159 valueTemplate = "ConcreteType";160 substVar = "_type";161 StringRef castCode = "(::llvm::cast<ConcreteType>(tablegen_opaque_val))";162 nonStaticMethodFmt.addSubst(substVar, castCode).withSelf(castCode);163 traitMethodFmt.addSubst(substVar,164 "(*static_cast<const ConcreteType *>(this))");165 extraDeclsFmt.addSubst(substVar, "(*this)");166 }167};168} // namespace169 170//===----------------------------------------------------------------------===//171// GEN: Interface definitions172//===----------------------------------------------------------------------===//173 174static void emitInterfaceMethodDoc(const InterfaceMethod &method,175 raw_ostream &os, StringRef prefix = "") {176 if (std::optional<StringRef> description = method.getDescription())177 tblgen::emitDescriptionComment(*description, os, prefix);178}179static void emitInterfaceDefMethods(StringRef interfaceQualName,180 const Interface &interface,181 StringRef valueType, const Twine &implValue,182 raw_ostream &os, bool isOpInterface) {183 for (auto &method : interface.getMethods()) {184 emitInterfaceMethodDoc(method, os);185 emitCPPType(method.getReturnType(), os);186 os << interfaceQualName << "::";187 emitMethodNameAndArgs(method, method.getName(), os, valueType,188 /*addThisArg=*/false,189 /*addConst=*/!isOpInterface);190 191 // Forward to the method on the concrete operation type.192 os << " {\n return " << implValue << "->" << method.getUniqueName()193 << '(';194 if (!method.isStatic()) {195 os << implValue << ", ";196 os << (isOpInterface ? "getOperation()" : "*this");197 os << (method.arg_empty() ? "" : ", ");198 }199 llvm::interleaveComma(200 method.getArguments(), os,201 [&](const InterfaceMethod::Argument &arg) { os << arg.name; });202 os << ");\n }\n";203 }204}205 206static void emitInterfaceDef(const Interface &interface, StringRef valueType,207 raw_ostream &os) {208 std::string interfaceQualNameStr = interface.getFullyQualifiedName();209 StringRef interfaceQualName = interfaceQualNameStr;210 interfaceQualName.consume_front("::");211 212 // Insert the method definitions.213 bool isOpInterface = isa<OpInterface>(interface);214 emitInterfaceDefMethods(interfaceQualName, interface, valueType, "getImpl()",215 os, isOpInterface);216 217 // Insert the method definitions for base classes.218 for (auto &base : interface.getBaseInterfaces()) {219 emitInterfaceDefMethods(interfaceQualName, base, valueType,220 "getImpl()->impl" + base.getName(), os,221 isOpInterface);222 }223}224 225bool InterfaceGenerator::emitInterfaceDefs() {226 llvm::emitSourceFileHeader("Interface Definitions", os);227 228 for (const auto *def : defs)229 emitInterfaceDef(Interface(def), valueType, os);230 return false;231}232 233//===----------------------------------------------------------------------===//234// GEN: Interface declarations235//===----------------------------------------------------------------------===//236 237void InterfaceGenerator::emitConceptDecl(const Interface &interface) {238 os << " struct Concept {\n";239 240 // Insert each of the pure virtual concept methods.241 os << " /// The methods defined by the interface.\n";242 for (auto &method : interface.getMethods()) {243 os << " ";244 emitCPPType(method.getReturnType(), os);245 os << "(*" << method.getUniqueName() << ")(";246 if (!method.isStatic()) {247 os << "const Concept *impl, ";248 emitCPPType(valueType, os) << (method.arg_empty() ? "" : ", ");249 }250 llvm::interleaveComma(251 method.getArguments(), os,252 [&](const InterfaceMethod::Argument &arg) { os << arg.type; });253 os << ");\n";254 }255 256 // Insert a field containing a concept for each of the base interfaces.257 auto baseInterfaces = interface.getBaseInterfaces();258 if (!baseInterfaces.empty()) {259 os << " /// The base classes of this interface.\n";260 for (const auto &base : interface.getBaseInterfaces()) {261 os << " const " << base.getFullyQualifiedName() << "::Concept *impl"262 << base.getName() << " = nullptr;\n";263 }264 265 // Define an "initialize" method that allows for the initialization of the266 // base class concepts.267 os << "\n void initializeInterfaceConcept(::mlir::detail::InterfaceMap "268 "&interfaceMap) {\n";269 std::string interfaceQualName = interface.getFullyQualifiedName();270 for (const auto &base : interface.getBaseInterfaces()) {271 StringRef baseName = base.getName();272 std::string baseQualName = base.getFullyQualifiedName();273 os << " impl" << baseName << " = interfaceMap.lookup<"274 << baseQualName << ">();\n"275 << " assert(impl" << baseName << " && \"`" << interfaceQualName276 << "` expected its base interface `" << baseQualName277 << "` to be registered\");\n";278 }279 os << " }\n";280 }281 282 os << " };\n";283}284 285void InterfaceGenerator::emitModelDecl(const Interface &interface) {286 // Emit the basic model and the fallback model.287 for (const char *modelClass : {"Model", "FallbackModel"}) {288 os << " template<typename " << valueTemplate << ">\n";289 os << " class " << modelClass << " : public Concept {\n public:\n";290 os << " using Interface = " << interface.getFullyQualifiedName()291 << ";\n";292 os << " " << modelClass << "() : Concept{";293 llvm::interleaveComma(294 interface.getMethods(), os,295 [&](const InterfaceMethod &method) { os << method.getUniqueName(); });296 os << "} {}\n\n";297 298 // Insert each of the virtual method overrides.299 for (auto &method : interface.getMethods()) {300 emitCPPType(method.getReturnType(), os << " static inline ");301 emitMethodNameAndArgs(method, method.getUniqueName(), os, valueType,302 /*addThisArg=*/!method.isStatic(),303 /*addConst=*/false);304 os << ";\n";305 }306 os << " };\n";307 }308 309 // Emit the template for the external model.310 os << " template<typename ConcreteModel, typename " << valueTemplate311 << ">\n";312 os << " class ExternalModel : public FallbackModel<ConcreteModel> {\n";313 os << " public:\n";314 os << " using ConcreteEntity = " << valueTemplate << ";\n";315 316 // Emit declarations for methods that have default implementations. Other317 // methods are expected to be implemented by the concrete derived model.318 for (auto &method : interface.getMethods()) {319 if (!method.getDefaultImplementation())320 continue;321 os << " ";322 if (method.isStatic())323 os << "static ";324 emitCPPType(method.getReturnType(), os);325 os << method.getUniqueName() << "(";326 if (!method.isStatic()) {327 emitCPPType(valueType, os);328 os << "tablegen_opaque_val";329 if (!method.arg_empty())330 os << ", ";331 }332 llvm::interleaveComma(method.getArguments(), os,333 [&](const InterfaceMethod::Argument &arg) {334 emitCPPType(arg.type, os);335 os << arg.name;336 });337 os << ")";338 if (!method.isStatic())339 os << " const";340 os << ";\n";341 }342 os << " };\n";343}344 345void InterfaceGenerator::emitModelMethodsDef(const Interface &interface) {346 llvm::NamespaceEmitter ns(os, interface.getCppNamespace());347 for (auto &method : interface.getMethods()) {348 os << "template<typename " << valueTemplate << ">\n";349 emitCPPType(method.getReturnType(), os);350 os << "detail::" << interface.getName() << "InterfaceTraits::Model<"351 << valueTemplate << ">::";352 emitMethodNameAndArgs(method, method.getUniqueName(), os, valueType,353 /*addThisArg=*/!method.isStatic(),354 /*addConst=*/false);355 os << " {\n ";356 357 // Check for a provided body to the function.358 if (std::optional<StringRef> body = method.getBody()) {359 if (method.isStatic())360 os << body->trim();361 else362 os << tblgen::tgfmt(body->trim(), &nonStaticMethodFmt);363 os << "\n}\n";364 continue;365 }366 367 // Forward to the method on the concrete operation type.368 if (method.isStatic())369 os << "return " << valueTemplate << "::";370 else371 os << tblgen::tgfmt("return $_self.", &nonStaticMethodFmt);372 373 // Add the arguments to the call.374 os << method.getName() << '(';375 llvm::interleaveComma(376 method.getArguments(), os,377 [&](const InterfaceMethod::Argument &arg) { os << arg.name; });378 os << ");\n}\n";379 }380 381 for (auto &method : interface.getMethods()) {382 os << "template<typename " << valueTemplate << ">\n";383 emitCPPType(method.getReturnType(), os);384 os << "detail::" << interface.getName() << "InterfaceTraits::FallbackModel<"385 << valueTemplate << ">::";386 emitMethodNameAndArgs(method, method.getUniqueName(), os, valueType,387 /*addThisArg=*/!method.isStatic(),388 /*addConst=*/false);389 os << " {\n ";390 391 // Forward to the method on the concrete Model implementation.392 if (method.isStatic())393 os << "return " << valueTemplate << "::";394 else395 os << "return static_cast<const " << valueTemplate << " *>(impl)->";396 397 // Add the arguments to the call.398 os << method.getUniqueName() << '(';399 if (!method.isStatic())400 os << "tablegen_opaque_val" << (method.arg_empty() ? "" : ", ");401 llvm::interleaveComma(402 method.getArguments(), os,403 [&](const InterfaceMethod::Argument &arg) { os << arg.name; });404 os << ");\n}\n";405 }406 407 // Emit default implementations for the external model.408 for (auto &method : interface.getMethods()) {409 if (!method.getDefaultImplementation())410 continue;411 os << "template<typename ConcreteModel, typename " << valueTemplate412 << ">\n";413 emitCPPType(method.getReturnType(), os);414 os << "detail::" << interface.getName()415 << "InterfaceTraits::ExternalModel<ConcreteModel, " << valueTemplate416 << ">::";417 418 os << method.getUniqueName() << "(";419 if (!method.isStatic()) {420 emitCPPType(valueType, os);421 os << "tablegen_opaque_val";422 if (!method.arg_empty())423 os << ", ";424 }425 llvm::interleaveComma(method.getArguments(), os,426 [&](const InterfaceMethod::Argument &arg) {427 emitCPPType(arg.type, os);428 os << arg.name;429 });430 os << ")";431 if (!method.isStatic())432 os << " const";433 434 os << " {\n";435 436 // Use the empty context for static methods.437 tblgen::FmtContext ctx;438 os << tblgen::tgfmt(method.getDefaultImplementation()->trim(),439 method.isStatic() ? &ctx : &nonStaticMethodFmt);440 os << "\n}\n";441 }442}443 444void InterfaceGenerator::emitInterfaceTraitDecl(const Interface &interface) {445 auto cppNamespace = (interface.getCppNamespace() + "::detail").str();446 llvm::NamespaceEmitter ns(os, cppNamespace);447 448 StringRef interfaceName = interface.getName();449 auto interfaceTraitsName = (interfaceName + "InterfaceTraits").str();450 os << llvm::formatv(" template <typename {3}>\n"451 " struct {0}Trait : public ::mlir::{2}<{0},"452 " detail::{1}>::Trait<{3}> {{\n",453 interfaceName, interfaceTraitsName, interfaceBaseType,454 valueTemplate);455 456 // Insert the default implementation for any methods.457 bool isOpInterface = isa<OpInterface>(interface);458 for (auto &method : interface.getMethods()) {459 // Flag interface methods named verifyTrait.460 if (method.getName() == "verifyTrait")461 PrintFatalError(462 formatv("'verifyTrait' method cannot be specified as interface "463 "method for '{0}'; use the 'verify' field instead",464 interfaceName));465 auto defaultImpl = method.getDefaultImplementation();466 if (!defaultImpl)467 continue;468 469 emitInterfaceMethodDoc(method, os, " ");470 os << " " << (method.isStatic() ? "static " : "");471 emitCPPType(method.getReturnType(), os);472 emitMethodNameAndArgs(method, method.getName(), os, valueType,473 /*addThisArg=*/false,474 /*addConst=*/!isOpInterface && !method.isStatic());475 os << " {\n " << tblgen::tgfmt(defaultImpl->trim(), &traitMethodFmt)476 << "\n }\n";477 }478 479 if (auto verify = interface.getVerify()) {480 assert(isa<OpInterface>(interface) && "only OpInterface supports 'verify'");481 482 tblgen::FmtContext verifyCtx;483 verifyCtx.addSubst("_op", "op");484 os << llvm::formatv(485 " static ::llvm::LogicalResult {0}(::mlir::Operation *op) ",486 (interface.verifyWithRegions() ? "verifyRegionTrait"487 : "verifyTrait"))488 << "{\n " << tblgen::tgfmt(verify->trim(), &verifyCtx)489 << "\n }\n";490 }491 if (auto extraTraitDecls = interface.getExtraTraitClassDeclaration())492 os << tblgen::tgfmt(*extraTraitDecls, &traitMethodFmt) << "\n";493 if (auto extraTraitDecls = interface.getExtraSharedClassDeclaration())494 os << tblgen::tgfmt(*extraTraitDecls, &traitMethodFmt) << "\n";495 496 os << " };\n";497}498 499static void emitInterfaceDeclMethods(const Interface &interface,500 raw_ostream &os, StringRef valueType,501 bool isOpInterface,502 tblgen::FmtContext &extraDeclsFmt) {503 for (auto &method : interface.getMethods()) {504 emitInterfaceMethodDoc(method, os, " ");505 emitCPPType(method.getReturnType(), os << " ");506 emitMethodNameAndArgs(method, method.getName(), os, valueType,507 /*addThisArg=*/false,508 /*addConst=*/!isOpInterface);509 os << ";\n";510 }511 512 // Emit any extra declarations.513 if (std::optional<StringRef> extraDecls =514 interface.getExtraClassDeclaration())515 os << extraDecls->rtrim() << "\n";516 if (std::optional<StringRef> extraDecls =517 interface.getExtraSharedClassDeclaration())518 os << tblgen::tgfmt(extraDecls->rtrim(), &extraDeclsFmt) << "\n";519}520 521void InterfaceGenerator::forwardDeclareInterface(const Interface &interface) {522 llvm::NamespaceEmitter ns(os, interface.getCppNamespace());523 524 // Emit a forward declaration of the interface class so that it becomes usable525 // in the signature of its methods.526 tblgen::emitSummaryAndDescComments(os, "",527 interface.getDescription().value_or(""));528 529 StringRef interfaceName = interface.getName();530 os << "class " << interfaceName << ";\n";531}532 533void InterfaceGenerator::emitInterfaceDecl(const Interface &interface) {534 llvm::NamespaceEmitter ns(os, interface.getCppNamespace());535 536 StringRef interfaceName = interface.getName();537 auto interfaceTraitsName = (interfaceName + "InterfaceTraits").str();538 539 // Emit a forward declaration of the interface class so that it becomes usable540 // in the signature of its methods.541 tblgen::emitSummaryAndDescComments(os, "",542 interface.getDescription().value_or(""));543 544 // Emit the traits struct containing the concept and model declarations.545 os << "namespace detail {\n"546 << "struct " << interfaceTraitsName << " {\n";547 emitConceptDecl(interface);548 emitModelDecl(interface);549 os << "};\n";550 551 // Emit the derived trait for the interface.552 os << "template <typename " << valueTemplate << ">\n";553 os << "struct " << interface.getName() << "Trait;\n";554 555 os << "\n} // namespace detail\n";556 557 // Emit the main interface class declaration.558 os << llvm::formatv("class {0} : public ::mlir::{3}<{1}, detail::{2}> {\n"559 "public:\n"560 " using ::mlir::{3}<{1}, detail::{2}>::{3};\n",561 interfaceName, interfaceName, interfaceTraitsName,562 interfaceBaseType);563 564 // Emit a utility wrapper trait class.565 os << llvm::formatv(" template <typename {1}>\n"566 " struct Trait : public detail::{0}Trait<{1}> {{};\n",567 interfaceName, valueTemplate);568 569 // Insert the method declarations.570 bool isOpInterface = isa<OpInterface>(interface);571 emitInterfaceDeclMethods(interface, os, valueType, isOpInterface,572 extraDeclsFmt);573 574 // Insert the method declarations for base classes.575 for (auto &base : interface.getBaseInterfaces()) {576 std::string baseQualName = base.getFullyQualifiedName();577 os << " //"578 "===---------------------------------------------------------------"579 "-===//\n"580 << " // Inherited from " << baseQualName << "\n"581 << " //"582 "===---------------------------------------------------------------"583 "-===//\n\n";584 585 // Allow implicit conversion to the base interface.586 os << " operator " << baseQualName << " () const {\n"587 << " if (!*this) return nullptr;\n"588 << " return " << baseQualName << "(*this, getImpl()->impl"589 << base.getName() << ");\n"590 << " }\n\n";591 592 // Inherit the base interface's methods.593 emitInterfaceDeclMethods(base, os, valueType, isOpInterface, extraDeclsFmt);594 }595 596 // Emit classof code if necessary.597 if (std::optional<StringRef> extraClassOf = interface.getExtraClassOf()) {598 auto extraClassOfFmt = tblgen::FmtContext();599 extraClassOfFmt.addSubst(substVar, "odsInterfaceInstance");600 os << " static bool classof(" << valueType << " base) {\n"601 << " auto* interface = getInterfaceFor(base);\n"602 << " if (!interface)\n"603 " return false;\n"604 " "605 << interfaceName << " odsInterfaceInstance(base, interface);\n"606 << " " << tblgen::tgfmt(extraClassOf->trim(), &extraClassOfFmt)607 << "\n }\n";608 }609 610 os << "};\n";611}612 613bool InterfaceGenerator::emitInterfaceDecls() {614 llvm::emitSourceFileHeader("Interface Declarations", os);615 // Sort according to ID, so defs are emitted in the order in which they appear616 // in the Tablegen file.617 std::vector<const Record *> sortedDefs(defs);618 llvm::sort(sortedDefs, [](const Record *lhs, const Record *rhs) {619 return lhs->getID() < rhs->getID();620 });621 for (const Record *def : sortedDefs)622 forwardDeclareInterface(Interface(def));623 for (const Record *def : sortedDefs)624 emitInterfaceDecl(Interface(def));625 for (const Record *def : sortedDefs)626 emitInterfaceTraitDecl(Interface(def));627 for (const Record *def : sortedDefs)628 emitModelMethodsDef(Interface(def));629 630 return false;631}632 633//===----------------------------------------------------------------------===//634// GEN: Interface documentation635//===----------------------------------------------------------------------===//636 637static void emitInterfaceDoc(const Record &interfaceDef, raw_ostream &os) {638 Interface interface(&interfaceDef);639 640 // Emit the interface name followed by the description.641 os << "\n## " << interface.getName() << " (`" << interfaceDef.getName()642 << "`)\n";643 if (auto description = interface.getDescription())644 mlir::tblgen::emitDescription(*description, os);645 646 // Emit the methods required by the interface.647 os << "\n### Methods:\n";648 for (const auto &method : interface.getMethods()) {649 // Emit the method name.650 os << "\n#### `" << method.getName() << "`\n\n```c++\n";651 652 // Emit the method signature.653 if (method.isStatic())654 os << "static ";655 emitCPPType(method.getReturnType(), os) << method.getName() << '(';656 llvm::interleaveComma(method.getArguments(), os,657 [&](const InterfaceMethod::Argument &arg) {658 emitCPPType(arg.type, os) << arg.name;659 });660 os << ");\n```\n";661 662 // Emit the description.663 if (auto description = method.getDescription())664 mlir::tblgen::emitDescription(*description, os);665 666 // If the body is not provided, this method must be provided by the user.667 if (!method.getBody())668 os << "\nNOTE: This method *must* be implemented by the user.";669 670 os << "\n";671 }672}673 674bool InterfaceGenerator::emitInterfaceDocs() {675 os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";676 os << "\n# " << interfaceBaseType << " definitions\n";677 678 for (const auto *def : defs)679 emitInterfaceDoc(*def, os);680 return false;681}682 683//===----------------------------------------------------------------------===//684// GEN: Interface registration hooks685//===----------------------------------------------------------------------===//686 687namespace {688template <typename GeneratorT>689struct InterfaceGenRegistration {690 InterfaceGenRegistration(StringRef genArg, StringRef genDesc)691 : genDeclArg(("gen-" + genArg + "-interface-decls").str()),692 genDefArg(("gen-" + genArg + "-interface-defs").str()),693 genDocArg(("gen-" + genArg + "-interface-docs").str()),694 genDeclDesc(("Generate " + genDesc + " interface declarations").str()),695 genDefDesc(("Generate " + genDesc + " interface definitions").str()),696 genDocDesc(("Generate " + genDesc + " interface documentation").str()),697 genDecls(genDeclArg, genDeclDesc,698 [](const RecordKeeper &records, raw_ostream &os) {699 return GeneratorT(records, os).emitInterfaceDecls();700 }),701 genDefs(genDefArg, genDefDesc,702 [](const RecordKeeper &records, raw_ostream &os) {703 return GeneratorT(records, os).emitInterfaceDefs();704 }),705 genDocs(genDocArg, genDocDesc,706 [](const RecordKeeper &records, raw_ostream &os) {707 return GeneratorT(records, os).emitInterfaceDocs();708 }) {}709 710 std::string genDeclArg, genDefArg, genDocArg;711 std::string genDeclDesc, genDefDesc, genDocDesc;712 mlir::GenRegistration genDecls, genDefs, genDocs;713};714} // namespace715 716static InterfaceGenRegistration<AttrInterfaceGenerator> attrGen("attr",717 "attribute");718static InterfaceGenRegistration<OpInterfaceGenerator> opGen("op", "op");719static InterfaceGenRegistration<TypeInterfaceGenerator> typeGen("type", "type");720