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