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