5032 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// OpDefinitionsGen uses the description of operations to generate C++10// definitions for ops.11//12//===----------------------------------------------------------------------===//13 14#include "CppGenUtilities.h"15#include "OpClass.h"16#include "OpFormatGen.h"17#include "OpGenHelpers.h"18#include "mlir/TableGen/Argument.h"19#include "mlir/TableGen/Attribute.h"20#include "mlir/TableGen/Builder.h"21#include "mlir/TableGen/Class.h"22#include "mlir/TableGen/CodeGenHelpers.h"23#include "mlir/TableGen/Format.h"24#include "mlir/TableGen/GenInfo.h"25#include "mlir/TableGen/Interfaces.h"26#include "mlir/TableGen/Operator.h"27#include "mlir/TableGen/Property.h"28#include "mlir/TableGen/Region.h"29#include "mlir/TableGen/SideEffects.h"30#include "mlir/TableGen/Successor.h"31#include "mlir/TableGen/Trait.h"32#include "llvm/ADT/BitVector.h"33#include "llvm/ADT/MapVector.h"34#include "llvm/ADT/PointerUnion.h"35#include "llvm/ADT/STLExtras.h"36#include "llvm/ADT/Sequence.h"37#include "llvm/ADT/SmallVector.h"38#include "llvm/ADT/StringExtras.h"39#include "llvm/ADT/StringMap.h"40#include "llvm/ADT/StringRef.h"41#include "llvm/ADT/StringSet.h"42#include "llvm/Support/Casting.h"43#include "llvm/Support/Debug.h"44#include "llvm/Support/ErrorHandling.h"45#include "llvm/Support/FormatVariadic.h"46#include "llvm/Support/Signals.h"47#include "llvm/Support/raw_ostream.h"48#include "llvm/TableGen/CodeGenHelpers.h"49#include "llvm/TableGen/Error.h"50#include "llvm/TableGen/Record.h"51#include "llvm/TableGen/TableGenBackend.h"52 53#define DEBUG_TYPE "mlir-tblgen-opdefgen"54 55using namespace llvm;56using namespace mlir;57using namespace mlir::tblgen;58 59static const char *const tblgenNamePrefix = "tblgen_";60static const char *const generatedArgName = "odsArg";61static const char *const odsBuilder = "odsBuilder";62static const char *const builderOpState = "odsState";63static const char *const propertyStorage = "propStorage";64static const char *const propertyValue = "propValue";65static const char *const propertyAttr = "propAttr";66static const char *const propertyDiag = "emitError";67 68/// The names of the implicit attributes that contain variadic operand and69/// result segment sizes.70static const char *const operandSegmentAttrName = "operandSegmentSizes";71static const char *const resultSegmentAttrName = "resultSegmentSizes";72 73/// Code for an Op to lookup an attribute. Uses cached identifiers and subrange74/// lookup.75///76/// {0}: Code snippet to get the attribute's name or identifier.77/// {1}: The lower bound on the sorted subrange.78/// {2}: The upper bound on the sorted subrange.79/// {3}: Code snippet to get the array of named attributes.80/// {4}: "Named" to get the named attribute.81static const char *const subrangeGetAttr =82 "::mlir::impl::get{4}AttrFromSortedRange({3}.begin() + {1}, {3}.end() - "83 "{2}, {0})";84 85/// The logic to calculate the actual value range for a declared operand/result86/// of an op with variadic operands/results. Note that this logic is not for87/// general use; it assumes all variadic operands/results must have the same88/// number of values.89///90/// {0}: The list of whether each declared operand/result is variadic.91/// {1}: The total number of non-variadic operands/results.92/// {2}: The total number of variadic operands/results.93/// {3}: The total number of actual values.94/// {4}: "operand" or "result".95static const char *const sameVariadicSizeValueRangeCalcCode = R"(96 bool isVariadic[] = {{{0}};97 int prevVariadicCount = 0;98 for (unsigned i = 0; i < index; ++i)99 if (isVariadic[i]) ++prevVariadicCount;100 101 // Calculate how many dynamic values a static variadic {4} corresponds to.102 // This assumes all static variadic {4}s have the same dynamic value count.103 int variadicSize = ({3} - {1}) / {2};104 // `index` passed in as the parameter is the static index which counts each105 // {4} (variadic or not) as size 1. So here for each previous static variadic106 // {4}, we need to offset by (variadicSize - 1) to get where the dynamic107 // value pack for this static {4} starts.108 int start = index + (variadicSize - 1) * prevVariadicCount;109 int size = isVariadic[index] ? variadicSize : 1;110 return {{start, size};111)";112 113/// The logic to calculate the actual value range for a declared operand/result114/// of an op with variadic operands/results. Note that this logic is assumes115/// the op has an attribute specifying the size of each operand/result segment116/// (variadic or not).117static const char *const attrSizedSegmentValueRangeCalcCode = R"(118 unsigned start = 0;119 for (unsigned i = 0; i < index; ++i)120 start += sizeAttr[i];121 return {start, sizeAttr[index]};122)";123/// The code snippet to initialize the sizes for the value range calculation.124///125/// {0}: The code to get the attribute.126static const char *const adapterSegmentSizeAttrInitCode = R"(127 assert({0} && "missing segment size attribute for op");128 auto sizeAttr = ::llvm::cast<::mlir::DenseI32ArrayAttr>({0});129)";130static const char *const adapterSegmentSizeAttrInitCodeProperties = R"(131 ::llvm::ArrayRef<int32_t> sizeAttr = {0};132)";133 134/// The code snippet to initialize the sizes for the value range calculation.135///136/// {0}: The code to get the attribute.137static const char *const opSegmentSizeAttrInitCode = R"(138 auto sizeAttr = ::llvm::cast<::mlir::DenseI32ArrayAttr>({0});139)";140 141/// The logic to calculate the actual value range for a declared operand142/// of an op with variadic of variadic operands within the OpAdaptor.143///144/// {0}: The name of the segment attribute.145/// {1}: The index of the main operand.146/// {2}: The range type of adaptor.147static const char *const variadicOfVariadicAdaptorCalcCode = R"(148 auto tblgenTmpOperands = getODSOperands({1});149 auto sizes = {0}();150 151 ::llvm::SmallVector<{2}> tblgenTmpOperandGroups;152 for (int i = 0, e = sizes.size(); i < e; ++i) {{153 tblgenTmpOperandGroups.push_back(tblgenTmpOperands.take_front(sizes[i]));154 tblgenTmpOperands = tblgenTmpOperands.drop_front(sizes[i]);155 }156 return tblgenTmpOperandGroups;157)";158 159/// The logic to build a range of either operand or result values.160///161/// {0}: The begin iterator of the actual values.162/// {1}: The call to generate the start and length of the value range.163static const char *const valueRangeReturnCode = R"(164 auto valueRange = {1};165 return {{std::next({0}, valueRange.first),166 std::next({0}, valueRange.first + valueRange.second)};167)";168 169/// Parse operand/result segment_size property.170/// {0}: Number of elements in the segment array171static const char *const parseTextualSegmentSizeFormat = R"(172 size_t i = 0;173 auto parseElem = [&]() -> ::mlir::ParseResult {174 if (i >= {0})175 return $_parser.emitError($_parser.getCurrentLocation(),176 "expected `]` after {0} segment sizes");177 if (failed($_parser.parseInteger($_storage[i])))178 return ::mlir::failure();179 i += 1;180 return ::mlir::success();181 };182 if (failed($_parser.parseCommaSeparatedList(183 ::mlir::AsmParser::Delimeter::Square, parseElem)))184 return failure();185 if (i < {0})186 return $_parser.emitError($_parser.getCurrentLocation(),187 "expected {0} segment sizes, found only ") << i;188 return success();189)";190 191static const char *const printTextualSegmentSize = R"(192 [&]() {193 $_printer << '[';194 ::llvm::interleaveComma($_storage, $_printer);195 $_printer << ']';196 }()197)";198 199/// Read operand/result segment_size from bytecode.200static const char *const readBytecodeSegmentSizeNative = R"(201 if ($_reader.getBytecodeVersion() >= /*kNativePropertiesODSSegmentSize=*/6)202 return $_reader.readSparseArray(::llvm::MutableArrayRef($_storage));203)";204 205static const char *const readBytecodeSegmentSizeLegacy = R"(206 if ($_reader.getBytecodeVersion() < /*kNativePropertiesODSSegmentSize=*/6) {207 auto &$_storage = prop.$_propName;208 ::mlir::DenseI32ArrayAttr attr;209 if (::mlir::failed($_reader.readAttribute(attr))) return ::mlir::failure();210 if (attr.size() > static_cast<int64_t>(sizeof($_storage) / sizeof(int32_t))) {211 $_reader.emitError("size mismatch for operand/result_segment_size");212 return ::mlir::failure();213 }214 ::llvm::copy(::llvm::ArrayRef<int32_t>(attr), $_storage.begin());215 }216)";217 218/// Write operand/result segment_size to bytecode.219static const char *const writeBytecodeSegmentSizeNative = R"(220 if ($_writer.getBytecodeVersion() >= /*kNativePropertiesODSSegmentSize=*/6)221 $_writer.writeSparseArray(::llvm::ArrayRef($_storage));222)";223 224/// Write operand/result segment_size to bytecode.225static const char *const writeBytecodeSegmentSizeLegacy = R"(226if ($_writer.getBytecodeVersion() < /*kNativePropertiesODSSegmentSize=*/6) {227 auto &$_storage = prop.$_propName;228 $_writer.writeAttribute(::mlir::DenseI32ArrayAttr::get($_ctxt, $_storage));229}230)";231 232/// A header for indicating code sections.233///234/// {0}: Some text, or a class name.235/// {1}: Some text.236static const char *const opCommentHeader = R"(237//===----------------------------------------------------------------------===//238// {0} {1}239//===----------------------------------------------------------------------===//240 241)";242 243static const char *const inlineCreateBody = R"(244 ::mlir::OperationState __state__({0}, getOperationName());245 build(builder, __state__{1});246 auto __res__ = ::llvm::dyn_cast<{2}>(builder.create(__state__));247 assert(__res__ && "builder didn't return the right type");248 return __res__;249)";250 251static const char *const inlineCreateBodyImplicitLoc = R"(252 return create(builder, builder.getLoc(){0});253)";254 255//===----------------------------------------------------------------------===//256// Utility structs and functions257//===----------------------------------------------------------------------===//258 259// Replaces all occurrences of `match` in `str` with `substitute`.260static std::string replaceAllSubstrs(std::string str, const std::string &match,261 const std::string &substitute) {262 std::string::size_type scanLoc = 0, matchLoc = std::string::npos;263 while ((matchLoc = str.find(match, scanLoc)) != std::string::npos) {264 str = str.replace(matchLoc, match.size(), substitute);265 scanLoc = matchLoc + substitute.size();266 }267 return str;268}269 270// Returns whether the record has a value of the given name that can be returned271// via getValueAsString.272static inline bool hasStringAttribute(const Record &record,273 StringRef fieldName) {274 auto *valueInit = record.getValueInit(fieldName);275 return isa<StringInit>(valueInit);276}277 278static std::string getArgumentName(const Operator &op, int index) {279 const auto &operand = op.getOperand(index);280 if (!operand.name.empty())281 return std::string(operand.name);282 return std::string(formatv("{0}_{1}", generatedArgName, index));283}284 285// Returns true if we can use unwrapped value for the given `attr` in builders.286static bool canUseUnwrappedRawValue(const tblgen::Attribute &attr) {287 return attr.getReturnType() != attr.getStorageType() &&288 // We need to wrap the raw value into an attribute in the builder impl289 // so we need to make sure that the attribute specifies how to do that.290 !attr.getConstBuilderTemplate().empty();291}292 293/// Build an attribute from a parameter value using the constant builder.294static std::string constBuildAttrFromParam(const tblgen::Attribute &attr,295 FmtContext &fctx,296 StringRef paramName) {297 std::string builderTemplate = attr.getConstBuilderTemplate().str();298 299 // For StringAttr, its constant builder call will wrap the input in300 // quotes, which is correct for normal string literals, but incorrect301 // here given we use function arguments. So we need to strip the302 // wrapping quotes.303 if (StringRef(builderTemplate).contains("\"$0\""))304 builderTemplate = replaceAllSubstrs(builderTemplate, "\"$0\"", "$0");305 306 return tgfmt(builderTemplate, &fctx, paramName).str();307}308 309namespace {310/// Metadata on a registered attribute. Given that attributes are stored in311/// sorted order on operations, we can use information from ODS to deduce the312/// number of required attributes less and and greater than each attribute,313/// allowing us to search only a subrange of the attributes in ODS-generated314/// getters.315struct AttributeMetadata {316 /// The attribute name.317 StringRef attrName;318 /// Whether the attribute is required.319 bool isRequired;320 /// The ODS attribute constraint. Not present for implicit attributes.321 std::optional<Attribute> constraint;322 /// The number of required attributes less than this attribute.323 unsigned lowerBound = 0;324 /// The number of required attributes greater than this attribute.325 unsigned upperBound = 0;326};327 328/// Helper class to select between OpAdaptor and Op code templates.329class OpOrAdaptorHelper {330public:331 OpOrAdaptorHelper(const Operator &op, bool emitForOp)332 : op(op), emitForOp(emitForOp) {333 computeAttrMetadata();334 }335 336 /// Object that wraps a functor in a stream operator for interop with337 /// llvm::formatv.338 class Formatter {339 public:340 template <typename Functor>341 Formatter(Functor &&func) : func(std::forward<Functor>(func)) {}342 343 std::string str() const {344 std::string result;345 llvm::raw_string_ostream os(result);346 os << *this;347 return os.str();348 }349 350 private:351 std::function<raw_ostream &(raw_ostream &)> func;352 353 friend raw_ostream &operator<<(raw_ostream &os, const Formatter &fmt) {354 return fmt.func(os);355 }356 };357 358 // Generate code for getting an attribute.359 Formatter getAttr(StringRef attrName, bool isNamed = false) const {360 assert(attrMetadata.count(attrName) && "expected attribute metadata");361 return [this, attrName, isNamed](raw_ostream &os) -> raw_ostream & {362 const AttributeMetadata &attr = attrMetadata.find(attrName)->second;363 if (hasProperties()) {364 assert(!isNamed);365 return os << "getProperties()." << attrName;366 }367 return os << formatv(subrangeGetAttr, getAttrName(attrName),368 attr.lowerBound, attr.upperBound, getAttrRange(),369 isNamed ? "Named" : "");370 };371 }372 373 // Generate code for getting the name of an attribute.374 Formatter getAttrName(StringRef attrName) const {375 return [this, attrName](raw_ostream &os) -> raw_ostream & {376 if (emitForOp)377 return os << op.getGetterName(attrName) << "AttrName()";378 return os << formatv("{0}::{1}AttrName(*odsOpName)", op.getCppClassName(),379 op.getGetterName(attrName));380 };381 }382 383 // Get the code snippet for getting the named attribute range.384 StringRef getAttrRange() const {385 return emitForOp ? "(*this)->getAttrs()" : "odsAttrs";386 }387 388 // Get the prefix code for emitting an error.389 Formatter emitErrorPrefix() const {390 return [this](raw_ostream &os) -> raw_ostream & {391 if (emitForOp)392 return os << "emitOpError(\"";393 return os << formatv("emitError(loc, \"'{0}' op ", op.getOperationName());394 };395 }396 397 // Get the call to get an operand or segment of operands.398 Formatter getOperand(unsigned index) const {399 return [this, index](raw_ostream &os) -> raw_ostream & {400 return os << formatv(op.getOperand(index).isVariadic()401 ? "this->getODSOperands({0})"402 : "(*this->getODSOperands({0}).begin())",403 index);404 };405 }406 407 // Get the call to get a result of segment of results.408 Formatter getResult(unsigned index) const {409 return [this, index](raw_ostream &os) -> raw_ostream & {410 if (!emitForOp)411 return os << "<no results should be generated>";412 return os << formatv(op.getResult(index).isVariadic()413 ? "this->getODSResults({0})"414 : "(*this->getODSResults({0}).begin())",415 index);416 };417 }418 419 // Return whether an op instance is available.420 bool isEmittingForOp() const { return emitForOp; }421 422 // Return the ODS operation wrapper.423 const Operator &getOp() const { return op; }424 425 // Get the attribute metadata sorted by name.426 const llvm::MapVector<StringRef, AttributeMetadata> &getAttrMetadata() const {427 return attrMetadata;428 }429 430 /// Returns whether to emit a `Properties` struct for this operation or not.431 bool hasProperties() const {432 if (!op.getProperties().empty())433 return true;434 if (!op.getDialect().usePropertiesForAttributes())435 return false;436 return true;437 }438 439 /// Returns whether the operation will have a non-empty `Properties` struct.440 bool hasNonEmptyPropertiesStruct() const {441 if (!op.getProperties().empty())442 return true;443 if (!hasProperties())444 return false;445 if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments") ||446 op.getTrait("::mlir::OpTrait::AttrSizedResultSegments"))447 return true;448 return llvm::any_of(getAttrMetadata(),449 [](const std::pair<StringRef, AttributeMetadata> &it) {450 return !it.second.constraint ||451 !it.second.constraint->isDerivedAttr();452 });453 }454 455 std::optional<NamedProperty> &getOperandSegmentsSize() {456 return operandSegmentsSize;457 }458 459 std::optional<NamedProperty> &getResultSegmentsSize() {460 return resultSegmentsSize;461 }462 463 uint32_t getOperandSegmentSizesLegacyIndex() {464 return operandSegmentSizesLegacyIndex;465 }466 467 uint32_t getResultSegmentSizesLegacyIndex() {468 return resultSegmentSizesLegacyIndex;469 }470 471private:472 // Compute the attribute metadata.473 void computeAttrMetadata();474 475 // The operation ODS wrapper.476 const Operator &op;477 // True if code is being generate for an op. False for an adaptor.478 const bool emitForOp;479 480 // The attribute metadata, mapped by name.481 llvm::MapVector<StringRef, AttributeMetadata> attrMetadata;482 483 // Property484 std::optional<NamedProperty> operandSegmentsSize;485 std::string operandSegmentsSizeStorage;486 std::string operandSegmentsSizeParser;487 std::optional<NamedProperty> resultSegmentsSize;488 std::string resultSegmentsSizeStorage;489 std::string resultSegmentsSizeParser;490 491 // Indices to store the position in the emission order of the operand/result492 // segment sizes attribute if emitted as part of the properties for legacy493 // bytecode encodings, i.e. versions less than 6.494 uint32_t operandSegmentSizesLegacyIndex = 0;495 uint32_t resultSegmentSizesLegacyIndex = 0;496 497 // The number of required attributes.498 unsigned numRequired;499};500 501} // namespace502 503void OpOrAdaptorHelper::computeAttrMetadata() {504 // Enumerate the attribute names of this op, ensuring the attribute names are505 // unique in case implicit attributes are explicitly registered.506 for (const NamedAttribute &namedAttr : op.getAttributes()) {507 Attribute attr = namedAttr.attr;508 bool isOptional =509 attr.hasDefaultValue() || attr.isOptional() || attr.isDerivedAttr();510 attrMetadata.insert(511 {namedAttr.name, AttributeMetadata{namedAttr.name, !isOptional, attr}});512 }513 514 auto makeProperty = [&](StringRef storageType, StringRef parserCall) {515 return Property(/*maybeDef=*/nullptr,516 /*summary=*/"",517 /*description=*/"",518 /*storageType=*/storageType,519 /*interfaceType=*/"::llvm::ArrayRef<int32_t>",520 /*convertFromStorageCall=*/"$_storage",521 /*assignToStorageCall=*/522 "::llvm::copy($_value, $_storage.begin())",523 /*convertToAttributeCall=*/524 "return ::mlir::DenseI32ArrayAttr::get($_ctxt, $_storage);",525 /*convertFromAttributeCall=*/526 "return convertFromAttribute($_storage, $_attr, $_diag);",527 /*parserCall=*/parserCall,528 /*optionalParserCall=*/"",529 /*printerCall=*/printTextualSegmentSize,530 /*readFromMlirBytecodeCall=*/readBytecodeSegmentSizeNative,531 /*writeToMlirBytecodeCall=*/writeBytecodeSegmentSizeNative,532 /*hashPropertyCall=*/533 "::llvm::hash_combine_range(std::begin($_storage), "534 "std::end($_storage));",535 /*StringRef defaultValue=*/"",536 /*storageTypeValueOverride=*/"");537 };538 // Include key attributes from several traits as implicitly registered.539 if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {540 if (op.getDialect().usePropertiesForAttributes()) {541 operandSegmentsSizeStorage =542 llvm::formatv("std::array<int32_t, {0}>", op.getNumOperands());543 operandSegmentsSizeParser =544 llvm::formatv(parseTextualSegmentSizeFormat, op.getNumOperands());545 operandSegmentsSize = {546 "operandSegmentSizes",547 makeProperty(operandSegmentsSizeStorage, operandSegmentsSizeParser)};548 } else {549 attrMetadata.insert(550 {operandSegmentAttrName, AttributeMetadata{operandSegmentAttrName,551 /*isRequired=*/true,552 /*attr=*/std::nullopt}});553 }554 }555 if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {556 if (op.getDialect().usePropertiesForAttributes()) {557 resultSegmentsSizeStorage =558 llvm::formatv("std::array<int32_t, {0}>", op.getNumResults());559 resultSegmentsSizeParser =560 llvm::formatv(parseTextualSegmentSizeFormat, op.getNumResults());561 resultSegmentsSize = {562 "resultSegmentSizes",563 makeProperty(resultSegmentsSizeStorage, resultSegmentsSizeParser)};564 } else {565 attrMetadata.insert(566 {resultSegmentAttrName,567 AttributeMetadata{resultSegmentAttrName, /*isRequired=*/true,568 /*attr=*/std::nullopt}});569 }570 }571 572 // Store the metadata in sorted order.573 SmallVector<AttributeMetadata> sortedAttrMetadata =574 llvm::to_vector(llvm::make_second_range(attrMetadata.takeVector()));575 llvm::sort(sortedAttrMetadata,576 [](const AttributeMetadata &lhs, const AttributeMetadata &rhs) {577 return lhs.attrName < rhs.attrName;578 });579 580 // Store the position of the legacy operand_segment_sizes /581 // result_segment_sizes so we can emit a backward compatible property readers582 // and writers.583 StringRef legacyOperandSegmentSizeName =584 StringLiteral("operand_segment_sizes");585 StringRef legacyResultSegmentSizeName = StringLiteral("result_segment_sizes");586 operandSegmentSizesLegacyIndex = 0;587 resultSegmentSizesLegacyIndex = 0;588 for (auto item : sortedAttrMetadata) {589 if (item.attrName < legacyOperandSegmentSizeName)590 ++operandSegmentSizesLegacyIndex;591 if (item.attrName < legacyResultSegmentSizeName)592 ++resultSegmentSizesLegacyIndex;593 }594 595 // Compute the subrange bounds for each attribute.596 numRequired = 0;597 for (AttributeMetadata &attr : sortedAttrMetadata) {598 attr.lowerBound = numRequired;599 numRequired += attr.isRequired;600 };601 for (AttributeMetadata &attr : sortedAttrMetadata)602 attr.upperBound = numRequired - attr.lowerBound - attr.isRequired;603 604 // Store the results back into the map.605 for (const AttributeMetadata &attr : sortedAttrMetadata)606 attrMetadata.insert({attr.attrName, attr});607}608 609//===----------------------------------------------------------------------===//610// Op emitter611//===----------------------------------------------------------------------===//612 613namespace {614// Helper class to emit a record into the given output stream.615class OpEmitter {616 using ConstArgument =617 llvm::PointerUnion<const AttributeMetadata *, const NamedProperty *>;618 619public:620 static void621 emitDecl(const Operator &op, raw_ostream &os,622 const StaticVerifierFunctionEmitter &staticVerifierEmitter);623 static void624 emitDef(const Operator &op, raw_ostream &os,625 const StaticVerifierFunctionEmitter &staticVerifierEmitter);626 627private:628 OpEmitter(const Operator &op,629 const StaticVerifierFunctionEmitter &staticVerifierEmitter);630 631 void emitDecl(raw_ostream &os);632 void emitDef(raw_ostream &os);633 634 // Generate methods for accessing the attribute names of this operation.635 void genAttrNameGetters();636 637 // Generates the OpAsmOpInterface for this operation if possible.638 void genOpAsmInterface();639 640 // Generates the `getOperationName` method for this op.641 void genOpNameGetter();642 643 // Generates code to manage the properties, if any!644 void genPropertiesSupport();645 646 // Generates code to manage the encoding of properties to bytecode.647 void648 genPropertiesSupportForBytecode(ArrayRef<ConstArgument> attrOrProperties);649 650 // Generates getters for the properties.651 void genPropGetters();652 653 // Generates seters for the properties.654 void genPropSetters();655 656 // Generates getters for the attributes.657 void genAttrGetters();658 659 // Generates setter for the attributes.660 void genAttrSetters();661 662 // Generates removers for optional attributes.663 void genOptionalAttrRemovers();664 665 // Generates getters for named operands.666 void genNamedOperandGetters();667 668 // Generates setters for named operands.669 void genNamedOperandSetters();670 671 // Generates getters for named results.672 void genNamedResultGetters();673 674 // Generates getters for named regions.675 void genNamedRegionGetters();676 677 // Generates getters for named successors.678 void genNamedSuccessorGetters();679 680 // Generates the method to populate default attributes.681 void genPopulateDefaultAttributes();682 683 // Generates builder methods for the operation.684 void genBuilder();685 686 // Generates the build() method that takes each operand/attribute687 // as a stand-alone parameter.688 void genSeparateArgParamBuilder();689 void genInlineCreateBody(const SmallVector<MethodParameter> ¶mList);690 691 // Generates the build() method that takes each operand/attribute as a692 // stand-alone parameter. The generated build() method uses first operand's693 // type as all results' types.694 void genUseOperandAsResultTypeSeparateParamBuilder();695 696 // The kind of collective builder to generate697 enum class CollectiveBuilderKind {698 PropStruct, // Inherent attributes/properties are passed by `const699 // Properties&`700 AttrDict, // Inherent attributes/properties are passed by attribute701 // dictionary702 };703 704 // Generates the build() method that takes all operands/attributes705 // collectively as one parameter. The generated build() method uses first706 // operand's type as all results' types.707 void708 genUseOperandAsResultTypeCollectiveParamBuilder(CollectiveBuilderKind kind);709 710 // Generates the build() method that takes aggregate operands/attributes711 // parameters. This build() method uses inferred types as result types.712 // Requires: The type needs to be inferable via InferTypeOpInterface.713 void genInferredTypeCollectiveParamBuilder(CollectiveBuilderKind kind);714 715 // Generates the build() method that takesaggregate operands/attributes as716 // parameters. The generated build() method uses first attribute's717 // type as all result's types.718 void genUseAttrAsResultTypeCollectiveParamBuilder(CollectiveBuilderKind kind);719 720 // Generates the build() method that takes all result types collectively as721 // one parameter. Similarly for operands and attributes.722 void genCollectiveParamBuilder(CollectiveBuilderKind kind);723 724 // The kind of parameter to generate for result types in builders.725 enum class TypeParamKind {726 None, // No result type in parameter list.727 Separate, // A separate parameter for each result type.728 Collective, // An ArrayRef<Type> for all result types.729 };730 731 // The kind of parameter to generate for attributes in builders.732 enum class AttrParamKind {733 WrappedAttr, // A wrapped MLIR Attribute instance.734 UnwrappedValue, // A raw value without MLIR Attribute wrapper.735 };736 737 // Builds the parameter list for build() method of this op. This method writes738 // to `paramList` the comma-separated parameter list and updates739 // `resultTypeNames` with the names for parameters for specifying result740 // types. `inferredAttributes` is populated with any attributes that are741 // elided from the build list. The given `typeParamKind` and `attrParamKind`742 // controls how result types and attributes are placed in the parameter list.743 void buildParamList(SmallVectorImpl<MethodParameter> ¶mList,744 llvm::StringSet<> &inferredAttributes,745 SmallVectorImpl<std::string> &resultTypeNames,746 TypeParamKind typeParamKind,747 AttrParamKind attrParamKind = AttrParamKind::WrappedAttr);748 749 // Adds op arguments and regions into operation state for build() methods.750 void751 genCodeForAddingArgAndRegionForBuilder(MethodBody &body,752 llvm::StringSet<> &inferredAttributes,753 bool isRawValueAttr = false);754 755 // Generates canonicalizer declaration for the operation.756 void genCanonicalizerDecls();757 758 // Generates the folder declaration for the operation.759 void genFolderDecls();760 761 // Generates the parser for the operation.762 void genParser();763 764 // Generates the printer for the operation.765 void genPrinter();766 767 // Generates verify method for the operation.768 void genVerifier();769 770 // Generates custom verify methods for the operation.771 void genCustomVerifier();772 773 // Generates verify statements for operands and results in the operation.774 // The generated code will be attached to `body`.775 void genOperandResultVerifier(MethodBody &body,776 Operator::const_value_range values,777 StringRef valueKind);778 779 // Generates verify statements for regions in the operation.780 // The generated code will be attached to `body`.781 void genRegionVerifier(MethodBody &body);782 783 // Generates verify statements for successors in the operation.784 // The generated code will be attached to `body`.785 void genSuccessorVerifier(MethodBody &body);786 787 // Generates the traits used by the object.788 void genTraits();789 790 // Generate the OpInterface methods for all interfaces.791 void genOpInterfaceMethods();792 793 // Generate op interface methods for the given interface.794 void genOpInterfaceMethods(const tblgen::InterfaceTrait *trait);795 796 // Generate op interface method for the given interface method. If797 // 'declaration' is true, generates a declaration, else a definition.798 Method *genOpInterfaceMethod(const tblgen::InterfaceMethod &method,799 bool declaration = true);800 801 // Generate a `using` declaration for the op interface method to include802 // the default implementation from the interface trait.803 // This is needed when the interface defines multiple methods with the same804 // name, but some have a default implementation and some don't.805 UsingDeclaration *806 genOpInterfaceMethodUsingDecl(const tblgen::InterfaceTrait *opTrait,807 const tblgen::InterfaceMethod &method);808 809 // Generate the side effect interface methods.810 void genSideEffectInterfaceMethods();811 812 // Generate the type inference interface methods.813 void genTypeInterfaceMethods();814 815private:816 // The TableGen record for this op.817 // TODO: OpEmitter should not have a Record directly,818 // it should rather go through the Operator for better abstraction.819 const Record &def;820 821 // The wrapper operator class for querying information from this op.822 const Operator &op;823 824 // The C++ code builder for this op825 OpClass opClass;826 827 // The format context for verification code generation.828 FmtContext verifyCtx;829 830 // The emitter containing all of the locally emitted verification functions.831 const StaticVerifierFunctionEmitter &staticVerifierEmitter;832 833 // Helper for emitting op code.834 OpOrAdaptorHelper emitHelper;835 836 // Keep track of the interface using declarations that have been generated to837 // avoid duplicates.838 llvm::StringSet<> interfaceUsingNames;839};840 841} // namespace842 843// Populate the format context `ctx` with substitutions of attributes, operands844// and results.845static void populateSubstitutions(const OpOrAdaptorHelper &emitHelper,846 FmtContext &ctx) {847 // Populate substitutions for attributes.848 auto &op = emitHelper.getOp();849 for (const auto &namedAttr : op.getAttributes())850 ctx.addSubst(namedAttr.name,851 emitHelper.getOp().getGetterName(namedAttr.name) + "()");852 853 // Populate substitutions for named operands.854 for (int i = 0, e = op.getNumOperands(); i < e; ++i) {855 auto &value = op.getOperand(i);856 if (!value.name.empty())857 ctx.addSubst(value.name, emitHelper.getOperand(i).str());858 }859 860 // Populate substitutions for results.861 for (int i = 0, e = op.getNumResults(); i < e; ++i) {862 auto &value = op.getResult(i);863 if (!value.name.empty())864 ctx.addSubst(value.name, emitHelper.getResult(i).str());865 }866}867 868/// Generate verification on native traits requiring attributes.869static void genNativeTraitAttrVerifier(MethodBody &body,870 const OpOrAdaptorHelper &emitHelper) {871 // Check that the variadic segment sizes attribute exists and contains the872 // expected number of elements.873 //874 // {0}: Attribute name.875 // {1}: Expected number of elements.876 // {2}: "operand" or "result".877 // {3}: Emit error prefix.878 const char *const checkAttrSizedValueSegmentsCode = R"(879 {880 auto sizeAttr = ::llvm::cast<::mlir::DenseI32ArrayAttr>(tblgen_{0});881 auto numElements = sizeAttr.asArrayRef().size();882 if (numElements != {1})883 return {3}"'{0}' attribute for specifying {2} segments must have {1} "884 "elements, but got ") << numElements;885 }886 )";887 888 // Verify a few traits first so that we can use getODSOperands() and889 // getODSResults() in the rest of the verifier.890 auto &op = emitHelper.getOp();891 if (!op.getDialect().usePropertiesForAttributes()) {892 if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {893 body << formatv(checkAttrSizedValueSegmentsCode, operandSegmentAttrName,894 op.getNumOperands(), "operand",895 emitHelper.emitErrorPrefix());896 }897 if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {898 body << formatv(checkAttrSizedValueSegmentsCode, resultSegmentAttrName,899 op.getNumResults(), "result",900 emitHelper.emitErrorPrefix());901 }902 }903}904 905// Return true if a verifier can be emitted for the attribute: it is not a906// derived attribute, it has a predicate, its condition is not empty, and, for907// adaptors, the condition does not reference the op.908static bool canEmitAttrVerifier(Attribute attr, bool isEmittingForOp) {909 if (attr.isDerivedAttr())910 return false;911 Pred pred = attr.getPredicate();912 if (pred.isNull())913 return false;914 std::string condition = pred.getCondition();915 return !condition.empty() &&916 (!StringRef(condition).contains("$_op") || isEmittingForOp);917}918 919// Generate attribute verification. If an op instance is not available, then920// attribute checks that require one will not be emitted.921//922// Attribute verification is performed as follows:923//924// 1. Verify that all required attributes are present in sorted order. This925// ensures that we can use subrange lookup even with potentially missing926// attributes.927// 2. Verify native trait attributes so that other attributes may call methods928// that depend on the validity of these attributes, e.g. segment size attributes929// and operand or result getters.930// 3. Verify the constraints on all present attributes.931static void932genAttributeVerifier(const OpOrAdaptorHelper &emitHelper, FmtContext &ctx,933 MethodBody &body,934 const StaticVerifierFunctionEmitter &staticVerifierEmitter,935 bool useProperties) {936 if (emitHelper.getAttrMetadata().empty())937 return;938 939 // Verify the attribute if it is present. This assumes that default values940 // are valid. This code snippet pastes the condition inline.941 //942 // TODO: verify the default value is valid (perhaps in debug mode only).943 //944 // {0}: Attribute variable name.945 // {1}: Attribute condition code.946 // {2}: Emit error prefix.947 // {3}: Attribute name.948 // {4}: Attribute/constraint description.949 const char *const verifyAttrInline = R"(950 if ({0} && !({1}))951 return {2}attribute '{3}' failed to satisfy constraint: {4}");952)";953 // Verify the attribute using a uniqued constraint. Can only be used within954 // the context of an op.955 //956 // {0}: Unique constraint name.957 // {1}: Attribute variable name.958 // {2}: Attribute name.959 const char *const verifyAttrUnique = R"(960 if (::mlir::failed({0}(*this, {1}, "{2}")))961 return ::mlir::failure();962)";963 964 // Traverse the array until the required attribute is found. Return an error965 // if the traversal reached the end.966 //967 // {0}: Code to get the name of the attribute.968 // {1}: The emit error prefix.969 // {2}: The name of the attribute.970 const char *const findRequiredAttr = R"(971while (true) {{972 if (namedAttrIt == namedAttrRange.end())973 return {1}"requires attribute '{2}'");974 if (namedAttrIt->getName() == {0}) {{975 tblgen_{2} = namedAttrIt->getValue();976 break;977 })";978 979 // Emit a check to see if the iteration has encountered an optional attribute.980 //981 // {0}: Code to get the name of the attribute.982 // {1}: The name of the attribute.983 const char *const checkOptionalAttr = R"(984 else if (namedAttrIt->getName() == {0}) {{985 tblgen_{1} = namedAttrIt->getValue();986 })";987 988 // Emit the start of the loop for checking trailing attributes.989 const char *const checkTrailingAttrs = R"(while (true) {990 if (namedAttrIt == namedAttrRange.end()) {991 break;992 })";993 994 // Emit the verifier for the attribute.995 const auto emitVerifier = [&](Attribute attr, StringRef attrName,996 StringRef varName) {997 std::string condition = attr.getPredicate().getCondition();998 999 std::optional<StringRef> constraintFn;1000 if (emitHelper.isEmittingForOp() &&1001 (constraintFn = staticVerifierEmitter.getAttrConstraintFn(attr))) {1002 body << formatv(verifyAttrUnique, *constraintFn, varName, attrName);1003 } else {1004 body << formatv(1005 verifyAttrInline, varName, tgfmt(condition, &ctx.withSelf(varName)),1006 emitHelper.emitErrorPrefix(), attrName,1007 buildErrorStreamingString(attr.getSummary(), ctx.withSelf(varName),1008 ErrorStreamType::InsideOpError));1009 }1010 };1011 1012 // Prefix variables with `tblgen_` to avoid hiding the attribute accessor.1013 const auto getVarName = [&](StringRef attrName) {1014 return (tblgenNamePrefix + attrName).str();1015 };1016 1017 body.indent();1018 if (useProperties) {1019 for (const std::pair<StringRef, AttributeMetadata> &it :1020 emitHelper.getAttrMetadata()) {1021 const AttributeMetadata &metadata = it.second;1022 if (metadata.constraint && metadata.constraint->isDerivedAttr())1023 continue;1024 body << formatv(1025 "auto tblgen_{0} = getProperties().{0}; (void)tblgen_{0};\n",1026 it.first);1027 if (metadata.isRequired)1028 body << formatv(1029 "if (!tblgen_{0}) return {1}requires attribute '{0}'\");\n",1030 it.first, emitHelper.emitErrorPrefix());1031 }1032 } else {1033 body << formatv("auto namedAttrRange = {0};\n", emitHelper.getAttrRange());1034 body << "auto namedAttrIt = namedAttrRange.begin();\n";1035 1036 // Iterate over the attributes in sorted order. Keep track of the optional1037 // attributes that may be encountered along the way.1038 SmallVector<const AttributeMetadata *> optionalAttrs;1039 1040 for (const std::pair<StringRef, AttributeMetadata> &it :1041 emitHelper.getAttrMetadata()) {1042 const AttributeMetadata &metadata = it.second;1043 if (!metadata.isRequired) {1044 optionalAttrs.push_back(&metadata);1045 continue;1046 }1047 1048 body << formatv("::mlir::Attribute {0};\n", getVarName(it.first));1049 for (const AttributeMetadata *optional : optionalAttrs) {1050 body << formatv("::mlir::Attribute {0};\n",1051 getVarName(optional->attrName));1052 }1053 body << formatv(findRequiredAttr, emitHelper.getAttrName(it.first),1054 emitHelper.emitErrorPrefix(), it.first);1055 for (const AttributeMetadata *optional : optionalAttrs) {1056 body << formatv(checkOptionalAttr,1057 emitHelper.getAttrName(optional->attrName),1058 optional->attrName);1059 }1060 body << "\n ++namedAttrIt;\n}\n";1061 optionalAttrs.clear();1062 }1063 // Get trailing optional attributes.1064 if (!optionalAttrs.empty()) {1065 for (const AttributeMetadata *optional : optionalAttrs) {1066 body << formatv("::mlir::Attribute {0};\n",1067 getVarName(optional->attrName));1068 }1069 body << checkTrailingAttrs;1070 for (const AttributeMetadata *optional : optionalAttrs) {1071 body << formatv(checkOptionalAttr,1072 emitHelper.getAttrName(optional->attrName),1073 optional->attrName);1074 }1075 body << "\n ++namedAttrIt;\n}\n";1076 }1077 }1078 body.unindent();1079 1080 // Emit the checks for segment attributes first so that the other1081 // constraints can call operand and result getters.1082 genNativeTraitAttrVerifier(body, emitHelper);1083 1084 bool isEmittingForOp = emitHelper.isEmittingForOp();1085 for (const auto &namedAttr : emitHelper.getOp().getAttributes())1086 if (canEmitAttrVerifier(namedAttr.attr, isEmittingForOp))1087 emitVerifier(namedAttr.attr, namedAttr.name, getVarName(namedAttr.name));1088}1089 1090static void genPropertyVerifier(1091 const OpOrAdaptorHelper &emitHelper, FmtContext &ctx, MethodBody &body,1092 const StaticVerifierFunctionEmitter &staticVerifierEmitter) {1093 1094 // Code to get a reference to a property into a variable to avoid multiple1095 // evaluations while verifying a property.1096 // {0}: Property variable name.1097 // {1}: Property name, with the first letter capitalized, to find the getter.1098 // {2}: Property interface type.1099 const char *const fetchProperty = R"(1100 [[maybe_unused]] {2} {0} = this->get{1}();1101)";1102 1103 // Code to verify that the predicate of a property holds. Embeds the1104 // condition inline.1105 // {0}: Property condition code, with tgfmt() applied.1106 // {1}: Emit error prefix.1107 // {2}: Property name.1108 // {3}: Property description.1109 const char *const verifyPropertyInline = R"(1110 if (!({0}))1111 return {1}property '{2}' failed to satisfy constraint: {3}");1112)";1113 1114 // Verify the property using a uniqued constraint. Can only be used1115 // within the context of an op.1116 //1117 // {0}: Unique constraint name.1118 // {1}: Property variable name in interface type.1119 // {2}: Property name.1120 const char *const verifyPropertyUniqued = R"(1121 if (::mlir::failed({0}(*this, {1}, "{2}")))1122 return ::mlir::failure();1123)";1124 1125 // Prefix variables with `tblgen_` to avoid hiding the attribute accessor.1126 const auto getVarName = [&](const NamedProperty &prop) {1127 std::string varName =1128 convertToCamelFromSnakeCase(prop.name, /*capitalizeFirst=*/false);1129 return (tblgenNamePrefix + Twine(varName)).str();1130 };1131 1132 for (const NamedProperty &prop : emitHelper.getOp().getProperties()) {1133 Pred predicate = prop.prop.getPredicate();1134 // Null predicate, nothing to verify.1135 if (predicate == Pred())1136 continue;1137 1138 std::string rawCondition = predicate.getCondition();1139 if (rawCondition == "true")1140 continue;1141 bool needsOp = StringRef(rawCondition).contains("$_op");1142 if (needsOp && !emitHelper.isEmittingForOp())1143 continue;1144 1145 auto scope = body.scope("{\n", "}\n", /*indent=*/true);1146 std::string varName = getVarName(prop);1147 std::string getterName =1148 convertToCamelFromSnakeCase(prop.name, /*capitalizeFirst=*/true);1149 body << formatv(fetchProperty, varName, getterName,1150 prop.prop.getInterfaceType());1151 auto uniquedFn = staticVerifierEmitter.getPropConstraintFn(prop.prop);1152 if (uniquedFn.has_value() && emitHelper.isEmittingForOp())1153 body << formatv(verifyPropertyUniqued, *uniquedFn, varName, prop.name);1154 else1155 body << formatv(verifyPropertyInline,1156 tgfmt(rawCondition, &ctx.withSelf(varName)),1157 emitHelper.emitErrorPrefix(), prop.name,1158 buildErrorStreamingString(1159 prop.prop.getSummary(), ctx.withSelf(varName),1160 ErrorStreamType::InsideOpError));1161 }1162}1163 1164/// Include declarations specified on NativeTrait1165static std::string formatExtraDeclarations(const Operator &op) {1166 SmallVector<StringRef> extraDeclarations;1167 // Include extra class declarations from NativeTrait1168 for (const auto &trait : op.getTraits()) {1169 if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {1170 StringRef value = opTrait->getExtraConcreteClassDeclaration();1171 if (value.empty())1172 continue;1173 extraDeclarations.push_back(value);1174 }1175 }1176 extraDeclarations.push_back(op.getExtraClassDeclaration());1177 return llvm::join(extraDeclarations, "\n");1178}1179 1180/// Op extra class definitions have a `$cppClass` substitution that is to be1181/// replaced by the C++ class name.1182/// Include declarations specified on NativeTrait1183static std::string formatExtraDefinitions(const Operator &op) {1184 SmallVector<StringRef> extraDefinitions;1185 // Include extra class definitions from NativeTrait1186 for (const auto &trait : op.getTraits()) {1187 if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {1188 StringRef value = opTrait->getExtraConcreteClassDefinition();1189 if (value.empty())1190 continue;1191 extraDefinitions.push_back(value);1192 }1193 }1194 extraDefinitions.push_back(op.getExtraClassDefinition());1195 FmtContext ctx = FmtContext().addSubst("cppClass", op.getCppClassName());1196 return tgfmt(llvm::join(extraDefinitions, "\n"), &ctx).str();1197}1198 1199OpEmitter::OpEmitter(const Operator &op,1200 const StaticVerifierFunctionEmitter &staticVerifierEmitter)1201 : def(op.getDef()), op(op),1202 opClass(op.getCppClassName(), formatExtraDeclarations(op),1203 formatExtraDefinitions(op)),1204 staticVerifierEmitter(staticVerifierEmitter),1205 emitHelper(op, /*emitForOp=*/true) {1206 verifyCtx.addSubst("_op", "(*this->getOperation())");1207 verifyCtx.addSubst("_ctxt", "this->getOperation()->getContext()");1208 1209 genTraits();1210 1211 // Generate C++ code for various op methods. The order here determines the1212 // methods in the generated file.1213 genAttrNameGetters();1214 genOpAsmInterface();1215 genOpNameGetter();1216 genNamedOperandGetters();1217 genNamedOperandSetters();1218 genNamedResultGetters();1219 genNamedRegionGetters();1220 genNamedSuccessorGetters();1221 genPropertiesSupport();1222 genPropGetters();1223 genPropSetters();1224 genAttrGetters();1225 genAttrSetters();1226 genOptionalAttrRemovers();1227 genBuilder();1228 genPopulateDefaultAttributes();1229 genParser();1230 genPrinter();1231 genVerifier();1232 genCustomVerifier();1233 genCanonicalizerDecls();1234 genFolderDecls();1235 genTypeInterfaceMethods();1236 genOpInterfaceMethods();1237 generateOpFormat(op, opClass, emitHelper.hasProperties());1238 genSideEffectInterfaceMethods();1239}1240void OpEmitter::emitDecl(1241 const Operator &op, raw_ostream &os,1242 const StaticVerifierFunctionEmitter &staticVerifierEmitter) {1243 OpEmitter(op, staticVerifierEmitter).emitDecl(os);1244}1245 1246void OpEmitter::emitDef(1247 const Operator &op, raw_ostream &os,1248 const StaticVerifierFunctionEmitter &staticVerifierEmitter) {1249 OpEmitter(op, staticVerifierEmitter).emitDef(os);1250}1251 1252void OpEmitter::emitDecl(raw_ostream &os) {1253 opClass.finalize();1254 opClass.writeDeclTo(os);1255}1256 1257void OpEmitter::emitDef(raw_ostream &os) {1258 opClass.finalize();1259 opClass.writeDefTo(os);1260}1261 1262static void errorIfPruned(size_t line, Method *m, const Twine &methodName,1263 const Operator &op) {1264 if (m)1265 return;1266 PrintFatalError(op.getLoc(), "Unexpected overlap when generating `" +1267 methodName + "` for " +1268 op.getOperationName() + " (from line " +1269 Twine(line) + ")");1270}1271 1272#define ERROR_IF_PRUNED(M, N, O) errorIfPruned(__LINE__, M, N, O)1273 1274void OpEmitter::genAttrNameGetters() {1275 const llvm::MapVector<StringRef, AttributeMetadata> &attributes =1276 emitHelper.getAttrMetadata();1277 bool hasOperandSegmentsSize =1278 op.getDialect().usePropertiesForAttributes() &&1279 op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");1280 // Emit the getAttributeNames method.1281 {1282 auto *method = opClass.addStaticInlineMethod(1283 "::llvm::ArrayRef<::llvm::StringRef>", "getAttributeNames");1284 ERROR_IF_PRUNED(method, "getAttributeNames", op);1285 auto &body = method->body();1286 if (!hasOperandSegmentsSize && attributes.empty()) {1287 body << " return {};";1288 // Nothing else to do if there are no registered attributes. Exit early.1289 return;1290 }1291 body << " static ::llvm::StringRef attrNames[] = {";1292 llvm::interleaveComma(llvm::make_first_range(attributes), body,1293 [&](StringRef attrName) {1294 body << "::llvm::StringRef(\"" << attrName << "\")";1295 });1296 if (hasOperandSegmentsSize) {1297 if (!attributes.empty())1298 body << ", ";1299 body << "::llvm::StringRef(\"" << operandSegmentAttrName << "\")";1300 }1301 body << "};\n return ::llvm::ArrayRef(attrNames);";1302 }1303 1304 // Emit the getAttributeNameForIndex methods.1305 {1306 auto *method = opClass.addInlineMethod<Method::Private>(1307 "::mlir::StringAttr", "getAttributeNameForIndex",1308 MethodParameter("unsigned", "index"));1309 ERROR_IF_PRUNED(method, "getAttributeNameForIndex", op);1310 method->body()1311 << " return getAttributeNameForIndex((*this)->getName(), index);";1312 }1313 {1314 auto *method = opClass.addStaticInlineMethod<Method::Private>(1315 "::mlir::StringAttr", "getAttributeNameForIndex",1316 MethodParameter("::mlir::OperationName", "name"),1317 MethodParameter("unsigned", "index"));1318 ERROR_IF_PRUNED(method, "getAttributeNameForIndex", op);1319 1320 if (attributes.empty()) {1321 method->body() << " return {};";1322 } else {1323 const char *const getAttrName = R"(1324 assert(index < {0} && "invalid attribute index");1325 assert(name.getStringRef() == getOperationName() && "invalid operation name");1326 assert(name.isRegistered() && "Operation isn't registered, missing a "1327 "dependent dialect loading?");1328 return name.getAttributeNames()[index];1329)";1330 method->body() << formatv(getAttrName, attributes.size());1331 }1332 }1333 1334 // Generate the <attr>AttrName methods, that expose the attribute names to1335 // users.1336 const char *attrNameMethodBody = " return getAttributeNameForIndex({0});";1337 for (auto [index, attr] :1338 llvm::enumerate(llvm::make_first_range(attributes))) {1339 std::string name = op.getGetterName(attr);1340 std::string methodName = name + "AttrName";1341 1342 // Generate the non-static variant.1343 {1344 auto *method = opClass.addInlineMethod("::mlir::StringAttr", methodName);1345 ERROR_IF_PRUNED(method, methodName, op);1346 method->body() << llvm::formatv(attrNameMethodBody, index);1347 }1348 1349 // Generate the static variant.1350 {1351 auto *method = opClass.addStaticInlineMethod(1352 "::mlir::StringAttr", methodName,1353 MethodParameter("::mlir::OperationName", "name"));1354 ERROR_IF_PRUNED(method, methodName, op);1355 method->body() << llvm::formatv(attrNameMethodBody,1356 "name, " + Twine(index));1357 }1358 }1359 if (hasOperandSegmentsSize) {1360 std::string name = op.getGetterName(operandSegmentAttrName);1361 std::string methodName = name + "AttrName";1362 // Generate the non-static variant.1363 {1364 auto *method = opClass.addInlineMethod("::mlir::StringAttr", methodName);1365 ERROR_IF_PRUNED(method, methodName, op);1366 method->body()1367 << " return (*this)->getName().getAttributeNames().back();";1368 }1369 1370 // Generate the static variant.1371 {1372 auto *method = opClass.addStaticInlineMethod(1373 "::mlir::StringAttr", methodName,1374 MethodParameter("::mlir::OperationName", "name"));1375 ERROR_IF_PRUNED(method, methodName, op);1376 method->body() << " return name.getAttributeNames().back();";1377 }1378 }1379}1380 1381// Emit the getter for a named property.1382// It is templated to be shared between the Op and the adaptor class.1383template <typename OpClassOrAdaptor>1384static void emitPropGetter(OpClassOrAdaptor &opClass, const Operator &op,1385 StringRef name, const Property &prop) {1386 auto *method = opClass.addInlineMethod(prop.getInterfaceType(), name);1387 ERROR_IF_PRUNED(method, name, op);1388 method->body() << formatv(" return getProperties().{0}();", name);1389}1390 1391// Emit the getter for an attribute with the return type specified.1392// It is templated to be shared between the Op and the adaptor class.1393template <typename OpClassOrAdaptor>1394static void emitAttrGetterWithReturnType(FmtContext &fctx,1395 OpClassOrAdaptor &opClass,1396 const Operator &op, StringRef name,1397 Attribute attr) {1398 auto *method = opClass.addMethod(attr.getReturnType(), name);1399 ERROR_IF_PRUNED(method, name, op);1400 auto &body = method->body();1401 body << " auto attr = " << name << "Attr();\n";1402 if (attr.hasDefaultValue() && attr.isOptional()) {1403 // Returns the default value if not set.1404 // TODO: this is inefficient, we are recreating the attribute for every1405 // call. This should be set instead.1406 if (!attr.isConstBuildable()) {1407 PrintFatalError("DefaultValuedAttr of type " + attr.getAttrDefName() +1408 " must have a constBuilder");1409 }1410 std::string defaultValue =1411 std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,1412 tgfmt(attr.getDefaultValue(), &fctx)));1413 body << " if (!attr)\n return "1414 << tgfmt(attr.getConvertFromStorageCall(),1415 &fctx.withSelf(defaultValue))1416 << ";\n";1417 }1418 body << " return "1419 << tgfmt(attr.getConvertFromStorageCall(), &fctx.withSelf("attr"))1420 << ";\n";1421}1422 1423void OpEmitter::genPropertiesSupport() {1424 if (!emitHelper.hasProperties())1425 return;1426 1427 SmallVector<ConstArgument> attrOrProperties;1428 for (const std::pair<StringRef, AttributeMetadata> &it :1429 emitHelper.getAttrMetadata()) {1430 if (!it.second.constraint || !it.second.constraint->isDerivedAttr())1431 attrOrProperties.push_back(&it.second);1432 }1433 for (const NamedProperty &prop : op.getProperties())1434 attrOrProperties.push_back(&prop);1435 if (emitHelper.getOperandSegmentsSize())1436 attrOrProperties.push_back(&emitHelper.getOperandSegmentsSize().value());1437 if (emitHelper.getResultSegmentsSize())1438 attrOrProperties.push_back(&emitHelper.getResultSegmentsSize().value());1439 auto &setPropMethod =1440 opClass1441 .addStaticMethod(1442 "::llvm::LogicalResult", "setPropertiesFromAttr",1443 MethodParameter("Properties &", "prop"),1444 MethodParameter("::mlir::Attribute", "attr"),1445 MethodParameter(1446 "::llvm::function_ref<::mlir::InFlightDiagnostic()>",1447 "emitError"))1448 ->body();1449 auto &getPropMethod =1450 opClass1451 .addStaticMethod("::mlir::Attribute", "getPropertiesAsAttr",1452 MethodParameter("::mlir::MLIRContext *", "ctx"),1453 MethodParameter("const Properties &", "prop"))1454 ->body();1455 auto &hashMethod =1456 opClass1457 .addStaticMethod("llvm::hash_code", "computePropertiesHash",1458 MethodParameter("const Properties &", "prop"))1459 ->body();1460 auto &getInherentAttrMethod =1461 opClass1462 .addStaticMethod("std::optional<mlir::Attribute>", "getInherentAttr",1463 MethodParameter("::mlir::MLIRContext *", "ctx"),1464 MethodParameter("const Properties &", "prop"),1465 MethodParameter("llvm::StringRef", "name"))1466 ->body();1467 auto &setInherentAttrMethod =1468 opClass1469 .addStaticMethod("void", "setInherentAttr",1470 MethodParameter("Properties &", "prop"),1471 MethodParameter("llvm::StringRef", "name"),1472 MethodParameter("mlir::Attribute", "value"))1473 ->body();1474 auto &populateInherentAttrsMethod =1475 opClass1476 .addStaticMethod("void", "populateInherentAttrs",1477 MethodParameter("::mlir::MLIRContext *", "ctx"),1478 MethodParameter("const Properties &", "prop"),1479 MethodParameter("::mlir::NamedAttrList &", "attrs"))1480 ->body();1481 auto &verifyInherentAttrsMethod =1482 opClass1483 .addStaticMethod(1484 "::llvm::LogicalResult", "verifyInherentAttrs",1485 MethodParameter("::mlir::OperationName", "opName"),1486 MethodParameter("::mlir::NamedAttrList &", "attrs"),1487 MethodParameter(1488 "llvm::function_ref<::mlir::InFlightDiagnostic()>",1489 "emitError"))1490 ->body();1491 1492 opClass.declare<UsingDeclaration>("Properties", "FoldAdaptor::Properties");1493 1494 // Convert the property to the attribute form.1495 1496 setPropMethod << R"decl(1497 ::mlir::DictionaryAttr dict = ::llvm::dyn_cast<::mlir::DictionaryAttr>(attr);1498 if (!dict) {1499 emitError() << "expected DictionaryAttr to set properties";1500 return ::mlir::failure();1501 }1502 )decl";1503 const char *propFromAttrFmt = R"decl(1504 auto setFromAttr = [] (auto &propStorage, ::mlir::Attribute propAttr,1505 ::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) -> ::mlir::LogicalResult {{1506 {0}1507 };1508 {1};1509)decl";1510 const char *attrGetNoDefaultFmt = R"decl(;1511 if (attr && ::mlir::failed(setFromAttr(prop.{0}, attr, emitError)))1512 return ::mlir::failure();1513)decl";1514 const char *attrGetDefaultFmt = R"decl(;1515 if (attr) {{1516 if (::mlir::failed(setFromAttr(prop.{0}, attr, emitError)))1517 return ::mlir::failure();1518 } else {{1519 prop.{0} = {1};1520 }1521)decl";1522 1523 for (const auto &attrOrProp : attrOrProperties) {1524 if (const auto *namedProperty =1525 llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {1526 StringRef name = namedProperty->name;1527 auto &prop = namedProperty->prop;1528 FmtContext fctx;1529 1530 std::string getAttr;1531 llvm::raw_string_ostream os(getAttr);1532 os << " auto attr = dict.get(\"" << name << "\");";1533 if (name == operandSegmentAttrName) {1534 // Backward compat for now, TODO: Remove at some point.1535 os << " if (!attr) attr = dict.get(\"operand_segment_sizes\");";1536 }1537 if (name == resultSegmentAttrName) {1538 // Backward compat for now, TODO: Remove at some point.1539 os << " if (!attr) attr = dict.get(\"result_segment_sizes\");";1540 }1541 1542 fctx.withBuilder(odsBuilder);1543 setPropMethod << "{\n"1544 << formatv(propFromAttrFmt,1545 tgfmt(prop.getConvertFromAttributeCall(),1546 &fctx.addSubst("_attr", propertyAttr)1547 .addSubst("_storage", propertyStorage)1548 .addSubst("_diag", propertyDiag)),1549 getAttr);1550 if (prop.hasStorageTypeValueOverride()) {1551 setPropMethod << formatv(attrGetDefaultFmt, name,1552 prop.getStorageTypeValueOverride());1553 } else if (prop.hasDefaultValue()) {1554 setPropMethod << formatv(attrGetDefaultFmt, name,1555 tgfmt(prop.getDefaultValue(), &fctx));1556 } else {1557 setPropMethod << formatv(attrGetNoDefaultFmt, name);1558 }1559 setPropMethod << " }\n";1560 } else {1561 const auto *namedAttr =1562 llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);1563 StringRef name = namedAttr->attrName;1564 std::string getAttr;1565 llvm::raw_string_ostream os(getAttr);1566 os << " auto attr = dict.get(\"" << name << "\");";1567 if (name == operandSegmentAttrName) {1568 // Backward compat for now1569 os << " if (!attr) attr = dict.get(\"operand_segment_sizes\");";1570 }1571 if (name == resultSegmentAttrName) {1572 // Backward compat for now1573 os << " if (!attr) attr = dict.get(\"result_segment_sizes\");";1574 }1575 1576 setPropMethod << formatv(R"decl(1577 {{1578 auto &propStorage = prop.{0};1579 {1}1580 if (attr) {{1581 auto convertedAttr = ::llvm::dyn_cast<std::remove_reference_t<decltype(propStorage)>>(attr);1582 if (convertedAttr) {{1583 propStorage = convertedAttr;1584 } else {{1585 emitError() << "Invalid attribute `{0}` in property conversion: " << attr;1586 return ::mlir::failure();1587 }1588 }1589 }1590)decl",1591 name, getAttr);1592 }1593 }1594 setPropMethod << " return ::mlir::success();\n";1595 1596 // Convert the attribute form to the property.1597 1598 getPropMethod << " ::mlir::SmallVector<::mlir::NamedAttribute> attrs;\n"1599 << " ::mlir::Builder odsBuilder{ctx};\n";1600 const char *propToAttrFmt = R"decl(1601 {1602 const auto &propStorage = prop.{0};1603 auto attr = [&]() -> ::mlir::Attribute {{1604 {1}1605 }();1606 attrs.push_back(odsBuilder.getNamedAttr("{0}", attr));1607 }1608)decl";1609 for (const auto &attrOrProp : attrOrProperties) {1610 if (const auto *namedProperty =1611 llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {1612 StringRef name = namedProperty->name;1613 auto &prop = namedProperty->prop;1614 FmtContext fctx;1615 getPropMethod << formatv(1616 propToAttrFmt, name,1617 tgfmt(prop.getConvertToAttributeCall(),1618 &fctx.addSubst("_ctxt", "ctx")1619 .addSubst("_storage", propertyStorage)));1620 continue;1621 }1622 const auto *namedAttr =1623 llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);1624 StringRef name = namedAttr->attrName;1625 getPropMethod << formatv(R"decl(1626 {{1627 const auto &propStorage = prop.{0};1628 if (propStorage)1629 attrs.push_back(odsBuilder.getNamedAttr("{0}",1630 propStorage));1631 }1632)decl",1633 name);1634 }1635 getPropMethod << R"decl(1636 if (!attrs.empty())1637 return odsBuilder.getDictionaryAttr(attrs);1638 return {};1639)decl";1640 1641 // Hashing for the property1642 1643 const char *propHashFmt = R"decl(1644 auto hash_{0}_ = [] (const auto &propStorage) -> llvm::hash_code {1645 using ::llvm::hash_value;1646 return {1};1647 };1648)decl";1649 for (const auto &attrOrProp : attrOrProperties) {1650 if (const auto *namedProperty =1651 llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {1652 StringRef name = namedProperty->name;1653 auto &prop = namedProperty->prop;1654 FmtContext fctx;1655 if (!prop.getHashPropertyCall().empty()) {1656 hashMethod << formatv(1657 propHashFmt, name,1658 tgfmt(prop.getHashPropertyCall(),1659 &fctx.addSubst("_storage", propertyStorage)));1660 }1661 }1662 }1663 hashMethod << " using llvm::hash_value;\n";1664 hashMethod << " return llvm::hash_combine(";1665 llvm::interleaveComma(1666 attrOrProperties, hashMethod, [&](const ConstArgument &attrOrProp) {1667 if (const auto *namedProperty =1668 llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {1669 if (!namedProperty->prop.getHashPropertyCall().empty()) {1670 hashMethod << "\n hash_" << namedProperty->name << "_(prop."1671 << namedProperty->name << ")";1672 } else {1673 hashMethod << "\n hash_value(prop." << namedProperty->name1674 << ")";1675 }1676 return;1677 }1678 const auto *namedAttr =1679 llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);1680 StringRef name = namedAttr->attrName;1681 hashMethod << "\n llvm::hash_value(prop." << name1682 << ".getAsOpaquePointer())";1683 });1684 hashMethod << ");\n";1685 1686 const char *getInherentAttrMethodFmt = R"decl(1687 if (name == "{0}")1688 return prop.{0};1689)decl";1690 const char *setInherentAttrMethodFmt = R"decl(1691 if (name == "{0}") {{1692 prop.{0} = ::llvm::dyn_cast_or_null<std::remove_reference_t<decltype(prop.{0})>>(value);1693 return;1694 }1695)decl";1696 const char *populateInherentAttrsMethodFmt = R"decl(1697 if (prop.{0}) attrs.append("{0}", prop.{0});1698)decl";1699 for (const auto &attrOrProp : attrOrProperties) {1700 if (const auto *namedAttr =1701 llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp)) {1702 StringRef name = namedAttr->attrName;1703 getInherentAttrMethod << formatv(getInherentAttrMethodFmt, name);1704 setInherentAttrMethod << formatv(setInherentAttrMethodFmt, name);1705 populateInherentAttrsMethod1706 << formatv(populateInherentAttrsMethodFmt, name);1707 continue;1708 }1709 // The ODS segment size property is "special": we expose it as an attribute1710 // even though it is a native property.1711 const auto *namedProperty = cast<const NamedProperty *>(attrOrProp);1712 StringRef name = namedProperty->name;1713 if (name != operandSegmentAttrName && name != resultSegmentAttrName)1714 continue;1715 auto &prop = namedProperty->prop;1716 FmtContext fctx;1717 fctx.addSubst("_ctxt", "ctx");1718 fctx.addSubst("_storage", Twine("prop.") + name);1719 if (name == operandSegmentAttrName) {1720 getInherentAttrMethod1721 << formatv(" if (name == \"operand_segment_sizes\" || name == "1722 "\"{0}\") return ",1723 operandSegmentAttrName);1724 } else {1725 getInherentAttrMethod1726 << formatv(" if (name == \"result_segment_sizes\" || name == "1727 "\"{0}\") return ",1728 resultSegmentAttrName);1729 }1730 getInherentAttrMethod << "[&]() -> ::mlir::Attribute { "1731 << tgfmt(prop.getConvertToAttributeCall(), &fctx)1732 << " }();\n";1733 1734 if (name == operandSegmentAttrName) {1735 setInherentAttrMethod1736 << formatv(" if (name == \"operand_segment_sizes\" || name == "1737 "\"{0}\") {{",1738 operandSegmentAttrName);1739 } else {1740 setInherentAttrMethod1741 << formatv(" if (name == \"result_segment_sizes\" || name == "1742 "\"{0}\") {{",1743 resultSegmentAttrName);1744 }1745 setInherentAttrMethod << formatv(R"decl(1746 auto arrAttr = ::llvm::dyn_cast_or_null<::mlir::DenseI32ArrayAttr>(value);1747 if (!arrAttr) return;1748 if (arrAttr.size() != sizeof(prop.{0}) / sizeof(int32_t))1749 return;1750 llvm::copy(arrAttr.asArrayRef(), prop.{0}.begin());1751 return;1752 }1753)decl",1754 name);1755 if (name == operandSegmentAttrName) {1756 populateInherentAttrsMethod << formatv(1757 " attrs.append(\"{0}\", [&]() -> ::mlir::Attribute { {1} }());\n",1758 operandSegmentAttrName,1759 tgfmt(prop.getConvertToAttributeCall(), &fctx));1760 } else {1761 populateInherentAttrsMethod << formatv(1762 " attrs.append(\"{0}\", [&]() -> ::mlir::Attribute { {1} }());\n",1763 resultSegmentAttrName,1764 tgfmt(prop.getConvertToAttributeCall(), &fctx));1765 }1766 }1767 getInherentAttrMethod << " return std::nullopt;\n";1768 1769 // Emit the verifiers method for backward compatibility with the generic1770 // syntax. This method verifies the constraint on the properties attributes1771 // before they are set, since dyn_cast<> will silently omit failures.1772 for (const auto &attrOrProp : attrOrProperties) {1773 const auto *namedAttr =1774 llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);1775 if (!namedAttr || !namedAttr->constraint)1776 continue;1777 Attribute attr = *namedAttr->constraint;1778 std::optional<StringRef> constraintFn =1779 staticVerifierEmitter.getAttrConstraintFn(attr);1780 if (!constraintFn)1781 continue;1782 if (canEmitAttrVerifier(attr,1783 /*isEmittingForOp=*/false)) {1784 std::string name = op.getGetterName(namedAttr->attrName);1785 verifyInherentAttrsMethod1786 << formatv(R"(1787 {{1788 ::mlir::Attribute attr = attrs.get({0}AttrName(opName));1789 if (attr && ::mlir::failed({1}(attr, "{2}", emitError)))1790 return ::mlir::failure();1791 }1792)",1793 name, constraintFn, namedAttr->attrName);1794 }1795 }1796 verifyInherentAttrsMethod << " return ::mlir::success();";1797 1798 // Generate methods to interact with bytecode.1799 genPropertiesSupportForBytecode(attrOrProperties);1800}1801 1802void OpEmitter::genPropertiesSupportForBytecode(1803 ArrayRef<ConstArgument> attrOrProperties) {1804 if (attrOrProperties.empty())1805 return;1806 1807 if (op.useCustomPropertiesEncoding()) {1808 opClass.declareStaticMethod(1809 "::llvm::LogicalResult", "readProperties",1810 MethodParameter("::mlir::DialectBytecodeReader &", "reader"),1811 MethodParameter("::mlir::OperationState &", "state"));1812 opClass.declareMethod(1813 "void", "writeProperties",1814 MethodParameter("::mlir::DialectBytecodeWriter &", "writer"));1815 return;1816 }1817 1818 auto &readPropertiesMethod =1819 opClass1820 .addStaticMethod(1821 "::llvm::LogicalResult", "readProperties",1822 MethodParameter("::mlir::DialectBytecodeReader &", "reader"),1823 MethodParameter("::mlir::OperationState &", "state"))1824 ->body();1825 1826 auto &writePropertiesMethod =1827 opClass1828 .addMethod(1829 "void", "writeProperties",1830 MethodParameter("::mlir::DialectBytecodeWriter &", "writer"))1831 ->body();1832 1833 // Populate bytecode serialization logic.1834 readPropertiesMethod1835 << " auto &prop = state.getOrAddProperties<Properties>(); (void)prop;";1836 writePropertiesMethod << " auto &prop = getProperties(); (void)prop;\n";1837 for (const auto &item : llvm::enumerate(attrOrProperties)) {1838 auto &attrOrProp = item.value();1839 FmtContext fctx;1840 fctx.addSubst("_reader", "reader")1841 .addSubst("_writer", "writer")1842 .addSubst("_storage", propertyStorage)1843 .addSubst("_ctxt", "this->getContext()");1844 // If the op emits operand/result segment sizes as a property, emit the1845 // legacy reader/writer in the appropriate order to allow backward1846 // compatibility and back deployment.1847 if (emitHelper.getOperandSegmentsSize().has_value() &&1848 item.index() == emitHelper.getOperandSegmentSizesLegacyIndex()) {1849 FmtContext fmtCtxt(fctx);1850 fmtCtxt.addSubst("_propName", operandSegmentAttrName);1851 readPropertiesMethod << tgfmt(readBytecodeSegmentSizeLegacy, &fmtCtxt);1852 writePropertiesMethod << tgfmt(writeBytecodeSegmentSizeLegacy, &fmtCtxt);1853 }1854 if (emitHelper.getResultSegmentsSize().has_value() &&1855 item.index() == emitHelper.getResultSegmentSizesLegacyIndex()) {1856 FmtContext fmtCtxt(fctx);1857 fmtCtxt.addSubst("_propName", resultSegmentAttrName);1858 readPropertiesMethod << tgfmt(readBytecodeSegmentSizeLegacy, &fmtCtxt);1859 writePropertiesMethod << tgfmt(writeBytecodeSegmentSizeLegacy, &fmtCtxt);1860 }1861 if (const auto *namedProperty =1862 dyn_cast<const NamedProperty *>(attrOrProp)) {1863 StringRef name = namedProperty->name;1864 readPropertiesMethod << formatv(1865 R"(1866 {{1867 auto &propStorage = prop.{0};1868 auto readProp = [&]() {1869 {1};1870 return ::mlir::success();1871 };1872 if (::mlir::failed(readProp()))1873 return ::mlir::failure();1874 }1875)",1876 name,1877 tgfmt(namedProperty->prop.getReadFromMlirBytecodeCall(), &fctx));1878 writePropertiesMethod << formatv(1879 R"(1880 {{1881 auto &propStorage = prop.{0};1882 {1};1883 }1884)",1885 name, tgfmt(namedProperty->prop.getWriteToMlirBytecodeCall(), &fctx));1886 continue;1887 }1888 const auto *namedAttr = dyn_cast<const AttributeMetadata *>(attrOrProp);1889 StringRef name = namedAttr->attrName;1890 if (namedAttr->isRequired) {1891 readPropertiesMethod << formatv(R"(1892 if (::mlir::failed(reader.readAttribute(prop.{0})))1893 return ::mlir::failure();1894)",1895 name);1896 writePropertiesMethod1897 << formatv(" writer.writeAttribute(prop.{0});\n", name);1898 } else {1899 readPropertiesMethod << formatv(R"(1900 if (::mlir::failed(reader.readOptionalAttribute(prop.{0})))1901 return ::mlir::failure();1902)",1903 name);1904 writePropertiesMethod << formatv(R"(1905 writer.writeOptionalAttribute(prop.{0});1906)",1907 name);1908 }1909 }1910 readPropertiesMethod << " return ::mlir::success();";1911}1912 1913void OpEmitter::genPropGetters() {1914 for (const NamedProperty &prop : op.getProperties()) {1915 std::string name = op.getGetterName(prop.name);1916 emitPropGetter(opClass, op, name, prop.prop);1917 }1918}1919 1920void OpEmitter::genPropSetters() {1921 for (const NamedProperty &prop : op.getProperties()) {1922 std::string name = op.getSetterName(prop.name);1923 std::string argName = "new" + convertToCamelFromSnakeCase(1924 prop.name, /*capitalizeFirst=*/true);1925 auto *method = opClass.addInlineMethod(1926 "void", name, MethodParameter(prop.prop.getInterfaceType(), argName));1927 if (!method)1928 return;1929 method->body() << formatv(" getProperties().{0}({1});", name, argName);1930 }1931}1932 1933void OpEmitter::genAttrGetters() {1934 FmtContext fctx;1935 fctx.withBuilder("::mlir::Builder((*this)->getContext())");1936 1937 // Emit the derived attribute body.1938 auto emitDerivedAttr = [&](StringRef name, Attribute attr) {1939 if (auto *method = opClass.addMethod(attr.getReturnType(), name))1940 method->body() << " " << attr.getDerivedCodeBody() << "\n";1941 };1942 1943 // Generate named accessor with Attribute return type. This is a wrapper1944 // class that allows referring to the attributes via accessors instead of1945 // having to use the string interface for better compile time verification.1946 auto emitAttrWithStorageType = [&](StringRef name, StringRef attrName,1947 Attribute attr) {1948 // The method body for this getter is trivial. Emit it inline.1949 auto *method =1950 opClass.addInlineMethod(attr.getStorageType(), name + "Attr");1951 if (!method)1952 return;1953 method->body() << formatv(1954 " return ::llvm::{1}<{2}>({0});", emitHelper.getAttr(attrName),1955 attr.isOptional() || attr.hasDefaultValue() ? "dyn_cast_or_null"1956 : "cast",1957 attr.getStorageType());1958 };1959 1960 for (const NamedAttribute &namedAttr : op.getAttributes()) {1961 std::string name = op.getGetterName(namedAttr.name);1962 if (namedAttr.attr.isDerivedAttr()) {1963 emitDerivedAttr(name, namedAttr.attr);1964 } else {1965 emitAttrWithStorageType(name, namedAttr.name, namedAttr.attr);1966 emitAttrGetterWithReturnType(fctx, opClass, op, name, namedAttr.attr);1967 }1968 }1969 1970 auto derivedAttrs = make_filter_range(op.getAttributes(),1971 [](const NamedAttribute &namedAttr) {1972 return namedAttr.attr.isDerivedAttr();1973 });1974 if (derivedAttrs.empty())1975 return;1976 1977 opClass.addTrait("::mlir::DerivedAttributeOpInterface::Trait");1978 // Generate helper method to query whether a named attribute is a derived1979 // attribute. This enables, for example, avoiding adding an attribute that1980 // overlaps with a derived attribute.1981 {1982 auto *method =1983 opClass.addStaticMethod("bool", "isDerivedAttribute",1984 MethodParameter("::llvm::StringRef", "name"));1985 ERROR_IF_PRUNED(method, "isDerivedAttribute", op);1986 auto &body = method->body();1987 for (auto namedAttr : derivedAttrs)1988 body << " if (name == \"" << namedAttr.name << "\") return true;\n";1989 body << " return false;";1990 }1991 // Generate method to materialize derived attributes as a DictionaryAttr.1992 {1993 auto *method = opClass.addMethod("::mlir::DictionaryAttr",1994 "materializeDerivedAttributes");1995 ERROR_IF_PRUNED(method, "materializeDerivedAttributes", op);1996 auto &body = method->body();1997 1998 auto nonMaterializable =1999 make_filter_range(derivedAttrs, [](const NamedAttribute &namedAttr) {2000 return namedAttr.attr.getConvertFromStorageCall().empty();2001 });2002 if (!nonMaterializable.empty()) {2003 std::string attrs;2004 llvm::raw_string_ostream os(attrs);2005 interleaveComma(nonMaterializable, os, [&](const NamedAttribute &attr) {2006 os << op.getGetterName(attr.name);2007 });2008 PrintWarning(2009 op.getLoc(),2010 formatv(2011 "op has non-materializable derived attributes '{0}', skipping",2012 os.str()));2013 body << formatv(" emitOpError(\"op has non-materializable derived "2014 "attributes '{0}'\");\n",2015 attrs);2016 body << " return nullptr;";2017 return;2018 }2019 2020 body << " ::mlir::MLIRContext* ctx = getContext();\n";2021 body << " ::mlir::Builder odsBuilder(ctx); (void)odsBuilder;\n";2022 body << " return ::mlir::DictionaryAttr::get(";2023 body << " ctx, {\n";2024 interleave(2025 derivedAttrs, body,2026 [&](const NamedAttribute &namedAttr) {2027 auto tmpl = namedAttr.attr.getConvertFromStorageCall();2028 std::string name = op.getGetterName(namedAttr.name);2029 body << " {" << name << "AttrName(),\n"2030 << tgfmt(tmpl, &fctx.withSelf(name + "()")2031 .withBuilder("odsBuilder")2032 .addSubst("_ctxt", "ctx")2033 .addSubst("_storage", "ctx"))2034 << "}";2035 },2036 ",\n");2037 body << "});";2038 }2039}2040 2041void OpEmitter::genAttrSetters() {2042 bool useProperties = op.getDialect().usePropertiesForAttributes();2043 2044 // Generate the code to set an attribute.2045 auto emitSetAttr = [&](Method *method, StringRef getterName,2046 StringRef attrName, StringRef attrVar) {2047 if (useProperties) {2048 method->body() << formatv(" getProperties().{0} = {1};", attrName,2049 attrVar);2050 } else {2051 method->body() << formatv(" (*this)->setAttr({0}AttrName(), {1});",2052 getterName, attrVar);2053 }2054 };2055 2056 // Generate raw named setter type. This is a wrapper class that allows setting2057 // to the attributes via setters instead of having to use the string interface2058 // for better compile time verification.2059 auto emitAttrWithStorageType = [&](StringRef setterName, StringRef getterName,2060 StringRef attrName, Attribute attr) {2061 // This method body is trivial, so emit it inline.2062 auto *method =2063 opClass.addInlineMethod("void", setterName + "Attr",2064 MethodParameter(attr.getStorageType(), "attr"));2065 if (method)2066 emitSetAttr(method, getterName, attrName, "attr");2067 };2068 2069 // Generate a setter that accepts the underlying C++ type as opposed to the2070 // attribute type.2071 auto emitAttrWithReturnType = [&](StringRef setterName, StringRef getterName,2072 StringRef attrName, Attribute attr) {2073 Attribute baseAttr = attr.getBaseAttr();2074 if (!canUseUnwrappedRawValue(baseAttr))2075 return;2076 FmtContext fctx;2077 fctx.withBuilder("::mlir::Builder((*this)->getContext())");2078 bool isUnitAttr = attr.getAttrDefName() == "UnitAttr";2079 bool isOptional = attr.isOptional();2080 2081 auto createMethod = [&](const Twine ¶mType) {2082 return opClass.addMethod("void", setterName,2083 MethodParameter(paramType.str(), "attrValue"));2084 };2085 2086 // Build the method using the correct parameter type depending on2087 // optionality.2088 Method *method = nullptr;2089 if (isUnitAttr)2090 method = createMethod("bool");2091 else if (isOptional)2092 method =2093 createMethod("::std::optional<" + baseAttr.getReturnType() + ">");2094 else2095 method = createMethod(attr.getReturnType());2096 if (!method)2097 return;2098 2099 // If the value isn't optional, just set it directly.2100 if (!isOptional) {2101 emitSetAttr(method, getterName, attrName,2102 constBuildAttrFromParam(attr, fctx, "attrValue"));2103 return;2104 }2105 2106 // Otherwise, we only set if the provided value is valid. If it isn't, we2107 // remove the attribute.2108 2109 // TODO: Handle unit attr parameters specially, given that it is treated as2110 // optional but not in the same way as the others (i.e. it uses bool over2111 // std::optional<>).2112 StringRef paramStr = isUnitAttr ? "attrValue" : "*attrValue";2113 if (!useProperties) {2114 const char *optionalCodeBody = R"(2115 if (attrValue)2116 return (*this)->setAttr({0}AttrName(), {1});2117 (*this)->removeAttr({0}AttrName());)";2118 method->body() << formatv(2119 optionalCodeBody, getterName,2120 constBuildAttrFromParam(baseAttr, fctx, paramStr));2121 } else {2122 const char *optionalCodeBody = R"(2123 auto &odsProp = getProperties().{0};2124 if (attrValue)2125 odsProp = {1};2126 else2127 odsProp = nullptr;)";2128 method->body() << formatv(2129 optionalCodeBody, attrName,2130 constBuildAttrFromParam(baseAttr, fctx, paramStr));2131 }2132 };2133 2134 for (const NamedAttribute &namedAttr : op.getAttributes()) {2135 if (namedAttr.attr.isDerivedAttr())2136 continue;2137 std::string setterName = op.getSetterName(namedAttr.name);2138 std::string getterName = op.getGetterName(namedAttr.name);2139 emitAttrWithStorageType(setterName, getterName, namedAttr.name,2140 namedAttr.attr);2141 emitAttrWithReturnType(setterName, getterName, namedAttr.name,2142 namedAttr.attr);2143 }2144}2145 2146void OpEmitter::genOptionalAttrRemovers() {2147 // Generate methods for removing optional attributes, instead of having to2148 // use the string interface. Enables better compile time verification.2149 auto emitRemoveAttr = [&](StringRef name, bool useProperties) {2150 auto *method = opClass.addInlineMethod("::mlir::Attribute",2151 op.getRemoverName(name) + "Attr");2152 if (!method)2153 return;2154 if (useProperties) {2155 method->body() << formatv(R"(2156 auto attr = getProperties().{0};2157 getProperties().{0} = {{};2158 return attr;2159)",2160 name);2161 return;2162 }2163 method->body() << formatv("return (*this)->removeAttr({0}AttrName());",2164 op.getGetterName(name));2165 };2166 2167 for (const NamedAttribute &namedAttr : op.getAttributes())2168 if (namedAttr.attr.isOptional())2169 emitRemoveAttr(namedAttr.name,2170 op.getDialect().usePropertiesForAttributes());2171}2172 2173// Generates the code to compute the start and end index of an operand or result2174// range.2175template <typename RangeT>2176static void generateValueRangeStartAndEnd(2177 Class &opClass, bool isGenericAdaptorBase, StringRef methodName,2178 int numVariadic, int numNonVariadic, StringRef rangeSizeCall,2179 bool hasAttrSegmentSize, StringRef sizeAttrInit, RangeT &&odsValues) {2180 2181 SmallVector<MethodParameter> parameters{MethodParameter("unsigned", "index")};2182 if (isGenericAdaptorBase) {2183 parameters.emplace_back("unsigned", "odsOperandsSize");2184 // The range size is passed per parameter for generic adaptor bases as2185 // using the rangeSizeCall would require the operands, which are not2186 // accessible in the base class.2187 rangeSizeCall = "odsOperandsSize";2188 }2189 2190 // The method is trivial if the operation does not have any variadic operands.2191 // In that case, make sure to generate it in-line.2192 auto *method = opClass.addMethod("std::pair<unsigned, unsigned>", methodName,2193 numVariadic == 0 ? Method::Properties::Inline2194 : Method::Properties::None,2195 parameters);2196 if (!method)2197 return;2198 auto &body = method->body();2199 if (numVariadic == 0) {2200 body << " return {index, 1};\n";2201 } else if (hasAttrSegmentSize) {2202 body << sizeAttrInit << attrSizedSegmentValueRangeCalcCode;2203 } else {2204 // Because the op can have arbitrarily interleaved variadic and non-variadic2205 // operands, we need to embed a list in the "sink" getter method for2206 // calculation at run-time.2207 SmallVector<StringRef, 4> isVariadic;2208 isVariadic.reserve(llvm::size(odsValues));2209 for (auto &it : odsValues)2210 isVariadic.push_back(it.isVariableLength() ? "true" : "false");2211 std::string isVariadicList = llvm::join(isVariadic, ", ");2212 body << formatv(sameVariadicSizeValueRangeCalcCode, isVariadicList,2213 numNonVariadic, numVariadic, rangeSizeCall, "operand");2214 }2215}2216 2217static std::string generateTypeForGetter(const NamedTypeConstraint &value) {2218 return llvm::formatv("::mlir::TypedValue<{0}>", value.constraint.getCppType())2219 .str();2220}2221 2222// Generates the named operand getter methods for the given Operator `op` and2223// puts them in `opClass`. Uses `rangeType` as the return type of getters that2224// return a range of operands (individual operands are `Value ` and each2225// element in the range must also be `Value `); use `rangeBeginCall` to get2226// an iterator to the beginning of the operand range; use `rangeSizeCall` to2227// obtain the number of operands. `getOperandCallPattern` contains the code2228// necessary to obtain a single operand whose position will be substituted2229// instead of2230// "{0}" marker in the pattern. Note that the pattern should work for any kind2231// of ops, in particular for one-operand ops that may not have the2232// `getOperand(unsigned)` method.2233static void2234generateNamedOperandGetters(const Operator &op, Class &opClass,2235 Class *genericAdaptorBase, StringRef sizeAttrInit,2236 StringRef rangeType, StringRef rangeElementType,2237 StringRef rangeBeginCall, StringRef rangeSizeCall,2238 StringRef getOperandCallPattern) {2239 const int numOperands = op.getNumOperands();2240 const int numVariadicOperands = op.getNumVariableLengthOperands();2241 const int numNormalOperands = numOperands - numVariadicOperands;2242 2243 const auto *sameVariadicSize =2244 op.getTrait("::mlir::OpTrait::SameVariadicOperandSize");2245 const auto *attrSizedOperands =2246 op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");2247 2248 if (numVariadicOperands > 1 && !sameVariadicSize && !attrSizedOperands) {2249 PrintFatalError(op.getLoc(), "op has multiple variadic operands but no "2250 "specification over their sizes");2251 }2252 2253 if (numVariadicOperands < 2 && attrSizedOperands) {2254 PrintFatalError(op.getLoc(), "op must have at least two variadic operands "2255 "to use 'AttrSizedOperandSegments' trait");2256 }2257 2258 if (attrSizedOperands && sameVariadicSize) {2259 PrintFatalError(op.getLoc(),2260 "op cannot have both 'AttrSizedOperandSegments' and "2261 "'SameVariadicOperandSize' traits");2262 }2263 2264 // Print the ods names so they don't need to be hardcoded in the source.2265 for (int i = 0; i != numOperands; ++i) {2266 const auto &operand = op.getOperand(i);2267 if (operand.name.empty())2268 continue;2269 2270 opClass.declare<Field>("static constexpr int", Twine("odsIndex_") +2271 operand.name + " = " +2272 Twine(i));2273 }2274 2275 // First emit a few "sink" getter methods upon which we layer all nicer named2276 // getter methods.2277 // If generating for an adaptor, the method is put into the non-templated2278 // generic base class, to not require being defined in the header.2279 // Since the operand size can't be determined from the base class however,2280 // it has to be passed as an additional argument. The trampoline below2281 // generates the function with the same signature as the Op in the generic2282 // adaptor.2283 bool isGenericAdaptorBase = genericAdaptorBase != nullptr;2284 generateValueRangeStartAndEnd(2285 /*opClass=*/isGenericAdaptorBase ? *genericAdaptorBase : opClass,2286 isGenericAdaptorBase,2287 /*methodName=*/"getODSOperandIndexAndLength", numVariadicOperands,2288 numNormalOperands, rangeSizeCall, attrSizedOperands, sizeAttrInit,2289 const_cast<Operator &>(op).getOperands());2290 if (isGenericAdaptorBase) {2291 // Generate trampoline for calling 'getODSOperandIndexAndLength' with just2292 // the index. This just calls the implementation in the base class but2293 // passes the operand size as parameter.2294 Method *method = opClass.addInlineMethod(2295 "std::pair<unsigned, unsigned>", "getODSOperandIndexAndLength",2296 MethodParameter("unsigned", "index"));2297 ERROR_IF_PRUNED(method, "getODSOperandIndexAndLength", op);2298 MethodBody &body = method->body();2299 body.indent() << formatv(2300 "return Base::getODSOperandIndexAndLength(index, {0});", rangeSizeCall);2301 }2302 2303 // The implementation of this method is trivial and it is very load-bearing.2304 // Generate it inline.2305 auto *m = opClass.addInlineMethod(rangeType, "getODSOperands",2306 MethodParameter("unsigned", "index"));2307 ERROR_IF_PRUNED(m, "getODSOperands", op);2308 auto &body = m->body();2309 body << formatv(valueRangeReturnCode, rangeBeginCall,2310 "getODSOperandIndexAndLength(index)");2311 2312 // Then we emit nicer named getter methods by redirecting to the "sink" getter2313 // method.2314 for (int i = 0; i != numOperands; ++i) {2315 const auto &operand = op.getOperand(i);2316 if (operand.name.empty())2317 continue;2318 std::string name = op.getGetterName(operand.name);2319 if (operand.isOptional()) {2320 m = opClass.addInlineMethod(isGenericAdaptorBase2321 ? rangeElementType2322 : generateTypeForGetter(operand),2323 name);2324 ERROR_IF_PRUNED(m, name, op);2325 m->body().indent() << formatv("auto operands = getODSOperands({0});\n"2326 "return operands.empty() ? {1}{{} : ",2327 i, m->getReturnType());2328 if (!isGenericAdaptorBase)2329 m->body() << llvm::formatv("::llvm::cast<{0}>", m->getReturnType());2330 m->body() << "(*operands.begin());";2331 } else if (operand.isVariadicOfVariadic()) {2332 std::string segmentAttr = op.getGetterName(2333 operand.constraint.getVariadicOfVariadicSegmentSizeAttr());2334 if (genericAdaptorBase) {2335 m = opClass.addMethod("::llvm::SmallVector<" + rangeType + ">", name);2336 ERROR_IF_PRUNED(m, name, op);2337 m->body() << llvm::formatv(variadicOfVariadicAdaptorCalcCode,2338 segmentAttr, i, rangeType);2339 continue;2340 }2341 2342 m = opClass.addInlineMethod("::mlir::OperandRangeRange", name);2343 ERROR_IF_PRUNED(m, name, op);2344 m->body() << " return getODSOperands(" << i << ").split(" << segmentAttr2345 << "Attr());";2346 } else if (operand.isVariadic()) {2347 m = opClass.addInlineMethod(rangeType, name);2348 ERROR_IF_PRUNED(m, name, op);2349 m->body() << " return getODSOperands(" << i << ");";2350 } else {2351 m = opClass.addInlineMethod(isGenericAdaptorBase2352 ? rangeElementType2353 : generateTypeForGetter(operand),2354 name);2355 ERROR_IF_PRUNED(m, name, op);2356 m->body().indent() << "return ";2357 if (!isGenericAdaptorBase)2358 m->body() << llvm::formatv("::llvm::cast<{0}>", m->getReturnType());2359 m->body() << llvm::formatv("(*getODSOperands({0}).begin());", i);2360 }2361 }2362}2363 2364void OpEmitter::genNamedOperandGetters() {2365 // Build the code snippet used for initializing the operand_segment_size)s2366 // array.2367 std::string attrSizeInitCode;2368 if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {2369 if (op.getDialect().usePropertiesForAttributes())2370 attrSizeInitCode = formatv(adapterSegmentSizeAttrInitCodeProperties,2371 "getProperties().operandSegmentSizes");2372 2373 else2374 attrSizeInitCode = formatv(opSegmentSizeAttrInitCode,2375 emitHelper.getAttr(operandSegmentAttrName));2376 }2377 2378 generateNamedOperandGetters(2379 op, opClass,2380 /*genericAdaptorBase=*/nullptr,2381 /*sizeAttrInit=*/attrSizeInitCode,2382 /*rangeType=*/"::mlir::Operation::operand_range",2383 /*rangeElementType=*/"::mlir::Value",2384 /*rangeBeginCall=*/"getOperation()->operand_begin()",2385 /*rangeSizeCall=*/"getOperation()->getNumOperands()",2386 /*getOperandCallPattern=*/"getOperation()->getOperand({0})");2387}2388 2389void OpEmitter::genNamedOperandSetters() {2390 auto *attrSizedOperands =2391 op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");2392 for (int i = 0, e = op.getNumOperands(); i != e; ++i) {2393 const auto &operand = op.getOperand(i);2394 if (operand.name.empty())2395 continue;2396 std::string name = op.getGetterName(operand.name);2397 2398 StringRef returnType;2399 if (operand.isVariadicOfVariadic()) {2400 returnType = "::mlir::MutableOperandRangeRange";2401 } else if (operand.isVariableLength()) {2402 returnType = "::mlir::MutableOperandRange";2403 } else {2404 returnType = "::mlir::OpOperand &";2405 }2406 bool isVariadicOperand =2407 operand.isVariadicOfVariadic() || operand.isVariableLength();2408 auto *m = opClass.addMethod(returnType, name + "Mutable",2409 isVariadicOperand ? Method::Properties::None2410 : Method::Properties::Inline);2411 ERROR_IF_PRUNED(m, name, op);2412 auto &body = m->body();2413 body << " auto range = getODSOperandIndexAndLength(" << i << ");\n";2414 2415 if (!isVariadicOperand) {2416 // In case of a single operand, return a single OpOperand.2417 body << " return getOperation()->getOpOperand(range.first);\n";2418 continue;2419 }2420 2421 body << " auto mutableRange = "2422 "::mlir::MutableOperandRange(getOperation(), "2423 "range.first, range.second";2424 if (attrSizedOperands) {2425 if (emitHelper.hasProperties())2426 body << formatv(", ::mlir::MutableOperandRange::OperandSegment({0}u, "2427 "{{getOperandSegmentSizesAttrName(), "2428 "::mlir::DenseI32ArrayAttr::get(getContext(), "2429 "getProperties().operandSegmentSizes)})",2430 i);2431 else2432 body << formatv(2433 ", ::mlir::MutableOperandRange::OperandSegment({0}u, *{1})", i,2434 emitHelper.getAttr(operandSegmentAttrName, /*isNamed=*/true));2435 }2436 body << ");\n";2437 2438 // If this operand is a nested variadic, we split the range into a2439 // MutableOperandRangeRange that provides a range over all of the2440 // sub-ranges.2441 if (operand.isVariadicOfVariadic()) {2442 body << " return "2443 "mutableRange.split(*(*this)->getAttrDictionary().getNamed("2444 << op.getGetterName(2445 operand.constraint.getVariadicOfVariadicSegmentSizeAttr())2446 << "AttrName()));\n";2447 } else {2448 // Otherwise, we use the full range directly.2449 body << " return mutableRange;\n";2450 }2451 }2452}2453 2454void OpEmitter::genNamedResultGetters() {2455 const int numResults = op.getNumResults();2456 const int numVariadicResults = op.getNumVariableLengthResults();2457 const int numNormalResults = numResults - numVariadicResults;2458 2459 // If we have more than one variadic results, we need more complicated logic2460 // to calculate the value range for each result.2461 2462 const auto *sameVariadicSize =2463 op.getTrait("::mlir::OpTrait::SameVariadicResultSize");2464 const auto *attrSizedResults =2465 op.getTrait("::mlir::OpTrait::AttrSizedResultSegments");2466 2467 if (numVariadicResults > 1 && !sameVariadicSize && !attrSizedResults) {2468 PrintFatalError(op.getLoc(), "op has multiple variadic results but no "2469 "specification over their sizes");2470 }2471 2472 if (numVariadicResults < 2 && attrSizedResults) {2473 PrintFatalError(op.getLoc(), "op must have at least two variadic results "2474 "to use 'AttrSizedResultSegments' trait");2475 }2476 2477 if (attrSizedResults && sameVariadicSize) {2478 PrintFatalError(op.getLoc(),2479 "op cannot have both 'AttrSizedResultSegments' and "2480 "'SameVariadicResultSize' traits");2481 }2482 2483 // Build the initializer string for the result segment size attribute.2484 std::string attrSizeInitCode;2485 if (attrSizedResults) {2486 if (op.getDialect().usePropertiesForAttributes())2487 attrSizeInitCode = formatv(adapterSegmentSizeAttrInitCodeProperties,2488 "getProperties().resultSegmentSizes");2489 2490 else2491 attrSizeInitCode = formatv(opSegmentSizeAttrInitCode,2492 emitHelper.getAttr(resultSegmentAttrName));2493 }2494 2495 generateValueRangeStartAndEnd(2496 opClass, /*isGenericAdaptorBase=*/false, "getODSResultIndexAndLength",2497 numVariadicResults, numNormalResults, "getOperation()->getNumResults()",2498 attrSizedResults, attrSizeInitCode, op.getResults());2499 2500 // The implementation of this method is trivial and it is very load-bearing.2501 // Generate it inline.2502 auto *m = opClass.addInlineMethod("::mlir::Operation::result_range",2503 "getODSResults",2504 MethodParameter("unsigned", "index"));2505 ERROR_IF_PRUNED(m, "getODSResults", op);2506 m->body() << formatv(valueRangeReturnCode, "getOperation()->result_begin()",2507 "getODSResultIndexAndLength(index)");2508 2509 for (int i = 0; i != numResults; ++i) {2510 const auto &result = op.getResult(i);2511 if (result.name.empty())2512 continue;2513 std::string name = op.getGetterName(result.name);2514 if (result.isOptional()) {2515 m = opClass.addInlineMethod(generateTypeForGetter(result), name);2516 ERROR_IF_PRUNED(m, name, op);2517 m->body() << " auto results = getODSResults(" << i << ");\n"2518 << llvm::formatv(" return results.empty()"2519 " ? {0}()"2520 " : ::llvm::cast<{0}>(*results.begin());",2521 m->getReturnType());2522 } else if (result.isVariadic()) {2523 m = opClass.addInlineMethod("::mlir::Operation::result_range", name);2524 ERROR_IF_PRUNED(m, name, op);2525 m->body() << " return getODSResults(" << i << ");";2526 } else {2527 m = opClass.addInlineMethod(generateTypeForGetter(result), name);2528 ERROR_IF_PRUNED(m, name, op);2529 m->body() << llvm::formatv(2530 " return ::llvm::cast<{0}>(*getODSResults({1}).begin());",2531 m->getReturnType(), i);2532 }2533 }2534}2535 2536void OpEmitter::genNamedRegionGetters() {2537 unsigned numRegions = op.getNumRegions();2538 for (unsigned i = 0; i < numRegions; ++i) {2539 const auto ®ion = op.getRegion(i);2540 if (region.name.empty())2541 continue;2542 std::string name = op.getGetterName(region.name);2543 2544 // Generate the accessors for a variadic region.2545 if (region.isVariadic()) {2546 auto *m = opClass.addInlineMethod(2547 "::mlir::MutableArrayRef<::mlir::Region>", name);2548 ERROR_IF_PRUNED(m, name, op);2549 m->body() << formatv(" return (*this)->getRegions().drop_front({0});",2550 i);2551 continue;2552 }2553 2554 auto *m = opClass.addInlineMethod("::mlir::Region &", name);2555 ERROR_IF_PRUNED(m, name, op);2556 m->body() << formatv(" return (*this)->getRegion({0});", i);2557 }2558}2559 2560void OpEmitter::genNamedSuccessorGetters() {2561 unsigned numSuccessors = op.getNumSuccessors();2562 for (unsigned i = 0; i < numSuccessors; ++i) {2563 const NamedSuccessor &successor = op.getSuccessor(i);2564 if (successor.name.empty())2565 continue;2566 std::string name = op.getGetterName(successor.name);2567 // Generate the accessors for a variadic successor list.2568 if (successor.isVariadic()) {2569 auto *m = opClass.addInlineMethod("::mlir::SuccessorRange", name);2570 ERROR_IF_PRUNED(m, name, op);2571 m->body() << formatv(2572 " return {std::next((*this)->successor_begin(), {0}), "2573 "(*this)->successor_end()};",2574 i);2575 continue;2576 }2577 2578 auto *m = opClass.addInlineMethod("::mlir::Block *", name);2579 ERROR_IF_PRUNED(m, name, op);2580 m->body() << formatv(" return (*this)->getSuccessor({0});", i);2581 }2582}2583 2584static bool canGenerateUnwrappedBuilder(const Operator &op) {2585 // If this op does not have native attributes at all, return directly to avoid2586 // redefining builders.2587 if (op.getNumNativeAttributes() == 0)2588 return false;2589 2590 bool canGenerate = false;2591 // We are generating builders that take raw values for attributes. We need to2592 // make sure the native attributes have a meaningful "unwrapped" value type2593 // different from the wrapped mlir::Attribute type to avoid redefining2594 // builders. This checks for the op has at least one such native attribute.2595 for (int i = 0, e = op.getNumNativeAttributes(); i < e; ++i) {2596 const NamedAttribute &namedAttr = op.getAttribute(i);2597 if (canUseUnwrappedRawValue(namedAttr.attr)) {2598 canGenerate = true;2599 break;2600 }2601 }2602 return canGenerate;2603}2604 2605static bool canInferType(const Operator &op) {2606 return op.getTrait("::mlir::InferTypeOpInterface::Trait");2607}2608 2609void OpEmitter::genInlineCreateBody(2610 const SmallVector<MethodParameter> ¶mList) {2611 SmallVector<MethodParameter> createParamListOpBuilder;2612 SmallVector<MethodParameter> createParamListImplicitLocOpBuilder;2613 SmallVector<llvm::StringRef, 4> nonBuilderStateArgsList;2614 createParamListOpBuilder.emplace_back("::mlir::OpBuilder &", "builder");2615 createParamListImplicitLocOpBuilder.emplace_back(2616 "::mlir::ImplicitLocOpBuilder &", "builder");2617 std::string locParamName = "location";2618 while (llvm::find_if(paramList, [&locParamName](const MethodParameter &p) {2619 return p.getName() == locParamName;2620 }) != paramList.end()) {2621 locParamName += "_";2622 }2623 createParamListOpBuilder.emplace_back("::mlir::Location", locParamName);2624 2625 for (auto ¶m : paramList) {2626 if (param.getType() == "::mlir::OpBuilder &" ||2627 param.getType() == "::mlir::OperationState &")2628 continue;2629 createParamListOpBuilder.emplace_back(param.getType(), param.getName(),2630 param.getDefaultValue(),2631 param.isOptional());2632 createParamListImplicitLocOpBuilder.emplace_back(2633 param.getType(), param.getName(), param.getDefaultValue(),2634 param.isOptional());2635 nonBuilderStateArgsList.push_back(param.getName());2636 }2637 auto *cWithLoc = opClass.addStaticMethod(opClass.getClassName(), "create",2638 createParamListOpBuilder);2639 auto *cImplicitLoc = opClass.addStaticMethod(2640 opClass.getClassName(), "create", createParamListImplicitLocOpBuilder);2641 std::string nonBuilderStateArgs = "";2642 if (!nonBuilderStateArgsList.empty()) {2643 llvm::raw_string_ostream nonBuilderStateArgsOS(nonBuilderStateArgs);2644 interleaveComma(nonBuilderStateArgsList, nonBuilderStateArgsOS);2645 nonBuilderStateArgs = ", " + nonBuilderStateArgs;2646 }2647 if (cWithLoc)2648 cWithLoc->body() << llvm::formatv(inlineCreateBody, locParamName,2649 nonBuilderStateArgs,2650 opClass.getClassName());2651 if (cImplicitLoc)2652 cImplicitLoc->body() << llvm::formatv(inlineCreateBodyImplicitLoc,2653 nonBuilderStateArgs);2654}2655 2656void OpEmitter::genSeparateArgParamBuilder() {2657 SmallVector<AttrParamKind, 2> attrBuilderType;2658 attrBuilderType.push_back(AttrParamKind::WrappedAttr);2659 if (canGenerateUnwrappedBuilder(op))2660 attrBuilderType.push_back(AttrParamKind::UnwrappedValue);2661 2662 // Emit with separate builders with or without unwrapped attributes and/or2663 // inferring result type.2664 auto emit = [&](AttrParamKind attrType, TypeParamKind paramKind,2665 bool inferType) {2666 SmallVector<MethodParameter> paramList;2667 SmallVector<std::string, 4> resultNames;2668 llvm::StringSet<> inferredAttributes;2669 buildParamList(paramList, inferredAttributes, resultNames, paramKind,2670 attrType);2671 2672 auto *m = opClass.addStaticMethod("void", "build", paramList);2673 // If the builder is redundant, skip generating the method.2674 if (!m)2675 return;2676 genInlineCreateBody(paramList);2677 2678 auto &body = m->body();2679 genCodeForAddingArgAndRegionForBuilder(body, inferredAttributes,2680 /*isRawValueAttr=*/attrType ==2681 AttrParamKind::UnwrappedValue);2682 2683 // Push all result types to the operation state2684 2685 if (inferType) {2686 // Generate builder that infers type too.2687 // TODO: Subsume this with general checking if type can be2688 // inferred automatically.2689 body << formatv(R"(2690 ::llvm::SmallVector<::mlir::Type, 2> inferredReturnTypes;2691 if (::mlir::succeeded({0}::inferReturnTypes(odsBuilder.getContext(),2692 {1}.location, {1}.operands,2693 {1}.attributes.getDictionary({1}.getContext()),2694 {1}.getRawProperties(),2695 {1}.regions, inferredReturnTypes)))2696 {1}.addTypes(inferredReturnTypes);2697 else2698 ::mlir::detail::reportFatalInferReturnTypesError({1});2699 )",2700 opClass.getClassName(), builderOpState);2701 return;2702 }2703 2704 switch (paramKind) {2705 case TypeParamKind::None:2706 return;2707 case TypeParamKind::Separate:2708 for (int i = 0, e = op.getNumResults(); i < e; ++i) {2709 if (op.getResult(i).isOptional())2710 body << " if (" << resultNames[i] << ")\n ";2711 body << " " << builderOpState << ".addTypes(" << resultNames[i]2712 << ");\n";2713 }2714 2715 // Automatically create the 'resultSegmentSizes' attribute using2716 // the length of the type ranges.2717 if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {2718 if (op.getDialect().usePropertiesForAttributes()) {2719 body << " ::llvm::copy(::llvm::ArrayRef<int32_t>({";2720 } else {2721 std::string getterName = op.getGetterName(resultSegmentAttrName);2722 body << " " << builderOpState << ".addAttribute(" << getterName2723 << "AttrName(" << builderOpState << ".name), "2724 << "odsBuilder.getDenseI32ArrayAttr({";2725 }2726 interleaveComma(2727 llvm::seq<int>(0, op.getNumResults()), body, [&](int i) {2728 const NamedTypeConstraint &result = op.getResult(i);2729 if (!result.isVariableLength()) {2730 body << "1";2731 } else if (result.isOptional()) {2732 body << "(" << resultNames[i] << " ? 1 : 0)";2733 } else {2734 // VariadicOfVariadic of results are currently unsupported in2735 // MLIR, hence it can only be a simple variadic.2736 // TODO: Add implementation for VariadicOfVariadic results here2737 // once supported.2738 assert(result.isVariadic());2739 body << "static_cast<int32_t>(" << resultNames[i] << ".size())";2740 }2741 });2742 if (op.getDialect().usePropertiesForAttributes()) {2743 body << "}), " << builderOpState2744 << ".getOrAddProperties<Properties>()."2745 "resultSegmentSizes.begin());\n";2746 } else {2747 body << "}));\n";2748 }2749 }2750 2751 return;2752 case TypeParamKind::Collective: {2753 int numResults = op.getNumResults();2754 int numVariadicResults = op.getNumVariableLengthResults();2755 int numNonVariadicResults = numResults - numVariadicResults;2756 bool hasVariadicResult = numVariadicResults != 0;2757 2758 // Avoid emitting "resultTypes.size() >= 0u" which is always true.2759 if (!hasVariadicResult || numNonVariadicResults != 0)2760 body << " " << "assert(resultTypes.size() "2761 << (hasVariadicResult ? ">=" : "==") << " "2762 << numNonVariadicResults2763 << "u && \"mismatched number of results\");\n";2764 body << " " << builderOpState << ".addTypes(resultTypes);\n";2765 }2766 return;2767 }2768 llvm_unreachable("unhandled TypeParamKind");2769 };2770 2771 // Some of the build methods generated here may be ambiguous, but TableGen's2772 // ambiguous function detection will elide those ones.2773 for (auto attrType : attrBuilderType) {2774 emit(attrType, TypeParamKind::Separate, /*inferType=*/false);2775 if (canInferType(op))2776 emit(attrType, TypeParamKind::None, /*inferType=*/true);2777 emit(attrType, TypeParamKind::Collective, /*inferType=*/false);2778 }2779}2780 2781void OpEmitter::genUseOperandAsResultTypeCollectiveParamBuilder(2782 CollectiveBuilderKind kind) {2783 int numResults = op.getNumResults();2784 2785 // Signature2786 SmallVector<MethodParameter> paramList;2787 paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder");2788 paramList.emplace_back("::mlir::OperationState &", builderOpState);2789 paramList.emplace_back("::mlir::ValueRange", "operands");2790 if (kind == CollectiveBuilderKind::PropStruct)2791 paramList.emplace_back("const Properties &", "properties");2792 // Provide default value for `attributes` when its the last parameter2793 StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}";2794 StringRef attributesName = kind == CollectiveBuilderKind::PropStruct2795 ? "discardableAttributes"2796 : "attributes";2797 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",2798 attributesName, attributesDefaultValue);2799 if (op.getNumVariadicRegions())2800 paramList.emplace_back("unsigned", "numRegions");2801 2802 auto *m = opClass.addStaticMethod("void", "build", paramList);2803 // If the builder is redundant, skip generating the method2804 if (!m)2805 return;2806 genInlineCreateBody(paramList);2807 auto &body = m->body();2808 2809 // Operands2810 body << " " << builderOpState << ".addOperands(operands);\n";2811 2812 if (kind == CollectiveBuilderKind::PropStruct)2813 body << " " << builderOpState2814 << ".useProperties(const_cast<Properties&>(properties));\n";2815 // Attributes2816 body << " " << builderOpState << ".addAttributes(" << attributesName2817 << ");\n";2818 2819 // Create the correct number of regions2820 if (int numRegions = op.getNumRegions()) {2821 body << llvm::formatv(2822 " for (unsigned i = 0; i != {0}; ++i)\n",2823 (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions)));2824 body << " (void)" << builderOpState << ".addRegion();\n";2825 }2826 2827 // Result types2828 SmallVector<std::string, 2> resultTypes(numResults, "operands[0].getType()");2829 body << " " << builderOpState << ".addTypes({"2830 << llvm::join(resultTypes, ", ") << "});\n\n";2831}2832 2833void OpEmitter::genPopulateDefaultAttributes() {2834 // All done if no attributes, except optional ones, have default values.2835 if (llvm::all_of(op.getAttributes(), [](const NamedAttribute &named) {2836 return !named.attr.hasDefaultValue() || named.attr.isOptional();2837 }))2838 return;2839 2840 if (emitHelper.hasProperties()) {2841 SmallVector<MethodParameter> paramList;2842 paramList.emplace_back("::mlir::OperationName", "opName");2843 paramList.emplace_back("Properties &", "properties");2844 auto *m =2845 opClass.addStaticMethod("void", "populateDefaultProperties", paramList);2846 ERROR_IF_PRUNED(m, "populateDefaultProperties", op);2847 auto &body = m->body();2848 body.indent();2849 body << "::mlir::Builder " << odsBuilder << "(opName.getContext());\n";2850 for (const NamedAttribute &namedAttr : op.getAttributes()) {2851 auto &attr = namedAttr.attr;2852 if (!attr.hasDefaultValue() || attr.isOptional())2853 continue;2854 StringRef name = namedAttr.name;2855 FmtContext fctx;2856 fctx.withBuilder(odsBuilder);2857 body << "if (!properties." << name << ")\n"2858 << " properties." << name << " = "2859 << std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,2860 tgfmt(attr.getDefaultValue(), &fctx)))2861 << ";\n";2862 }2863 return;2864 }2865 2866 SmallVector<MethodParameter> paramList;2867 paramList.emplace_back("const ::mlir::OperationName &", "opName");2868 paramList.emplace_back("::mlir::NamedAttrList &", "attributes");2869 auto *m = opClass.addStaticMethod("void", "populateDefaultAttrs", paramList);2870 ERROR_IF_PRUNED(m, "populateDefaultAttrs", op);2871 auto &body = m->body();2872 body.indent();2873 2874 // Set default attributes that are unset.2875 body << "auto attrNames = opName.getAttributeNames();\n";2876 body << "::mlir::Builder " << odsBuilder2877 << "(attrNames.front().getContext());\n";2878 StringMap<int> attrIndex;2879 for (const auto &it : llvm::enumerate(emitHelper.getAttrMetadata())) {2880 attrIndex[it.value().first] = it.index();2881 }2882 for (const NamedAttribute &namedAttr : op.getAttributes()) {2883 auto &attr = namedAttr.attr;2884 if (!attr.hasDefaultValue() || attr.isOptional())2885 continue;2886 auto index = attrIndex[namedAttr.name];2887 body << "if (!attributes.get(attrNames[" << index << "])) {\n";2888 FmtContext fctx;2889 fctx.withBuilder(odsBuilder);2890 2891 std::string defaultValue =2892 std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,2893 tgfmt(attr.getDefaultValue(), &fctx)));2894 body.indent() << formatv("attributes.append(attrNames[{0}], {1});\n", index,2895 defaultValue);2896 body.unindent() << "}\n";2897 }2898}2899 2900void OpEmitter::genInferredTypeCollectiveParamBuilder(2901 CollectiveBuilderKind kind) {2902 SmallVector<MethodParameter> paramList;2903 paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder");2904 paramList.emplace_back("::mlir::OperationState &", builderOpState);2905 paramList.emplace_back("::mlir::ValueRange", "operands");2906 if (kind == CollectiveBuilderKind::PropStruct)2907 paramList.emplace_back("const Properties &", "properties");2908 StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}";2909 StringRef attributesName = kind == CollectiveBuilderKind::PropStruct2910 ? "discardableAttributes"2911 : "attributes";2912 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",2913 attributesName, attributesDefaultValue);2914 if (op.getNumVariadicRegions())2915 paramList.emplace_back("unsigned", "numRegions");2916 2917 auto *m = opClass.addStaticMethod("void", "build", paramList);2918 // If the builder is redundant, skip generating the method2919 if (!m)2920 return;2921 genInlineCreateBody(paramList);2922 auto &body = m->body();2923 2924 int numResults = op.getNumResults();2925 int numVariadicResults = op.getNumVariableLengthResults();2926 int numNonVariadicResults = numResults - numVariadicResults;2927 2928 int numOperands = op.getNumOperands();2929 int numVariadicOperands = op.getNumVariableLengthOperands();2930 int numNonVariadicOperands = numOperands - numVariadicOperands;2931 2932 // Operands2933 if (numVariadicOperands == 0 || numNonVariadicOperands != 0)2934 body << " assert(operands.size()"2935 << (numVariadicOperands != 0 ? " >= " : " == ")2936 << numNonVariadicOperands2937 << "u && \"mismatched number of parameters\");\n";2938 body << " " << builderOpState << ".addOperands(operands);\n";2939 if (kind == CollectiveBuilderKind::PropStruct)2940 body << " " << builderOpState2941 << ".useProperties(const_cast<Properties &>(properties));\n";2942 body << " " << builderOpState << ".addAttributes(" << attributesName2943 << ");\n";2944 2945 // Create the correct number of regions2946 if (int numRegions = op.getNumRegions()) {2947 body << llvm::formatv(2948 " for (unsigned i = 0; i != {0}; ++i)\n",2949 (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions)));2950 body << " (void)" << builderOpState << ".addRegion();\n";2951 }2952 2953 // Result types2954 if (emitHelper.hasNonEmptyPropertiesStruct() &&2955 kind == CollectiveBuilderKind::AttrDict) {2956 // Initialize the properties from Attributes before invoking the infer2957 // function.2958 body << formatv(R"(2959 if (!attributes.empty()) {2960 ::mlir::OpaqueProperties properties =2961 &{1}.getOrAddProperties<{0}::Properties>();2962 std::optional<::mlir::RegisteredOperationName> info =2963 {1}.name.getRegisteredInfo();2964 if (failed(info->setOpPropertiesFromAttribute({1}.name, properties,2965 {1}.attributes.getDictionary({1}.getContext()), nullptr)))2966 ::llvm::report_fatal_error("Property conversion failed.");2967 })",2968 opClass.getClassName(), builderOpState);2969 }2970 body << formatv(R"(2971 ::llvm::SmallVector<::mlir::Type, 2> inferredReturnTypes;2972 if (::mlir::succeeded({0}::inferReturnTypes(odsBuilder.getContext(),2973 {1}.location, operands,2974 {1}.attributes.getDictionary({1}.getContext()),2975 {1}.getRawProperties(),2976 {1}.regions, inferredReturnTypes))) {{)",2977 opClass.getClassName(), builderOpState);2978 if (numVariadicResults == 0 || numNonVariadicResults != 0)2979 body << "\n assert(inferredReturnTypes.size()"2980 << (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults2981 << "u && \"mismatched number of return types\");";2982 body << "\n " << builderOpState << ".addTypes(inferredReturnTypes);";2983 2984 body << R"(2985 } else {2986 ::llvm::report_fatal_error("Failed to infer result type(s).");2987 })";2988}2989 2990void OpEmitter::genUseOperandAsResultTypeSeparateParamBuilder() {2991 auto emit = [&](AttrParamKind attrType) {2992 SmallVector<MethodParameter> paramList;2993 SmallVector<std::string, 4> resultNames;2994 llvm::StringSet<> inferredAttributes;2995 buildParamList(paramList, inferredAttributes, resultNames,2996 TypeParamKind::None, attrType);2997 2998 auto *m = opClass.addStaticMethod("void", "build", paramList);2999 // If the builder is redundant, skip generating the method3000 if (!m)3001 return;3002 genInlineCreateBody(paramList);3003 auto &body = m->body();3004 genCodeForAddingArgAndRegionForBuilder(body, inferredAttributes,3005 /*isRawValueAttr=*/attrType ==3006 AttrParamKind::UnwrappedValue);3007 3008 auto numResults = op.getNumResults();3009 if (numResults == 0)3010 return;3011 3012 // Push all result types to the operation state3013 const char *index = op.getOperand(0).isVariadic() ? ".front()" : "";3014 std::string resultType =3015 formatv("{0}{1}.getType()", getArgumentName(op, 0), index).str();3016 body << " " << builderOpState << ".addTypes({" << resultType;3017 for (int i = 1; i != numResults; ++i)3018 body << ", " << resultType;3019 body << "});\n\n";3020 };3021 3022 emit(AttrParamKind::WrappedAttr);3023 // Generate additional builder(s) if any attributes can be "unwrapped"3024 if (canGenerateUnwrappedBuilder(op))3025 emit(AttrParamKind::UnwrappedValue);3026}3027 3028void OpEmitter::genUseAttrAsResultTypeCollectiveParamBuilder(3029 CollectiveBuilderKind kind) {3030 SmallVector<MethodParameter> paramList;3031 paramList.emplace_back("::mlir::OpBuilder &", "odsBuilder");3032 paramList.emplace_back("::mlir::OperationState &", builderOpState);3033 paramList.emplace_back("::mlir::ValueRange", "operands");3034 if (kind == CollectiveBuilderKind::PropStruct)3035 paramList.emplace_back("const Properties &", "properties");3036 StringRef attributesName = kind == CollectiveBuilderKind::PropStruct3037 ? "discardableAttributes"3038 : "attributes";3039 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",3040 attributesName, "{}");3041 auto *m = opClass.addStaticMethod("void", "build", paramList);3042 // If the builder is redundant, skip generating the method3043 if (!m)3044 return;3045 genInlineCreateBody(paramList);3046 3047 auto &body = m->body();3048 3049 // Push all result types to the operation state3050 std::string resultType;3051 const auto &namedAttr = op.getAttribute(0);3052 3053 if (namedAttr.attr.isTypeAttr()) {3054 resultType = "::llvm::cast<::mlir::TypeAttr>(typeAttr).getValue()";3055 } else {3056 resultType = "::llvm::cast<::mlir::TypedAttr>(typeAttr).getType()";3057 }3058 3059 if (kind == CollectiveBuilderKind::PropStruct) {3060 body << " ::mlir::Attribute typeAttr = properties."3061 << op.getGetterName(namedAttr.name) << "();\n";3062 } else {3063 body << " ::mlir::Attribute typeAttr;\n"3064 << " auto attrName = " << op.getGetterName(namedAttr.name)3065 << "AttrName(" << builderOpState3066 << ".name);\n"3067 " for (auto attr : attributes) {\n"3068 " if (attr.getName() == attrName) {\n"3069 " typeAttr = attr.getValue();\n"3070 " break;\n"3071 " }\n"3072 " }\n";3073 }3074 3075 // Operands3076 body << " " << builderOpState << ".addOperands(operands);\n";3077 3078 // Properties3079 if (kind == CollectiveBuilderKind::PropStruct)3080 body << " " << builderOpState3081 << ".useProperties(const_cast<Properties&>(properties));\n";3082 3083 // Attributes3084 body << " " << builderOpState << ".addAttributes(" << attributesName3085 << ");\n";3086 3087 // Result types3088 SmallVector<std::string, 2> resultTypes(op.getNumResults(), resultType);3089 body << " " << builderOpState << ".addTypes({"3090 << llvm::join(resultTypes, ", ") << "});\n";3091}3092 3093/// Returns a signature of the builder. Updates the context `fctx` to enable3094/// replacement of $_builder and $_state in the body.3095static SmallVector<MethodParameter>3096getBuilderSignature(const Builder &builder) {3097 ArrayRef<Builder::Parameter> params(builder.getParameters());3098 3099 // Inject builder and state arguments.3100 SmallVector<MethodParameter> arguments;3101 arguments.reserve(params.size() + 2);3102 arguments.emplace_back("::mlir::OpBuilder &", odsBuilder);3103 arguments.emplace_back("::mlir::OperationState &", builderOpState);3104 3105 FmtContext fctx;3106 fctx.withBuilder(odsBuilder);3107 3108 for (unsigned i = 0, e = params.size(); i < e; ++i) {3109 // If no name is provided, generate one.3110 std::optional<StringRef> paramName = params[i].getName();3111 std::string name =3112 paramName ? paramName->str() : "odsArg" + std::to_string(i);3113 3114 StringRef defaultValue;3115 if (std::optional<StringRef> defaultParamValue =3116 params[i].getDefaultValue())3117 defaultValue = *defaultParamValue;3118 3119 arguments.emplace_back(params[i].getCppType(), std::move(name),3120 tgfmt(defaultValue, &fctx));3121 }3122 3123 return arguments;3124}3125 3126void OpEmitter::genBuilder() {3127 // Handle custom builders if provided.3128 for (const Builder &builder : op.getBuilders()) {3129 SmallVector<MethodParameter> arguments = getBuilderSignature(builder);3130 3131 std::optional<StringRef> body = builder.getBody();3132 auto properties = body ? Method::Static : Method::StaticDeclaration;3133 auto *method = opClass.addMethod("void", "build", properties, arguments);3134 3135 ERROR_IF_PRUNED(method, "build", op);3136 3137 if (method)3138 method->setDeprecated(builder.getDeprecatedMessage());3139 3140 FmtContext fctx;3141 fctx.withBuilder(odsBuilder);3142 fctx.addSubst("_state", builderOpState);3143 if (body)3144 method->body() << tgfmt(*body, &fctx);3145 genInlineCreateBody(arguments);3146 }3147 3148 // Generate default builders that requires all result type, operands, and3149 // attributes as parameters.3150 if (op.skipDefaultBuilders())3151 return;3152 3153 // We generate three classes of builders here:3154 // 1. one having a stand-alone parameter for each operand / attribute, and3155 genSeparateArgParamBuilder();3156 // 2. one having an aggregated parameter for all result types / operands /3157 // [properties / discardable] attributes, and3158 genCollectiveParamBuilder(CollectiveBuilderKind::AttrDict);3159 if (emitHelper.hasProperties())3160 genCollectiveParamBuilder(CollectiveBuilderKind::PropStruct);3161 // 3. one having a stand-alone parameter for each operand and attribute,3162 // use the first operand or attribute's type as all result types3163 // to facilitate different call patterns.3164 if (op.getNumVariableLengthResults() == 0) {3165 if (op.getTrait("::mlir::OpTrait::SameOperandsAndResultType")) {3166 genUseOperandAsResultTypeSeparateParamBuilder();3167 genUseOperandAsResultTypeCollectiveParamBuilder(3168 CollectiveBuilderKind::AttrDict);3169 if (emitHelper.hasProperties())3170 genUseOperandAsResultTypeCollectiveParamBuilder(3171 CollectiveBuilderKind::PropStruct);3172 }3173 if (op.getTrait("::mlir::OpTrait::FirstAttrDerivedResultType")) {3174 genUseAttrAsResultTypeCollectiveParamBuilder(3175 CollectiveBuilderKind::AttrDict);3176 genUseAttrAsResultTypeCollectiveParamBuilder(3177 CollectiveBuilderKind::PropStruct);3178 }3179 }3180}3181 3182void OpEmitter::genCollectiveParamBuilder(CollectiveBuilderKind kind) {3183 int numResults = op.getNumResults();3184 int numVariadicResults = op.getNumVariableLengthResults();3185 int numNonVariadicResults = numResults - numVariadicResults;3186 3187 int numOperands = op.getNumOperands();3188 int numVariadicOperands = op.getNumVariableLengthOperands();3189 int numNonVariadicOperands = numOperands - numVariadicOperands;3190 3191 SmallVector<MethodParameter> paramList;3192 paramList.emplace_back("::mlir::OpBuilder &", "");3193 paramList.emplace_back("::mlir::OperationState &", builderOpState);3194 paramList.emplace_back("::mlir::TypeRange", "resultTypes");3195 paramList.emplace_back("::mlir::ValueRange", "operands");3196 if (kind == CollectiveBuilderKind::PropStruct)3197 paramList.emplace_back("const Properties &", "properties");3198 // Provide default value for `attributes` when its the last parameter3199 StringRef attributesDefaultValue = op.getNumVariadicRegions() ? "" : "{}";3200 StringRef attributesName = kind == CollectiveBuilderKind::PropStruct3201 ? "discardableAttributes"3202 : "attributes";3203 paramList.emplace_back("::llvm::ArrayRef<::mlir::NamedAttribute>",3204 attributesName, attributesDefaultValue);3205 if (op.getNumVariadicRegions())3206 paramList.emplace_back("unsigned", "numRegions");3207 3208 auto *m = opClass.addStaticMethod("void", "build", paramList);3209 // If the builder is redundant, skip generating the method3210 if (!m)3211 return;3212 genInlineCreateBody(paramList);3213 auto &body = m->body();3214 3215 // Operands3216 if (numVariadicOperands == 0 || numNonVariadicOperands != 0)3217 body << " assert(operands.size()"3218 << (numVariadicOperands != 0 ? " >= " : " == ")3219 << numNonVariadicOperands3220 << "u && \"mismatched number of parameters\");\n";3221 body << " " << builderOpState << ".addOperands(operands);\n";3222 3223 // Properties3224 if (kind == CollectiveBuilderKind::PropStruct)3225 body << " " << builderOpState3226 << ".useProperties(const_cast<Properties&>(properties));\n";3227 3228 // Attributes3229 body << " " << builderOpState << ".addAttributes(" << attributesName3230 << ");\n";3231 3232 // Create the correct number of regions3233 if (int numRegions = op.getNumRegions()) {3234 body << llvm::formatv(3235 " for (unsigned i = 0; i != {0}; ++i)\n",3236 (op.getNumVariadicRegions() ? "numRegions" : Twine(numRegions)));3237 body << " (void)" << builderOpState << ".addRegion();\n";3238 }3239 3240 // Result types3241 if (numVariadicResults == 0 || numNonVariadicResults != 0)3242 body << " assert(resultTypes.size()"3243 << (numVariadicResults != 0 ? " >= " : " == ") << numNonVariadicResults3244 << "u && \"mismatched number of return types\");\n";3245 body << " " << builderOpState << ".addTypes(resultTypes);\n";3246 3247 if (emitHelper.hasNonEmptyPropertiesStruct() &&3248 kind == CollectiveBuilderKind::AttrDict) {3249 // Initialize the properties from Attributes before invoking the infer3250 // function.3251 body << formatv(R"(3252 if (!attributes.empty()) {3253 ::mlir::OpaqueProperties properties =3254 &{1}.getOrAddProperties<{0}::Properties>();3255 std::optional<::mlir::RegisteredOperationName> info =3256 {1}.name.getRegisteredInfo();3257 if (failed(info->setOpPropertiesFromAttribute({1}.name, properties,3258 {1}.attributes.getDictionary({1}.getContext()), nullptr)))3259 ::llvm::report_fatal_error("Property conversion failed.");3260 })",3261 opClass.getClassName(), builderOpState);3262 }3263 3264 // Generate builder that infers type too.3265 // TODO: Expand to handle successors.3266 if (canInferType(op) && op.getNumSuccessors() == 0)3267 genInferredTypeCollectiveParamBuilder(kind);3268}3269 3270void OpEmitter::buildParamList(SmallVectorImpl<MethodParameter> ¶mList,3271 llvm::StringSet<> &inferredAttributes,3272 SmallVectorImpl<std::string> &resultTypeNames,3273 TypeParamKind typeParamKind,3274 AttrParamKind attrParamKind) {3275 resultTypeNames.clear();3276 auto numResults = op.getNumResults();3277 resultTypeNames.reserve(numResults);3278 3279 paramList.emplace_back("::mlir::OpBuilder &", odsBuilder);3280 paramList.emplace_back("::mlir::OperationState &", builderOpState);3281 3282 switch (typeParamKind) {3283 case TypeParamKind::None:3284 break;3285 case TypeParamKind::Separate: {3286 // Add parameters for all return types3287 for (int i = 0; i < numResults; ++i) {3288 const auto &result = op.getResult(i);3289 std::string resultName = std::string(result.name);3290 if (resultName.empty())3291 resultName = std::string(formatv("resultType{0}", i));3292 3293 StringRef type =3294 result.isVariadic() ? "::mlir::TypeRange" : "::mlir::Type";3295 3296 paramList.emplace_back(type, resultName, result.isOptional());3297 resultTypeNames.emplace_back(std::move(resultName));3298 }3299 } break;3300 case TypeParamKind::Collective: {3301 paramList.emplace_back("::mlir::TypeRange", "resultTypes");3302 resultTypeNames.push_back("resultTypes");3303 } break;3304 }3305 3306 // Add parameters for all arguments (operands and attributes).3307 // Track "attr-like" (property and attribute) optional values separate from3308 // attributes themselves so that the disambiguation code can look at the first3309 // attribute specifically when determining where to trim the optional-value3310 // list to avoid ambiguity while preserving the ability of all-property ops to3311 // use default parameters.3312 int defaultValuedAttrLikeStartIndex = op.getNumArgs();3313 int defaultValuedAttrStartIndex = op.getNumArgs();3314 // Successors and variadic regions go at the end of the parameter list, so no3315 // default arguments are possible.3316 bool hasTrailingParams = op.getNumSuccessors() || op.getNumVariadicRegions();3317 if (!hasTrailingParams) {3318 // Calculate the start index from which we can attach default values in the3319 // builder declaration.3320 for (int i = op.getNumArgs() - 1; i >= 0; --i) {3321 auto *namedAttr =3322 llvm::dyn_cast_if_present<tblgen::NamedAttribute *>(op.getArg(i));3323 auto *namedProperty =3324 llvm::dyn_cast_if_present<tblgen::NamedProperty *>(op.getArg(i));3325 if (namedProperty) {3326 Property prop = namedProperty->prop;3327 if (!prop.hasDefaultValue())3328 break;3329 defaultValuedAttrLikeStartIndex = i;3330 continue;3331 }3332 if (!namedAttr)3333 break;3334 3335 Attribute attr = namedAttr->attr;3336 // TODO: Currently we can't differentiate between optional meaning do not3337 // verify/not always error if missing or optional meaning need not be3338 // specified in builder. Expand isOptional once we can differentiate.3339 if (!attr.hasDefaultValue() && !attr.isDerivedAttr())3340 break;3341 3342 // Creating an APInt requires us to provide bitwidth, value, and3343 // signedness, which is complicated compared to others. Similarly3344 // for APFloat.3345 // TODO: Adjust the 'returnType' field of such attributes3346 // to support them.3347 StringRef retType = namedAttr->attr.getReturnType();3348 if (retType == "::llvm::APInt" || retType == "::llvm::APFloat")3349 break;3350 3351 defaultValuedAttrLikeStartIndex = i;3352 defaultValuedAttrStartIndex = i;3353 }3354 }3355 3356 // Check if parameters besides default valued one are enough to distinguish3357 // between builders with wrapped and unwrapped arguments.3358 bool hasBuilderAmbiguity = true;3359 for (const auto &arg : op.getArgs()) {3360 auto *namedAttr = dyn_cast<NamedAttribute *>(arg);3361 if (!namedAttr)3362 continue;3363 Attribute attr = namedAttr->attr;3364 if (attr.hasDefaultValue() || attr.isDerivedAttr())3365 continue;3366 3367 if (attrParamKind != AttrParamKind::WrappedAttr ||3368 !canUseUnwrappedRawValue(attr))3369 continue;3370 3371 hasBuilderAmbiguity = false;3372 break;3373 }3374 3375 // Avoid generating build methods that are ambiguous due to default values by3376 // requiring at least one attribute.3377 if (defaultValuedAttrStartIndex < op.getNumArgs()) {3378 // TODO: This should have been possible as a cast<NamedAttribute> but3379 // required template instantiations is not yet defined for the tblgen helper3380 // classes.3381 auto *namedAttr =3382 cast<NamedAttribute *>(op.getArg(defaultValuedAttrStartIndex));3383 Attribute attr = namedAttr->attr;3384 if ((attrParamKind == AttrParamKind::WrappedAttr &&3385 canUseUnwrappedRawValue(attr) && hasBuilderAmbiguity) ||3386 (attrParamKind == AttrParamKind::UnwrappedValue &&3387 !canUseUnwrappedRawValue(attr) && hasBuilderAmbiguity)) {3388 ++defaultValuedAttrStartIndex;3389 defaultValuedAttrLikeStartIndex = defaultValuedAttrStartIndex;3390 }3391 }3392 3393 /// Collect any inferred attributes.3394 for (const NamedTypeConstraint &operand : op.getOperands()) {3395 if (operand.isVariadicOfVariadic()) {3396 inferredAttributes.insert(3397 operand.constraint.getVariadicOfVariadicSegmentSizeAttr());3398 }3399 }3400 3401 FmtContext fctx;3402 fctx.withBuilder(odsBuilder);3403 3404 for (int i = 0, e = op.getNumArgs(), numOperands = 0; i < e; ++i) {3405 Argument arg = op.getArg(i);3406 if (const auto *operand =3407 llvm::dyn_cast_if_present<NamedTypeConstraint *>(arg)) {3408 StringRef type;3409 if (operand->isVariadicOfVariadic())3410 type = "::llvm::ArrayRef<::mlir::ValueRange>";3411 else if (operand->isVariadic())3412 type = "::mlir::ValueRange";3413 else3414 type = "::mlir::Value";3415 3416 paramList.emplace_back(type, getArgumentName(op, numOperands++),3417 operand->isOptional());3418 continue;3419 }3420 if (auto *propArg = llvm::dyn_cast_if_present<NamedProperty *>(arg)) {3421 const Property &prop = propArg->prop;3422 StringRef type = prop.getInterfaceType();3423 std::string defaultValue;3424 if (prop.hasDefaultValue() && i >= defaultValuedAttrLikeStartIndex) {3425 defaultValue = tgfmt(prop.getDefaultValue(), &fctx);3426 }3427 bool isOptional = prop.hasDefaultValue();3428 paramList.emplace_back(type, propArg->name, StringRef(defaultValue),3429 isOptional);3430 continue;3431 }3432 const NamedAttribute &namedAttr = *cast<NamedAttribute *>(arg);3433 const Attribute &attr = namedAttr.attr;3434 3435 // Inferred attributes don't need to be added to the param list.3436 if (inferredAttributes.contains(namedAttr.name))3437 continue;3438 3439 StringRef type;3440 switch (attrParamKind) {3441 case AttrParamKind::WrappedAttr:3442 type = attr.getStorageType();3443 break;3444 case AttrParamKind::UnwrappedValue:3445 if (canUseUnwrappedRawValue(attr))3446 type = attr.getReturnType();3447 else3448 type = attr.getStorageType();3449 break;3450 }3451 3452 // Attach default value if requested and possible.3453 std::string defaultValue;3454 if (i >= defaultValuedAttrStartIndex) {3455 if (attrParamKind == AttrParamKind::UnwrappedValue &&3456 canUseUnwrappedRawValue(attr))3457 defaultValue += tgfmt(attr.getDefaultValue(), &fctx);3458 else3459 defaultValue += "nullptr";3460 }3461 paramList.emplace_back(type, namedAttr.name, StringRef(defaultValue),3462 attr.isOptional());3463 }3464 3465 /// Insert parameters for each successor.3466 for (const NamedSuccessor &succ : op.getSuccessors()) {3467 StringRef type =3468 succ.isVariadic() ? "::mlir::BlockRange" : "::mlir::Block *";3469 paramList.emplace_back(type, succ.name);3470 }3471 3472 /// Insert parameters for variadic regions.3473 for (const NamedRegion ®ion : op.getRegions())3474 if (region.isVariadic())3475 paramList.emplace_back("unsigned",3476 llvm::formatv("{0}Count", region.name).str());3477}3478 3479void OpEmitter::genCodeForAddingArgAndRegionForBuilder(3480 MethodBody &body, llvm::StringSet<> &inferredAttributes,3481 bool isRawValueAttr) {3482 // Push all operands to the result.3483 for (int i = 0, e = op.getNumOperands(); i < e; ++i) {3484 std::string argName = getArgumentName(op, i);3485 const NamedTypeConstraint &operand = op.getOperand(i);3486 if (operand.constraint.isVariadicOfVariadic()) {3487 body << " for (::mlir::ValueRange range : " << argName << ")\n "3488 << builderOpState << ".addOperands(range);\n";3489 3490 // Add the segment attribute.3491 body << " {\n"3492 << " ::llvm::SmallVector<int32_t> rangeSegments;\n"3493 << " for (::mlir::ValueRange range : " << argName << ")\n"3494 << " rangeSegments.push_back(range.size());\n"3495 << " auto rangeAttr = " << odsBuilder3496 << ".getDenseI32ArrayAttr(rangeSegments);\n";3497 if (op.getDialect().usePropertiesForAttributes()) {3498 body << " " << builderOpState << ".getOrAddProperties<Properties>()."3499 << operand.constraint.getVariadicOfVariadicSegmentSizeAttr()3500 << " = rangeAttr;";3501 } else {3502 body << " " << builderOpState << ".addAttribute("3503 << op.getGetterName(3504 operand.constraint.getVariadicOfVariadicSegmentSizeAttr())3505 << "AttrName(" << builderOpState << ".name), rangeAttr);";3506 }3507 body << " }\n";3508 continue;3509 }3510 3511 if (operand.isOptional())3512 body << " if (" << argName << ")\n ";3513 body << " " << builderOpState << ".addOperands(" << argName << ");\n";3514 }3515 3516 // If the operation has the operand segment size attribute, add it here.3517 auto emitSegment = [&]() {3518 interleaveComma(llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) {3519 const NamedTypeConstraint &operand = op.getOperand(i);3520 if (!operand.isVariableLength()) {3521 body << "1";3522 return;3523 }3524 3525 std::string operandName = getArgumentName(op, i);3526 if (operand.isOptional()) {3527 body << "(" << operandName << " ? 1 : 0)";3528 } else if (operand.isVariadicOfVariadic()) {3529 body << llvm::formatv(3530 "llvm::accumulate({0}, int32_t(0), "3531 "[](int32_t curSum, ::mlir::ValueRange range) {{ return curSum + "3532 "static_cast<int32_t>(range.size()); })",3533 operandName);3534 } else {3535 body << "static_cast<int32_t>(" << getArgumentName(op, i) << ".size())";3536 }3537 });3538 };3539 if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {3540 std::string sizes = op.getGetterName(operandSegmentAttrName);3541 if (op.getDialect().usePropertiesForAttributes()) {3542 body << " ::llvm::copy(::llvm::ArrayRef<int32_t>({";3543 emitSegment();3544 body << "}), " << builderOpState3545 << ".getOrAddProperties<Properties>()."3546 "operandSegmentSizes.begin());\n";3547 } else {3548 body << " " << builderOpState << ".addAttribute(" << sizes << "AttrName("3549 << builderOpState << ".name), "3550 << "odsBuilder.getDenseI32ArrayAttr({";3551 emitSegment();3552 body << "}));\n";3553 }3554 }3555 3556 // Push all properties to the result.3557 for (const auto &namedProp : op.getProperties()) {3558 // Use the setter from the Properties struct since the conversion from the3559 // interface type (used in the builder argument) to the storage type (used3560 // in the state) is not necessarily trivial.3561 std::string setterName = op.getSetterName(namedProp.name);3562 body << formatv(" {0}.getOrAddProperties<Properties>().{1}({2});\n",3563 builderOpState, setterName, namedProp.name);3564 }3565 // Push all attributes to the result.3566 for (const auto &namedAttr : op.getAttributes()) {3567 auto &attr = namedAttr.attr;3568 if (attr.isDerivedAttr() || inferredAttributes.contains(namedAttr.name))3569 continue;3570 3571 // TODO: The wrapping of optional is different for default or not, so don't3572 // unwrap for default ones that would fail below.3573 bool emitNotNullCheck =3574 (attr.isOptional() && !attr.hasDefaultValue()) ||3575 (attr.hasDefaultValue() && !isRawValueAttr) ||3576 // TODO: UnitAttr is optional, not wrapped, but needs to be guarded as3577 // the constant materialization is only for true case.3578 (isRawValueAttr && attr.getAttrDefName() == "UnitAttr");3579 if (emitNotNullCheck)3580 body.indent() << formatv("if ({0}) ", namedAttr.name) << "{\n";3581 3582 if (isRawValueAttr && canUseUnwrappedRawValue(attr)) {3583 // If this is a raw value, then we need to wrap it in an Attribute3584 // instance.3585 FmtContext fctx;3586 fctx.withBuilder("odsBuilder");3587 if (op.getDialect().usePropertiesForAttributes()) {3588 body << formatv(" {0}.getOrAddProperties<Properties>().{1} = {2};\n",3589 builderOpState, namedAttr.name,3590 constBuildAttrFromParam(attr, fctx, namedAttr.name));3591 } else {3592 body << formatv(" {0}.addAttribute({1}AttrName({0}.name), {2});\n",3593 builderOpState, op.getGetterName(namedAttr.name),3594 constBuildAttrFromParam(attr, fctx, namedAttr.name));3595 }3596 } else {3597 if (op.getDialect().usePropertiesForAttributes()) {3598 body << formatv(" {0}.getOrAddProperties<Properties>().{1} = {1};\n",3599 builderOpState, namedAttr.name);3600 } else {3601 body << formatv(" {0}.addAttribute({1}AttrName({0}.name), {2});\n",3602 builderOpState, op.getGetterName(namedAttr.name),3603 namedAttr.name);3604 }3605 }3606 if (emitNotNullCheck)3607 body.unindent() << " }\n";3608 }3609 3610 // Create the correct number of regions.3611 for (const NamedRegion ®ion : op.getRegions()) {3612 if (region.isVariadic())3613 body << formatv(" for (unsigned i = 0; i < {0}Count; ++i)\n ",3614 region.name);3615 3616 body << " (void)" << builderOpState << ".addRegion();\n";3617 }3618 3619 // Push all successors to the result.3620 for (const NamedSuccessor &namedSuccessor : op.getSuccessors()) {3621 body << formatv(" {0}.addSuccessors({1});\n", builderOpState,3622 namedSuccessor.name);3623 }3624}3625 3626void OpEmitter::genCanonicalizerDecls() {3627 bool hasCanonicalizeMethod = def.getValueAsBit("hasCanonicalizeMethod");3628 if (hasCanonicalizeMethod) {3629 // static LogicResult FooOp::3630 // canonicalize(FooOp op, PatternRewriter &rewriter);3631 SmallVector<MethodParameter> paramList;3632 paramList.emplace_back(op.getCppClassName(), "op");3633 paramList.emplace_back("::mlir::PatternRewriter &", "rewriter");3634 auto *m = opClass.declareStaticMethod("::llvm::LogicalResult",3635 "canonicalize", std::move(paramList));3636 ERROR_IF_PRUNED(m, "canonicalize", op);3637 }3638 3639 // We get a prototype for 'getCanonicalizationPatterns' if requested directly3640 // or if using a 'canonicalize' method.3641 bool hasCanonicalizer = def.getValueAsBit("hasCanonicalizer");3642 if (!hasCanonicalizeMethod && !hasCanonicalizer)3643 return;3644 3645 // We get a body for 'getCanonicalizationPatterns' when using a 'canonicalize'3646 // method, but not implementing 'getCanonicalizationPatterns' manually.3647 bool hasBody = hasCanonicalizeMethod && !hasCanonicalizer;3648 3649 // Add a signature for getCanonicalizationPatterns if implemented by the3650 // dialect or if synthesized to call 'canonicalize'.3651 SmallVector<MethodParameter> paramList;3652 paramList.emplace_back("::mlir::RewritePatternSet &", "results");3653 paramList.emplace_back("::mlir::MLIRContext *", "context");3654 auto kind = hasBody ? Method::Static : Method::StaticDeclaration;3655 auto *method = opClass.addMethod("void", "getCanonicalizationPatterns", kind,3656 std::move(paramList));3657 3658 // If synthesizing the method, fill it.3659 if (hasBody) {3660 ERROR_IF_PRUNED(method, "getCanonicalizationPatterns", op);3661 method->body() << " results.add(canonicalize);\n";3662 }3663}3664 3665void OpEmitter::genFolderDecls() {3666 if (!op.hasFolder())3667 return;3668 3669 SmallVector<MethodParameter> paramList;3670 paramList.emplace_back("FoldAdaptor", "adaptor");3671 3672 StringRef retType;3673 bool hasSingleResult =3674 op.getNumResults() == 1 && op.getNumVariableLengthResults() == 0;3675 if (hasSingleResult) {3676 retType = "::mlir::OpFoldResult";3677 } else {3678 paramList.emplace_back("::llvm::SmallVectorImpl<::mlir::OpFoldResult> &",3679 "results");3680 retType = "::llvm::LogicalResult";3681 }3682 3683 auto *m = opClass.declareMethod(retType, "fold", std::move(paramList));3684 ERROR_IF_PRUNED(m, "fold", op);3685}3686 3687void OpEmitter::genOpInterfaceMethods(const tblgen::InterfaceTrait *opTrait) {3688 Interface interface = opTrait->getInterface();3689 3690 // Get the set of methods that should always be declared.3691 auto alwaysDeclaredMethodsVec = opTrait->getAlwaysDeclaredMethods();3692 llvm::StringSet<> alwaysDeclaredMethods;3693 alwaysDeclaredMethods.insert_range(alwaysDeclaredMethodsVec);3694 3695 for (const InterfaceMethod &method : interface.getMethods()) {3696 // Don't declare if the method has a body.3697 if (method.getBody())3698 continue;3699 // Don't declare if the method has a default implementation and the op3700 // didn't request that it always be declared.3701 if (method.getDefaultImplementation() &&3702 !alwaysDeclaredMethods.count(method.getName())) {3703 genOpInterfaceMethodUsingDecl(opTrait, method);3704 continue;3705 }3706 // Interface methods are allowed to overlap with existing methods, so don't3707 // check if pruned.3708 (void)genOpInterfaceMethod(method);3709 }3710}3711 3712Method *OpEmitter::genOpInterfaceMethod(const InterfaceMethod &method,3713 bool declaration) {3714 SmallVector<MethodParameter> paramList;3715 for (const InterfaceMethod::Argument &arg : method.getArguments())3716 paramList.emplace_back(arg.type, arg.name);3717 3718 auto props = (method.isStatic() ? Method::Static : Method::None) |3719 (declaration ? Method::Declaration : Method::None);3720 return opClass.addMethod(method.getReturnType(), method.getName(), props,3721 std::move(paramList));3722}3723 3724UsingDeclaration *3725OpEmitter::genOpInterfaceMethodUsingDecl(const tblgen::InterfaceTrait *opTrait,3726 const InterfaceMethod &method) {3727 std::string name = (llvm::Twine(opTrait->getFullyQualifiedTraitName()) + "<" +3728 op.getCppClassName() + ">::" + method.getName())3729 .str();3730 if (interfaceUsingNames.insert(name).second)3731 return opClass.declare<UsingDeclaration>(std::move(name));3732 return nullptr;3733}3734 3735void OpEmitter::genOpInterfaceMethods() {3736 for (const auto &trait : op.getTraits()) {3737 if (const auto *opTrait = dyn_cast<tblgen::InterfaceTrait>(&trait))3738 if (opTrait->shouldDeclareMethods())3739 genOpInterfaceMethods(opTrait);3740 }3741}3742 3743void OpEmitter::genSideEffectInterfaceMethods() {3744 enum EffectKind { Operand, Result, Symbol, Static };3745 struct EffectLocation {3746 /// The effect applied.3747 SideEffect effect;3748 3749 /// The index if the kind is not static.3750 unsigned index;3751 3752 /// The kind of the location.3753 unsigned kind;3754 };3755 3756 StringMap<SmallVector<EffectLocation, 1>> interfaceEffects;3757 auto resolveDecorators = [&](Operator::var_decorator_range decorators,3758 unsigned index, unsigned kind) {3759 for (auto decorator : decorators)3760 if (SideEffect *effect = dyn_cast<SideEffect>(&decorator)) {3761 opClass.addTrait(effect->getInterfaceTrait());3762 interfaceEffects[effect->getBaseEffectName()].push_back(3763 EffectLocation{*effect, index, kind});3764 }3765 };3766 3767 // Collect effects that were specified via:3768 /// Traits.3769 for (const auto &trait : op.getTraits()) {3770 const auto *opTrait = dyn_cast<tblgen::SideEffectTrait>(&trait);3771 if (!opTrait)3772 continue;3773 auto &effects = interfaceEffects[opTrait->getBaseEffectName()];3774 for (auto decorator : opTrait->getEffects())3775 effects.push_back(EffectLocation{cast<SideEffect>(decorator),3776 /*index=*/0, EffectKind::Static});3777 }3778 /// Attributes and Operands.3779 for (unsigned i = 0, operandIt = 0, e = op.getNumArgs(); i != e; ++i) {3780 Argument arg = op.getArg(i);3781 if (isa<NamedTypeConstraint *>(arg)) {3782 resolveDecorators(op.getArgDecorators(i), operandIt, EffectKind::Operand);3783 ++operandIt;3784 continue;3785 }3786 if (isa<NamedProperty *>(arg))3787 continue;3788 const NamedAttribute *attr = cast<NamedAttribute *>(arg);3789 if (attr->attr.getBaseAttr().isSymbolRefAttr())3790 resolveDecorators(op.getArgDecorators(i), i, EffectKind::Symbol);3791 }3792 /// Results.3793 for (unsigned i = 0, e = op.getNumResults(); i != e; ++i)3794 resolveDecorators(op.getResultDecorators(i), i, EffectKind::Result);3795 3796 // The code used to add an effect instance.3797 // {0}: The effect class.3798 // {1}: Optional value or symbol reference.3799 // {2}: The side effect stage.3800 // {3}: Does this side effect act on every single value of resource.3801 // {4}: The resource class.3802 const char *addEffectCode =3803 " effects.emplace_back({0}::get(), {1}{2}, {3}, {4}::get());\n";3804 3805 for (auto &it : interfaceEffects) {3806 // Generate the 'getEffects' method.3807 std::string type = llvm::formatv("::llvm::SmallVectorImpl<::mlir::"3808 "SideEffects::EffectInstance<{0}>> &",3809 it.first())3810 .str();3811 auto *getEffects = opClass.addMethod("void", "getEffects",3812 MethodParameter(type, "effects"));3813 ERROR_IF_PRUNED(getEffects, "getEffects", op);3814 auto &body = getEffects->body();3815 3816 // Add effect instances for each of the locations marked on the operation.3817 for (auto &location : it.second) {3818 StringRef effect = location.effect.getName();3819 StringRef resource = location.effect.getResource();3820 int stage = (int)location.effect.getStage();3821 bool effectOnFullRegion = (int)location.effect.getEffectOnfullRegion();3822 if (location.kind == EffectKind::Static) {3823 // A static instance has no attached value.3824 body << llvm::formatv(addEffectCode, effect, "", stage,3825 effectOnFullRegion, resource)3826 .str();3827 } else if (location.kind == EffectKind::Symbol) {3828 // A symbol reference requires adding the proper attribute.3829 const auto *attr = cast<NamedAttribute *>(op.getArg(location.index));3830 std::string argName = op.getGetterName(attr->name);3831 if (attr->attr.isOptional()) {3832 body << " if (auto symbolRef = " << argName << "Attr())\n "3833 << llvm::formatv(addEffectCode, effect, "symbolRef, ", stage,3834 effectOnFullRegion, resource)3835 .str();3836 } else {3837 body << llvm::formatv(addEffectCode, effect, argName + "Attr(), ",3838 stage, effectOnFullRegion, resource)3839 .str();3840 }3841 } else {3842 // Otherwise this is an operand/result, so we need to attach the Value.3843 body << " {\n auto valueRange = getODS"3844 << (location.kind == EffectKind::Operand ? "Operand" : "Result")3845 << "IndexAndLength(" << location.index << ");\n"3846 << " for (unsigned idx = valueRange.first; idx < "3847 "valueRange.first"3848 << " + valueRange.second; idx++) {\n "3849 << llvm::formatv(addEffectCode, effect,3850 (location.kind == EffectKind::Operand3851 ? "&getOperation()->getOpOperand(idx), "3852 : "getOperation()->getOpResult(idx), "),3853 stage, effectOnFullRegion, resource)3854 << " }\n }\n";3855 }3856 }3857 }3858}3859 3860void OpEmitter::genTypeInterfaceMethods() {3861 if (!op.allResultTypesKnown())3862 return;3863 // Generate 'inferReturnTypes' method declaration using the interface method3864 // declared in 'InferTypeOpInterface' op interface.3865 const auto *trait =3866 cast<InterfaceTrait>(op.getTrait("::mlir::InferTypeOpInterface::Trait"));3867 Interface interface = trait->getInterface();3868 Method *method = [&]() -> Method * {3869 for (const InterfaceMethod &interfaceMethod : interface.getMethods()) {3870 if (interfaceMethod.getName() == "inferReturnTypes") {3871 return genOpInterfaceMethod(interfaceMethod, /*declaration=*/false);3872 }3873 }3874 assert(0 && "unable to find inferReturnTypes interface method");3875 return nullptr;3876 }();3877 ERROR_IF_PRUNED(method, "inferReturnTypes", op);3878 auto &body = method->body();3879 body << " inferredReturnTypes.resize(" << op.getNumResults() << ");\n";3880 3881 FmtContext fctx;3882 fctx.withBuilder("odsBuilder");3883 fctx.addSubst("_ctxt", "context");3884 body << " ::mlir::Builder odsBuilder(context);\n";3885 3886 // Preprocessing stage to verify all accesses to operands are valid.3887 int maxAccessedIndex = -1;3888 for (int i = 0, e = op.getNumResults(); i != e; ++i) {3889 const InferredResultType &infer = op.getInferredResultType(i);3890 if (!infer.isArg())3891 continue;3892 Operator::OperandAttrOrProp arg =3893 op.getArgToOperandAttrOrProp(infer.getIndex());3894 if (arg.kind() == Operator::OperandAttrOrProp::Kind::Operand) {3895 maxAccessedIndex =3896 std::max(maxAccessedIndex, arg.operandOrAttributeIndex());3897 }3898 }3899 if (maxAccessedIndex != -1) {3900 body << " if (operands.size() <= " << Twine(maxAccessedIndex) << ")\n";3901 body << " return ::mlir::failure();\n";3902 }3903 3904 // Process the type inference graph in topological order, starting from types3905 // that are always fully-inferred: operands and results with constructible3906 // types. The type inference graph here will always be a DAG, so this gives3907 // us the correct order for generating the types. -1 is a placeholder to3908 // indicate the type for a result has not been generated.3909 SmallVector<int> constructedIndices(op.getNumResults(), -1);3910 int inferredTypeIdx = 0;3911 for (int numResults = op.getNumResults(); inferredTypeIdx != numResults;) {3912 for (int i = 0, e = op.getNumResults(); i != e; ++i) {3913 if (constructedIndices[i] >= 0)3914 continue;3915 const InferredResultType &infer = op.getInferredResultType(i);3916 std::string typeStr;3917 if (infer.isArg()) {3918 // If this is an operand, just index into operand list to access the3919 // type.3920 Operator::OperandAttrOrProp arg =3921 op.getArgToOperandAttrOrProp(infer.getIndex());3922 if (arg.kind() == Operator::OperandAttrOrProp::Kind::Operand) {3923 typeStr = ("operands[" + Twine(arg.operandOrAttributeIndex()) +3924 "].getType()")3925 .str();3926 3927 // If this is an attribute, index into the attribute dictionary.3928 } else if (auto *attr = dyn_cast<NamedAttribute *>(3929 op.getArg(arg.operandOrAttributeIndex()))) {3930 body << " ::mlir::TypedAttr odsInferredTypeAttr" << inferredTypeIdx3931 << " = ";3932 if (op.getDialect().usePropertiesForAttributes()) {3933 body << "(properties ? properties.as<Properties *>()->"3934 << attr->name3935 << " : "3936 "::llvm::dyn_cast_or_null<::mlir::TypedAttr>(attributes."3937 "get(\"" +3938 attr->name + "\")));\n";3939 } else {3940 body << "::llvm::dyn_cast_or_null<::mlir::TypedAttr>(attributes."3941 "get(\"" +3942 attr->name + "\"));\n";3943 }3944 body << " if (!odsInferredTypeAttr" << inferredTypeIdx3945 << ") return ::mlir::failure();\n";3946 typeStr =3947 ("odsInferredTypeAttr" + Twine(inferredTypeIdx) + ".getType()")3948 .str();3949 } else {3950 llvm::PrintFatalError(&op.getDef(),3951 "Properties cannot be used for type inference");3952 }3953 } else if (std::optional<StringRef> builder =3954 op.getResult(infer.getResultIndex())3955 .constraint.getBuilderCall()) {3956 typeStr = tgfmt(*builder, &fctx).str();3957 } else if (int index = constructedIndices[infer.getResultIndex()];3958 index >= 0) {3959 typeStr = ("odsInferredType" + Twine(index)).str();3960 } else {3961 continue;3962 }3963 body << " ::mlir::Type odsInferredType" << inferredTypeIdx++ << " = "3964 << tgfmt(infer.getTransformer(), &fctx.withSelf(typeStr)) << ";\n";3965 constructedIndices[i] = inferredTypeIdx - 1;3966 }3967 }3968 for (auto [i, index] : llvm::enumerate(constructedIndices))3969 body << " inferredReturnTypes[" << i << "] = odsInferredType" << index3970 << ";\n";3971 body << " return ::mlir::success();";3972}3973 3974void OpEmitter::genParser() {3975 if (hasStringAttribute(def, "assemblyFormat"))3976 return;3977 3978 if (!def.getValueAsBit("hasCustomAssemblyFormat"))3979 return;3980 3981 SmallVector<MethodParameter> paramList;3982 paramList.emplace_back("::mlir::OpAsmParser &", "parser");3983 paramList.emplace_back("::mlir::OperationState &", "result");3984 3985 auto *method = opClass.declareStaticMethod("::mlir::ParseResult", "parse",3986 std::move(paramList));3987 ERROR_IF_PRUNED(method, "parse", op);3988}3989 3990void OpEmitter::genPrinter() {3991 if (hasStringAttribute(def, "assemblyFormat"))3992 return;3993 3994 // Check to see if this op uses a c++ format.3995 if (!def.getValueAsBit("hasCustomAssemblyFormat"))3996 return;3997 auto *method = opClass.declareMethod(3998 "void", "print", MethodParameter("::mlir::OpAsmPrinter &", "p"));3999 ERROR_IF_PRUNED(method, "print", op);4000}4001 4002void OpEmitter::genVerifier() {4003 auto *implMethod =4004 opClass.addMethod("::llvm::LogicalResult", "verifyInvariantsImpl");4005 ERROR_IF_PRUNED(implMethod, "verifyInvariantsImpl", op);4006 auto &implBody = implMethod->body();4007 bool useProperties = emitHelper.hasProperties();4008 4009 populateSubstitutions(emitHelper, verifyCtx);4010 genPropertyVerifier(emitHelper, verifyCtx, implBody, staticVerifierEmitter);4011 genAttributeVerifier(emitHelper, verifyCtx, implBody, staticVerifierEmitter,4012 useProperties);4013 genOperandResultVerifier(implBody, op.getOperands(), "operand");4014 genOperandResultVerifier(implBody, op.getResults(), "result");4015 4016 for (auto &trait : op.getTraits()) {4017 if (auto *t = dyn_cast<tblgen::PredTrait>(&trait)) {4018 implBody << tgfmt(" if (!($0))\n "4019 "return emitOpError(\"failed to verify that $1\");\n",4020 &verifyCtx, tgfmt(t->getPredTemplate(), &verifyCtx),4021 t->getSummary());4022 }4023 }4024 4025 genRegionVerifier(implBody);4026 genSuccessorVerifier(implBody);4027 4028 implBody << " return ::mlir::success();\n";4029 4030 // TODO: Some places use the `verifyInvariants` to do operation verification.4031 // This may not act as their expectation because this doesn't call any4032 // verifiers of native/interface traits. Needs to review those use cases and4033 // see if we should use the mlir::verify() instead.4034 auto *method = opClass.addMethod("::llvm::LogicalResult", "verifyInvariants");4035 ERROR_IF_PRUNED(method, "verifyInvariants", op);4036 auto &body = method->body();4037 if (def.getValueAsBit("hasVerifier")) {4038 body << " if(::mlir::succeeded(verifyInvariantsImpl()) && "4039 "::mlir::succeeded(verify()))\n";4040 body << " return ::mlir::success();\n";4041 body << " return ::mlir::failure();";4042 } else {4043 body << " return verifyInvariantsImpl();";4044 }4045}4046 4047void OpEmitter::genCustomVerifier() {4048 if (def.getValueAsBit("hasVerifier")) {4049 auto *method = opClass.declareMethod("::llvm::LogicalResult", "verify");4050 ERROR_IF_PRUNED(method, "verify", op);4051 }4052 4053 if (def.getValueAsBit("hasRegionVerifier")) {4054 auto *method =4055 opClass.declareMethod("::llvm::LogicalResult", "verifyRegions");4056 ERROR_IF_PRUNED(method, "verifyRegions", op);4057 }4058}4059 4060void OpEmitter::genOperandResultVerifier(MethodBody &body,4061 Operator::const_value_range values,4062 StringRef valueKind) {4063 // Check that an optional value is at most 1 element.4064 //4065 // {0}: Value index.4066 // {1}: "operand" or "result"4067 const char *const verifyOptional = R"(4068 if (valueGroup{0}.size() > 1) {4069 return emitOpError("{1} group starting at #") << index4070 << " requires 0 or 1 element, but found " << valueGroup{0}.size();4071 }4072)";4073 // Check the types of a range of values.4074 //4075 // {0}: Value index.4076 // {1}: Type constraint function.4077 // {2}: "operand" or "result"4078 const char *const verifyValues = R"(4079 for (auto v : valueGroup{0}) {4080 if (::mlir::failed({1}(*this, v.getType(), "{2}", index++)))4081 return ::mlir::failure();4082 }4083)";4084 4085 const auto canSkip = [](const NamedTypeConstraint &value) {4086 return !value.hasPredicate() && !value.isOptional() &&4087 !value.isVariadicOfVariadic();4088 };4089 if (values.empty() || llvm::all_of(values, canSkip))4090 return;4091 4092 FmtContext fctx;4093 4094 body << " {\n unsigned index = 0; (void)index;\n";4095 4096 for (const auto &staticValue : llvm::enumerate(values)) {4097 const NamedTypeConstraint &value = staticValue.value();4098 4099 bool hasPredicate = value.hasPredicate();4100 bool isOptional = value.isOptional();4101 bool isVariadicOfVariadic = value.isVariadicOfVariadic();4102 if (!hasPredicate && !isOptional && !isVariadicOfVariadic)4103 continue;4104 body << formatv(" auto valueGroup{2} = getODS{0}{1}s({2});\n",4105 // Capitalize the first letter to match the function name4106 valueKind.substr(0, 1).upper(), valueKind.substr(1),4107 staticValue.index());4108 4109 // If the constraint is optional check that the value group has at most 14110 // value.4111 if (isOptional) {4112 body << formatv(verifyOptional, staticValue.index(), valueKind);4113 } else if (isVariadicOfVariadic) {4114 body << formatv(4115 " if (::mlir::failed(::mlir::OpTrait::impl::verifyValueSizeAttr("4116 "*this, \"{0}\", \"{1}\", valueGroup{2}.size())))\n"4117 " return ::mlir::failure();\n",4118 value.constraint.getVariadicOfVariadicSegmentSizeAttr(), value.name,4119 staticValue.index());4120 }4121 4122 // Otherwise, if there is no predicate there is nothing left to do.4123 if (!hasPredicate)4124 continue;4125 // Emit a loop to check all the dynamic values in the pack.4126 StringRef constraintFn =4127 staticVerifierEmitter.getTypeConstraintFn(value.constraint);4128 body << formatv(verifyValues, staticValue.index(), constraintFn, valueKind);4129 }4130 4131 body << " }\n";4132}4133 4134void OpEmitter::genRegionVerifier(MethodBody &body) {4135 /// Code to verify a region.4136 ///4137 /// {0}: Getter for the regions.4138 /// {1}: The region constraint.4139 /// {2}: The region's name.4140 /// {3}: The region description.4141 const char *const verifyRegion = R"(4142 for (auto ®ion : {0})4143 if (::mlir::failed({1}(*this, region, "{2}", index++)))4144 return ::mlir::failure();4145)";4146 /// Get a single region.4147 ///4148 /// {0}: The region's index.4149 const char *const getSingleRegion =4150 "::llvm::MutableArrayRef((*this)->getRegion({0}))";4151 4152 // If we have no regions, there is nothing more to do.4153 const auto canSkip = [](const NamedRegion ®ion) {4154 return region.constraint.getPredicate().isNull();4155 };4156 auto regions = op.getRegions();4157 if (regions.empty() && llvm::all_of(regions, canSkip))4158 return;4159 4160 body << " {\n unsigned index = 0; (void)index;\n";4161 for (const auto &it : llvm::enumerate(regions)) {4162 const auto ®ion = it.value();4163 if (canSkip(region))4164 continue;4165 4166 auto getRegion = region.isVariadic()4167 ? formatv("{0}()", op.getGetterName(region.name)).str()4168 : formatv(getSingleRegion, it.index()).str();4169 auto constraintFn =4170 staticVerifierEmitter.getRegionConstraintFn(region.constraint);4171 body << formatv(verifyRegion, getRegion, constraintFn, region.name);4172 }4173 body << " }\n";4174}4175 4176void OpEmitter::genSuccessorVerifier(MethodBody &body) {4177 const char *const verifySuccessor = R"(4178 for (auto *successor : {0})4179 if (::mlir::failed({1}(*this, successor, "{2}", index++)))4180 return ::mlir::failure();4181)";4182 /// Get a single successor.4183 ///4184 /// {0}: The successor's name.4185 const char *const getSingleSuccessor = "::llvm::MutableArrayRef({0}())";4186 4187 // If we have no successors, there is nothing more to do.4188 const auto canSkip = [](const NamedSuccessor &successor) {4189 return successor.constraint.getPredicate().isNull();4190 };4191 auto successors = op.getSuccessors();4192 if (successors.empty() && llvm::all_of(successors, canSkip))4193 return;4194 4195 body << " {\n unsigned index = 0; (void)index;\n";4196 4197 for (auto it : llvm::enumerate(successors)) {4198 const auto &successor = it.value();4199 if (canSkip(successor))4200 continue;4201 4202 auto getSuccessor =4203 formatv(successor.isVariadic() ? "{0}()" : getSingleSuccessor,4204 successor.name)4205 .str();4206 auto constraintFn =4207 staticVerifierEmitter.getSuccessorConstraintFn(successor.constraint);4208 body << formatv(verifySuccessor, getSuccessor, constraintFn,4209 successor.name);4210 }4211 body << " }\n";4212}4213 4214/// Add a size count trait to the given operation class.4215static void addSizeCountTrait(OpClass &opClass, StringRef traitKind,4216 int numTotal, int numVariadic) {4217 if (numVariadic != 0) {4218 if (numTotal == numVariadic)4219 opClass.addTrait("::mlir::OpTrait::Variadic" + traitKind + "s");4220 else4221 opClass.addTrait("::mlir::OpTrait::AtLeastN" + traitKind + "s<" +4222 Twine(numTotal - numVariadic) + ">::Impl");4223 return;4224 }4225 switch (numTotal) {4226 case 0:4227 opClass.addTrait("::mlir::OpTrait::Zero" + traitKind + "s");4228 break;4229 case 1:4230 opClass.addTrait("::mlir::OpTrait::One" + traitKind);4231 break;4232 default:4233 opClass.addTrait("::mlir::OpTrait::N" + traitKind + "s<" + Twine(numTotal) +4234 ">::Impl");4235 break;4236 }4237}4238 4239void OpEmitter::genTraits() {4240 // Add region size trait.4241 unsigned numRegions = op.getNumRegions();4242 unsigned numVariadicRegions = op.getNumVariadicRegions();4243 addSizeCountTrait(opClass, "Region", numRegions, numVariadicRegions);4244 4245 // Add result size traits.4246 int numResults = op.getNumResults();4247 int numVariadicResults = op.getNumVariableLengthResults();4248 addSizeCountTrait(opClass, "Result", numResults, numVariadicResults);4249 4250 // For single result ops with a known specific type, generate a OneTypedResult4251 // trait.4252 if (numResults == 1 && numVariadicResults == 0) {4253 auto cppName = op.getResults().begin()->constraint.getCppType();4254 opClass.addTrait("::mlir::OpTrait::OneTypedResult<" + cppName + ">::Impl");4255 }4256 4257 // Add successor size trait.4258 unsigned numSuccessors = op.getNumSuccessors();4259 unsigned numVariadicSuccessors = op.getNumVariadicSuccessors();4260 addSizeCountTrait(opClass, "Successor", numSuccessors, numVariadicSuccessors);4261 4262 // Add variadic size trait and normal op traits.4263 int numOperands = op.getNumOperands();4264 int numVariadicOperands = op.getNumVariableLengthOperands();4265 4266 // Add operand size trait.4267 addSizeCountTrait(opClass, "Operand", numOperands, numVariadicOperands);4268 4269 // The op traits defined internal are ensured that they can be verified4270 // earlier.4271 for (const auto &trait : op.getTraits()) {4272 if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {4273 if (opTrait->isStructuralOpTrait())4274 opClass.addTrait(opTrait->getFullyQualifiedTraitName());4275 }4276 }4277 4278 // OpInvariants wrapps the verifyInvariants which needs to be run before4279 // native/interface traits and after all the traits with `StructuralOpTrait`.4280 opClass.addTrait("::mlir::OpTrait::OpInvariants");4281 4282 if (emitHelper.hasNonEmptyPropertiesStruct())4283 opClass.addTrait("::mlir::BytecodeOpInterface::Trait");4284 4285 // Add the native and interface traits.4286 for (const auto &trait : op.getTraits()) {4287 if (auto *opTrait = dyn_cast<tblgen::NativeTrait>(&trait)) {4288 if (!opTrait->isStructuralOpTrait())4289 opClass.addTrait(opTrait->getFullyQualifiedTraitName());4290 } else if (auto *opTrait = dyn_cast<tblgen::InterfaceTrait>(&trait)) {4291 opClass.addTrait(opTrait->getFullyQualifiedTraitName());4292 }4293 }4294}4295 4296void OpEmitter::genOpNameGetter() {4297 auto *method = opClass.addStaticMethod<Method::Constexpr>(4298 "::llvm::StringLiteral", "getOperationName");4299 ERROR_IF_PRUNED(method, "getOperationName", op);4300 method->body() << " return ::llvm::StringLiteral(\"" << op.getOperationName()4301 << "\");";4302}4303 4304void OpEmitter::genOpAsmInterface() {4305 // If the user only has one results or specifically added the Asm trait,4306 // then don't generate it for them. We specifically only handle multi result4307 // operations, because the name of a single result in the common case is not4308 // interesting(generally 'result'/'output'/etc.).4309 // TODO: We could also add a flag to allow operations to opt in to this4310 // generation, even if they only have a single operation.4311 int numResults = op.getNumResults();4312 if (numResults <= 1 || op.getTrait("::mlir::OpAsmOpInterface::Trait"))4313 return;4314 4315 SmallVector<StringRef, 4> resultNames(numResults);4316 for (int i = 0; i != numResults; ++i)4317 resultNames[i] = op.getResultName(i);4318 4319 // Don't add the trait if none of the results have a valid name.4320 if (llvm::all_of(resultNames, [](StringRef name) { return name.empty(); }))4321 return;4322 opClass.addTrait("::mlir::OpAsmOpInterface::Trait");4323 4324 // Generate the right accessor for the number of results.4325 auto *method = opClass.addMethod(4326 "void", "getAsmResultNames",4327 MethodParameter("::mlir::OpAsmSetValueNameFn", "setNameFn"));4328 ERROR_IF_PRUNED(method, "getAsmResultNames", op);4329 auto &body = method->body();4330 for (int i = 0; i != numResults; ++i) {4331 body << " auto resultGroup" << i << " = getODSResults(" << i << ");\n"4332 << " if (!resultGroup" << i << ".empty())\n"4333 << " setNameFn(*resultGroup" << i << ".begin(), \""4334 << resultNames[i] << "\");\n";4335 }4336}4337 4338//===----------------------------------------------------------------------===//4339// OpOperandAdaptor emitter4340//===----------------------------------------------------------------------===//4341 4342namespace {4343// Helper class to emit Op operand adaptors to an output stream. Operand4344// adaptors are wrappers around random access ranges that provide named operand4345// getters identical to those defined in the Op.4346// This currently generates 3 classes per Op:4347// * A Base class within the 'detail' namespace, which contains all logic and4348// members independent of the random access range that is indexed into.4349// In other words, it contains all the attribute and region getters.4350// * A templated class named '{OpName}GenericAdaptor' with a template parameter4351// 'RangeT' that is indexed into by the getters to access the operands.4352// It contains all getters to access operands and inherits from the previous4353// class.4354// * A class named '{OpName}Adaptor', which inherits from the 'GenericAdaptor'4355// with 'mlir::ValueRange' as template parameter. It adds a constructor from4356// an instance of the op type and a verify function.4357class OpOperandAdaptorEmitter {4358public:4359 static void4360 emitDecl(const Operator &op,4361 const StaticVerifierFunctionEmitter &staticVerifierEmitter,4362 raw_ostream &os);4363 static void4364 emitDef(const Operator &op,4365 const StaticVerifierFunctionEmitter &staticVerifierEmitter,4366 raw_ostream &os);4367 4368private:4369 explicit OpOperandAdaptorEmitter(4370 const Operator &op,4371 const StaticVerifierFunctionEmitter &staticVerifierEmitter);4372 4373 // Add verification function. This generates a verify method for the adaptor4374 // which verifies all the op-independent attribute constraints.4375 void addVerification();4376 4377 // The operation for which to emit an adaptor.4378 const Operator &op;4379 4380 // The generated adaptor classes.4381 Class genericAdaptorBase;4382 Class genericAdaptor;4383 Class adaptor;4384 4385 // The emitter containing all of the locally emitted verification functions.4386 const StaticVerifierFunctionEmitter &staticVerifierEmitter;4387 4388 // Helper for emitting adaptor code.4389 OpOrAdaptorHelper emitHelper;4390};4391} // namespace4392 4393OpOperandAdaptorEmitter::OpOperandAdaptorEmitter(4394 const Operator &op,4395 const StaticVerifierFunctionEmitter &staticVerifierEmitter)4396 : op(op), genericAdaptorBase(op.getGenericAdaptorName() + "Base"),4397 genericAdaptor(op.getGenericAdaptorName()), adaptor(op.getAdaptorName()),4398 staticVerifierEmitter(staticVerifierEmitter),4399 emitHelper(op, /*emitForOp=*/false) {4400 4401 FmtContext fctx;4402 fctx.withBuilder(odsBuilder);4403 4404 genericAdaptorBase.declare<VisibilityDeclaration>(Visibility::Public);4405 bool useProperties = emitHelper.hasProperties();4406 if (useProperties) {4407 // Define the properties struct with multiple members.4408 using ConstArgument =4409 llvm::PointerUnion<const AttributeMetadata *, const NamedProperty *>;4410 SmallVector<ConstArgument> attrOrProperties;4411 for (const std::pair<StringRef, AttributeMetadata> &it :4412 emitHelper.getAttrMetadata()) {4413 if (!it.second.constraint || !it.second.constraint->isDerivedAttr())4414 attrOrProperties.push_back(&it.second);4415 }4416 for (const NamedProperty &prop : op.getProperties())4417 attrOrProperties.push_back(&prop);4418 if (emitHelper.getOperandSegmentsSize())4419 attrOrProperties.push_back(&emitHelper.getOperandSegmentsSize().value());4420 if (emitHelper.getResultSegmentsSize())4421 attrOrProperties.push_back(&emitHelper.getResultSegmentsSize().value());4422 std::string declarations = " struct Properties {\n";4423 llvm::raw_string_ostream os(declarations);4424 std::string comparator =4425 " bool operator==(const Properties &rhs) const {\n"4426 " return \n";4427 llvm::raw_string_ostream comparatorOs(comparator);4428 for (const auto &attrOrProp : attrOrProperties) {4429 if (const auto *namedProperty =4430 llvm::dyn_cast_if_present<const NamedProperty *>(attrOrProp)) {4431 StringRef name = namedProperty->name;4432 if (name.empty())4433 report_fatal_error("missing name for property");4434 std::string camelName =4435 convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);4436 auto &prop = namedProperty->prop;4437 // Generate the data member using the storage type.4438 os << " using " << name << "Ty = " << prop.getStorageType() << ";\n"4439 << " " << name << "Ty " << name;4440 if (prop.hasStorageTypeValueOverride())4441 os << " = " << prop.getStorageTypeValueOverride();4442 else if (prop.hasDefaultValue())4443 os << " = " << tgfmt(prop.getDefaultValue(), &fctx);4444 comparatorOs << " rhs." << name << " == this->" << name4445 << " &&\n";4446 // Emit accessors using the interface type.4447 const char *accessorFmt = R"decl(;4448 {0} get{1}() const {4449 auto &propStorage = this->{2};4450 return {3};4451 }4452 void set{1}({0} propValue) {4453 auto &propStorage = this->{2};4454 {4};4455 }4456)decl";4457 FmtContext fctx;4458 os << formatv(accessorFmt, prop.getInterfaceType(), camelName, name,4459 tgfmt(prop.getConvertFromStorageCall(),4460 &fctx.addSubst("_storage", propertyStorage)),4461 tgfmt(prop.getAssignToStorageCall(),4462 &fctx.addSubst("_value", propertyValue)4463 .addSubst("_storage", propertyStorage)));4464 continue;4465 }4466 const auto *namedAttr =4467 llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);4468 const Attribute *attr = nullptr;4469 if (namedAttr->constraint)4470 attr = &*namedAttr->constraint;4471 StringRef name = namedAttr->attrName;4472 if (name.empty())4473 report_fatal_error("missing name for property attr");4474 std::string camelName =4475 convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);4476 // Generate the data member using the storage type.4477 StringRef storageType;4478 if (attr) {4479 storageType = attr->getStorageType();4480 } else {4481 if (name != operandSegmentAttrName && name != resultSegmentAttrName) {4482 report_fatal_error("unexpected AttributeMetadata");4483 }4484 // TODO: update to use native integers.4485 storageType = "::mlir::DenseI32ArrayAttr";4486 }4487 os << " using " << name << "Ty = " << storageType << ";\n"4488 << " " << name << "Ty " << name << ";\n";4489 comparatorOs << " rhs." << name << " == this->" << name << " &&\n";4490 4491 // Emit accessors using the interface type.4492 if (attr) {4493 const char *accessorFmt = R"decl(4494 auto get{0}() const {4495 auto &propStorage = this->{1};4496 return ::llvm::{2}<{3}>(propStorage);4497 }4498 void set{0}(const {3} &propValue) {4499 this->{1} = propValue;4500 }4501)decl";4502 os << formatv(accessorFmt, camelName, name,4503 attr->isOptional() || attr->hasDefaultValue()4504 ? "dyn_cast_or_null"4505 : "cast",4506 storageType);4507 }4508 }4509 comparatorOs << " true;\n }\n"4510 " bool operator!=(const Properties &rhs) const {\n"4511 " return !(*this == rhs);\n"4512 " }\n";4513 os << comparator;4514 os << " };\n";4515 4516 if (attrOrProperties.empty())4517 genericAdaptorBase.declare<UsingDeclaration>("Properties",4518 "::mlir::EmptyProperties");4519 else4520 genericAdaptorBase.declare<ExtraClassDeclaration>(4521 std::move(declarations));4522 }4523 genericAdaptorBase.declare<VisibilityDeclaration>(Visibility::Protected);4524 genericAdaptorBase.declare<Field>("::mlir::DictionaryAttr", "odsAttrs");4525 genericAdaptorBase.declare<Field>("::std::optional<::mlir::OperationName>",4526 "odsOpName");4527 if (useProperties)4528 genericAdaptorBase.declare<Field>("Properties", "properties");4529 genericAdaptorBase.declare<Field>("::mlir::RegionRange", "odsRegions");4530 4531 genericAdaptor.addTemplateParam("RangeT");4532 genericAdaptor.addField("RangeT", "odsOperands");4533 genericAdaptor.addParent(4534 ParentClass("detail::" + genericAdaptorBase.getClassName()));4535 genericAdaptor.declare<UsingDeclaration>(4536 "ValueT", "::llvm::detail::ValueOfRange<RangeT>");4537 genericAdaptor.declare<UsingDeclaration>(4538 "Base", "detail::" + genericAdaptorBase.getClassName());4539 4540 const auto *attrSizedOperands =4541 op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments");4542 {4543 SmallVector<MethodParameter> paramList;4544 if (useProperties) {4545 // Properties can't be given a default constructor here due to Properties4546 // struct being defined in the enclosing class which isn't complete by4547 // here.4548 paramList.emplace_back("::mlir::DictionaryAttr", "attrs");4549 paramList.emplace_back("const Properties &", "properties");4550 } else {4551 paramList.emplace_back("::mlir::DictionaryAttr", "attrs", "{}");4552 paramList.emplace_back("const ::mlir::EmptyProperties &", "properties",4553 "{}");4554 }4555 paramList.emplace_back("::mlir::RegionRange", "regions", "{}");4556 auto *baseConstructor =4557 genericAdaptorBase.addConstructor<Method::Inline>(paramList);4558 baseConstructor->addMemberInitializer("odsAttrs", "attrs");4559 if (useProperties)4560 baseConstructor->addMemberInitializer("properties", "properties");4561 baseConstructor->addMemberInitializer("odsRegions", "regions");4562 4563 MethodBody &body = baseConstructor->body();4564 body.indent() << "if (odsAttrs)\n";4565 body.indent() << formatv(4566 "odsOpName.emplace(\"{0}\", odsAttrs.getContext());\n",4567 op.getOperationName());4568 4569 paramList.insert(paramList.begin(), MethodParameter("RangeT", "values"));4570 auto *constructor = genericAdaptor.addConstructor(paramList);4571 constructor->addMemberInitializer("Base", "attrs, properties, regions");4572 constructor->addMemberInitializer("odsOperands", "values");4573 4574 // Add a forwarding constructor to the previous one that accepts4575 // OpaqueProperties instead and check for null and perform the cast to the4576 // actual properties type.4577 paramList[1] = MethodParameter("::mlir::DictionaryAttr", "attrs");4578 paramList[2] = MethodParameter("::mlir::OpaqueProperties", "properties");4579 auto *opaquePropertiesConstructor =4580 genericAdaptor.addConstructor(std::move(paramList));4581 if (useProperties) {4582 opaquePropertiesConstructor->addMemberInitializer(4583 genericAdaptor.getClassName(),4584 "values, "4585 "attrs, "4586 "(properties ? *properties.as<Properties *>() : Properties{}), "4587 "regions");4588 } else {4589 opaquePropertiesConstructor->addMemberInitializer(4590 genericAdaptor.getClassName(),4591 "values, "4592 "attrs, "4593 "(properties ? *properties.as<::mlir::EmptyProperties *>() : "4594 "::mlir::EmptyProperties{}), "4595 "regions");4596 }4597 4598 // Add forwarding constructor that constructs Properties.4599 if (useProperties) {4600 SmallVector<MethodParameter> paramList;4601 paramList.emplace_back("RangeT", "values");4602 paramList.emplace_back("::mlir::DictionaryAttr", "attrs",4603 attrSizedOperands ? "" : "nullptr");4604 auto *noPropertiesConstructor =4605 genericAdaptor.addConstructor(std::move(paramList));4606 noPropertiesConstructor->addMemberInitializer(4607 genericAdaptor.getClassName(), "values, "4608 "attrs, "4609 "Properties{}, "4610 "{}");4611 }4612 }4613 4614 // Create a constructor that creates a new generic adaptor by copying4615 // everything from another adaptor, except for the values.4616 {4617 SmallVector<MethodParameter> paramList;4618 paramList.emplace_back("RangeT", "values");4619 paramList.emplace_back("const " + op.getGenericAdaptorName() + "Base &",4620 "base");4621 auto *constructor =4622 genericAdaptor.addConstructor<Method::Inline>(paramList);4623 constructor->addMemberInitializer("Base", "base");4624 constructor->addMemberInitializer("odsOperands", "values");4625 }4626 4627 // Create constructors constructing the adaptor from an instance of the op.4628 // This takes the attributes, properties and regions from the op instance4629 // and the value range from the parameter.4630 {4631 // Base class is in the cpp file and can simply access the members of the op4632 // class to initialize the template independent fields. If the op doesn't4633 // have properties, we can emit a generic constructor inline. Otherwise,4634 // emit it out-of-line because we need the op to be defined.4635 Constructor *constructor;4636 if (useProperties) {4637 constructor = genericAdaptorBase.addConstructor(4638 MethodParameter(op.getCppClassName(), "op"));4639 } else {4640 constructor = genericAdaptorBase.addConstructor<Method::Inline>(4641 MethodParameter("::mlir::Operation *", "op"));4642 }4643 constructor->addMemberInitializer("odsAttrs",4644 "op->getRawDictionaryAttrs()");4645 // Retrieve the operation name from the op directly.4646 constructor->addMemberInitializer("odsOpName", "op->getName()");4647 if (useProperties)4648 constructor->addMemberInitializer("properties", "op.getProperties()");4649 constructor->addMemberInitializer("odsRegions", "op->getRegions()");4650 4651 // Generic adaptor is templated and therefore defined inline in the header.4652 // We cannot use the Op class here as it is an incomplete type (we have a4653 // circular reference between the two).4654 // Use a template trick to make the constructor be instantiated at call site4655 // when the op class is complete.4656 constructor = genericAdaptor.addConstructor(4657 MethodParameter("RangeT", "values"), MethodParameter("LateInst", "op"));4658 constructor->addTemplateParam("LateInst = " + op.getCppClassName());4659 constructor->addTemplateParam(4660 "= std::enable_if_t<std::is_same_v<LateInst, " + op.getCppClassName() +4661 ">>");4662 constructor->addMemberInitializer("Base", "op");4663 constructor->addMemberInitializer("odsOperands", "values");4664 }4665 4666 std::string sizeAttrInit;4667 if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {4668 if (op.getDialect().usePropertiesForAttributes())4669 sizeAttrInit =4670 formatv(adapterSegmentSizeAttrInitCodeProperties,4671 llvm::formatv("getProperties().operandSegmentSizes"));4672 else4673 sizeAttrInit = formatv(adapterSegmentSizeAttrInitCode,4674 emitHelper.getAttr(operandSegmentAttrName));4675 }4676 generateNamedOperandGetters(op, genericAdaptor,4677 /*genericAdaptorBase=*/&genericAdaptorBase,4678 /*sizeAttrInit=*/sizeAttrInit,4679 /*rangeType=*/"RangeT",4680 /*rangeElementType=*/"ValueT",4681 /*rangeBeginCall=*/"odsOperands.begin()",4682 /*rangeSizeCall=*/"odsOperands.size()",4683 /*getOperandCallPattern=*/"odsOperands[{0}]");4684 4685 // Any invalid overlap for `getOperands` will have been diagnosed before4686 // here already.4687 if (auto *m = genericAdaptor.addMethod("RangeT", "getOperands"))4688 m->body() << " return odsOperands;";4689 4690 fctx.withBuilder("::mlir::Builder(odsAttrs.getContext())");4691 4692 // Generate named accessor with Attribute return type.4693 auto emitAttrWithStorageType = [&](StringRef name, StringRef emitName,4694 Attribute attr) {4695 // The method body is trivial if the attribute does not have a default4696 // value, in which case the default value may be arbitrary code.4697 auto *method = genericAdaptorBase.addMethod(4698 attr.getStorageType(), emitName + "Attr",4699 attr.hasDefaultValue() || !useProperties ? Method::Properties::None4700 : Method::Properties::Inline);4701 ERROR_IF_PRUNED(method, "Adaptor::" + emitName + "Attr", op);4702 auto &body = method->body().indent();4703 if (!useProperties)4704 body << "assert(odsAttrs && \"no attributes when constructing "4705 "adapter\");\n";4706 body << formatv(4707 "auto attr = ::llvm::{1}<{2}>({0});\n", emitHelper.getAttr(name),4708 attr.hasDefaultValue() || attr.isOptional() ? "dyn_cast_or_null"4709 : "cast",4710 attr.getStorageType());4711 4712 if (attr.hasDefaultValue() && attr.isOptional()) {4713 // Use the default value if attribute is not set.4714 // TODO: this is inefficient, we are recreating the attribute for every4715 // call. This should be set instead.4716 std::string defaultValue =4717 std::string(tgfmt(attr.getConstBuilderTemplate(), &fctx,4718 tgfmt(attr.getDefaultValue(), &fctx)));4719 body << "if (!attr)\n attr = " << defaultValue << ";\n";4720 }4721 body << "return attr;\n";4722 };4723 4724 if (useProperties) {4725 auto *m = genericAdaptorBase.addInlineMethod("const Properties &",4726 "getProperties");4727 ERROR_IF_PRUNED(m, "Adaptor::getProperties", op);4728 m->body() << " return properties;";4729 }4730 {4731 auto *m = genericAdaptorBase.addInlineMethod("::mlir::DictionaryAttr",4732 "getAttributes");4733 ERROR_IF_PRUNED(m, "Adaptor::getAttributes", op);4734 m->body() << " return odsAttrs;";4735 }4736 for (auto &namedProp : op.getProperties()) {4737 std::string name = op.getGetterName(namedProp.name);4738 emitPropGetter(genericAdaptorBase, op, name, namedProp.prop);4739 }4740 4741 for (auto &namedAttr : op.getAttributes()) {4742 const auto &name = namedAttr.name;4743 const auto &attr = namedAttr.attr;4744 if (attr.isDerivedAttr())4745 continue;4746 std::string emitName = op.getGetterName(name);4747 emitAttrWithStorageType(name, emitName, attr);4748 emitAttrGetterWithReturnType(fctx, genericAdaptorBase, op, emitName, attr);4749 }4750 4751 unsigned numRegions = op.getNumRegions();4752 for (unsigned i = 0; i < numRegions; ++i) {4753 const auto ®ion = op.getRegion(i);4754 if (region.name.empty())4755 continue;4756 4757 // Generate the accessors for a variadic region.4758 std::string name = op.getGetterName(region.name);4759 if (region.isVariadic()) {4760 auto *m = genericAdaptorBase.addInlineMethod("::mlir::RegionRange", name);4761 ERROR_IF_PRUNED(m, "Adaptor::" + name, op);4762 m->body() << formatv(" return odsRegions.drop_front({0});", i);4763 continue;4764 }4765 4766 auto *m = genericAdaptorBase.addInlineMethod("::mlir::Region &", name);4767 ERROR_IF_PRUNED(m, "Adaptor::" + name, op);4768 m->body() << formatv(" return *odsRegions[{0}];", i);4769 }4770 if (numRegions > 0) {4771 // Any invalid overlap for `getRegions` will have been diagnosed before4772 // here already.4773 if (auto *m = genericAdaptorBase.addInlineMethod("::mlir::RegionRange",4774 "getRegions"))4775 m->body() << " return odsRegions;";4776 }4777 4778 StringRef genericAdaptorClassName = genericAdaptor.getClassName();4779 adaptor.addParent(ParentClass(genericAdaptorClassName))4780 .addTemplateParam("::mlir::ValueRange");4781 adaptor.declare<VisibilityDeclaration>(Visibility::Public);4782 adaptor.declare<UsingDeclaration>(genericAdaptorClassName +4783 "::" + genericAdaptorClassName);4784 {4785 // Constructor taking the Op as single parameter.4786 auto *constructor =4787 adaptor.addConstructor(MethodParameter(op.getCppClassName(), "op"));4788 constructor->addMemberInitializer(genericAdaptorClassName,4789 "op->getOperands(), op");4790 }4791 4792 // Add verification function.4793 addVerification();4794 4795 genericAdaptorBase.finalize();4796 genericAdaptor.finalize();4797 adaptor.finalize();4798}4799 4800void OpOperandAdaptorEmitter::addVerification() {4801 auto *method = adaptor.addMethod("::llvm::LogicalResult", "verify",4802 MethodParameter("::mlir::Location", "loc"));4803 ERROR_IF_PRUNED(method, "verify", op);4804 auto &body = method->body();4805 bool useProperties = emitHelper.hasProperties();4806 4807 FmtContext verifyCtx;4808 populateSubstitutions(emitHelper, verifyCtx);4809 genPropertyVerifier(emitHelper, verifyCtx, body, staticVerifierEmitter);4810 genAttributeVerifier(emitHelper, verifyCtx, body, staticVerifierEmitter,4811 useProperties);4812 4813 body << " return ::mlir::success();";4814}4815 4816void OpOperandAdaptorEmitter::emitDecl(4817 const Operator &op,4818 const StaticVerifierFunctionEmitter &staticVerifierEmitter,4819 raw_ostream &os) {4820 OpOperandAdaptorEmitter emitter(op, staticVerifierEmitter);4821 {4822 NamespaceEmitter ns(os, "detail");4823 emitter.genericAdaptorBase.writeDeclTo(os);4824 }4825 emitter.genericAdaptor.writeDeclTo(os);4826 emitter.adaptor.writeDeclTo(os);4827}4828 4829void OpOperandAdaptorEmitter::emitDef(4830 const Operator &op,4831 const StaticVerifierFunctionEmitter &staticVerifierEmitter,4832 raw_ostream &os) {4833 OpOperandAdaptorEmitter emitter(op, staticVerifierEmitter);4834 {4835 NamespaceEmitter ns(os, "detail");4836 emitter.genericAdaptorBase.writeDefTo(os);4837 }4838 emitter.genericAdaptor.writeDefTo(os);4839 emitter.adaptor.writeDefTo(os);4840}4841 4842/// Emit the class declarations or definitions for the given op defs.4843static void emitOpClasses(4844 const RecordKeeper &records, ArrayRef<const Record *> defs, raw_ostream &os,4845 const StaticVerifierFunctionEmitter &staticVerifierEmitter, bool emitDecl) {4846 if (defs.empty())4847 return;4848 4849 for (auto *def : defs) {4850 Operator op(*def);4851 if (emitDecl) {4852 {4853 NamespaceEmitter emitter(os, op.getCppNamespace());4854 os << formatv(opCommentHeader, op.getQualCppClassName(),4855 "declarations");4856 OpOperandAdaptorEmitter::emitDecl(op, staticVerifierEmitter, os);4857 OpEmitter::emitDecl(op, os, staticVerifierEmitter);4858 }4859 // Emit the TypeID explicit specialization to have a single definition.4860 if (!op.getCppNamespace().empty())4861 os << "MLIR_DECLARE_EXPLICIT_TYPE_ID(" << op.getCppNamespace()4862 << "::" << op.getCppClassName() << ")\n\n";4863 } else {4864 {4865 NamespaceEmitter emitter(os, op.getCppNamespace());4866 os << formatv(opCommentHeader, op.getQualCppClassName(), "definitions");4867 OpOperandAdaptorEmitter::emitDef(op, staticVerifierEmitter, os);4868 OpEmitter::emitDef(op, os, staticVerifierEmitter);4869 }4870 // Emit the TypeID explicit specialization to have a single definition.4871 if (!op.getCppNamespace().empty())4872 os << "MLIR_DEFINE_EXPLICIT_TYPE_ID(" << op.getCppNamespace()4873 << "::" << op.getCppClassName() << ")\n\n";4874 }4875 }4876}4877 4878/// Emit the declarations for the provided op classes.4879static void emitOpClassDecls(const RecordKeeper &records,4880 ArrayRef<const Record *> defs, raw_ostream &os) {4881 // First emit forward declaration for each class, this allows them to refer4882 // to each others in traits for example.4883 for (const Record *def : defs) {4884 Operator op(*def);4885 NamespaceEmitter emitter(os, op.getCppNamespace());4886 tblgen::emitSummaryAndDescComments(os, op.getSummary(),4887 op.getDescription());4888 os << "class " << op.getCppClassName() << ";\n";4889 }4890 4891 // Emit the op class declarations.4892 IfDefEmitter scope(os, "GET_OP_CLASSES");4893 if (defs.empty())4894 return;4895 StaticVerifierFunctionEmitter staticVerifierEmitter(os, records);4896 staticVerifierEmitter.collectOpConstraints(defs);4897 emitOpClasses(records, defs, os, staticVerifierEmitter,4898 /*emitDecl=*/true);4899}4900 4901/// Emit the definitions for the provided op classes.4902static void emitOpClassDefs(const RecordKeeper &records,4903 ArrayRef<const Record *> defs, raw_ostream &os,4904 StringRef constraintPrefix = "") {4905 if (defs.empty())4906 return;4907 4908 // Generate all of the locally instantiated methods first.4909 StaticVerifierFunctionEmitter staticVerifierEmitter(os, records,4910 constraintPrefix);4911 os << formatv(opCommentHeader, "Local Utility Method", "Definitions");4912 staticVerifierEmitter.collectOpConstraints(defs);4913 staticVerifierEmitter.emitOpConstraints();4914 4915 // Emit the classes.4916 emitOpClasses(records, defs, os, staticVerifierEmitter,4917 /*emitDecl=*/false);4918}4919 4920/// Emit op declarations for all op records.4921static bool emitOpDecls(const RecordKeeper &records, raw_ostream &os) {4922 emitSourceFileHeader("Op Declarations", os, records);4923 4924 std::vector<const Record *> defs = getRequestedOpDefinitions(records);4925 emitOpClassDecls(records, defs, os);4926 4927 // If we are generating sharded op definitions, emit the sharded op4928 // registration hooks.4929 SmallVector<ArrayRef<const Record *>, 4> shardedDefs;4930 shardOpDefinitions(defs, shardedDefs);4931 if (defs.empty() || shardedDefs.size() <= 1)4932 return false;4933 4934 Dialect dialect = Operator(defs.front()).getDialect();4935 DialectNamespaceEmitter ns(os, dialect);4936 4937 const char *const opRegistrationHook =4938 "void register{0}Operations{1}({2}::{0} *dialect);\n";4939 os << formatv(opRegistrationHook, dialect.getCppClassName(), "",4940 dialect.getCppNamespace());4941 for (unsigned i = 0; i < shardedDefs.size(); ++i) {4942 os << formatv(opRegistrationHook, dialect.getCppClassName(), i,4943 dialect.getCppNamespace());4944 }4945 4946 return false;4947}4948 4949/// Generate the dialect op registration hook and the op class definitions for a4950/// shard of ops.4951static void emitOpDefShard(const RecordKeeper &records,4952 ArrayRef<const Record *> defs,4953 const Dialect &dialect, unsigned shardIndex,4954 unsigned shardCount, raw_ostream &os) {4955 std::string shardGuard = "GET_OP_DEFS_";4956 std::string indexStr = std::to_string(shardIndex);4957 shardGuard += indexStr;4958 IfDefEmitter scope(os, shardGuard);4959 4960 // Emit the op registration hook in the first shard.4961 const char *const opRegistrationHook =4962 "void {0}::register{1}Operations{2}({0}::{1} *dialect) {{\n";4963 if (shardIndex == 0) {4964 os << formatv(opRegistrationHook, dialect.getCppNamespace(),4965 dialect.getCppClassName(), "");4966 for (unsigned i = 0; i < shardCount; ++i) {4967 os << formatv(" {0}::register{1}Operations{2}(dialect);\n",4968 dialect.getCppNamespace(), dialect.getCppClassName(), i);4969 }4970 os << "}\n";4971 }4972 4973 // Generate the per-shard op registration hook.4974 os << formatv(opCommentHeader, dialect.getCppClassName(),4975 "Op Registration Hook")4976 << formatv(opRegistrationHook, dialect.getCppNamespace(),4977 dialect.getCppClassName(), shardIndex);4978 for (const Record *def : defs) {4979 os << formatv(" ::mlir::RegisteredOperationName::insert<{0}>(*dialect);\n",4980 Operator(def).getQualCppClassName());4981 }4982 os << "}\n";4983 4984 // Generate the per-shard op definitions.4985 emitOpClassDefs(records, defs, os, indexStr);4986}4987 4988/// Emit op definitions for all op records.4989static bool emitOpDefs(const RecordKeeper &records, raw_ostream &os) {4990 emitSourceFileHeader("Op Definitions", os, records);4991 4992 std::vector<const Record *> defs = getRequestedOpDefinitions(records);4993 SmallVector<ArrayRef<const Record *>, 4> shardedDefs;4994 shardOpDefinitions(defs, shardedDefs);4995 4996 // If no shard was requested, emit the regular op list and class definitions.4997 if (shardedDefs.size() == 1) {4998 {4999 IfDefEmitter scope(os, "GET_OP_LIST");5000 interleave(5001 defs, os,5002 [&](const Record *def) { os << Operator(def).getQualCppClassName(); },5003 ",\n");5004 }5005 {5006 IfDefEmitter scope(os, "GET_OP_CLASSES");5007 emitOpClassDefs(records, defs, os);5008 }5009 return false;5010 }5011 5012 if (defs.empty())5013 return false;5014 Dialect dialect = Operator(defs.front()).getDialect();5015 for (auto [idx, value] : llvm::enumerate(shardedDefs)) {5016 emitOpDefShard(records, value, dialect, idx, shardedDefs.size(), os);5017 }5018 return false;5019}5020 5021static mlir::GenRegistration5022 genOpDecls("gen-op-decls", "Generate op declarations",5023 [](const RecordKeeper &records, raw_ostream &os) {5024 return emitOpDecls(records, os);5025 });5026 5027static mlir::GenRegistration genOpDefs("gen-op-defs", "Generate op definitions",5028 [](const RecordKeeper &records,5029 raw_ostream &os) {5030 return emitOpDefs(records, os);5031 });5032