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