| //===- Operator.cpp - Operator class --------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | // | 
 | // Operator wrapper to simplify using TableGen Record defining a MLIR Op. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "mlir/TableGen/Operator.h" | 
 | #include "mlir/TableGen/Argument.h" | 
 | #include "mlir/TableGen/Predicate.h" | 
 | #include "mlir/TableGen/Trait.h" | 
 | #include "mlir/TableGen/Type.h" | 
 | #include "llvm/ADT/EquivalenceClasses.h" | 
 | #include "llvm/ADT/STLExtras.h" | 
 | #include "llvm/ADT/Sequence.h" | 
 | #include "llvm/ADT/SmallPtrSet.h" | 
 | #include "llvm/ADT/StringExtras.h" | 
 | #include "llvm/ADT/TypeSwitch.h" | 
 | #include "llvm/Support/Debug.h" | 
 | #include "llvm/Support/ErrorHandling.h" | 
 | #include "llvm/Support/FormatVariadic.h" | 
 | #include "llvm/TableGen/Error.h" | 
 | #include "llvm/TableGen/Record.h" | 
 | #include <list> | 
 |  | 
 | #define DEBUG_TYPE "mlir-tblgen-operator" | 
 |  | 
 | using namespace mlir; | 
 | using namespace mlir::tblgen; | 
 |  | 
 | using llvm::DagInit; | 
 | using llvm::DefInit; | 
 | using llvm::Record; | 
 |  | 
 | Operator::Operator(const llvm::Record &def) | 
 |     : dialect(def.getValueAsDef("opDialect")), def(def) { | 
 |   // The first `_` in the op's TableGen def name is treated as separating the | 
 |   // dialect prefix and the op class name. The dialect prefix will be ignored if | 
 |   // not empty. Otherwise, if def name starts with a `_`, the `_` is considered | 
 |   // as part of the class name. | 
 |   StringRef prefix; | 
 |   std::tie(prefix, cppClassName) = def.getName().split('_'); | 
 |   if (prefix.empty()) { | 
 |     // Class name with a leading underscore and without dialect prefix | 
 |     cppClassName = def.getName(); | 
 |   } else if (cppClassName.empty()) { | 
 |     // Class name without dialect prefix | 
 |     cppClassName = prefix; | 
 |   } | 
 |  | 
 |   cppNamespace = def.getValueAsString("cppNamespace"); | 
 |  | 
 |   populateOpStructure(); | 
 |   assertInvariants(); | 
 | } | 
 |  | 
 | std::string Operator::getOperationName() const { | 
 |   auto prefix = dialect.getName(); | 
 |   auto opName = def.getValueAsString("opName"); | 
 |   if (prefix.empty()) | 
 |     return std::string(opName); | 
 |   return std::string(llvm::formatv("{0}.{1}", prefix, opName)); | 
 | } | 
 |  | 
 | std::string Operator::getAdaptorName() const { | 
 |   return std::string(llvm::formatv("{0}Adaptor", getCppClassName())); | 
 | } | 
 |  | 
 | std::string Operator::getGenericAdaptorName() const { | 
 |   return std::string(llvm::formatv("{0}GenericAdaptor", getCppClassName())); | 
 | } | 
 |  | 
 | /// Assert the invariants of accessors generated for the given name. | 
 | static void assertAccessorInvariants(const Operator &op, StringRef name) { | 
 |   std::string accessorName = | 
 |       convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true); | 
 |  | 
 |   // Functor used to detect when an accessor will cause an overlap with an | 
 |   // operation API. | 
 |   // | 
 |   // There are a little bit more invasive checks possible for cases where not | 
 |   // all ops have the trait that would cause overlap. For many cases here, | 
 |   // renaming would be better (e.g., we can only guard in limited manner | 
 |   // against methods from traits and interfaces here, so avoiding these in op | 
 |   // definition is safer). | 
 |   auto nameOverlapsWithOpAPI = [&](StringRef newName) { | 
 |     if (newName == "AttributeNames" || newName == "Attributes" || | 
 |         newName == "Operation") | 
 |       return true; | 
 |     if (newName == "Operands") | 
 |       return op.getNumOperands() != 1 || op.getNumVariableLengthOperands() != 1; | 
 |     if (newName == "Regions") | 
 |       return op.getNumRegions() != 1 || op.getNumVariadicRegions() != 1; | 
 |     if (newName == "Type") | 
 |       return op.getNumResults() != 1; | 
 |     return false; | 
 |   }; | 
 |   if (nameOverlapsWithOpAPI(accessorName)) { | 
 |     // This error could be avoided in situations where the final function is | 
 |     // identical, but preferably the op definition should avoid using generic | 
 |     // names. | 
 |     PrintFatalError(op.getLoc(), "generated accessor for `" + name + | 
 |                                      "` overlaps with a default one; please " | 
 |                                      "rename to avoid overlap"); | 
 |   } | 
 | } | 
 |  | 
 | void Operator::assertInvariants() const { | 
 |   // Check that the name of arguments/results/regions/successors don't overlap. | 
 |   DenseMap<StringRef, StringRef> existingNames; | 
 |   auto checkName = [&](StringRef name, StringRef entity) { | 
 |     if (name.empty()) | 
 |       return; | 
 |     auto insertion = existingNames.insert({name, entity}); | 
 |     if (insertion.second) { | 
 |       // Assert invariants for accessors generated for this name. | 
 |       assertAccessorInvariants(*this, name); | 
 |       return; | 
 |     } | 
 |     if (entity == insertion.first->second) | 
 |       PrintFatalError(getLoc(), "op has a conflict with two " + entity + | 
 |                                     " having the same name '" + name + "'"); | 
 |     PrintFatalError(getLoc(), "op has a conflict with " + | 
 |                                   insertion.first->second + " and " + entity + | 
 |                                   " both having an entry with the name '" + | 
 |                                   name + "'"); | 
 |   }; | 
 |   // Check operands amongst themselves. | 
 |   for (int i : llvm::seq<int>(0, getNumOperands())) | 
 |     checkName(getOperand(i).name, "operands"); | 
 |  | 
 |   // Check results amongst themselves and against operands. | 
 |   for (int i : llvm::seq<int>(0, getNumResults())) | 
 |     checkName(getResult(i).name, "results"); | 
 |  | 
 |   // Check regions amongst themselves and against operands and results. | 
 |   for (int i : llvm::seq<int>(0, getNumRegions())) | 
 |     checkName(getRegion(i).name, "regions"); | 
 |  | 
 |   // Check successors amongst themselves and against operands, results, and | 
 |   // regions. | 
 |   for (int i : llvm::seq<int>(0, getNumSuccessors())) | 
 |     checkName(getSuccessor(i).name, "successors"); | 
 | } | 
 |  | 
 | StringRef Operator::getDialectName() const { return dialect.getName(); } | 
 |  | 
 | StringRef Operator::getCppClassName() const { return cppClassName; } | 
 |  | 
 | std::string Operator::getQualCppClassName() const { | 
 |   if (cppNamespace.empty()) | 
 |     return std::string(cppClassName); | 
 |   return std::string(llvm::formatv("{0}::{1}", cppNamespace, cppClassName)); | 
 | } | 
 |  | 
 | StringRef Operator::getCppNamespace() const { return cppNamespace; } | 
 |  | 
 | int Operator::getNumResults() const { | 
 |   DagInit *results = def.getValueAsDag("results"); | 
 |   return results->getNumArgs(); | 
 | } | 
 |  | 
 | StringRef Operator::getExtraClassDeclaration() const { | 
 |   constexpr auto attr = "extraClassDeclaration"; | 
 |   if (def.isValueUnset(attr)) | 
 |     return {}; | 
 |   return def.getValueAsString(attr); | 
 | } | 
 |  | 
 | StringRef Operator::getExtraClassDefinition() const { | 
 |   constexpr auto attr = "extraClassDefinition"; | 
 |   if (def.isValueUnset(attr)) | 
 |     return {}; | 
 |   return def.getValueAsString(attr); | 
 | } | 
 |  | 
 | const llvm::Record &Operator::getDef() const { return def; } | 
 |  | 
 | bool Operator::skipDefaultBuilders() const { | 
 |   return def.getValueAsBit("skipDefaultBuilders"); | 
 | } | 
 |  | 
 | auto Operator::result_begin() const -> const_value_iterator { | 
 |   return results.begin(); | 
 | } | 
 |  | 
 | auto Operator::result_end() const -> const_value_iterator { | 
 |   return results.end(); | 
 | } | 
 |  | 
 | auto Operator::getResults() const -> const_value_range { | 
 |   return {result_begin(), result_end()}; | 
 | } | 
 |  | 
 | TypeConstraint Operator::getResultTypeConstraint(int index) const { | 
 |   DagInit *results = def.getValueAsDag("results"); | 
 |   return TypeConstraint(cast<DefInit>(results->getArg(index))); | 
 | } | 
 |  | 
 | StringRef Operator::getResultName(int index) const { | 
 |   DagInit *results = def.getValueAsDag("results"); | 
 |   return results->getArgNameStr(index); | 
 | } | 
 |  | 
 | auto Operator::getResultDecorators(int index) const -> var_decorator_range { | 
 |   Record *result = | 
 |       cast<DefInit>(def.getValueAsDag("results")->getArg(index))->getDef(); | 
 |   if (!result->isSubClassOf("OpVariable")) | 
 |     return var_decorator_range(nullptr, nullptr); | 
 |   return *result->getValueAsListInit("decorators"); | 
 | } | 
 |  | 
 | unsigned Operator::getNumVariableLengthResults() const { | 
 |   return llvm::count_if(results, [](const NamedTypeConstraint &c) { | 
 |     return c.constraint.isVariableLength(); | 
 |   }); | 
 | } | 
 |  | 
 | unsigned Operator::getNumVariableLengthOperands() const { | 
 |   return llvm::count_if(operands, [](const NamedTypeConstraint &c) { | 
 |     return c.constraint.isVariableLength(); | 
 |   }); | 
 | } | 
 |  | 
 | bool Operator::hasSingleVariadicArg() const { | 
 |   return getNumArgs() == 1 && getArg(0).is<NamedTypeConstraint *>() && | 
 |          getOperand(0).isVariadic(); | 
 | } | 
 |  | 
 | Operator::arg_iterator Operator::arg_begin() const { return arguments.begin(); } | 
 |  | 
 | Operator::arg_iterator Operator::arg_end() const { return arguments.end(); } | 
 |  | 
 | Operator::arg_range Operator::getArgs() const { | 
 |   return {arg_begin(), arg_end()}; | 
 | } | 
 |  | 
 | StringRef Operator::getArgName(int index) const { | 
 |   DagInit *argumentValues = def.getValueAsDag("arguments"); | 
 |   return argumentValues->getArgNameStr(index); | 
 | } | 
 |  | 
 | auto Operator::getArgDecorators(int index) const -> var_decorator_range { | 
 |   Record *arg = | 
 |       cast<DefInit>(def.getValueAsDag("arguments")->getArg(index))->getDef(); | 
 |   if (!arg->isSubClassOf("OpVariable")) | 
 |     return var_decorator_range(nullptr, nullptr); | 
 |   return *arg->getValueAsListInit("decorators"); | 
 | } | 
 |  | 
 | const Trait *Operator::getTrait(StringRef trait) const { | 
 |   for (const auto &t : traits) { | 
 |     if (const auto *traitDef = dyn_cast<NativeTrait>(&t)) { | 
 |       if (traitDef->getFullyQualifiedTraitName() == trait) | 
 |         return traitDef; | 
 |     } else if (const auto *traitDef = dyn_cast<InternalTrait>(&t)) { | 
 |       if (traitDef->getFullyQualifiedTraitName() == trait) | 
 |         return traitDef; | 
 |     } else if (const auto *traitDef = dyn_cast<InterfaceTrait>(&t)) { | 
 |       if (traitDef->getFullyQualifiedTraitName() == trait) | 
 |         return traitDef; | 
 |     } | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | auto Operator::region_begin() const -> const_region_iterator { | 
 |   return regions.begin(); | 
 | } | 
 | auto Operator::region_end() const -> const_region_iterator { | 
 |   return regions.end(); | 
 | } | 
 | auto Operator::getRegions() const | 
 |     -> llvm::iterator_range<const_region_iterator> { | 
 |   return {region_begin(), region_end()}; | 
 | } | 
 |  | 
 | unsigned Operator::getNumRegions() const { return regions.size(); } | 
 |  | 
 | const NamedRegion &Operator::getRegion(unsigned index) const { | 
 |   return regions[index]; | 
 | } | 
 |  | 
 | unsigned Operator::getNumVariadicRegions() const { | 
 |   return llvm::count_if(regions, | 
 |                         [](const NamedRegion &c) { return c.isVariadic(); }); | 
 | } | 
 |  | 
 | auto Operator::successor_begin() const -> const_successor_iterator { | 
 |   return successors.begin(); | 
 | } | 
 | auto Operator::successor_end() const -> const_successor_iterator { | 
 |   return successors.end(); | 
 | } | 
 | auto Operator::getSuccessors() const | 
 |     -> llvm::iterator_range<const_successor_iterator> { | 
 |   return {successor_begin(), successor_end()}; | 
 | } | 
 |  | 
 | unsigned Operator::getNumSuccessors() const { return successors.size(); } | 
 |  | 
 | const NamedSuccessor &Operator::getSuccessor(unsigned index) const { | 
 |   return successors[index]; | 
 | } | 
 |  | 
 | unsigned Operator::getNumVariadicSuccessors() const { | 
 |   return llvm::count_if(successors, | 
 |                         [](const NamedSuccessor &c) { return c.isVariadic(); }); | 
 | } | 
 |  | 
 | auto Operator::trait_begin() const -> const_trait_iterator { | 
 |   return traits.begin(); | 
 | } | 
 | auto Operator::trait_end() const -> const_trait_iterator { | 
 |   return traits.end(); | 
 | } | 
 | auto Operator::getTraits() const -> llvm::iterator_range<const_trait_iterator> { | 
 |   return {trait_begin(), trait_end()}; | 
 | } | 
 |  | 
 | auto Operator::attribute_begin() const -> const_attribute_iterator { | 
 |   return attributes.begin(); | 
 | } | 
 | auto Operator::attribute_end() const -> const_attribute_iterator { | 
 |   return attributes.end(); | 
 | } | 
 | auto Operator::getAttributes() const | 
 |     -> llvm::iterator_range<const_attribute_iterator> { | 
 |   return {attribute_begin(), attribute_end()}; | 
 | } | 
 | auto Operator::attribute_begin() -> attribute_iterator { | 
 |   return attributes.begin(); | 
 | } | 
 | auto Operator::attribute_end() -> attribute_iterator { | 
 |   return attributes.end(); | 
 | } | 
 | auto Operator::getAttributes() -> llvm::iterator_range<attribute_iterator> { | 
 |   return {attribute_begin(), attribute_end()}; | 
 | } | 
 |  | 
 | auto Operator::operand_begin() const -> const_value_iterator { | 
 |   return operands.begin(); | 
 | } | 
 | auto Operator::operand_end() const -> const_value_iterator { | 
 |   return operands.end(); | 
 | } | 
 | auto Operator::getOperands() const -> const_value_range { | 
 |   return {operand_begin(), operand_end()}; | 
 | } | 
 |  | 
 | auto Operator::getArg(int index) const -> Argument { return arguments[index]; } | 
 |  | 
 | bool Operator::isVariadic() const { | 
 |   return any_of(llvm::concat<const NamedTypeConstraint>(operands, results), | 
 |                 [](const NamedTypeConstraint &op) { return op.isVariadic(); }); | 
 | } | 
 |  | 
 | void Operator::populateTypeInferenceInfo( | 
 |     const llvm::StringMap<int> &argumentsAndResultsIndex) { | 
 |   // If the type inference op interface is not registered, then do not attempt | 
 |   // to determine if the result types an be inferred. | 
 |   auto &recordKeeper = def.getRecords(); | 
 |   auto *inferTrait = recordKeeper.getDef(inferTypeOpInterface); | 
 |   allResultsHaveKnownTypes = false; | 
 |   if (!inferTrait) | 
 |     return; | 
 |  | 
 |   // If there are no results, the skip this else the build method generated | 
 |   // overlaps with another autogenerated builder. | 
 |   if (getNumResults() == 0) | 
 |     return; | 
 |  | 
 |   // Skip ops with variadic or optional results. | 
 |   if (getNumVariableLengthResults() > 0) | 
 |     return; | 
 |  | 
 |   // Skip cases currently being custom generated. | 
 |   // TODO: Remove special cases. | 
 |   if (getTrait("::mlir::OpTrait::SameOperandsAndResultType")) { | 
 |     // Check for a non-variable length operand to use as the type anchor. | 
 |     auto *operandI = llvm::find_if(arguments, [](const Argument &arg) { | 
 |       NamedTypeConstraint *operand = llvm::dyn_cast_if_present<NamedTypeConstraint *>(arg); | 
 |       return operand && !operand->isVariableLength(); | 
 |     }); | 
 |     if (operandI == arguments.end()) | 
 |       return; | 
 |  | 
 |     // All result types are inferred from the operand type. | 
 |     int operandIdx = operandI - arguments.begin(); | 
 |     for (int i = 0; i < getNumResults(); ++i) | 
 |       resultTypeMapping.emplace_back(operandIdx, "$_self"); | 
 |  | 
 |     allResultsHaveKnownTypes = true; | 
 |     traits.push_back(Trait::create(inferTrait->getDefInit())); | 
 |     return; | 
 |   } | 
 |  | 
 |   /// This struct represents a node in this operation's result type inferenece | 
 |   /// graph. Each node has a list of incoming type inference edges `sources`. | 
 |   /// Each edge represents a "source" from which the result type can be | 
 |   /// inferred, either an operand (leaf) or another result (node). When a node | 
 |   /// is known to have a fully-inferred type, `inferred` is set to true. | 
 |   struct ResultTypeInference { | 
 |     /// The list of incoming type inference edges. | 
 |     SmallVector<InferredResultType> sources; | 
 |     /// This flag is set to true when the result type is known to be inferrable. | 
 |     bool inferred = false; | 
 |   }; | 
 |  | 
 |   // This vector represents the type inference graph, with one node for each | 
 |   // operation result. The nth element is the node for the nth result. | 
 |   SmallVector<ResultTypeInference> inference(getNumResults(), {}); | 
 |  | 
 |   // For all results whose types are buildable, initialize their type inference | 
 |   // nodes with an edge to themselves. Mark those nodes are fully-inferred. | 
 |   for (auto [idx, infer] : llvm::enumerate(inference)) { | 
 |     if (getResult(idx).constraint.getBuilderCall()) { | 
 |       infer.sources.emplace_back(InferredResultType::mapResultIndex(idx), | 
 |                                  "$_self"); | 
 |       infer.inferred = true; | 
 |     } | 
 |   } | 
 |  | 
 |   // Use `AllTypesMatch` and `TypesMatchWith` operation traits to build the | 
 |   // result type inference graph. | 
 |   for (const Trait &trait : traits) { | 
 |     const llvm::Record &def = trait.getDef(); | 
 |  | 
 |     // If the infer type op interface was manually added, then treat it as | 
 |     // intention that the op needs special handling. | 
 |     // TODO: Reconsider whether to always generate, this is more conservative | 
 |     // and keeps existing behavior so starting that way for now. | 
 |     if (def.isSubClassOf( | 
 |             llvm::formatv("{0}::Trait", inferTypeOpInterface).str())) | 
 |       return; | 
 |     if (const auto *traitDef = dyn_cast<InterfaceTrait>(&trait)) | 
 |       if (&traitDef->getDef() == inferTrait) | 
 |         return; | 
 |  | 
 |     // The `TypesMatchWith` trait represents a 1 -> 1 type inference edge with a | 
 |     // type transformer. | 
 |     if (def.isSubClassOf("TypesMatchWith")) { | 
 |       int target = argumentsAndResultsIndex.lookup(def.getValueAsString("rhs")); | 
 |       // Ignore operand type inference. | 
 |       if (InferredResultType::isArgIndex(target)) | 
 |         continue; | 
 |       int resultIndex = InferredResultType::unmapResultIndex(target); | 
 |       ResultTypeInference &infer = inference[resultIndex]; | 
 |       // If the type of the result has already been inferred, do nothing. | 
 |       if (infer.inferred) | 
 |         continue; | 
 |       int sourceIndex = | 
 |           argumentsAndResultsIndex.lookup(def.getValueAsString("lhs")); | 
 |       infer.sources.emplace_back(sourceIndex, | 
 |                                  def.getValueAsString("transformer").str()); | 
 |       // Locally propagate inferredness. | 
 |       infer.inferred = | 
 |           InferredResultType::isArgIndex(sourceIndex) || | 
 |           inference[InferredResultType::unmapResultIndex(sourceIndex)].inferred; | 
 |       continue; | 
 |     } | 
 |  | 
 |     if (!def.isSubClassOf("AllTypesMatch")) | 
 |       continue; | 
 |  | 
 |     auto values = def.getValueAsListOfStrings("values"); | 
 |     // The `AllTypesMatch` trait represents an N <-> N fanin and fanout. That | 
 |     // is, every result type has an edge from every other type. However, if any | 
 |     // one of the values refers to an operand or a result with a fully-inferred | 
 |     // type, we can infer all other types from that value. Try to find a | 
 |     // fully-inferred type in the list. | 
 |     std::optional<int> fullyInferredIndex; | 
 |     SmallVector<int> resultIndices; | 
 |     for (StringRef name : values) { | 
 |       int index = argumentsAndResultsIndex.lookup(name); | 
 |       if (InferredResultType::isResultIndex(index)) | 
 |         resultIndices.push_back(InferredResultType::unmapResultIndex(index)); | 
 |       if (InferredResultType::isArgIndex(index) || | 
 |           inference[InferredResultType::unmapResultIndex(index)].inferred) | 
 |         fullyInferredIndex = index; | 
 |     } | 
 |     if (fullyInferredIndex) { | 
 |       // Make the fully-inferred type the only source for all results that | 
 |       // aren't already inferred -- a 1 -> N fanout. | 
 |       for (int resultIndex : resultIndices) { | 
 |         ResultTypeInference &infer = inference[resultIndex]; | 
 |         if (!infer.inferred) { | 
 |           infer.sources.assign(1, {*fullyInferredIndex, "$_self"}); | 
 |           infer.inferred = true; | 
 |         } | 
 |       } | 
 |     } else { | 
 |       // Add an edge between every result and every other type; N <-> N. | 
 |       for (int resultIndex : resultIndices) { | 
 |         for (int otherResultIndex : resultIndices) { | 
 |           if (resultIndex == otherResultIndex) | 
 |             continue; | 
 |           inference[resultIndex].sources.emplace_back(otherResultIndex, | 
 |                                                       "$_self"); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Propagate inferredness until a fixed point. | 
 |   std::vector<ResultTypeInference *> worklist; | 
 |   for (ResultTypeInference &infer : inference) | 
 |     if (!infer.inferred) | 
 |       worklist.push_back(&infer); | 
 |   bool changed; | 
 |   do { | 
 |     changed = false; | 
 |     for (auto cur = worklist.begin(); cur != worklist.end();) { | 
 |       ResultTypeInference &infer = **cur; | 
 |  | 
 |       InferredResultType *iter = | 
 |           llvm::find_if(infer.sources, [&](const InferredResultType &source) { | 
 |             assert(InferredResultType::isResultIndex(source.getIndex())); | 
 |             return inference[InferredResultType::unmapResultIndex( | 
 |                                  source.getIndex())] | 
 |                 .inferred; | 
 |           }); | 
 |       if (iter == infer.sources.end()) { | 
 |         ++cur; | 
 |         continue; | 
 |       } | 
 |  | 
 |       changed = true; | 
 |       infer.inferred = true; | 
 |       // Make this the only source for the result. This breaks any cycles. | 
 |       infer.sources.assign(1, *iter); | 
 |       cur = worklist.erase(cur); | 
 |     } | 
 |   } while (changed); | 
 |  | 
 |   allResultsHaveKnownTypes = worklist.empty(); | 
 |  | 
 |   // If the types could be computed, then add type inference trait. | 
 |   if (allResultsHaveKnownTypes) { | 
 |     traits.push_back(Trait::create(inferTrait->getDefInit())); | 
 |     for (const ResultTypeInference &infer : inference) | 
 |       resultTypeMapping.push_back(infer.sources.front()); | 
 |   } | 
 | } | 
 |  | 
 | void Operator::populateOpStructure() { | 
 |   auto &recordKeeper = def.getRecords(); | 
 |   auto *typeConstraintClass = recordKeeper.getClass("TypeConstraint"); | 
 |   auto *attrClass = recordKeeper.getClass("Attr"); | 
 |   auto *propertyClass = recordKeeper.getClass("Property"); | 
 |   auto *derivedAttrClass = recordKeeper.getClass("DerivedAttr"); | 
 |   auto *opVarClass = recordKeeper.getClass("OpVariable"); | 
 |   numNativeAttributes = 0; | 
 |  | 
 |   DagInit *argumentValues = def.getValueAsDag("arguments"); | 
 |   unsigned numArgs = argumentValues->getNumArgs(); | 
 |  | 
 |   // Mapping from name of to argument or result index. Arguments are indexed | 
 |   // to match getArg index, while the results are negatively indexed. | 
 |   llvm::StringMap<int> argumentsAndResultsIndex; | 
 |  | 
 |   // Handle operands and native attributes. | 
 |   for (unsigned i = 0; i != numArgs; ++i) { | 
 |     auto *arg = argumentValues->getArg(i); | 
 |     auto givenName = argumentValues->getArgNameStr(i); | 
 |     auto *argDefInit = dyn_cast<DefInit>(arg); | 
 |     if (!argDefInit) | 
 |       PrintFatalError(def.getLoc(), | 
 |                       Twine("undefined type for argument #") + Twine(i)); | 
 |     Record *argDef = argDefInit->getDef(); | 
 |     if (argDef->isSubClassOf(opVarClass)) | 
 |       argDef = argDef->getValueAsDef("constraint"); | 
 |  | 
 |     if (argDef->isSubClassOf(typeConstraintClass)) { | 
 |       operands.push_back( | 
 |           NamedTypeConstraint{givenName, TypeConstraint(argDef)}); | 
 |     } else if (argDef->isSubClassOf(attrClass)) { | 
 |       if (givenName.empty()) | 
 |         PrintFatalError(argDef->getLoc(), "attributes must be named"); | 
 |       if (argDef->isSubClassOf(derivedAttrClass)) | 
 |         PrintFatalError(argDef->getLoc(), | 
 |                         "derived attributes not allowed in argument list"); | 
 |       attributes.push_back({givenName, Attribute(argDef)}); | 
 |       ++numNativeAttributes; | 
 |     } else if (argDef->isSubClassOf(propertyClass)) { | 
 |       if (givenName.empty()) | 
 |         PrintFatalError(argDef->getLoc(), "properties must be named"); | 
 |       properties.push_back({givenName, Property(argDef)}); | 
 |     } else { | 
 |       PrintFatalError(def.getLoc(), | 
 |                       "unexpected def type; only defs deriving " | 
 |                       "from TypeConstraint or Attr or Property are allowed"); | 
 |     } | 
 |     if (!givenName.empty()) | 
 |       argumentsAndResultsIndex[givenName] = i; | 
 |   } | 
 |  | 
 |   // Handle derived attributes. | 
 |   for (const auto &val : def.getValues()) { | 
 |     if (auto *record = dyn_cast<llvm::RecordRecTy>(val.getType())) { | 
 |       if (!record->isSubClassOf(attrClass)) | 
 |         continue; | 
 |       if (!record->isSubClassOf(derivedAttrClass)) | 
 |         PrintFatalError(def.getLoc(), | 
 |                         "unexpected Attr where only DerivedAttr is allowed"); | 
 |  | 
 |       if (record->getClasses().size() != 1) { | 
 |         PrintFatalError( | 
 |             def.getLoc(), | 
 |             "unsupported attribute modelling, only single class expected"); | 
 |       } | 
 |       attributes.push_back( | 
 |           {cast<llvm::StringInit>(val.getNameInit())->getValue(), | 
 |            Attribute(cast<DefInit>(val.getValue()))}); | 
 |     } | 
 |   } | 
 |  | 
 |   // Populate `arguments`. This must happen after we've finalized `operands` and | 
 |   // `attributes` because we will put their elements' pointers in `arguments`. | 
 |   // SmallVector may perform re-allocation under the hood when adding new | 
 |   // elements. | 
 |   int operandIndex = 0, attrIndex = 0, propIndex = 0; | 
 |   for (unsigned i = 0; i != numArgs; ++i) { | 
 |     Record *argDef = dyn_cast<DefInit>(argumentValues->getArg(i))->getDef(); | 
 |     if (argDef->isSubClassOf(opVarClass)) | 
 |       argDef = argDef->getValueAsDef("constraint"); | 
 |  | 
 |     if (argDef->isSubClassOf(typeConstraintClass)) { | 
 |       attrOrOperandMapping.push_back( | 
 |           {OperandOrAttribute::Kind::Operand, operandIndex}); | 
 |       arguments.emplace_back(&operands[operandIndex++]); | 
 |     } else if (argDef->isSubClassOf(attrClass)) { | 
 |       attrOrOperandMapping.push_back( | 
 |           {OperandOrAttribute::Kind::Attribute, attrIndex}); | 
 |       arguments.emplace_back(&attributes[attrIndex++]); | 
 |     } else { | 
 |       assert(argDef->isSubClassOf(propertyClass)); | 
 |       arguments.emplace_back(&properties[propIndex++]); | 
 |     } | 
 |   } | 
 |  | 
 |   auto *resultsDag = def.getValueAsDag("results"); | 
 |   auto *outsOp = dyn_cast<DefInit>(resultsDag->getOperator()); | 
 |   if (!outsOp || outsOp->getDef()->getName() != "outs") { | 
 |     PrintFatalError(def.getLoc(), "'results' must have 'outs' directive"); | 
 |   } | 
 |  | 
 |   // Handle results. | 
 |   for (unsigned i = 0, e = resultsDag->getNumArgs(); i < e; ++i) { | 
 |     auto name = resultsDag->getArgNameStr(i); | 
 |     auto *resultInit = dyn_cast<DefInit>(resultsDag->getArg(i)); | 
 |     if (!resultInit) { | 
 |       PrintFatalError(def.getLoc(), | 
 |                       Twine("undefined type for result #") + Twine(i)); | 
 |     } | 
 |     auto *resultDef = resultInit->getDef(); | 
 |     if (resultDef->isSubClassOf(opVarClass)) | 
 |       resultDef = resultDef->getValueAsDef("constraint"); | 
 |     results.push_back({name, TypeConstraint(resultDef)}); | 
 |     if (!name.empty()) | 
 |       argumentsAndResultsIndex[name] = InferredResultType::mapResultIndex(i); | 
 |  | 
 |     // We currently only support VariadicOfVariadic operands. | 
 |     if (results.back().constraint.isVariadicOfVariadic()) { | 
 |       PrintFatalError( | 
 |           def.getLoc(), | 
 |           "'VariadicOfVariadic' results are currently not supported"); | 
 |     } | 
 |   } | 
 |  | 
 |   // Handle successors | 
 |   auto *successorsDag = def.getValueAsDag("successors"); | 
 |   auto *successorsOp = dyn_cast<DefInit>(successorsDag->getOperator()); | 
 |   if (!successorsOp || successorsOp->getDef()->getName() != "successor") { | 
 |     PrintFatalError(def.getLoc(), | 
 |                     "'successors' must have 'successor' directive"); | 
 |   } | 
 |  | 
 |   for (unsigned i = 0, e = successorsDag->getNumArgs(); i < e; ++i) { | 
 |     auto name = successorsDag->getArgNameStr(i); | 
 |     auto *successorInit = dyn_cast<DefInit>(successorsDag->getArg(i)); | 
 |     if (!successorInit) { | 
 |       PrintFatalError(def.getLoc(), | 
 |                       Twine("undefined kind for successor #") + Twine(i)); | 
 |     } | 
 |     Successor successor(successorInit->getDef()); | 
 |  | 
 |     // Only support variadic successors if it is the last one for now. | 
 |     if (i != e - 1 && successor.isVariadic()) | 
 |       PrintFatalError(def.getLoc(), "only the last successor can be variadic"); | 
 |     successors.push_back({name, successor}); | 
 |   } | 
 |  | 
 |   // Create list of traits, skipping over duplicates: appending to lists in | 
 |   // tablegen is easy, making them unique less so, so dedupe here. | 
 |   if (auto *traitList = def.getValueAsListInit("traits")) { | 
 |     // This is uniquing based on pointers of the trait. | 
 |     SmallPtrSet<const llvm::Init *, 32> traitSet; | 
 |     traits.reserve(traitSet.size()); | 
 |  | 
 |     // The declaration order of traits imply the verification order of traits. | 
 |     // Some traits may require other traits to be verified first then they can | 
 |     // do further verification based on those verified facts. If you see this | 
 |     // error, fix the traits declaration order by checking the `dependentTraits` | 
 |     // field. | 
 |     auto verifyTraitValidity = [&](Record *trait) { | 
 |       auto *dependentTraits = trait->getValueAsListInit("dependentTraits"); | 
 |       for (auto *traitInit : *dependentTraits) | 
 |         if (!traitSet.contains(traitInit)) | 
 |           PrintFatalError( | 
 |               def.getLoc(), | 
 |               trait->getValueAsString("trait") + " requires " + | 
 |                   cast<DefInit>(traitInit)->getDef()->getValueAsString( | 
 |                       "trait") + | 
 |                   " to precede it in traits list"); | 
 |     }; | 
 |  | 
 |     std::function<void(llvm::ListInit *)> insert; | 
 |     insert = [&](llvm::ListInit *traitList) { | 
 |       for (auto *traitInit : *traitList) { | 
 |         auto *def = cast<DefInit>(traitInit)->getDef(); | 
 |         if (def->isSubClassOf("TraitList")) { | 
 |           insert(def->getValueAsListInit("traits")); | 
 |           continue; | 
 |         } | 
 |  | 
 |         // Ignore duplicates. | 
 |         if (!traitSet.insert(traitInit).second) | 
 |           continue; | 
 |  | 
 |         // If this is an interface with base classes, add the bases to the | 
 |         // trait list. | 
 |         if (def->isSubClassOf("Interface")) | 
 |           insert(def->getValueAsListInit("baseInterfaces")); | 
 |  | 
 |         // Verify if the trait has all the dependent traits declared before | 
 |         // itself. | 
 |         verifyTraitValidity(def); | 
 |         traits.push_back(Trait::create(traitInit)); | 
 |       } | 
 |     }; | 
 |     insert(traitList); | 
 |   } | 
 |  | 
 |   populateTypeInferenceInfo(argumentsAndResultsIndex); | 
 |  | 
 |   // Handle regions | 
 |   auto *regionsDag = def.getValueAsDag("regions"); | 
 |   auto *regionsOp = dyn_cast<DefInit>(regionsDag->getOperator()); | 
 |   if (!regionsOp || regionsOp->getDef()->getName() != "region") { | 
 |     PrintFatalError(def.getLoc(), "'regions' must have 'region' directive"); | 
 |   } | 
 |  | 
 |   for (unsigned i = 0, e = regionsDag->getNumArgs(); i < e; ++i) { | 
 |     auto name = regionsDag->getArgNameStr(i); | 
 |     auto *regionInit = dyn_cast<DefInit>(regionsDag->getArg(i)); | 
 |     if (!regionInit) { | 
 |       PrintFatalError(def.getLoc(), | 
 |                       Twine("undefined kind for region #") + Twine(i)); | 
 |     } | 
 |     Region region(regionInit->getDef()); | 
 |     if (region.isVariadic()) { | 
 |       // Only support variadic regions if it is the last one for now. | 
 |       if (i != e - 1) | 
 |         PrintFatalError(def.getLoc(), "only the last region can be variadic"); | 
 |       if (name.empty()) | 
 |         PrintFatalError(def.getLoc(), "variadic regions must be named"); | 
 |     } | 
 |  | 
 |     regions.push_back({name, region}); | 
 |   } | 
 |  | 
 |   // Populate the builders. | 
 |   auto *builderList = | 
 |       dyn_cast_or_null<llvm::ListInit>(def.getValueInit("builders")); | 
 |   if (builderList && !builderList->empty()) { | 
 |     for (llvm::Init *init : builderList->getValues()) | 
 |       builders.emplace_back(cast<llvm::DefInit>(init)->getDef(), def.getLoc()); | 
 |   } else if (skipDefaultBuilders()) { | 
 |     PrintFatalError( | 
 |         def.getLoc(), | 
 |         "default builders are skipped and no custom builders provided"); | 
 |   } | 
 |  | 
 |   LLVM_DEBUG(print(llvm::dbgs())); | 
 | } | 
 |  | 
 | const InferredResultType &Operator::getInferredResultType(int index) const { | 
 |   assert(allResultTypesKnown()); | 
 |   return resultTypeMapping[index]; | 
 | } | 
 |  | 
 | ArrayRef<SMLoc> Operator::getLoc() const { return def.getLoc(); } | 
 |  | 
 | bool Operator::hasDescription() const { | 
 |   return def.getValue("description") != nullptr; | 
 | } | 
 |  | 
 | StringRef Operator::getDescription() const { | 
 |   return def.getValueAsString("description"); | 
 | } | 
 |  | 
 | bool Operator::hasSummary() const { return def.getValue("summary") != nullptr; } | 
 |  | 
 | StringRef Operator::getSummary() const { | 
 |   return def.getValueAsString("summary"); | 
 | } | 
 |  | 
 | bool Operator::hasAssemblyFormat() const { | 
 |   auto *valueInit = def.getValueInit("assemblyFormat"); | 
 |   return isa<llvm::StringInit>(valueInit); | 
 | } | 
 |  | 
 | StringRef Operator::getAssemblyFormat() const { | 
 |   return TypeSwitch<llvm::Init *, StringRef>(def.getValueInit("assemblyFormat")) | 
 |       .Case<llvm::StringInit>([&](auto *init) { return init->getValue(); }); | 
 | } | 
 |  | 
 | void Operator::print(llvm::raw_ostream &os) const { | 
 |   os << "op '" << getOperationName() << "'\n"; | 
 |   for (Argument arg : arguments) { | 
 |     if (auto *attr = llvm::dyn_cast_if_present<NamedAttribute *>(arg)) | 
 |       os << "[attribute] " << attr->name << '\n'; | 
 |     else | 
 |       os << "[operand] " << arg.get<NamedTypeConstraint *>()->name << '\n'; | 
 |   } | 
 | } | 
 |  | 
 | auto Operator::VariableDecoratorIterator::unwrap(llvm::Init *init) | 
 |     -> VariableDecorator { | 
 |   return VariableDecorator(cast<llvm::DefInit>(init)->getDef()); | 
 | } | 
 |  | 
 | auto Operator::getArgToOperandOrAttribute(int index) const | 
 |     -> OperandOrAttribute { | 
 |   return attrOrOperandMapping[index]; | 
 | } | 
 |  | 
 | std::string Operator::getGetterName(StringRef name) const { | 
 |   return "get" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true); | 
 | } | 
 |  | 
 | std::string Operator::getSetterName(StringRef name) const { | 
 |   return "set" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true); | 
 | } | 
 |  | 
 | std::string Operator::getRemoverName(StringRef name) const { | 
 |   return "remove" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true); | 
 | } | 
 |  | 
 | bool Operator::hasFolder() const { return def.getValueAsBit("hasFolder"); } |