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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // OpInterfacesGen generates definitions for operation interfaces. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "DocGenUtilities.h" 14 #include "mlir/TableGen/Format.h" 15 #include "mlir/TableGen/GenInfo.h" 16 #include "mlir/TableGen/Interfaces.h" 17 #include "llvm/ADT/SmallVector.h" 18 #include "llvm/ADT/StringExtras.h" 19 #include "llvm/Support/FormatVariadic.h" 20 #include "llvm/Support/raw_ostream.h" 21 #include "llvm/TableGen/Error.h" 22 #include "llvm/TableGen/Record.h" 23 #include "llvm/TableGen/TableGenBackend.h" 24 25 using namespace mlir; 26 using mlir::tblgen::Interface; 27 using mlir::tblgen::InterfaceMethod; 28 using mlir::tblgen::OpInterface; 29 30 /// Emit a string corresponding to a C++ type, followed by a space if necessary. 31 static raw_ostream &emitCPPType(StringRef type, raw_ostream &os) { 32 type = type.trim(); 33 os << type; 34 if (type.back() != '&' && type.back() != '*') 35 os << " "; 36 return os; 37 } 38 39 /// Emit the method name and argument list for the given method. If 'addThisArg' 40 /// is true, then an argument is added to the beginning of the argument list for 41 /// the concrete value. 42 static void emitMethodNameAndArgs(const InterfaceMethod &method, 43 raw_ostream &os, StringRef valueType, 44 bool addThisArg, bool addConst) { 45 os << method.getName() << '('; 46 if (addThisArg) { 47 if (addConst) 48 os << "const "; 49 os << "const Concept *impl, "; 50 emitCPPType(valueType, os) 51 << "tablegen_opaque_val" << (method.arg_empty() ? "" : ", "); 52 } 53 llvm::interleaveComma(method.getArguments(), os, 54 [&](const InterfaceMethod::Argument &arg) { 55 os << arg.type << " " << arg.name; 56 }); 57 os << ')'; 58 if (addConst) 59 os << " const"; 60 } 61 62 /// Get an array of all OpInterface definitions but exclude those subclassing 63 /// "DeclareOpInterfaceMethods". 64 static std::vector<llvm::Record *> 65 getAllInterfaceDefinitions(const llvm::RecordKeeper &recordKeeper, 66 StringRef name) { 67 std::vector<llvm::Record *> defs = 68 recordKeeper.getAllDerivedDefinitions((name + "Interface").str()); 69 70 std::string declareName = ("Declare" + name + "InterfaceMethods").str(); 71 llvm::erase_if(defs, [&](const llvm::Record *def) { 72 // Ignore any "declare methods" interfaces. 73 if (def->isSubClassOf(declareName)) 74 return true; 75 // Ignore interfaces defined outside of the top-level file. 76 return llvm::SrcMgr.FindBufferContainingLoc(def->getLoc()[0]) != 77 llvm::SrcMgr.getMainFileID(); 78 }); 79 return defs; 80 } 81 82 namespace { 83 /// This struct is the base generator used when processing tablegen interfaces. 84 class InterfaceGenerator { 85 public: 86 bool emitInterfaceDefs(); 87 bool emitInterfaceDecls(); 88 bool emitInterfaceDocs(); 89 90 protected: 91 InterfaceGenerator(std::vector<llvm::Record *> &&defs, raw_ostream &os) 92 : defs(std::move(defs)), os(os) {} 93 94 void emitConceptDecl(const Interface &interface); 95 void emitModelDecl(const Interface &interface); 96 void emitModelMethodsDef(const Interface &interface); 97 void emitTraitDecl(const Interface &interface, StringRef interfaceName, 98 StringRef interfaceTraitsName); 99 void emitInterfaceDecl(const Interface &interface); 100 101 /// The set of interface records to emit. 102 std::vector<llvm::Record *> defs; 103 // The stream to emit to. 104 raw_ostream &os; 105 /// The C++ value type of the interface, e.g. Operation*. 106 StringRef valueType; 107 /// The C++ base interface type. 108 StringRef interfaceBaseType; 109 /// The name of the typename for the value template. 110 StringRef valueTemplate; 111 /// The format context to use for methods. 112 tblgen::FmtContext nonStaticMethodFmt; 113 tblgen::FmtContext traitMethodFmt; 114 tblgen::FmtContext extraDeclsFmt; 115 }; 116 117 /// A specialized generator for attribute interfaces. 118 struct AttrInterfaceGenerator : public InterfaceGenerator { 119 AttrInterfaceGenerator(const llvm::RecordKeeper &records, raw_ostream &os) 120 : InterfaceGenerator(getAllInterfaceDefinitions(records, "Attr"), os) { 121 valueType = "::mlir::Attribute"; 122 interfaceBaseType = "AttributeInterface"; 123 valueTemplate = "ConcreteAttr"; 124 StringRef castCode = "(tablegen_opaque_val.cast<ConcreteAttr>())"; 125 nonStaticMethodFmt.addSubst("_attr", castCode).withSelf(castCode); 126 traitMethodFmt.addSubst("_attr", 127 "(*static_cast<const ConcreteAttr *>(this))"); 128 extraDeclsFmt.addSubst("_attr", "(*this)"); 129 } 130 }; 131 /// A specialized generator for operation interfaces. 132 struct OpInterfaceGenerator : public InterfaceGenerator { 133 OpInterfaceGenerator(const llvm::RecordKeeper &records, raw_ostream &os) 134 : InterfaceGenerator(getAllInterfaceDefinitions(records, "Op"), os) { 135 valueType = "::mlir::Operation *"; 136 interfaceBaseType = "OpInterface"; 137 valueTemplate = "ConcreteOp"; 138 StringRef castCode = "(llvm::cast<ConcreteOp>(tablegen_opaque_val))"; 139 nonStaticMethodFmt.addSubst("_this", "impl") 140 .withOp(castCode) 141 .withSelf(castCode); 142 traitMethodFmt.withOp("(*static_cast<ConcreteOp *>(this))"); 143 extraDeclsFmt.withOp("(*this)"); 144 } 145 }; 146 /// A specialized generator for type interfaces. 147 struct TypeInterfaceGenerator : public InterfaceGenerator { 148 TypeInterfaceGenerator(const llvm::RecordKeeper &records, raw_ostream &os) 149 : InterfaceGenerator(getAllInterfaceDefinitions(records, "Type"), os) { 150 valueType = "::mlir::Type"; 151 interfaceBaseType = "TypeInterface"; 152 valueTemplate = "ConcreteType"; 153 StringRef castCode = "(tablegen_opaque_val.cast<ConcreteType>())"; 154 nonStaticMethodFmt.addSubst("_type", castCode).withSelf(castCode); 155 traitMethodFmt.addSubst("_type", 156 "(*static_cast<const ConcreteType *>(this))"); 157 extraDeclsFmt.addSubst("_type", "(*this)"); 158 } 159 }; 160 } // namespace 161 162 //===----------------------------------------------------------------------===// 163 // GEN: Interface definitions 164 //===----------------------------------------------------------------------===// 165 166 static void emitInterfaceMethodDoc(const InterfaceMethod &method, 167 raw_ostream &os, StringRef prefix = "") { 168 if (std::optional<StringRef> description = method.getDescription()) 169 tblgen::emitDescriptionComment(*description, os, prefix); 170 } 171 172 static void emitInterfaceDef(const Interface &interface, StringRef valueType, 173 raw_ostream &os) { 174 StringRef interfaceName = interface.getName(); 175 StringRef cppNamespace = interface.getCppNamespace(); 176 cppNamespace.consume_front("::"); 177 178 // Insert the method definitions. 179 bool isOpInterface = isa<OpInterface>(interface); 180 for (auto &method : interface.getMethods()) { 181 emitInterfaceMethodDoc(method, os); 182 emitCPPType(method.getReturnType(), os); 183 if (!cppNamespace.empty()) 184 os << cppNamespace << "::"; 185 os << interfaceName << "::"; 186 emitMethodNameAndArgs(method, os, valueType, /*addThisArg=*/false, 187 /*addConst=*/!isOpInterface); 188 189 // Forward to the method on the concrete operation type. 190 os << " {\n return getImpl()->" << method.getName() << '('; 191 if (!method.isStatic()) { 192 os << "getImpl(), "; 193 os << (isOpInterface ? "getOperation()" : "*this"); 194 os << (method.arg_empty() ? "" : ", "); 195 } 196 llvm::interleaveComma( 197 method.getArguments(), os, 198 [&](const InterfaceMethod::Argument &arg) { os << arg.name; }); 199 os << ");\n }\n"; 200 } 201 } 202 203 bool InterfaceGenerator::emitInterfaceDefs() { 204 llvm::emitSourceFileHeader("Interface Definitions", os); 205 206 for (const auto *def : defs) 207 emitInterfaceDef(Interface(def), valueType, os); 208 return false; 209 } 210 211 //===----------------------------------------------------------------------===// 212 // GEN: Interface declarations 213 //===----------------------------------------------------------------------===// 214 215 void InterfaceGenerator::emitConceptDecl(const Interface &interface) { 216 os << " struct Concept {\n"; 217 218 // Insert each of the pure virtual concept methods. 219 for (auto &method : interface.getMethods()) { 220 os << " "; 221 emitCPPType(method.getReturnType(), os); 222 os << "(*" << method.getName() << ")("; 223 if (!method.isStatic()) { 224 os << "const Concept *impl, "; 225 emitCPPType(valueType, os) << (method.arg_empty() ? "" : ", "); 226 } 227 llvm::interleaveComma( 228 method.getArguments(), os, 229 [&](const InterfaceMethod::Argument &arg) { os << arg.type; }); 230 os << ");\n"; 231 } 232 os << " };\n"; 233 } 234 235 void InterfaceGenerator::emitModelDecl(const Interface &interface) { 236 // Emit the basic model and the fallback model. 237 for (const char *modelClass : {"Model", "FallbackModel"}) { 238 os << " template<typename " << valueTemplate << ">\n"; 239 os << " class " << modelClass << " : public Concept {\n public:\n"; 240 os << " using Interface = " << interface.getCppNamespace() 241 << (interface.getCppNamespace().empty() ? "" : "::") 242 << interface.getName() << ";\n"; 243 os << " " << modelClass << "() : Concept{"; 244 llvm::interleaveComma( 245 interface.getMethods(), os, 246 [&](const InterfaceMethod &method) { os << method.getName(); }); 247 os << "} {}\n\n"; 248 249 // Insert each of the virtual method overrides. 250 for (auto &method : interface.getMethods()) { 251 emitCPPType(method.getReturnType(), os << " static inline "); 252 emitMethodNameAndArgs(method, os, valueType, 253 /*addThisArg=*/!method.isStatic(), 254 /*addConst=*/false); 255 os << ";\n"; 256 } 257 os << " };\n"; 258 } 259 260 // Emit the template for the external model. 261 os << " template<typename ConcreteModel, typename " << valueTemplate 262 << ">\n"; 263 os << " class ExternalModel : public FallbackModel<ConcreteModel> {\n"; 264 os << " public:\n"; 265 os << " using ConcreteEntity = " << valueTemplate << ";\n"; 266 267 // Emit declarations for methods that have default implementations. Other 268 // methods are expected to be implemented by the concrete derived model. 269 for (auto &method : interface.getMethods()) { 270 if (!method.getDefaultImplementation()) 271 continue; 272 os << " "; 273 if (method.isStatic()) 274 os << "static "; 275 emitCPPType(method.getReturnType(), os); 276 os << method.getName() << "("; 277 if (!method.isStatic()) { 278 emitCPPType(valueType, os); 279 os << "tablegen_opaque_val"; 280 if (!method.arg_empty()) 281 os << ", "; 282 } 283 llvm::interleaveComma(method.getArguments(), os, 284 [&](const InterfaceMethod::Argument &arg) { 285 emitCPPType(arg.type, os); 286 os << arg.name; 287 }); 288 os << ")"; 289 if (!method.isStatic()) 290 os << " const"; 291 os << ";\n"; 292 } 293 os << " };\n"; 294 } 295 296 void InterfaceGenerator::emitModelMethodsDef(const Interface &interface) { 297 llvm::SmallVector<StringRef, 2> namespaces; 298 llvm::SplitString(interface.getCppNamespace(), namespaces, "::"); 299 for (StringRef ns : namespaces) 300 os << "namespace " << ns << " {\n"; 301 302 for (auto &method : interface.getMethods()) { 303 os << "template<typename " << valueTemplate << ">\n"; 304 emitCPPType(method.getReturnType(), os); 305 os << "detail::" << interface.getName() << "InterfaceTraits::Model<" 306 << valueTemplate << ">::"; 307 emitMethodNameAndArgs(method, os, valueType, 308 /*addThisArg=*/!method.isStatic(), 309 /*addConst=*/false); 310 os << " {\n "; 311 312 // Check for a provided body to the function. 313 if (std::optional<StringRef> body = method.getBody()) { 314 if (method.isStatic()) 315 os << body->trim(); 316 else 317 os << tblgen::tgfmt(body->trim(), &nonStaticMethodFmt); 318 os << "\n}\n"; 319 continue; 320 } 321 322 // Forward to the method on the concrete operation type. 323 if (method.isStatic()) 324 os << "return " << valueTemplate << "::"; 325 else 326 os << tblgen::tgfmt("return $_self.", &nonStaticMethodFmt); 327 328 // Add the arguments to the call. 329 os << method.getName() << '('; 330 llvm::interleaveComma( 331 method.getArguments(), os, 332 [&](const InterfaceMethod::Argument &arg) { os << arg.name; }); 333 os << ");\n}\n"; 334 } 335 336 for (auto &method : interface.getMethods()) { 337 os << "template<typename " << valueTemplate << ">\n"; 338 emitCPPType(method.getReturnType(), os); 339 os << "detail::" << interface.getName() << "InterfaceTraits::FallbackModel<" 340 << valueTemplate << ">::"; 341 emitMethodNameAndArgs(method, os, valueType, 342 /*addThisArg=*/!method.isStatic(), 343 /*addConst=*/false); 344 os << " {\n "; 345 346 // Forward to the method on the concrete Model implementation. 347 if (method.isStatic()) 348 os << "return " << valueTemplate << "::"; 349 else 350 os << "return static_cast<const " << valueTemplate << " *>(impl)->"; 351 352 // Add the arguments to the call. 353 os << method.getName() << '('; 354 if (!method.isStatic()) 355 os << "tablegen_opaque_val" << (method.arg_empty() ? "" : ", "); 356 llvm::interleaveComma( 357 method.getArguments(), os, 358 [&](const InterfaceMethod::Argument &arg) { os << arg.name; }); 359 os << ");\n}\n"; 360 } 361 362 // Emit default implementations for the external model. 363 for (auto &method : interface.getMethods()) { 364 if (!method.getDefaultImplementation()) 365 continue; 366 os << "template<typename ConcreteModel, typename " << valueTemplate 367 << ">\n"; 368 emitCPPType(method.getReturnType(), os); 369 os << "detail::" << interface.getName() 370 << "InterfaceTraits::ExternalModel<ConcreteModel, " << valueTemplate 371 << ">::"; 372 373 os << method.getName() << "("; 374 if (!method.isStatic()) { 375 emitCPPType(valueType, os); 376 os << "tablegen_opaque_val"; 377 if (!method.arg_empty()) 378 os << ", "; 379 } 380 llvm::interleaveComma(method.getArguments(), os, 381 [&](const InterfaceMethod::Argument &arg) { 382 emitCPPType(arg.type, os); 383 os << arg.name; 384 }); 385 os << ")"; 386 if (!method.isStatic()) 387 os << " const"; 388 389 os << " {\n"; 390 391 // Use the empty context for static methods. 392 tblgen::FmtContext ctx; 393 os << tblgen::tgfmt(method.getDefaultImplementation()->trim(), 394 method.isStatic() ? &ctx : &nonStaticMethodFmt); 395 os << "\n}\n"; 396 } 397 398 for (StringRef ns : llvm::reverse(namespaces)) 399 os << "} // namespace " << ns << "\n"; 400 } 401 402 void InterfaceGenerator::emitTraitDecl(const Interface &interface, 403 StringRef interfaceName, 404 StringRef interfaceTraitsName) { 405 os << llvm::formatv(" template <typename {3}>\n" 406 " struct {0}Trait : public ::mlir::{2}<{0}," 407 " detail::{1}>::Trait<{3}> {{\n", 408 interfaceName, interfaceTraitsName, interfaceBaseType, 409 valueTemplate); 410 411 // Insert the default implementation for any methods. 412 bool isOpInterface = isa<OpInterface>(interface); 413 for (auto &method : interface.getMethods()) { 414 // Flag interface methods named verifyTrait. 415 if (method.getName() == "verifyTrait") 416 PrintFatalError( 417 formatv("'verifyTrait' method cannot be specified as interface " 418 "method for '{0}'; use the 'verify' field instead", 419 interfaceName)); 420 auto defaultImpl = method.getDefaultImplementation(); 421 if (!defaultImpl) 422 continue; 423 424 emitInterfaceMethodDoc(method, os, " "); 425 os << " " << (method.isStatic() ? "static " : ""); 426 emitCPPType(method.getReturnType(), os); 427 emitMethodNameAndArgs(method, os, valueType, /*addThisArg=*/false, 428 /*addConst=*/!isOpInterface && !method.isStatic()); 429 os << " {\n " << tblgen::tgfmt(defaultImpl->trim(), &traitMethodFmt) 430 << "\n }\n"; 431 } 432 433 if (auto verify = interface.getVerify()) { 434 assert(isa<OpInterface>(interface) && "only OpInterface supports 'verify'"); 435 436 tblgen::FmtContext verifyCtx; 437 verifyCtx.withOp("op"); 438 os << llvm::formatv( 439 " static ::mlir::LogicalResult {0}(::mlir::Operation *op) ", 440 (interface.verifyWithRegions() ? "verifyRegionTrait" 441 : "verifyTrait")) 442 << "{\n " << tblgen::tgfmt(verify->trim(), &verifyCtx) 443 << "\n }\n"; 444 } 445 if (auto extraTraitDecls = interface.getExtraTraitClassDeclaration()) 446 os << tblgen::tgfmt(*extraTraitDecls, &traitMethodFmt) << "\n"; 447 if (auto extraTraitDecls = interface.getExtraSharedClassDeclaration()) 448 os << tblgen::tgfmt(*extraTraitDecls, &traitMethodFmt) << "\n"; 449 450 os << " };\n"; 451 } 452 453 void InterfaceGenerator::emitInterfaceDecl(const Interface &interface) { 454 llvm::SmallVector<StringRef, 2> namespaces; 455 llvm::SplitString(interface.getCppNamespace(), namespaces, "::"); 456 for (StringRef ns : namespaces) 457 os << "namespace " << ns << " {\n"; 458 459 StringRef interfaceName = interface.getName(); 460 auto interfaceTraitsName = (interfaceName + "InterfaceTraits").str(); 461 462 // Emit a forward declaration of the interface class so that it becomes usable 463 // in the signature of its methods. 464 os << "class " << interfaceName << ";\n"; 465 466 // Emit the traits struct containing the concept and model declarations. 467 os << "namespace detail {\n" 468 << "struct " << interfaceTraitsName << " {\n"; 469 emitConceptDecl(interface); 470 emitModelDecl(interface); 471 os << "};"; 472 473 // Emit the derived trait for the interface. 474 os << "template <typename " << valueTemplate << ">\n"; 475 os << "struct " << interface.getName() << "Trait;\n"; 476 477 os << "\n} // namespace detail\n"; 478 479 // Emit the main interface class declaration. 480 os << llvm::formatv("class {0} : public ::mlir::{3}<{1}, detail::{2}> {\n" 481 "public:\n" 482 " using ::mlir::{3}<{1}, detail::{2}>::{3};\n", 483 interfaceName, interfaceName, interfaceTraitsName, 484 interfaceBaseType); 485 486 // Emit a utility wrapper trait class. 487 os << llvm::formatv(" template <typename {1}>\n" 488 " struct Trait : public detail::{0}Trait<{1}> {{};\n", 489 interfaceName, valueTemplate); 490 491 // Insert the method declarations. 492 bool isOpInterface = isa<OpInterface>(interface); 493 for (auto &method : interface.getMethods()) { 494 emitInterfaceMethodDoc(method, os, " "); 495 emitCPPType(method.getReturnType(), os << " "); 496 emitMethodNameAndArgs(method, os, valueType, /*addThisArg=*/false, 497 /*addConst=*/!isOpInterface); 498 os << ";\n"; 499 } 500 501 // Emit any extra declarations. 502 if (std::optional<StringRef> extraDecls = 503 interface.getExtraClassDeclaration()) 504 os << *extraDecls << "\n"; 505 if (std::optional<StringRef> extraDecls = 506 interface.getExtraSharedClassDeclaration()) 507 os << tblgen::tgfmt(*extraDecls, &extraDeclsFmt); 508 509 os << "};\n"; 510 511 os << "namespace detail {\n"; 512 emitTraitDecl(interface, interfaceName, interfaceTraitsName); 513 os << "}// namespace detail\n"; 514 515 for (StringRef ns : llvm::reverse(namespaces)) 516 os << "} // namespace " << ns << "\n"; 517 } 518 519 bool InterfaceGenerator::emitInterfaceDecls() { 520 llvm::emitSourceFileHeader("Interface Declarations", os); 521 522 for (const llvm::Record *def : defs) 523 emitInterfaceDecl(Interface(def)); 524 for (const llvm::Record *def : defs) 525 emitModelMethodsDef(Interface(def)); 526 return false; 527 } 528 529 //===----------------------------------------------------------------------===// 530 // GEN: Interface documentation 531 //===----------------------------------------------------------------------===// 532 533 static void emitInterfaceDoc(const llvm::Record &interfaceDef, 534 raw_ostream &os) { 535 Interface interface(&interfaceDef); 536 537 // Emit the interface name followed by the description. 538 os << "## " << interface.getName() << " (`" << interfaceDef.getName() 539 << "`)\n\n"; 540 if (auto description = interface.getDescription()) 541 mlir::tblgen::emitDescription(*description, os); 542 543 // Emit the methods required by the interface. 544 os << "\n### Methods:\n"; 545 for (const auto &method : interface.getMethods()) { 546 // Emit the method name. 547 os << "#### `" << method.getName() << "`\n\n```c++\n"; 548 549 // Emit the method signature. 550 if (method.isStatic()) 551 os << "static "; 552 emitCPPType(method.getReturnType(), os) << method.getName() << '('; 553 llvm::interleaveComma(method.getArguments(), os, 554 [&](const InterfaceMethod::Argument &arg) { 555 emitCPPType(arg.type, os) << arg.name; 556 }); 557 os << ");\n```\n"; 558 559 // Emit the description. 560 if (auto description = method.getDescription()) 561 mlir::tblgen::emitDescription(*description, os); 562 563 // If the body is not provided, this method must be provided by the user. 564 if (!method.getBody()) 565 os << "\nNOTE: This method *must* be implemented by the user."; 566 567 os << "\n\n"; 568 } 569 } 570 571 bool InterfaceGenerator::emitInterfaceDocs() { 572 os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n"; 573 os << "# " << interfaceBaseType << " definitions\n"; 574 575 for (const auto *def : defs) 576 emitInterfaceDoc(*def, os); 577 return false; 578 } 579 580 //===----------------------------------------------------------------------===// 581 // GEN: Interface registration hooks 582 //===----------------------------------------------------------------------===// 583 584 namespace { 585 template <typename GeneratorT> 586 struct InterfaceGenRegistration { 587 InterfaceGenRegistration(StringRef genArg, StringRef genDesc) 588 : genDeclArg(("gen-" + genArg + "-interface-decls").str()), 589 genDefArg(("gen-" + genArg + "-interface-defs").str()), 590 genDocArg(("gen-" + genArg + "-interface-docs").str()), 591 genDeclDesc(("Generate " + genDesc + " interface declarations").str()), 592 genDefDesc(("Generate " + genDesc + " interface definitions").str()), 593 genDocDesc(("Generate " + genDesc + " interface documentation").str()), 594 genDecls(genDeclArg, genDeclDesc, 595 [](const llvm::RecordKeeper &records, raw_ostream &os) { 596 return GeneratorT(records, os).emitInterfaceDecls(); 597 }), 598 genDefs(genDefArg, genDefDesc, 599 [](const llvm::RecordKeeper &records, raw_ostream &os) { 600 return GeneratorT(records, os).emitInterfaceDefs(); 601 }), 602 genDocs(genDocArg, genDocDesc, 603 [](const llvm::RecordKeeper &records, raw_ostream &os) { 604 return GeneratorT(records, os).emitInterfaceDocs(); 605 }) {} 606 607 std::string genDeclArg, genDefArg, genDocArg; 608 std::string genDeclDesc, genDefDesc, genDocDesc; 609 mlir::GenRegistration genDecls, genDefs, genDocs; 610 }; 611 } // namespace 612 613 static InterfaceGenRegistration<AttrInterfaceGenerator> attrGen("attr", 614 "attribute"); 615 static InterfaceGenRegistration<OpInterfaceGenerator> opGen("op", "op"); 616 static InterfaceGenRegistration<TypeInterfaceGenerator> typeGen("type", "type"); 617