brintos

brintos / llvm-project-archived public Read only

0
0
Text · 32.6 KiB · 926ffd0 Raw
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