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