894 lines · cpp
1//===- Operator.cpp - Operator class --------------------------------------===//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// Operator wrapper to simplify using TableGen Record defining a MLIR Op.10//11//===----------------------------------------------------------------------===//12 13#include "mlir/TableGen/Operator.h"14#include "mlir/TableGen/Argument.h"15#include "mlir/TableGen/Predicate.h"16#include "mlir/TableGen/Trait.h"17#include "mlir/TableGen/Type.h"18#include "llvm/ADT/STLExtras.h"19#include "llvm/ADT/Sequence.h"20#include "llvm/ADT/SmallPtrSet.h"21#include "llvm/ADT/StringExtras.h"22#include "llvm/ADT/TypeSwitch.h"23#include "llvm/Support/Debug.h"24#include "llvm/Support/ErrorHandling.h"25#include "llvm/Support/FormatVariadic.h"26#include "llvm/TableGen/Error.h"27#include "llvm/TableGen/Record.h"28 29#define DEBUG_TYPE "mlir-tblgen-operator"30 31using namespace mlir;32using namespace mlir::tblgen;33 34using llvm::DagInit;35using llvm::DefInit;36using llvm::Init;37using llvm::ListInit;38using llvm::Record;39using llvm::StringInit;40 41Operator::Operator(const Record &def)42 : dialect(def.getValueAsDef("opDialect")), def(def) {43 // The first `_` in the op's TableGen def name is treated as separating the44 // dialect prefix and the op class name. The dialect prefix will be ignored if45 // not empty. Otherwise, if def name starts with a `_`, the `_` is considered46 // as part of the class name.47 StringRef prefix;48 std::tie(prefix, cppClassName) = def.getName().split('_');49 if (prefix.empty()) {50 // Class name with a leading underscore and without dialect prefix51 cppClassName = def.getName();52 } else if (cppClassName.empty()) {53 // Class name without dialect prefix54 cppClassName = prefix;55 }56 57 cppNamespace = def.getValueAsString("cppNamespace");58 59 populateOpStructure();60 assertInvariants();61}62 63std::string Operator::getOperationName() const {64 auto prefix = dialect.getName();65 auto opName = def.getValueAsString("opName");66 if (prefix.empty())67 return std::string(opName);68 return std::string(llvm::formatv("{0}.{1}", prefix, opName));69}70 71std::string Operator::getAdaptorName() const {72 return std::string(llvm::formatv("{0}Adaptor", getCppClassName()));73}74 75std::string Operator::getGenericAdaptorName() const {76 return std::string(llvm::formatv("{0}GenericAdaptor", getCppClassName()));77}78 79/// Assert the invariants of accessors generated for the given name.80static void assertAccessorInvariants(const Operator &op, StringRef name) {81 std::string accessorName =82 convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);83 84 // Functor used to detect when an accessor will cause an overlap with an85 // operation API.86 //87 // There are a little bit more invasive checks possible for cases where not88 // all ops have the trait that would cause overlap. For many cases here,89 // renaming would be better (e.g., we can only guard in limited manner90 // against methods from traits and interfaces here, so avoiding these in op91 // definition is safer).92 auto nameOverlapsWithOpAPI = [&](StringRef newName) {93 if (newName == "AttributeNames" || newName == "Attributes" ||94 newName == "Operation")95 return true;96 if (newName == "Operands")97 return op.getNumOperands() != 1 || op.getNumVariableLengthOperands() != 1;98 if (newName == "Regions")99 return op.getNumRegions() != 1 || op.getNumVariadicRegions() != 1;100 if (newName == "Type")101 return op.getNumResults() != 1;102 return false;103 };104 if (nameOverlapsWithOpAPI(accessorName)) {105 // This error could be avoided in situations where the final function is106 // identical, but preferably the op definition should avoid using generic107 // names.108 PrintFatalError(op.getLoc(), "generated accessor for `" + name +109 "` overlaps with a default one; please "110 "rename to avoid overlap");111 }112}113 114void Operator::assertInvariants() const {115 // Check that the name of arguments/results/regions/successors don't overlap.116 DenseMap<StringRef, StringRef> existingNames;117 auto checkName = [&](StringRef name, StringRef entity) {118 if (name.empty())119 return;120 auto insertion = existingNames.insert({name, entity});121 if (insertion.second) {122 // Assert invariants for accessors generated for this name.123 assertAccessorInvariants(*this, name);124 return;125 }126 if (entity == insertion.first->second)127 PrintFatalError(getLoc(), "op has a conflict with two " + entity +128 " having the same name '" + name + "'");129 PrintFatalError(getLoc(), "op has a conflict with " +130 insertion.first->second + " and " + entity +131 " both having an entry with the name '" +132 name + "'");133 };134 // Check operands amongst themselves.135 for (int i : llvm::seq<int>(0, getNumOperands()))136 checkName(getOperand(i).name, "operands");137 138 // Check results amongst themselves and against operands.139 for (int i : llvm::seq<int>(0, getNumResults()))140 checkName(getResult(i).name, "results");141 142 // Check regions amongst themselves and against operands and results.143 for (int i : llvm::seq<int>(0, getNumRegions()))144 checkName(getRegion(i).name, "regions");145 146 // Check successors amongst themselves and against operands, results, and147 // regions.148 for (int i : llvm::seq<int>(0, getNumSuccessors()))149 checkName(getSuccessor(i).name, "successors");150}151 152StringRef Operator::getDialectName() const { return dialect.getName(); }153 154StringRef Operator::getCppClassName() const { return cppClassName; }155 156std::string Operator::getQualCppClassName() const {157 if (cppNamespace.empty())158 return std::string(cppClassName);159 return std::string(llvm::formatv("{0}::{1}", cppNamespace, cppClassName));160}161 162StringRef Operator::getCppNamespace() const { return cppNamespace; }163 164int Operator::getNumResults() const {165 const DagInit *results = def.getValueAsDag("results");166 return results->getNumArgs();167}168 169StringRef Operator::getExtraClassDeclaration() const {170 constexpr auto attr = "extraClassDeclaration";171 if (def.isValueUnset(attr))172 return {};173 return def.getValueAsString(attr);174}175 176StringRef Operator::getExtraClassDefinition() const {177 constexpr auto attr = "extraClassDefinition";178 if (def.isValueUnset(attr))179 return {};180 return def.getValueAsString(attr);181}182 183const Record &Operator::getDef() const { return def; }184 185bool Operator::skipDefaultBuilders() const {186 return def.getValueAsBit("skipDefaultBuilders");187}188 189auto Operator::result_begin() const -> const_value_iterator {190 return results.begin();191}192 193auto Operator::result_end() const -> const_value_iterator {194 return results.end();195}196 197auto Operator::getResults() const -> const_value_range {198 return {result_begin(), result_end()};199}200 201TypeConstraint Operator::getResultTypeConstraint(int index) const {202 const DagInit *results = def.getValueAsDag("results");203 return TypeConstraint(cast<DefInit>(results->getArg(index)));204}205 206StringRef Operator::getResultName(int index) const {207 const DagInit *results = def.getValueAsDag("results");208 return results->getArgNameStr(index);209}210 211auto Operator::getResultDecorators(int index) const -> var_decorator_range {212 const Record *result =213 cast<DefInit>(def.getValueAsDag("results")->getArg(index))->getDef();214 if (!result->isSubClassOf("OpVariable"))215 return var_decorator_range(nullptr, nullptr);216 return *result->getValueAsListInit("decorators");217}218 219unsigned Operator::getNumVariableLengthResults() const {220 return llvm::count_if(results, [](const NamedTypeConstraint &c) {221 return c.constraint.isVariableLength();222 });223}224 225unsigned Operator::getNumVariableLengthOperands() const {226 return llvm::count_if(operands, [](const NamedTypeConstraint &c) {227 return c.constraint.isVariableLength();228 });229}230 231bool Operator::hasSingleVariadicArg() const {232 return getNumArgs() == 1 && isa<NamedTypeConstraint *>(getArg(0)) &&233 getOperand(0).isVariadic();234}235 236Operator::arg_iterator Operator::arg_begin() const { return arguments.begin(); }237 238Operator::arg_iterator Operator::arg_end() const { return arguments.end(); }239 240Operator::arg_range Operator::getArgs() const {241 return {arg_begin(), arg_end()};242}243 244StringRef Operator::getArgName(int index) const {245 const DagInit *argumentValues = def.getValueAsDag("arguments");246 return argumentValues->getArgNameStr(index);247}248 249auto Operator::getArgDecorators(int index) const -> var_decorator_range {250 const Record *arg =251 cast<DefInit>(def.getValueAsDag("arguments")->getArg(index))->getDef();252 if (!arg->isSubClassOf("OpVariable"))253 return var_decorator_range(nullptr, nullptr);254 return *arg->getValueAsListInit("decorators");255}256 257const Trait *Operator::getTrait(StringRef trait) const {258 for (const auto &t : traits) {259 if (const auto *traitDef = dyn_cast<NativeTrait>(&t)) {260 if (traitDef->getFullyQualifiedTraitName() == trait)261 return traitDef;262 } else if (const auto *traitDef = dyn_cast<InternalTrait>(&t)) {263 if (traitDef->getFullyQualifiedTraitName() == trait)264 return traitDef;265 } else if (const auto *traitDef = dyn_cast<InterfaceTrait>(&t)) {266 if (traitDef->getFullyQualifiedTraitName() == trait)267 return traitDef;268 }269 }270 return nullptr;271}272 273auto Operator::region_begin() const -> const_region_iterator {274 return regions.begin();275}276auto Operator::region_end() const -> const_region_iterator {277 return regions.end();278}279auto Operator::getRegions() const280 -> llvm::iterator_range<const_region_iterator> {281 return {region_begin(), region_end()};282}283 284unsigned Operator::getNumRegions() const { return regions.size(); }285 286const NamedRegion &Operator::getRegion(unsigned index) const {287 return regions[index];288}289 290unsigned Operator::getNumVariadicRegions() const {291 return llvm::count_if(regions,292 [](const NamedRegion &c) { return c.isVariadic(); });293}294 295auto Operator::successor_begin() const -> const_successor_iterator {296 return successors.begin();297}298auto Operator::successor_end() const -> const_successor_iterator {299 return successors.end();300}301auto Operator::getSuccessors() const302 -> llvm::iterator_range<const_successor_iterator> {303 return {successor_begin(), successor_end()};304}305 306unsigned Operator::getNumSuccessors() const { return successors.size(); }307 308const NamedSuccessor &Operator::getSuccessor(unsigned index) const {309 return successors[index];310}311 312unsigned Operator::getNumVariadicSuccessors() const {313 return llvm::count_if(successors,314 [](const NamedSuccessor &c) { return c.isVariadic(); });315}316 317auto Operator::trait_begin() const -> const_trait_iterator {318 return traits.begin();319}320auto Operator::trait_end() const -> const_trait_iterator {321 return traits.end();322}323auto Operator::getTraits() const -> llvm::iterator_range<const_trait_iterator> {324 return {trait_begin(), trait_end()};325}326 327auto Operator::attribute_begin() const -> const_attribute_iterator {328 return attributes.begin();329}330auto Operator::attribute_end() const -> const_attribute_iterator {331 return attributes.end();332}333auto Operator::getAttributes() const334 -> llvm::iterator_range<const_attribute_iterator> {335 return {attribute_begin(), attribute_end()};336}337auto Operator::attribute_begin() -> attribute_iterator {338 return attributes.begin();339}340auto Operator::attribute_end() -> attribute_iterator {341 return attributes.end();342}343auto Operator::getAttributes() -> llvm::iterator_range<attribute_iterator> {344 return {attribute_begin(), attribute_end()};345}346 347auto Operator::operand_begin() const -> const_value_iterator {348 return operands.begin();349}350auto Operator::operand_end() const -> const_value_iterator {351 return operands.end();352}353auto Operator::getOperands() const -> const_value_range {354 return {operand_begin(), operand_end()};355}356 357auto Operator::getArg(int index) const -> Argument { return arguments[index]; }358 359bool Operator::isVariadic() const {360 return any_of(llvm::concat<const NamedTypeConstraint>(operands, results),361 [](const NamedTypeConstraint &op) { return op.isVariadic(); });362}363 364void Operator::populateTypeInferenceInfo(365 const llvm::StringMap<int> &argumentsAndResultsIndex) {366 // If the type inference op interface is not registered, then do not attempt367 // to determine if the result types an be inferred.368 auto &recordKeeper = def.getRecords();369 auto *inferTrait = recordKeeper.getDef(inferTypeOpInterface);370 allResultsHaveKnownTypes = false;371 if (!inferTrait)372 return;373 374 // If there are no results, the skip this else the build method generated375 // overlaps with another autogenerated builder.376 if (getNumResults() == 0)377 return;378 379 // Skip ops with variadic or optional results.380 if (getNumVariableLengthResults() > 0)381 return;382 383 // Skip cases currently being custom generated.384 // TODO: Remove special cases.385 if (getTrait("::mlir::OpTrait::SameOperandsAndResultType")) {386 // Check for a non-variable length operand to use as the type anchor.387 auto *operandI = llvm::find_if(arguments, [](const Argument &arg) {388 NamedTypeConstraint *operand =389 llvm::dyn_cast_if_present<NamedTypeConstraint *>(arg);390 return operand && !operand->isVariableLength();391 });392 if (operandI == arguments.end())393 return;394 395 // All result types are inferred from the operand type.396 int operandIdx = operandI - arguments.begin();397 for (int i = 0; i < getNumResults(); ++i)398 resultTypeMapping.emplace_back(operandIdx, "$_self");399 400 allResultsHaveKnownTypes = true;401 traits.push_back(Trait::create(inferTrait->getDefInit()));402 return;403 }404 405 /// This struct represents a node in this operation's result type inferenece406 /// graph. Each node has a list of incoming type inference edges `sources`.407 /// Each edge represents a "source" from which the result type can be408 /// inferred, either an operand (leaf) or another result (node). When a node409 /// is known to have a fully-inferred type, `inferred` is set to true.410 struct ResultTypeInference {411 /// The list of incoming type inference edges.412 SmallVector<InferredResultType> sources;413 /// This flag is set to true when the result type is known to be inferrable.414 bool inferred = false;415 };416 417 // This vector represents the type inference graph, with one node for each418 // operation result. The nth element is the node for the nth result.419 SmallVector<ResultTypeInference> inference(getNumResults(), {});420 421 // For all results whose types are buildable, initialize their type inference422 // nodes with an edge to themselves. Mark those nodes are fully-inferred.423 for (auto [idx, infer] : llvm::enumerate(inference)) {424 if (getResult(idx).constraint.getBuilderCall()) {425 infer.sources.emplace_back(InferredResultType::mapResultIndex(idx),426 "$_self");427 infer.inferred = true;428 }429 }430 431 // Use `AllTypesMatch` and `TypesMatchWith` operation traits to build the432 // result type inference graph.433 for (const Trait &trait : traits) {434 const Record &def = trait.getDef();435 436 // If the infer type op interface was manually added, then treat it as437 // intention that the op needs special handling.438 // TODO: Reconsider whether to always generate, this is more conservative439 // and keeps existing behavior so starting that way for now.440 if (def.isSubClassOf(441 llvm::formatv("{0}::Trait", inferTypeOpInterface).str()))442 return;443 if (const auto *traitDef = dyn_cast<InterfaceTrait>(&trait))444 if (&traitDef->getDef() == inferTrait)445 return;446 447 // The `TypesMatchWith` trait represents a 1 -> 1 type inference edge with a448 // type transformer.449 if (def.isSubClassOf("TypesMatchWith")) {450 int target = argumentsAndResultsIndex.lookup(def.getValueAsString("rhs"));451 // Ignore operand type inference.452 if (InferredResultType::isArgIndex(target))453 continue;454 int resultIndex = InferredResultType::unmapResultIndex(target);455 ResultTypeInference &infer = inference[resultIndex];456 // If the type of the result has already been inferred, do nothing.457 if (infer.inferred)458 continue;459 int sourceIndex =460 argumentsAndResultsIndex.lookup(def.getValueAsString("lhs"));461 infer.sources.emplace_back(sourceIndex,462 def.getValueAsString("transformer").str());463 // Locally propagate inferredness.464 infer.inferred =465 InferredResultType::isArgIndex(sourceIndex) ||466 inference[InferredResultType::unmapResultIndex(sourceIndex)].inferred;467 continue;468 }469 470 // The `ShapedTypeMatchesElementCountAndTypes` trait represents a 1 -> 1471 // type inference edge where a shaped type matches element count and types472 // of variadic elements.473 if (def.isSubClassOf("ShapedTypeMatchesElementCountAndTypes")) {474 StringRef shapedArg = def.getValueAsString("shaped");475 StringRef elementsArg = def.getValueAsString("elements");476 477 int shapedIndex = argumentsAndResultsIndex.lookup(shapedArg);478 int elementsIndex = argumentsAndResultsIndex.lookup(elementsArg);479 480 // Handle result type inference from shaped type to variadic elements.481 if (InferredResultType::isResultIndex(elementsIndex) &&482 InferredResultType::isArgIndex(shapedIndex)) {483 int resultIndex = InferredResultType::unmapResultIndex(elementsIndex);484 ResultTypeInference &infer = inference[resultIndex];485 if (!infer.inferred) {486 infer.sources.emplace_back(487 shapedIndex,488 "::llvm::SmallVector<::mlir::Type>(::llvm::cast<::mlir::"489 "ShapedType>($_self).getNumElements(), "490 "::llvm::cast<::mlir::ShapedType>($_self).getElementType())");491 infer.inferred = true;492 }493 }494 495 // Type inference in the opposite direction is not possible as the actual496 // shaped type can't be inferred from the variadic elements.497 498 continue;499 }500 501 if (!def.isSubClassOf("AllTypesMatch"))502 continue;503 504 auto values = def.getValueAsListOfStrings("values");505 // The `AllTypesMatch` trait represents an N <-> N fanin and fanout. That506 // is, every result type has an edge from every other type. However, if any507 // one of the values refers to an operand or a result with a fully-inferred508 // type, we can infer all other types from that value. Try to find a509 // fully-inferred type in the list.510 std::optional<int> fullyInferredIndex;511 SmallVector<int> resultIndices;512 for (StringRef name : values) {513 int index = argumentsAndResultsIndex.lookup(name);514 if (InferredResultType::isResultIndex(index))515 resultIndices.push_back(InferredResultType::unmapResultIndex(index));516 if (InferredResultType::isArgIndex(index) ||517 inference[InferredResultType::unmapResultIndex(index)].inferred)518 fullyInferredIndex = index;519 }520 if (fullyInferredIndex) {521 // Make the fully-inferred type the only source for all results that522 // aren't already inferred -- a 1 -> N fanout.523 for (int resultIndex : resultIndices) {524 ResultTypeInference &infer = inference[resultIndex];525 if (!infer.inferred) {526 infer.sources.assign(1, {*fullyInferredIndex, "$_self"});527 infer.inferred = true;528 }529 }530 } else {531 // Add an edge between every result and every other type; N <-> N.532 for (int resultIndex : resultIndices) {533 for (int otherResultIndex : resultIndices) {534 if (resultIndex == otherResultIndex)535 continue;536 inference[resultIndex].sources.emplace_back(537 InferredResultType::unmapResultIndex(otherResultIndex), "$_self");538 }539 }540 }541 }542 543 // Propagate inferredness until a fixed point.544 std::vector<ResultTypeInference *> worklist;545 for (ResultTypeInference &infer : inference)546 if (!infer.inferred)547 worklist.push_back(&infer);548 bool changed;549 do {550 changed = false;551 for (auto cur = worklist.begin(); cur != worklist.end();) {552 ResultTypeInference &infer = **cur;553 554 InferredResultType *iter =555 llvm::find_if(infer.sources, [&](const InferredResultType &source) {556 assert(InferredResultType::isResultIndex(source.getIndex()));557 return inference[InferredResultType::unmapResultIndex(558 source.getIndex())]559 .inferred;560 });561 if (iter == infer.sources.end()) {562 ++cur;563 continue;564 }565 566 changed = true;567 infer.inferred = true;568 // Make this the only source for the result. This breaks any cycles.569 infer.sources.assign(1, *iter);570 cur = worklist.erase(cur);571 }572 } while (changed);573 574 allResultsHaveKnownTypes = worklist.empty();575 576 // If the types could be computed, then add type inference trait.577 if (allResultsHaveKnownTypes) {578 traits.push_back(Trait::create(inferTrait->getDefInit()));579 for (const ResultTypeInference &infer : inference)580 resultTypeMapping.push_back(infer.sources.front());581 }582}583 584void Operator::populateOpStructure() {585 auto &recordKeeper = def.getRecords();586 auto *typeConstraintClass = recordKeeper.getClass("TypeConstraint");587 auto *attrClass = recordKeeper.getClass("Attr");588 auto *propertyClass = recordKeeper.getClass("Property");589 auto *derivedAttrClass = recordKeeper.getClass("DerivedAttr");590 auto *opVarClass = recordKeeper.getClass("OpVariable");591 numNativeAttributes = 0;592 593 const DagInit *argumentValues = def.getValueAsDag("arguments");594 unsigned numArgs = argumentValues->getNumArgs();595 596 // Mapping from name of to argument or result index. Arguments are indexed597 // to match getArg index, while the results are negatively indexed.598 llvm::StringMap<int> argumentsAndResultsIndex;599 600 // Handle operands and native attributes.601 for (unsigned i = 0; i != numArgs; ++i) {602 auto *arg = argumentValues->getArg(i);603 auto givenName = argumentValues->getArgNameStr(i);604 auto *argDefInit = dyn_cast<DefInit>(arg);605 if (!argDefInit)606 PrintFatalError(def.getLoc(),607 Twine("undefined type for argument #") + Twine(i));608 const Record *argDef = argDefInit->getDef();609 if (argDef->isSubClassOf(opVarClass))610 argDef = argDef->getValueAsDef("constraint");611 612 if (argDef->isSubClassOf(typeConstraintClass)) {613 operands.push_back(614 NamedTypeConstraint{givenName, TypeConstraint(argDef)});615 } else if (argDef->isSubClassOf(attrClass)) {616 if (givenName.empty())617 PrintFatalError(argDef->getLoc(), "attributes must be named");618 if (argDef->isSubClassOf(derivedAttrClass))619 PrintFatalError(argDef->getLoc(),620 "derived attributes not allowed in argument list");621 attributes.push_back({givenName, Attribute(argDef)});622 ++numNativeAttributes;623 } else if (argDef->isSubClassOf(propertyClass)) {624 if (givenName.empty())625 PrintFatalError(argDef->getLoc(), "properties must be named");626 properties.push_back({givenName, Property(argDef)});627 } else {628 PrintFatalError(def.getLoc(),629 "unexpected def type; only defs deriving "630 "from TypeConstraint or Attr or Property are allowed");631 }632 if (!givenName.empty())633 argumentsAndResultsIndex[givenName] = i;634 }635 636 // Handle derived attributes.637 for (const auto &val : def.getValues()) {638 if (auto *record = dyn_cast<llvm::RecordRecTy>(val.getType())) {639 if (!record->isSubClassOf(attrClass))640 continue;641 if (!record->isSubClassOf(derivedAttrClass))642 PrintFatalError(def.getLoc(),643 "unexpected Attr where only DerivedAttr is allowed");644 645 if (record->getClasses().size() != 1) {646 PrintFatalError(647 def.getLoc(),648 "unsupported attribute modelling, only single class expected");649 }650 attributes.push_back({cast<StringInit>(val.getNameInit())->getValue(),651 Attribute(cast<DefInit>(val.getValue()))});652 }653 }654 655 // Populate `arguments`. This must happen after we've finalized `operands` and656 // `attributes` because we will put their elements' pointers in `arguments`.657 // SmallVector may perform re-allocation under the hood when adding new658 // elements.659 int operandIndex = 0, attrIndex = 0, propIndex = 0;660 for (unsigned i = 0; i != numArgs; ++i) {661 const Record *argDef =662 dyn_cast<DefInit>(argumentValues->getArg(i))->getDef();663 if (argDef->isSubClassOf(opVarClass))664 argDef = argDef->getValueAsDef("constraint");665 666 if (argDef->isSubClassOf(typeConstraintClass)) {667 attrPropOrOperandMapping.push_back(668 {OperandAttrOrProp::Kind::Operand, operandIndex});669 arguments.emplace_back(&operands[operandIndex++]);670 } else if (argDef->isSubClassOf(attrClass)) {671 attrPropOrOperandMapping.push_back(672 {OperandAttrOrProp::Kind::Attribute, attrIndex});673 arguments.emplace_back(&attributes[attrIndex++]);674 } else {675 assert(argDef->isSubClassOf(propertyClass));676 attrPropOrOperandMapping.push_back(677 {OperandAttrOrProp::Kind::Property, propIndex});678 arguments.emplace_back(&properties[propIndex++]);679 }680 }681 682 auto *resultsDag = def.getValueAsDag("results");683 auto *outsOp = dyn_cast<DefInit>(resultsDag->getOperator());684 if (!outsOp || outsOp->getDef()->getName() != "outs") {685 PrintFatalError(def.getLoc(), "'results' must have 'outs' directive");686 }687 688 // Handle results.689 for (unsigned i = 0, e = resultsDag->getNumArgs(); i < e; ++i) {690 auto name = resultsDag->getArgNameStr(i);691 auto *resultInit = dyn_cast<DefInit>(resultsDag->getArg(i));692 if (!resultInit) {693 PrintFatalError(def.getLoc(),694 Twine("undefined type for result #") + Twine(i));695 }696 auto *resultDef = resultInit->getDef();697 if (resultDef->isSubClassOf(opVarClass))698 resultDef = resultDef->getValueAsDef("constraint");699 results.push_back({name, TypeConstraint(resultDef)});700 if (!name.empty())701 argumentsAndResultsIndex[name] = InferredResultType::mapResultIndex(i);702 703 // We currently only support VariadicOfVariadic operands.704 if (results.back().constraint.isVariadicOfVariadic()) {705 PrintFatalError(706 def.getLoc(),707 "'VariadicOfVariadic' results are currently not supported");708 }709 }710 711 // Handle successors712 auto *successorsDag = def.getValueAsDag("successors");713 auto *successorsOp = dyn_cast<DefInit>(successorsDag->getOperator());714 if (!successorsOp || successorsOp->getDef()->getName() != "successor") {715 PrintFatalError(def.getLoc(),716 "'successors' must have 'successor' directive");717 }718 719 for (unsigned i = 0, e = successorsDag->getNumArgs(); i < e; ++i) {720 auto name = successorsDag->getArgNameStr(i);721 auto *successorInit = dyn_cast<DefInit>(successorsDag->getArg(i));722 if (!successorInit) {723 PrintFatalError(def.getLoc(),724 Twine("undefined kind for successor #") + Twine(i));725 }726 Successor successor(successorInit->getDef());727 728 // Only support variadic successors if it is the last one for now.729 if (i != e - 1 && successor.isVariadic())730 PrintFatalError(def.getLoc(), "only the last successor can be variadic");731 successors.push_back({name, successor});732 }733 734 // Create list of traits, skipping over duplicates: appending to lists in735 // tablegen is easy, making them unique less so, so dedupe here.736 if (auto *traitList = def.getValueAsListInit("traits")) {737 // This is uniquing based on pointers of the trait.738 SmallPtrSet<const Init *, 32> traitSet;739 traits.reserve(traitSet.size());740 741 // The declaration order of traits imply the verification order of traits.742 // Some traits may require other traits to be verified first then they can743 // do further verification based on those verified facts. If you see this744 // error, fix the traits declaration order by checking the `dependentTraits`745 // field.746 auto verifyTraitValidity = [&](const Record *trait) {747 auto *dependentTraits = trait->getValueAsListInit("dependentTraits");748 for (auto *traitInit : *dependentTraits)749 if (!traitSet.contains(traitInit))750 PrintFatalError(751 def.getLoc(),752 trait->getValueAsString("trait") + " requires " +753 cast<DefInit>(traitInit)->getDef()->getValueAsString(754 "trait") +755 " to precede it in traits list");756 };757 758 std::function<void(const ListInit *)> insert;759 insert = [&](const ListInit *traitList) {760 for (auto *traitInit : *traitList) {761 auto *def = cast<DefInit>(traitInit)->getDef();762 if (def->isSubClassOf("TraitList")) {763 insert(def->getValueAsListInit("traits"));764 continue;765 }766 767 // Ignore duplicates.768 if (!traitSet.insert(traitInit).second)769 continue;770 771 // If this is an interface with base classes, add the bases to the772 // trait list.773 if (def->isSubClassOf("Interface"))774 insert(def->getValueAsListInit("baseInterfaces"));775 776 // Verify if the trait has all the dependent traits declared before777 // itself.778 verifyTraitValidity(def);779 traits.push_back(Trait::create(traitInit));780 }781 };782 insert(traitList);783 }784 785 populateTypeInferenceInfo(argumentsAndResultsIndex);786 787 // Handle regions788 auto *regionsDag = def.getValueAsDag("regions");789 auto *regionsOp = dyn_cast<DefInit>(regionsDag->getOperator());790 if (!regionsOp || regionsOp->getDef()->getName() != "region") {791 PrintFatalError(def.getLoc(), "'regions' must have 'region' directive");792 }793 794 for (unsigned i = 0, e = regionsDag->getNumArgs(); i < e; ++i) {795 auto name = regionsDag->getArgNameStr(i);796 auto *regionInit = dyn_cast<DefInit>(regionsDag->getArg(i));797 if (!regionInit) {798 PrintFatalError(def.getLoc(),799 Twine("undefined kind for region #") + Twine(i));800 }801 Region region(regionInit->getDef());802 if (region.isVariadic()) {803 // Only support variadic regions if it is the last one for now.804 if (i != e - 1)805 PrintFatalError(def.getLoc(), "only the last region can be variadic");806 if (name.empty())807 PrintFatalError(def.getLoc(), "variadic regions must be named");808 }809 810 regions.push_back({name, region});811 }812 813 // Populate the builders.814 auto *builderList = dyn_cast_or_null<ListInit>(def.getValueInit("builders"));815 if (builderList && !builderList->empty()) {816 for (const Init *init : builderList->getElements())817 builders.emplace_back(cast<DefInit>(init)->getDef(), def.getLoc());818 } else if (skipDefaultBuilders()) {819 PrintFatalError(820 def.getLoc(),821 "default builders are skipped and no custom builders provided");822 }823 824 LLVM_DEBUG(print(llvm::dbgs()));825}826 827const InferredResultType &Operator::getInferredResultType(int index) const {828 assert(allResultTypesKnown());829 return resultTypeMapping[index];830}831 832ArrayRef<SMLoc> Operator::getLoc() const { return def.getLoc(); }833 834bool Operator::hasDescription() const {835 return !getDescription().trim().empty();836}837 838StringRef Operator::getDescription() const {839 return def.getValueAsString("description");840}841 842bool Operator::hasSummary() const { return !getSummary().trim().empty(); }843 844StringRef Operator::getSummary() const {845 return def.getValueAsString("summary");846}847 848bool Operator::hasAssemblyFormat() const {849 auto *valueInit = def.getValueInit("assemblyFormat");850 return isa<StringInit>(valueInit);851}852 853StringRef Operator::getAssemblyFormat() const {854 return TypeSwitch<const Init *, StringRef>(def.getValueInit("assemblyFormat"))855 .Case<StringInit>([&](auto *init) { return init->getValue(); });856}857 858void Operator::print(llvm::raw_ostream &os) const {859 os << "op '" << getOperationName() << "'\n";860 for (Argument arg : arguments) {861 if (auto *attr = llvm::dyn_cast_if_present<NamedAttribute *>(arg))862 os << "[attribute] " << attr->name << '\n';863 else864 os << "[operand] " << cast<NamedTypeConstraint *>(arg)->name << '\n';865 }866}867 868auto Operator::VariableDecoratorIterator::unwrap(const Init *init)869 -> VariableDecorator {870 return VariableDecorator(cast<DefInit>(init)->getDef());871}872 873auto Operator::getArgToOperandAttrOrProp(int index) const -> OperandAttrOrProp {874 return attrPropOrOperandMapping[index];875}876 877std::string Operator::getGetterName(StringRef name) const {878 return "get" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);879}880 881std::string Operator::getSetterName(StringRef name) const {882 return "set" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);883}884 885std::string Operator::getRemoverName(StringRef name) const {886 return "remove" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);887}888 889bool Operator::hasFolder() const { return def.getValueAsBit("hasFolder"); }890 891bool Operator::useCustomPropertiesEncoding() const {892 return def.getValueAsBit("useCustomPropertiesEncoding");893}894