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