1 //===- AttrOrTypeDefGen.cpp - MLIR AttrOrType 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 #include "AttrOrTypeFormatGen.h" 10 #include "mlir/TableGen/AttrOrTypeDef.h" 11 #include "mlir/TableGen/Class.h" 12 #include "mlir/TableGen/CodeGenHelpers.h" 13 #include "mlir/TableGen/Format.h" 14 #include "mlir/TableGen/GenInfo.h" 15 #include "mlir/TableGen/Interfaces.h" 16 #include "llvm/ADT/StringSet.h" 17 #include "llvm/Support/CommandLine.h" 18 #include "llvm/TableGen/Error.h" 19 #include "llvm/TableGen/TableGenBackend.h" 20 21 #define DEBUG_TYPE "mlir-tblgen-attrortypedefgen" 22 23 using namespace mlir; 24 using namespace mlir::tblgen; 25 26 //===----------------------------------------------------------------------===// 27 // Utility Functions 28 //===----------------------------------------------------------------------===// 29 30 /// Find all the AttrOrTypeDef for the specified dialect. If no dialect 31 /// specified and can only find one dialect's defs, use that. 32 static void collectAllDefs(StringRef selectedDialect, 33 std::vector<llvm::Record *> records, 34 SmallVectorImpl<AttrOrTypeDef> &resultDefs) { 35 // Nothing to do if no defs were found. 36 if (records.empty()) 37 return; 38 39 auto defs = llvm::map_range( 40 records, [&](const llvm::Record *rec) { return AttrOrTypeDef(rec); }); 41 if (selectedDialect.empty()) { 42 // If a dialect was not specified, ensure that all found defs belong to the 43 // same dialect. 44 if (!llvm::is_splat(llvm::map_range( 45 defs, [](const auto &def) { return def.getDialect(); }))) { 46 llvm::PrintFatalError("defs belonging to more than one dialect. Must " 47 "select one via '--(attr|type)defs-dialect'"); 48 } 49 resultDefs.assign(defs.begin(), defs.end()); 50 } else { 51 // Otherwise, generate the defs that belong to the selected dialect. 52 auto dialectDefs = llvm::make_filter_range(defs, [&](const auto &def) { 53 return def.getDialect().getName().equals(selectedDialect); 54 }); 55 resultDefs.assign(dialectDefs.begin(), dialectDefs.end()); 56 } 57 } 58 59 //===----------------------------------------------------------------------===// 60 // DefGen 61 //===----------------------------------------------------------------------===// 62 63 namespace { 64 class DefGen { 65 public: 66 /// Create the attribute or type class. 67 DefGen(const AttrOrTypeDef &def); 68 69 void emitDecl(raw_ostream &os) const { 70 if (storageCls) { 71 NamespaceEmitter ns(os, def.getStorageNamespace()); 72 os << "struct " << def.getStorageClassName() << ";\n"; 73 } 74 defCls.writeDeclTo(os); 75 } 76 void emitDef(raw_ostream &os) const { 77 if (storageCls && def.genStorageClass()) { 78 NamespaceEmitter ns(os, def.getStorageNamespace()); 79 storageCls->writeDeclTo(os); // everything is inline 80 } 81 defCls.writeDefTo(os); 82 } 83 84 private: 85 /// Add traits from the TableGen definition to the class. 86 void createParentWithTraits(); 87 /// Emit top-level declarations: using declarations and any extra class 88 /// declarations. 89 void emitTopLevelDeclarations(); 90 /// Emit attribute or type builders. 91 void emitBuilders(); 92 /// Emit a verifier for the def. 93 void emitVerifier(); 94 /// Emit parsers and printers. 95 void emitParserPrinter(); 96 /// Emit parameter accessors, if required. 97 void emitAccessors(); 98 /// Emit interface methods. 99 void emitInterfaceMethods(); 100 101 //===--------------------------------------------------------------------===// 102 // Builder Emission 103 104 /// Emit the default builder `Attribute::get` 105 void emitDefaultBuilder(); 106 /// Emit the checked builder `Attribute::getChecked` 107 void emitCheckedBuilder(); 108 /// Emit a custom builder. 109 void emitCustomBuilder(const AttrOrTypeBuilder &builder); 110 /// Emit a checked custom builder. 111 void emitCheckedCustomBuilder(const AttrOrTypeBuilder &builder); 112 113 //===--------------------------------------------------------------------===// 114 // Interface Method Emission 115 116 /// Emit methods for a trait. 117 void emitTraitMethods(const InterfaceTrait &trait); 118 /// Emit a trait method. 119 void emitTraitMethod(const InterfaceMethod &method); 120 121 //===--------------------------------------------------------------------===// 122 // Storage Class Emission 123 void emitStorageClass(); 124 /// Generate the storage class constructor. 125 void emitStorageConstructor(); 126 /// Emit the key type `KeyTy`. 127 void emitKeyType(); 128 /// Emit the equality comparison operator. 129 void emitEquals(); 130 /// Emit the key hash function. 131 void emitHashKey(); 132 /// Emit the function to construct the storage class. 133 void emitConstruct(); 134 135 //===--------------------------------------------------------------------===// 136 // Utility Function Declarations 137 138 /// Get the method parameters for a def builder, where the first several 139 /// parameters may be different. 140 SmallVector<MethodParameter> 141 getBuilderParams(std::initializer_list<MethodParameter> prefix) const; 142 143 //===--------------------------------------------------------------------===// 144 // Class fields 145 146 /// The attribute or type definition. 147 const AttrOrTypeDef &def; 148 /// The list of attribute or type parameters. 149 ArrayRef<AttrOrTypeParameter> params; 150 /// The attribute or type class. 151 Class defCls; 152 /// An optional attribute or type storage class. The storage class will 153 /// exist if and only if the def has more than zero parameters. 154 Optional<Class> storageCls; 155 156 /// The C++ base value of the def, either "Attribute" or "Type". 157 StringRef valueType; 158 /// The prefix/suffix of the TableGen def name, either "Attr" or "Type". 159 StringRef defType; 160 }; 161 } // namespace 162 163 DefGen::DefGen(const AttrOrTypeDef &def) 164 : def(def), params(def.getParameters()), defCls(def.getCppClassName()), 165 valueType(isa<AttrDef>(def) ? "Attribute" : "Type"), 166 defType(isa<AttrDef>(def) ? "Attr" : "Type") { 167 // Check that all parameters have names. 168 for (const AttrOrTypeParameter ¶m : def.getParameters()) 169 if (param.isAnonymous()) 170 llvm::PrintFatalError("all parameters must have a name"); 171 172 // If a storage class is needed, create one. 173 if (def.getNumParameters() > 0) 174 storageCls.emplace(def.getStorageClassName(), /*isStruct=*/true); 175 176 // Create the parent class with any indicated traits. 177 createParentWithTraits(); 178 // Emit top-level declarations. 179 emitTopLevelDeclarations(); 180 // Emit builders for defs with parameters 181 if (storageCls) 182 emitBuilders(); 183 // Emit the verifier. 184 if (storageCls && def.genVerifyDecl()) 185 emitVerifier(); 186 // Emit the mnemonic, if there is one, and any associated parser and printer. 187 if (def.getMnemonic()) 188 emitParserPrinter(); 189 // Emit accessors 190 if (def.genAccessors()) 191 emitAccessors(); 192 // Emit trait interface methods 193 emitInterfaceMethods(); 194 defCls.finalize(); 195 // Emit a storage class if one is needed 196 if (storageCls && def.genStorageClass()) 197 emitStorageClass(); 198 } 199 200 void DefGen::createParentWithTraits() { 201 ParentClass defParent(strfmt("::mlir::{0}::{1}Base", valueType, defType)); 202 defParent.addTemplateParam(def.getCppClassName()); 203 defParent.addTemplateParam(def.getCppBaseClassName()); 204 defParent.addTemplateParam(storageCls 205 ? strfmt("{0}::{1}", def.getStorageNamespace(), 206 def.getStorageClassName()) 207 : strfmt("::mlir::{0}Storage", valueType)); 208 for (auto &trait : def.getTraits()) { 209 defParent.addTemplateParam( 210 isa<NativeTrait>(&trait) 211 ? cast<NativeTrait>(&trait)->getFullyQualifiedTraitName() 212 : cast<InterfaceTrait>(&trait)->getFullyQualifiedTraitName()); 213 } 214 defCls.addParent(std::move(defParent)); 215 } 216 217 void DefGen::emitTopLevelDeclarations() { 218 // Inherit constructors from the attribute or type class. 219 defCls.declare<VisibilityDeclaration>(Visibility::Public); 220 defCls.declare<UsingDeclaration>("Base::Base"); 221 222 // Emit the extra declarations first in case there's a definition in there. 223 if (Optional<StringRef> extraDecl = def.getExtraDecls()) 224 defCls.declare<ExtraClassDeclaration>(*extraDecl); 225 } 226 227 void DefGen::emitBuilders() { 228 if (!def.skipDefaultBuilders()) { 229 emitDefaultBuilder(); 230 if (def.genVerifyDecl()) 231 emitCheckedBuilder(); 232 } 233 for (auto &builder : def.getBuilders()) { 234 emitCustomBuilder(builder); 235 if (def.genVerifyDecl()) 236 emitCheckedCustomBuilder(builder); 237 } 238 } 239 240 void DefGen::emitVerifier() { 241 defCls.declare<UsingDeclaration>("Base::getChecked"); 242 defCls.declareStaticMethod( 243 "::mlir::LogicalResult", "verify", 244 getBuilderParams({{"::llvm::function_ref<::mlir::InFlightDiagnostic()>", 245 "emitError"}})); 246 } 247 248 void DefGen::emitParserPrinter() { 249 auto *mnemonic = defCls.addStaticMethod<Method::Constexpr>( 250 "::llvm::StringLiteral", "getMnemonic"); 251 mnemonic->body().indent() << strfmt("return {\"{0}\"};", *def.getMnemonic()); 252 253 // Declare the parser and printer, if needed. 254 bool hasAssemblyFormat = def.getAssemblyFormat().has_value(); 255 if (!def.hasCustomAssemblyFormat() && !hasAssemblyFormat) 256 return; 257 258 // Declare the parser. 259 SmallVector<MethodParameter> parserParams; 260 parserParams.emplace_back("::mlir::AsmParser &", "odsParser"); 261 if (isa<AttrDef>(&def)) 262 parserParams.emplace_back("::mlir::Type", "odsType"); 263 auto *parser = defCls.addMethod(strfmt("::mlir::{0}", valueType), "parse", 264 hasAssemblyFormat ? Method::Static 265 : Method::StaticDeclaration, 266 std::move(parserParams)); 267 // Declare the printer. 268 auto props = hasAssemblyFormat ? Method::Const : Method::ConstDeclaration; 269 Method *printer = 270 defCls.addMethod("void", "print", props, 271 MethodParameter("::mlir::AsmPrinter &", "odsPrinter")); 272 // Emit the bodies if we are using the declarative format. 273 if (hasAssemblyFormat) 274 return generateAttrOrTypeFormat(def, parser->body(), printer->body()); 275 } 276 277 void DefGen::emitAccessors() { 278 for (auto ¶m : params) { 279 Method *m = defCls.addMethod( 280 param.getCppAccessorType(), param.getAccessorName(), 281 def.genStorageClass() ? Method::Const : Method::ConstDeclaration); 282 // Generate accessor definitions only if we also generate the storage 283 // class. Otherwise, let the user define the exact accessor definition. 284 if (!def.genStorageClass()) 285 continue; 286 auto scope = m->body().indent().scope("return getImpl()->", ";"); 287 if (isa<AttributeSelfTypeParameter>(param)) 288 m->body() << formatv("getType().cast<{0}>()", param.getCppType()); 289 else 290 m->body() << param.getName(); 291 } 292 } 293 294 void DefGen::emitInterfaceMethods() { 295 for (auto &traitDef : def.getTraits()) 296 if (auto *trait = dyn_cast<InterfaceTrait>(&traitDef)) 297 if (trait->shouldDeclareMethods()) 298 emitTraitMethods(*trait); 299 } 300 301 //===----------------------------------------------------------------------===// 302 // Builder Emission 303 304 SmallVector<MethodParameter> 305 DefGen::getBuilderParams(std::initializer_list<MethodParameter> prefix) const { 306 SmallVector<MethodParameter> builderParams; 307 builderParams.append(prefix.begin(), prefix.end()); 308 for (auto ¶m : params) 309 builderParams.emplace_back(param.getCppType(), param.getName()); 310 return builderParams; 311 } 312 313 void DefGen::emitDefaultBuilder() { 314 Method *m = defCls.addStaticMethod( 315 def.getCppClassName(), "get", 316 getBuilderParams({{"::mlir::MLIRContext *", "context"}})); 317 MethodBody &body = m->body().indent(); 318 auto scope = body.scope("return Base::get(context", ");"); 319 llvm::for_each(params, [&](auto ¶m) { body << ", " << param.getName(); }); 320 } 321 322 void DefGen::emitCheckedBuilder() { 323 Method *m = defCls.addStaticMethod( 324 def.getCppClassName(), "getChecked", 325 getBuilderParams( 326 {{"::llvm::function_ref<::mlir::InFlightDiagnostic()>", "emitError"}, 327 {"::mlir::MLIRContext *", "context"}})); 328 MethodBody &body = m->body().indent(); 329 auto scope = body.scope("return Base::getChecked(emitError, context", ");"); 330 llvm::for_each(params, [&](auto ¶m) { body << ", " << param.getName(); }); 331 } 332 333 static SmallVector<MethodParameter> 334 getCustomBuilderParams(std::initializer_list<MethodParameter> prefix, 335 const AttrOrTypeBuilder &builder) { 336 auto params = builder.getParameters(); 337 SmallVector<MethodParameter> builderParams; 338 builderParams.append(prefix.begin(), prefix.end()); 339 if (!builder.hasInferredContextParameter()) 340 builderParams.emplace_back("::mlir::MLIRContext *", "context"); 341 for (auto ¶m : params) { 342 builderParams.emplace_back(param.getCppType(), *param.getName(), 343 param.getDefaultValue()); 344 } 345 return builderParams; 346 } 347 348 void DefGen::emitCustomBuilder(const AttrOrTypeBuilder &builder) { 349 // Don't emit a body if there isn't one. 350 auto props = builder.getBody() ? Method::Static : Method::StaticDeclaration; 351 Method *m = defCls.addMethod(def.getCppClassName(), "get", props, 352 getCustomBuilderParams({}, builder)); 353 if (!builder.getBody()) 354 return; 355 356 // Format the body and emit it. 357 FmtContext ctx; 358 ctx.addSubst("_get", "Base::get"); 359 if (!builder.hasInferredContextParameter()) 360 ctx.addSubst("_ctxt", "context"); 361 std::string bodyStr = tgfmt(*builder.getBody(), &ctx); 362 m->body().indent().getStream().printReindented(bodyStr); 363 } 364 365 /// Replace all instances of 'from' to 'to' in `str` and return the new string. 366 static std::string replaceInStr(std::string str, StringRef from, StringRef to) { 367 size_t pos = 0; 368 while ((pos = str.find(from.data(), pos, from.size())) != std::string::npos) 369 str.replace(pos, from.size(), to.data(), to.size()); 370 return str; 371 } 372 373 void DefGen::emitCheckedCustomBuilder(const AttrOrTypeBuilder &builder) { 374 // Don't emit a body if there isn't one. 375 auto props = builder.getBody() ? Method::Static : Method::StaticDeclaration; 376 Method *m = defCls.addMethod( 377 def.getCppClassName(), "getChecked", props, 378 getCustomBuilderParams( 379 {{"::llvm::function_ref<::mlir::InFlightDiagnostic()>", "emitError"}}, 380 builder)); 381 if (!builder.getBody()) 382 return; 383 384 // Format the body and emit it. Replace $_get(...) with 385 // Base::getChecked(emitError, ...) 386 FmtContext ctx; 387 if (!builder.hasInferredContextParameter()) 388 ctx.addSubst("_ctxt", "context"); 389 std::string bodyStr = replaceInStr(builder.getBody()->str(), "$_get(", 390 "Base::getChecked(emitError, "); 391 bodyStr = tgfmt(bodyStr, &ctx); 392 m->body().indent().getStream().printReindented(bodyStr); 393 } 394 395 //===----------------------------------------------------------------------===// 396 // Interface Method Emission 397 398 void DefGen::emitTraitMethods(const InterfaceTrait &trait) { 399 // Get the set of methods that should always be declared. 400 auto alwaysDeclaredMethods = trait.getAlwaysDeclaredMethods(); 401 StringSet<> alwaysDeclared; 402 alwaysDeclared.insert(alwaysDeclaredMethods.begin(), 403 alwaysDeclaredMethods.end()); 404 405 Interface iface = trait.getInterface(); // causes strange bugs if elided 406 for (auto &method : iface.getMethods()) { 407 // Don't declare if the method has a body. Or if the method has a default 408 // implementation and the def didn't request that it always be declared. 409 if (method.getBody() || (method.getDefaultImplementation() && 410 !alwaysDeclared.count(method.getName()))) 411 continue; 412 emitTraitMethod(method); 413 } 414 } 415 416 void DefGen::emitTraitMethod(const InterfaceMethod &method) { 417 // All interface methods are declaration-only. 418 auto props = 419 method.isStatic() ? Method::StaticDeclaration : Method::ConstDeclaration; 420 SmallVector<MethodParameter> params; 421 for (auto ¶m : method.getArguments()) 422 params.emplace_back(param.type, param.name); 423 defCls.addMethod(method.getReturnType(), method.getName(), props, 424 std::move(params)); 425 } 426 427 //===----------------------------------------------------------------------===// 428 // Storage Class Emission 429 430 void DefGen::emitStorageConstructor() { 431 Constructor *ctor = 432 storageCls->addConstructor<Method::Inline>(getBuilderParams({})); 433 if (auto *attrDef = dyn_cast<AttrDef>(&def)) { 434 // For attributes, a parameter marked with AttributeSelfTypeParameter is 435 // the type initializer that must be passed to the parent constructor. 436 const auto isSelfType = [](const AttrOrTypeParameter ¶m) { 437 return isa<AttributeSelfTypeParameter>(param); 438 }; 439 auto *selfTypeParam = llvm::find_if(params, isSelfType); 440 if (std::count_if(selfTypeParam, params.end(), isSelfType) > 1) { 441 PrintFatalError(def.getLoc(), 442 "Only one attribute parameter can be marked as " 443 "AttributeSelfTypeParameter"); 444 } 445 // Alternatively, if a type builder was specified, use that instead. 446 std::string attrStorageInit = 447 selfTypeParam == params.end() ? "" : selfTypeParam->getName().str(); 448 if (attrDef->getTypeBuilder()) { 449 FmtContext ctx; 450 for (auto ¶m : params) 451 ctx.addSubst(strfmt("_{0}", param.getName()), param.getName()); 452 attrStorageInit = tgfmt(*attrDef->getTypeBuilder(), &ctx); 453 } 454 ctor->addMemberInitializer("::mlir::AttributeStorage", 455 std::move(attrStorageInit)); 456 // Initialize members that aren't the attribute's type. 457 for (auto ¶m : params) 458 if (selfTypeParam == params.end() || *selfTypeParam != param) 459 ctor->addMemberInitializer(param.getName(), param.getName()); 460 } else { 461 for (auto ¶m : params) 462 ctor->addMemberInitializer(param.getName(), param.getName()); 463 } 464 } 465 466 void DefGen::emitKeyType() { 467 std::string keyType("std::tuple<"); 468 llvm::raw_string_ostream os(keyType); 469 llvm::interleaveComma(params, os, 470 [&](auto ¶m) { os << param.getCppType(); }); 471 os << '>'; 472 storageCls->declare<UsingDeclaration>("KeyTy", std::move(os.str())); 473 } 474 475 void DefGen::emitEquals() { 476 Method *eq = storageCls->addConstMethod<Method::Inline>( 477 "bool", "operator==", MethodParameter("const KeyTy &", "tblgenKey")); 478 auto &body = eq->body().indent(); 479 auto scope = body.scope("return (", ");"); 480 const auto eachFn = [&](auto it) { 481 FmtContext ctx({{"_lhs", isa<AttributeSelfTypeParameter>(it.value()) 482 ? "getType()" 483 : it.value().getName()}, 484 {"_rhs", strfmt("std::get<{0}>(tblgenKey)", it.index())}}); 485 body << tgfmt(it.value().getComparator(), &ctx); 486 }; 487 llvm::interleave(llvm::enumerate(params), body, eachFn, ") && ("); 488 } 489 490 void DefGen::emitHashKey() { 491 Method *hash = storageCls->addStaticInlineMethod( 492 "::llvm::hash_code", "hashKey", 493 MethodParameter("const KeyTy &", "tblgenKey")); 494 auto &body = hash->body().indent(); 495 auto scope = body.scope("return ::llvm::hash_combine(", ");"); 496 llvm::interleaveComma(llvm::enumerate(params), body, [&](auto it) { 497 body << llvm::formatv("std::get<{0}>(tblgenKey)", it.index()); 498 }); 499 } 500 501 void DefGen::emitConstruct() { 502 Method *construct = storageCls->addMethod<Method::Inline>( 503 strfmt("{0} *", def.getStorageClassName()), "construct", 504 def.hasStorageCustomConstructor() ? Method::StaticDeclaration 505 : Method::Static, 506 MethodParameter(strfmt("::mlir::{0}StorageAllocator &", valueType), 507 "allocator"), 508 MethodParameter("const KeyTy &", "tblgenKey")); 509 if (!def.hasStorageCustomConstructor()) { 510 auto &body = construct->body().indent(); 511 for (const auto &it : llvm::enumerate(params)) { 512 body << formatv("auto {0} = std::get<{1}>(tblgenKey);\n", 513 it.value().getName(), it.index()); 514 } 515 // Use the parameters' custom allocator code, if provided. 516 FmtContext ctx = FmtContext().addSubst("_allocator", "allocator"); 517 for (auto ¶m : params) { 518 if (Optional<StringRef> allocCode = param.getAllocator()) { 519 ctx.withSelf(param.getName()).addSubst("_dst", param.getName()); 520 body << tgfmt(*allocCode, &ctx) << '\n'; 521 } 522 } 523 auto scope = 524 body.scope(strfmt("return new (allocator.allocate<{0}>()) {0}(", 525 def.getStorageClassName()), 526 ");"); 527 llvm::interleaveComma(params, body, 528 [&](auto ¶m) { body << param.getName(); }); 529 } 530 } 531 532 void DefGen::emitStorageClass() { 533 // Add the appropriate parent class. 534 storageCls->addParent(strfmt("::mlir::{0}Storage", valueType)); 535 // Add the constructor. 536 emitStorageConstructor(); 537 // Declare the key type. 538 emitKeyType(); 539 // Add the comparison method. 540 emitEquals(); 541 // Emit the key hash method. 542 emitHashKey(); 543 // Emit the storage constructor. Just declare it if the user wants to define 544 // it themself. 545 emitConstruct(); 546 // Emit the storage class members as public, at the very end of the struct. 547 storageCls->finalize(); 548 for (auto ¶m : params) 549 if (!isa<AttributeSelfTypeParameter>(param)) 550 storageCls->declare<Field>(param.getCppType(), param.getName()); 551 } 552 553 //===----------------------------------------------------------------------===// 554 // DefGenerator 555 //===----------------------------------------------------------------------===// 556 557 namespace { 558 /// This struct is the base generator used when processing tablegen interfaces. 559 class DefGenerator { 560 public: 561 bool emitDecls(StringRef selectedDialect); 562 bool emitDefs(StringRef selectedDialect); 563 564 protected: 565 DefGenerator(std::vector<llvm::Record *> &&defs, raw_ostream &os, 566 StringRef defType, StringRef valueType, bool isAttrGenerator) 567 : defRecords(std::move(defs)), os(os), defType(defType), 568 valueType(valueType), isAttrGenerator(isAttrGenerator) {} 569 570 /// Emit the list of def type names. 571 void emitTypeDefList(ArrayRef<AttrOrTypeDef> defs); 572 /// Emit the code to dispatch between different defs during parsing/printing. 573 void emitParsePrintDispatch(ArrayRef<AttrOrTypeDef> defs); 574 575 /// The set of def records to emit. 576 std::vector<llvm::Record *> defRecords; 577 /// The attribute or type class to emit. 578 /// The stream to emit to. 579 raw_ostream &os; 580 /// The prefix of the tablegen def name, e.g. Attr or Type. 581 StringRef defType; 582 /// The C++ base value type of the def, e.g. Attribute or Type. 583 StringRef valueType; 584 /// Flag indicating if this generator is for Attributes. False if the 585 /// generator is for types. 586 bool isAttrGenerator; 587 }; 588 589 /// A specialized generator for AttrDefs. 590 struct AttrDefGenerator : public DefGenerator { 591 AttrDefGenerator(const llvm::RecordKeeper &records, raw_ostream &os) 592 : DefGenerator(records.getAllDerivedDefinitionsIfDefined("AttrDef"), os, 593 "Attr", "Attribute", /*isAttrGenerator=*/true) {} 594 }; 595 /// A specialized generator for TypeDefs. 596 struct TypeDefGenerator : public DefGenerator { 597 TypeDefGenerator(const llvm::RecordKeeper &records, raw_ostream &os) 598 : DefGenerator(records.getAllDerivedDefinitionsIfDefined("TypeDef"), os, 599 "Type", "Type", /*isAttrGenerator=*/false) {} 600 }; 601 } // namespace 602 603 //===----------------------------------------------------------------------===// 604 // GEN: Declarations 605 //===----------------------------------------------------------------------===// 606 607 /// Print this above all the other declarations. Contains type declarations used 608 /// later on. 609 static const char *const typeDefDeclHeader = R"( 610 namespace mlir { 611 class AsmParser; 612 class AsmPrinter; 613 } // namespace mlir 614 )"; 615 616 bool DefGenerator::emitDecls(StringRef selectedDialect) { 617 emitSourceFileHeader((defType + "Def Declarations").str(), os); 618 IfDefScope scope("GET_" + defType.upper() + "DEF_CLASSES", os); 619 620 // Output the common "header". 621 os << typeDefDeclHeader; 622 623 SmallVector<AttrOrTypeDef, 16> defs; 624 collectAllDefs(selectedDialect, defRecords, defs); 625 if (defs.empty()) 626 return false; 627 { 628 NamespaceEmitter nsEmitter(os, defs.front().getDialect()); 629 630 // Declare all the def classes first (in case they reference each other). 631 for (const AttrOrTypeDef &def : defs) 632 os << "class " << def.getCppClassName() << ";\n"; 633 634 // Emit the declarations. 635 for (const AttrOrTypeDef &def : defs) 636 DefGen(def).emitDecl(os); 637 } 638 // Emit the TypeID explicit specializations to have a single definition for 639 // each of these. 640 for (const AttrOrTypeDef &def : defs) 641 if (!def.getDialect().getCppNamespace().empty()) 642 os << "MLIR_DECLARE_EXPLICIT_TYPE_ID(" 643 << def.getDialect().getCppNamespace() << "::" << def.getCppClassName() 644 << ")\n"; 645 646 return false; 647 } 648 649 //===----------------------------------------------------------------------===// 650 // GEN: Def List 651 //===----------------------------------------------------------------------===// 652 653 void DefGenerator::emitTypeDefList(ArrayRef<AttrOrTypeDef> defs) { 654 IfDefScope scope("GET_" + defType.upper() + "DEF_LIST", os); 655 auto interleaveFn = [&](const AttrOrTypeDef &def) { 656 os << def.getDialect().getCppNamespace() << "::" << def.getCppClassName(); 657 }; 658 llvm::interleave(defs, os, interleaveFn, ",\n"); 659 os << "\n"; 660 } 661 662 //===----------------------------------------------------------------------===// 663 // GEN: Definitions 664 //===----------------------------------------------------------------------===// 665 666 /// The code block for default attribute parser/printer dispatch boilerplate. 667 /// {0}: the dialect fully qualified class name. 668 /// {1}: the optional code for the dynamic attribute parser dispatch. 669 /// {2}: the optional code for the dynamic attribute printer dispatch. 670 static const char *const dialectDefaultAttrPrinterParserDispatch = R"( 671 /// Parse an attribute registered to this dialect. 672 ::mlir::Attribute {0}::parseAttribute(::mlir::DialectAsmParser &parser, 673 ::mlir::Type type) const {{ 674 ::llvm::SMLoc typeLoc = parser.getCurrentLocation(); 675 ::llvm::StringRef attrTag; 676 {{ 677 ::mlir::Attribute attr; 678 auto parseResult = generatedAttributeParser(parser, &attrTag, type, attr); 679 if (parseResult.hasValue()) 680 return attr; 681 } 682 {1} 683 parser.emitError(typeLoc) << "unknown attribute `" 684 << attrTag << "` in dialect `" << getNamespace() << "`"; 685 return {{}; 686 } 687 /// Print an attribute registered to this dialect. 688 void {0}::printAttribute(::mlir::Attribute attr, 689 ::mlir::DialectAsmPrinter &printer) const {{ 690 if (::mlir::succeeded(generatedAttributePrinter(attr, printer))) 691 return; 692 {2} 693 } 694 )"; 695 696 /// The code block for dynamic attribute parser dispatch boilerplate. 697 static const char *const dialectDynamicAttrParserDispatch = R"( 698 { 699 ::mlir::Attribute genAttr; 700 auto parseResult = parseOptionalDynamicAttr(attrTag, parser, genAttr); 701 if (parseResult.hasValue()) { 702 if (::mlir::succeeded(parseResult.getValue())) 703 return genAttr; 704 return Attribute(); 705 } 706 } 707 )"; 708 709 /// The code block for dynamic type printer dispatch boilerplate. 710 static const char *const dialectDynamicAttrPrinterDispatch = R"( 711 if (::mlir::succeeded(printIfDynamicAttr(attr, printer))) 712 return; 713 )"; 714 715 /// The code block for default type parser/printer dispatch boilerplate. 716 /// {0}: the dialect fully qualified class name. 717 /// {1}: the optional code for the dynamic type parser dispatch. 718 /// {2}: the optional code for the dynamic type printer dispatch. 719 static const char *const dialectDefaultTypePrinterParserDispatch = R"( 720 /// Parse a type registered to this dialect. 721 ::mlir::Type {0}::parseType(::mlir::DialectAsmParser &parser) const {{ 722 ::llvm::SMLoc typeLoc = parser.getCurrentLocation(); 723 ::llvm::StringRef mnemonic; 724 ::mlir::Type genType; 725 auto parseResult = generatedTypeParser(parser, &mnemonic, genType); 726 if (parseResult.hasValue()) 727 return genType; 728 {1} 729 parser.emitError(typeLoc) << "unknown type `" 730 << mnemonic << "` in dialect `" << getNamespace() << "`"; 731 return {{}; 732 } 733 /// Print a type registered to this dialect. 734 void {0}::printType(::mlir::Type type, 735 ::mlir::DialectAsmPrinter &printer) const {{ 736 if (::mlir::succeeded(generatedTypePrinter(type, printer))) 737 return; 738 {2} 739 } 740 )"; 741 742 /// The code block for dynamic type parser dispatch boilerplate. 743 static const char *const dialectDynamicTypeParserDispatch = R"( 744 { 745 auto parseResult = parseOptionalDynamicType(mnemonic, parser, genType); 746 if (parseResult.hasValue()) { 747 if (::mlir::succeeded(parseResult.getValue())) 748 return genType; 749 return Type(); 750 } 751 } 752 )"; 753 754 /// The code block for dynamic type printer dispatch boilerplate. 755 static const char *const dialectDynamicTypePrinterDispatch = R"( 756 if (::mlir::succeeded(printIfDynamicType(type, printer))) 757 return; 758 )"; 759 760 /// Emit the dialect printer/parser dispatcher. User's code should call these 761 /// functions from their dialect's print/parse methods. 762 void DefGenerator::emitParsePrintDispatch(ArrayRef<AttrOrTypeDef> defs) { 763 if (llvm::none_of(defs, [](const AttrOrTypeDef &def) { 764 return def.getMnemonic().has_value(); 765 })) { 766 return; 767 } 768 // Declare the parser. 769 SmallVector<MethodParameter> params = {{"::mlir::AsmParser &", "parser"}, 770 {"::llvm::StringRef *", "mnemonic"}}; 771 if (isAttrGenerator) 772 params.emplace_back("::mlir::Type", "type"); 773 params.emplace_back(strfmt("::mlir::{0} &", valueType), "value"); 774 Method parse("::mlir::OptionalParseResult", 775 strfmt("generated{0}Parser", valueType), Method::StaticInline, 776 std::move(params)); 777 // Declare the printer. 778 Method printer("::mlir::LogicalResult", 779 strfmt("generated{0}Printer", valueType), Method::StaticInline, 780 {{strfmt("::mlir::{0}", valueType), "def"}, 781 {"::mlir::AsmPrinter &", "printer"}}); 782 783 // The parser dispatch uses a KeywordSwitch, matching on the mnemonic and 784 // calling the def's parse function. 785 parse.body() << " return " 786 "::mlir::AsmParser::KeywordSwitch<::mlir::" 787 "OptionalParseResult>(parser)\n"; 788 const char *const getValueForMnemonic = 789 R"( .Case({0}::getMnemonic(), [&](llvm::StringRef, llvm::SMLoc) {{ 790 value = {0}::{1}; 791 return ::mlir::success(!!value); 792 }) 793 )"; 794 795 // The printer dispatch uses llvm::TypeSwitch to find and call the correct 796 // printer. 797 printer.body() << " return ::llvm::TypeSwitch<::mlir::" << valueType 798 << ", ::mlir::LogicalResult>(def)"; 799 const char *const printValue = R"( .Case<{0}>([&](auto t) {{ 800 printer << {0}::getMnemonic();{1} 801 return ::mlir::success(); 802 }) 803 )"; 804 for (auto &def : defs) { 805 if (!def.getMnemonic()) 806 continue; 807 bool hasParserPrinterDecl = 808 def.hasCustomAssemblyFormat() || def.getAssemblyFormat(); 809 std::string defClass = strfmt( 810 "{0}::{1}", def.getDialect().getCppNamespace(), def.getCppClassName()); 811 812 // If the def has no parameters or parser code, invoke a normal `get`. 813 std::string parseOrGet = 814 hasParserPrinterDecl 815 ? strfmt("parse(parser{0})", isAttrGenerator ? ", type" : "") 816 : "get(parser.getContext())"; 817 parse.body() << llvm::formatv(getValueForMnemonic, defClass, parseOrGet); 818 819 // If the def has no parameters and no printer, just print the mnemonic. 820 StringRef printDef = ""; 821 if (hasParserPrinterDecl) 822 printDef = "\nt.print(printer);"; 823 printer.body() << llvm::formatv(printValue, defClass, printDef); 824 } 825 parse.body() << " .Default([&](llvm::StringRef keyword, llvm::SMLoc) {\n" 826 " *mnemonic = keyword;\n" 827 " return llvm::None;\n" 828 " });"; 829 printer.body() << " .Default([](auto) { return ::mlir::failure(); });"; 830 831 raw_indented_ostream indentedOs(os); 832 parse.writeDeclTo(indentedOs); 833 printer.writeDeclTo(indentedOs); 834 } 835 836 bool DefGenerator::emitDefs(StringRef selectedDialect) { 837 emitSourceFileHeader((defType + "Def Definitions").str(), os); 838 839 SmallVector<AttrOrTypeDef, 16> defs; 840 collectAllDefs(selectedDialect, defRecords, defs); 841 if (defs.empty()) 842 return false; 843 emitTypeDefList(defs); 844 845 IfDefScope scope("GET_" + defType.upper() + "DEF_CLASSES", os); 846 emitParsePrintDispatch(defs); 847 for (const AttrOrTypeDef &def : defs) { 848 { 849 NamespaceEmitter ns(os, def.getDialect()); 850 DefGen gen(def); 851 gen.emitDef(os); 852 } 853 // Emit the TypeID explicit specializations to have a single symbol def. 854 if (!def.getDialect().getCppNamespace().empty()) 855 os << "MLIR_DEFINE_EXPLICIT_TYPE_ID(" 856 << def.getDialect().getCppNamespace() << "::" << def.getCppClassName() 857 << ")\n"; 858 } 859 860 Dialect firstDialect = defs.front().getDialect(); 861 862 // Emit the default parser/printer for Attributes if the dialect asked for it. 863 if (isAttrGenerator && firstDialect.useDefaultAttributePrinterParser()) { 864 NamespaceEmitter nsEmitter(os, firstDialect); 865 if (firstDialect.isExtensible()) { 866 os << llvm::formatv(dialectDefaultAttrPrinterParserDispatch, 867 firstDialect.getCppClassName(), 868 dialectDynamicAttrParserDispatch, 869 dialectDynamicAttrPrinterDispatch); 870 } else { 871 os << llvm::formatv(dialectDefaultAttrPrinterParserDispatch, 872 firstDialect.getCppClassName(), "", ""); 873 } 874 } 875 876 // Emit the default parser/printer for Types if the dialect asked for it. 877 if (!isAttrGenerator && firstDialect.useDefaultTypePrinterParser()) { 878 NamespaceEmitter nsEmitter(os, firstDialect); 879 if (firstDialect.isExtensible()) { 880 os << llvm::formatv(dialectDefaultTypePrinterParserDispatch, 881 firstDialect.getCppClassName(), 882 dialectDynamicTypeParserDispatch, 883 dialectDynamicTypePrinterDispatch); 884 } else { 885 os << llvm::formatv(dialectDefaultTypePrinterParserDispatch, 886 firstDialect.getCppClassName(), "", ""); 887 } 888 } 889 890 return false; 891 } 892 893 //===----------------------------------------------------------------------===// 894 // GEN: Registration hooks 895 //===----------------------------------------------------------------------===// 896 897 //===----------------------------------------------------------------------===// 898 // AttrDef 899 900 static llvm::cl::OptionCategory attrdefGenCat("Options for -gen-attrdef-*"); 901 static llvm::cl::opt<std::string> 902 attrDialect("attrdefs-dialect", 903 llvm::cl::desc("Generate attributes for this dialect"), 904 llvm::cl::cat(attrdefGenCat), llvm::cl::CommaSeparated); 905 906 static mlir::GenRegistration 907 genAttrDefs("gen-attrdef-defs", "Generate AttrDef definitions", 908 [](const llvm::RecordKeeper &records, raw_ostream &os) { 909 AttrDefGenerator generator(records, os); 910 return generator.emitDefs(attrDialect); 911 }); 912 static mlir::GenRegistration 913 genAttrDecls("gen-attrdef-decls", "Generate AttrDef declarations", 914 [](const llvm::RecordKeeper &records, raw_ostream &os) { 915 AttrDefGenerator generator(records, os); 916 return generator.emitDecls(attrDialect); 917 }); 918 919 //===----------------------------------------------------------------------===// 920 // TypeDef 921 922 static llvm::cl::OptionCategory typedefGenCat("Options for -gen-typedef-*"); 923 static llvm::cl::opt<std::string> 924 typeDialect("typedefs-dialect", 925 llvm::cl::desc("Generate types for this dialect"), 926 llvm::cl::cat(typedefGenCat), llvm::cl::CommaSeparated); 927 928 static mlir::GenRegistration 929 genTypeDefs("gen-typedef-defs", "Generate TypeDef definitions", 930 [](const llvm::RecordKeeper &records, raw_ostream &os) { 931 TypeDefGenerator generator(records, os); 932 return generator.emitDefs(typeDialect); 933 }); 934 static mlir::GenRegistration 935 genTypeDecls("gen-typedef-decls", "Generate TypeDef declarations", 936 [](const llvm::RecordKeeper &records, raw_ostream &os) { 937 TypeDefGenerator generator(records, os); 938 return generator.emitDecls(typeDialect); 939 }); 940