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