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