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