1 //===- DialectGen.cpp - MLIR dialect definitions 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 // DialectGen uses the description of dialects to generate C++ definitions.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "mlir/TableGen/Class.h"
14 #include "mlir/TableGen/CodeGenHelpers.h"
15 #include "mlir/TableGen/Format.h"
16 #include "mlir/TableGen/GenInfo.h"
17 #include "mlir/TableGen/Interfaces.h"
18 #include "mlir/TableGen/Operator.h"
19 #include "mlir/TableGen/Trait.h"
20 #include "llvm/ADT/Optional.h"
21 #include "llvm/ADT/Sequence.h"
22 #include "llvm/ADT/StringExtras.h"
23 #include "llvm/Support/CommandLine.h"
24 #include "llvm/Support/Signals.h"
25 #include "llvm/TableGen/Error.h"
26 #include "llvm/TableGen/Record.h"
27 #include "llvm/TableGen/TableGenBackend.h"
28 
29 #define DEBUG_TYPE "mlir-tblgen-opdefgen"
30 
31 using namespace mlir;
32 using namespace mlir::tblgen;
33 
34 static llvm::cl::OptionCategory dialectGenCat("Options for -gen-dialect-*");
35 llvm::cl::opt<std::string>
36     selectedDialect("dialect", llvm::cl::desc("The dialect to gen for"),
37                     llvm::cl::cat(dialectGenCat), llvm::cl::CommaSeparated);
38 
39 /// Utility iterator used for filtering records for a specific dialect.
40 namespace {
41 using DialectFilterIterator =
42     llvm::filter_iterator<ArrayRef<llvm::Record *>::iterator,
43                           std::function<bool(const llvm::Record *)>>;
44 } // namespace
45 
46 /// Given a set of records for a T, filter the ones that correspond to
47 /// the given dialect.
48 template <typename T>
49 static iterator_range<DialectFilterIterator>
50 filterForDialect(ArrayRef<llvm::Record *> records, Dialect &dialect) {
51   auto filterFn = [&](const llvm::Record *record) {
52     return T(record).getDialect() == dialect;
53   };
54   return {DialectFilterIterator(records.begin(), records.end(), filterFn),
55           DialectFilterIterator(records.end(), records.end(), filterFn)};
56 }
57 
58 static Optional<Dialect>
59 findSelectedDialect(ArrayRef<const llvm::Record *> dialectDefs) {
60   // Select the dialect to gen for.
61   if (dialectDefs.size() == 1 && selectedDialect.getNumOccurrences() == 0) {
62     return Dialect(dialectDefs.front());
63   }
64 
65   if (selectedDialect.getNumOccurrences() == 0) {
66     llvm::errs() << "when more than 1 dialect is present, one must be selected "
67                     "via '-dialect'\n";
68     return llvm::None;
69   }
70 
71   const auto *dialectIt =
72       llvm::find_if(dialectDefs, [](const llvm::Record *def) {
73         return Dialect(def).getName() == selectedDialect;
74       });
75   if (dialectIt == dialectDefs.end()) {
76     llvm::errs() << "selected dialect with '-dialect' does not exist\n";
77     return llvm::None;
78   }
79   return Dialect(*dialectIt);
80 }
81 
82 //===----------------------------------------------------------------------===//
83 // GEN: Dialect declarations
84 //===----------------------------------------------------------------------===//
85 
86 /// The code block for the start of a dialect class declaration.
87 ///
88 /// {0}: The name of the dialect class.
89 /// {1}: The dialect namespace.
90 /// {2}: initialization code that is emitted in the ctor body before calling
91 /// initialize()
92 static const char *const dialectDeclBeginStr = R"(
93 class {0} : public ::mlir::Dialect {
94   explicit {0}(::mlir::MLIRContext *context)
95     : ::mlir::Dialect(getDialectNamespace(), context,
96       ::mlir::TypeID::get<{0}>()) {{
97     {2}
98     initialize();
99   }
100 
101   void initialize();
102   friend class ::mlir::MLIRContext;
103 public:
104   ~{0}() override;
105   static constexpr ::llvm::StringLiteral getDialectNamespace() {
106     return ::llvm::StringLiteral("{1}");
107   }
108 )";
109 
110 /// Registration for a single dependent dialect: to be inserted in the ctor
111 /// above for each dependent dialect.
112 const char *const dialectRegistrationTemplate = R"(
113     getContext()->getOrLoadDialect<{0}>();
114 )";
115 
116 /// The code block for the attribute parser/printer hooks.
117 static const char *const attrParserDecl = R"(
118   /// Parse an attribute registered to this dialect.
119   ::mlir::Attribute parseAttribute(::mlir::DialectAsmParser &parser,
120                                    ::mlir::Type type) const override;
121 
122   /// Print an attribute registered to this dialect.
123   void printAttribute(::mlir::Attribute attr,
124                       ::mlir::DialectAsmPrinter &os) const override;
125 )";
126 
127 /// The code block for the type parser/printer hooks.
128 static const char *const typeParserDecl = R"(
129   /// Parse a type registered to this dialect.
130   ::mlir::Type parseType(::mlir::DialectAsmParser &parser) const override;
131 
132   /// Print a type registered to this dialect.
133   void printType(::mlir::Type type,
134                  ::mlir::DialectAsmPrinter &os) const override;
135 )";
136 
137 /// The code block for the canonicalization pattern registration hook.
138 static const char *const canonicalizerDecl = R"(
139   /// Register canonicalization patterns.
140   void getCanonicalizationPatterns(
141       ::mlir::RewritePatternSet &results) const override;
142 )";
143 
144 /// The code block for the constant materializer hook.
145 static const char *const constantMaterializerDecl = R"(
146   /// Materialize a single constant operation from a given attribute value with
147   /// the desired resultant type.
148   ::mlir::Operation *materializeConstant(::mlir::OpBuilder &builder,
149                                          ::mlir::Attribute value,
150                                          ::mlir::Type type,
151                                          ::mlir::Location loc) override;
152 )";
153 
154 /// The code block for the operation attribute verifier hook.
155 static const char *const opAttrVerifierDecl = R"(
156     /// Provides a hook for verifying dialect attributes attached to the given
157     /// op.
158     ::mlir::LogicalResult verifyOperationAttribute(
159         ::mlir::Operation *op, ::mlir::NamedAttribute attribute) override;
160 )";
161 
162 /// The code block for the region argument attribute verifier hook.
163 static const char *const regionArgAttrVerifierDecl = R"(
164     /// Provides a hook for verifying dialect attributes attached to the given
165     /// op's region argument.
166     ::mlir::LogicalResult verifyRegionArgAttribute(
167         ::mlir::Operation *op, unsigned regionIndex, unsigned argIndex,
168         ::mlir::NamedAttribute attribute) override;
169 )";
170 
171 /// The code block for the region result attribute verifier hook.
172 static const char *const regionResultAttrVerifierDecl = R"(
173     /// Provides a hook for verifying dialect attributes attached to the given
174     /// op's region result.
175     ::mlir::LogicalResult verifyRegionResultAttribute(
176         ::mlir::Operation *op, unsigned regionIndex, unsigned resultIndex,
177         ::mlir::NamedAttribute attribute) override;
178 )";
179 
180 /// The code block for the op interface fallback hook.
181 static const char *const operationInterfaceFallbackDecl = R"(
182     /// Provides a hook for op interface.
183     void *getRegisteredInterfaceForOp(mlir::TypeID interfaceID,
184                                       mlir::OperationName opName) override;
185 )";
186 
187 /// Generate the declaration for the given dialect class.
188 static void
189 emitDialectDecl(Dialect &dialect,
190                 const iterator_range<DialectFilterIterator> &dialectAttrs,
191                 const iterator_range<DialectFilterIterator> &dialectTypes,
192                 raw_ostream &os) {
193   /// Build the list of dependent dialects
194   std::string dependentDialectRegistrations;
195   {
196     llvm::raw_string_ostream dialectsOs(dependentDialectRegistrations);
197     for (StringRef dependentDialect : dialect.getDependentDialects())
198       dialectsOs << llvm::formatv(dialectRegistrationTemplate,
199                                   dependentDialect);
200   }
201 
202   // Emit all nested namespaces.
203   {
204     NamespaceEmitter nsEmitter(os, dialect);
205 
206     // Emit the start of the decl.
207     std::string cppName = dialect.getCppClassName();
208     os << llvm::formatv(dialectDeclBeginStr, cppName, dialect.getName(),
209                         dependentDialectRegistrations);
210 
211     // Check for any attributes/types registered to this dialect.  If there are,
212     // add the hooks for parsing/printing.
213     if (!dialectAttrs.empty() && dialect.useDefaultAttributePrinterParser())
214       os << attrParserDecl;
215     if (!dialectTypes.empty() && dialect.useDefaultTypePrinterParser())
216       os << typeParserDecl;
217 
218     // Add the decls for the various features of the dialect.
219     if (dialect.hasCanonicalizer())
220       os << canonicalizerDecl;
221     if (dialect.hasConstantMaterializer())
222       os << constantMaterializerDecl;
223     if (dialect.hasOperationAttrVerify())
224       os << opAttrVerifierDecl;
225     if (dialect.hasRegionArgAttrVerify())
226       os << regionArgAttrVerifierDecl;
227     if (dialect.hasRegionResultAttrVerify())
228       os << regionResultAttrVerifierDecl;
229     if (dialect.hasOperationInterfaceFallback())
230       os << operationInterfaceFallbackDecl;
231     if (llvm::Optional<StringRef> extraDecl =
232             dialect.getExtraClassDeclaration())
233       os << *extraDecl;
234 
235     // End the dialect decl.
236     os << "};\n";
237   }
238   if (!dialect.getCppNamespace().empty())
239     os << "DECLARE_EXPLICIT_TYPE_ID(" << dialect.getCppNamespace()
240        << "::" << dialect.getCppClassName() << ")\n";
241 }
242 
243 static bool emitDialectDecls(const llvm::RecordKeeper &recordKeeper,
244                              raw_ostream &os) {
245   emitSourceFileHeader("Dialect Declarations", os);
246 
247   auto dialectDefs = recordKeeper.getAllDerivedDefinitions("Dialect");
248   if (dialectDefs.empty())
249     return false;
250 
251   Optional<Dialect> dialect = findSelectedDialect(dialectDefs);
252   if (!dialect)
253     return true;
254   auto attrDefs = recordKeeper.getAllDerivedDefinitions("DialectAttr");
255   auto typeDefs = recordKeeper.getAllDerivedDefinitions("DialectType");
256   emitDialectDecl(*dialect, filterForDialect<Attribute>(attrDefs, *dialect),
257                   filterForDialect<Type>(typeDefs, *dialect), os);
258   return false;
259 }
260 
261 //===----------------------------------------------------------------------===//
262 // GEN: Dialect definitions
263 //===----------------------------------------------------------------------===//
264 
265 /// The code block to generate a default desturctor definition.
266 ///
267 /// {0}: The name of the dialect class.
268 static const char *const dialectDestructorStr = R"(
269 {0}::~{0}() = default;
270 
271 )";
272 
273 static void emitDialectDef(Dialect &dialect, raw_ostream &os) {
274   // Emit the TypeID explicit specializations to have a single symbol def.
275   if (!dialect.getCppNamespace().empty())
276     os << "DEFINE_EXPLICIT_TYPE_ID(" << dialect.getCppNamespace()
277        << "::" << dialect.getCppClassName() << ")\n";
278 
279   // Emit all nested namespaces.
280   NamespaceEmitter nsEmitter(os, dialect);
281 
282   if (!dialect.hasNonDefaultDestructor())
283     os << llvm::formatv(dialectDestructorStr, dialect.getCppClassName());
284 }
285 
286 static bool emitDialectDefs(const llvm::RecordKeeper &recordKeeper,
287                             raw_ostream &os) {
288   emitSourceFileHeader("Dialect Definitions", os);
289 
290   auto dialectDefs = recordKeeper.getAllDerivedDefinitions("Dialect");
291   if (dialectDefs.empty())
292     return false;
293 
294   Optional<Dialect> dialect = findSelectedDialect(dialectDefs);
295   if (!dialect)
296     return true;
297   emitDialectDef(*dialect, os);
298   return false;
299 }
300 
301 //===----------------------------------------------------------------------===//
302 // GEN: Dialect registration hooks
303 //===----------------------------------------------------------------------===//
304 
305 static mlir::GenRegistration
306     genDialectDecls("gen-dialect-decls", "Generate dialect declarations",
307                     [](const llvm::RecordKeeper &records, raw_ostream &os) {
308                       return emitDialectDecls(records, os);
309                     });
310 
311 static mlir::GenRegistration
312     genDialectDefs("gen-dialect-defs", "Generate dialect definitions",
313                    [](const llvm::RecordKeeper &records, raw_ostream &os) {
314                      return emitDialectDefs(records, os);
315                    });
316