1 //===- OpDefinitionsGen.cpp - MLIR op 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 // OpDefinitionsGen uses the description of operations to generate C++ 10 // definitions for ops. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "OpFormatGen.h" 15 #include "OpGenHelpers.h" 16 #include "mlir/TableGen/CodeGenHelpers.h" 17 #include "mlir/TableGen/Format.h" 18 #include "mlir/TableGen/GenInfo.h" 19 #include "mlir/TableGen/Interfaces.h" 20 #include "mlir/TableGen/OpClass.h" 21 #include "mlir/TableGen/Operator.h" 22 #include "mlir/TableGen/SideEffects.h" 23 #include "mlir/TableGen/Trait.h" 24 #include "llvm/ADT/MapVector.h" 25 #include "llvm/ADT/Sequence.h" 26 #include "llvm/ADT/StringExtras.h" 27 #include "llvm/Support/Signals.h" 28 #include "llvm/TableGen/Error.h" 29 #include "llvm/TableGen/Record.h" 30 #include "llvm/TableGen/TableGenBackend.h" 31 32 #define DEBUG_TYPE "mlir-tblgen-opdefgen" 33 34 using namespace llvm; 35 using namespace mlir; 36 using namespace mlir::tblgen; 37 38 static const char *const tblgenNamePrefix = "tblgen_"; 39 static const char *const generatedArgName = "odsArg"; 40 static const char *const odsBuilder = "odsBuilder"; 41 static const char *const builderOpState = "odsState"; 42 43 // The logic to calculate the actual value range for a declared operand/result 44 // of an op with variadic operands/results. Note that this logic is not for 45 // general use; it assumes all variadic operands/results must have the same 46 // number of values. 47 // 48 // {0}: The list of whether each declared operand/result is variadic. 49 // {1}: The total number of non-variadic operands/results. 50 // {2}: The total number of variadic operands/results. 51 // {3}: The total number of actual values. 52 // {4}: "operand" or "result". 53 const char *sameVariadicSizeValueRangeCalcCode = R"( 54 bool isVariadic[] = {{{0}}; 55 int prevVariadicCount = 0; 56 for (unsigned i = 0; i < index; ++i) 57 if (isVariadic[i]) ++prevVariadicCount; 58 59 // Calculate how many dynamic values a static variadic {4} corresponds to. 60 // This assumes all static variadic {4}s have the same dynamic value count. 61 int variadicSize = ({3} - {1}) / {2}; 62 // `index` passed in as the parameter is the static index which counts each 63 // {4} (variadic or not) as size 1. So here for each previous static variadic 64 // {4}, we need to offset by (variadicSize - 1) to get where the dynamic 65 // value pack for this static {4} starts. 66 int start = index + (variadicSize - 1) * prevVariadicCount; 67 int size = isVariadic[index] ? variadicSize : 1; 68 return {{start, size}; 69 )"; 70 71 // The logic to calculate the actual value range for a declared operand/result 72 // of an op with variadic operands/results. Note that this logic is assumes 73 // the op has an attribute specifying the size of each operand/result segment 74 // (variadic or not). 75 // 76 // {0}: The name of the attribute specifying the segment sizes. 77 const char *adapterSegmentSizeAttrInitCode = R"( 78 assert(odsAttrs && "missing segment size attribute for op"); 79 auto sizeAttr = odsAttrs.get("{0}").cast<::mlir::DenseIntElementsAttr>(); 80 )"; 81 const char *opSegmentSizeAttrInitCode = R"( 82 auto sizeAttr = (*this)->getAttr({0}).cast<::mlir::DenseIntElementsAttr>(); 83 )"; 84 const char *attrSizedSegmentValueRangeCalcCode = R"( 85 auto sizeAttrValues = sizeAttr.getValues<uint32_t>(); 86 unsigned start = 0; 87 for (unsigned i = 0; i < index; ++i) 88 start += *(sizeAttrValues.begin() + i); 89 unsigned size = *(sizeAttrValues.begin() + index); 90 return {start, size}; 91 )"; 92 93 // The logic to build a range of either operand or result values. 94 // 95 // {0}: The begin iterator of the actual values. 96 // {1}: The call to generate the start and length of the value range. 97 const char *valueRangeReturnCode = R"( 98 auto valueRange = {1}; 99 return {{std::next({0}, valueRange.first), 100 std::next({0}, valueRange.first + valueRange.second)}; 101 )"; 102 103 const char *typeVerifierSignature = 104 "static ::mlir::LogicalResult {0}(::mlir::Operation *op, ::mlir::Type " 105 "type, ::llvm::StringRef valueKind, unsigned valueGroupStartIndex)"; 106 107 const char *typeVerifierErrorHandler = 108 " op->emitOpError(valueKind) << \" #\" << valueGroupStartIndex << \" must " 109 "be {0}, but got \" << type"; 110 111 static const char *const opCommentHeader = R"( 112 //===----------------------------------------------------------------------===// 113 // {0} {1} 114 //===----------------------------------------------------------------------===// 115 116 )"; 117 118 //===----------------------------------------------------------------------===// 119 // Utility structs and functions 120 //===----------------------------------------------------------------------===// 121 122 // Replaces all occurrences of `match` in `str` with `substitute`. 123 static std::string replaceAllSubstrs(std::string str, const std::string &match, 124 const std::string &substitute) { 125 std::string::size_type scanLoc = 0, matchLoc = std::string::npos; 126 while ((matchLoc = str.find(match, scanLoc)) != std::string::npos) { 127 str = str.replace(matchLoc, match.size(), substitute); 128 scanLoc = matchLoc + substitute.size(); 129 } 130 return str; 131 } 132 133 // Returns whether the record has a value of the given name that can be returned 134 // via getValueAsString. 135 static inline bool hasStringAttribute(const Record &record, 136 StringRef fieldName) { 137 auto valueInit = record.getValueInit(fieldName); 138 return isa<StringInit>(valueInit); 139 } 140 141 static std::string getArgumentName(const Operator &op, int index) { 142 const auto &operand = op.getOperand(index); 143 if (!operand.name.empty()) 144 return std::string(operand.name); 145 else 146 return std::string(formatv("{0}_{1}", generatedArgName, index)); 147 } 148 149 // Returns true if we can use unwrapped value for the given `attr` in builders. 150 static bool canUseUnwrappedRawValue(const tblgen::Attribute &attr) { 151 return attr.getReturnType() != attr.getStorageType() && 152 // We need to wrap the raw value into an attribute in the builder impl 153 // so we need to make sure that the attribute specifies how to do that. 154 !attr.getConstBuilderTemplate().empty(); 155 } 156 157 //===----------------------------------------------------------------------===// 158 // Op emitter 159 //===----------------------------------------------------------------------===// 160 161 namespace { 162 // Helper class to emit a record into the given output stream. 163 class OpEmitter { 164 public: 165 static void 166 emitDecl(const Operator &op, raw_ostream &os, 167 const StaticVerifierFunctionEmitter &staticVerifierEmitter); 168 static void 169 emitDef(const Operator &op, raw_ostream &os, 170 const StaticVerifierFunctionEmitter &staticVerifierEmitter); 171 172 private: 173 OpEmitter(const Operator &op, 174 const StaticVerifierFunctionEmitter &staticVerifierEmitter); 175 176 void emitDecl(raw_ostream &os); 177 void emitDef(raw_ostream &os); 178 179 // Generate methods for accessing the attribute names of this operation. 180 void genAttrNameGetters(); 181 182 // Generates the OpAsmOpInterface for this operation if possible. 183 void genOpAsmInterface(); 184 185 // Generates the `getOperationName` method for this op. 186 void genOpNameGetter(); 187 188 // Generates getters for the attributes. 189 void genAttrGetters(); 190 191 // Generates setter for the attributes. 192 void genAttrSetters(); 193 194 // Generates removers for optional attributes. 195 void genOptionalAttrRemovers(); 196 197 // Generates getters for named operands. 198 void genNamedOperandGetters(); 199 200 // Generates setters for named operands. 201 void genNamedOperandSetters(); 202 203 // Generates getters for named results. 204 void genNamedResultGetters(); 205 206 // Generates getters for named regions. 207 void genNamedRegionGetters(); 208 209 // Generates getters for named successors. 210 void genNamedSuccessorGetters(); 211 212 // Generates builder methods for the operation. 213 void genBuilder(); 214 215 // Generates the build() method that takes each operand/attribute 216 // as a stand-alone parameter. 217 void genSeparateArgParamBuilder(); 218 219 // Generates the build() method that takes each operand/attribute as a 220 // stand-alone parameter. The generated build() method uses first operand's 221 // type as all results' types. 222 void genUseOperandAsResultTypeSeparateParamBuilder(); 223 224 // Generates the build() method that takes all operands/attributes 225 // collectively as one parameter. The generated build() method uses first 226 // operand's type as all results' types. 227 void genUseOperandAsResultTypeCollectiveParamBuilder(); 228 229 // Generates the build() method that takes aggregate operands/attributes 230 // parameters. This build() method uses inferred types as result types. 231 // Requires: The type needs to be inferable via InferTypeOpInterface. 232 void genInferredTypeCollectiveParamBuilder(); 233 234 // Generates the build() method that takes each operand/attribute as a 235 // stand-alone parameter. The generated build() method uses first attribute's 236 // type as all result's types. 237 void genUseAttrAsResultTypeBuilder(); 238 239 // Generates the build() method that takes all result types collectively as 240 // one parameter. Similarly for operands and attributes. 241 void genCollectiveParamBuilder(); 242 243 // The kind of parameter to generate for result types in builders. 244 enum class TypeParamKind { 245 None, // No result type in parameter list. 246 Separate, // A separate parameter for each result type. 247 Collective, // An ArrayRef<Type> for all result types. 248 }; 249 250 // The kind of parameter to generate for attributes in builders. 251 enum class AttrParamKind { 252 WrappedAttr, // A wrapped MLIR Attribute instance. 253 UnwrappedValue, // A raw value without MLIR Attribute wrapper. 254 }; 255 256 // Builds the parameter list for build() method of this op. This method writes 257 // to `paramList` the comma-separated parameter list and updates 258 // `resultTypeNames` with the names for parameters for specifying result 259 // types. The given `typeParamKind` and `attrParamKind` controls how result 260 // types and attributes are placed in the parameter list. 261 void buildParamList(llvm::SmallVectorImpl<OpMethodParameter> ¶mList, 262 SmallVectorImpl<std::string> &resultTypeNames, 263 TypeParamKind typeParamKind, 264 AttrParamKind attrParamKind = AttrParamKind::WrappedAttr); 265 266 // Adds op arguments and regions into operation state for build() methods. 267 void genCodeForAddingArgAndRegionForBuilder(OpMethodBody &body, 268 bool isRawValueAttr = false); 269 270 // Generates canonicalizer declaration for the operation. 271 void genCanonicalizerDecls(); 272 273 // Generates the folder declaration for the operation. 274 void genFolderDecls(); 275 276 // Generates the parser for the operation. 277 void genParser(); 278 279 // Generates the printer for the operation. 280 void genPrinter(); 281 282 // Generates verify method for the operation. 283 void genVerifier(); 284 285 // Generates verify statements for operands and results in the operation. 286 // The generated code will be attached to `body`. 287 void genOperandResultVerifier(OpMethodBody &body, 288 Operator::value_range values, 289 StringRef valueKind); 290 291 // Generates verify statements for regions in the operation. 292 // The generated code will be attached to `body`. 293 void genRegionVerifier(OpMethodBody &body); 294 295 // Generates verify statements for successors in the operation. 296 // The generated code will be attached to `body`. 297 void genSuccessorVerifier(OpMethodBody &body); 298 299 // Generates the traits used by the object. 300 void genTraits(); 301 302 // Generate the OpInterface methods for all interfaces. 303 void genOpInterfaceMethods(); 304 305 // Generate op interface methods for the given interface. 306 void genOpInterfaceMethods(const tblgen::InterfaceTrait *trait); 307 308 // Generate op interface method for the given interface method. If 309 // 'declaration' is true, generates a declaration, else a definition. 310 OpMethod *genOpInterfaceMethod(const tblgen::InterfaceMethod &method, 311 bool declaration = true); 312 313 // Generate the side effect interface methods. 314 void genSideEffectInterfaceMethods(); 315 316 // Generate the type inference interface methods. 317 void genTypeInterfaceMethods(); 318 319 private: 320 // The TableGen record for this op. 321 // TODO: OpEmitter should not have a Record directly, 322 // it should rather go through the Operator for better abstraction. 323 const Record &def; 324 325 // The wrapper operator class for querying information from this op. 326 Operator op; 327 328 // The C++ code builder for this op 329 OpClass opClass; 330 331 // The format context for verification code generation. 332 FmtContext verifyCtx; 333 334 // The emitter containing all of the locally emitted verification functions. 335 const StaticVerifierFunctionEmitter &staticVerifierEmitter; 336 337 // A map of attribute names (including implicit attributes) registered to the 338 // current operation, to the relative order in which they were registered. 339 llvm::MapVector<StringRef, unsigned> attributeNames; 340 }; 341 } // end anonymous namespace 342 343 // Populate the format context `ctx` with substitutions of attributes, operands 344 // and results. 345 // - attrGet corresponds to the name of the function to call to get value of 346 // attribute (the generated function call returns an Attribute); 347 // - operandGet corresponds to the name of the function with which to retrieve 348 // an operand (the generated function call returns an OperandRange); 349 // - resultGet corresponds to the name of the function to get an result (the 350 // generated function call returns a ValueRange); 351 static void populateSubstitutions(const Operator &op, const char *attrGet, 352 const char *operandGet, const char *resultGet, 353 FmtContext &ctx) { 354 // Populate substitutions for attributes and named operands. 355 for (const auto &namedAttr : op.getAttributes()) 356 ctx.addSubst(namedAttr.name, 357 formatv("{0}(\"{1}\")", attrGet, namedAttr.name)); 358 for (int i = 0, e = op.getNumOperands(); i < e; ++i) { 359 auto &value = op.getOperand(i); 360 if (value.name.empty()) 361 continue; 362 363 if (value.isVariadic()) 364 ctx.addSubst(value.name, formatv("{0}({1})", operandGet, i)); 365 else 366 ctx.addSubst(value.name, formatv("(*{0}({1}).begin())", operandGet, i)); 367 } 368 369 // Populate substitutions for results. 370 for (int i = 0, e = op.getNumResults(); i < e; ++i) { 371 auto &value = op.getResult(i); 372 if (value.name.empty()) 373 continue; 374 375 if (value.isVariadic()) 376 ctx.addSubst(value.name, formatv("{0}({1})", resultGet, i)); 377 else 378 ctx.addSubst(value.name, formatv("(*{0}({1}).begin())", resultGet, i)); 379 } 380 } 381 382 // Generate attribute verification. If emitVerificationRequiringOp is set then 383 // only verification for attributes whose value depend on op being known are 384 // emitted, else only verification that doesn't depend on the op being known are 385 // generated. 386 // - emitErrorPrefix is the prefix for the error emitting call which consists 387 // of the entire function call up to start of error message fragment; 388 // - emitVerificationRequiringOp specifies whether verification should be 389 // emitted for verification that require the op to exist; 390 static void genAttributeVerifier(const Operator &op, const char *attrGet, 391 const Twine &emitErrorPrefix, 392 bool emitVerificationRequiringOp, 393 FmtContext &ctx, OpMethodBody &body) { 394 for (const auto &namedAttr : op.getAttributes()) { 395 const auto &attr = namedAttr.attr; 396 if (attr.isDerivedAttr()) 397 continue; 398 399 auto attrName = namedAttr.name; 400 bool allowMissingAttr = attr.hasDefaultValue() || attr.isOptional(); 401 auto attrPred = attr.getPredicate(); 402 auto condition = attrPred.isNull() ? "" : attrPred.getCondition(); 403 // There is a condition to emit only if the use of $_op and whether to 404 // emit verifications for op matches. 405 bool hasConditionToEmit = (!(condition.find("$_op") != StringRef::npos) ^ 406 emitVerificationRequiringOp); 407 408 // Prefix with `tblgen_` to avoid hiding the attribute accessor. 409 auto varName = tblgenNamePrefix + attrName; 410 411 // If the attribute is 412 // 1. Required (not allowed missing) and not in op verification, or 413 // 2. Has a condition that will get verified 414 // then the variable will be used. 415 // 416 // Therefore, for optional attributes whose verification requires that an 417 // op already exists for verification/emitVerificationRequiringOp is set 418 // has nothing that can be verified here. 419 if ((allowMissingAttr || emitVerificationRequiringOp) && 420 !hasConditionToEmit) 421 continue; 422 423 body << formatv(" {\n auto {0} = {1}(\"{2}\");\n", varName, attrGet, 424 attrName); 425 426 if (!emitVerificationRequiringOp && !allowMissingAttr) { 427 body << " if (!" << varName << ") return " << emitErrorPrefix 428 << "\"requires attribute '" << attrName << "'\");\n"; 429 } 430 431 if (!hasConditionToEmit) { 432 body << " }\n"; 433 continue; 434 } 435 436 if (allowMissingAttr) { 437 // If the attribute has a default value, then only verify the predicate if 438 // set. This does effectively assume that the default value is valid. 439 // TODO: verify the debug value is valid (perhaps in debug mode only). 440 body << " if (" << varName << ") {\n"; 441 } 442 443 body << tgfmt(" if (!($0)) return $1\"attribute '$2' " 444 "failed to satisfy constraint: $3\");\n", 445 /*ctx=*/nullptr, tgfmt(condition, &ctx.withSelf(varName)), 446 emitErrorPrefix, attrName, attr.getSummary()); 447 if (allowMissingAttr) 448 body << " }\n"; 449 body << " }\n"; 450 } 451 } 452 453 OpEmitter::OpEmitter(const Operator &op, 454 const StaticVerifierFunctionEmitter &staticVerifierEmitter) 455 : def(op.getDef()), op(op), 456 opClass(op.getCppClassName(), op.getExtraClassDeclaration()), 457 staticVerifierEmitter(staticVerifierEmitter) { 458 verifyCtx.withOp("(*this->getOperation())"); 459 verifyCtx.addSubst("_ctxt", "this->getOperation()->getContext()"); 460 461 genTraits(); 462 463 // Generate C++ code for various op methods. The order here determines the 464 // methods in the generated file. 465 genAttrNameGetters(); 466 genOpAsmInterface(); 467 genOpNameGetter(); 468 genNamedOperandGetters(); 469 genNamedOperandSetters(); 470 genNamedResultGetters(); 471 genNamedRegionGetters(); 472 genNamedSuccessorGetters(); 473 genAttrGetters(); 474 genAttrSetters(); 475 genOptionalAttrRemovers(); 476 genBuilder(); 477 genParser(); 478 genPrinter(); 479 genVerifier(); 480 genCanonicalizerDecls(); 481 genFolderDecls(); 482 genTypeInterfaceMethods(); 483 genOpInterfaceMethods(); 484 generateOpFormat(op, opClass); 485 genSideEffectInterfaceMethods(); 486 } 487 void OpEmitter::emitDecl( 488 const Operator &op, raw_ostream &os, 489 const StaticVerifierFunctionEmitter &staticVerifierEmitter) { 490 OpEmitter(op, staticVerifierEmitter).emitDecl(os); 491 } 492 493 void OpEmitter::emitDef( 494 const Operator &op, raw_ostream &os, 495 const StaticVerifierFunctionEmitter &staticVerifierEmitter) { 496 OpEmitter(op, staticVerifierEmitter).emitDef(os); 497 } 498 499 void OpEmitter::emitDecl(raw_ostream &os) { opClass.writeDeclTo(os); } 500 501 void OpEmitter::emitDef(raw_ostream &os) { opClass.writeDefTo(os); } 502 503 void OpEmitter::genAttrNameGetters() { 504 // Enumerate the attribute names of this op, assigning each a relative 505 // ordering. 506 auto addAttrName = [&](StringRef name) { 507 unsigned index = attributeNames.size(); 508 attributeNames.insert({name, index}); 509 }; 510 for (const NamedAttribute &namedAttr : op.getAttributes()) 511 addAttrName(namedAttr.name); 512 // Include key attributes from several traits as implicitly registered. 513 if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) 514 addAttrName("operand_segment_sizes"); 515 if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) 516 addAttrName("result_segment_sizes"); 517 518 // Emit the getAttributeNames method. 519 { 520 auto *method = opClass.addMethodAndPrune( 521 "::llvm::ArrayRef<::llvm::StringRef>", "getAttributeNames", 522 OpMethod::Property(OpMethod::MP_Static | OpMethod::MP_Inline)); 523 auto &body = method->body(); 524 if (attributeNames.empty()) { 525 body << " return {};"; 526 } else { 527 body << " static ::llvm::StringRef attrNames[] = {"; 528 llvm::interleaveComma(llvm::make_first_range(attributeNames), body, 529 [&](StringRef attrName) { 530 body << "::llvm::StringRef(\"" << attrName 531 << "\")"; 532 }); 533 body << "};\n return ::llvm::makeArrayRef(attrNames);"; 534 } 535 } 536 if (attributeNames.empty()) 537 return; 538 539 // Emit the getAttributeNameForIndex methods. 540 { 541 auto *method = opClass.addMethodAndPrune( 542 "::mlir::Identifier", "getAttributeNameForIndex", 543 OpMethod::Property(OpMethod::MP_Private | OpMethod::MP_Inline), 544 "unsigned", "index"); 545 method->body() 546 << " return getAttributeNameForIndex((*this)->getName(), index);"; 547 } 548 { 549 auto *method = opClass.addMethodAndPrune( 550 "::mlir::Identifier", "getAttributeNameForIndex", 551 OpMethod::Property(OpMethod::MP_Private | OpMethod::MP_Inline | 552 OpMethod::MP_Static), 553 "::mlir::OperationName name, unsigned index"); 554 method->body() << "assert(index < " << attributeNames.size() 555 << " && \"invalid attribute index\");\n" 556 " return name.getAbstractOperation()" 557 "->getAttributeNames()[index];"; 558 } 559 560 // Generate the <attr>AttrName methods, that expose the attribute names to 561 // users. 562 const char *attrNameMethodBody = " return getAttributeNameForIndex({0});"; 563 for (const std::pair<StringRef, unsigned> &attrIt : attributeNames) { 564 std::string methodName = (attrIt.first + "AttrName").str(); 565 566 // Generate the non-static variant. 567 { 568 auto *method = 569 opClass.addMethodAndPrune("::mlir::Identifier", methodName, 570 OpMethod::Property(OpMethod::MP_Inline)); 571 method->body() << llvm::formatv(attrNameMethodBody, attrIt.second).str(); 572 } 573 574 // Generate the static variant. 575 { 576 auto *method = opClass.addMethodAndPrune( 577 "::mlir::Identifier", methodName, 578 OpMethod::Property(OpMethod::MP_Inline | OpMethod::MP_Static), 579 "::mlir::OperationName", "name"); 580 method->body() << llvm::formatv(attrNameMethodBody, 581 "name, " + Twine(attrIt.second)) 582 .str(); 583 } 584 } 585 } 586 587 void OpEmitter::genAttrGetters() { 588 FmtContext fctx; 589 fctx.withBuilder("::mlir::Builder((*this)->getContext())"); 590 591 // Emit the derived attribute body. 592 auto emitDerivedAttr = [&](StringRef name, Attribute attr) { 593 if (auto *method = opClass.addMethodAndPrune(attr.getReturnType(), name)) 594 method->body() << " " << attr.getDerivedCodeBody() << "\n"; 595 }; 596 597 // Emit with return type specified. 598 auto emitAttrWithReturnType = [&](StringRef name, Attribute attr) { 599 auto *method = opClass.addMethodAndPrune(attr.getReturnType(), name); 600 auto &body = method->body(); 601 body << " auto attr = " << name << "Attr();\n"; 602 if (attr.hasDefaultValue()) { 603 // Returns the default value if not set. 604 // TODO: this is inefficient, we are recreating the attribute for every 605 // call. This should be set instead. 606 std::string defaultValue = std::string( 607 tgfmt(attr.getConstBuilderTemplate(), &fctx, attr.getDefaultValue())); 608 body << " if (!attr)\n return " 609 << tgfmt(attr.getConvertFromStorageCall(), 610 &fctx.withSelf(defaultValue)) 611 << ";\n"; 612 } 613 body << " return " 614 << tgfmt(attr.getConvertFromStorageCall(), &fctx.withSelf("attr")) 615 << ";\n"; 616 }; 617 618 // Generate raw named accessor type. This is a wrapper class that allows 619 // referring to the attributes via accessors instead of having to use 620 // the string interface for better compile time verification. 621 auto emitAttrWithStorageType = [&](StringRef name, Attribute attr) { 622 auto *method = 623 opClass.addMethodAndPrune(attr.getStorageType(), (name + "Attr").str()); 624 if (!method) 625 return; 626 auto &body = method->body(); 627 body << " return (*this)->getAttr(" << name << "AttrName()).template "; 628 if (attr.isOptional() || attr.hasDefaultValue()) 629 body << "dyn_cast_or_null<"; 630 else 631 body << "cast<"; 632 body << attr.getStorageType() << ">();"; 633 }; 634 635 for (const NamedAttribute &namedAttr : op.getAttributes()) { 636 if (namedAttr.attr.isDerivedAttr()) { 637 emitDerivedAttr(namedAttr.name, namedAttr.attr); 638 } else { 639 emitAttrWithStorageType(namedAttr.name, namedAttr.attr); 640 emitAttrWithReturnType(namedAttr.name, namedAttr.attr); 641 } 642 } 643 644 auto derivedAttrs = make_filter_range(op.getAttributes(), 645 [](const NamedAttribute &namedAttr) { 646 return namedAttr.attr.isDerivedAttr(); 647 }); 648 if (!derivedAttrs.empty()) { 649 opClass.addTrait("::mlir::DerivedAttributeOpInterface::Trait"); 650 // Generate helper method to query whether a named attribute is a derived 651 // attribute. This enables, for example, avoiding adding an attribute that 652 // overlaps with a derived attribute. 653 { 654 auto *method = opClass.addMethodAndPrune("bool", "isDerivedAttribute", 655 OpMethod::MP_Static, 656 "::llvm::StringRef", "name"); 657 auto &body = method->body(); 658 for (auto namedAttr : derivedAttrs) 659 body << " if (name == \"" << namedAttr.name << "\") return true;\n"; 660 body << " return false;"; 661 } 662 // Generate method to materialize derived attributes as a DictionaryAttr. 663 { 664 auto *method = opClass.addMethodAndPrune("::mlir::DictionaryAttr", 665 "materializeDerivedAttributes"); 666 auto &body = method->body(); 667 668 auto nonMaterializable = 669 make_filter_range(derivedAttrs, [](const NamedAttribute &namedAttr) { 670 return namedAttr.attr.getConvertFromStorageCall().empty(); 671 }); 672 if (!nonMaterializable.empty()) { 673 std::string attrs; 674 llvm::raw_string_ostream os(attrs); 675 interleaveComma(nonMaterializable, os, 676 [&](const NamedAttribute &attr) { os << attr.name; }); 677 PrintWarning( 678 op.getLoc(), 679 formatv( 680 "op has non-materializable derived attributes '{0}', skipping", 681 os.str())); 682 body << formatv(" emitOpError(\"op has non-materializable derived " 683 "attributes '{0}'\");\n", 684 attrs); 685 body << " return nullptr;"; 686 return; 687 } 688 689 body << " ::mlir::MLIRContext* ctx = getContext();\n"; 690 body << " ::mlir::Builder odsBuilder(ctx); (void)odsBuilder;\n"; 691 body << " return ::mlir::DictionaryAttr::get("; 692 body << " ctx, {\n"; 693 interleave( 694 derivedAttrs, body, 695 [&](const NamedAttribute &namedAttr) { 696 auto tmpl = namedAttr.attr.getConvertFromStorageCall(); 697 body << " {" << namedAttr.name << "AttrName(),\n" 698 << tgfmt(tmpl, &fctx.withSelf(namedAttr.name + "()") 699 .withBuilder("odsBuilder") 700 .addSubst("_ctx", "ctx")) 701 << "}"; 702 }, 703 ",\n"); 704 body << "});"; 705 } 706 } 707 } 708 709 void OpEmitter::genAttrSetters() { 710 // Generate raw named setter type. This is a wrapper class that allows setting 711 // to the attributes via setters instead of having to use the string interface 712 // for better compile time verification. 713 auto emitAttrWithStorageType = [&](StringRef name, Attribute attr) { 714 auto *method = opClass.addMethodAndPrune("void", (name + "Attr").str(), 715 attr.getStorageType(), "attr"); 716 if (method) 717 method->body() << " (*this)->setAttr(" << name << "AttrName(), attr);"; 718 }; 719 720 for (const NamedAttribute &namedAttr : op.getAttributes()) 721 if (!namedAttr.attr.isDerivedAttr()) 722 emitAttrWithStorageType(namedAttr.name, namedAttr.attr); 723 } 724 725 void OpEmitter::genOptionalAttrRemovers() { 726 // Generate methods for removing optional attributes, instead of having to 727 // use the string interface. Enables better compile time verification. 728 auto emitRemoveAttr = [&](StringRef name) { 729 auto upperInitial = name.take_front().upper(); 730 auto suffix = name.drop_front(); 731 auto *method = opClass.addMethodAndPrune( 732 "::mlir::Attribute", ("remove" + upperInitial + suffix + "Attr").str()); 733 if (!method) 734 return; 735 method->body() << " return (*this)->removeAttr(" << name << "AttrName());"; 736 }; 737 738 for (const NamedAttribute &namedAttr : op.getAttributes()) 739 if (namedAttr.attr.isOptional()) 740 emitRemoveAttr(namedAttr.name); 741 } 742 743 // Generates the code to compute the start and end index of an operand or result 744 // range. 745 template <typename RangeT> 746 static void 747 generateValueRangeStartAndEnd(Class &opClass, StringRef methodName, 748 int numVariadic, int numNonVariadic, 749 StringRef rangeSizeCall, bool hasAttrSegmentSize, 750 StringRef sizeAttrInit, RangeT &&odsValues) { 751 auto *method = opClass.addMethodAndPrune("std::pair<unsigned, unsigned>", 752 methodName, "unsigned", "index"); 753 if (!method) 754 return; 755 auto &body = method->body(); 756 if (numVariadic == 0) { 757 body << " return {index, 1};\n"; 758 } else if (hasAttrSegmentSize) { 759 body << sizeAttrInit << attrSizedSegmentValueRangeCalcCode; 760 } else { 761 // Because the op can have arbitrarily interleaved variadic and non-variadic 762 // operands, we need to embed a list in the "sink" getter method for 763 // calculation at run-time. 764 llvm::SmallVector<StringRef, 4> isVariadic; 765 isVariadic.reserve(llvm::size(odsValues)); 766 for (auto &it : odsValues) 767 isVariadic.push_back(it.isVariableLength() ? "true" : "false"); 768 std::string isVariadicList = llvm::join(isVariadic, ", "); 769 body << formatv(sameVariadicSizeValueRangeCalcCode, isVariadicList, 770 numNonVariadic, numVariadic, rangeSizeCall, "operand"); 771 } 772 } 773 774 // Generates the named operand getter methods for the given Operator `op` and 775 // puts them in `opClass`. Uses `rangeType` as the return type of getters that 776 // return a range of operands (individual operands are `Value ` and each 777 // element in the range must also be `Value `); use `rangeBeginCall` to get 778 // an iterator to the beginning of the operand range; use `rangeSizeCall` to 779 // obtain the number of operands. `getOperandCallPattern` contains the code 780 // necessary to obtain a single operand whose position will be substituted 781 // instead of 782 // "{0}" marker in the pattern. Note that the pattern should work for any kind 783 // of ops, in particular for one-operand ops that may not have the 784 // `getOperand(unsigned)` method. 785 static void generateNamedOperandGetters(const Operator &op, Class &opClass, 786 StringRef sizeAttrInit, 787 StringRef rangeType, 788 StringRef rangeBeginCall, 789 StringRef rangeSizeCall, 790 StringRef getOperandCallPattern) { 791 const int numOperands = op.getNumOperands(); 792 const int numVariadicOperands = op.getNumVariableLengthOperands(); 793 const int numNormalOperands = numOperands - numVariadicOperands; 794 795 const auto *sameVariadicSize = 796 op.getTrait("::mlir::OpTrait::SameVariadicOperandSize"); 797 const auto *attrSizedOperands = 798 op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments"); 799 800 if (numVariadicOperands > 1 && !sameVariadicSize && !attrSizedOperands) { 801 PrintFatalError(op.getLoc(), "op has multiple variadic operands but no " 802 "specification over their sizes"); 803 } 804 805 if (numVariadicOperands < 2 && attrSizedOperands) { 806 PrintFatalError(op.getLoc(), "op must have at least two variadic operands " 807 "to use 'AttrSizedOperandSegments' trait"); 808 } 809 810 if (attrSizedOperands && sameVariadicSize) { 811 PrintFatalError(op.getLoc(), 812 "op cannot have both 'AttrSizedOperandSegments' and " 813 "'SameVariadicOperandSize' traits"); 814 } 815 816 // First emit a few "sink" getter methods upon which we layer all nicer named 817 // getter methods. 818 generateValueRangeStartAndEnd(opClass, "getODSOperandIndexAndLength", 819 numVariadicOperands, numNormalOperands, 820 rangeSizeCall, attrSizedOperands, sizeAttrInit, 821 const_cast<Operator &>(op).getOperands()); 822 823 auto *m = opClass.addMethodAndPrune(rangeType, "getODSOperands", "unsigned", 824 "index"); 825 auto &body = m->body(); 826 body << formatv(valueRangeReturnCode, rangeBeginCall, 827 "getODSOperandIndexAndLength(index)"); 828 829 // Then we emit nicer named getter methods by redirecting to the "sink" getter 830 // method. 831 for (int i = 0; i != numOperands; ++i) { 832 const auto &operand = op.getOperand(i); 833 if (operand.name.empty()) 834 continue; 835 836 if (operand.isOptional()) { 837 m = opClass.addMethodAndPrune("::mlir::Value", operand.name); 838 m->body() 839 << " auto operands = getODSOperands(" << i << ");\n" 840 << " return operands.empty() ? ::mlir::Value() : *operands.begin();"; 841 } else if (operand.isVariadic()) { 842 m = opClass.addMethodAndPrune(rangeType, operand.name); 843 m->body() << " return getODSOperands(" << i << ");"; 844 } else { 845 m = opClass.addMethodAndPrune("::mlir::Value", operand.name); 846 m->body() << " return *getODSOperands(" << i << ").begin();"; 847 } 848 } 849 } 850 851 void OpEmitter::genNamedOperandGetters() { 852 // Build the code snippet used for initializing the operand_segment_sizes 853 // array. 854 std::string attrSizeInitCode; 855 if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) { 856 attrSizeInitCode = 857 formatv(opSegmentSizeAttrInitCode, "operand_segment_sizesAttrName()") 858 .str(); 859 } 860 861 generateNamedOperandGetters( 862 op, opClass, 863 /*sizeAttrInit=*/attrSizeInitCode, 864 /*rangeType=*/"::mlir::Operation::operand_range", 865 /*rangeBeginCall=*/"getOperation()->operand_begin()", 866 /*rangeSizeCall=*/"getOperation()->getNumOperands()", 867 /*getOperandCallPattern=*/"getOperation()->getOperand({0})"); 868 } 869 870 void OpEmitter::genNamedOperandSetters() { 871 auto *attrSizedOperands = 872 op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments"); 873 for (int i = 0, e = op.getNumOperands(); i != e; ++i) { 874 const auto &operand = op.getOperand(i); 875 if (operand.name.empty()) 876 continue; 877 auto *m = opClass.addMethodAndPrune("::mlir::MutableOperandRange", 878 (operand.name + "Mutable").str()); 879 auto &body = m->body(); 880 body << " auto range = getODSOperandIndexAndLength(" << i << ");\n" 881 << " return ::mlir::MutableOperandRange(getOperation(), " 882 "range.first, range.second"; 883 if (attrSizedOperands) 884 body << ", ::mlir::MutableOperandRange::OperandSegment(" << i 885 << "u, *getOperation()->getAttrDictionary().getNamed(" 886 "operand_segment_sizesAttrName()))"; 887 body << ");\n"; 888 } 889 } 890 891 void OpEmitter::genNamedResultGetters() { 892 const int numResults = op.getNumResults(); 893 const int numVariadicResults = op.getNumVariableLengthResults(); 894 const int numNormalResults = numResults - numVariadicResults; 895 896 // If we have more than one variadic results, we need more complicated logic 897 // to calculate the value range for each result. 898 899 const auto *sameVariadicSize = 900 op.getTrait("::mlir::OpTrait::SameVariadicResultSize"); 901 const auto *attrSizedResults = 902 op.getTrait("::mlir::OpTrait::AttrSizedResultSegments"); 903 904 if (numVariadicResults > 1 && !sameVariadicSize && !attrSizedResults) { 905 PrintFatalError(op.getLoc(), "op has multiple variadic results but no " 906 "specification over their sizes"); 907 } 908 909 if (numVariadicResults < 2 && attrSizedResults) { 910 PrintFatalError(op.getLoc(), "op must have at least two variadic results " 911 "to use 'AttrSizedResultSegments' trait"); 912 } 913 914 if (attrSizedResults && sameVariadicSize) { 915 PrintFatalError(op.getLoc(), 916 "op cannot have both 'AttrSizedResultSegments' and " 917 "'SameVariadicResultSize' traits"); 918 } 919 920 // Build the initializer string for the result segment size attribute. 921 std::string attrSizeInitCode; 922 if (attrSizedResults) { 923 attrSizeInitCode = 924 formatv(opSegmentSizeAttrInitCode, "result_segment_sizesAttrName()") 925 .str(); 926 } 927 928 generateValueRangeStartAndEnd( 929 opClass, "getODSResultIndexAndLength", numVariadicResults, 930 numNormalResults, "getOperation()->getNumResults()", attrSizedResults, 931 attrSizeInitCode, op.getResults()); 932 933 auto *m = opClass.addMethodAndPrune("::mlir::Operation::result_range", 934 "getODSResults", "unsigned", "index"); 935 m->body() << formatv(valueRangeReturnCode, "getOperation()->result_begin()", 936 "getODSResultIndexAndLength(index)"); 937 938 for (int i = 0; i != numResults; ++i) { 939 const auto &result = op.getResult(i); 940 if (result.name.empty()) 941 continue; 942 943 if (result.isOptional()) { 944 m = opClass.addMethodAndPrune("::mlir::Value", result.name); 945 m->body() 946 << " auto results = getODSResults(" << i << ");\n" 947 << " return results.empty() ? ::mlir::Value() : *results.begin();"; 948 } else if (result.isVariadic()) { 949 m = opClass.addMethodAndPrune("::mlir::Operation::result_range", 950 result.name); 951 m->body() << " return getODSResults(" << i << ");"; 952 } else { 953 m = opClass.addMethodAndPrune("::mlir::Value", result.name); 954 m->body() << " return *getODSResults(" << i << ").begin();"; 955 } 956 } 957 } 958 959 void OpEmitter::genNamedRegionGetters() { 960 unsigned numRegions = op.getNumRegions(); 961 for (unsigned i = 0; i < numRegions; ++i) { 962 const auto ®ion = op.getRegion(i); 963 if (region.name.empty()) 964 continue; 965 966 // Generate the accessors for a variadic region. 967 if (region.isVariadic()) { 968 auto *m = opClass.addMethodAndPrune( 969 "::mlir::MutableArrayRef<::mlir::Region>", region.name); 970 m->body() << formatv(" return (*this)->getRegions().drop_front({0});", 971 i); 972 continue; 973 } 974 975 auto *m = opClass.addMethodAndPrune("::mlir::Region &", region.name); 976 m->body() << formatv(" return (*this)->getRegion({0});", i); 977 } 978 } 979 980 void OpEmitter::genNamedSuccessorGetters() { 981 unsigned numSuccessors = op.getNumSuccessors(); 982 for (unsigned i = 0; i < numSuccessors; ++i) { 983 const NamedSuccessor &successor = op.getSuccessor(i); 984 if (successor.name.empty()) 985 continue; 986 987 // Generate the accessors for a variadic successor list. 988 if (successor.isVariadic()) { 989 auto *m = 990 opClass.addMethodAndPrune("::mlir::SuccessorRange", successor.name); 991 m->body() << formatv( 992 " return {std::next((*this)->successor_begin(), {0}), " 993 "(*this)->successor_end()};", 994 i); 995 continue; 996 } 997 998 auto *m = opClass.addMethodAndPrune("::mlir::Block *", successor.name); 999 m->body() << formatv(" return (*this)->getSuccessor({0});", i); 1000 } 1001 } 1002 1003 static bool canGenerateUnwrappedBuilder(Operator &op) { 1004 // If this op does not have native attributes at all, return directly to avoid 1005 // redefining builders. 1006 if (op.getNumNativeAttributes() == 0) 1007 return false; 1008 1009 bool canGenerate = false; 1010 // We are generating builders that take raw values for attributes. We need to 1011 // make sure the native attributes have a meaningful "unwrapped" value type 1012 // different from the wrapped mlir::Attribute type to avoid redefining 1013 // builders. This checks for the op has at least one such native attribute. 1014 for (int i = 0, e = op.getNumNativeAttributes(); i < e; ++i) { 1015 NamedAttribute &namedAttr = op.getAttribute(i); 1016 if (canUseUnwrappedRawValue(namedAttr.attr)) { 1017 canGenerate = true; 1018 break; 1019 } 1020 } 1021 return canGenerate; 1022 } 1023 1024 static bool canInferType(Operator &op) { 1025 return op.getTrait("::mlir::InferTypeOpInterface::Trait") && 1026 op.getNumRegions() == 0; 1027 } 1028 1029 void OpEmitter::genSeparateArgParamBuilder() { 1030 SmallVector<AttrParamKind, 2> attrBuilderType; 1031 attrBuilderType.push_back(AttrParamKind::WrappedAttr); 1032 if (canGenerateUnwrappedBuilder(op)) 1033 attrBuilderType.push_back(AttrParamKind::UnwrappedValue); 1034 1035 // Emit with separate builders with or without unwrapped attributes and/or 1036 // inferring result type. 1037 auto emit = [&](AttrParamKind attrType, TypeParamKind paramKind, 1038 bool inferType) { 1039 llvm::SmallVector<OpMethodParameter, 4> paramList; 1040 llvm::SmallVector<std::string, 4> resultNames; 1041 buildParamList(paramList, resultNames, paramKind, attrType); 1042 1043 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1044 std::move(paramList)); 1045 // If the builder is redundant, skip generating the method. 1046 if (!m) 1047 return; 1048 auto &body = m->body(); 1049 genCodeForAddingArgAndRegionForBuilder( 1050 body, /*isRawValueAttr=*/attrType == AttrParamKind::UnwrappedValue); 1051 1052 // Push all result types to the operation state 1053 1054 if (inferType) { 1055 // Generate builder that infers type too. 1056 // TODO: Subsume this with general checking if type can be 1057 // inferred automatically. 1058 // TODO: Expand to handle regions. 1059 body << formatv(R"( 1060 ::llvm::SmallVector<::mlir::Type, 2> inferredReturnTypes; 1061 if (::mlir::succeeded({0}::inferReturnTypes(odsBuilder.getContext(), 1062 {1}.location, {1}.operands, 1063 {1}.attributes.getDictionary({1}.getContext()), 1064 /*regions=*/{{}, inferredReturnTypes))) 1065 {1}.addTypes(inferredReturnTypes); 1066 else 1067 ::llvm::report_fatal_error("Failed to infer result type(s).");)", 1068 opClass.getClassName(), builderOpState); 1069 return; 1070 } 1071 1072 switch (paramKind) { 1073 case TypeParamKind::None: 1074 return; 1075 case TypeParamKind::Separate: 1076 for (int i = 0, e = op.getNumResults(); i < e; ++i) { 1077 if (op.getResult(i).isOptional()) 1078 body << " if (" << resultNames[i] << ")\n "; 1079 body << " " << builderOpState << ".addTypes(" << resultNames[i] 1080 << ");\n"; 1081 } 1082 return; 1083 case TypeParamKind::Collective: { 1084 int numResults = op.getNumResults(); 1085 int numVariadicResults = op.getNumVariableLengthResults(); 1086 int numNonVariadicResults = numResults - numVariadicResults; 1087 bool hasVariadicResult = numVariadicResults != 0; 1088 1089 // Avoid emitting "resultTypes.size() >= 0u" which is always true. 1090 if (!(hasVariadicResult && numNonVariadicResults == 0)) 1091 body << " " 1092 << "assert(resultTypes.size() " 1093 << (hasVariadicResult ? ">=" : "==") << " " 1094 << numNonVariadicResults 1095 << "u && \"mismatched number of results\");\n"; 1096 body << " " << builderOpState << ".addTypes(resultTypes);\n"; 1097 } 1098 return; 1099 } 1100 llvm_unreachable("unhandled TypeParamKind"); 1101 }; 1102 1103 // Some of the build methods generated here may be ambiguous, but TableGen's 1104 // ambiguous function detection will elide those ones. 1105 for (auto attrType : attrBuilderType) { 1106 emit(attrType, TypeParamKind::Separate, /*inferType=*/false); 1107 if (canInferType(op)) 1108 emit(attrType, TypeParamKind::None, /*inferType=*/true); 1109 emit(attrType, TypeParamKind::Collective, /*inferType=*/false); 1110 } 1111 } 1112 1113 void OpEmitter::genUseOperandAsResultTypeCollectiveParamBuilder() { 1114 int numResults = op.getNumResults(); 1115 1116 // Signature 1117 llvm::SmallVector<OpMethodParameter, 4> paramList; 1118 paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder"); 1119 paramList.emplace_back("::mlir::OperationState &", builderOpState); 1120 paramList.emplace_back("::mlir::ValueRange", "operands"); 1121 // Provide default value for `attributes` when its the last parameter 1122 StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}"; 1123 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>", 1124 "attributes", attributesDefaultValue); 1125 if (op.getNumVariadicRegions()) 1126 paramList.emplace_back("unsigned", "numRegions"); 1127 1128 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1129 std::move(paramList)); 1130 // If the builder is redundant, skip generating the method 1131 if (!m) 1132 return; 1133 auto &body = m->body(); 1134 1135 // Operands 1136 body << " " << builderOpState << ".addOperands(operands);\n"; 1137 1138 // Attributes 1139 body << " " << builderOpState << ".addAttributes(attributes);\n"; 1140 1141 // Create the correct number of regions 1142 if (int numRegions = op.getNumRegions()) { 1143 body << llvm::formatv( 1144 " for (unsigned i = 0; i != {0}; ++i)\n", 1145 (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions))); 1146 body << " (void)" << builderOpState << ".addRegion();\n"; 1147 } 1148 1149 // Result types 1150 SmallVector<std::string, 2> resultTypes(numResults, "operands[0].getType()"); 1151 body << " " << builderOpState << ".addTypes({" 1152 << llvm::join(resultTypes, ", ") << "});\n\n"; 1153 } 1154 1155 void OpEmitter::genInferredTypeCollectiveParamBuilder() { 1156 // TODO: Expand to support regions. 1157 SmallVector<OpMethodParameter, 4> paramList; 1158 paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder"); 1159 paramList.emplace_back("::mlir::OperationState &", builderOpState); 1160 paramList.emplace_back("::mlir::ValueRange", "operands"); 1161 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>", 1162 "attributes", "{}"); 1163 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1164 std::move(paramList)); 1165 // If the builder is redundant, skip generating the method 1166 if (!m) 1167 return; 1168 auto &body = m->body(); 1169 1170 int numResults = op.getNumResults(); 1171 int numVariadicResults = op.getNumVariableLengthResults(); 1172 int numNonVariadicResults = numResults - numVariadicResults; 1173 1174 int numOperands = op.getNumOperands(); 1175 int numVariadicOperands = op.getNumVariableLengthOperands(); 1176 int numNonVariadicOperands = numOperands - numVariadicOperands; 1177 1178 // Operands 1179 if (numVariadicOperands == 0 || numNonVariadicOperands != 0) 1180 body << " assert(operands.size()" 1181 << (numVariadicOperands != 0 ? " >= " : " == ") 1182 << numNonVariadicOperands 1183 << "u && \"mismatched number of parameters\");\n"; 1184 body << " " << builderOpState << ".addOperands(operands);\n"; 1185 body << " " << builderOpState << ".addAttributes(attributes);\n"; 1186 1187 // Create the correct number of regions 1188 if (int numRegions = op.getNumRegions()) { 1189 body << llvm::formatv( 1190 " for (unsigned i = 0; i != {0}; ++i)\n", 1191 (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions))); 1192 body << " (void)" << builderOpState << ".addRegion();\n"; 1193 } 1194 1195 // Result types 1196 body << formatv(R"( 1197 ::mlir::SmallVector<::mlir::Type, 2> inferredReturnTypes; 1198 if (::mlir::succeeded({0}::inferReturnTypes(odsBuilder.getContext(), 1199 {1}.location, operands, 1200 {1}.attributes.getDictionary({1}.getContext()), 1201 /*regions=*/{{}, inferredReturnTypes))) {{)", 1202 opClass.getClassName(), builderOpState); 1203 if (numVariadicResults == 0 || numNonVariadicResults != 0) 1204 body << " assert(inferredReturnTypes.size()" 1205 << (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults 1206 << "u && \"mismatched number of return types\");\n"; 1207 body << " " << builderOpState << ".addTypes(inferredReturnTypes);"; 1208 1209 body << formatv(R"( 1210 } else 1211 ::llvm::report_fatal_error("Failed to infer result type(s).");)", 1212 opClass.getClassName(), builderOpState); 1213 } 1214 1215 void OpEmitter::genUseOperandAsResultTypeSeparateParamBuilder() { 1216 llvm::SmallVector<OpMethodParameter, 4> paramList; 1217 llvm::SmallVector<std::string, 4> resultNames; 1218 buildParamList(paramList, resultNames, TypeParamKind::None); 1219 1220 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1221 std::move(paramList)); 1222 // If the builder is redundant, skip generating the method 1223 if (!m) 1224 return; 1225 auto &body = m->body(); 1226 genCodeForAddingArgAndRegionForBuilder(body); 1227 1228 auto numResults = op.getNumResults(); 1229 if (numResults == 0) 1230 return; 1231 1232 // Push all result types to the operation state 1233 const char *index = op.getOperand(0).isVariadic() ? ".front()" : ""; 1234 std::string resultType = 1235 formatv("{0}{1}.getType()", getArgumentName(op, 0), index).str(); 1236 body << " " << builderOpState << ".addTypes({" << resultType; 1237 for (int i = 1; i != numResults; ++i) 1238 body << ", " << resultType; 1239 body << "});\n\n"; 1240 } 1241 1242 void OpEmitter::genUseAttrAsResultTypeBuilder() { 1243 SmallVector<OpMethodParameter, 4> paramList; 1244 paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder"); 1245 paramList.emplace_back("::mlir::OperationState &", builderOpState); 1246 paramList.emplace_back("::mlir::ValueRange", "operands"); 1247 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>", 1248 "attributes", "{}"); 1249 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1250 std::move(paramList)); 1251 // If the builder is redundant, skip generating the method 1252 if (!m) 1253 return; 1254 1255 auto &body = m->body(); 1256 1257 // Push all result types to the operation state 1258 std::string resultType; 1259 const auto &namedAttr = op.getAttribute(0); 1260 1261 body << " auto attrName = " << namedAttr.name << "AttrName(" 1262 << builderOpState 1263 << ".name);\n" 1264 " for (auto attr : attributes) {\n" 1265 " if (attr.first != attrName) continue;\n"; 1266 if (namedAttr.attr.isTypeAttr()) { 1267 resultType = "attr.second.cast<::mlir::TypeAttr>().getValue()"; 1268 } else { 1269 resultType = "attr.second.getType()"; 1270 } 1271 1272 // Operands 1273 body << " " << builderOpState << ".addOperands(operands);\n"; 1274 1275 // Attributes 1276 body << " " << builderOpState << ".addAttributes(attributes);\n"; 1277 1278 // Result types 1279 SmallVector<std::string, 2> resultTypes(op.getNumResults(), resultType); 1280 body << " " << builderOpState << ".addTypes({" 1281 << llvm::join(resultTypes, ", ") << "});\n"; 1282 body << " }\n"; 1283 } 1284 1285 /// Returns a signature of the builder. Updates the context `fctx` to enable 1286 /// replacement of $_builder and $_state in the body. 1287 static std::string getBuilderSignature(const Builder &builder) { 1288 ArrayRef<Builder::Parameter> params(builder.getParameters()); 1289 1290 // Inject builder and state arguments. 1291 llvm::SmallVector<std::string, 8> arguments; 1292 arguments.reserve(params.size() + 2); 1293 arguments.push_back( 1294 llvm::formatv("::mlir::OpBuilder &{0}", odsBuilder).str()); 1295 arguments.push_back( 1296 llvm::formatv("::mlir::OperationState &{0}", builderOpState).str()); 1297 1298 for (unsigned i = 0, e = params.size(); i < e; ++i) { 1299 // If no name is provided, generate one. 1300 Optional<StringRef> paramName = params[i].getName(); 1301 std::string name = 1302 paramName ? paramName->str() : "odsArg" + std::to_string(i); 1303 1304 std::string defaultValue; 1305 if (Optional<StringRef> defaultParamValue = params[i].getDefaultValue()) 1306 defaultValue = llvm::formatv(" = {0}", *defaultParamValue).str(); 1307 arguments.push_back( 1308 llvm::formatv("{0} {1}{2}", params[i].getCppType(), name, defaultValue) 1309 .str()); 1310 } 1311 1312 return llvm::join(arguments, ", "); 1313 } 1314 1315 void OpEmitter::genBuilder() { 1316 // Handle custom builders if provided. 1317 for (const Builder &builder : op.getBuilders()) { 1318 std::string paramStr = getBuilderSignature(builder); 1319 1320 Optional<StringRef> body = builder.getBody(); 1321 OpMethod::Property properties = 1322 body ? OpMethod::MP_Static : OpMethod::MP_StaticDeclaration; 1323 auto *method = 1324 opClass.addMethodAndPrune("void", "build", properties, paramStr); 1325 1326 FmtContext fctx; 1327 fctx.withBuilder(odsBuilder); 1328 fctx.addSubst("_state", builderOpState); 1329 if (body) 1330 method->body() << tgfmt(*body, &fctx); 1331 } 1332 1333 // Generate default builders that requires all result type, operands, and 1334 // attributes as parameters. 1335 if (op.skipDefaultBuilders()) 1336 return; 1337 1338 // We generate three classes of builders here: 1339 // 1. one having a stand-alone parameter for each operand / attribute, and 1340 genSeparateArgParamBuilder(); 1341 // 2. one having an aggregated parameter for all result types / operands / 1342 // attributes, and 1343 genCollectiveParamBuilder(); 1344 // 3. one having a stand-alone parameter for each operand and attribute, 1345 // use the first operand or attribute's type as all result types 1346 // to facilitate different call patterns. 1347 if (op.getNumVariableLengthResults() == 0) { 1348 if (op.getTrait("::mlir::OpTrait::SameOperandsAndResultType")) { 1349 genUseOperandAsResultTypeSeparateParamBuilder(); 1350 genUseOperandAsResultTypeCollectiveParamBuilder(); 1351 } 1352 if (op.getTrait("::mlir::OpTrait::FirstAttrDerivedResultType")) 1353 genUseAttrAsResultTypeBuilder(); 1354 } 1355 } 1356 1357 void OpEmitter::genCollectiveParamBuilder() { 1358 int numResults = op.getNumResults(); 1359 int numVariadicResults = op.getNumVariableLengthResults(); 1360 int numNonVariadicResults = numResults - numVariadicResults; 1361 1362 int numOperands = op.getNumOperands(); 1363 int numVariadicOperands = op.getNumVariableLengthOperands(); 1364 int numNonVariadicOperands = numOperands - numVariadicOperands; 1365 1366 SmallVector<OpMethodParameter, 4> paramList; 1367 paramList.emplace_back("::mlir::OpBuilder &", ""); 1368 paramList.emplace_back("::mlir::OperationState &", builderOpState); 1369 paramList.emplace_back("::mlir::TypeRange", "resultTypes"); 1370 paramList.emplace_back("::mlir::ValueRange", "operands"); 1371 // Provide default value for `attributes` when its the last parameter 1372 StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}"; 1373 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>", 1374 "attributes", attributesDefaultValue); 1375 if (op.getNumVariadicRegions()) 1376 paramList.emplace_back("unsigned", "numRegions"); 1377 1378 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1379 std::move(paramList)); 1380 // If the builder is redundant, skip generating the method 1381 if (!m) 1382 return; 1383 auto &body = m->body(); 1384 1385 // Operands 1386 if (numVariadicOperands == 0 || numNonVariadicOperands != 0) 1387 body << " assert(operands.size()" 1388 << (numVariadicOperands != 0 ? " >= " : " == ") 1389 << numNonVariadicOperands 1390 << "u && \"mismatched number of parameters\");\n"; 1391 body << " " << builderOpState << ".addOperands(operands);\n"; 1392 1393 // Attributes 1394 body << " " << builderOpState << ".addAttributes(attributes);\n"; 1395 1396 // Create the correct number of regions 1397 if (int numRegions = op.getNumRegions()) { 1398 body << llvm::formatv( 1399 " for (unsigned i = 0; i != {0}; ++i)\n", 1400 (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions))); 1401 body << " (void)" << builderOpState << ".addRegion();\n"; 1402 } 1403 1404 // Result types 1405 if (numVariadicResults == 0 || numNonVariadicResults != 0) 1406 body << " assert(resultTypes.size()" 1407 << (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults 1408 << "u && \"mismatched number of return types\");\n"; 1409 body << " " << builderOpState << ".addTypes(resultTypes);\n"; 1410 1411 // Generate builder that infers type too. 1412 // TODO: Expand to handle regions and successors. 1413 if (canInferType(op) && op.getNumSuccessors() == 0) 1414 genInferredTypeCollectiveParamBuilder(); 1415 } 1416 1417 void OpEmitter::buildParamList(SmallVectorImpl<OpMethodParameter> ¶mList, 1418 SmallVectorImpl<std::string> &resultTypeNames, 1419 TypeParamKind typeParamKind, 1420 AttrParamKind attrParamKind) { 1421 resultTypeNames.clear(); 1422 auto numResults = op.getNumResults(); 1423 resultTypeNames.reserve(numResults); 1424 1425 paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder"); 1426 paramList.emplace_back("::mlir::OperationState &", builderOpState); 1427 1428 switch (typeParamKind) { 1429 case TypeParamKind::None: 1430 break; 1431 case TypeParamKind::Separate: { 1432 // Add parameters for all return types 1433 for (int i = 0; i < numResults; ++i) { 1434 const auto &result = op.getResult(i); 1435 std::string resultName = std::string(result.name); 1436 if (resultName.empty()) 1437 resultName = std::string(formatv("resultType{0}", i)); 1438 1439 StringRef type = 1440 result.isVariadic() ? "::mlir::TypeRange" : "::mlir::Type"; 1441 OpMethodParameter::Property properties = OpMethodParameter::PP_None; 1442 if (result.isOptional()) 1443 properties = OpMethodParameter::PP_Optional; 1444 1445 paramList.emplace_back(type, resultName, properties); 1446 resultTypeNames.emplace_back(std::move(resultName)); 1447 } 1448 } break; 1449 case TypeParamKind::Collective: { 1450 paramList.emplace_back("::mlir::TypeRange", "resultTypes"); 1451 resultTypeNames.push_back("resultTypes"); 1452 } break; 1453 } 1454 1455 // Add parameters for all arguments (operands and attributes). 1456 1457 int numOperands = 0; 1458 int numAttrs = 0; 1459 1460 int defaultValuedAttrStartIndex = op.getNumArgs(); 1461 if (attrParamKind == AttrParamKind::UnwrappedValue) { 1462 // Calculate the start index from which we can attach default values in the 1463 // builder declaration. 1464 for (int i = op.getNumArgs() - 1; i >= 0; --i) { 1465 auto *namedAttr = op.getArg(i).dyn_cast<tblgen::NamedAttribute *>(); 1466 if (!namedAttr || !namedAttr->attr.hasDefaultValue()) 1467 break; 1468 1469 if (!canUseUnwrappedRawValue(namedAttr->attr)) 1470 break; 1471 1472 // Creating an APInt requires us to provide bitwidth, value, and 1473 // signedness, which is complicated compared to others. Similarly 1474 // for APFloat. 1475 // TODO: Adjust the 'returnType' field of such attributes 1476 // to support them. 1477 StringRef retType = namedAttr->attr.getReturnType(); 1478 if (retType == "::llvm::APInt" || retType == "::llvm::APFloat") 1479 break; 1480 1481 defaultValuedAttrStartIndex = i; 1482 } 1483 } 1484 1485 for (int i = 0, e = op.getNumArgs(); i < e; ++i) { 1486 auto argument = op.getArg(i); 1487 if (argument.is<tblgen::NamedTypeConstraint *>()) { 1488 const auto &operand = op.getOperand(numOperands); 1489 StringRef type = 1490 operand.isVariadic() ? "::mlir::ValueRange" : "::mlir::Value"; 1491 OpMethodParameter::Property properties = OpMethodParameter::PP_None; 1492 if (operand.isOptional()) 1493 properties = OpMethodParameter::PP_Optional; 1494 1495 paramList.emplace_back(type, getArgumentName(op, numOperands), 1496 properties); 1497 ++numOperands; 1498 } else { 1499 const auto &namedAttr = op.getAttribute(numAttrs); 1500 const auto &attr = namedAttr.attr; 1501 1502 OpMethodParameter::Property properties = OpMethodParameter::PP_None; 1503 if (attr.isOptional()) 1504 properties = OpMethodParameter::PP_Optional; 1505 1506 StringRef type; 1507 switch (attrParamKind) { 1508 case AttrParamKind::WrappedAttr: 1509 type = attr.getStorageType(); 1510 break; 1511 case AttrParamKind::UnwrappedValue: 1512 if (canUseUnwrappedRawValue(attr)) 1513 type = attr.getReturnType(); 1514 else 1515 type = attr.getStorageType(); 1516 break; 1517 } 1518 1519 std::string defaultValue; 1520 // Attach default value if requested and possible. 1521 if (attrParamKind == AttrParamKind::UnwrappedValue && 1522 i >= defaultValuedAttrStartIndex) { 1523 bool isString = attr.getReturnType() == "::llvm::StringRef"; 1524 if (isString) 1525 defaultValue.append("\""); 1526 defaultValue += attr.getDefaultValue(); 1527 if (isString) 1528 defaultValue.append("\""); 1529 } 1530 paramList.emplace_back(type, namedAttr.name, defaultValue, properties); 1531 ++numAttrs; 1532 } 1533 } 1534 1535 /// Insert parameters for each successor. 1536 for (const NamedSuccessor &succ : op.getSuccessors()) { 1537 StringRef type = 1538 succ.isVariadic() ? "::mlir::BlockRange" : "::mlir::Block *"; 1539 paramList.emplace_back(type, succ.name); 1540 } 1541 1542 /// Insert parameters for variadic regions. 1543 for (const NamedRegion ®ion : op.getRegions()) 1544 if (region.isVariadic()) 1545 paramList.emplace_back("unsigned", 1546 llvm::formatv("{0}Count", region.name).str()); 1547 } 1548 1549 void OpEmitter::genCodeForAddingArgAndRegionForBuilder(OpMethodBody &body, 1550 bool isRawValueAttr) { 1551 // Push all operands to the result. 1552 for (int i = 0, e = op.getNumOperands(); i < e; ++i) { 1553 std::string argName = getArgumentName(op, i); 1554 if (op.getOperand(i).isOptional()) 1555 body << " if (" << argName << ")\n "; 1556 body << " " << builderOpState << ".addOperands(" << argName << ");\n"; 1557 } 1558 1559 // If the operation has the operand segment size attribute, add it here. 1560 if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) { 1561 body << " " << builderOpState 1562 << ".addAttribute(operand_segment_sizesAttrName(" << builderOpState 1563 << ".name), " 1564 << "odsBuilder.getI32VectorAttr({"; 1565 interleaveComma(llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) { 1566 if (op.getOperand(i).isOptional()) 1567 body << "(" << getArgumentName(op, i) << " ? 1 : 0)"; 1568 else if (op.getOperand(i).isVariadic()) 1569 body << "static_cast<int32_t>(" << getArgumentName(op, i) << ".size())"; 1570 else 1571 body << "1"; 1572 }); 1573 body << "}));\n"; 1574 } 1575 1576 // Push all attributes to the result. 1577 for (const auto &namedAttr : op.getAttributes()) { 1578 auto &attr = namedAttr.attr; 1579 if (!attr.isDerivedAttr()) { 1580 bool emitNotNullCheck = attr.isOptional(); 1581 if (emitNotNullCheck) 1582 body << formatv(" if ({0}) ", namedAttr.name) << "{\n"; 1583 1584 if (isRawValueAttr && canUseUnwrappedRawValue(attr)) { 1585 // If this is a raw value, then we need to wrap it in an Attribute 1586 // instance. 1587 FmtContext fctx; 1588 fctx.withBuilder("odsBuilder"); 1589 1590 std::string builderTemplate = 1591 std::string(attr.getConstBuilderTemplate()); 1592 1593 // For StringAttr, its constant builder call will wrap the input in 1594 // quotes, which is correct for normal string literals, but incorrect 1595 // here given we use function arguments. So we need to strip the 1596 // wrapping quotes. 1597 if (StringRef(builderTemplate).contains("\"$0\"")) 1598 builderTemplate = replaceAllSubstrs(builderTemplate, "\"$0\"", "$0"); 1599 1600 std::string value = 1601 std::string(tgfmt(builderTemplate, &fctx, namedAttr.name)); 1602 body << formatv(" {0}.addAttribute({1}AttrName({0}.name), {2});\n", 1603 builderOpState, namedAttr.name, value); 1604 } else { 1605 body << formatv(" {0}.addAttribute({1}AttrName({0}.name), {1});\n", 1606 builderOpState, namedAttr.name); 1607 } 1608 if (emitNotNullCheck) 1609 body << " }\n"; 1610 } 1611 } 1612 1613 // Create the correct number of regions. 1614 for (const NamedRegion ®ion : op.getRegions()) { 1615 if (region.isVariadic()) 1616 body << formatv(" for (unsigned i = 0; i < {0}Count; ++i)\n ", 1617 region.name); 1618 1619 body << " (void)" << builderOpState << ".addRegion();\n"; 1620 } 1621 1622 // Push all successors to the result. 1623 for (const NamedSuccessor &namedSuccessor : op.getSuccessors()) { 1624 body << formatv(" {0}.addSuccessors({1});\n", builderOpState, 1625 namedSuccessor.name); 1626 } 1627 } 1628 1629 void OpEmitter::genCanonicalizerDecls() { 1630 bool hasCanonicalizeMethod = def.getValueAsBit("hasCanonicalizeMethod"); 1631 if (hasCanonicalizeMethod) { 1632 // static LogicResult FooOp:: 1633 // canonicalize(FooOp op, PatternRewriter &rewriter); 1634 SmallVector<OpMethodParameter, 2> paramList; 1635 paramList.emplace_back(op.getCppClassName(), "op"); 1636 paramList.emplace_back("::mlir::PatternRewriter &", "rewriter"); 1637 opClass.addMethodAndPrune("::mlir::LogicalResult", "canonicalize", 1638 OpMethod::MP_StaticDeclaration, 1639 std::move(paramList)); 1640 } 1641 1642 // We get a prototype for 'getCanonicalizationPatterns' if requested directly 1643 // or if using a 'canonicalize' method. 1644 bool hasCanonicalizer = def.getValueAsBit("hasCanonicalizer"); 1645 if (!hasCanonicalizeMethod && !hasCanonicalizer) 1646 return; 1647 1648 // We get a body for 'getCanonicalizationPatterns' when using a 'canonicalize' 1649 // method, but not implementing 'getCanonicalizationPatterns' manually. 1650 bool hasBody = hasCanonicalizeMethod && !hasCanonicalizer; 1651 1652 // Add a signature for getCanonicalizationPatterns if implemented by the 1653 // dialect or if synthesized to call 'canonicalize'. 1654 SmallVector<OpMethodParameter, 2> paramList; 1655 paramList.emplace_back("::mlir::RewritePatternSet &", "results"); 1656 paramList.emplace_back("::mlir::MLIRContext *", "context"); 1657 auto kind = hasBody ? OpMethod::MP_Static : OpMethod::MP_StaticDeclaration; 1658 auto *method = opClass.addMethodAndPrune( 1659 "void", "getCanonicalizationPatterns", kind, std::move(paramList)); 1660 1661 // If synthesizing the method, fill it it. 1662 if (hasBody) 1663 method->body() << " results.add(canonicalize);\n"; 1664 } 1665 1666 void OpEmitter::genFolderDecls() { 1667 bool hasSingleResult = 1668 op.getNumResults() == 1 && op.getNumVariableLengthResults() == 0; 1669 1670 if (def.getValueAsBit("hasFolder")) { 1671 if (hasSingleResult) { 1672 opClass.addMethodAndPrune( 1673 "::mlir::OpFoldResult", "fold", OpMethod::MP_Declaration, 1674 "::llvm::ArrayRef<::mlir::Attribute>", "operands"); 1675 } else { 1676 SmallVector<OpMethodParameter, 2> paramList; 1677 paramList.emplace_back("::llvm::ArrayRef<::mlir::Attribute>", "operands"); 1678 paramList.emplace_back("::llvm::SmallVectorImpl<::mlir::OpFoldResult> &", 1679 "results"); 1680 opClass.addMethodAndPrune("::mlir::LogicalResult", "fold", 1681 OpMethod::MP_Declaration, std::move(paramList)); 1682 } 1683 } 1684 } 1685 1686 void OpEmitter::genOpInterfaceMethods(const tblgen::InterfaceTrait *opTrait) { 1687 Interface interface = opTrait->getInterface(); 1688 1689 // Get the set of methods that should always be declared. 1690 auto alwaysDeclaredMethodsVec = opTrait->getAlwaysDeclaredMethods(); 1691 llvm::StringSet<> alwaysDeclaredMethods; 1692 alwaysDeclaredMethods.insert(alwaysDeclaredMethodsVec.begin(), 1693 alwaysDeclaredMethodsVec.end()); 1694 1695 for (const InterfaceMethod &method : interface.getMethods()) { 1696 // Don't declare if the method has a body. 1697 if (method.getBody()) 1698 continue; 1699 // Don't declare if the method has a default implementation and the op 1700 // didn't request that it always be declared. 1701 if (method.getDefaultImplementation() && 1702 !alwaysDeclaredMethods.count(method.getName())) 1703 continue; 1704 genOpInterfaceMethod(method); 1705 } 1706 } 1707 1708 OpMethod *OpEmitter::genOpInterfaceMethod(const InterfaceMethod &method, 1709 bool declaration) { 1710 SmallVector<OpMethodParameter, 4> paramList; 1711 for (const InterfaceMethod::Argument &arg : method.getArguments()) 1712 paramList.emplace_back(arg.type, arg.name); 1713 1714 auto properties = method.isStatic() ? OpMethod::MP_Static : OpMethod::MP_None; 1715 if (declaration) 1716 properties = 1717 static_cast<OpMethod::Property>(properties | OpMethod::MP_Declaration); 1718 return opClass.addMethodAndPrune(method.getReturnType(), method.getName(), 1719 properties, std::move(paramList)); 1720 } 1721 1722 void OpEmitter::genOpInterfaceMethods() { 1723 for (const auto &trait : op.getTraits()) { 1724 if (const auto *opTrait = dyn_cast<tblgen::InterfaceTrait>(&trait)) 1725 if (opTrait->shouldDeclareMethods()) 1726 genOpInterfaceMethods(opTrait); 1727 } 1728 } 1729 1730 void OpEmitter::genSideEffectInterfaceMethods() { 1731 enum EffectKind { Operand, Result, Symbol, Static }; 1732 struct EffectLocation { 1733 /// The effect applied. 1734 SideEffect effect; 1735 1736 /// The index if the kind is not static. 1737 unsigned index : 30; 1738 1739 /// The kind of the location. 1740 unsigned kind : 2; 1741 }; 1742 1743 StringMap<SmallVector<EffectLocation, 1>> interfaceEffects; 1744 auto resolveDecorators = [&](Operator::var_decorator_range decorators, 1745 unsigned index, unsigned kind) { 1746 for (auto decorator : decorators) 1747 if (SideEffect *effect = dyn_cast<SideEffect>(&decorator)) { 1748 opClass.addTrait(effect->getInterfaceTrait()); 1749 interfaceEffects[effect->getBaseEffectName()].push_back( 1750 EffectLocation{*effect, index, kind}); 1751 } 1752 }; 1753 1754 // Collect effects that were specified via: 1755 /// Traits. 1756 for (const auto &trait : op.getTraits()) { 1757 const auto *opTrait = dyn_cast<tblgen::SideEffectTrait>(&trait); 1758 if (!opTrait) 1759 continue; 1760 auto &effects = interfaceEffects[opTrait->getBaseEffectName()]; 1761 for (auto decorator : opTrait->getEffects()) 1762 effects.push_back(EffectLocation{cast<SideEffect>(decorator), 1763 /*index=*/0, EffectKind::Static}); 1764 } 1765 /// Attributes and Operands. 1766 for (unsigned i = 0, operandIt = 0, e = op.getNumArgs(); i != e; ++i) { 1767 Argument arg = op.getArg(i); 1768 if (arg.is<NamedTypeConstraint *>()) { 1769 resolveDecorators(op.getArgDecorators(i), operandIt, EffectKind::Operand); 1770 ++operandIt; 1771 continue; 1772 } 1773 const NamedAttribute *attr = arg.get<NamedAttribute *>(); 1774 if (attr->attr.getBaseAttr().isSymbolRefAttr()) 1775 resolveDecorators(op.getArgDecorators(i), i, EffectKind::Symbol); 1776 } 1777 /// Results. 1778 for (unsigned i = 0, e = op.getNumResults(); i != e; ++i) 1779 resolveDecorators(op.getResultDecorators(i), i, EffectKind::Result); 1780 1781 // The code used to add an effect instance. 1782 // {0}: The effect class. 1783 // {1}: Optional value or symbol reference. 1784 // {1}: The resource class. 1785 const char *addEffectCode = 1786 " effects.emplace_back({0}::get(), {1}{2}::get());\n"; 1787 1788 for (auto &it : interfaceEffects) { 1789 // Generate the 'getEffects' method. 1790 std::string type = llvm::formatv("::mlir::SmallVectorImpl<::mlir::" 1791 "SideEffects::EffectInstance<{0}>> &", 1792 it.first()) 1793 .str(); 1794 auto *getEffects = 1795 opClass.addMethodAndPrune("void", "getEffects", type, "effects"); 1796 auto &body = getEffects->body(); 1797 1798 // Add effect instances for each of the locations marked on the operation. 1799 for (auto &location : it.second) { 1800 StringRef effect = location.effect.getName(); 1801 StringRef resource = location.effect.getResource(); 1802 if (location.kind == EffectKind::Static) { 1803 // A static instance has no attached value. 1804 body << llvm::formatv(addEffectCode, effect, "", resource).str(); 1805 } else if (location.kind == EffectKind::Symbol) { 1806 // A symbol reference requires adding the proper attribute. 1807 const auto *attr = op.getArg(location.index).get<NamedAttribute *>(); 1808 if (attr->attr.isOptional()) { 1809 body << " if (auto symbolRef = " << attr->name << "Attr())\n " 1810 << llvm::formatv(addEffectCode, effect, "symbolRef, ", resource) 1811 .str(); 1812 } else { 1813 body << llvm::formatv(addEffectCode, effect, attr->name + "(), ", 1814 resource) 1815 .str(); 1816 } 1817 } else { 1818 // Otherwise this is an operand/result, so we need to attach the Value. 1819 body << " for (::mlir::Value value : getODS" 1820 << (location.kind == EffectKind::Operand ? "Operands" : "Results") 1821 << "(" << location.index << "))\n " 1822 << llvm::formatv(addEffectCode, effect, "value, ", resource).str(); 1823 } 1824 } 1825 } 1826 } 1827 1828 void OpEmitter::genTypeInterfaceMethods() { 1829 if (!op.allResultTypesKnown()) 1830 return; 1831 // Generate 'inferReturnTypes' method declaration using the interface method 1832 // declared in 'InferTypeOpInterface' op interface. 1833 const auto *trait = dyn_cast<InterfaceTrait>( 1834 op.getTrait("::mlir::InferTypeOpInterface::Trait")); 1835 Interface interface = trait->getInterface(); 1836 OpMethod *method = [&]() -> OpMethod * { 1837 for (const InterfaceMethod &interfaceMethod : interface.getMethods()) { 1838 if (interfaceMethod.getName() == "inferReturnTypes") { 1839 return genOpInterfaceMethod(interfaceMethod, /*declaration=*/false); 1840 } 1841 } 1842 assert(0 && "unable to find inferReturnTypes interface method"); 1843 return nullptr; 1844 }(); 1845 auto &body = method->body(); 1846 body << " inferredReturnTypes.resize(" << op.getNumResults() << ");\n"; 1847 1848 FmtContext fctx; 1849 fctx.withBuilder("odsBuilder"); 1850 body << " ::mlir::Builder odsBuilder(context);\n"; 1851 1852 auto emitType = 1853 [&](const tblgen::Operator::ArgOrType &type) -> OpMethodBody & { 1854 if (type.isArg()) { 1855 auto argIndex = type.getArg(); 1856 assert(!op.getArg(argIndex).is<NamedAttribute *>()); 1857 auto arg = op.getArgToOperandOrAttribute(argIndex); 1858 if (arg.kind() == Operator::OperandOrAttribute::Kind::Operand) 1859 return body << "operands[" << arg.operandOrAttributeIndex() 1860 << "].getType()"; 1861 return body << "attributes[" << arg.operandOrAttributeIndex() 1862 << "].getType()"; 1863 } else { 1864 return body << tgfmt(*type.getType().getBuilderCall(), &fctx); 1865 } 1866 }; 1867 1868 for (int i = 0, e = op.getNumResults(); i != e; ++i) { 1869 body << " inferredReturnTypes[" << i << "] = "; 1870 auto types = op.getSameTypeAsResult(i); 1871 emitType(types[0]) << ";\n"; 1872 if (types.size() == 1) 1873 continue; 1874 // TODO: We could verify equality here, but skipping that for verification. 1875 } 1876 body << " return ::mlir::success();"; 1877 } 1878 1879 void OpEmitter::genParser() { 1880 if (!hasStringAttribute(def, "parser") || 1881 hasStringAttribute(def, "assemblyFormat")) 1882 return; 1883 1884 SmallVector<OpMethodParameter, 2> paramList; 1885 paramList.emplace_back("::mlir::OpAsmParser &", "parser"); 1886 paramList.emplace_back("::mlir::OperationState &", "result"); 1887 auto *method = 1888 opClass.addMethodAndPrune("::mlir::ParseResult", "parse", 1889 OpMethod::MP_Static, std::move(paramList)); 1890 1891 FmtContext fctx; 1892 fctx.addSubst("cppClass", opClass.getClassName()); 1893 auto parser = def.getValueAsString("parser").ltrim().rtrim(" \t\v\f\r"); 1894 method->body() << " " << tgfmt(parser, &fctx); 1895 } 1896 1897 void OpEmitter::genPrinter() { 1898 if (hasStringAttribute(def, "assemblyFormat")) 1899 return; 1900 1901 auto valueInit = def.getValueInit("printer"); 1902 StringInit *stringInit = dyn_cast<StringInit>(valueInit); 1903 if (!stringInit) 1904 return; 1905 1906 auto *method = 1907 opClass.addMethodAndPrune("void", "print", "::mlir::OpAsmPrinter &", "p"); 1908 FmtContext fctx; 1909 fctx.addSubst("cppClass", opClass.getClassName()); 1910 auto printer = stringInit->getValue().ltrim().rtrim(" \t\v\f\r"); 1911 method->body() << " " << tgfmt(printer, &fctx); 1912 } 1913 1914 void OpEmitter::genVerifier() { 1915 auto *method = opClass.addMethodAndPrune("::mlir::LogicalResult", "verify"); 1916 auto &body = method->body(); 1917 body << " if (::mlir::failed(" << op.getAdaptorName() 1918 << "(*this).verify((*this)->getLoc()))) " 1919 << "return ::mlir::failure();\n"; 1920 1921 auto *valueInit = def.getValueInit("verifier"); 1922 StringInit *stringInit = dyn_cast<StringInit>(valueInit); 1923 bool hasCustomVerify = stringInit && !stringInit->getValue().empty(); 1924 populateSubstitutions(op, "(*this)->getAttr", "this->getODSOperands", 1925 "this->getODSResults", verifyCtx); 1926 1927 genAttributeVerifier(op, "(*this)->getAttr", "emitOpError(", 1928 /*emitVerificationRequiringOp=*/true, verifyCtx, body); 1929 genOperandResultVerifier(body, op.getOperands(), "operand"); 1930 genOperandResultVerifier(body, op.getResults(), "result"); 1931 1932 for (auto &trait : op.getTraits()) { 1933 if (auto *t = dyn_cast<tblgen::PredTrait>(&trait)) { 1934 body << tgfmt(" if (!($0))\n " 1935 "return emitOpError(\"failed to verify that $1\");\n", 1936 &verifyCtx, tgfmt(t->getPredTemplate(), &verifyCtx), 1937 t->getSummary()); 1938 } 1939 } 1940 1941 genRegionVerifier(body); 1942 genSuccessorVerifier(body); 1943 1944 if (hasCustomVerify) { 1945 FmtContext fctx; 1946 fctx.addSubst("cppClass", opClass.getClassName()); 1947 auto printer = stringInit->getValue().ltrim().rtrim(" \t\v\f\r"); 1948 body << " " << tgfmt(printer, &fctx); 1949 } else { 1950 body << " return ::mlir::success();\n"; 1951 } 1952 } 1953 1954 void OpEmitter::genOperandResultVerifier(OpMethodBody &body, 1955 Operator::value_range values, 1956 StringRef valueKind) { 1957 FmtContext fctx; 1958 1959 body << " {\n"; 1960 body << " unsigned index = 0; (void)index;\n"; 1961 1962 for (auto staticValue : llvm::enumerate(values)) { 1963 bool hasPredicate = staticValue.value().hasPredicate(); 1964 bool isOptional = staticValue.value().isOptional(); 1965 if (!hasPredicate && !isOptional) 1966 continue; 1967 body << formatv(" auto valueGroup{2} = getODS{0}{1}s({2});\n", 1968 // Capitalize the first letter to match the function name 1969 valueKind.substr(0, 1).upper(), valueKind.substr(1), 1970 staticValue.index()); 1971 1972 // If the constraint is optional check that the value group has at most 1 1973 // value. 1974 if (isOptional) { 1975 body << formatv(" if (valueGroup{0}.size() > 1)\n" 1976 " return emitOpError(\"{1} group starting at #\") " 1977 "<< index << \" requires 0 or 1 element, but found \" << " 1978 "valueGroup{0}.size();\n", 1979 staticValue.index(), valueKind); 1980 } 1981 1982 // Otherwise, if there is no predicate there is nothing left to do. 1983 if (!hasPredicate) 1984 continue; 1985 // Emit a loop to check all the dynamic values in the pack. 1986 StringRef constraintFn = staticVerifierEmitter.getTypeConstraintFn( 1987 staticValue.value().constraint); 1988 body << " for (::mlir::Value v : valueGroup" << staticValue.index() 1989 << ") {\n" 1990 << " if (::mlir::failed(" << constraintFn 1991 << "(getOperation(), v.getType(), \"" << valueKind << "\", index)))\n" 1992 << " return ::mlir::failure();\n" 1993 << " ++index;\n" 1994 << " }\n"; 1995 } 1996 1997 body << " }\n"; 1998 } 1999 2000 void OpEmitter::genRegionVerifier(OpMethodBody &body) { 2001 // If we have no regions, there is nothing more to do. 2002 unsigned numRegions = op.getNumRegions(); 2003 if (numRegions == 0) 2004 return; 2005 2006 body << "{\n"; 2007 body << " unsigned index = 0; (void)index;\n"; 2008 2009 for (unsigned i = 0; i < numRegions; ++i) { 2010 const auto ®ion = op.getRegion(i); 2011 if (region.constraint.getPredicate().isNull()) 2012 continue; 2013 2014 body << " for (::mlir::Region ®ion : "; 2015 body << formatv(region.isVariadic() 2016 ? "{0}()" 2017 : "::mlir::MutableArrayRef<::mlir::Region>((*this)" 2018 "->getRegion({1}))", 2019 region.name, i); 2020 body << ") {\n"; 2021 auto constraint = tgfmt(region.constraint.getConditionTemplate(), 2022 &verifyCtx.withSelf("region")) 2023 .str(); 2024 2025 body << formatv(" (void)region;\n" 2026 " if (!({0})) {\n " 2027 "return emitOpError(\"region #\") << index << \" {1}" 2028 "failed to " 2029 "verify constraint: {2}\";\n }\n", 2030 constraint, 2031 region.name.empty() ? "" : "('" + region.name + "') ", 2032 region.constraint.getSummary()) 2033 << " ++index;\n" 2034 << " }\n"; 2035 } 2036 body << " }\n"; 2037 } 2038 2039 void OpEmitter::genSuccessorVerifier(OpMethodBody &body) { 2040 // If we have no successors, there is nothing more to do. 2041 unsigned numSuccessors = op.getNumSuccessors(); 2042 if (numSuccessors == 0) 2043 return; 2044 2045 body << "{\n"; 2046 body << " unsigned index = 0; (void)index;\n"; 2047 2048 for (unsigned i = 0; i < numSuccessors; ++i) { 2049 const auto &successor = op.getSuccessor(i); 2050 if (successor.constraint.getPredicate().isNull()) 2051 continue; 2052 2053 if (successor.isVariadic()) { 2054 body << formatv(" for (::mlir::Block *successor : {0}()) {\n", 2055 successor.name); 2056 } else { 2057 body << " {\n"; 2058 body << formatv(" ::mlir::Block *successor = {0}();\n", 2059 successor.name); 2060 } 2061 auto constraint = tgfmt(successor.constraint.getConditionTemplate(), 2062 &verifyCtx.withSelf("successor")) 2063 .str(); 2064 2065 body << formatv(" (void)successor;\n" 2066 " if (!({0})) {\n " 2067 "return emitOpError(\"successor #\") << index << \"('{1}') " 2068 "failed to " 2069 "verify constraint: {2}\";\n }\n", 2070 constraint, successor.name, 2071 successor.constraint.getSummary()) 2072 << " ++index;\n" 2073 << " }\n"; 2074 } 2075 body << " }\n"; 2076 } 2077 2078 /// Add a size count trait to the given operation class. 2079 static void addSizeCountTrait(OpClass &opClass, StringRef traitKind, 2080 int numTotal, int numVariadic) { 2081 if (numVariadic != 0) { 2082 if (numTotal == numVariadic) 2083 opClass.addTrait("::mlir::OpTrait::Variadic" + traitKind + "s"); 2084 else 2085 opClass.addTrait("::mlir::OpTrait::AtLeastN" + traitKind + "s<" + 2086 Twine(numTotal - numVariadic) + ">::Impl"); 2087 return; 2088 } 2089 switch (numTotal) { 2090 case 0: 2091 opClass.addTrait("::mlir::OpTrait::Zero" + traitKind); 2092 break; 2093 case 1: 2094 opClass.addTrait("::mlir::OpTrait::One" + traitKind); 2095 break; 2096 default: 2097 opClass.addTrait("::mlir::OpTrait::N" + traitKind + "s<" + Twine(numTotal) + 2098 ">::Impl"); 2099 break; 2100 } 2101 } 2102 2103 void OpEmitter::genTraits() { 2104 // Add region size trait. 2105 unsigned numRegions = op.getNumRegions(); 2106 unsigned numVariadicRegions = op.getNumVariadicRegions(); 2107 addSizeCountTrait(opClass, "Region", numRegions, numVariadicRegions); 2108 2109 // Add result size traits. 2110 int numResults = op.getNumResults(); 2111 int numVariadicResults = op.getNumVariableLengthResults(); 2112 addSizeCountTrait(opClass, "Result", numResults, numVariadicResults); 2113 2114 // For single result ops with a known specific type, generate a OneTypedResult 2115 // trait. 2116 if (numResults == 1 && numVariadicResults == 0) { 2117 auto cppName = op.getResults().begin()->constraint.getCPPClassName(); 2118 opClass.addTrait("::mlir::OpTrait::OneTypedResult<" + cppName + ">::Impl"); 2119 } 2120 2121 // Add successor size trait. 2122 unsigned numSuccessors = op.getNumSuccessors(); 2123 unsigned numVariadicSuccessors = op.getNumVariadicSuccessors(); 2124 addSizeCountTrait(opClass, "Successor", numSuccessors, numVariadicSuccessors); 2125 2126 // Add variadic size trait and normal op traits. 2127 int numOperands = op.getNumOperands(); 2128 int numVariadicOperands = op.getNumVariableLengthOperands(); 2129 2130 // Add operand size trait. 2131 if (numVariadicOperands != 0) { 2132 if (numOperands == numVariadicOperands) 2133 opClass.addTrait("::mlir::OpTrait::VariadicOperands"); 2134 else 2135 opClass.addTrait("::mlir::OpTrait::AtLeastNOperands<" + 2136 Twine(numOperands - numVariadicOperands) + ">::Impl"); 2137 } else { 2138 switch (numOperands) { 2139 case 0: 2140 opClass.addTrait("::mlir::OpTrait::ZeroOperands"); 2141 break; 2142 case 1: 2143 opClass.addTrait("::mlir::OpTrait::OneOperand"); 2144 break; 2145 default: 2146 opClass.addTrait("::mlir::OpTrait::NOperands<" + Twine(numOperands) + 2147 ">::Impl"); 2148 break; 2149 } 2150 } 2151 2152 // Add the native and interface traits. 2153 for (const auto &trait : op.getTraits()) { 2154 if (auto opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) 2155 opClass.addTrait(opTrait->getFullyQualifiedTraitName()); 2156 else if (auto opTrait = dyn_cast<tblgen::InterfaceTrait>(&trait)) 2157 opClass.addTrait(opTrait->getFullyQualifiedTraitName()); 2158 } 2159 } 2160 2161 void OpEmitter::genOpNameGetter() { 2162 auto *method = opClass.addMethodAndPrune( 2163 "::llvm::StringLiteral", "getOperationName", 2164 OpMethod::Property(OpMethod::MP_Static | OpMethod::MP_Constexpr)); 2165 method->body() << " return ::llvm::StringLiteral(\"" << op.getOperationName() 2166 << "\");"; 2167 } 2168 2169 void OpEmitter::genOpAsmInterface() { 2170 // If the user only has one results or specifically added the Asm trait, 2171 // then don't generate it for them. We specifically only handle multi result 2172 // operations, because the name of a single result in the common case is not 2173 // interesting(generally 'result'/'output'/etc.). 2174 // TODO: We could also add a flag to allow operations to opt in to this 2175 // generation, even if they only have a single operation. 2176 int numResults = op.getNumResults(); 2177 if (numResults <= 1 || op.getTrait("::mlir::OpAsmOpInterface::Trait")) 2178 return; 2179 2180 SmallVector<StringRef, 4> resultNames(numResults); 2181 for (int i = 0; i != numResults; ++i) 2182 resultNames[i] = op.getResultName(i); 2183 2184 // Don't add the trait if none of the results have a valid name. 2185 if (llvm::all_of(resultNames, [](StringRef name) { return name.empty(); })) 2186 return; 2187 opClass.addTrait("::mlir::OpAsmOpInterface::Trait"); 2188 2189 // Generate the right accessor for the number of results. 2190 auto *method = opClass.addMethodAndPrune( 2191 "void", "getAsmResultNames", "::mlir::OpAsmSetValueNameFn", "setNameFn"); 2192 auto &body = method->body(); 2193 for (int i = 0; i != numResults; ++i) { 2194 body << " auto resultGroup" << i << " = getODSResults(" << i << ");\n" 2195 << " if (!llvm::empty(resultGroup" << i << "))\n" 2196 << " setNameFn(*resultGroup" << i << ".begin(), \"" 2197 << resultNames[i] << "\");\n"; 2198 } 2199 } 2200 2201 //===----------------------------------------------------------------------===// 2202 // OpOperandAdaptor emitter 2203 //===----------------------------------------------------------------------===// 2204 2205 namespace { 2206 // Helper class to emit Op operand adaptors to an output stream. Operand 2207 // adaptors are wrappers around ArrayRef<Value> that provide named operand 2208 // getters identical to those defined in the Op. 2209 class OpOperandAdaptorEmitter { 2210 public: 2211 static void emitDecl(const Operator &op, raw_ostream &os); 2212 static void emitDef(const Operator &op, raw_ostream &os); 2213 2214 private: 2215 explicit OpOperandAdaptorEmitter(const Operator &op); 2216 2217 // Add verification function. This generates a verify method for the adaptor 2218 // which verifies all the op-independent attribute constraints. 2219 void addVerification(); 2220 2221 const Operator &op; 2222 Class adaptor; 2223 }; 2224 } // end namespace 2225 2226 OpOperandAdaptorEmitter::OpOperandAdaptorEmitter(const Operator &op) 2227 : op(op), adaptor(op.getAdaptorName()) { 2228 adaptor.newField("::mlir::ValueRange", "odsOperands"); 2229 adaptor.newField("::mlir::DictionaryAttr", "odsAttrs"); 2230 adaptor.newField("::mlir::RegionRange", "odsRegions"); 2231 const auto *attrSizedOperands = 2232 op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments"); 2233 { 2234 SmallVector<OpMethodParameter, 2> paramList; 2235 paramList.emplace_back("::mlir::ValueRange", "values"); 2236 paramList.emplace_back("::mlir::DictionaryAttr", "attrs", 2237 attrSizedOperands ? "" : "nullptr"); 2238 paramList.emplace_back("::mlir::RegionRange", "regions", "{}"); 2239 auto *constructor = adaptor.addConstructorAndPrune(std::move(paramList)); 2240 2241 constructor->addMemberInitializer("odsOperands", "values"); 2242 constructor->addMemberInitializer("odsAttrs", "attrs"); 2243 constructor->addMemberInitializer("odsRegions", "regions"); 2244 } 2245 2246 { 2247 auto *constructor = adaptor.addConstructorAndPrune( 2248 llvm::formatv("{0}&", op.getCppClassName()).str(), "op"); 2249 constructor->addMemberInitializer("odsOperands", "op->getOperands()"); 2250 constructor->addMemberInitializer("odsAttrs", "op->getAttrDictionary()"); 2251 constructor->addMemberInitializer("odsRegions", "op->getRegions()"); 2252 } 2253 2254 { 2255 auto *m = adaptor.addMethodAndPrune("::mlir::ValueRange", "getOperands"); 2256 m->body() << " return odsOperands;"; 2257 } 2258 std::string sizeAttrInit = 2259 formatv(adapterSegmentSizeAttrInitCode, "operand_segment_sizes"); 2260 generateNamedOperandGetters(op, adaptor, sizeAttrInit, 2261 /*rangeType=*/"::mlir::ValueRange", 2262 /*rangeBeginCall=*/"odsOperands.begin()", 2263 /*rangeSizeCall=*/"odsOperands.size()", 2264 /*getOperandCallPattern=*/"odsOperands[{0}]"); 2265 2266 FmtContext fctx; 2267 fctx.withBuilder("::mlir::Builder(odsAttrs.getContext())"); 2268 2269 auto emitAttr = [&](StringRef name, Attribute attr) { 2270 auto &body = adaptor.addMethodAndPrune(attr.getStorageType(), name)->body(); 2271 body << " assert(odsAttrs && \"no attributes when constructing adapter\");" 2272 << "\n " << attr.getStorageType() << " attr = " 2273 << "odsAttrs.get(\"" << name << "\")."; 2274 if (attr.hasDefaultValue() || attr.isOptional()) 2275 body << "dyn_cast_or_null<"; 2276 else 2277 body << "cast<"; 2278 body << attr.getStorageType() << ">();\n"; 2279 2280 if (attr.hasDefaultValue()) { 2281 // Use the default value if attribute is not set. 2282 // TODO: this is inefficient, we are recreating the attribute for every 2283 // call. This should be set instead. 2284 std::string defaultValue = std::string( 2285 tgfmt(attr.getConstBuilderTemplate(), &fctx, attr.getDefaultValue())); 2286 body << " if (!attr)\n attr = " << defaultValue << ";\n"; 2287 } 2288 body << " return attr;\n"; 2289 }; 2290 2291 { 2292 auto *m = 2293 adaptor.addMethodAndPrune("::mlir::DictionaryAttr", "getAttributes"); 2294 m->body() << " return odsAttrs;"; 2295 } 2296 for (auto &namedAttr : op.getAttributes()) { 2297 const auto &name = namedAttr.name; 2298 const auto &attr = namedAttr.attr; 2299 if (!attr.isDerivedAttr()) 2300 emitAttr(name, attr); 2301 } 2302 2303 unsigned numRegions = op.getNumRegions(); 2304 if (numRegions > 0) { 2305 auto *m = adaptor.addMethodAndPrune("::mlir::RegionRange", "getRegions"); 2306 m->body() << " return odsRegions;"; 2307 } 2308 for (unsigned i = 0; i < numRegions; ++i) { 2309 const auto ®ion = op.getRegion(i); 2310 if (region.name.empty()) 2311 continue; 2312 2313 // Generate the accessors for a variadic region. 2314 if (region.isVariadic()) { 2315 auto *m = adaptor.addMethodAndPrune("::mlir::RegionRange", region.name); 2316 m->body() << formatv(" return odsRegions.drop_front({0});", i); 2317 continue; 2318 } 2319 2320 auto *m = adaptor.addMethodAndPrune("::mlir::Region &", region.name); 2321 m->body() << formatv(" return *odsRegions[{0}];", i); 2322 } 2323 2324 // Add verification function. 2325 addVerification(); 2326 } 2327 2328 void OpOperandAdaptorEmitter::addVerification() { 2329 auto *method = adaptor.addMethodAndPrune("::mlir::LogicalResult", "verify", 2330 "::mlir::Location", "loc"); 2331 auto &body = method->body(); 2332 2333 const char *checkAttrSizedValueSegmentsCode = R"( 2334 { 2335 auto sizeAttr = odsAttrs.get("{0}").cast<::mlir::DenseIntElementsAttr>(); 2336 auto numElements = sizeAttr.getType().cast<::mlir::ShapedType>().getNumElements(); 2337 if (numElements != {1}) 2338 return emitError(loc, "'{0}' attribute for specifying {2} segments " 2339 "must have {1} elements, but got ") << numElements; 2340 } 2341 )"; 2342 2343 // Verify a few traits first so that we can use 2344 // getODSOperands()/getODSResults() in the rest of the verifier. 2345 for (auto &trait : op.getTraits()) { 2346 if (auto *t = dyn_cast<tblgen::NativeTrait>(&trait)) { 2347 if (t->getFullyQualifiedTraitName() == 2348 "::mlir::OpTrait::AttrSizedOperandSegments") { 2349 body << formatv(checkAttrSizedValueSegmentsCode, 2350 "operand_segment_sizes", op.getNumOperands(), 2351 "operand"); 2352 } else if (t->getFullyQualifiedTraitName() == 2353 "::mlir::OpTrait::AttrSizedResultSegments") { 2354 body << formatv(checkAttrSizedValueSegmentsCode, "result_segment_sizes", 2355 op.getNumResults(), "result"); 2356 } 2357 } 2358 } 2359 2360 FmtContext verifyCtx; 2361 populateSubstitutions(op, "odsAttrs.get", "getODSOperands", 2362 "<no results should be generated>", verifyCtx); 2363 genAttributeVerifier(op, "odsAttrs.get", 2364 Twine("emitError(loc, \"'") + op.getOperationName() + 2365 "' op \"", 2366 /*emitVerificationRequiringOp*/ false, verifyCtx, body); 2367 2368 body << " return ::mlir::success();"; 2369 } 2370 2371 void OpOperandAdaptorEmitter::emitDecl(const Operator &op, raw_ostream &os) { 2372 OpOperandAdaptorEmitter(op).adaptor.writeDeclTo(os); 2373 } 2374 2375 void OpOperandAdaptorEmitter::emitDef(const Operator &op, raw_ostream &os) { 2376 OpOperandAdaptorEmitter(op).adaptor.writeDefTo(os); 2377 } 2378 2379 // Emits the opcode enum and op classes. 2380 static void emitOpClasses(const RecordKeeper &recordKeeper, 2381 const std::vector<Record *> &defs, raw_ostream &os, 2382 bool emitDecl) { 2383 // First emit forward declaration for each class, this allows them to refer 2384 // to each others in traits for example. 2385 if (emitDecl) { 2386 os << "#if defined(GET_OP_CLASSES) || defined(GET_OP_FWD_DEFINES)\n"; 2387 os << "#undef GET_OP_FWD_DEFINES\n"; 2388 for (auto *def : defs) { 2389 Operator op(*def); 2390 NamespaceEmitter emitter(os, op.getCppNamespace()); 2391 os << "class " << op.getCppClassName() << ";\n"; 2392 } 2393 os << "#endif\n\n"; 2394 } 2395 2396 IfDefScope scope("GET_OP_CLASSES", os); 2397 if (defs.empty()) 2398 return; 2399 2400 // Generate all of the locally instantiated methods first. 2401 StaticVerifierFunctionEmitter staticVerifierEmitter(recordKeeper, os); 2402 os << formatv(opCommentHeader, "Local Utility Method", "Definitions"); 2403 staticVerifierEmitter.emitFunctionsFor( 2404 typeVerifierSignature, typeVerifierErrorHandler, /*typeArgName=*/"type", 2405 defs, emitDecl); 2406 2407 for (auto *def : defs) { 2408 Operator op(*def); 2409 if (emitDecl) { 2410 { 2411 NamespaceEmitter emitter(os, op.getCppNamespace()); 2412 os << formatv(opCommentHeader, op.getQualCppClassName(), 2413 "declarations"); 2414 OpOperandAdaptorEmitter::emitDecl(op, os); 2415 OpEmitter::emitDecl(op, os, staticVerifierEmitter); 2416 } 2417 // Emit the TypeID explicit specialization to have a single definition. 2418 if (!op.getCppNamespace().empty()) 2419 os << "DECLARE_EXPLICIT_TYPE_ID(" << op.getCppNamespace() 2420 << "::" << op.getCppClassName() << ")\n\n"; 2421 } else { 2422 { 2423 NamespaceEmitter emitter(os, op.getCppNamespace()); 2424 os << formatv(opCommentHeader, op.getQualCppClassName(), "definitions"); 2425 OpOperandAdaptorEmitter::emitDef(op, os); 2426 OpEmitter::emitDef(op, os, staticVerifierEmitter); 2427 } 2428 // Emit the TypeID explicit specialization to have a single definition. 2429 if (!op.getCppNamespace().empty()) 2430 os << "DEFINE_EXPLICIT_TYPE_ID(" << op.getCppNamespace() 2431 << "::" << op.getCppClassName() << ")\n\n"; 2432 } 2433 } 2434 } 2435 2436 // Emits a comma-separated list of the ops. 2437 static void emitOpList(const std::vector<Record *> &defs, raw_ostream &os) { 2438 IfDefScope scope("GET_OP_LIST", os); 2439 2440 interleave( 2441 // TODO: We are constructing the Operator wrapper instance just for 2442 // getting it's qualified class name here. Reduce the overhead by having a 2443 // lightweight version of Operator class just for that purpose. 2444 defs, [&os](Record *def) { os << Operator(def).getQualCppClassName(); }, 2445 [&os]() { os << ",\n"; }); 2446 } 2447 2448 static bool emitOpDecls(const RecordKeeper &recordKeeper, raw_ostream &os) { 2449 emitSourceFileHeader("Op Declarations", os); 2450 2451 std::vector<Record *> defs = getRequestedOpDefinitions(recordKeeper); 2452 emitOpClasses(recordKeeper, defs, os, /*emitDecl=*/true); 2453 2454 return false; 2455 } 2456 2457 static bool emitOpDefs(const RecordKeeper &recordKeeper, raw_ostream &os) { 2458 emitSourceFileHeader("Op Definitions", os); 2459 2460 std::vector<Record *> defs = getRequestedOpDefinitions(recordKeeper); 2461 emitOpList(defs, os); 2462 emitOpClasses(recordKeeper, defs, os, /*emitDecl=*/false); 2463 2464 return false; 2465 } 2466 2467 static mlir::GenRegistration 2468 genOpDecls("gen-op-decls", "Generate op declarations", 2469 [](const RecordKeeper &records, raw_ostream &os) { 2470 return emitOpDecls(records, os); 2471 }); 2472 2473 static mlir::GenRegistration genOpDefs("gen-op-defs", "Generate op definitions", 2474 [](const RecordKeeper &records, 2475 raw_ostream &os) { 2476 return emitOpDefs(records, os); 2477 }); 2478