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