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