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 genCodeForAddingArgAndRegionForBuilder( 689 body, /*isRawValueAttr=*/attrType == AttrParamKind::UnwrappedValue); 690 691 // Push all result types to the operation state 692 693 if (inferType) { 694 // Generate builder that infers type too. 695 // TODO(jpienaar): Subsume this with general checking if type can be 696 // inferred automatically. 697 // TODO(jpienaar): Expand to handle regions. 698 body << formatv(R"( 699 SmallVector<Type, 2> inferredReturnTypes; 700 if (succeeded({0}::inferReturnTypes(odsBuilder->getContext(), 701 {1}.location, {1}.operands, {1}.attributes, 702 /*regions=*/{{}, inferredReturnTypes))) 703 {1}.addTypes(inferredReturnTypes); 704 else 705 llvm::report_fatal_error("Failed to infer result type(s).");)", 706 opClass.getClassName(), builderOpState); 707 return; 708 } 709 710 switch (paramKind) { 711 case TypeParamKind::None: 712 return; 713 case TypeParamKind::Separate: 714 for (int i = 0, e = op.getNumResults(); i < e; ++i) { 715 if (op.getResult(i).isOptional()) 716 body << " if (" << resultNames[i] << ")\n "; 717 body << " " << builderOpState << ".addTypes(" << resultNames[i] 718 << ");\n"; 719 } 720 return; 721 case TypeParamKind::Collective: 722 body << " " 723 << "assert(resultTypes.size() " 724 << (op.getNumVariableLengthResults() == 0 ? "==" : ">=") << " " 725 << (op.getNumResults() - op.getNumVariableLengthResults()) 726 << "u && \"mismatched number of results\");\n"; 727 body << " " << builderOpState << ".addTypes(resultTypes);\n"; 728 return; 729 } 730 llvm_unreachable("unhandled TypeParamKind"); 731 }; 732 733 bool canInferType = 734 op.getTrait("InferTypeOpInterface::Trait") && op.getNumRegions() == 0; 735 for (auto attrType : attrBuilderType) { 736 emit(attrType, TypeParamKind::Separate, /*inferType=*/false); 737 if (canInferType) 738 emit(attrType, TypeParamKind::None, /*inferType=*/true); 739 // Emit separate arg build with collective type, unless there is only one 740 // variadic result, in which case the above would have already generated 741 // the same build method. 742 if (!(op.getNumResults() == 1 && op.getResult(0).isVariableLength())) 743 emit(attrType, TypeParamKind::Collective, /*inferType=*/false); 744 } 745 } 746 747 void OpEmitter::genUseOperandAsResultTypeCollectiveParamBuilder() { 748 // If this op has a variadic result, we cannot generate this builder because 749 // we don't know how many results to create. 750 if (op.getNumVariableLengthResults() != 0) 751 return; 752 753 int numResults = op.getNumResults(); 754 755 // Signature 756 std::string params = 757 std::string("Builder *odsBuilder, OperationState &") + builderOpState + 758 ", ValueRange operands, ArrayRef<NamedAttribute> attributes"; 759 if (op.getNumVariadicRegions()) 760 params += ", unsigned numRegions"; 761 auto &m = opClass.newMethod("void", "build", params, OpMethod::MP_Static); 762 auto &body = m.body(); 763 764 // Operands 765 body << " " << builderOpState << ".addOperands(operands);\n\n"; 766 767 // Attributes 768 body << " " << builderOpState << ".addAttributes(attributes);\n"; 769 770 // Create the correct number of regions 771 if (int numRegions = op.getNumRegions()) { 772 body << llvm::formatv( 773 " for (unsigned i = 0; i != {0}; ++i)\n", 774 (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions))); 775 body << " (void)" << builderOpState << ".addRegion();\n"; 776 } 777 778 // Result types 779 SmallVector<std::string, 2> resultTypes(numResults, "operands[0].getType()"); 780 body << " " << builderOpState << ".addTypes({" 781 << llvm::join(resultTypes, ", ") << "});\n\n"; 782 } 783 784 void OpEmitter::genInferredTypeCollectiveParamBuilder() { 785 // TODO(jpienaar): Expand to support regions. 786 const char *params = 787 "Builder *odsBuilder, OperationState &{0}, " 788 "ValueRange operands, ArrayRef<NamedAttribute> attributes"; 789 auto &m = 790 opClass.newMethod("void", "build", formatv(params, builderOpState).str(), 791 OpMethod::MP_Static); 792 auto &body = m.body(); 793 body << formatv(R"( 794 SmallVector<Type, 2> inferredReturnTypes; 795 if (succeeded({0}::inferReturnTypes(odsBuilder->getContext(), 796 {1}.location, operands, attributes, 797 /*regions=*/{{}, inferredReturnTypes))) 798 build(odsBuilder, odsState, inferredReturnTypes, operands, attributes); 799 else 800 llvm::report_fatal_error("Failed to infer result type(s).");)", 801 opClass.getClassName(), builderOpState); 802 } 803 804 void OpEmitter::genUseOperandAsResultTypeSeparateParamBuilder() { 805 std::string paramList; 806 llvm::SmallVector<std::string, 4> resultNames; 807 buildParamList(paramList, resultNames, TypeParamKind::None); 808 809 auto &m = opClass.newMethod("void", "build", paramList, OpMethod::MP_Static); 810 genCodeForAddingArgAndRegionForBuilder(m.body()); 811 812 auto numResults = op.getNumResults(); 813 if (numResults == 0) 814 return; 815 816 // Push all result types to the operation state 817 const char *index = op.getOperand(0).isVariadic() ? ".front()" : ""; 818 std::string resultType = 819 formatv("{0}{1}.getType()", getArgumentName(op, 0), index).str(); 820 m.body() << " " << builderOpState << ".addTypes({" << resultType; 821 for (int i = 1; i != numResults; ++i) 822 m.body() << ", " << resultType; 823 m.body() << "});\n\n"; 824 } 825 826 void OpEmitter::genUseAttrAsResultTypeBuilder() { 827 std::string params = 828 std::string("Builder *odsBuilder, OperationState &") + builderOpState + 829 ", ValueRange operands, ArrayRef<NamedAttribute> attributes"; 830 auto &m = opClass.newMethod("void", "build", params, OpMethod::MP_Static); 831 auto &body = m.body(); 832 833 // Push all result types to the operation state 834 std::string resultType; 835 const auto &namedAttr = op.getAttribute(0); 836 837 body << " for (auto attr : attributes) {\n"; 838 body << " if (attr.first != \"" << namedAttr.name << "\") continue;\n"; 839 if (namedAttr.attr.isTypeAttr()) { 840 resultType = "attr.second.cast<TypeAttr>().getValue()"; 841 } else { 842 resultType = "attr.second.getType()"; 843 } 844 845 // Operands 846 body << " " << builderOpState << ".addOperands(operands);\n\n"; 847 // Attributes 848 body << " " << builderOpState << ".addAttributes(attributes);\n"; 849 850 // Result types 851 SmallVector<std::string, 2> resultTypes(op.getNumResults(), resultType); 852 body << " " << builderOpState << ".addTypes({" 853 << llvm::join(resultTypes, ", ") << "});\n"; 854 body << " }\n"; 855 } 856 857 void OpEmitter::genBuilder() { 858 // Handle custom builders if provided. 859 // TODO(antiagainst): Create wrapper class for OpBuilder to hide the native 860 // TableGen API calls here. 861 { 862 auto *listInit = dyn_cast_or_null<ListInit>(def.getValueInit("builders")); 863 if (listInit) { 864 for (Init *init : listInit->getValues()) { 865 Record *builderDef = cast<DefInit>(init)->getDef(); 866 StringRef params = builderDef->getValueAsString("params"); 867 StringRef body = builderDef->getValueAsString("body"); 868 bool hasBody = !body.empty(); 869 870 auto &method = 871 opClass.newMethod("void", "build", params, OpMethod::MP_Static, 872 /*declOnly=*/!hasBody); 873 if (hasBody) 874 method.body() << body; 875 } 876 } 877 if (op.skipDefaultBuilders()) { 878 if (!listInit || listInit->empty()) 879 PrintFatalError( 880 op.getLoc(), 881 "default builders are skipped and no custom builders provided"); 882 return; 883 } 884 } 885 886 // Generate default builders that requires all result type, operands, and 887 // attributes as parameters. 888 889 // We generate three classes of builders here: 890 // 1. one having a stand-alone parameter for each operand / attribute, and 891 genSeparateArgParamBuilder(); 892 // 2. one having an aggregated parameter for all result types / operands / 893 // attributes, and 894 genCollectiveParamBuilder(); 895 // 3. one having a stand-alone parameter for each operand and attribute, 896 // use the first operand or attribute's type as all result types 897 // to facilitate different call patterns. 898 if (op.getNumVariableLengthResults() == 0) { 899 if (op.getTrait("OpTrait::SameOperandsAndResultType")) { 900 genUseOperandAsResultTypeSeparateParamBuilder(); 901 genUseOperandAsResultTypeCollectiveParamBuilder(); 902 } 903 if (op.getTrait("OpTrait::FirstAttrDerivedResultType")) 904 genUseAttrAsResultTypeBuilder(); 905 } 906 } 907 908 void OpEmitter::genCollectiveParamBuilder() { 909 int numResults = op.getNumResults(); 910 int numVariadicResults = op.getNumVariableLengthResults(); 911 int numNonVariadicResults = numResults - numVariadicResults; 912 913 int numOperands = op.getNumOperands(); 914 int numVariadicOperands = op.getNumVariableLengthOperands(); 915 int numNonVariadicOperands = numOperands - numVariadicOperands; 916 // Signature 917 std::string params = std::string("Builder *, OperationState &") + 918 builderOpState + 919 ", ArrayRef<Type> resultTypes, ValueRange operands, " 920 "ArrayRef<NamedAttribute> attributes"; 921 if (op.getNumVariadicRegions()) 922 params += ", unsigned numRegions"; 923 auto &m = opClass.newMethod("void", "build", params, OpMethod::MP_Static); 924 auto &body = m.body(); 925 926 // Operands 927 if (numVariadicOperands == 0 || numNonVariadicOperands != 0) 928 body << " assert(operands.size()" 929 << (numVariadicOperands != 0 ? " >= " : " == ") 930 << numNonVariadicOperands 931 << "u && \"mismatched number of parameters\");\n"; 932 body << " " << builderOpState << ".addOperands(operands);\n\n"; 933 934 // Attributes 935 body << " " << builderOpState << ".addAttributes(attributes);\n"; 936 937 // Create the correct number of regions 938 if (int numRegions = op.getNumRegions()) { 939 body << llvm::formatv( 940 " for (unsigned i = 0; i != {0}; ++i)\n", 941 (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions))); 942 body << " (void)" << builderOpState << ".addRegion();\n"; 943 } 944 945 // Result types 946 if (numVariadicResults == 0 || numNonVariadicResults != 0) 947 body << " assert(resultTypes.size()" 948 << (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults 949 << "u && \"mismatched number of return types\");\n"; 950 body << " " << builderOpState << ".addTypes(resultTypes);\n"; 951 952 // Generate builder that infers type too. 953 // TODO(jpienaar): Subsume this with general checking if type can be inferred 954 // automatically. 955 // TODO(jpienaar): Expand to handle regions and successors. 956 if (op.getTrait("InferTypeOpInterface::Trait") && op.getNumRegions() == 0 && 957 op.getNumSuccessors() == 0) 958 genInferredTypeCollectiveParamBuilder(); 959 } 960 961 void OpEmitter::buildParamList(std::string ¶mList, 962 SmallVectorImpl<std::string> &resultTypeNames, 963 TypeParamKind typeParamKind, 964 AttrParamKind attrParamKind) { 965 resultTypeNames.clear(); 966 auto numResults = op.getNumResults(); 967 resultTypeNames.reserve(numResults); 968 969 paramList = "Builder *odsBuilder, OperationState &"; 970 paramList.append(builderOpState); 971 972 switch (typeParamKind) { 973 case TypeParamKind::None: 974 break; 975 case TypeParamKind::Separate: { 976 // Add parameters for all return types 977 for (int i = 0; i < numResults; ++i) { 978 const auto &result = op.getResult(i); 979 std::string resultName = std::string(result.name); 980 if (resultName.empty()) 981 resultName = std::string(formatv("resultType{0}", i)); 982 983 if (result.isOptional()) 984 paramList.append(", /*optional*/Type "); 985 else if (result.isVariadic()) 986 paramList.append(", ArrayRef<Type> "); 987 else 988 paramList.append(", Type "); 989 paramList.append(resultName); 990 991 resultTypeNames.emplace_back(std::move(resultName)); 992 } 993 } break; 994 case TypeParamKind::Collective: { 995 paramList.append(", ArrayRef<Type> resultTypes"); 996 resultTypeNames.push_back("resultTypes"); 997 } break; 998 } 999 1000 // Add parameters for all arguments (operands and attributes). 1001 1002 int numOperands = 0; 1003 int numAttrs = 0; 1004 1005 int defaultValuedAttrStartIndex = op.getNumArgs(); 1006 if (attrParamKind == AttrParamKind::UnwrappedValue) { 1007 // Calculate the start index from which we can attach default values in the 1008 // builder declaration. 1009 for (int i = op.getNumArgs() - 1; i >= 0; --i) { 1010 auto *namedAttr = op.getArg(i).dyn_cast<tblgen::NamedAttribute *>(); 1011 if (!namedAttr || !namedAttr->attr.hasDefaultValue()) 1012 break; 1013 1014 if (!canUseUnwrappedRawValue(namedAttr->attr)) 1015 break; 1016 1017 // Creating an APInt requires us to provide bitwidth, value, and 1018 // signedness, which is complicated compared to others. Similarly 1019 // for APFloat. 1020 // TODO(b/144412160) Adjust the 'returnType' field of such attributes 1021 // to support them. 1022 StringRef retType = namedAttr->attr.getReturnType(); 1023 if (retType == "APInt" || retType == "APFloat") 1024 break; 1025 1026 defaultValuedAttrStartIndex = i; 1027 } 1028 } 1029 1030 for (int i = 0, e = op.getNumArgs(); i < e; ++i) { 1031 auto argument = op.getArg(i); 1032 if (argument.is<tblgen::NamedTypeConstraint *>()) { 1033 const auto &operand = op.getOperand(numOperands); 1034 if (operand.isOptional()) 1035 paramList.append(", /*optional*/Value "); 1036 else if (operand.isVariadic()) 1037 paramList.append(", ValueRange "); 1038 else 1039 paramList.append(", Value "); 1040 paramList.append(getArgumentName(op, numOperands)); 1041 ++numOperands; 1042 } else { 1043 const auto &namedAttr = op.getAttribute(numAttrs); 1044 const auto &attr = namedAttr.attr; 1045 paramList.append(", "); 1046 1047 if (attr.isOptional()) 1048 paramList.append("/*optional*/"); 1049 1050 switch (attrParamKind) { 1051 case AttrParamKind::WrappedAttr: 1052 paramList.append(std::string(attr.getStorageType())); 1053 break; 1054 case AttrParamKind::UnwrappedValue: 1055 if (canUseUnwrappedRawValue(attr)) { 1056 paramList.append(std::string(attr.getReturnType())); 1057 } else { 1058 paramList.append(std::string(attr.getStorageType())); 1059 } 1060 break; 1061 } 1062 paramList.append(" "); 1063 paramList.append(std::string(namedAttr.name)); 1064 1065 // Attach default value if requested and possible. 1066 if (attrParamKind == AttrParamKind::UnwrappedValue && 1067 i >= defaultValuedAttrStartIndex) { 1068 bool isString = attr.getReturnType() == "StringRef"; 1069 paramList.append(" = "); 1070 if (isString) 1071 paramList.append("\""); 1072 paramList.append(std::string(attr.getDefaultValue())); 1073 if (isString) 1074 paramList.append("\""); 1075 } 1076 ++numAttrs; 1077 } 1078 } 1079 1080 /// Insert parameters for each successor. 1081 for (const NamedSuccessor &succ : op.getSuccessors()) { 1082 paramList += (succ.isVariadic() ? ", ArrayRef<Block *> " : ", Block *"); 1083 paramList += succ.name; 1084 } 1085 1086 /// Insert parameters for variadic regions. 1087 for (const NamedRegion ®ion : op.getRegions()) { 1088 if (region.isVariadic()) 1089 paramList += llvm::formatv(", unsigned {0}Count", region.name).str(); 1090 } 1091 } 1092 1093 void OpEmitter::genCodeForAddingArgAndRegionForBuilder(OpMethodBody &body, 1094 bool isRawValueAttr) { 1095 // Push all operands to the result. 1096 for (int i = 0, e = op.getNumOperands(); i < e; ++i) { 1097 std::string argName = getArgumentName(op, i); 1098 if (op.getOperand(i).isOptional()) 1099 body << " if (" << argName << ")\n "; 1100 body << " " << builderOpState << ".addOperands(" << argName << ");\n"; 1101 } 1102 1103 // If the operation has the operand segment size attribute, add it here. 1104 if (op.getTrait("OpTrait::AttrSizedOperandSegments")) { 1105 body << " " << builderOpState 1106 << ".addAttribute(\"operand_segment_sizes\", " 1107 "odsBuilder->getI32VectorAttr({"; 1108 llvm::interleaveComma( 1109 llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) { 1110 if (op.getOperand(i).isOptional()) 1111 body << "(" << getArgumentName(op, i) << " ? 1 : 0)"; 1112 else if (op.getOperand(i).isVariadic()) 1113 body << "static_cast<int32_t>(" << getArgumentName(op, i) 1114 << ".size())"; 1115 else 1116 body << "1"; 1117 }); 1118 body << "}));\n"; 1119 } 1120 1121 // Push all attributes to the result. 1122 for (const auto &namedAttr : op.getAttributes()) { 1123 auto &attr = namedAttr.attr; 1124 if (!attr.isDerivedAttr()) { 1125 bool emitNotNullCheck = attr.isOptional(); 1126 if (emitNotNullCheck) { 1127 body << formatv(" if ({0}) ", namedAttr.name) << "{\n"; 1128 } 1129 if (isRawValueAttr && canUseUnwrappedRawValue(attr)) { 1130 // If this is a raw value, then we need to wrap it in an Attribute 1131 // instance. 1132 FmtContext fctx; 1133 fctx.withBuilder("(*odsBuilder)"); 1134 1135 std::string builderTemplate = 1136 std::string(attr.getConstBuilderTemplate()); 1137 1138 // For StringAttr, its constant builder call will wrap the input in 1139 // quotes, which is correct for normal string literals, but incorrect 1140 // here given we use function arguments. So we need to strip the 1141 // wrapping quotes. 1142 if (StringRef(builderTemplate).contains("\"$0\"")) 1143 builderTemplate = replaceAllSubstrs(builderTemplate, "\"$0\"", "$0"); 1144 1145 std::string value = 1146 std::string(tgfmt(builderTemplate, &fctx, namedAttr.name)); 1147 body << formatv(" {0}.addAttribute(\"{1}\", {2});\n", builderOpState, 1148 namedAttr.name, value); 1149 } else { 1150 body << formatv(" {0}.addAttribute(\"{1}\", {1});\n", builderOpState, 1151 namedAttr.name); 1152 } 1153 if (emitNotNullCheck) { 1154 body << " }\n"; 1155 } 1156 } 1157 } 1158 1159 // Create the correct number of regions. 1160 for (const NamedRegion ®ion : op.getRegions()) { 1161 if (region.isVariadic()) 1162 body << formatv(" for (unsigned i = 0; i < {0}Count; ++i)\n ", 1163 region.name); 1164 1165 body << " (void)" << builderOpState << ".addRegion();\n"; 1166 } 1167 1168 // Push all successors to the result. 1169 for (const NamedSuccessor &namedSuccessor : op.getSuccessors()) { 1170 body << formatv(" {0}.addSuccessors({1});\n", builderOpState, 1171 namedSuccessor.name); 1172 } 1173 } 1174 1175 void OpEmitter::genCanonicalizerDecls() { 1176 if (!def.getValueAsBit("hasCanonicalizer")) 1177 return; 1178 1179 const char *const params = 1180 "OwningRewritePatternList &results, MLIRContext *context"; 1181 opClass.newMethod("void", "getCanonicalizationPatterns", params, 1182 OpMethod::MP_Static, /*declOnly=*/true); 1183 } 1184 1185 void OpEmitter::genFolderDecls() { 1186 bool hasSingleResult = 1187 op.getNumResults() == 1 && op.getNumVariableLengthResults() == 0; 1188 1189 if (def.getValueAsBit("hasFolder")) { 1190 if (hasSingleResult) { 1191 const char *const params = "ArrayRef<Attribute> operands"; 1192 opClass.newMethod("OpFoldResult", "fold", params, OpMethod::MP_None, 1193 /*declOnly=*/true); 1194 } else { 1195 const char *const params = "ArrayRef<Attribute> operands, " 1196 "SmallVectorImpl<OpFoldResult> &results"; 1197 opClass.newMethod("LogicalResult", "fold", params, OpMethod::MP_None, 1198 /*declOnly=*/true); 1199 } 1200 } 1201 } 1202 1203 void OpEmitter::genOpInterfaceMethods() { 1204 for (const auto &trait : op.getTraits()) { 1205 auto opTrait = dyn_cast<tblgen::InterfaceOpTrait>(&trait); 1206 if (!opTrait || !opTrait->shouldDeclareMethods()) 1207 continue; 1208 auto interface = opTrait->getOpInterface(); 1209 for (auto method : interface.getMethods()) { 1210 // Don't declare if the method has a body or a default implementation. 1211 if (method.getBody() || method.getDefaultImplementation()) 1212 continue; 1213 std::string args; 1214 llvm::raw_string_ostream os(args); 1215 llvm::interleaveComma(method.getArguments(), os, 1216 [&](const OpInterfaceMethod::Argument &arg) { 1217 os << arg.type << " " << arg.name; 1218 }); 1219 opClass.newMethod(method.getReturnType(), method.getName(), os.str(), 1220 method.isStatic() ? OpMethod::MP_Static 1221 : OpMethod::MP_None, 1222 /*declOnly=*/true); 1223 } 1224 } 1225 } 1226 1227 void OpEmitter::genSideEffectInterfaceMethods() { 1228 enum EffectKind { Operand, Result, Static }; 1229 struct EffectLocation { 1230 /// The effect applied. 1231 SideEffect effect; 1232 1233 /// The index if the kind is either operand or result. 1234 unsigned index : 30; 1235 1236 /// The kind of the location. 1237 unsigned kind : 2; 1238 }; 1239 1240 StringMap<SmallVector<EffectLocation, 1>> interfaceEffects; 1241 auto resolveDecorators = [&](Operator::var_decorator_range decorators, 1242 unsigned index, unsigned kind) { 1243 for (auto decorator : decorators) 1244 if (SideEffect *effect = dyn_cast<SideEffect>(&decorator)) 1245 interfaceEffects[effect->getBaseEffectName()].push_back( 1246 EffectLocation{*effect, index, kind}); 1247 }; 1248 1249 // Collect effects that were specified via: 1250 /// Traits. 1251 for (const auto &trait : op.getTraits()) { 1252 const auto *opTrait = dyn_cast<tblgen::SideEffectTrait>(&trait); 1253 if (!opTrait) 1254 continue; 1255 auto &effects = interfaceEffects[opTrait->getBaseEffectName()]; 1256 for (auto decorator : opTrait->getEffects()) 1257 effects.push_back(EffectLocation{cast<SideEffect>(decorator), 1258 /*index=*/0, EffectKind::Static}); 1259 } 1260 /// Operands. 1261 for (unsigned i = 0, operandIt = 0, e = op.getNumArgs(); i != e; ++i) { 1262 if (op.getArg(i).is<NamedTypeConstraint *>()) { 1263 resolveDecorators(op.getArgDecorators(i), operandIt, EffectKind::Operand); 1264 ++operandIt; 1265 } 1266 } 1267 /// Results. 1268 for (unsigned i = 0, e = op.getNumResults(); i != e; ++i) 1269 resolveDecorators(op.getResultDecorators(i), i, EffectKind::Result); 1270 1271 for (auto &it : interfaceEffects) { 1272 auto effectsParam = 1273 llvm::formatv( 1274 "SmallVectorImpl<SideEffects::EffectInstance<{0}>> &effects", 1275 it.first()) 1276 .str(); 1277 1278 // Generate the 'getEffects' method. 1279 auto &getEffects = opClass.newMethod("void", "getEffects", effectsParam); 1280 auto &body = getEffects.body(); 1281 1282 // Add effect instances for each of the locations marked on the operation. 1283 for (auto &location : it.second) { 1284 if (location.kind != EffectKind::Static) { 1285 body << " for (Value value : getODS" 1286 << (location.kind == EffectKind::Operand ? "Operands" : "Results") 1287 << "(" << location.index << "))\n "; 1288 } 1289 1290 body << " effects.emplace_back(" << location.effect.getName() 1291 << "::get()"; 1292 1293 // If the effect isn't static, it has a specific value attached to it. 1294 if (location.kind != EffectKind::Static) 1295 body << ", value"; 1296 body << ", " << location.effect.getResource() << "::get());\n"; 1297 } 1298 } 1299 } 1300 1301 void OpEmitter::genParser() { 1302 if (!hasStringAttribute(def, "parser") || 1303 hasStringAttribute(def, "assemblyFormat")) 1304 return; 1305 1306 auto &method = opClass.newMethod( 1307 "ParseResult", "parse", "OpAsmParser &parser, OperationState &result", 1308 OpMethod::MP_Static); 1309 FmtContext fctx; 1310 fctx.addSubst("cppClass", opClass.getClassName()); 1311 auto parser = def.getValueAsString("parser").ltrim().rtrim(" \t\v\f\r"); 1312 method.body() << " " << tgfmt(parser, &fctx); 1313 } 1314 1315 void OpEmitter::genPrinter() { 1316 if (hasStringAttribute(def, "assemblyFormat")) 1317 return; 1318 1319 auto valueInit = def.getValueInit("printer"); 1320 CodeInit *codeInit = dyn_cast<CodeInit>(valueInit); 1321 if (!codeInit) 1322 return; 1323 1324 auto &method = opClass.newMethod("void", "print", "OpAsmPrinter &p"); 1325 FmtContext fctx; 1326 fctx.addSubst("cppClass", opClass.getClassName()); 1327 auto printer = codeInit->getValue().ltrim().rtrim(" \t\v\f\r"); 1328 method.body() << " " << tgfmt(printer, &fctx); 1329 } 1330 1331 void OpEmitter::genVerifier() { 1332 auto valueInit = def.getValueInit("verifier"); 1333 CodeInit *codeInit = dyn_cast<CodeInit>(valueInit); 1334 bool hasCustomVerify = codeInit && !codeInit->getValue().empty(); 1335 1336 auto &method = opClass.newMethod("LogicalResult", "verify", /*params=*/""); 1337 auto &body = method.body(); 1338 1339 const char *checkAttrSizedValueSegmentsCode = R"( 1340 auto sizeAttr = getAttrOfType<DenseIntElementsAttr>("{0}"); 1341 auto numElements = sizeAttr.getType().cast<ShapedType>().getNumElements(); 1342 if (numElements != {1}) {{ 1343 return emitOpError("'{0}' attribute for specifying {2} segments " 1344 "must have {1} elements"); 1345 } 1346 )"; 1347 1348 // Verify a few traits first so that we can use 1349 // getODSOperands()/getODSResults() in the rest of the verifier. 1350 for (auto &trait : op.getTraits()) { 1351 if (auto *t = dyn_cast<tblgen::NativeOpTrait>(&trait)) { 1352 if (t->getTrait() == "OpTrait::AttrSizedOperandSegments") { 1353 body << formatv(checkAttrSizedValueSegmentsCode, 1354 "operand_segment_sizes", op.getNumOperands(), 1355 "operand"); 1356 } else if (t->getTrait() == "OpTrait::AttrSizedResultSegments") { 1357 body << formatv(checkAttrSizedValueSegmentsCode, "result_segment_sizes", 1358 op.getNumResults(), "result"); 1359 } 1360 } 1361 } 1362 1363 // Populate substitutions for attributes and named operands and results. 1364 for (const auto &namedAttr : op.getAttributes()) 1365 verifyCtx.addSubst(namedAttr.name, 1366 formatv("this->getAttr(\"{0}\")", namedAttr.name)); 1367 for (int i = 0, e = op.getNumOperands(); i < e; ++i) { 1368 auto &value = op.getOperand(i); 1369 if (value.name.empty()) 1370 continue; 1371 1372 if (value.isVariadic()) 1373 verifyCtx.addSubst(value.name, formatv("this->getODSOperands({0})", i)); 1374 else 1375 verifyCtx.addSubst(value.name, 1376 formatv("(*this->getODSOperands({0}).begin())", i)); 1377 } 1378 for (int i = 0, e = op.getNumResults(); i < e; ++i) { 1379 auto &value = op.getResult(i); 1380 if (value.name.empty()) 1381 continue; 1382 1383 if (value.isVariadic()) 1384 verifyCtx.addSubst(value.name, formatv("this->getODSResults({0})", i)); 1385 else 1386 verifyCtx.addSubst(value.name, 1387 formatv("(*this->getODSResults({0}).begin())", i)); 1388 } 1389 1390 // Verify the attributes have the correct type. 1391 for (const auto &namedAttr : op.getAttributes()) { 1392 const auto &attr = namedAttr.attr; 1393 if (attr.isDerivedAttr()) 1394 continue; 1395 1396 auto attrName = namedAttr.name; 1397 // Prefix with `tblgen_` to avoid hiding the attribute accessor. 1398 auto varName = tblgenNamePrefix + attrName; 1399 body << formatv(" auto {0} = this->getAttr(\"{1}\");\n", varName, 1400 attrName); 1401 1402 bool allowMissingAttr = attr.hasDefaultValue() || attr.isOptional(); 1403 if (allowMissingAttr) { 1404 // If the attribute has a default value, then only verify the predicate if 1405 // set. This does effectively assume that the default value is valid. 1406 // TODO: verify the debug value is valid (perhaps in debug mode only). 1407 body << " if (" << varName << ") {\n"; 1408 } else { 1409 body << " if (!" << varName 1410 << ") return emitOpError(\"requires attribute '" << attrName 1411 << "'\");\n {\n"; 1412 } 1413 1414 auto attrPred = attr.getPredicate(); 1415 if (!attrPred.isNull()) { 1416 body << tgfmt( 1417 " if (!($0)) return emitOpError(\"attribute '$1' " 1418 "failed to satisfy constraint: $2\");\n", 1419 /*ctx=*/nullptr, 1420 tgfmt(attrPred.getCondition(), &verifyCtx.withSelf(varName)), 1421 attrName, attr.getDescription()); 1422 } 1423 1424 body << " }\n"; 1425 } 1426 1427 genOperandResultVerifier(body, op.getOperands(), "operand"); 1428 genOperandResultVerifier(body, op.getResults(), "result"); 1429 1430 for (auto &trait : op.getTraits()) { 1431 if (auto *t = dyn_cast<tblgen::PredOpTrait>(&trait)) { 1432 body << tgfmt(" if (!($0)) {\n " 1433 "return emitOpError(\"failed to verify that $1\");\n }\n", 1434 &verifyCtx, tgfmt(t->getPredTemplate(), &verifyCtx), 1435 t->getDescription()); 1436 } 1437 } 1438 1439 genRegionVerifier(body); 1440 genSuccessorVerifier(body); 1441 1442 if (hasCustomVerify) { 1443 FmtContext fctx; 1444 fctx.addSubst("cppClass", opClass.getClassName()); 1445 auto printer = codeInit->getValue().ltrim().rtrim(" \t\v\f\r"); 1446 body << " " << tgfmt(printer, &fctx); 1447 } else { 1448 body << " return mlir::success();\n"; 1449 } 1450 } 1451 1452 void OpEmitter::genOperandResultVerifier(OpMethodBody &body, 1453 Operator::value_range values, 1454 StringRef valueKind) { 1455 FmtContext fctx; 1456 1457 body << " {\n"; 1458 body << " unsigned index = 0; (void)index;\n"; 1459 1460 for (auto staticValue : llvm::enumerate(values)) { 1461 bool hasPredicate = staticValue.value().hasPredicate(); 1462 bool isOptional = staticValue.value().isOptional(); 1463 if (!hasPredicate && !isOptional) 1464 continue; 1465 body << formatv(" auto valueGroup{2} = getODS{0}{1}s({2});\n", 1466 // Capitalize the first letter to match the function name 1467 valueKind.substr(0, 1).upper(), valueKind.substr(1), 1468 staticValue.index()); 1469 1470 // If the constraint is optional check that the value group has at most 1 1471 // value. 1472 if (isOptional) { 1473 body << formatv(" if (valueGroup{0}.size() > 1)\n" 1474 " return emitOpError(\"{1} group starting at #\") " 1475 "<< index << \" requires 0 or 1 element, but found \" << " 1476 "valueGroup{0}.size();\n", 1477 staticValue.index(), valueKind); 1478 } 1479 1480 // Otherwise, if there is no predicate there is nothing left to do. 1481 if (!hasPredicate) 1482 continue; 1483 1484 // Emit a loop to check all the dynamic values in the pack. 1485 body << " for (Value v : valueGroup" << staticValue.index() << ") {\n"; 1486 1487 auto constraint = staticValue.value().constraint; 1488 body << " (void)v;\n" 1489 << " if (!(" 1490 << tgfmt(constraint.getConditionTemplate(), 1491 &fctx.withSelf("v.getType()")) 1492 << ")) {\n" 1493 << formatv(" return emitOpError(\"{0} #\") << index " 1494 "<< \" must be {1}, but got \" << v.getType();\n", 1495 valueKind, constraint.getDescription()) 1496 << " }\n" // if 1497 << " ++index;\n" 1498 << " }\n"; // for 1499 } 1500 1501 body << " }\n"; 1502 } 1503 1504 void OpEmitter::genRegionVerifier(OpMethodBody &body) { 1505 // If we have no regions, there is nothing more to do. 1506 unsigned numRegions = op.getNumRegions(); 1507 if (numRegions == 0) 1508 return; 1509 1510 body << "{\n"; 1511 body << " unsigned index = 0; (void)index;\n"; 1512 1513 for (unsigned i = 0; i < numRegions; ++i) { 1514 const auto ®ion = op.getRegion(i); 1515 if (region.constraint.getPredicate().isNull()) 1516 continue; 1517 1518 body << " for (Region ®ion : "; 1519 body << formatv( 1520 region.isVariadic() 1521 ? "{0}()" 1522 : "MutableArrayRef<Region>(this->getOperation()->getRegion({1}))", 1523 region.name, i); 1524 body << ") {\n"; 1525 auto constraint = tgfmt(region.constraint.getConditionTemplate(), 1526 &verifyCtx.withSelf("region")) 1527 .str(); 1528 1529 body << formatv(" (void)region;\n" 1530 " if (!({0})) {\n " 1531 "return emitOpError(\"region #\") << index << \" {1}" 1532 "failed to " 1533 "verify constraint: {2}\";\n }\n", 1534 constraint, 1535 region.name.empty() ? "" : "('" + region.name + "') ", 1536 region.constraint.getDescription()) 1537 << " ++index;\n" 1538 << " }\n"; 1539 } 1540 body << " }\n"; 1541 } 1542 1543 void OpEmitter::genSuccessorVerifier(OpMethodBody &body) { 1544 // If we have no successors, there is nothing more to do. 1545 unsigned numSuccessors = op.getNumSuccessors(); 1546 if (numSuccessors == 0) 1547 return; 1548 1549 body << "{\n"; 1550 body << " unsigned index = 0; (void)index;\n"; 1551 1552 for (unsigned i = 0; i < numSuccessors; ++i) { 1553 const auto &successor = op.getSuccessor(i); 1554 if (successor.constraint.getPredicate().isNull()) 1555 continue; 1556 1557 body << " for (Block *successor : "; 1558 body << formatv(successor.isVariadic() ? "{0}()" 1559 : "ArrayRef<Block *>({0}())", 1560 successor.name); 1561 body << ") {\n"; 1562 auto constraint = tgfmt(successor.constraint.getConditionTemplate(), 1563 &verifyCtx.withSelf("successor")) 1564 .str(); 1565 1566 body << formatv(" (void)successor;\n" 1567 " if (!({0})) {\n " 1568 "return emitOpError(\"successor #\") << index << \"('{1}') " 1569 "failed to " 1570 "verify constraint: {2}\";\n }\n", 1571 constraint, successor.name, 1572 successor.constraint.getDescription()) 1573 << " ++index;\n" 1574 << " }\n"; 1575 } 1576 body << " }\n"; 1577 } 1578 1579 /// Add a size count trait to the given operation class. 1580 static void addSizeCountTrait(OpClass &opClass, StringRef traitKind, 1581 int numTotal, int numVariadic) { 1582 if (numVariadic != 0) { 1583 if (numTotal == numVariadic) 1584 opClass.addTrait("OpTrait::Variadic" + traitKind + "s"); 1585 else 1586 opClass.addTrait("OpTrait::AtLeastN" + traitKind + "s<" + 1587 Twine(numTotal - numVariadic) + ">::Impl"); 1588 return; 1589 } 1590 switch (numTotal) { 1591 case 0: 1592 opClass.addTrait("OpTrait::Zero" + traitKind); 1593 break; 1594 case 1: 1595 opClass.addTrait("OpTrait::One" + traitKind); 1596 break; 1597 default: 1598 opClass.addTrait("OpTrait::N" + traitKind + "s<" + Twine(numTotal) + 1599 ">::Impl"); 1600 break; 1601 } 1602 } 1603 1604 void OpEmitter::genTraits() { 1605 // Add region size trait. 1606 unsigned numRegions = op.getNumRegions(); 1607 unsigned numVariadicRegions = op.getNumVariadicRegions(); 1608 addSizeCountTrait(opClass, "Region", numRegions, numVariadicRegions); 1609 1610 // Add result size trait. 1611 int numResults = op.getNumResults(); 1612 int numVariadicResults = op.getNumVariableLengthResults(); 1613 addSizeCountTrait(opClass, "Result", numResults, numVariadicResults); 1614 1615 // Add successor size trait. 1616 unsigned numSuccessors = op.getNumSuccessors(); 1617 unsigned numVariadicSuccessors = op.getNumVariadicSuccessors(); 1618 addSizeCountTrait(opClass, "Successor", numSuccessors, numVariadicSuccessors); 1619 1620 // Add variadic size trait and normal op traits. 1621 int numOperands = op.getNumOperands(); 1622 int numVariadicOperands = op.getNumVariableLengthOperands(); 1623 1624 // Add operand size trait. 1625 if (numVariadicOperands != 0) { 1626 if (numOperands == numVariadicOperands) 1627 opClass.addTrait("OpTrait::VariadicOperands"); 1628 else 1629 opClass.addTrait("OpTrait::AtLeastNOperands<" + 1630 Twine(numOperands - numVariadicOperands) + ">::Impl"); 1631 } else { 1632 switch (numOperands) { 1633 case 0: 1634 opClass.addTrait("OpTrait::ZeroOperands"); 1635 break; 1636 case 1: 1637 opClass.addTrait("OpTrait::OneOperand"); 1638 break; 1639 default: 1640 opClass.addTrait("OpTrait::NOperands<" + Twine(numOperands) + ">::Impl"); 1641 break; 1642 } 1643 } 1644 1645 // Add the native and interface traits. 1646 for (const auto &trait : op.getTraits()) { 1647 if (auto opTrait = dyn_cast<tblgen::NativeOpTrait>(&trait)) 1648 opClass.addTrait(opTrait->getTrait()); 1649 else if (auto opTrait = dyn_cast<tblgen::InterfaceOpTrait>(&trait)) 1650 opClass.addTrait(opTrait->getTrait()); 1651 } 1652 } 1653 1654 void OpEmitter::genOpNameGetter() { 1655 auto &method = opClass.newMethod("StringRef", "getOperationName", 1656 /*params=*/"", OpMethod::MP_Static); 1657 method.body() << " return \"" << op.getOperationName() << "\";\n"; 1658 } 1659 1660 void OpEmitter::genOpAsmInterface() { 1661 // If the user only has one results or specifically added the Asm trait, 1662 // then don't generate it for them. We specifically only handle multi result 1663 // operations, because the name of a single result in the common case is not 1664 // interesting(generally 'result'/'output'/etc.). 1665 // TODO: We could also add a flag to allow operations to opt in to this 1666 // generation, even if they only have a single operation. 1667 int numResults = op.getNumResults(); 1668 if (numResults <= 1 || op.getTrait("OpAsmOpInterface::Trait")) 1669 return; 1670 1671 SmallVector<StringRef, 4> resultNames(numResults); 1672 for (int i = 0; i != numResults; ++i) 1673 resultNames[i] = op.getResultName(i); 1674 1675 // Don't add the trait if none of the results have a valid name. 1676 if (llvm::all_of(resultNames, [](StringRef name) { return name.empty(); })) 1677 return; 1678 opClass.addTrait("OpAsmOpInterface::Trait"); 1679 1680 // Generate the right accessor for the number of results. 1681 auto &method = opClass.newMethod("void", "getAsmResultNames", 1682 "OpAsmSetValueNameFn setNameFn"); 1683 auto &body = method.body(); 1684 for (int i = 0; i != numResults; ++i) { 1685 body << " auto resultGroup" << i << " = getODSResults(" << i << ");\n" 1686 << " if (!llvm::empty(resultGroup" << i << "))\n" 1687 << " setNameFn(*resultGroup" << i << ".begin(), \"" 1688 << resultNames[i] << "\");\n"; 1689 } 1690 } 1691 1692 //===----------------------------------------------------------------------===// 1693 // OpOperandAdaptor emitter 1694 //===----------------------------------------------------------------------===// 1695 1696 namespace { 1697 // Helper class to emit Op operand adaptors to an output stream. Operand 1698 // adaptors are wrappers around ArrayRef<Value> that provide named operand 1699 // getters identical to those defined in the Op. 1700 class OpOperandAdaptorEmitter { 1701 public: 1702 static void emitDecl(const Operator &op, raw_ostream &os); 1703 static void emitDef(const Operator &op, raw_ostream &os); 1704 1705 private: 1706 explicit OpOperandAdaptorEmitter(const Operator &op); 1707 1708 Class adapterClass; 1709 }; 1710 } // end namespace 1711 1712 OpOperandAdaptorEmitter::OpOperandAdaptorEmitter(const Operator &op) 1713 : adapterClass(op.getCppClassName().str() + "OperandAdaptor") { 1714 adapterClass.newField("ArrayRef<Value>", "tblgen_operands"); 1715 auto &constructor = adapterClass.newConstructor("ArrayRef<Value> values"); 1716 constructor.body() << " tblgen_operands = values;\n"; 1717 1718 generateNamedOperandGetters(op, adapterClass, 1719 /*rangeType=*/"ArrayRef<Value>", 1720 /*rangeBeginCall=*/"tblgen_operands.begin()", 1721 /*rangeSizeCall=*/"tblgen_operands.size()", 1722 /*getOperandCallPattern=*/"tblgen_operands[{0}]"); 1723 } 1724 1725 void OpOperandAdaptorEmitter::emitDecl(const Operator &op, raw_ostream &os) { 1726 OpOperandAdaptorEmitter(op).adapterClass.writeDeclTo(os); 1727 } 1728 1729 void OpOperandAdaptorEmitter::emitDef(const Operator &op, raw_ostream &os) { 1730 OpOperandAdaptorEmitter(op).adapterClass.writeDefTo(os); 1731 } 1732 1733 // Emits the opcode enum and op classes. 1734 static void emitOpClasses(const std::vector<Record *> &defs, raw_ostream &os, 1735 bool emitDecl) { 1736 IfDefScope scope("GET_OP_CLASSES", os); 1737 // First emit forward declaration for each class, this allows them to refer 1738 // to each others in traits for example. 1739 if (emitDecl) { 1740 for (auto *def : defs) { 1741 Operator op(*def); 1742 os << "class " << op.getCppClassName() << ";\n"; 1743 } 1744 } 1745 for (auto *def : defs) { 1746 Operator op(*def); 1747 const auto *attrSizedOperands = 1748 op.getTrait("OpTrait::AttrSizedOperandSegments"); 1749 if (emitDecl) { 1750 os << formatv(opCommentHeader, op.getQualCppClassName(), "declarations"); 1751 // We cannot generate the operand adaptor class if operand getters depend 1752 // on an attribute. 1753 if (!attrSizedOperands) 1754 OpOperandAdaptorEmitter::emitDecl(op, os); 1755 OpEmitter::emitDecl(op, os); 1756 } else { 1757 os << formatv(opCommentHeader, op.getQualCppClassName(), "definitions"); 1758 if (!attrSizedOperands) 1759 OpOperandAdaptorEmitter::emitDef(op, os); 1760 OpEmitter::emitDef(op, os); 1761 } 1762 } 1763 } 1764 1765 // Emits a comma-separated list of the ops. 1766 static void emitOpList(const std::vector<Record *> &defs, raw_ostream &os) { 1767 IfDefScope scope("GET_OP_LIST", os); 1768 1769 llvm::interleave( 1770 // TODO: We are constructing the Operator wrapper instance just for 1771 // getting it's qualified class name here. Reduce the overhead by having a 1772 // lightweight version of Operator class just for that purpose. 1773 defs, [&os](Record *def) { os << Operator(def).getQualCppClassName(); }, 1774 [&os]() { os << ",\n"; }); 1775 } 1776 1777 static bool emitOpDecls(const RecordKeeper &recordKeeper, raw_ostream &os) { 1778 emitSourceFileHeader("Op Declarations", os); 1779 1780 const auto &defs = recordKeeper.getAllDerivedDefinitions("Op"); 1781 emitOpClasses(defs, os, /*emitDecl=*/true); 1782 1783 return false; 1784 } 1785 1786 static bool emitOpDefs(const RecordKeeper &recordKeeper, raw_ostream &os) { 1787 emitSourceFileHeader("Op Definitions", os); 1788 1789 const auto &defs = recordKeeper.getAllDerivedDefinitions("Op"); 1790 emitOpList(defs, os); 1791 emitOpClasses(defs, os, /*emitDecl=*/false); 1792 1793 return false; 1794 } 1795 1796 static mlir::GenRegistration 1797 genOpDecls("gen-op-decls", "Generate op declarations", 1798 [](const RecordKeeper &records, raw_ostream &os) { 1799 return emitOpDecls(records, os); 1800 }); 1801 1802 static mlir::GenRegistration genOpDefs("gen-op-defs", "Generate op definitions", 1803 [](const RecordKeeper &records, 1804 raw_ostream &os) { 1805 return emitOpDefs(records, os); 1806 }); 1807