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 verifyCtx.addSubst("_ctxt", "this->getOperation()->getContext()"); 583 584 genTraits(); 585 586 // Generate C++ code for various op methods. The order here determines the 587 // methods in the generated file. 588 genOpAsmInterface(); 589 genOpNameGetter(); 590 genNamedOperandGetters(); 591 genNamedOperandSetters(); 592 genNamedResultGetters(); 593 genNamedRegionGetters(); 594 genNamedSuccessorGetters(); 595 genAttrGetters(); 596 genAttrSetters(); 597 genOptionalAttrRemovers(); 598 genBuilder(); 599 genParser(); 600 genPrinter(); 601 genVerifier(); 602 genCanonicalizerDecls(); 603 genFolderDecls(); 604 genTypeInterfaceMethods(); 605 genOpInterfaceMethods(); 606 generateOpFormat(op, opClass); 607 genSideEffectInterfaceMethods(); 608 } 609 610 void OpEmitter::emitDecl( 611 const Operator &op, raw_ostream &os, 612 const StaticVerifierFunctionEmitter &staticVerifierEmitter) { 613 OpEmitter(op, staticVerifierEmitter).emitDecl(os); 614 } 615 616 void OpEmitter::emitDef( 617 const Operator &op, raw_ostream &os, 618 const StaticVerifierFunctionEmitter &staticVerifierEmitter) { 619 OpEmitter(op, staticVerifierEmitter).emitDef(os); 620 } 621 622 void OpEmitter::emitDecl(raw_ostream &os) { opClass.writeDeclTo(os); } 623 624 void OpEmitter::emitDef(raw_ostream &os) { opClass.writeDefTo(os); } 625 626 void OpEmitter::genAttrGetters() { 627 FmtContext fctx; 628 fctx.withBuilder("::mlir::Builder((*this)->getContext())"); 629 630 Dialect opDialect = op.getDialect(); 631 // Emit the derived attribute body. 632 auto emitDerivedAttr = [&](StringRef name, Attribute attr) { 633 auto *method = opClass.addMethodAndPrune(attr.getReturnType(), name); 634 if (!method) 635 return; 636 auto &body = method->body(); 637 body << " " << attr.getDerivedCodeBody() << "\n"; 638 }; 639 640 // Emit with return type specified. 641 auto emitAttrWithReturnType = [&](StringRef name, Attribute attr) { 642 auto *method = opClass.addMethodAndPrune(attr.getReturnType(), name); 643 auto &body = method->body(); 644 body << " auto attr = " << name << "Attr();\n"; 645 if (attr.hasDefaultValue()) { 646 // Returns the default value if not set. 647 // TODO: this is inefficient, we are recreating the attribute for every 648 // call. This should be set instead. 649 std::string defaultValue = std::string( 650 tgfmt(attr.getConstBuilderTemplate(), &fctx, attr.getDefaultValue())); 651 body << " if (!attr)\n return " 652 << tgfmt(attr.getConvertFromStorageCall(), 653 &fctx.withSelf(defaultValue)) 654 << ";\n"; 655 } 656 body << " return " 657 << tgfmt(attr.getConvertFromStorageCall(), &fctx.withSelf("attr")) 658 << ";\n"; 659 }; 660 661 // Generate raw named accessor type. This is a wrapper class that allows 662 // referring to the attributes via accessors instead of having to use 663 // the string interface for better compile time verification. 664 auto emitAttrWithStorageType = [&](StringRef name, Attribute attr) { 665 auto *method = 666 opClass.addMethodAndPrune(attr.getStorageType(), (name + "Attr").str()); 667 if (!method) 668 return; 669 auto &body = method->body(); 670 body << " return (*this)->getAttr(\"" << name << "\").template "; 671 if (attr.isOptional() || attr.hasDefaultValue()) 672 body << "dyn_cast_or_null<"; 673 else 674 body << "cast<"; 675 body << attr.getStorageType() << ">();"; 676 }; 677 678 for (auto &namedAttr : op.getAttributes()) { 679 const auto &name = namedAttr.name; 680 const auto &attr = namedAttr.attr; 681 if (attr.isDerivedAttr()) { 682 emitDerivedAttr(name, attr); 683 } else { 684 emitAttrWithStorageType(name, attr); 685 emitAttrWithReturnType(name, attr); 686 } 687 } 688 689 auto derivedAttrs = make_filter_range(op.getAttributes(), 690 [](const NamedAttribute &namedAttr) { 691 return namedAttr.attr.isDerivedAttr(); 692 }); 693 if (!derivedAttrs.empty()) { 694 opClass.addTrait("::mlir::DerivedAttributeOpInterface::Trait"); 695 // Generate helper method to query whether a named attribute is a derived 696 // attribute. This enables, for example, avoiding adding an attribute that 697 // overlaps with a derived attribute. 698 { 699 auto *method = opClass.addMethodAndPrune("bool", "isDerivedAttribute", 700 OpMethod::MP_Static, 701 "::llvm::StringRef", "name"); 702 auto &body = method->body(); 703 for (auto namedAttr : derivedAttrs) 704 body << " if (name == \"" << namedAttr.name << "\") return true;\n"; 705 body << " return false;"; 706 } 707 // Generate method to materialize derived attributes as a DictionaryAttr. 708 { 709 auto *method = opClass.addMethodAndPrune("::mlir::DictionaryAttr", 710 "materializeDerivedAttributes"); 711 auto &body = method->body(); 712 713 auto nonMaterializable = 714 make_filter_range(derivedAttrs, [](const NamedAttribute &namedAttr) { 715 return namedAttr.attr.getConvertFromStorageCall().empty(); 716 }); 717 if (!nonMaterializable.empty()) { 718 std::string attrs; 719 llvm::raw_string_ostream os(attrs); 720 interleaveComma(nonMaterializable, os, 721 [&](const NamedAttribute &attr) { os << attr.name; }); 722 PrintWarning( 723 op.getLoc(), 724 formatv( 725 "op has non-materializable derived attributes '{0}', skipping", 726 os.str())); 727 body << formatv(" emitOpError(\"op has non-materializable derived " 728 "attributes '{0}'\");\n", 729 attrs); 730 body << " return nullptr;"; 731 return; 732 } 733 734 body << " ::mlir::MLIRContext* ctx = getContext();\n"; 735 body << " ::mlir::Builder odsBuilder(ctx); (void)odsBuilder;\n"; 736 body << " return ::mlir::DictionaryAttr::get("; 737 body << " ctx, {\n"; 738 interleave( 739 derivedAttrs, body, 740 [&](const NamedAttribute &namedAttr) { 741 auto tmpl = namedAttr.attr.getConvertFromStorageCall(); 742 body << " {::mlir::Identifier::get(\"" << namedAttr.name 743 << "\", ctx),\n" 744 << tgfmt(tmpl, &fctx.withSelf(namedAttr.name + "()") 745 .withBuilder("odsBuilder") 746 .addSubst("_ctx", "ctx")) 747 << "}"; 748 }, 749 ",\n"); 750 body << "});"; 751 } 752 } 753 } 754 755 void OpEmitter::genAttrSetters() { 756 // Generate raw named setter type. This is a wrapper class that allows setting 757 // to the attributes via setters instead of having to use the string interface 758 // for better compile time verification. 759 auto emitAttrWithStorageType = [&](StringRef name, Attribute attr) { 760 auto *method = opClass.addMethodAndPrune("void", (name + "Attr").str(), 761 attr.getStorageType(), "attr"); 762 if (!method) 763 return; 764 auto &body = method->body(); 765 body << " (*this)->setAttr(\"" << name << "\", attr);"; 766 }; 767 768 for (auto &namedAttr : op.getAttributes()) { 769 const auto &name = namedAttr.name; 770 const auto &attr = namedAttr.attr; 771 if (!attr.isDerivedAttr()) 772 emitAttrWithStorageType(name, attr); 773 } 774 } 775 776 void OpEmitter::genOptionalAttrRemovers() { 777 // Generate methods for removing optional attributes, instead of having to 778 // use the string interface. Enables better compile time verification. 779 auto emitRemoveAttr = [&](StringRef name) { 780 auto upperInitial = name.take_front().upper(); 781 auto suffix = name.drop_front(); 782 auto *method = opClass.addMethodAndPrune( 783 "::mlir::Attribute", ("remove" + upperInitial + suffix + "Attr").str()); 784 if (!method) 785 return; 786 auto &body = method->body(); 787 body << " return (*this)->removeAttr(\"" << name << "\");"; 788 }; 789 790 for (const auto &namedAttr : op.getAttributes()) { 791 const auto &name = namedAttr.name; 792 const auto &attr = namedAttr.attr; 793 if (attr.isOptional()) 794 emitRemoveAttr(name); 795 } 796 } 797 798 // Generates the code to compute the start and end index of an operand or result 799 // range. 800 template <typename RangeT> 801 static void 802 generateValueRangeStartAndEnd(Class &opClass, StringRef methodName, 803 int numVariadic, int numNonVariadic, 804 StringRef rangeSizeCall, bool hasAttrSegmentSize, 805 StringRef sizeAttrInit, RangeT &&odsValues) { 806 auto *method = opClass.addMethodAndPrune("std::pair<unsigned, unsigned>", 807 methodName, "unsigned", "index"); 808 if (!method) 809 return; 810 auto &body = method->body(); 811 if (numVariadic == 0) { 812 body << " return {index, 1};\n"; 813 } else if (hasAttrSegmentSize) { 814 body << sizeAttrInit << attrSizedSegmentValueRangeCalcCode; 815 } else { 816 // Because the op can have arbitrarily interleaved variadic and non-variadic 817 // operands, we need to embed a list in the "sink" getter method for 818 // calculation at run-time. 819 llvm::SmallVector<StringRef, 4> isVariadic; 820 isVariadic.reserve(llvm::size(odsValues)); 821 for (auto &it : odsValues) 822 isVariadic.push_back(it.isVariableLength() ? "true" : "false"); 823 std::string isVariadicList = llvm::join(isVariadic, ", "); 824 body << formatv(sameVariadicSizeValueRangeCalcCode, isVariadicList, 825 numNonVariadic, numVariadic, rangeSizeCall, "operand"); 826 } 827 } 828 829 // Generates the named operand getter methods for the given Operator `op` and 830 // puts them in `opClass`. Uses `rangeType` as the return type of getters that 831 // return a range of operands (individual operands are `Value ` and each 832 // element in the range must also be `Value `); use `rangeBeginCall` to get 833 // an iterator to the beginning of the operand range; use `rangeSizeCall` to 834 // obtain the number of operands. `getOperandCallPattern` contains the code 835 // necessary to obtain a single operand whose position will be substituted 836 // instead of 837 // "{0}" marker in the pattern. Note that the pattern should work for any kind 838 // of ops, in particular for one-operand ops that may not have the 839 // `getOperand(unsigned)` method. 840 static void generateNamedOperandGetters(const Operator &op, Class &opClass, 841 StringRef sizeAttrInit, 842 StringRef rangeType, 843 StringRef rangeBeginCall, 844 StringRef rangeSizeCall, 845 StringRef getOperandCallPattern) { 846 const int numOperands = op.getNumOperands(); 847 const int numVariadicOperands = op.getNumVariableLengthOperands(); 848 const int numNormalOperands = numOperands - numVariadicOperands; 849 850 const auto *sameVariadicSize = 851 op.getTrait("::mlir::OpTrait::SameVariadicOperandSize"); 852 const auto *attrSizedOperands = 853 op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments"); 854 855 if (numVariadicOperands > 1 && !sameVariadicSize && !attrSizedOperands) { 856 PrintFatalError(op.getLoc(), "op has multiple variadic operands but no " 857 "specification over their sizes"); 858 } 859 860 if (numVariadicOperands < 2 && attrSizedOperands) { 861 PrintFatalError(op.getLoc(), "op must have at least two variadic operands " 862 "to use 'AttrSizedOperandSegments' trait"); 863 } 864 865 if (attrSizedOperands && sameVariadicSize) { 866 PrintFatalError(op.getLoc(), 867 "op cannot have both 'AttrSizedOperandSegments' and " 868 "'SameVariadicOperandSize' traits"); 869 } 870 871 // First emit a few "sink" getter methods upon which we layer all nicer named 872 // getter methods. 873 generateValueRangeStartAndEnd(opClass, "getODSOperandIndexAndLength", 874 numVariadicOperands, numNormalOperands, 875 rangeSizeCall, attrSizedOperands, sizeAttrInit, 876 const_cast<Operator &>(op).getOperands()); 877 878 auto *m = opClass.addMethodAndPrune(rangeType, "getODSOperands", "unsigned", 879 "index"); 880 auto &body = m->body(); 881 body << formatv(valueRangeReturnCode, rangeBeginCall, 882 "getODSOperandIndexAndLength(index)"); 883 884 // Then we emit nicer named getter methods by redirecting to the "sink" getter 885 // method. 886 for (int i = 0; i != numOperands; ++i) { 887 const auto &operand = op.getOperand(i); 888 if (operand.name.empty()) 889 continue; 890 891 if (operand.isOptional()) { 892 m = opClass.addMethodAndPrune("::mlir::Value", operand.name); 893 m->body() 894 << " auto operands = getODSOperands(" << i << ");\n" 895 << " return operands.empty() ? ::mlir::Value() : *operands.begin();"; 896 } else if (operand.isVariadic()) { 897 m = opClass.addMethodAndPrune(rangeType, operand.name); 898 m->body() << " return getODSOperands(" << i << ");"; 899 } else { 900 m = opClass.addMethodAndPrune("::mlir::Value", operand.name); 901 m->body() << " return *getODSOperands(" << i << ").begin();"; 902 } 903 } 904 } 905 906 void OpEmitter::genNamedOperandGetters() { 907 generateNamedOperandGetters( 908 op, opClass, 909 /*sizeAttrInit=*/ 910 formatv(opSegmentSizeAttrInitCode, "operand_segment_sizes").str(), 911 /*rangeType=*/"::mlir::Operation::operand_range", 912 /*rangeBeginCall=*/"getOperation()->operand_begin()", 913 /*rangeSizeCall=*/"getOperation()->getNumOperands()", 914 /*getOperandCallPattern=*/"getOperation()->getOperand({0})"); 915 } 916 917 void OpEmitter::genNamedOperandSetters() { 918 auto *attrSizedOperands = 919 op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments"); 920 for (int i = 0, e = op.getNumOperands(); i != e; ++i) { 921 const auto &operand = op.getOperand(i); 922 if (operand.name.empty()) 923 continue; 924 auto *m = opClass.addMethodAndPrune("::mlir::MutableOperandRange", 925 (operand.name + "Mutable").str()); 926 auto &body = m->body(); 927 body << " auto range = getODSOperandIndexAndLength(" << i << ");\n" 928 << " return ::mlir::MutableOperandRange(getOperation(), " 929 "range.first, range.second"; 930 if (attrSizedOperands) 931 body << ", ::mlir::MutableOperandRange::OperandSegment(" << i 932 << "u, *getOperation()->getAttrDictionary().getNamed(" 933 "\"operand_segment_sizes\"))"; 934 body << ");\n"; 935 } 936 } 937 938 void OpEmitter::genNamedResultGetters() { 939 const int numResults = op.getNumResults(); 940 const int numVariadicResults = op.getNumVariableLengthResults(); 941 const int numNormalResults = numResults - numVariadicResults; 942 943 // If we have more than one variadic results, we need more complicated logic 944 // to calculate the value range for each result. 945 946 const auto *sameVariadicSize = 947 op.getTrait("::mlir::OpTrait::SameVariadicResultSize"); 948 const auto *attrSizedResults = 949 op.getTrait("::mlir::OpTrait::AttrSizedResultSegments"); 950 951 if (numVariadicResults > 1 && !sameVariadicSize && !attrSizedResults) { 952 PrintFatalError(op.getLoc(), "op has multiple variadic results but no " 953 "specification over their sizes"); 954 } 955 956 if (numVariadicResults < 2 && attrSizedResults) { 957 PrintFatalError(op.getLoc(), "op must have at least two variadic results " 958 "to use 'AttrSizedResultSegments' trait"); 959 } 960 961 if (attrSizedResults && sameVariadicSize) { 962 PrintFatalError(op.getLoc(), 963 "op cannot have both 'AttrSizedResultSegments' and " 964 "'SameVariadicResultSize' traits"); 965 } 966 967 generateValueRangeStartAndEnd( 968 opClass, "getODSResultIndexAndLength", numVariadicResults, 969 numNormalResults, "getOperation()->getNumResults()", attrSizedResults, 970 formatv(opSegmentSizeAttrInitCode, "result_segment_sizes").str(), 971 op.getResults()); 972 973 auto *m = opClass.addMethodAndPrune("::mlir::Operation::result_range", 974 "getODSResults", "unsigned", "index"); 975 m->body() << formatv(valueRangeReturnCode, "getOperation()->result_begin()", 976 "getODSResultIndexAndLength(index)"); 977 978 for (int i = 0; i != numResults; ++i) { 979 const auto &result = op.getResult(i); 980 if (result.name.empty()) 981 continue; 982 983 if (result.isOptional()) { 984 m = opClass.addMethodAndPrune("::mlir::Value", result.name); 985 m->body() 986 << " auto results = getODSResults(" << i << ");\n" 987 << " return results.empty() ? ::mlir::Value() : *results.begin();"; 988 } else if (result.isVariadic()) { 989 m = opClass.addMethodAndPrune("::mlir::Operation::result_range", 990 result.name); 991 m->body() << " return getODSResults(" << i << ");"; 992 } else { 993 m = opClass.addMethodAndPrune("::mlir::Value", result.name); 994 m->body() << " return *getODSResults(" << i << ").begin();"; 995 } 996 } 997 } 998 999 void OpEmitter::genNamedRegionGetters() { 1000 unsigned numRegions = op.getNumRegions(); 1001 for (unsigned i = 0; i < numRegions; ++i) { 1002 const auto ®ion = op.getRegion(i); 1003 if (region.name.empty()) 1004 continue; 1005 1006 // Generate the accessors for a variadic region. 1007 if (region.isVariadic()) { 1008 auto *m = opClass.addMethodAndPrune( 1009 "::mlir::MutableArrayRef<::mlir::Region>", region.name); 1010 m->body() << formatv(" return (*this)->getRegions().drop_front({0});", 1011 i); 1012 continue; 1013 } 1014 1015 auto *m = opClass.addMethodAndPrune("::mlir::Region &", region.name); 1016 m->body() << formatv(" return (*this)->getRegion({0});", i); 1017 } 1018 } 1019 1020 void OpEmitter::genNamedSuccessorGetters() { 1021 unsigned numSuccessors = op.getNumSuccessors(); 1022 for (unsigned i = 0; i < numSuccessors; ++i) { 1023 const NamedSuccessor &successor = op.getSuccessor(i); 1024 if (successor.name.empty()) 1025 continue; 1026 1027 // Generate the accessors for a variadic successor list. 1028 if (successor.isVariadic()) { 1029 auto *m = 1030 opClass.addMethodAndPrune("::mlir::SuccessorRange", successor.name); 1031 m->body() << formatv( 1032 " return {std::next((*this)->successor_begin(), {0}), " 1033 "(*this)->successor_end()};", 1034 i); 1035 continue; 1036 } 1037 1038 auto *m = opClass.addMethodAndPrune("::mlir::Block *", successor.name); 1039 m->body() << formatv(" return (*this)->getSuccessor({0});", i); 1040 } 1041 } 1042 1043 static bool canGenerateUnwrappedBuilder(Operator &op) { 1044 // If this op does not have native attributes at all, return directly to avoid 1045 // redefining builders. 1046 if (op.getNumNativeAttributes() == 0) 1047 return false; 1048 1049 bool canGenerate = false; 1050 // We are generating builders that take raw values for attributes. We need to 1051 // make sure the native attributes have a meaningful "unwrapped" value type 1052 // different from the wrapped mlir::Attribute type to avoid redefining 1053 // builders. This checks for the op has at least one such native attribute. 1054 for (int i = 0, e = op.getNumNativeAttributes(); i < e; ++i) { 1055 NamedAttribute &namedAttr = op.getAttribute(i); 1056 if (canUseUnwrappedRawValue(namedAttr.attr)) { 1057 canGenerate = true; 1058 break; 1059 } 1060 } 1061 return canGenerate; 1062 } 1063 1064 static bool canInferType(Operator &op) { 1065 return op.getTrait("::mlir::InferTypeOpInterface::Trait") && 1066 op.getNumRegions() == 0; 1067 } 1068 1069 void OpEmitter::genSeparateArgParamBuilder() { 1070 SmallVector<AttrParamKind, 2> attrBuilderType; 1071 attrBuilderType.push_back(AttrParamKind::WrappedAttr); 1072 if (canGenerateUnwrappedBuilder(op)) 1073 attrBuilderType.push_back(AttrParamKind::UnwrappedValue); 1074 1075 // Emit with separate builders with or without unwrapped attributes and/or 1076 // inferring result type. 1077 auto emit = [&](AttrParamKind attrType, TypeParamKind paramKind, 1078 bool inferType) { 1079 llvm::SmallVector<OpMethodParameter, 4> paramList; 1080 llvm::SmallVector<std::string, 4> resultNames; 1081 buildParamList(paramList, resultNames, paramKind, attrType); 1082 1083 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1084 std::move(paramList)); 1085 // If the builder is redundant, skip generating the method. 1086 if (!m) 1087 return; 1088 auto &body = m->body(); 1089 genCodeForAddingArgAndRegionForBuilder( 1090 body, /*isRawValueAttr=*/attrType == AttrParamKind::UnwrappedValue); 1091 1092 // Push all result types to the operation state 1093 1094 if (inferType) { 1095 // Generate builder that infers type too. 1096 // TODO: Subsume this with general checking if type can be 1097 // inferred automatically. 1098 // TODO: Expand to handle regions. 1099 body << formatv(R"( 1100 ::llvm::SmallVector<::mlir::Type, 2> inferredReturnTypes; 1101 if (succeeded({0}::inferReturnTypes(odsBuilder.getContext(), 1102 {1}.location, {1}.operands, 1103 {1}.attributes.getDictionary({1}.getContext()), 1104 /*regions=*/{{}, inferredReturnTypes))) 1105 {1}.addTypes(inferredReturnTypes); 1106 else 1107 ::llvm::report_fatal_error("Failed to infer result type(s).");)", 1108 opClass.getClassName(), builderOpState); 1109 return; 1110 } 1111 1112 switch (paramKind) { 1113 case TypeParamKind::None: 1114 return; 1115 case TypeParamKind::Separate: 1116 for (int i = 0, e = op.getNumResults(); i < e; ++i) { 1117 if (op.getResult(i).isOptional()) 1118 body << " if (" << resultNames[i] << ")\n "; 1119 body << " " << builderOpState << ".addTypes(" << resultNames[i] 1120 << ");\n"; 1121 } 1122 return; 1123 case TypeParamKind::Collective: { 1124 int numResults = op.getNumResults(); 1125 int numVariadicResults = op.getNumVariableLengthResults(); 1126 int numNonVariadicResults = numResults - numVariadicResults; 1127 bool hasVariadicResult = numVariadicResults != 0; 1128 1129 // Avoid emitting "resultTypes.size() >= 0u" which is always true. 1130 if (!(hasVariadicResult && numNonVariadicResults == 0)) 1131 body << " " 1132 << "assert(resultTypes.size() " 1133 << (hasVariadicResult ? ">=" : "==") << " " 1134 << numNonVariadicResults 1135 << "u && \"mismatched number of results\");\n"; 1136 body << " " << builderOpState << ".addTypes(resultTypes);\n"; 1137 } 1138 return; 1139 } 1140 llvm_unreachable("unhandled TypeParamKind"); 1141 }; 1142 1143 // Some of the build methods generated here may be ambiguous, but TableGen's 1144 // ambiguous function detection will elide those ones. 1145 for (auto attrType : attrBuilderType) { 1146 emit(attrType, TypeParamKind::Separate, /*inferType=*/false); 1147 if (canInferType(op)) 1148 emit(attrType, TypeParamKind::None, /*inferType=*/true); 1149 emit(attrType, TypeParamKind::Collective, /*inferType=*/false); 1150 } 1151 } 1152 1153 void OpEmitter::genUseOperandAsResultTypeCollectiveParamBuilder() { 1154 int numResults = op.getNumResults(); 1155 1156 // Signature 1157 llvm::SmallVector<OpMethodParameter, 4> paramList; 1158 paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder"); 1159 paramList.emplace_back("::mlir::OperationState &", builderOpState); 1160 paramList.emplace_back("::mlir::ValueRange", "operands"); 1161 // Provide default value for `attributes` when its the last parameter 1162 StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}"; 1163 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>", 1164 "attributes", attributesDefaultValue); 1165 if (op.getNumVariadicRegions()) 1166 paramList.emplace_back("unsigned", "numRegions"); 1167 1168 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1169 std::move(paramList)); 1170 // If the builder is redundant, skip generating the method 1171 if (!m) 1172 return; 1173 auto &body = m->body(); 1174 1175 // Operands 1176 body << " " << builderOpState << ".addOperands(operands);\n"; 1177 1178 // Attributes 1179 body << " " << builderOpState << ".addAttributes(attributes);\n"; 1180 1181 // Create the correct number of regions 1182 if (int numRegions = op.getNumRegions()) { 1183 body << llvm::formatv( 1184 " for (unsigned i = 0; i != {0}; ++i)\n", 1185 (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions))); 1186 body << " (void)" << builderOpState << ".addRegion();\n"; 1187 } 1188 1189 // Result types 1190 SmallVector<std::string, 2> resultTypes(numResults, "operands[0].getType()"); 1191 body << " " << builderOpState << ".addTypes({" 1192 << llvm::join(resultTypes, ", ") << "});\n\n"; 1193 } 1194 1195 void OpEmitter::genInferredTypeCollectiveParamBuilder() { 1196 // TODO: Expand to support regions. 1197 SmallVector<OpMethodParameter, 4> paramList; 1198 paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder"); 1199 paramList.emplace_back("::mlir::OperationState &", builderOpState); 1200 paramList.emplace_back("::mlir::ValueRange", "operands"); 1201 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>", 1202 "attributes", "{}"); 1203 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1204 std::move(paramList)); 1205 // If the builder is redundant, skip generating the method 1206 if (!m) 1207 return; 1208 auto &body = m->body(); 1209 1210 int numResults = op.getNumResults(); 1211 int numVariadicResults = op.getNumVariableLengthResults(); 1212 int numNonVariadicResults = numResults - numVariadicResults; 1213 1214 int numOperands = op.getNumOperands(); 1215 int numVariadicOperands = op.getNumVariableLengthOperands(); 1216 int numNonVariadicOperands = numOperands - numVariadicOperands; 1217 1218 // Operands 1219 if (numVariadicOperands == 0 || numNonVariadicOperands != 0) 1220 body << " assert(operands.size()" 1221 << (numVariadicOperands != 0 ? " >= " : " == ") 1222 << numNonVariadicOperands 1223 << "u && \"mismatched number of parameters\");\n"; 1224 body << " " << builderOpState << ".addOperands(operands);\n"; 1225 body << " " << builderOpState << ".addAttributes(attributes);\n"; 1226 1227 // Create the correct number of regions 1228 if (int numRegions = op.getNumRegions()) { 1229 body << llvm::formatv( 1230 " for (unsigned i = 0; i != {0}; ++i)\n", 1231 (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions))); 1232 body << " (void)" << builderOpState << ".addRegion();\n"; 1233 } 1234 1235 // Result types 1236 body << formatv(R"( 1237 ::mlir::SmallVector<::mlir::Type, 2> inferredReturnTypes; 1238 if (succeeded({0}::inferReturnTypes(odsBuilder.getContext(), 1239 {1}.location, operands, 1240 {1}.attributes.getDictionary({1}.getContext()), 1241 /*regions=*/{{}, inferredReturnTypes))) {{)", 1242 opClass.getClassName(), builderOpState); 1243 if (numVariadicResults == 0 || numNonVariadicResults != 0) 1244 body << " assert(inferredReturnTypes.size()" 1245 << (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults 1246 << "u && \"mismatched number of return types\");\n"; 1247 body << " " << builderOpState << ".addTypes(inferredReturnTypes);"; 1248 1249 body << formatv(R"( 1250 } else 1251 ::llvm::report_fatal_error("Failed to infer result type(s).");)", 1252 opClass.getClassName(), builderOpState); 1253 } 1254 1255 void OpEmitter::genUseOperandAsResultTypeSeparateParamBuilder() { 1256 llvm::SmallVector<OpMethodParameter, 4> paramList; 1257 llvm::SmallVector<std::string, 4> resultNames; 1258 buildParamList(paramList, resultNames, TypeParamKind::None); 1259 1260 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1261 std::move(paramList)); 1262 // If the builder is redundant, skip generating the method 1263 if (!m) 1264 return; 1265 auto &body = m->body(); 1266 genCodeForAddingArgAndRegionForBuilder(body); 1267 1268 auto numResults = op.getNumResults(); 1269 if (numResults == 0) 1270 return; 1271 1272 // Push all result types to the operation state 1273 const char *index = op.getOperand(0).isVariadic() ? ".front()" : ""; 1274 std::string resultType = 1275 formatv("{0}{1}.getType()", getArgumentName(op, 0), index).str(); 1276 body << " " << builderOpState << ".addTypes({" << resultType; 1277 for (int i = 1; i != numResults; ++i) 1278 body << ", " << resultType; 1279 body << "});\n\n"; 1280 } 1281 1282 void OpEmitter::genUseAttrAsResultTypeBuilder() { 1283 SmallVector<OpMethodParameter, 4> paramList; 1284 paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder"); 1285 paramList.emplace_back("::mlir::OperationState &", builderOpState); 1286 paramList.emplace_back("::mlir::ValueRange", "operands"); 1287 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>", 1288 "attributes", "{}"); 1289 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1290 std::move(paramList)); 1291 // If the builder is redundant, skip generating the method 1292 if (!m) 1293 return; 1294 1295 auto &body = m->body(); 1296 1297 // Push all result types to the operation state 1298 std::string resultType; 1299 const auto &namedAttr = op.getAttribute(0); 1300 1301 body << " for (auto attr : attributes) {\n"; 1302 body << " if (attr.first != \"" << namedAttr.name << "\") continue;\n"; 1303 if (namedAttr.attr.isTypeAttr()) { 1304 resultType = "attr.second.cast<::mlir::TypeAttr>().getValue()"; 1305 } else { 1306 resultType = "attr.second.getType()"; 1307 } 1308 1309 // Operands 1310 body << " " << builderOpState << ".addOperands(operands);\n"; 1311 1312 // Attributes 1313 body << " " << builderOpState << ".addAttributes(attributes);\n"; 1314 1315 // Result types 1316 SmallVector<std::string, 2> resultTypes(op.getNumResults(), resultType); 1317 body << " " << builderOpState << ".addTypes({" 1318 << llvm::join(resultTypes, ", ") << "});\n"; 1319 body << " }\n"; 1320 } 1321 1322 /// Returns a signature of the builder. Updates the context `fctx` to enable 1323 /// replacement of $_builder and $_state in the body. 1324 static std::string getBuilderSignature(const Builder &builder) { 1325 ArrayRef<Builder::Parameter> params(builder.getParameters()); 1326 1327 // Inject builder and state arguments. 1328 llvm::SmallVector<std::string, 8> arguments; 1329 arguments.reserve(params.size() + 2); 1330 arguments.push_back( 1331 llvm::formatv("::mlir::OpBuilder &{0}", odsBuilder).str()); 1332 arguments.push_back( 1333 llvm::formatv("::mlir::OperationState &{0}", builderOpState).str()); 1334 1335 for (unsigned i = 0, e = params.size(); i < e; ++i) { 1336 // If no name is provided, generate one. 1337 Optional<StringRef> paramName = params[i].getName(); 1338 std::string name = 1339 paramName ? paramName->str() : "odsArg" + std::to_string(i); 1340 1341 std::string defaultValue; 1342 if (Optional<StringRef> defaultParamValue = params[i].getDefaultValue()) 1343 defaultValue = llvm::formatv(" = {0}", *defaultParamValue).str(); 1344 arguments.push_back( 1345 llvm::formatv("{0} {1}{2}", params[i].getCppType(), name, defaultValue) 1346 .str()); 1347 } 1348 1349 return llvm::join(arguments, ", "); 1350 } 1351 1352 void OpEmitter::genBuilder() { 1353 // Handle custom builders if provided. 1354 for (const Builder &builder : op.getBuilders()) { 1355 std::string paramStr = getBuilderSignature(builder); 1356 1357 Optional<StringRef> body = builder.getBody(); 1358 OpMethod::Property properties = 1359 body ? OpMethod::MP_Static : OpMethod::MP_StaticDeclaration; 1360 auto *method = 1361 opClass.addMethodAndPrune("void", "build", properties, paramStr); 1362 1363 FmtContext fctx; 1364 fctx.withBuilder(odsBuilder); 1365 fctx.addSubst("_state", builderOpState); 1366 if (body) 1367 method->body() << tgfmt(*body, &fctx); 1368 } 1369 1370 // Generate default builders that requires all result type, operands, and 1371 // attributes as parameters. 1372 if (op.skipDefaultBuilders()) 1373 return; 1374 1375 // We generate three classes of builders here: 1376 // 1. one having a stand-alone parameter for each operand / attribute, and 1377 genSeparateArgParamBuilder(); 1378 // 2. one having an aggregated parameter for all result types / operands / 1379 // attributes, and 1380 genCollectiveParamBuilder(); 1381 // 3. one having a stand-alone parameter for each operand and attribute, 1382 // use the first operand or attribute's type as all result types 1383 // to facilitate different call patterns. 1384 if (op.getNumVariableLengthResults() == 0) { 1385 if (op.getTrait("::mlir::OpTrait::SameOperandsAndResultType")) { 1386 genUseOperandAsResultTypeSeparateParamBuilder(); 1387 genUseOperandAsResultTypeCollectiveParamBuilder(); 1388 } 1389 if (op.getTrait("::mlir::OpTrait::FirstAttrDerivedResultType")) 1390 genUseAttrAsResultTypeBuilder(); 1391 } 1392 } 1393 1394 void OpEmitter::genCollectiveParamBuilder() { 1395 int numResults = op.getNumResults(); 1396 int numVariadicResults = op.getNumVariableLengthResults(); 1397 int numNonVariadicResults = numResults - numVariadicResults; 1398 1399 int numOperands = op.getNumOperands(); 1400 int numVariadicOperands = op.getNumVariableLengthOperands(); 1401 int numNonVariadicOperands = numOperands - numVariadicOperands; 1402 1403 SmallVector<OpMethodParameter, 4> paramList; 1404 paramList.emplace_back("::mlir::OpBuilder &", ""); 1405 paramList.emplace_back("::mlir::OperationState &", builderOpState); 1406 paramList.emplace_back("::mlir::TypeRange", "resultTypes"); 1407 paramList.emplace_back("::mlir::ValueRange", "operands"); 1408 // Provide default value for `attributes` when its the last parameter 1409 StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}"; 1410 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>", 1411 "attributes", attributesDefaultValue); 1412 if (op.getNumVariadicRegions()) 1413 paramList.emplace_back("unsigned", "numRegions"); 1414 1415 auto *m = opClass.addMethodAndPrune("void", "build", OpMethod::MP_Static, 1416 std::move(paramList)); 1417 // If the builder is redundant, skip generating the method 1418 if (!m) 1419 return; 1420 auto &body = m->body(); 1421 1422 // Operands 1423 if (numVariadicOperands == 0 || numNonVariadicOperands != 0) 1424 body << " assert(operands.size()" 1425 << (numVariadicOperands != 0 ? " >= " : " == ") 1426 << numNonVariadicOperands 1427 << "u && \"mismatched number of parameters\");\n"; 1428 body << " " << builderOpState << ".addOperands(operands);\n"; 1429 1430 // Attributes 1431 body << " " << builderOpState << ".addAttributes(attributes);\n"; 1432 1433 // Create the correct number of regions 1434 if (int numRegions = op.getNumRegions()) { 1435 body << llvm::formatv( 1436 " for (unsigned i = 0; i != {0}; ++i)\n", 1437 (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions))); 1438 body << " (void)" << builderOpState << ".addRegion();\n"; 1439 } 1440 1441 // Result types 1442 if (numVariadicResults == 0 || numNonVariadicResults != 0) 1443 body << " assert(resultTypes.size()" 1444 << (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults 1445 << "u && \"mismatched number of return types\");\n"; 1446 body << " " << builderOpState << ".addTypes(resultTypes);\n"; 1447 1448 // Generate builder that infers type too. 1449 // TODO: Expand to handle regions and successors. 1450 if (canInferType(op) && op.getNumSuccessors() == 0) 1451 genInferredTypeCollectiveParamBuilder(); 1452 } 1453 1454 void OpEmitter::buildParamList(SmallVectorImpl<OpMethodParameter> ¶mList, 1455 SmallVectorImpl<std::string> &resultTypeNames, 1456 TypeParamKind typeParamKind, 1457 AttrParamKind attrParamKind) { 1458 resultTypeNames.clear(); 1459 auto numResults = op.getNumResults(); 1460 resultTypeNames.reserve(numResults); 1461 1462 paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder"); 1463 paramList.emplace_back("::mlir::OperationState &", builderOpState); 1464 1465 switch (typeParamKind) { 1466 case TypeParamKind::None: 1467 break; 1468 case TypeParamKind::Separate: { 1469 // Add parameters for all return types 1470 for (int i = 0; i < numResults; ++i) { 1471 const auto &result = op.getResult(i); 1472 std::string resultName = std::string(result.name); 1473 if (resultName.empty()) 1474 resultName = std::string(formatv("resultType{0}", i)); 1475 1476 StringRef type = 1477 result.isVariadic() ? "::mlir::TypeRange" : "::mlir::Type"; 1478 OpMethodParameter::Property properties = OpMethodParameter::PP_None; 1479 if (result.isOptional()) 1480 properties = OpMethodParameter::PP_Optional; 1481 1482 paramList.emplace_back(type, resultName, properties); 1483 resultTypeNames.emplace_back(std::move(resultName)); 1484 } 1485 } break; 1486 case TypeParamKind::Collective: { 1487 paramList.emplace_back("::mlir::TypeRange", "resultTypes"); 1488 resultTypeNames.push_back("resultTypes"); 1489 } break; 1490 } 1491 1492 // Add parameters for all arguments (operands and attributes). 1493 1494 int numOperands = 0; 1495 int numAttrs = 0; 1496 1497 int defaultValuedAttrStartIndex = op.getNumArgs(); 1498 if (attrParamKind == AttrParamKind::UnwrappedValue) { 1499 // Calculate the start index from which we can attach default values in the 1500 // builder declaration. 1501 for (int i = op.getNumArgs() - 1; i >= 0; --i) { 1502 auto *namedAttr = op.getArg(i).dyn_cast<tblgen::NamedAttribute *>(); 1503 if (!namedAttr || !namedAttr->attr.hasDefaultValue()) 1504 break; 1505 1506 if (!canUseUnwrappedRawValue(namedAttr->attr)) 1507 break; 1508 1509 // Creating an APInt requires us to provide bitwidth, value, and 1510 // signedness, which is complicated compared to others. Similarly 1511 // for APFloat. 1512 // TODO: Adjust the 'returnType' field of such attributes 1513 // to support them. 1514 StringRef retType = namedAttr->attr.getReturnType(); 1515 if (retType == "::llvm::APInt" || retType == "::llvm::APFloat") 1516 break; 1517 1518 defaultValuedAttrStartIndex = i; 1519 } 1520 } 1521 1522 for (int i = 0, e = op.getNumArgs(); i < e; ++i) { 1523 auto argument = op.getArg(i); 1524 if (argument.is<tblgen::NamedTypeConstraint *>()) { 1525 const auto &operand = op.getOperand(numOperands); 1526 StringRef type = 1527 operand.isVariadic() ? "::mlir::ValueRange" : "::mlir::Value"; 1528 OpMethodParameter::Property properties = OpMethodParameter::PP_None; 1529 if (operand.isOptional()) 1530 properties = OpMethodParameter::PP_Optional; 1531 1532 paramList.emplace_back(type, getArgumentName(op, numOperands), 1533 properties); 1534 ++numOperands; 1535 } else { 1536 const auto &namedAttr = op.getAttribute(numAttrs); 1537 const auto &attr = namedAttr.attr; 1538 1539 OpMethodParameter::Property properties = OpMethodParameter::PP_None; 1540 if (attr.isOptional()) 1541 properties = OpMethodParameter::PP_Optional; 1542 1543 StringRef type; 1544 switch (attrParamKind) { 1545 case AttrParamKind::WrappedAttr: 1546 type = attr.getStorageType(); 1547 break; 1548 case AttrParamKind::UnwrappedValue: 1549 if (canUseUnwrappedRawValue(attr)) 1550 type = attr.getReturnType(); 1551 else 1552 type = attr.getStorageType(); 1553 break; 1554 } 1555 1556 std::string defaultValue; 1557 // Attach default value if requested and possible. 1558 if (attrParamKind == AttrParamKind::UnwrappedValue && 1559 i >= defaultValuedAttrStartIndex) { 1560 bool isString = attr.getReturnType() == "::llvm::StringRef"; 1561 if (isString) 1562 defaultValue.append("\""); 1563 defaultValue += attr.getDefaultValue(); 1564 if (isString) 1565 defaultValue.append("\""); 1566 } 1567 paramList.emplace_back(type, namedAttr.name, defaultValue, properties); 1568 ++numAttrs; 1569 } 1570 } 1571 1572 /// Insert parameters for each successor. 1573 for (const NamedSuccessor &succ : op.getSuccessors()) { 1574 StringRef type = 1575 succ.isVariadic() ? "::mlir::BlockRange" : "::mlir::Block *"; 1576 paramList.emplace_back(type, succ.name); 1577 } 1578 1579 /// Insert parameters for variadic regions. 1580 for (const NamedRegion ®ion : op.getRegions()) 1581 if (region.isVariadic()) 1582 paramList.emplace_back("unsigned", 1583 llvm::formatv("{0}Count", region.name).str()); 1584 } 1585 1586 void OpEmitter::genCodeForAddingArgAndRegionForBuilder(OpMethodBody &body, 1587 bool isRawValueAttr) { 1588 // Push all operands to the result. 1589 for (int i = 0, e = op.getNumOperands(); i < e; ++i) { 1590 std::string argName = getArgumentName(op, i); 1591 if (op.getOperand(i).isOptional()) 1592 body << " if (" << argName << ")\n "; 1593 body << " " << builderOpState << ".addOperands(" << argName << ");\n"; 1594 } 1595 1596 // If the operation has the operand segment size attribute, add it here. 1597 if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) { 1598 body << " " << builderOpState 1599 << ".addAttribute(\"operand_segment_sizes\", " 1600 "odsBuilder.getI32VectorAttr({"; 1601 interleaveComma(llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) { 1602 if (op.getOperand(i).isOptional()) 1603 body << "(" << getArgumentName(op, i) << " ? 1 : 0)"; 1604 else if (op.getOperand(i).isVariadic()) 1605 body << "static_cast<int32_t>(" << getArgumentName(op, i) << ".size())"; 1606 else 1607 body << "1"; 1608 }); 1609 body << "}));\n"; 1610 } 1611 1612 // Push all attributes to the result. 1613 for (const auto &namedAttr : op.getAttributes()) { 1614 auto &attr = namedAttr.attr; 1615 if (!attr.isDerivedAttr()) { 1616 bool emitNotNullCheck = attr.isOptional(); 1617 if (emitNotNullCheck) { 1618 body << formatv(" if ({0}) ", namedAttr.name) << "{\n"; 1619 } 1620 if (isRawValueAttr && canUseUnwrappedRawValue(attr)) { 1621 // If this is a raw value, then we need to wrap it in an Attribute 1622 // instance. 1623 FmtContext fctx; 1624 fctx.withBuilder("odsBuilder"); 1625 1626 std::string builderTemplate = 1627 std::string(attr.getConstBuilderTemplate()); 1628 1629 // For StringAttr, its constant builder call will wrap the input in 1630 // quotes, which is correct for normal string literals, but incorrect 1631 // here given we use function arguments. So we need to strip the 1632 // wrapping quotes. 1633 if (StringRef(builderTemplate).contains("\"$0\"")) 1634 builderTemplate = replaceAllSubstrs(builderTemplate, "\"$0\"", "$0"); 1635 1636 std::string value = 1637 std::string(tgfmt(builderTemplate, &fctx, namedAttr.name)); 1638 body << formatv(" {0}.addAttribute(\"{1}\", {2});\n", builderOpState, 1639 namedAttr.name, value); 1640 } else { 1641 body << formatv(" {0}.addAttribute(\"{1}\", {1});\n", builderOpState, 1642 namedAttr.name); 1643 } 1644 if (emitNotNullCheck) { 1645 body << " }\n"; 1646 } 1647 } 1648 } 1649 1650 // Create the correct number of regions. 1651 for (const NamedRegion ®ion : op.getRegions()) { 1652 if (region.isVariadic()) 1653 body << formatv(" for (unsigned i = 0; i < {0}Count; ++i)\n ", 1654 region.name); 1655 1656 body << " (void)" << builderOpState << ".addRegion();\n"; 1657 } 1658 1659 // Push all successors to the result. 1660 for (const NamedSuccessor &namedSuccessor : op.getSuccessors()) { 1661 body << formatv(" {0}.addSuccessors({1});\n", builderOpState, 1662 namedSuccessor.name); 1663 } 1664 } 1665 1666 void OpEmitter::genCanonicalizerDecls() { 1667 bool hasCanonicalizeMethod = def.getValueAsBit("hasCanonicalizeMethod"); 1668 if (hasCanonicalizeMethod) { 1669 // static LogicResult FooOp:: 1670 // canonicalize(FooOp op, PatternRewriter &rewriter); 1671 SmallVector<OpMethodParameter, 2> paramList; 1672 paramList.emplace_back(op.getCppClassName(), "op"); 1673 paramList.emplace_back("::mlir::PatternRewriter &", "rewriter"); 1674 opClass.addMethodAndPrune("::mlir::LogicalResult", "canonicalize", 1675 OpMethod::MP_StaticDeclaration, 1676 std::move(paramList)); 1677 } 1678 1679 // We get a prototype for 'getCanonicalizationPatterns' if requested directly 1680 // or if using a 'canonicalize' method. 1681 bool hasCanonicalizer = def.getValueAsBit("hasCanonicalizer"); 1682 if (!hasCanonicalizeMethod && !hasCanonicalizer) 1683 return; 1684 1685 // We get a body for 'getCanonicalizationPatterns' when using a 'canonicalize' 1686 // method, but not implementing 'getCanonicalizationPatterns' manually. 1687 bool hasBody = hasCanonicalizeMethod && !hasCanonicalizer; 1688 1689 // Add a signature for getCanonicalizationPatterns if implemented by the 1690 // dialect or if synthesized to call 'canonicalize'. 1691 SmallVector<OpMethodParameter, 2> paramList; 1692 paramList.emplace_back("::mlir::RewritePatternSet &", "results"); 1693 paramList.emplace_back("::mlir::MLIRContext *", "context"); 1694 auto kind = hasBody ? OpMethod::MP_Static : OpMethod::MP_StaticDeclaration; 1695 auto *method = opClass.addMethodAndPrune( 1696 "void", "getCanonicalizationPatterns", kind, std::move(paramList)); 1697 1698 // If synthesizing the method, fill it it. 1699 if (hasBody) 1700 method->body() << " results.add(canonicalize);\n"; 1701 } 1702 1703 void OpEmitter::genFolderDecls() { 1704 bool hasSingleResult = 1705 op.getNumResults() == 1 && op.getNumVariableLengthResults() == 0; 1706 1707 if (def.getValueAsBit("hasFolder")) { 1708 if (hasSingleResult) { 1709 opClass.addMethodAndPrune( 1710 "::mlir::OpFoldResult", "fold", OpMethod::MP_Declaration, 1711 "::llvm::ArrayRef<::mlir::Attribute>", "operands"); 1712 } else { 1713 SmallVector<OpMethodParameter, 2> paramList; 1714 paramList.emplace_back("::llvm::ArrayRef<::mlir::Attribute>", "operands"); 1715 paramList.emplace_back("::llvm::SmallVectorImpl<::mlir::OpFoldResult> &", 1716 "results"); 1717 opClass.addMethodAndPrune("::mlir::LogicalResult", "fold", 1718 OpMethod::MP_Declaration, std::move(paramList)); 1719 } 1720 } 1721 } 1722 1723 void OpEmitter::genOpInterfaceMethods(const tblgen::InterfaceTrait *opTrait) { 1724 Interface interface = opTrait->getInterface(); 1725 1726 // Get the set of methods that should always be declared. 1727 auto alwaysDeclaredMethodsVec = opTrait->getAlwaysDeclaredMethods(); 1728 llvm::StringSet<> alwaysDeclaredMethods; 1729 alwaysDeclaredMethods.insert(alwaysDeclaredMethodsVec.begin(), 1730 alwaysDeclaredMethodsVec.end()); 1731 1732 for (const InterfaceMethod &method : interface.getMethods()) { 1733 // Don't declare if the method has a body. 1734 if (method.getBody()) 1735 continue; 1736 // Don't declare if the method has a default implementation and the op 1737 // didn't request that it always be declared. 1738 if (method.getDefaultImplementation() && 1739 !alwaysDeclaredMethods.count(method.getName())) 1740 continue; 1741 genOpInterfaceMethod(method); 1742 } 1743 } 1744 1745 OpMethod *OpEmitter::genOpInterfaceMethod(const InterfaceMethod &method, 1746 bool declaration) { 1747 SmallVector<OpMethodParameter, 4> paramList; 1748 for (const InterfaceMethod::Argument &arg : method.getArguments()) 1749 paramList.emplace_back(arg.type, arg.name); 1750 1751 auto properties = method.isStatic() ? OpMethod::MP_Static : OpMethod::MP_None; 1752 if (declaration) 1753 properties = 1754 static_cast<OpMethod::Property>(properties | OpMethod::MP_Declaration); 1755 return opClass.addMethodAndPrune(method.getReturnType(), method.getName(), 1756 properties, std::move(paramList)); 1757 } 1758 1759 void OpEmitter::genOpInterfaceMethods() { 1760 for (const auto &trait : op.getTraits()) { 1761 if (const auto *opTrait = dyn_cast<tblgen::InterfaceTrait>(&trait)) 1762 if (opTrait->shouldDeclareMethods()) 1763 genOpInterfaceMethods(opTrait); 1764 } 1765 } 1766 1767 void OpEmitter::genSideEffectInterfaceMethods() { 1768 enum EffectKind { Operand, Result, Symbol, Static }; 1769 struct EffectLocation { 1770 /// The effect applied. 1771 SideEffect effect; 1772 1773 /// The index if the kind is not static. 1774 unsigned index : 30; 1775 1776 /// The kind of the location. 1777 unsigned kind : 2; 1778 }; 1779 1780 StringMap<SmallVector<EffectLocation, 1>> interfaceEffects; 1781 auto resolveDecorators = [&](Operator::var_decorator_range decorators, 1782 unsigned index, unsigned kind) { 1783 for (auto decorator : decorators) 1784 if (SideEffect *effect = dyn_cast<SideEffect>(&decorator)) { 1785 opClass.addTrait(effect->getInterfaceTrait()); 1786 interfaceEffects[effect->getBaseEffectName()].push_back( 1787 EffectLocation{*effect, index, kind}); 1788 } 1789 }; 1790 1791 // Collect effects that were specified via: 1792 /// Traits. 1793 for (const auto &trait : op.getTraits()) { 1794 const auto *opTrait = dyn_cast<tblgen::SideEffectTrait>(&trait); 1795 if (!opTrait) 1796 continue; 1797 auto &effects = interfaceEffects[opTrait->getBaseEffectName()]; 1798 for (auto decorator : opTrait->getEffects()) 1799 effects.push_back(EffectLocation{cast<SideEffect>(decorator), 1800 /*index=*/0, EffectKind::Static}); 1801 } 1802 /// Attributes and Operands. 1803 for (unsigned i = 0, operandIt = 0, e = op.getNumArgs(); i != e; ++i) { 1804 Argument arg = op.getArg(i); 1805 if (arg.is<NamedTypeConstraint *>()) { 1806 resolveDecorators(op.getArgDecorators(i), operandIt, EffectKind::Operand); 1807 ++operandIt; 1808 continue; 1809 } 1810 const NamedAttribute *attr = arg.get<NamedAttribute *>(); 1811 if (attr->attr.getBaseAttr().isSymbolRefAttr()) 1812 resolveDecorators(op.getArgDecorators(i), i, EffectKind::Symbol); 1813 } 1814 /// Results. 1815 for (unsigned i = 0, e = op.getNumResults(); i != e; ++i) 1816 resolveDecorators(op.getResultDecorators(i), i, EffectKind::Result); 1817 1818 // The code used to add an effect instance. 1819 // {0}: The effect class. 1820 // {1}: Optional value or symbol reference. 1821 // {1}: The resource class. 1822 const char *addEffectCode = 1823 " effects.emplace_back({0}::get(), {1}{2}::get());\n"; 1824 1825 for (auto &it : interfaceEffects) { 1826 // Generate the 'getEffects' method. 1827 std::string type = llvm::formatv("::mlir::SmallVectorImpl<::mlir::" 1828 "SideEffects::EffectInstance<{0}>> &", 1829 it.first()) 1830 .str(); 1831 auto *getEffects = 1832 opClass.addMethodAndPrune("void", "getEffects", type, "effects"); 1833 auto &body = getEffects->body(); 1834 1835 // Add effect instances for each of the locations marked on the operation. 1836 for (auto &location : it.second) { 1837 StringRef effect = location.effect.getName(); 1838 StringRef resource = location.effect.getResource(); 1839 if (location.kind == EffectKind::Static) { 1840 // A static instance has no attached value. 1841 body << llvm::formatv(addEffectCode, effect, "", resource).str(); 1842 } else if (location.kind == EffectKind::Symbol) { 1843 // A symbol reference requires adding the proper attribute. 1844 const auto *attr = op.getArg(location.index).get<NamedAttribute *>(); 1845 if (attr->attr.isOptional()) { 1846 body << " if (auto symbolRef = " << attr->name << "Attr())\n " 1847 << llvm::formatv(addEffectCode, effect, "symbolRef, ", resource) 1848 .str(); 1849 } else { 1850 body << llvm::formatv(addEffectCode, effect, attr->name + "(), ", 1851 resource) 1852 .str(); 1853 } 1854 } else { 1855 // Otherwise this is an operand/result, so we need to attach the Value. 1856 body << " for (::mlir::Value value : getODS" 1857 << (location.kind == EffectKind::Operand ? "Operands" : "Results") 1858 << "(" << location.index << "))\n " 1859 << llvm::formatv(addEffectCode, effect, "value, ", resource).str(); 1860 } 1861 } 1862 } 1863 } 1864 1865 void OpEmitter::genTypeInterfaceMethods() { 1866 if (!op.allResultTypesKnown()) 1867 return; 1868 // Generate 'inferReturnTypes' method declaration using the interface method 1869 // declared in 'InferTypeOpInterface' op interface. 1870 const auto *trait = dyn_cast<InterfaceTrait>( 1871 op.getTrait("::mlir::InferTypeOpInterface::Trait")); 1872 Interface interface = trait->getInterface(); 1873 OpMethod *method = [&]() -> OpMethod * { 1874 for (const InterfaceMethod &interfaceMethod : interface.getMethods()) { 1875 if (interfaceMethod.getName() == "inferReturnTypes") { 1876 return genOpInterfaceMethod(interfaceMethod, /*declaration=*/false); 1877 } 1878 } 1879 assert(0 && "unable to find inferReturnTypes interface method"); 1880 return nullptr; 1881 }(); 1882 auto &body = method->body(); 1883 body << " inferredReturnTypes.resize(" << op.getNumResults() << ");\n"; 1884 1885 FmtContext fctx; 1886 fctx.withBuilder("odsBuilder"); 1887 body << " ::mlir::Builder odsBuilder(context);\n"; 1888 1889 auto emitType = 1890 [&](const tblgen::Operator::ArgOrType &type) -> OpMethodBody & { 1891 if (type.isArg()) { 1892 auto argIndex = type.getArg(); 1893 assert(!op.getArg(argIndex).is<NamedAttribute *>()); 1894 auto arg = op.getArgToOperandOrAttribute(argIndex); 1895 if (arg.kind() == Operator::OperandOrAttribute::Kind::Operand) 1896 return body << "operands[" << arg.operandOrAttributeIndex() 1897 << "].getType()"; 1898 return body << "attributes[" << arg.operandOrAttributeIndex() 1899 << "].getType()"; 1900 } else { 1901 return body << tgfmt(*type.getType().getBuilderCall(), &fctx); 1902 } 1903 }; 1904 1905 for (int i = 0, e = op.getNumResults(); i != e; ++i) { 1906 body << " inferredReturnTypes[" << i << "] = "; 1907 auto types = op.getSameTypeAsResult(i); 1908 emitType(types[0]) << ";\n"; 1909 if (types.size() == 1) 1910 continue; 1911 // TODO: We could verify equality here, but skipping that for verification. 1912 } 1913 body << " return ::mlir::success();"; 1914 } 1915 1916 void OpEmitter::genParser() { 1917 if (!hasStringAttribute(def, "parser") || 1918 hasStringAttribute(def, "assemblyFormat")) 1919 return; 1920 1921 SmallVector<OpMethodParameter, 2> paramList; 1922 paramList.emplace_back("::mlir::OpAsmParser &", "parser"); 1923 paramList.emplace_back("::mlir::OperationState &", "result"); 1924 auto *method = 1925 opClass.addMethodAndPrune("::mlir::ParseResult", "parse", 1926 OpMethod::MP_Static, std::move(paramList)); 1927 1928 FmtContext fctx; 1929 fctx.addSubst("cppClass", opClass.getClassName()); 1930 auto parser = def.getValueAsString("parser").ltrim().rtrim(" \t\v\f\r"); 1931 method->body() << " " << tgfmt(parser, &fctx); 1932 } 1933 1934 void OpEmitter::genPrinter() { 1935 if (hasStringAttribute(def, "assemblyFormat")) 1936 return; 1937 1938 auto valueInit = def.getValueInit("printer"); 1939 StringInit *stringInit = dyn_cast<StringInit>(valueInit); 1940 if (!stringInit) 1941 return; 1942 1943 auto *method = 1944 opClass.addMethodAndPrune("void", "print", "::mlir::OpAsmPrinter &", "p"); 1945 FmtContext fctx; 1946 fctx.addSubst("cppClass", opClass.getClassName()); 1947 auto printer = stringInit->getValue().ltrim().rtrim(" \t\v\f\r"); 1948 method->body() << " " << tgfmt(printer, &fctx); 1949 } 1950 1951 void OpEmitter::genVerifier() { 1952 auto *method = opClass.addMethodAndPrune("::mlir::LogicalResult", "verify"); 1953 auto &body = method->body(); 1954 body << " if (failed(" << op.getAdaptorName() 1955 << "(*this).verify((*this)->getLoc()))) " 1956 << "return ::mlir::failure();\n"; 1957 1958 auto *valueInit = def.getValueInit("verifier"); 1959 StringInit *stringInit = dyn_cast<StringInit>(valueInit); 1960 bool hasCustomVerify = stringInit && !stringInit->getValue().empty(); 1961 populateSubstitutions(op, "(*this)->getAttr", "this->getODSOperands", 1962 "this->getODSResults", verifyCtx); 1963 1964 genAttributeVerifier(op, "(*this)->getAttr", "emitOpError(", 1965 /*emitVerificationRequiringOp=*/true, verifyCtx, body); 1966 genOperandResultVerifier(body, op.getOperands(), "operand"); 1967 genOperandResultVerifier(body, op.getResults(), "result"); 1968 1969 for (auto &trait : op.getTraits()) { 1970 if (auto *t = dyn_cast<tblgen::PredTrait>(&trait)) { 1971 body << tgfmt(" if (!($0))\n " 1972 "return emitOpError(\"failed to verify that $1\");\n", 1973 &verifyCtx, tgfmt(t->getPredTemplate(), &verifyCtx), 1974 t->getSummary()); 1975 } 1976 } 1977 1978 genRegionVerifier(body); 1979 genSuccessorVerifier(body); 1980 1981 if (hasCustomVerify) { 1982 FmtContext fctx; 1983 fctx.addSubst("cppClass", opClass.getClassName()); 1984 auto printer = stringInit->getValue().ltrim().rtrim(" \t\v\f\r"); 1985 body << " " << tgfmt(printer, &fctx); 1986 } else { 1987 body << " return ::mlir::success();\n"; 1988 } 1989 } 1990 1991 void OpEmitter::genOperandResultVerifier(OpMethodBody &body, 1992 Operator::value_range values, 1993 StringRef valueKind) { 1994 FmtContext fctx; 1995 1996 body << " {\n"; 1997 body << " unsigned index = 0; (void)index;\n"; 1998 1999 for (auto staticValue : llvm::enumerate(values)) { 2000 bool hasPredicate = staticValue.value().hasPredicate(); 2001 bool isOptional = staticValue.value().isOptional(); 2002 if (!hasPredicate && !isOptional) 2003 continue; 2004 body << formatv(" auto valueGroup{2} = getODS{0}{1}s({2});\n", 2005 // Capitalize the first letter to match the function name 2006 valueKind.substr(0, 1).upper(), valueKind.substr(1), 2007 staticValue.index()); 2008 2009 // If the constraint is optional check that the value group has at most 1 2010 // value. 2011 if (isOptional) { 2012 body << formatv(" if (valueGroup{0}.size() > 1)\n" 2013 " return emitOpError(\"{1} group starting at #\") " 2014 "<< index << \" requires 0 or 1 element, but found \" << " 2015 "valueGroup{0}.size();\n", 2016 staticValue.index(), valueKind); 2017 } 2018 2019 // Otherwise, if there is no predicate there is nothing left to do. 2020 if (!hasPredicate) 2021 continue; 2022 // Emit a loop to check all the dynamic values in the pack. 2023 StringRef constraintFn = staticVerifierEmitter.getTypeConstraintFn( 2024 staticValue.value().constraint); 2025 body << " for (::mlir::Value v : valueGroup" << staticValue.index() 2026 << ") {\n" 2027 << " if (::mlir::failed(" << constraintFn 2028 << "(getOperation(), v.getType(), \"" << valueKind << "\", index)))\n" 2029 << " return ::mlir::failure();\n" 2030 << " ++index;\n" 2031 << " }\n"; 2032 } 2033 2034 body << " }\n"; 2035 } 2036 2037 void OpEmitter::genRegionVerifier(OpMethodBody &body) { 2038 // If we have no regions, there is nothing more to do. 2039 unsigned numRegions = op.getNumRegions(); 2040 if (numRegions == 0) 2041 return; 2042 2043 body << "{\n"; 2044 body << " unsigned index = 0; (void)index;\n"; 2045 2046 for (unsigned i = 0; i < numRegions; ++i) { 2047 const auto ®ion = op.getRegion(i); 2048 if (region.constraint.getPredicate().isNull()) 2049 continue; 2050 2051 body << " for (::mlir::Region ®ion : "; 2052 body << formatv(region.isVariadic() 2053 ? "{0}()" 2054 : "::mlir::MutableArrayRef<::mlir::Region>((*this)" 2055 "->getRegion({1}))", 2056 region.name, i); 2057 body << ") {\n"; 2058 auto constraint = tgfmt(region.constraint.getConditionTemplate(), 2059 &verifyCtx.withSelf("region")) 2060 .str(); 2061 2062 body << formatv(" (void)region;\n" 2063 " if (!({0})) {\n " 2064 "return emitOpError(\"region #\") << index << \" {1}" 2065 "failed to " 2066 "verify constraint: {2}\";\n }\n", 2067 constraint, 2068 region.name.empty() ? "" : "('" + region.name + "') ", 2069 region.constraint.getSummary()) 2070 << " ++index;\n" 2071 << " }\n"; 2072 } 2073 body << " }\n"; 2074 } 2075 2076 void OpEmitter::genSuccessorVerifier(OpMethodBody &body) { 2077 // If we have no successors, there is nothing more to do. 2078 unsigned numSuccessors = op.getNumSuccessors(); 2079 if (numSuccessors == 0) 2080 return; 2081 2082 body << "{\n"; 2083 body << " unsigned index = 0; (void)index;\n"; 2084 2085 for (unsigned i = 0; i < numSuccessors; ++i) { 2086 const auto &successor = op.getSuccessor(i); 2087 if (successor.constraint.getPredicate().isNull()) 2088 continue; 2089 2090 if (successor.isVariadic()) { 2091 body << formatv(" for (::mlir::Block *successor : {0}()) {\n", 2092 successor.name); 2093 } else { 2094 body << " {\n"; 2095 body << formatv(" ::mlir::Block *successor = {0}();\n", 2096 successor.name); 2097 } 2098 auto constraint = tgfmt(successor.constraint.getConditionTemplate(), 2099 &verifyCtx.withSelf("successor")) 2100 .str(); 2101 2102 body << formatv(" (void)successor;\n" 2103 " if (!({0})) {\n " 2104 "return emitOpError(\"successor #\") << index << \"('{1}') " 2105 "failed to " 2106 "verify constraint: {2}\";\n }\n", 2107 constraint, successor.name, 2108 successor.constraint.getSummary()) 2109 << " ++index;\n" 2110 << " }\n"; 2111 } 2112 body << " }\n"; 2113 } 2114 2115 /// Add a size count trait to the given operation class. 2116 static void addSizeCountTrait(OpClass &opClass, StringRef traitKind, 2117 int numTotal, int numVariadic) { 2118 if (numVariadic != 0) { 2119 if (numTotal == numVariadic) 2120 opClass.addTrait("::mlir::OpTrait::Variadic" + traitKind + "s"); 2121 else 2122 opClass.addTrait("::mlir::OpTrait::AtLeastN" + traitKind + "s<" + 2123 Twine(numTotal - numVariadic) + ">::Impl"); 2124 return; 2125 } 2126 switch (numTotal) { 2127 case 0: 2128 opClass.addTrait("::mlir::OpTrait::Zero" + traitKind); 2129 break; 2130 case 1: 2131 opClass.addTrait("::mlir::OpTrait::One" + traitKind); 2132 break; 2133 default: 2134 opClass.addTrait("::mlir::OpTrait::N" + traitKind + "s<" + Twine(numTotal) + 2135 ">::Impl"); 2136 break; 2137 } 2138 } 2139 2140 void OpEmitter::genTraits() { 2141 // Add region size trait. 2142 unsigned numRegions = op.getNumRegions(); 2143 unsigned numVariadicRegions = op.getNumVariadicRegions(); 2144 addSizeCountTrait(opClass, "Region", numRegions, numVariadicRegions); 2145 2146 // Add result size traits. 2147 int numResults = op.getNumResults(); 2148 int numVariadicResults = op.getNumVariableLengthResults(); 2149 addSizeCountTrait(opClass, "Result", numResults, numVariadicResults); 2150 2151 // For single result ops with a known specific type, generate a OneTypedResult 2152 // trait. 2153 if (numResults == 1 && numVariadicResults == 0) { 2154 auto cppName = op.getResults().begin()->constraint.getCPPClassName(); 2155 opClass.addTrait("::mlir::OpTrait::OneTypedResult<" + cppName + ">::Impl"); 2156 } 2157 2158 // Add successor size trait. 2159 unsigned numSuccessors = op.getNumSuccessors(); 2160 unsigned numVariadicSuccessors = op.getNumVariadicSuccessors(); 2161 addSizeCountTrait(opClass, "Successor", numSuccessors, numVariadicSuccessors); 2162 2163 // Add variadic size trait and normal op traits. 2164 int numOperands = op.getNumOperands(); 2165 int numVariadicOperands = op.getNumVariableLengthOperands(); 2166 2167 // Add operand size trait. 2168 if (numVariadicOperands != 0) { 2169 if (numOperands == numVariadicOperands) 2170 opClass.addTrait("::mlir::OpTrait::VariadicOperands"); 2171 else 2172 opClass.addTrait("::mlir::OpTrait::AtLeastNOperands<" + 2173 Twine(numOperands - numVariadicOperands) + ">::Impl"); 2174 } else { 2175 switch (numOperands) { 2176 case 0: 2177 opClass.addTrait("::mlir::OpTrait::ZeroOperands"); 2178 break; 2179 case 1: 2180 opClass.addTrait("::mlir::OpTrait::OneOperand"); 2181 break; 2182 default: 2183 opClass.addTrait("::mlir::OpTrait::NOperands<" + Twine(numOperands) + 2184 ">::Impl"); 2185 break; 2186 } 2187 } 2188 2189 // Add the native and interface traits. 2190 for (const auto &trait : op.getTraits()) { 2191 if (auto opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) 2192 opClass.addTrait(opTrait->getFullyQualifiedTraitName()); 2193 else if (auto opTrait = dyn_cast<tblgen::InterfaceTrait>(&trait)) 2194 opClass.addTrait(opTrait->getFullyQualifiedTraitName()); 2195 } 2196 } 2197 2198 void OpEmitter::genOpNameGetter() { 2199 auto *method = opClass.addMethodAndPrune( 2200 "::llvm::StringLiteral", "getOperationName", 2201 OpMethod::Property(OpMethod::MP_Static | OpMethod::MP_Constexpr)); 2202 method->body() << " return ::llvm::StringLiteral(\"" << op.getOperationName() 2203 << "\");"; 2204 } 2205 2206 void OpEmitter::genOpAsmInterface() { 2207 // If the user only has one results or specifically added the Asm trait, 2208 // then don't generate it for them. We specifically only handle multi result 2209 // operations, because the name of a single result in the common case is not 2210 // interesting(generally 'result'/'output'/etc.). 2211 // TODO: We could also add a flag to allow operations to opt in to this 2212 // generation, even if they only have a single operation. 2213 int numResults = op.getNumResults(); 2214 if (numResults <= 1 || op.getTrait("::mlir::OpAsmOpInterface::Trait")) 2215 return; 2216 2217 SmallVector<StringRef, 4> resultNames(numResults); 2218 for (int i = 0; i != numResults; ++i) 2219 resultNames[i] = op.getResultName(i); 2220 2221 // Don't add the trait if none of the results have a valid name. 2222 if (llvm::all_of(resultNames, [](StringRef name) { return name.empty(); })) 2223 return; 2224 opClass.addTrait("::mlir::OpAsmOpInterface::Trait"); 2225 2226 // Generate the right accessor for the number of results. 2227 auto *method = opClass.addMethodAndPrune( 2228 "void", "getAsmResultNames", "::mlir::OpAsmSetValueNameFn", "setNameFn"); 2229 auto &body = method->body(); 2230 for (int i = 0; i != numResults; ++i) { 2231 body << " auto resultGroup" << i << " = getODSResults(" << i << ");\n" 2232 << " if (!llvm::empty(resultGroup" << i << "))\n" 2233 << " setNameFn(*resultGroup" << i << ".begin(), \"" 2234 << resultNames[i] << "\");\n"; 2235 } 2236 } 2237 2238 //===----------------------------------------------------------------------===// 2239 // OpOperandAdaptor emitter 2240 //===----------------------------------------------------------------------===// 2241 2242 namespace { 2243 // Helper class to emit Op operand adaptors to an output stream. Operand 2244 // adaptors are wrappers around ArrayRef<Value> that provide named operand 2245 // getters identical to those defined in the Op. 2246 class OpOperandAdaptorEmitter { 2247 public: 2248 static void emitDecl(const Operator &op, raw_ostream &os); 2249 static void emitDef(const Operator &op, raw_ostream &os); 2250 2251 private: 2252 explicit OpOperandAdaptorEmitter(const Operator &op); 2253 2254 // Add verification function. This generates a verify method for the adaptor 2255 // which verifies all the op-independent attribute constraints. 2256 void addVerification(); 2257 2258 const Operator &op; 2259 Class adaptor; 2260 }; 2261 } // end namespace 2262 2263 OpOperandAdaptorEmitter::OpOperandAdaptorEmitter(const Operator &op) 2264 : op(op), adaptor(op.getAdaptorName()) { 2265 adaptor.newField("::mlir::ValueRange", "odsOperands"); 2266 adaptor.newField("::mlir::DictionaryAttr", "odsAttrs"); 2267 adaptor.newField("::mlir::RegionRange", "odsRegions"); 2268 const auto *attrSizedOperands = 2269 op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments"); 2270 { 2271 SmallVector<OpMethodParameter, 2> paramList; 2272 paramList.emplace_back("::mlir::ValueRange", "values"); 2273 paramList.emplace_back("::mlir::DictionaryAttr", "attrs", 2274 attrSizedOperands ? "" : "nullptr"); 2275 paramList.emplace_back("::mlir::RegionRange", "regions", "{}"); 2276 auto *constructor = adaptor.addConstructorAndPrune(std::move(paramList)); 2277 2278 constructor->addMemberInitializer("odsOperands", "values"); 2279 constructor->addMemberInitializer("odsAttrs", "attrs"); 2280 constructor->addMemberInitializer("odsRegions", "regions"); 2281 } 2282 2283 { 2284 auto *constructor = adaptor.addConstructorAndPrune( 2285 llvm::formatv("{0}&", op.getCppClassName()).str(), "op"); 2286 constructor->addMemberInitializer("odsOperands", "op->getOperands()"); 2287 constructor->addMemberInitializer("odsAttrs", "op->getAttrDictionary()"); 2288 constructor->addMemberInitializer("odsRegions", "op->getRegions()"); 2289 } 2290 2291 { 2292 auto *m = adaptor.addMethodAndPrune("::mlir::ValueRange", "getOperands"); 2293 m->body() << " return odsOperands;"; 2294 } 2295 std::string sizeAttrInit = 2296 formatv(adapterSegmentSizeAttrInitCode, "operand_segment_sizes"); 2297 generateNamedOperandGetters(op, adaptor, sizeAttrInit, 2298 /*rangeType=*/"::mlir::ValueRange", 2299 /*rangeBeginCall=*/"odsOperands.begin()", 2300 /*rangeSizeCall=*/"odsOperands.size()", 2301 /*getOperandCallPattern=*/"odsOperands[{0}]"); 2302 2303 FmtContext fctx; 2304 fctx.withBuilder("::mlir::Builder(odsAttrs.getContext())"); 2305 2306 auto emitAttr = [&](StringRef name, Attribute attr) { 2307 auto &body = adaptor.addMethodAndPrune(attr.getStorageType(), name)->body(); 2308 body << " assert(odsAttrs && \"no attributes when constructing adapter\");" 2309 << "\n " << attr.getStorageType() << " attr = " 2310 << "odsAttrs.get(\"" << name << "\")."; 2311 if (attr.hasDefaultValue() || attr.isOptional()) 2312 body << "dyn_cast_or_null<"; 2313 else 2314 body << "cast<"; 2315 body << attr.getStorageType() << ">();\n"; 2316 2317 if (attr.hasDefaultValue()) { 2318 // Use the default value if attribute is not set. 2319 // TODO: this is inefficient, we are recreating the attribute for every 2320 // call. This should be set instead. 2321 std::string defaultValue = std::string( 2322 tgfmt(attr.getConstBuilderTemplate(), &fctx, attr.getDefaultValue())); 2323 body << " if (!attr)\n attr = " << defaultValue << ";\n"; 2324 } 2325 body << " return attr;\n"; 2326 }; 2327 2328 { 2329 auto *m = 2330 adaptor.addMethodAndPrune("::mlir::DictionaryAttr", "getAttributes"); 2331 m->body() << " return odsAttrs;"; 2332 } 2333 for (auto &namedAttr : op.getAttributes()) { 2334 const auto &name = namedAttr.name; 2335 const auto &attr = namedAttr.attr; 2336 if (!attr.isDerivedAttr()) 2337 emitAttr(name, attr); 2338 } 2339 2340 unsigned numRegions = op.getNumRegions(); 2341 if (numRegions > 0) { 2342 auto *m = adaptor.addMethodAndPrune("::mlir::RegionRange", "getRegions"); 2343 m->body() << " return odsRegions;"; 2344 } 2345 for (unsigned i = 0; i < numRegions; ++i) { 2346 const auto ®ion = op.getRegion(i); 2347 if (region.name.empty()) 2348 continue; 2349 2350 // Generate the accessors for a variadic region. 2351 if (region.isVariadic()) { 2352 auto *m = adaptor.addMethodAndPrune("::mlir::RegionRange", region.name); 2353 m->body() << formatv(" return odsRegions.drop_front({0});", i); 2354 continue; 2355 } 2356 2357 auto *m = adaptor.addMethodAndPrune("::mlir::Region &", region.name); 2358 m->body() << formatv(" return *odsRegions[{0}];", i); 2359 } 2360 2361 // Add verification function. 2362 addVerification(); 2363 } 2364 2365 void OpOperandAdaptorEmitter::addVerification() { 2366 auto *method = adaptor.addMethodAndPrune("::mlir::LogicalResult", "verify", 2367 "::mlir::Location", "loc"); 2368 auto &body = method->body(); 2369 2370 const char *checkAttrSizedValueSegmentsCode = R"( 2371 { 2372 auto sizeAttr = odsAttrs.get("{0}").cast<::mlir::DenseIntElementsAttr>(); 2373 auto numElements = sizeAttr.getType().cast<::mlir::ShapedType>().getNumElements(); 2374 if (numElements != {1}) 2375 return emitError(loc, "'{0}' attribute for specifying {2} segments " 2376 "must have {1} elements, but got ") << numElements; 2377 } 2378 )"; 2379 2380 // Verify a few traits first so that we can use 2381 // getODSOperands()/getODSResults() in the rest of the verifier. 2382 for (auto &trait : op.getTraits()) { 2383 if (auto *t = dyn_cast<tblgen::NativeTrait>(&trait)) { 2384 if (t->getFullyQualifiedTraitName() == 2385 "::mlir::OpTrait::AttrSizedOperandSegments") { 2386 body << formatv(checkAttrSizedValueSegmentsCode, 2387 "operand_segment_sizes", op.getNumOperands(), 2388 "operand"); 2389 } else if (t->getFullyQualifiedTraitName() == 2390 "::mlir::OpTrait::AttrSizedResultSegments") { 2391 body << formatv(checkAttrSizedValueSegmentsCode, "result_segment_sizes", 2392 op.getNumResults(), "result"); 2393 } 2394 } 2395 } 2396 2397 FmtContext verifyCtx; 2398 populateSubstitutions(op, "odsAttrs.get", "getODSOperands", 2399 "<no results should be generated>", verifyCtx); 2400 genAttributeVerifier(op, "odsAttrs.get", 2401 Twine("emitError(loc, \"'") + op.getOperationName() + 2402 "' op \"", 2403 /*emitVerificationRequiringOp*/ false, verifyCtx, body); 2404 2405 body << " return ::mlir::success();"; 2406 } 2407 2408 void OpOperandAdaptorEmitter::emitDecl(const Operator &op, raw_ostream &os) { 2409 OpOperandAdaptorEmitter(op).adaptor.writeDeclTo(os); 2410 } 2411 2412 void OpOperandAdaptorEmitter::emitDef(const Operator &op, raw_ostream &os) { 2413 OpOperandAdaptorEmitter(op).adaptor.writeDefTo(os); 2414 } 2415 2416 // Emits the opcode enum and op classes. 2417 static void emitOpClasses(const RecordKeeper &recordKeeper, 2418 const std::vector<Record *> &defs, raw_ostream &os, 2419 bool emitDecl) { 2420 // First emit forward declaration for each class, this allows them to refer 2421 // to each others in traits for example. 2422 if (emitDecl) { 2423 os << "#if defined(GET_OP_CLASSES) || defined(GET_OP_FWD_DEFINES)\n"; 2424 os << "#undef GET_OP_FWD_DEFINES\n"; 2425 for (auto *def : defs) { 2426 Operator op(*def); 2427 NamespaceEmitter emitter(os, op.getCppNamespace()); 2428 os << "class " << op.getCppClassName() << ";\n"; 2429 } 2430 os << "#endif\n\n"; 2431 } 2432 2433 IfDefScope scope("GET_OP_CLASSES", os); 2434 if (defs.empty()) 2435 return; 2436 2437 // Generate all of the locally instantiated methods first. 2438 StaticVerifierFunctionEmitter staticVerifierEmitter(recordKeeper, defs, os, 2439 emitDecl); 2440 for (auto *def : defs) { 2441 Operator op(*def); 2442 NamespaceEmitter emitter(os, op.getCppNamespace()); 2443 if (emitDecl) { 2444 os << formatv(opCommentHeader, op.getQualCppClassName(), "declarations"); 2445 OpOperandAdaptorEmitter::emitDecl(op, os); 2446 OpEmitter::emitDecl(op, os, staticVerifierEmitter); 2447 } else { 2448 os << formatv(opCommentHeader, op.getQualCppClassName(), "definitions"); 2449 OpOperandAdaptorEmitter::emitDef(op, os); 2450 OpEmitter::emitDef(op, os, staticVerifierEmitter); 2451 } 2452 } 2453 } 2454 2455 // Emits a comma-separated list of the ops. 2456 static void emitOpList(const std::vector<Record *> &defs, raw_ostream &os) { 2457 IfDefScope scope("GET_OP_LIST", os); 2458 2459 interleave( 2460 // TODO: We are constructing the Operator wrapper instance just for 2461 // getting it's qualified class name here. Reduce the overhead by having a 2462 // lightweight version of Operator class just for that purpose. 2463 defs, [&os](Record *def) { os << Operator(def).getQualCppClassName(); }, 2464 [&os]() { os << ",\n"; }); 2465 } 2466 2467 static bool emitOpDecls(const RecordKeeper &recordKeeper, raw_ostream &os) { 2468 emitSourceFileHeader("Op Declarations", os); 2469 2470 std::vector<Record *> defs = getRequestedOpDefinitions(recordKeeper); 2471 emitOpClasses(recordKeeper, defs, os, /*emitDecl=*/true); 2472 2473 return false; 2474 } 2475 2476 static bool emitOpDefs(const RecordKeeper &recordKeeper, raw_ostream &os) { 2477 emitSourceFileHeader("Op Definitions", os); 2478 2479 std::vector<Record *> defs = getRequestedOpDefinitions(recordKeeper); 2480 emitOpList(defs, os); 2481 emitOpClasses(recordKeeper, defs, os, /*emitDecl=*/false); 2482 2483 return false; 2484 } 2485 2486 static mlir::GenRegistration 2487 genOpDecls("gen-op-decls", "Generate op declarations", 2488 [](const RecordKeeper &records, raw_ostream &os) { 2489 return emitOpDecls(records, os); 2490 }); 2491 2492 static mlir::GenRegistration genOpDefs("gen-op-defs", "Generate op definitions", 2493 [](const RecordKeeper &records, 2494 raw_ostream &os) { 2495 return emitOpDefs(records, os); 2496 }); 2497