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