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