| //===- 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/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" |
| |
| #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())); |
| } |
| |
| 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) |
| 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); |
| } |
| |
| const llvm::Record &Operator::getDef() const { return def; } |
| |
| bool Operator::skipDefaultBuilders() const { |
| return def.getValueAsBit("skipDefaultBuilders"); |
| } |
| |
| auto Operator::result_begin() -> value_iterator { return results.begin(); } |
| |
| auto Operator::result_end() -> value_iterator { return results.end(); } |
| |
| auto Operator::getResults() -> 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 -> attribute_iterator { |
| return attributes.begin(); |
| } |
| auto Operator::attribute_end() const -> attribute_iterator { |
| return attributes.end(); |
| } |
| auto Operator::getAttributes() const |
| -> llvm::iterator_range<attribute_iterator> { |
| return {attribute_begin(), attribute_end()}; |
| } |
| |
| auto Operator::operand_begin() -> value_iterator { return operands.begin(); } |
| auto Operator::operand_end() -> value_iterator { return operands.end(); } |
| auto Operator::getOperands() -> value_range { |
| return {operand_begin(), operand_end()}; |
| } |
| |
| auto Operator::getArg(int index) const -> Argument { return arguments[index]; } |
| |
| // Mapping from result index to combined argument and result index. Arguments |
| // are indexed to match getArg index, while the result indexes are mapped to |
| // avoid overlap. |
| static int resultIndex(int i) { return -1 - i; } |
| |
| 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 for ops with variadic operands/results. |
| // TODO: This can be relaxed. |
| if (isVariadic()) |
| return; |
| |
| // Skip cases currently being custom generated. |
| // TODO: Remove special cases. |
| if (getTrait("::mlir::OpTrait::SameOperandsAndResultType")) |
| return; |
| |
| // We create equivalence classes of argument/result types where arguments |
| // and results are mapped into the same index space and indices corresponding |
| // to the same type are in the same equivalence class. |
| llvm::EquivalenceClasses<int> ecs; |
| resultTypeMapping.resize(getNumResults()); |
| // Captures the argument whose type matches a given result type. Preference |
| // towards capturing operands first before attributes. |
| auto captureMapping = [&](int i) { |
| bool found = false; |
| ecs.insert(resultIndex(i)); |
| auto mi = ecs.findLeader(resultIndex(i)); |
| for (auto me = ecs.member_end(); mi != me; ++mi) { |
| if (*mi < 0) { |
| auto tc = getResultTypeConstraint(i); |
| if (tc.getBuilderCall().hasValue()) { |
| resultTypeMapping[i].emplace_back(tc); |
| found = true; |
| } |
| continue; |
| } |
| |
| if (getArg(*mi).is<NamedAttribute *>()) { |
| // TODO: Handle attributes. |
| continue; |
| } else { |
| resultTypeMapping[i].emplace_back(*mi); |
| found = true; |
| } |
| } |
| return found; |
| }; |
| |
| 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; |
| |
| if (!def.isSubClassOf("AllTypesMatch")) |
| continue; |
| |
| auto values = def.getValueAsListOfStrings("values"); |
| auto root = argumentsAndResultsIndex.lookup(values.front()); |
| for (StringRef str : values) |
| ecs.unionSets(argumentsAndResultsIndex.lookup(str), root); |
| } |
| |
| // Verifies that all output types have a corresponding known input type |
| // and chooses matching operand or attribute (in that order) that |
| // matches it. |
| allResultsHaveKnownTypes = |
| all_of(llvm::seq<int>(0, getNumResults()), captureMapping); |
| |
| // If the types could be computed, then add type inference trait. |
| if (allResultsHaveKnownTypes) |
| traits.push_back(Trait::create(inferTrait->getDefInit())); |
| } |
| |
| void Operator::populateOpStructure() { |
| auto &recordKeeper = def.getRecords(); |
| auto *typeConstraintClass = recordKeeper.getClass("TypeConstraint"); |
| auto *attrClass = recordKeeper.getClass("Attr"); |
| 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 { |
| PrintFatalError(def.getLoc(), "unexpected def type; only defs deriving " |
| "from TypeConstraint or Attr 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; |
| 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 { |
| assert(argDef->isSubClassOf(attrClass)); |
| attrOrOperandMapping.push_back( |
| {OperandOrAttribute::Kind::Attribute, attrIndex}); |
| arguments.emplace_back(&attributes[attrIndex++]); |
| } |
| } |
| |
| 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] = resultIndex(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()); |
| |
| std::function<void(llvm::ListInit *)> insert; |
| insert = [&](llvm::ListInit *traitList) { |
| for (auto *traitInit : *traitList) { |
| auto *def = cast<DefInit>(traitInit)->getDef(); |
| if (def->isSubClassOf("OpTraitList")) { |
| insert(def->getValueAsListInit("traits")); |
| continue; |
| } |
| // Keep traits in the same order while skipping over duplicates. |
| if (traitSet.insert(traitInit).second) |
| 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())); |
| } |
| |
| auto Operator::getSameTypeAsResult(int index) const -> ArrayRef<ArgOrType> { |
| assert(allResultTypesKnown()); |
| return resultTypeMapping[index]; |
| } |
| |
| ArrayRef<llvm::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 = arg.dyn_cast<NamedAttribute *>()) |
| 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]; |
| } |
| |
| // Helper to return the names for accessor. |
| static SmallVector<std::string, 2> |
| getGetterOrSetterNames(bool isGetter, const Operator &op, StringRef name) { |
| Dialect::EmitPrefix prefixType = op.getDialect().getEmitAccessorPrefix(); |
| std::string prefix; |
| if (prefixType != Dialect::EmitPrefix::Raw) |
| prefix = isGetter ? "get" : "set"; |
| |
| SmallVector<std::string, 2> names; |
| bool rawToo = prefixType == Dialect::EmitPrefix::Both; |
| |
| // Whether to skip generating prefixed form for argument. This just does some |
| // basic checks. |
| // |
| // 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 skip = [&](StringRef newName) { |
| bool shouldSkip = newName == "getAttributeNames" || |
| newName == "getAttributes" || newName == "getOperation" || |
| newName == "getType"; |
| if (newName == "getOperands") { |
| // To reduce noise, skip generating the prefixed form and the warning if |
| // $operands correspond to single variadic argument. |
| if (op.getNumOperands() == 1 && op.getNumVariableLengthOperands() == 1) |
| return true; |
| shouldSkip = true; |
| } |
| if (newName == "getRegions") { |
| if (op.getNumRegions() == 1 && op.getNumVariadicRegions() == 1) |
| return true; |
| shouldSkip = true; |
| } |
| if (!shouldSkip) |
| return false; |
| |
| // This note could be avoided where the final function generated would |
| // have been identical. But preferably in the op definition avoiding using |
| // the generic name and then getting a more specialize type is better. |
| PrintNote(op.getLoc(), |
| "Skipping generation of prefixed accessor `" + newName + |
| "` as it overlaps with default one; generating raw form (`" + |
| name + "`) still"); |
| return true; |
| }; |
| |
| if (!prefix.empty()) { |
| names.push_back( |
| prefix + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true)); |
| // Skip cases which would overlap with default ones for now. |
| if (skip(names.back())) { |
| rawToo = true; |
| names.clear(); |
| } else if (rawToo) { |
| LLVM_DEBUG(llvm::errs() << "WITH_GETTER(\"" << op.getQualCppClassName() |
| << "::" << name << "\")\n" |
| << "WITH_GETTER(\"" << op.getQualCppClassName() |
| << "Adaptor::" << name << "\")\n";); |
| } |
| } |
| |
| if (prefix.empty() || rawToo) |
| names.push_back(name.str()); |
| return names; |
| } |
| |
| SmallVector<std::string, 2> Operator::getGetterNames(StringRef name) const { |
| return getGetterOrSetterNames(/*isGetter=*/true, *this, name); |
| } |
| |
| SmallVector<std::string, 2> Operator::getSetterNames(StringRef name) const { |
| return getGetterOrSetterNames(/*isGetter=*/false, *this, name); |
| } |