xref: /llvm-project/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp (revision 3e731af9124cc74d2276da19031e6dd275a7c33f)
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