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