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 StringRef returnType = def.getCppClassName(); 352 if (Optional<StringRef> builderReturnType = builder.getReturnType()) 353 returnType = *builderReturnType; 354 Method *m = defCls.addMethod(returnType, "get", props, 355 getCustomBuilderParams({}, builder)); 356 if (!builder.getBody()) 357 return; 358 359 // Format the body and emit it. 360 FmtContext ctx; 361 ctx.addSubst("_get", "Base::get"); 362 if (!builder.hasInferredContextParameter()) 363 ctx.addSubst("_ctxt", "context"); 364 std::string bodyStr = tgfmt(*builder.getBody(), &ctx); 365 m->body().indent().getStream().printReindented(bodyStr); 366 } 367 368 /// Replace all instances of 'from' to 'to' in `str` and return the new string. 369 static std::string replaceInStr(std::string str, StringRef from, StringRef to) { 370 size_t pos = 0; 371 while ((pos = str.find(from.data(), pos, from.size())) != std::string::npos) 372 str.replace(pos, from.size(), to.data(), to.size()); 373 return str; 374 } 375 376 void DefGen::emitCheckedCustomBuilder(const AttrOrTypeBuilder &builder) { 377 // Don't emit a body if there isn't one. 378 auto props = builder.getBody() ? Method::Static : Method::StaticDeclaration; 379 StringRef returnType = def.getCppClassName(); 380 if (Optional<StringRef> builderReturnType = builder.getReturnType()) 381 returnType = *builderReturnType; 382 Method *m = defCls.addMethod( 383 returnType, "getChecked", props, 384 getCustomBuilderParams( 385 {{"::llvm::function_ref<::mlir::InFlightDiagnostic()>", "emitError"}}, 386 builder)); 387 if (!builder.getBody()) 388 return; 389 390 // Format the body and emit it. Replace $_get(...) with 391 // Base::getChecked(emitError, ...) 392 FmtContext ctx; 393 if (!builder.hasInferredContextParameter()) 394 ctx.addSubst("_ctxt", "context"); 395 std::string bodyStr = replaceInStr(builder.getBody()->str(), "$_get(", 396 "Base::getChecked(emitError, "); 397 bodyStr = tgfmt(bodyStr, &ctx); 398 m->body().indent().getStream().printReindented(bodyStr); 399 } 400 401 //===----------------------------------------------------------------------===// 402 // Interface Method Emission 403 404 void DefGen::emitTraitMethods(const InterfaceTrait &trait) { 405 // Get the set of methods that should always be declared. 406 auto alwaysDeclaredMethods = trait.getAlwaysDeclaredMethods(); 407 StringSet<> alwaysDeclared; 408 alwaysDeclared.insert(alwaysDeclaredMethods.begin(), 409 alwaysDeclaredMethods.end()); 410 411 Interface iface = trait.getInterface(); // causes strange bugs if elided 412 for (auto &method : iface.getMethods()) { 413 // Don't declare if the method has a body. Or if the method has a default 414 // implementation and the def didn't request that it always be declared. 415 if (method.getBody() || (method.getDefaultImplementation() && 416 !alwaysDeclared.count(method.getName()))) 417 continue; 418 emitTraitMethod(method); 419 } 420 } 421 422 void DefGen::emitTraitMethod(const InterfaceMethod &method) { 423 // All interface methods are declaration-only. 424 auto props = 425 method.isStatic() ? Method::StaticDeclaration : Method::ConstDeclaration; 426 SmallVector<MethodParameter> params; 427 for (auto ¶m : method.getArguments()) 428 params.emplace_back(param.type, param.name); 429 defCls.addMethod(method.getReturnType(), method.getName(), props, 430 std::move(params)); 431 } 432 433 //===----------------------------------------------------------------------===// 434 // Storage Class Emission 435 436 void DefGen::emitStorageConstructor() { 437 Constructor *ctor = 438 storageCls->addConstructor<Method::Inline>(getBuilderParams({})); 439 if (auto *attrDef = dyn_cast<AttrDef>(&def)) { 440 // For attributes, a parameter marked with AttributeSelfTypeParameter is 441 // the type initializer that must be passed to the parent constructor. 442 const auto isSelfType = [](const AttrOrTypeParameter ¶m) { 443 return isa<AttributeSelfTypeParameter>(param); 444 }; 445 auto *selfTypeParam = llvm::find_if(params, isSelfType); 446 if (std::count_if(selfTypeParam, params.end(), isSelfType) > 1) { 447 PrintFatalError(def.getLoc(), 448 "Only one attribute parameter can be marked as " 449 "AttributeSelfTypeParameter"); 450 } 451 // Alternatively, if a type builder was specified, use that instead. 452 std::string attrStorageInit = 453 selfTypeParam == params.end() ? "" : selfTypeParam->getName().str(); 454 if (attrDef->getTypeBuilder()) { 455 FmtContext ctx; 456 for (auto ¶m : params) 457 ctx.addSubst(strfmt("_{0}", param.getName()), param.getName()); 458 attrStorageInit = tgfmt(*attrDef->getTypeBuilder(), &ctx); 459 } 460 ctor->addMemberInitializer("::mlir::AttributeStorage", 461 std::move(attrStorageInit)); 462 // Initialize members that aren't the attribute's type. 463 for (auto ¶m : params) 464 if (selfTypeParam == params.end() || *selfTypeParam != param) 465 ctor->addMemberInitializer(param.getName(), param.getName()); 466 } else { 467 for (auto ¶m : params) 468 ctor->addMemberInitializer(param.getName(), param.getName()); 469 } 470 } 471 472 void DefGen::emitKeyType() { 473 std::string keyType("std::tuple<"); 474 llvm::raw_string_ostream os(keyType); 475 llvm::interleaveComma(params, os, 476 [&](auto ¶m) { os << param.getCppType(); }); 477 os << '>'; 478 storageCls->declare<UsingDeclaration>("KeyTy", std::move(os.str())); 479 } 480 481 void DefGen::emitEquals() { 482 Method *eq = storageCls->addConstMethod<Method::Inline>( 483 "bool", "operator==", MethodParameter("const KeyTy &", "tblgenKey")); 484 auto &body = eq->body().indent(); 485 auto scope = body.scope("return (", ");"); 486 const auto eachFn = [&](auto it) { 487 FmtContext ctx({{"_lhs", isa<AttributeSelfTypeParameter>(it.value()) 488 ? "getType()" 489 : it.value().getName()}, 490 {"_rhs", strfmt("std::get<{0}>(tblgenKey)", it.index())}}); 491 body << tgfmt(it.value().getComparator(), &ctx); 492 }; 493 llvm::interleave(llvm::enumerate(params), body, eachFn, ") && ("); 494 } 495 496 void DefGen::emitHashKey() { 497 Method *hash = storageCls->addStaticInlineMethod( 498 "::llvm::hash_code", "hashKey", 499 MethodParameter("const KeyTy &", "tblgenKey")); 500 auto &body = hash->body().indent(); 501 auto scope = body.scope("return ::llvm::hash_combine(", ");"); 502 llvm::interleaveComma(llvm::enumerate(params), body, [&](auto it) { 503 body << llvm::formatv("std::get<{0}>(tblgenKey)", it.index()); 504 }); 505 } 506 507 void DefGen::emitConstruct() { 508 Method *construct = storageCls->addMethod<Method::Inline>( 509 strfmt("{0} *", def.getStorageClassName()), "construct", 510 def.hasStorageCustomConstructor() ? Method::StaticDeclaration 511 : Method::Static, 512 MethodParameter(strfmt("::mlir::{0}StorageAllocator &", valueType), 513 "allocator"), 514 MethodParameter("const KeyTy &", "tblgenKey")); 515 if (!def.hasStorageCustomConstructor()) { 516 auto &body = construct->body().indent(); 517 for (const auto &it : llvm::enumerate(params)) { 518 body << formatv("auto {0} = std::get<{1}>(tblgenKey);\n", 519 it.value().getName(), it.index()); 520 } 521 // Use the parameters' custom allocator code, if provided. 522 FmtContext ctx = FmtContext().addSubst("_allocator", "allocator"); 523 for (auto ¶m : params) { 524 if (Optional<StringRef> allocCode = param.getAllocator()) { 525 ctx.withSelf(param.getName()).addSubst("_dst", param.getName()); 526 body << tgfmt(*allocCode, &ctx) << '\n'; 527 } 528 } 529 auto scope = 530 body.scope(strfmt("return new (allocator.allocate<{0}>()) {0}(", 531 def.getStorageClassName()), 532 ");"); 533 llvm::interleaveComma(params, body, 534 [&](auto ¶m) { body << param.getName(); }); 535 } 536 } 537 538 void DefGen::emitStorageClass() { 539 // Add the appropriate parent class. 540 storageCls->addParent(strfmt("::mlir::{0}Storage", valueType)); 541 // Add the constructor. 542 emitStorageConstructor(); 543 // Declare the key type. 544 emitKeyType(); 545 // Add the comparison method. 546 emitEquals(); 547 // Emit the key hash method. 548 emitHashKey(); 549 // Emit the storage constructor. Just declare it if the user wants to define 550 // it themself. 551 emitConstruct(); 552 // Emit the storage class members as public, at the very end of the struct. 553 storageCls->finalize(); 554 for (auto ¶m : params) 555 if (!isa<AttributeSelfTypeParameter>(param)) 556 storageCls->declare<Field>(param.getCppType(), param.getName()); 557 } 558 559 //===----------------------------------------------------------------------===// 560 // DefGenerator 561 //===----------------------------------------------------------------------===// 562 563 namespace { 564 /// This struct is the base generator used when processing tablegen interfaces. 565 class DefGenerator { 566 public: 567 bool emitDecls(StringRef selectedDialect); 568 bool emitDefs(StringRef selectedDialect); 569 570 protected: 571 DefGenerator(std::vector<llvm::Record *> &&defs, raw_ostream &os, 572 StringRef defType, StringRef valueType, bool isAttrGenerator) 573 : defRecords(std::move(defs)), os(os), defType(defType), 574 valueType(valueType), isAttrGenerator(isAttrGenerator) {} 575 576 /// Emit the list of def type names. 577 void emitTypeDefList(ArrayRef<AttrOrTypeDef> defs); 578 /// Emit the code to dispatch between different defs during parsing/printing. 579 void emitParsePrintDispatch(ArrayRef<AttrOrTypeDef> defs); 580 581 /// The set of def records to emit. 582 std::vector<llvm::Record *> defRecords; 583 /// The attribute or type class to emit. 584 /// The stream to emit to. 585 raw_ostream &os; 586 /// The prefix of the tablegen def name, e.g. Attr or Type. 587 StringRef defType; 588 /// The C++ base value type of the def, e.g. Attribute or Type. 589 StringRef valueType; 590 /// Flag indicating if this generator is for Attributes. False if the 591 /// generator is for types. 592 bool isAttrGenerator; 593 }; 594 595 /// A specialized generator for AttrDefs. 596 struct AttrDefGenerator : public DefGenerator { 597 AttrDefGenerator(const llvm::RecordKeeper &records, raw_ostream &os) 598 : DefGenerator(records.getAllDerivedDefinitionsIfDefined("AttrDef"), os, 599 "Attr", "Attribute", /*isAttrGenerator=*/true) {} 600 }; 601 /// A specialized generator for TypeDefs. 602 struct TypeDefGenerator : public DefGenerator { 603 TypeDefGenerator(const llvm::RecordKeeper &records, raw_ostream &os) 604 : DefGenerator(records.getAllDerivedDefinitionsIfDefined("TypeDef"), os, 605 "Type", "Type", /*isAttrGenerator=*/false) {} 606 }; 607 } // namespace 608 609 //===----------------------------------------------------------------------===// 610 // GEN: Declarations 611 //===----------------------------------------------------------------------===// 612 613 /// Print this above all the other declarations. Contains type declarations used 614 /// later on. 615 static const char *const typeDefDeclHeader = R"( 616 namespace mlir { 617 class AsmParser; 618 class AsmPrinter; 619 } // namespace mlir 620 )"; 621 622 bool DefGenerator::emitDecls(StringRef selectedDialect) { 623 emitSourceFileHeader((defType + "Def Declarations").str(), os); 624 IfDefScope scope("GET_" + defType.upper() + "DEF_CLASSES", os); 625 626 // Output the common "header". 627 os << typeDefDeclHeader; 628 629 SmallVector<AttrOrTypeDef, 16> defs; 630 collectAllDefs(selectedDialect, defRecords, defs); 631 if (defs.empty()) 632 return false; 633 { 634 NamespaceEmitter nsEmitter(os, defs.front().getDialect()); 635 636 // Declare all the def classes first (in case they reference each other). 637 for (const AttrOrTypeDef &def : defs) 638 os << "class " << def.getCppClassName() << ";\n"; 639 640 // Emit the declarations. 641 for (const AttrOrTypeDef &def : defs) 642 DefGen(def).emitDecl(os); 643 } 644 // Emit the TypeID explicit specializations to have a single definition for 645 // each of these. 646 for (const AttrOrTypeDef &def : defs) 647 if (!def.getDialect().getCppNamespace().empty()) 648 os << "MLIR_DECLARE_EXPLICIT_TYPE_ID(" 649 << def.getDialect().getCppNamespace() << "::" << def.getCppClassName() 650 << ")\n"; 651 652 return false; 653 } 654 655 //===----------------------------------------------------------------------===// 656 // GEN: Def List 657 //===----------------------------------------------------------------------===// 658 659 void DefGenerator::emitTypeDefList(ArrayRef<AttrOrTypeDef> defs) { 660 IfDefScope scope("GET_" + defType.upper() + "DEF_LIST", os); 661 auto interleaveFn = [&](const AttrOrTypeDef &def) { 662 os << def.getDialect().getCppNamespace() << "::" << def.getCppClassName(); 663 }; 664 llvm::interleave(defs, os, interleaveFn, ",\n"); 665 os << "\n"; 666 } 667 668 //===----------------------------------------------------------------------===// 669 // GEN: Definitions 670 //===----------------------------------------------------------------------===// 671 672 /// The code block for default attribute parser/printer dispatch boilerplate. 673 /// {0}: the dialect fully qualified class name. 674 /// {1}: the optional code for the dynamic attribute parser dispatch. 675 /// {2}: the optional code for the dynamic attribute printer dispatch. 676 static const char *const dialectDefaultAttrPrinterParserDispatch = R"( 677 /// Parse an attribute registered to this dialect. 678 ::mlir::Attribute {0}::parseAttribute(::mlir::DialectAsmParser &parser, 679 ::mlir::Type type) const {{ 680 ::llvm::SMLoc typeLoc = parser.getCurrentLocation(); 681 ::llvm::StringRef attrTag; 682 {{ 683 ::mlir::Attribute attr; 684 auto parseResult = generatedAttributeParser(parser, &attrTag, type, attr); 685 if (parseResult.hasValue()) 686 return attr; 687 } 688 {1} 689 parser.emitError(typeLoc) << "unknown attribute `" 690 << attrTag << "` in dialect `" << getNamespace() << "`"; 691 return {{}; 692 } 693 /// Print an attribute registered to this dialect. 694 void {0}::printAttribute(::mlir::Attribute attr, 695 ::mlir::DialectAsmPrinter &printer) const {{ 696 if (::mlir::succeeded(generatedAttributePrinter(attr, printer))) 697 return; 698 {2} 699 } 700 )"; 701 702 /// The code block for dynamic attribute parser dispatch boilerplate. 703 static const char *const dialectDynamicAttrParserDispatch = R"( 704 { 705 ::mlir::Attribute genAttr; 706 auto parseResult = parseOptionalDynamicAttr(attrTag, parser, genAttr); 707 if (parseResult.hasValue()) { 708 if (::mlir::succeeded(parseResult.getValue())) 709 return genAttr; 710 return Attribute(); 711 } 712 } 713 )"; 714 715 /// The code block for dynamic type printer dispatch boilerplate. 716 static const char *const dialectDynamicAttrPrinterDispatch = R"( 717 if (::mlir::succeeded(printIfDynamicAttr(attr, printer))) 718 return; 719 )"; 720 721 /// The code block for default type parser/printer dispatch boilerplate. 722 /// {0}: the dialect fully qualified class name. 723 /// {1}: the optional code for the dynamic type parser dispatch. 724 /// {2}: the optional code for the dynamic type printer dispatch. 725 static const char *const dialectDefaultTypePrinterParserDispatch = R"( 726 /// Parse a type registered to this dialect. 727 ::mlir::Type {0}::parseType(::mlir::DialectAsmParser &parser) const {{ 728 ::llvm::SMLoc typeLoc = parser.getCurrentLocation(); 729 ::llvm::StringRef mnemonic; 730 ::mlir::Type genType; 731 auto parseResult = generatedTypeParser(parser, &mnemonic, genType); 732 if (parseResult.hasValue()) 733 return genType; 734 {1} 735 parser.emitError(typeLoc) << "unknown type `" 736 << mnemonic << "` in dialect `" << getNamespace() << "`"; 737 return {{}; 738 } 739 /// Print a type registered to this dialect. 740 void {0}::printType(::mlir::Type type, 741 ::mlir::DialectAsmPrinter &printer) const {{ 742 if (::mlir::succeeded(generatedTypePrinter(type, printer))) 743 return; 744 {2} 745 } 746 )"; 747 748 /// The code block for dynamic type parser dispatch boilerplate. 749 static const char *const dialectDynamicTypeParserDispatch = R"( 750 { 751 auto parseResult = parseOptionalDynamicType(mnemonic, parser, genType); 752 if (parseResult.hasValue()) { 753 if (::mlir::succeeded(parseResult.getValue())) 754 return genType; 755 return Type(); 756 } 757 } 758 )"; 759 760 /// The code block for dynamic type printer dispatch boilerplate. 761 static const char *const dialectDynamicTypePrinterDispatch = R"( 762 if (::mlir::succeeded(printIfDynamicType(type, printer))) 763 return; 764 )"; 765 766 /// Emit the dialect printer/parser dispatcher. User's code should call these 767 /// functions from their dialect's print/parse methods. 768 void DefGenerator::emitParsePrintDispatch(ArrayRef<AttrOrTypeDef> defs) { 769 if (llvm::none_of(defs, [](const AttrOrTypeDef &def) { 770 return def.getMnemonic().has_value(); 771 })) { 772 return; 773 } 774 // Declare the parser. 775 SmallVector<MethodParameter> params = {{"::mlir::AsmParser &", "parser"}, 776 {"::llvm::StringRef *", "mnemonic"}}; 777 if (isAttrGenerator) 778 params.emplace_back("::mlir::Type", "type"); 779 params.emplace_back(strfmt("::mlir::{0} &", valueType), "value"); 780 Method parse("::mlir::OptionalParseResult", 781 strfmt("generated{0}Parser", valueType), Method::StaticInline, 782 std::move(params)); 783 // Declare the printer. 784 Method printer("::mlir::LogicalResult", 785 strfmt("generated{0}Printer", valueType), Method::StaticInline, 786 {{strfmt("::mlir::{0}", valueType), "def"}, 787 {"::mlir::AsmPrinter &", "printer"}}); 788 789 // The parser dispatch uses a KeywordSwitch, matching on the mnemonic and 790 // calling the def's parse function. 791 parse.body() << " return " 792 "::mlir::AsmParser::KeywordSwitch<::mlir::" 793 "OptionalParseResult>(parser)\n"; 794 const char *const getValueForMnemonic = 795 R"( .Case({0}::getMnemonic(), [&](llvm::StringRef, llvm::SMLoc) {{ 796 value = {0}::{1}; 797 return ::mlir::success(!!value); 798 }) 799 )"; 800 801 // The printer dispatch uses llvm::TypeSwitch to find and call the correct 802 // printer. 803 printer.body() << " return ::llvm::TypeSwitch<::mlir::" << valueType 804 << ", ::mlir::LogicalResult>(def)"; 805 const char *const printValue = R"( .Case<{0}>([&](auto t) {{ 806 printer << {0}::getMnemonic();{1} 807 return ::mlir::success(); 808 }) 809 )"; 810 for (auto &def : defs) { 811 if (!def.getMnemonic()) 812 continue; 813 bool hasParserPrinterDecl = 814 def.hasCustomAssemblyFormat() || def.getAssemblyFormat(); 815 std::string defClass = strfmt( 816 "{0}::{1}", def.getDialect().getCppNamespace(), def.getCppClassName()); 817 818 // If the def has no parameters or parser code, invoke a normal `get`. 819 std::string parseOrGet = 820 hasParserPrinterDecl 821 ? strfmt("parse(parser{0})", isAttrGenerator ? ", type" : "") 822 : "get(parser.getContext())"; 823 parse.body() << llvm::formatv(getValueForMnemonic, defClass, parseOrGet); 824 825 // If the def has no parameters and no printer, just print the mnemonic. 826 StringRef printDef = ""; 827 if (hasParserPrinterDecl) 828 printDef = "\nt.print(printer);"; 829 printer.body() << llvm::formatv(printValue, defClass, printDef); 830 } 831 parse.body() << " .Default([&](llvm::StringRef keyword, llvm::SMLoc) {\n" 832 " *mnemonic = keyword;\n" 833 " return llvm::None;\n" 834 " });"; 835 printer.body() << " .Default([](auto) { return ::mlir::failure(); });"; 836 837 raw_indented_ostream indentedOs(os); 838 parse.writeDeclTo(indentedOs); 839 printer.writeDeclTo(indentedOs); 840 } 841 842 bool DefGenerator::emitDefs(StringRef selectedDialect) { 843 emitSourceFileHeader((defType + "Def Definitions").str(), os); 844 845 SmallVector<AttrOrTypeDef, 16> defs; 846 collectAllDefs(selectedDialect, defRecords, defs); 847 if (defs.empty()) 848 return false; 849 emitTypeDefList(defs); 850 851 IfDefScope scope("GET_" + defType.upper() + "DEF_CLASSES", os); 852 emitParsePrintDispatch(defs); 853 for (const AttrOrTypeDef &def : defs) { 854 { 855 NamespaceEmitter ns(os, def.getDialect()); 856 DefGen gen(def); 857 gen.emitDef(os); 858 } 859 // Emit the TypeID explicit specializations to have a single symbol def. 860 if (!def.getDialect().getCppNamespace().empty()) 861 os << "MLIR_DEFINE_EXPLICIT_TYPE_ID(" 862 << def.getDialect().getCppNamespace() << "::" << def.getCppClassName() 863 << ")\n"; 864 } 865 866 Dialect firstDialect = defs.front().getDialect(); 867 868 // Emit the default parser/printer for Attributes if the dialect asked for it. 869 if (isAttrGenerator && firstDialect.useDefaultAttributePrinterParser()) { 870 NamespaceEmitter nsEmitter(os, firstDialect); 871 if (firstDialect.isExtensible()) { 872 os << llvm::formatv(dialectDefaultAttrPrinterParserDispatch, 873 firstDialect.getCppClassName(), 874 dialectDynamicAttrParserDispatch, 875 dialectDynamicAttrPrinterDispatch); 876 } else { 877 os << llvm::formatv(dialectDefaultAttrPrinterParserDispatch, 878 firstDialect.getCppClassName(), "", ""); 879 } 880 } 881 882 // Emit the default parser/printer for Types if the dialect asked for it. 883 if (!isAttrGenerator && firstDialect.useDefaultTypePrinterParser()) { 884 NamespaceEmitter nsEmitter(os, firstDialect); 885 if (firstDialect.isExtensible()) { 886 os << llvm::formatv(dialectDefaultTypePrinterParserDispatch, 887 firstDialect.getCppClassName(), 888 dialectDynamicTypeParserDispatch, 889 dialectDynamicTypePrinterDispatch); 890 } else { 891 os << llvm::formatv(dialectDefaultTypePrinterParserDispatch, 892 firstDialect.getCppClassName(), "", ""); 893 } 894 } 895 896 return false; 897 } 898 899 //===----------------------------------------------------------------------===// 900 // GEN: Registration hooks 901 //===----------------------------------------------------------------------===// 902 903 //===----------------------------------------------------------------------===// 904 // AttrDef 905 906 static llvm::cl::OptionCategory attrdefGenCat("Options for -gen-attrdef-*"); 907 static llvm::cl::opt<std::string> 908 attrDialect("attrdefs-dialect", 909 llvm::cl::desc("Generate attributes for this dialect"), 910 llvm::cl::cat(attrdefGenCat), llvm::cl::CommaSeparated); 911 912 static mlir::GenRegistration 913 genAttrDefs("gen-attrdef-defs", "Generate AttrDef definitions", 914 [](const llvm::RecordKeeper &records, raw_ostream &os) { 915 AttrDefGenerator generator(records, os); 916 return generator.emitDefs(attrDialect); 917 }); 918 static mlir::GenRegistration 919 genAttrDecls("gen-attrdef-decls", "Generate AttrDef declarations", 920 [](const llvm::RecordKeeper &records, raw_ostream &os) { 921 AttrDefGenerator generator(records, os); 922 return generator.emitDecls(attrDialect); 923 }); 924 925 //===----------------------------------------------------------------------===// 926 // TypeDef 927 928 static llvm::cl::OptionCategory typedefGenCat("Options for -gen-typedef-*"); 929 static llvm::cl::opt<std::string> 930 typeDialect("typedefs-dialect", 931 llvm::cl::desc("Generate types for this dialect"), 932 llvm::cl::cat(typedefGenCat), llvm::cl::CommaSeparated); 933 934 static mlir::GenRegistration 935 genTypeDefs("gen-typedef-defs", "Generate TypeDef definitions", 936 [](const llvm::RecordKeeper &records, raw_ostream &os) { 937 TypeDefGenerator generator(records, os); 938 return generator.emitDefs(typeDialect); 939 }); 940 static mlir::GenRegistration 941 genTypeDecls("gen-typedef-decls", "Generate TypeDef declarations", 942 [](const llvm::RecordKeeper &records, raw_ostream &os) { 943 TypeDefGenerator generator(records, os); 944 return generator.emitDecls(typeDialect); 945 }); 946