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