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 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:
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 {
112   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 {
127   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 {
142   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 
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 
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 
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 
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 
254   // Emit declarations for methods that have default implementations. Other
255   // methods are expected to be implemented by the concrete derived model.
256   for (auto &method : interface.getMethods()) {
257     if (!method.getDefaultImplementation())
258       continue;
259     os << "    ";
260     if (method.isStatic())
261       os << "static ";
262     emitCPPType(method.getReturnType(), os);
263     os << method.getName() << "(";
264     if (!method.isStatic()) {
265       emitCPPType(valueType, os);
266       os << "tablegen_opaque_val";
267       if (!method.arg_empty())
268         os << ", ";
269     }
270     llvm::interleaveComma(method.getArguments(), os,
271                           [&](const InterfaceMethod::Argument &arg) {
272                             emitCPPType(arg.type, os);
273                             os << arg.name;
274                           });
275     os << ")";
276     if (!method.isStatic())
277       os << " const";
278     os << ";\n";
279   }
280   os << "  };\n";
281 }
282 
283 void InterfaceGenerator::emitModelMethodsDef(const Interface &interface) {
284   for (auto &method : interface.getMethods()) {
285     os << "template<typename " << valueTemplate << ">\n";
286     emitCPPType(method.getReturnType(), os);
287     os << "detail::" << interface.getName() << "InterfaceTraits::Model<"
288        << valueTemplate << ">::";
289     emitMethodNameAndArgs(method, os, valueType,
290                           /*addThisArg=*/!method.isStatic(),
291                           /*addConst=*/false);
292     os << " {\n  ";
293 
294     // Check for a provided body to the function.
295     if (Optional<StringRef> body = method.getBody()) {
296       if (method.isStatic())
297         os << body->trim();
298       else
299         os << tblgen::tgfmt(body->trim(), &nonStaticMethodFmt);
300       os << "\n}\n";
301       continue;
302     }
303 
304     // Forward to the method on the concrete operation type.
305     if (method.isStatic())
306       os << "return " << valueTemplate << "::";
307     else
308       os << tblgen::tgfmt("return $_self.", &nonStaticMethodFmt);
309 
310     // Add the arguments to the call.
311     os << method.getName() << '(';
312     llvm::interleaveComma(
313         method.getArguments(), os,
314         [&](const InterfaceMethod::Argument &arg) { os << arg.name; });
315     os << ");\n}\n";
316   }
317 
318   for (auto &method : interface.getMethods()) {
319     os << "template<typename " << valueTemplate << ">\n";
320     emitCPPType(method.getReturnType(), os);
321     os << "detail::" << interface.getName() << "InterfaceTraits::FallbackModel<"
322        << valueTemplate << ">::";
323     emitMethodNameAndArgs(method, os, valueType,
324                           /*addThisArg=*/!method.isStatic(),
325                           /*addConst=*/false);
326     os << " {\n  ";
327 
328     // Forward to the method on the concrete Model implementation.
329     if (method.isStatic())
330       os << "return " << valueTemplate << "::";
331     else
332       os << "return static_cast<const " << valueTemplate << " *>(impl)->";
333 
334     // Add the arguments to the call.
335     os << method.getName() << '(';
336     if (!method.isStatic())
337       os << "tablegen_opaque_val" << (method.arg_empty() ? "" : ", ");
338     llvm::interleaveComma(
339         method.getArguments(), os,
340         [&](const InterfaceMethod::Argument &arg) { os << arg.name; });
341     os << ");\n}\n";
342   }
343 
344   // Emit default implementations for the external model.
345   for (auto &method : interface.getMethods()) {
346     if (!method.getDefaultImplementation())
347       continue;
348     os << "template<typename ConcreteModel, typename " << valueTemplate
349        << ">\n";
350     emitCPPType(method.getReturnType(), os);
351     os << "detail::" << interface.getName()
352        << "InterfaceTraits::ExternalModel<ConcreteModel, " << valueTemplate
353        << ">::";
354 
355     os << method.getName() << "(";
356     if (!method.isStatic()) {
357       emitCPPType(valueType, os);
358       os << "tablegen_opaque_val";
359       if (!method.arg_empty())
360         os << ", ";
361     }
362     llvm::interleaveComma(method.getArguments(), os,
363                           [&](const InterfaceMethod::Argument &arg) {
364                             emitCPPType(arg.type, os);
365                             os << arg.name;
366                           });
367     os << ")";
368     if (!method.isStatic())
369       os << " const";
370 
371     os << " {\n";
372 
373     // Use the empty context for static methods.
374     tblgen::FmtContext ctx;
375     os << tblgen::tgfmt(method.getDefaultImplementation()->trim(),
376                         method.isStatic() ? &ctx : &nonStaticMethodFmt);
377     os << "\n}\n";
378   }
379 }
380 
381 void InterfaceGenerator::emitTraitDecl(const Interface &interface,
382                                        StringRef interfaceName,
383                                        StringRef interfaceTraitsName) {
384   os << llvm::formatv("  template <typename {3}>\n"
385                       "  struct {0}Trait : public ::mlir::{2}<{0},"
386                       " detail::{1}>::Trait<{3}> {{\n",
387                       interfaceName, interfaceTraitsName, interfaceBaseType,
388                       valueTemplate);
389 
390   // Insert the default implementation for any methods.
391   bool isOpInterface = isa<OpInterface>(interface);
392   for (auto &method : interface.getMethods()) {
393     // Flag interface methods named verifyTrait.
394     if (method.getName() == "verifyTrait")
395       PrintFatalError(
396           formatv("'verifyTrait' method cannot be specified as interface "
397                   "method for '{0}'; use the 'verify' field instead",
398                   interfaceName));
399     auto defaultImpl = method.getDefaultImplementation();
400     if (!defaultImpl)
401       continue;
402 
403     os << "    " << (method.isStatic() ? "static " : "");
404     emitCPPType(method.getReturnType(), os);
405     emitMethodNameAndArgs(method, os, valueType, /*addThisArg=*/false,
406                           /*addConst=*/!isOpInterface && !method.isStatic());
407     os << " {\n      " << tblgen::tgfmt(defaultImpl->trim(), &traitMethodFmt)
408        << "\n    }\n";
409   }
410 
411   if (auto verify = interface.getVerify()) {
412     assert(isa<OpInterface>(interface) && "only OpInterface supports 'verify'");
413 
414     tblgen::FmtContext verifyCtx;
415     verifyCtx.withOp("op");
416     os << llvm::formatv(
417               "    static ::mlir::LogicalResult {0}(::mlir::Operation *op) ",
418               (interface.verifyWithRegions() ? "verifyRegionTrait"
419                                              : "verifyTrait"))
420        << "{\n      " << tblgen::tgfmt(verify->trim(), &verifyCtx)
421        << "\n    }\n";
422   }
423   if (auto extraTraitDecls = interface.getExtraTraitClassDeclaration())
424     os << tblgen::tgfmt(*extraTraitDecls, &traitMethodFmt) << "\n";
425   if (auto extraTraitDecls = interface.getExtraSharedClassDeclaration())
426     os << tblgen::tgfmt(*extraTraitDecls, &traitMethodFmt) << "\n";
427 
428   os << "  };\n";
429 }
430 
431 void InterfaceGenerator::emitInterfaceDecl(const Interface &interface) {
432   llvm::SmallVector<StringRef, 2> namespaces;
433   llvm::SplitString(interface.getCppNamespace(), namespaces, "::");
434   for (StringRef ns : namespaces)
435     os << "namespace " << ns << " {\n";
436 
437   StringRef interfaceName = interface.getName();
438   auto interfaceTraitsName = (interfaceName + "InterfaceTraits").str();
439 
440   // Emit a forward declaration of the interface class so that it becomes usable
441   // in the signature of its methods.
442   os << "class " << interfaceName << ";\n";
443 
444   // Emit the traits struct containing the concept and model declarations.
445   os << "namespace detail {\n"
446      << "struct " << interfaceTraitsName << " {\n";
447   emitConceptDecl(interface);
448   emitModelDecl(interface);
449   os << "};";
450 
451   // Emit the derived trait for the interface.
452   os << "template <typename " << valueTemplate << ">\n";
453   os << "struct " << interface.getName() << "Trait;\n";
454 
455   os << "\n} // namespace detail\n";
456 
457   // Emit the main interface class declaration.
458   os << llvm::formatv("class {0} : public ::mlir::{3}<{1}, detail::{2}> {\n"
459                       "public:\n"
460                       "  using ::mlir::{3}<{1}, detail::{2}>::{3};\n",
461                       interfaceName, interfaceName, interfaceTraitsName,
462                       interfaceBaseType);
463 
464   // Emit a utility wrapper trait class.
465   os << llvm::formatv("  template <typename {1}>\n"
466                       "  struct Trait : public detail::{0}Trait<{1}> {{};\n",
467                       interfaceName, valueTemplate);
468 
469   // Insert the method declarations.
470   bool isOpInterface = isa<OpInterface>(interface);
471   for (auto &method : interface.getMethods()) {
472     emitCPPType(method.getReturnType(), os << "  ");
473     emitMethodNameAndArgs(method, os, valueType, /*addThisArg=*/false,
474                           /*addConst=*/!isOpInterface);
475     os << ";\n";
476   }
477 
478   // Emit any extra declarations.
479   if (Optional<StringRef> extraDecls = interface.getExtraClassDeclaration())
480     os << *extraDecls << "\n";
481   if (Optional<StringRef> extraDecls =
482           interface.getExtraSharedClassDeclaration())
483     os << tblgen::tgfmt(*extraDecls, &extraDeclsFmt);
484 
485   os << "};\n";
486 
487   os << "namespace detail {\n";
488   emitTraitDecl(interface, interfaceName, interfaceTraitsName);
489   os << "}// namespace detail\n";
490 
491   emitModelMethodsDef(interface);
492 
493   for (StringRef ns : llvm::reverse(namespaces))
494     os << "} // namespace " << ns << "\n";
495 }
496 
497 bool InterfaceGenerator::emitInterfaceDecls() {
498   llvm::emitSourceFileHeader("Interface Declarations", os);
499 
500   for (const auto *def : defs)
501     emitInterfaceDecl(Interface(def));
502   return false;
503 }
504 
505 //===----------------------------------------------------------------------===//
506 // GEN: Interface documentation
507 //===----------------------------------------------------------------------===//
508 
509 static void emitInterfaceDoc(const llvm::Record &interfaceDef,
510                              raw_ostream &os) {
511   Interface interface(&interfaceDef);
512 
513   // Emit the interface name followed by the description.
514   os << "## " << interface.getName() << " (`" << interfaceDef.getName()
515      << "`)\n\n";
516   if (auto description = interface.getDescription())
517     mlir::tblgen::emitDescription(*description, os);
518 
519   // Emit the methods required by the interface.
520   os << "\n### Methods:\n";
521   for (const auto &method : interface.getMethods()) {
522     // Emit the method name.
523     os << "#### `" << method.getName() << "`\n\n```c++\n";
524 
525     // Emit the method signature.
526     if (method.isStatic())
527       os << "static ";
528     emitCPPType(method.getReturnType(), os) << method.getName() << '(';
529     llvm::interleaveComma(method.getArguments(), os,
530                           [&](const InterfaceMethod::Argument &arg) {
531                             emitCPPType(arg.type, os) << arg.name;
532                           });
533     os << ");\n```\n";
534 
535     // Emit the description.
536     if (auto description = method.getDescription())
537       mlir::tblgen::emitDescription(*description, os);
538 
539     // If the body is not provided, this method must be provided by the user.
540     if (!method.getBody())
541       os << "\nNOTE: This method *must* be implemented by the user.\n\n";
542   }
543 }
544 
545 bool InterfaceGenerator::emitInterfaceDocs() {
546   os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
547   os << "# " << interfaceBaseType << " definitions\n";
548 
549   for (const auto *def : defs)
550     emitInterfaceDoc(*def, os);
551   return false;
552 }
553 
554 //===----------------------------------------------------------------------===//
555 // GEN: Interface registration hooks
556 //===----------------------------------------------------------------------===//
557 
558 namespace {
559 template <typename GeneratorT>
560 struct InterfaceGenRegistration {
561   InterfaceGenRegistration(StringRef genArg, StringRef genDesc)
562       : genDeclArg(("gen-" + genArg + "-interface-decls").str()),
563         genDefArg(("gen-" + genArg + "-interface-defs").str()),
564         genDocArg(("gen-" + genArg + "-interface-docs").str()),
565         genDeclDesc(("Generate " + genDesc + " interface declarations").str()),
566         genDefDesc(("Generate " + genDesc + " interface definitions").str()),
567         genDocDesc(("Generate " + genDesc + " interface documentation").str()),
568         genDecls(genDeclArg, genDeclDesc,
569                  [](const llvm::RecordKeeper &records, raw_ostream &os) {
570                    return GeneratorT(records, os).emitInterfaceDecls();
571                  }),
572         genDefs(genDefArg, genDefDesc,
573                 [](const llvm::RecordKeeper &records, raw_ostream &os) {
574                   return GeneratorT(records, os).emitInterfaceDefs();
575                 }),
576         genDocs(genDocArg, genDocDesc,
577                 [](const llvm::RecordKeeper &records, raw_ostream &os) {
578                   return GeneratorT(records, os).emitInterfaceDocs();
579                 }) {}
580 
581   std::string genDeclArg, genDefArg, genDocArg;
582   std::string genDeclDesc, genDefDesc, genDocDesc;
583   mlir::GenRegistration genDecls, genDefs, genDocs;
584 };
585 } // namespace
586 
587 static InterfaceGenRegistration<AttrInterfaceGenerator> attrGen("attr",
588                                                                 "attribute");
589 static InterfaceGenRegistration<OpInterfaceGenerator> opGen("op", "op");
590 static InterfaceGenRegistration<TypeInterfaceGenerator> typeGen("type", "type");
591