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(Interface &interface);
88   void emitModelDecl(Interface &interface);
89   void emitModelMethodsDef(Interface &interface);
90   void emitTraitDecl(Interface &interface, StringRef interfaceName,
91                      StringRef interfaceTraitsName);
92   void emitInterfaceDecl(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 };
108 
109 /// A specialized generator for attribute interfaces.
110 struct AttrInterfaceGenerator : public InterfaceGenerator {
111   AttrInterfaceGenerator(const llvm::RecordKeeper &records, raw_ostream &os)
112       : InterfaceGenerator(records.getAllDerivedDefinitions("AttrInterface"),
113                            os) {
114     valueType = "::mlir::Attribute";
115     interfaceBaseType = "AttributeInterface";
116     valueTemplate = "ConcreteAttr";
117     StringRef castCode = "(tablegen_opaque_val.cast<ConcreteAttr>())";
118     nonStaticMethodFmt.addSubst("_attr", castCode).withSelf(castCode);
119     traitMethodFmt.addSubst("_attr",
120                             "(*static_cast<const ConcreteAttr *>(this))");
121   }
122 };
123 /// A specialized generator for operation interfaces.
124 struct OpInterfaceGenerator : public InterfaceGenerator {
125   OpInterfaceGenerator(const llvm::RecordKeeper &records, raw_ostream &os)
126       : InterfaceGenerator(getAllOpInterfaceDefinitions(records), os) {
127     valueType = "::mlir::Operation *";
128     interfaceBaseType = "OpInterface";
129     valueTemplate = "ConcreteOp";
130     StringRef castCode = "(llvm::cast<ConcreteOp>(tablegen_opaque_val))";
131     nonStaticMethodFmt.addSubst("_this", "impl")
132         .withOp(castCode)
133         .withSelf(castCode);
134     traitMethodFmt.withOp("(*static_cast<ConcreteOp *>(this))");
135   }
136 };
137 /// A specialized generator for type interfaces.
138 struct TypeInterfaceGenerator : public InterfaceGenerator {
139   TypeInterfaceGenerator(const llvm::RecordKeeper &records, raw_ostream &os)
140       : InterfaceGenerator(records.getAllDerivedDefinitions("TypeInterface"),
141                            os) {
142     valueType = "::mlir::Type";
143     interfaceBaseType = "TypeInterface";
144     valueTemplate = "ConcreteType";
145     StringRef castCode = "(tablegen_opaque_val.cast<ConcreteType>())";
146     nonStaticMethodFmt.addSubst("_type", castCode).withSelf(castCode);
147     traitMethodFmt.addSubst("_type",
148                             "(*static_cast<const ConcreteType *>(this))");
149   }
150 };
151 } // end anonymous namespace
152 
153 //===----------------------------------------------------------------------===//
154 // GEN: Interface definitions
155 //===----------------------------------------------------------------------===//
156 
157 static void emitInterfaceDef(Interface interface, StringRef valueType,
158                              raw_ostream &os) {
159   StringRef interfaceName = interface.getName();
160   StringRef cppNamespace = interface.getCppNamespace();
161   cppNamespace.consume_front("::");
162 
163   // Insert the method definitions.
164   bool isOpInterface = isa<OpInterface>(interface);
165   for (auto &method : interface.getMethods()) {
166     emitCPPType(method.getReturnType(), os);
167     if (!cppNamespace.empty())
168       os << cppNamespace << "::";
169     os << interfaceName << "::";
170     emitMethodNameAndArgs(method, os, valueType, /*addThisArg=*/false,
171                           /*addConst=*/!isOpInterface);
172 
173     // Forward to the method on the concrete operation type.
174     os << " {\n      return getImpl()->" << method.getName() << '(';
175     if (!method.isStatic()) {
176       os << "getImpl(), ";
177       os << (isOpInterface ? "getOperation()" : "*this");
178       os << (method.arg_empty() ? "" : ", ");
179     }
180     llvm::interleaveComma(
181         method.getArguments(), os,
182         [&](const InterfaceMethod::Argument &arg) { os << arg.name; });
183     os << ");\n  }\n";
184   }
185 }
186 
187 bool InterfaceGenerator::emitInterfaceDefs() {
188   llvm::emitSourceFileHeader("Interface Definitions", os);
189 
190   for (const auto *def : defs)
191     emitInterfaceDef(Interface(def), valueType, os);
192   return false;
193 }
194 
195 //===----------------------------------------------------------------------===//
196 // GEN: Interface declarations
197 //===----------------------------------------------------------------------===//
198 
199 void InterfaceGenerator::emitConceptDecl(Interface &interface) {
200   os << "  struct Concept {\n";
201 
202   // Insert each of the pure virtual concept methods.
203   for (auto &method : interface.getMethods()) {
204     os << "    ";
205     emitCPPType(method.getReturnType(), os);
206     os << "(*" << method.getName() << ")(";
207     if (!method.isStatic()) {
208       os << "const Concept *impl, ";
209       emitCPPType(valueType, os) << (method.arg_empty() ? "" : ", ");
210     }
211     llvm::interleaveComma(
212         method.getArguments(), os,
213         [&](const InterfaceMethod::Argument &arg) { os << arg.type; });
214     os << ");\n";
215   }
216   os << "  };\n";
217 }
218 
219 void InterfaceGenerator::emitModelDecl(Interface &interface) {
220   for (const char *modelClass : {"Model", "FallbackModel"}) {
221     os << "  template<typename " << valueTemplate << ">\n";
222     os << "  class " << modelClass << " : public Concept {\n  public:\n";
223     os << "    " << modelClass << "() : Concept{";
224     llvm::interleaveComma(
225         interface.getMethods(), os,
226         [&](const InterfaceMethod &method) { os << method.getName(); });
227     os << "} {}\n\n";
228 
229     // Insert each of the virtual method overrides.
230     for (auto &method : interface.getMethods()) {
231       emitCPPType(method.getReturnType(), os << "    static inline ");
232       emitMethodNameAndArgs(method, os, valueType,
233                             /*addThisArg=*/!method.isStatic(),
234                             /*addConst=*/false);
235       os << ";\n";
236     }
237     os << "  };\n";
238   }
239 }
240 
241 void InterfaceGenerator::emitModelMethodsDef(Interface &interface) {
242   for (auto &method : interface.getMethods()) {
243     os << "template<typename " << valueTemplate << ">\n";
244     emitCPPType(method.getReturnType(), os);
245     os << "detail::" << interface.getName() << "InterfaceTraits::Model<"
246        << valueTemplate << ">::";
247     emitMethodNameAndArgs(method, os, valueType,
248                           /*addThisArg=*/!method.isStatic(),
249                           /*addConst=*/false);
250     os << " {\n  ";
251 
252     // Check for a provided body to the function.
253     if (Optional<StringRef> body = method.getBody()) {
254       if (method.isStatic())
255         os << body->trim();
256       else
257         os << tblgen::tgfmt(body->trim(), &nonStaticMethodFmt);
258       os << "\n}\n";
259       continue;
260     }
261 
262     // Forward to the method on the concrete operation type.
263     if (method.isStatic())
264       os << "return " << valueTemplate << "::";
265     else
266       os << tblgen::tgfmt("return $_self.", &nonStaticMethodFmt);
267 
268     // Add the arguments to the call.
269     os << method.getName() << '(';
270     llvm::interleaveComma(
271         method.getArguments(), os,
272         [&](const InterfaceMethod::Argument &arg) { os << arg.name; });
273     os << ");\n}\n";
274   }
275 
276   for (auto &method : interface.getMethods()) {
277     os << "template<typename " << valueTemplate << ">\n";
278     emitCPPType(method.getReturnType(), os);
279     os << "detail::" << interface.getName() << "InterfaceTraits::FallbackModel<"
280        << valueTemplate << ">::";
281     emitMethodNameAndArgs(method, os, valueType,
282                           /*addThisArg=*/!method.isStatic(),
283                           /*addConst=*/false);
284     os << " {\n  ";
285 
286     // Forward to the method on the concrete Model implementation.
287     if (method.isStatic())
288       os << "return " << valueTemplate << "::";
289     else
290       os << "return static_cast<const " << valueTemplate << " *>(impl)->";
291 
292     // Add the arguments to the call.
293     os << method.getName() << '(';
294     if (!method.isStatic())
295       os << "tablegen_opaque_val" << (method.arg_empty() ? "" : ", ");
296     llvm::interleaveComma(
297         method.getArguments(), os,
298         [&](const InterfaceMethod::Argument &arg) { os << arg.name; });
299     os << ");\n}\n";
300   }
301 }
302 
303 void InterfaceGenerator::emitTraitDecl(Interface &interface,
304                                        StringRef interfaceName,
305                                        StringRef interfaceTraitsName) {
306   os << llvm::formatv("  template <typename {3}>\n"
307                       "  struct {0}Trait : public ::mlir::{2}<{0},"
308                       " detail::{1}>::Trait<{3}> {{\n",
309                       interfaceName, interfaceTraitsName, interfaceBaseType,
310                       valueTemplate);
311 
312   // Insert the default implementation for any methods.
313   bool isOpInterface = isa<OpInterface>(interface);
314   for (auto &method : interface.getMethods()) {
315     // Flag interface methods named verifyTrait.
316     if (method.getName() == "verifyTrait")
317       PrintFatalError(
318           formatv("'verifyTrait' method cannot be specified as interface "
319                   "method for '{0}'; use the 'verify' field instead",
320                   interfaceName));
321     auto defaultImpl = method.getDefaultImplementation();
322     if (!defaultImpl)
323       continue;
324 
325     os << "    " << (method.isStatic() ? "static " : "");
326     emitCPPType(method.getReturnType(), os);
327     emitMethodNameAndArgs(method, os, valueType, /*addThisArg=*/false,
328                           /*addConst=*/!isOpInterface);
329     os << " {\n      " << tblgen::tgfmt(defaultImpl->trim(), &traitMethodFmt)
330        << "\n    }\n";
331   }
332 
333   if (auto verify = interface.getVerify()) {
334     assert(isa<OpInterface>(interface) && "only OpInterface supports 'verify'");
335 
336     tblgen::FmtContext verifyCtx;
337     verifyCtx.withOp("op");
338     os << "    static ::mlir::LogicalResult verifyTrait(::mlir::Operation *op) "
339           "{\n      "
340        << tblgen::tgfmt(verify->trim(), &verifyCtx) << "\n    }\n";
341   }
342   if (auto extraTraitDecls = interface.getExtraTraitClassDeclaration())
343     os << tblgen::tgfmt(*extraTraitDecls, &traitMethodFmt) << "\n";
344 
345   os << "  };\n";
346 
347   // Emit a utility wrapper trait class.
348   os << llvm::formatv("  template <typename {1}>\n"
349                       "  struct Trait : public {0}Trait<{1}> {{};\n",
350                       interfaceName, valueTemplate);
351 }
352 
353 void InterfaceGenerator::emitInterfaceDecl(Interface interface) {
354   llvm::SmallVector<StringRef, 2> namespaces;
355   llvm::SplitString(interface.getCppNamespace(), namespaces, "::");
356   for (StringRef ns : namespaces)
357     os << "namespace " << ns << " {\n";
358 
359   StringRef interfaceName = interface.getName();
360   auto interfaceTraitsName = (interfaceName + "InterfaceTraits").str();
361 
362   // Emit a forward declaration of the interface class so that it becomes usable
363   // in the signature of its methods.
364   os << "class " << interfaceName << ";\n";
365 
366   // Emit the traits struct containing the concept and model declarations.
367   os << "namespace detail {\n"
368      << "struct " << interfaceTraitsName << " {\n";
369   emitConceptDecl(interface);
370   emitModelDecl(interface);
371   os << "};\n} // end namespace detail\n";
372 
373   // Emit the main interface class declaration.
374   os << llvm::formatv("class {0} : public ::mlir::{3}<{1}, detail::{2}> {\n"
375                       "public:\n"
376                       "  using ::mlir::{3}<{1}, detail::{2}>::{3};\n",
377                       interfaceName, interfaceName, interfaceTraitsName,
378                       interfaceBaseType);
379 
380   // Emit the derived trait for the interface.
381   emitTraitDecl(interface, interfaceName, interfaceTraitsName);
382 
383   // Insert the method declarations.
384   bool isOpInterface = isa<OpInterface>(interface);
385   for (auto &method : interface.getMethods()) {
386     emitCPPType(method.getReturnType(), os << "  ");
387     emitMethodNameAndArgs(method, os, valueType, /*addThisArg=*/false,
388                           /*addConst=*/!isOpInterface);
389     os << ";\n";
390   }
391 
392   // Emit any extra declarations.
393   if (Optional<StringRef> extraDecls = interface.getExtraClassDeclaration())
394     os << *extraDecls << "\n";
395 
396   os << "};\n";
397 
398   emitModelMethodsDef(interface);
399 
400   for (StringRef ns : llvm::reverse(namespaces))
401     os << "} // namespace " << ns << "\n";
402 }
403 
404 bool InterfaceGenerator::emitInterfaceDecls() {
405   llvm::emitSourceFileHeader("Interface Declarations", os);
406 
407   for (const auto *def : defs)
408     emitInterfaceDecl(Interface(def));
409   return false;
410 }
411 
412 //===----------------------------------------------------------------------===//
413 // GEN: Interface documentation
414 //===----------------------------------------------------------------------===//
415 
416 static void emitInterfaceDoc(const llvm::Record &interfaceDef,
417                              raw_ostream &os) {
418   Interface interface(&interfaceDef);
419 
420   // Emit the interface name followed by the description.
421   os << "## " << interface.getName() << " (`" << interfaceDef.getName()
422      << "`)\n\n";
423   if (auto description = interface.getDescription())
424     mlir::tblgen::emitDescription(*description, os);
425 
426   // Emit the methods required by the interface.
427   os << "\n### Methods:\n";
428   for (const auto &method : interface.getMethods()) {
429     // Emit the method name.
430     os << "#### `" << method.getName() << "`\n\n```c++\n";
431 
432     // Emit the method signature.
433     if (method.isStatic())
434       os << "static ";
435     emitCPPType(method.getReturnType(), os) << method.getName() << '(';
436     llvm::interleaveComma(method.getArguments(), os,
437                           [&](const InterfaceMethod::Argument &arg) {
438                             emitCPPType(arg.type, os) << arg.name;
439                           });
440     os << ");\n```\n";
441 
442     // Emit the description.
443     if (auto description = method.getDescription())
444       mlir::tblgen::emitDescription(*description, os);
445 
446     // If the body is not provided, this method must be provided by the user.
447     if (!method.getBody())
448       os << "\nNOTE: This method *must* be implemented by the user.\n\n";
449   }
450 }
451 
452 bool InterfaceGenerator::emitInterfaceDocs() {
453   os << "<!-- Autogenerated by mlir-tblgen; don't manually edit -->\n";
454   os << "# " << interfaceBaseType << " definitions\n";
455 
456   for (const auto *def : defs)
457     emitInterfaceDoc(*def, os);
458   return false;
459 }
460 
461 //===----------------------------------------------------------------------===//
462 // GEN: Interface registration hooks
463 //===----------------------------------------------------------------------===//
464 
465 namespace {
466 template <typename GeneratorT> struct InterfaceGenRegistration {
467   InterfaceGenRegistration(StringRef genArg)
468       : genDeclArg(("gen-" + genArg + "-interface-decls").str()),
469         genDefArg(("gen-" + genArg + "-interface-defs").str()),
470         genDocArg(("gen-" + genArg + "-interface-docs").str()),
471         genDecls(genDeclArg, "Generate interface declarations",
472                  [](const llvm::RecordKeeper &records, raw_ostream &os) {
473                    return GeneratorT(records, os).emitInterfaceDecls();
474                  }),
475         genDefs(genDefArg, "Generate interface definitions",
476                 [](const llvm::RecordKeeper &records, raw_ostream &os) {
477                   return GeneratorT(records, os).emitInterfaceDefs();
478                 }),
479         genDocs(genDocArg, "Generate interface documentation",
480                 [](const llvm::RecordKeeper &records, raw_ostream &os) {
481                   return GeneratorT(records, os).emitInterfaceDocs();
482                 }) {}
483 
484   std::string genDeclArg, genDefArg, genDocArg;
485   mlir::GenRegistration genDecls, genDefs, genDocs;
486 };
487 } // end anonymous namespace
488 
489 static InterfaceGenRegistration<AttrInterfaceGenerator> attrGen("attr");
490 static InterfaceGenRegistration<OpInterfaceGenerator> opGen("op");
491 static InterfaceGenRegistration<TypeInterfaceGenerator> typeGen("type");
492