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