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