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