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