xref: /llvm-project/mlir/lib/TableGen/Operator.cpp (revision 7aebacbee965a83c5cc69f6a723605436651672c)
12a463c36SJacques Pienaar //===- Operator.cpp - Operator class --------------------------------------===//
22a463c36SJacques Pienaar //
330857107SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
456222a06SMehdi Amini // See https://llvm.org/LICENSE.txt for license information.
556222a06SMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
62a463c36SJacques Pienaar //
756222a06SMehdi Amini //===----------------------------------------------------------------------===//
82a463c36SJacques Pienaar //
9f8bbe5deSLei Zhang // Operator wrapper to simplify using TableGen Record defining a MLIR Op.
102a463c36SJacques Pienaar //
112a463c36SJacques Pienaar //===----------------------------------------------------------------------===//
122a463c36SJacques Pienaar 
132a463c36SJacques Pienaar #include "mlir/TableGen/Operator.h"
145e118f93SMehdi Amini #include "mlir/TableGen/Argument.h"
158f249438SJacques Pienaar #include "mlir/TableGen/Predicate.h"
1694662ee0SRiver Riddle #include "mlir/TableGen/Trait.h"
17b2cc2c34SLei Zhang #include "mlir/TableGen/Type.h"
1831f40f60SJacques Pienaar #include "llvm/ADT/EquivalenceClasses.h"
1931f40f60SJacques Pienaar #include "llvm/ADT/STLExtras.h"
2031f40f60SJacques Pienaar #include "llvm/ADT/Sequence.h"
21b6d54a1bSJacques Pienaar #include "llvm/ADT/SmallPtrSet.h"
227d1ed69cSFederico Lebrón #include "llvm/ADT/StringExtras.h"
23ebf190fcSRiver Riddle #include "llvm/ADT/TypeSwitch.h"
24b12a7c88SMehdi Amini #include "llvm/Support/Debug.h"
256a994233SJacques Pienaar #include "llvm/Support/ErrorHandling.h"
262a463c36SJacques Pienaar #include "llvm/Support/FormatVariadic.h"
272a463c36SJacques Pienaar #include "llvm/TableGen/Error.h"
282a463c36SJacques Pienaar #include "llvm/TableGen/Record.h"
291b60f0d7SJeff Niu #include <list>
302a463c36SJacques Pienaar 
31796ca609SLei Zhang #define DEBUG_TYPE "mlir-tblgen-operator"
32796ca609SLei Zhang 
332a463c36SJacques Pienaar using namespace mlir;
3412d16de5SRahul Joshi using namespace mlir::tblgen;
353e5ee82bSLei Zhang 
362a463c36SJacques Pienaar using llvm::DagInit;
372a463c36SJacques Pienaar using llvm::DefInit;
38659192b1SRahul Joshi using llvm::Init;
39659192b1SRahul Joshi using llvm::ListInit;
402a463c36SJacques Pienaar using llvm::Record;
41659192b1SRahul Joshi using llvm::StringInit;
422a463c36SJacques Pienaar 
43659192b1SRahul Joshi Operator::Operator(const Record &def)
4420e0cedfSLei Zhang     : dialect(def.getValueAsDef("opDialect")), def(def) {
4520e0cedfSLei Zhang   // The first `_` in the op's TableGen def name is treated as separating the
4620e0cedfSLei Zhang   // dialect prefix and the op class name. The dialect prefix will be ignored if
4720e0cedfSLei Zhang   // not empty. Otherwise, if def name starts with a `_`, the `_` is considered
4820e0cedfSLei Zhang   // as part of the class name.
4920e0cedfSLei Zhang   StringRef prefix;
5020e0cedfSLei Zhang   std::tie(prefix, cppClassName) = def.getName().split('_');
5120e0cedfSLei Zhang   if (prefix.empty()) {
5220e0cedfSLei Zhang     // Class name with a leading underscore and without dialect prefix
538bb83517SLei Zhang     cppClassName = def.getName();
548bb83517SLei Zhang   } else if (cppClassName.empty()) {
5520e0cedfSLei Zhang     // Class name without dialect prefix
5620e0cedfSLei Zhang     cppClassName = prefix;
572a463c36SJacques Pienaar   }
582a463c36SJacques Pienaar 
5949755871SSean Silva   cppNamespace = def.getValueAsString("cppNamespace");
6049755871SSean Silva 
618bb83517SLei Zhang   populateOpStructure();
627fb2394aSMehdi Amini   assertInvariants();
632a463c36SJacques Pienaar }
642a463c36SJacques Pienaar 
6512d16de5SRahul Joshi std::string Operator::getOperationName() const {
6620e0cedfSLei Zhang   auto prefix = dialect.getName();
6720e0cedfSLei Zhang   auto opName = def.getValueAsString("opName");
680ea6154bSJacques Pienaar   if (prefix.empty())
69adcd0268SBenjamin Kramer     return std::string(opName);
70adcd0268SBenjamin Kramer   return std::string(llvm::formatv("{0}.{1}", prefix, opName));
712a463c36SJacques Pienaar }
722a463c36SJacques Pienaar 
7312d16de5SRahul Joshi std::string Operator::getAdaptorName() const {
742d2c73c5SJacques Pienaar   return std::string(llvm::formatv("{0}Adaptor", getCppClassName()));
75fefe4366SJacques Pienaar }
76fefe4366SJacques Pienaar 
77cf6f2175SMarkus Böck std::string Operator::getGenericAdaptorName() const {
78cf6f2175SMarkus Böck   return std::string(llvm::formatv("{0}GenericAdaptor", getCppClassName()));
79cf6f2175SMarkus Böck }
80cf6f2175SMarkus Böck 
81b74192b7SRiver Riddle /// Assert the invariants of accessors generated for the given name.
82b74192b7SRiver Riddle static void assertAccessorInvariants(const Operator &op, StringRef name) {
83b74192b7SRiver Riddle   std::string accessorName =
84b74192b7SRiver Riddle       convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);
85b74192b7SRiver Riddle 
86b74192b7SRiver Riddle   // Functor used to detect when an accessor will cause an overlap with an
87b74192b7SRiver Riddle   // operation API.
88b74192b7SRiver Riddle   //
89b74192b7SRiver Riddle   // There are a little bit more invasive checks possible for cases where not
90b74192b7SRiver Riddle   // all ops have the trait that would cause overlap. For many cases here,
91b74192b7SRiver Riddle   // renaming would be better (e.g., we can only guard in limited manner
92b74192b7SRiver Riddle   // against methods from traits and interfaces here, so avoiding these in op
93b74192b7SRiver Riddle   // definition is safer).
94b74192b7SRiver Riddle   auto nameOverlapsWithOpAPI = [&](StringRef newName) {
95b74192b7SRiver Riddle     if (newName == "AttributeNames" || newName == "Attributes" ||
96b74192b7SRiver Riddle         newName == "Operation")
97b74192b7SRiver Riddle       return true;
98b74192b7SRiver Riddle     if (newName == "Operands")
99b74192b7SRiver Riddle       return op.getNumOperands() != 1 || op.getNumVariableLengthOperands() != 1;
100b74192b7SRiver Riddle     if (newName == "Regions")
101b74192b7SRiver Riddle       return op.getNumRegions() != 1 || op.getNumVariadicRegions() != 1;
102b74192b7SRiver Riddle     if (newName == "Type")
103b74192b7SRiver Riddle       return op.getNumResults() != 1;
104b74192b7SRiver Riddle     return false;
105b74192b7SRiver Riddle   };
106b74192b7SRiver Riddle   if (nameOverlapsWithOpAPI(accessorName)) {
107b74192b7SRiver Riddle     // This error could be avoided in situations where the final function is
108b74192b7SRiver Riddle     // identical, but preferably the op definition should avoid using generic
109b74192b7SRiver Riddle     // names.
110b74192b7SRiver Riddle     PrintFatalError(op.getLoc(), "generated accessor for `" + name +
111b74192b7SRiver Riddle                                      "` overlaps with a default one; please "
112b74192b7SRiver Riddle                                      "rename to avoid overlap");
113b74192b7SRiver Riddle   }
114b74192b7SRiver Riddle }
115b74192b7SRiver Riddle 
1167fb2394aSMehdi Amini void Operator::assertInvariants() const {
1177fb2394aSMehdi Amini   // Check that the name of arguments/results/regions/successors don't overlap.
1187fb2394aSMehdi Amini   DenseMap<StringRef, StringRef> existingNames;
1197fb2394aSMehdi Amini   auto checkName = [&](StringRef name, StringRef entity) {
1207fb2394aSMehdi Amini     if (name.empty())
1217fb2394aSMehdi Amini       return;
1227fb2394aSMehdi Amini     auto insertion = existingNames.insert({name, entity});
123b74192b7SRiver Riddle     if (insertion.second) {
124b74192b7SRiver Riddle       // Assert invariants for accessors generated for this name.
125b74192b7SRiver Riddle       assertAccessorInvariants(*this, name);
1267fb2394aSMehdi Amini       return;
127b74192b7SRiver Riddle     }
1287fb2394aSMehdi Amini     if (entity == insertion.first->second)
1297fb2394aSMehdi Amini       PrintFatalError(getLoc(), "op has a conflict with two " + entity +
1307fb2394aSMehdi Amini                                     " having the same name '" + name + "'");
1317fb2394aSMehdi Amini     PrintFatalError(getLoc(), "op has a conflict with " +
1327fb2394aSMehdi Amini                                   insertion.first->second + " and " + entity +
1337fb2394aSMehdi Amini                                   " both having an entry with the name '" +
1347fb2394aSMehdi Amini                                   name + "'");
1357fb2394aSMehdi Amini   };
1367fb2394aSMehdi Amini   // Check operands amongst themselves.
1377fb2394aSMehdi Amini   for (int i : llvm::seq<int>(0, getNumOperands()))
1387fb2394aSMehdi Amini     checkName(getOperand(i).name, "operands");
1397fb2394aSMehdi Amini 
1407fb2394aSMehdi Amini   // Check results amongst themselves and against operands.
1417fb2394aSMehdi Amini   for (int i : llvm::seq<int>(0, getNumResults()))
1427fb2394aSMehdi Amini     checkName(getResult(i).name, "results");
1437fb2394aSMehdi Amini 
1447fb2394aSMehdi Amini   // Check regions amongst themselves and against operands and results.
1457fb2394aSMehdi Amini   for (int i : llvm::seq<int>(0, getNumRegions()))
1467fb2394aSMehdi Amini     checkName(getRegion(i).name, "regions");
1477fb2394aSMehdi Amini 
1487fb2394aSMehdi Amini   // Check successors amongst themselves and against operands, results, and
1497fb2394aSMehdi Amini   // regions.
1507fb2394aSMehdi Amini   for (int i : llvm::seq<int>(0, getNumSuccessors()))
1517fb2394aSMehdi Amini     checkName(getSuccessor(i).name, "successors");
1527fb2394aSMehdi Amini }
1537fb2394aSMehdi Amini 
15412d16de5SRahul Joshi StringRef Operator::getDialectName() const { return dialect.getName(); }
15520e0cedfSLei Zhang 
15612d16de5SRahul Joshi StringRef Operator::getCppClassName() const { return cppClassName; }
1578bb83517SLei Zhang 
15812d16de5SRahul Joshi std::string Operator::getQualCppClassName() const {
15949755871SSean Silva   if (cppNamespace.empty())
160adcd0268SBenjamin Kramer     return std::string(cppClassName);
16149755871SSean Silva   return std::string(llvm::formatv("{0}::{1}", cppNamespace, cppClassName));
162c396c044SJacques Pienaar }
1632a463c36SJacques Pienaar 
16449755871SSean Silva StringRef Operator::getCppNamespace() const { return cppNamespace; }
16549755871SSean Silva 
16612d16de5SRahul Joshi int Operator::getNumResults() const {
167e768b076SRahul Joshi   const DagInit *results = def.getValueAsDag("results");
168c224a518SLei Zhang   return results->getNumArgs();
169c224a518SLei Zhang }
170c224a518SLei Zhang 
17112d16de5SRahul Joshi StringRef Operator::getExtraClassDeclaration() const {
172041e9618SJacques Pienaar   constexpr auto attr = "extraClassDeclaration";
173041e9618SJacques Pienaar   if (def.isValueUnset(attr))
174041e9618SJacques Pienaar     return {};
175041e9618SJacques Pienaar   return def.getValueAsString(attr);
176041e9618SJacques Pienaar }
177041e9618SJacques Pienaar 
178b0774e5fSMogball StringRef Operator::getExtraClassDefinition() const {
179b0774e5fSMogball   constexpr auto attr = "extraClassDefinition";
180b0774e5fSMogball   if (def.isValueUnset(attr))
181b0774e5fSMogball     return {};
182b0774e5fSMogball   return def.getValueAsString(attr);
183b0774e5fSMogball }
184b0774e5fSMogball 
185659192b1SRahul Joshi const Record &Operator::getDef() const { return def; }
186252de8ecSAlex Zinenko 
18712d16de5SRahul Joshi bool Operator::skipDefaultBuilders() const {
188ead1acaeSAlex Zinenko   return def.getValueAsBit("skipDefaultBuilders");
189ead1acaeSAlex Zinenko }
190ead1acaeSAlex Zinenko 
19178fdbdbfSMehdi Amini auto Operator::result_begin() const -> const_value_iterator {
19278fdbdbfSMehdi Amini   return results.begin();
19378fdbdbfSMehdi Amini }
1943f517af9SLei Zhang 
19578fdbdbfSMehdi Amini auto Operator::result_end() const -> const_value_iterator {
19678fdbdbfSMehdi Amini   return results.end();
19778fdbdbfSMehdi Amini }
1983f517af9SLei Zhang 
19978fdbdbfSMehdi Amini auto Operator::getResults() const -> const_value_range {
2003f517af9SLei Zhang   return {result_begin(), result_end()};
2013f517af9SLei Zhang }
2023f517af9SLei Zhang 
20312d16de5SRahul Joshi TypeConstraint Operator::getResultTypeConstraint(int index) const {
204e768b076SRahul Joshi   const DagInit *results = def.getValueAsDag("results");
2058f5fa566SLei Zhang   return TypeConstraint(cast<DefInit>(results->getArg(index)));
206c224a518SLei Zhang }
207c224a518SLei Zhang 
20812d16de5SRahul Joshi StringRef Operator::getResultName(int index) const {
209e768b076SRahul Joshi   const DagInit *results = def.getValueAsDag("results");
2101725b485SJacques Pienaar   return results->getArgNameStr(index);
211e0fc5038SLei Zhang }
212e0fc5038SLei Zhang 
21312d16de5SRahul Joshi auto Operator::getResultDecorators(int index) const -> var_decorator_range {
214d256b9e8SRahul Joshi   const Record *result =
21520dca522SRiver Riddle       cast<DefInit>(def.getValueAsDag("results")->getArg(index))->getDef();
21620dca522SRiver Riddle   if (!result->isSubClassOf("OpVariable"))
21720dca522SRiver Riddle     return var_decorator_range(nullptr, nullptr);
21820dca522SRiver Riddle   return *result->getValueAsListInit("decorators");
21920dca522SRiver Riddle }
22020dca522SRiver Riddle 
22112d16de5SRahul Joshi unsigned Operator::getNumVariableLengthResults() const {
222aba1acc8SRiver Riddle   return llvm::count_if(results, [](const NamedTypeConstraint &c) {
223aba1acc8SRiver Riddle     return c.constraint.isVariableLength();
224aba1acc8SRiver Riddle   });
225c224a518SLei Zhang }
226c224a518SLei Zhang 
22712d16de5SRahul Joshi unsigned Operator::getNumVariableLengthOperands() const {
228aba1acc8SRiver Riddle   return llvm::count_if(operands, [](const NamedTypeConstraint &c) {
229aba1acc8SRiver Riddle     return c.constraint.isVariableLength();
230aba1acc8SRiver Riddle   });
2311df6ca50SLei Zhang }
2321df6ca50SLei Zhang 
23312d16de5SRahul Joshi bool Operator::hasSingleVariadicArg() const {
23426d513d1SKazu Hirata   return getNumArgs() == 1 && isa<NamedTypeConstraint *>(getArg(0)) &&
23513d05787SRahul Joshi          getOperand(0).isVariadic();
23613d05787SRahul Joshi }
23713d05787SRahul Joshi 
23812d16de5SRahul Joshi Operator::arg_iterator Operator::arg_begin() const { return arguments.begin(); }
239020f9eb6SLei Zhang 
24012d16de5SRahul Joshi Operator::arg_iterator Operator::arg_end() const { return arguments.end(); }
241020f9eb6SLei Zhang 
24212d16de5SRahul Joshi Operator::arg_range Operator::getArgs() const {
243020f9eb6SLei Zhang   return {arg_begin(), arg_end()};
244020f9eb6SLei Zhang }
245020f9eb6SLei Zhang 
24612d16de5SRahul Joshi StringRef Operator::getArgName(int index) const {
247e768b076SRahul Joshi   const DagInit *argumentValues = def.getValueAsDag("arguments");
248c5a6712fSAlex Zinenko   return argumentValues->getArgNameStr(index);
2492a463c36SJacques Pienaar }
2502a463c36SJacques Pienaar 
25112d16de5SRahul Joshi auto Operator::getArgDecorators(int index) const -> var_decorator_range {
252d256b9e8SRahul Joshi   const Record *arg =
25320dca522SRiver Riddle       cast<DefInit>(def.getValueAsDag("arguments")->getArg(index))->getDef();
25420dca522SRiver Riddle   if (!arg->isSubClassOf("OpVariable"))
25520dca522SRiver Riddle     return var_decorator_range(nullptr, nullptr);
25620dca522SRiver Riddle   return *arg->getValueAsListInit("decorators");
25720dca522SRiver Riddle }
25820dca522SRiver Riddle 
25994662ee0SRiver Riddle const Trait *Operator::getTrait(StringRef trait) const {
26013c6e419SLei Zhang   for (const auto &t : traits) {
26194662ee0SRiver Riddle     if (const auto *traitDef = dyn_cast<NativeTrait>(&t)) {
26294662ee0SRiver Riddle       if (traitDef->getFullyQualifiedTraitName() == trait)
26394662ee0SRiver Riddle         return traitDef;
26494662ee0SRiver Riddle     } else if (const auto *traitDef = dyn_cast<InternalTrait>(&t)) {
26594662ee0SRiver Riddle       if (traitDef->getFullyQualifiedTraitName() == trait)
26694662ee0SRiver Riddle         return traitDef;
26794662ee0SRiver Riddle     } else if (const auto *traitDef = dyn_cast<InterfaceTrait>(&t)) {
26894662ee0SRiver Riddle       if (traitDef->getFullyQualifiedTraitName() == trait)
26994662ee0SRiver Riddle         return traitDef;
270c489f50eSFeng Liu     }
2711725b485SJacques Pienaar   }
27213c6e419SLei Zhang   return nullptr;
273eb3f8dcbSLei Zhang }
274eb3f8dcbSLei Zhang 
27512d16de5SRahul Joshi auto Operator::region_begin() const -> const_region_iterator {
2760359b86dSRiver Riddle   return regions.begin();
2770359b86dSRiver Riddle }
27812d16de5SRahul Joshi auto Operator::region_end() const -> const_region_iterator {
2790359b86dSRiver Riddle   return regions.end();
2800359b86dSRiver Riddle }
28112d16de5SRahul Joshi auto Operator::getRegions() const
2820359b86dSRiver Riddle     -> llvm::iterator_range<const_region_iterator> {
2830359b86dSRiver Riddle   return {region_begin(), region_end()};
2840359b86dSRiver Riddle }
2850359b86dSRiver Riddle 
28612d16de5SRahul Joshi unsigned Operator::getNumRegions() const { return regions.size(); }
2873650df50SLei Zhang 
28812d16de5SRahul Joshi const NamedRegion &Operator::getRegion(unsigned index) const {
2893650df50SLei Zhang   return regions[index];
2903650df50SLei Zhang }
291d4c8c8deSLei Zhang 
29212d16de5SRahul Joshi unsigned Operator::getNumVariadicRegions() const {
2930359b86dSRiver Riddle   return llvm::count_if(regions,
2940359b86dSRiver Riddle                         [](const NamedRegion &c) { return c.isVariadic(); });
2950359b86dSRiver Riddle }
2960359b86dSRiver Riddle 
29712d16de5SRahul Joshi auto Operator::successor_begin() const -> const_successor_iterator {
298b1de971bSRiver Riddle   return successors.begin();
299b1de971bSRiver Riddle }
30012d16de5SRahul Joshi auto Operator::successor_end() const -> const_successor_iterator {
301b1de971bSRiver Riddle   return successors.end();
302b1de971bSRiver Riddle }
30312d16de5SRahul Joshi auto Operator::getSuccessors() const
304b1de971bSRiver Riddle     -> llvm::iterator_range<const_successor_iterator> {
305b1de971bSRiver Riddle   return {successor_begin(), successor_end()};
306b1de971bSRiver Riddle }
307b1de971bSRiver Riddle 
30812d16de5SRahul Joshi unsigned Operator::getNumSuccessors() const { return successors.size(); }
309b1de971bSRiver Riddle 
31012d16de5SRahul Joshi const NamedSuccessor &Operator::getSuccessor(unsigned index) const {
311b1de971bSRiver Riddle   return successors[index];
312b1de971bSRiver Riddle }
313b1de971bSRiver Riddle 
31412d16de5SRahul Joshi unsigned Operator::getNumVariadicSuccessors() const {
315b1de971bSRiver Riddle   return llvm::count_if(successors,
316b1de971bSRiver Riddle                         [](const NamedSuccessor &c) { return c.isVariadic(); });
317b1de971bSRiver Riddle }
318b1de971bSRiver Riddle 
31912d16de5SRahul Joshi auto Operator::trait_begin() const -> const_trait_iterator {
3201725b485SJacques Pienaar   return traits.begin();
3211725b485SJacques Pienaar }
32212d16de5SRahul Joshi auto Operator::trait_end() const -> const_trait_iterator {
3231725b485SJacques Pienaar   return traits.end();
3241725b485SJacques Pienaar }
32512d16de5SRahul Joshi auto Operator::getTraits() const -> llvm::iterator_range<const_trait_iterator> {
3261725b485SJacques Pienaar   return {trait_begin(), trait_end()};
3271725b485SJacques Pienaar }
3281725b485SJacques Pienaar 
3295e118f93SMehdi Amini auto Operator::attribute_begin() const -> const_attribute_iterator {
3302a463c36SJacques Pienaar   return attributes.begin();
3312a463c36SJacques Pienaar }
3325e118f93SMehdi Amini auto Operator::attribute_end() const -> const_attribute_iterator {
3332a463c36SJacques Pienaar   return attributes.end();
3342a463c36SJacques Pienaar }
33512d16de5SRahul Joshi auto Operator::getAttributes() const
3365e118f93SMehdi Amini     -> llvm::iterator_range<const_attribute_iterator> {
3375e118f93SMehdi Amini   return {attribute_begin(), attribute_end()};
3385e118f93SMehdi Amini }
3395e118f93SMehdi Amini auto Operator::attribute_begin() -> attribute_iterator {
3405e118f93SMehdi Amini   return attributes.begin();
3415e118f93SMehdi Amini }
3425e118f93SMehdi Amini auto Operator::attribute_end() -> attribute_iterator {
3435e118f93SMehdi Amini   return attributes.end();
3445e118f93SMehdi Amini }
3455e118f93SMehdi Amini auto Operator::getAttributes() -> llvm::iterator_range<attribute_iterator> {
3462a463c36SJacques Pienaar   return {attribute_begin(), attribute_end()};
3472a463c36SJacques Pienaar }
3482a463c36SJacques Pienaar 
34978fdbdbfSMehdi Amini auto Operator::operand_begin() const -> const_value_iterator {
35078fdbdbfSMehdi Amini   return operands.begin();
35178fdbdbfSMehdi Amini }
35278fdbdbfSMehdi Amini auto Operator::operand_end() const -> const_value_iterator {
35378fdbdbfSMehdi Amini   return operands.end();
35478fdbdbfSMehdi Amini }
35578fdbdbfSMehdi Amini auto Operator::getOperands() const -> const_value_range {
3562a463c36SJacques Pienaar   return {operand_begin(), operand_end()};
3572a463c36SJacques Pienaar }
3582a463c36SJacques Pienaar 
35912d16de5SRahul Joshi auto Operator::getArg(int index) const -> Argument { return arguments[index]; }
3602a463c36SJacques Pienaar 
36112d16de5SRahul Joshi bool Operator::isVariadic() const {
36231f40f60SJacques Pienaar   return any_of(llvm::concat<const NamedTypeConstraint>(operands, results),
36331f40f60SJacques Pienaar                 [](const NamedTypeConstraint &op) { return op.isVariadic(); });
36431f40f60SJacques Pienaar }
36531f40f60SJacques Pienaar 
36612d16de5SRahul Joshi void Operator::populateTypeInferenceInfo(
36731f40f60SJacques Pienaar     const llvm::StringMap<int> &argumentsAndResultsIndex) {
36831f40f60SJacques Pienaar   // If the type inference op interface is not registered, then do not attempt
36931f40f60SJacques Pienaar   // to determine if the result types an be inferred.
37031f40f60SJacques Pienaar   auto &recordKeeper = def.getRecords();
37131f40f60SJacques Pienaar   auto *inferTrait = recordKeeper.getDef(inferTypeOpInterface);
37231f40f60SJacques Pienaar   allResultsHaveKnownTypes = false;
37331f40f60SJacques Pienaar   if (!inferTrait)
37431f40f60SJacques Pienaar     return;
37531f40f60SJacques Pienaar 
37631f40f60SJacques Pienaar   // If there are no results, the skip this else the build method generated
37731f40f60SJacques Pienaar   // overlaps with another autogenerated builder.
37831f40f60SJacques Pienaar   if (getNumResults() == 0)
37931f40f60SJacques Pienaar     return;
38031f40f60SJacques Pienaar 
3813ce2ee28SBenjamin Kramer   // Skip ops with variadic or optional results.
3823ce2ee28SBenjamin Kramer   if (getNumVariableLengthResults() > 0)
38331f40f60SJacques Pienaar     return;
38431f40f60SJacques Pienaar 
38531f40f60SJacques Pienaar   // Skip cases currently being custom generated.
38631f40f60SJacques Pienaar   // TODO: Remove special cases.
38792a836daSRiver Riddle   if (getTrait("::mlir::OpTrait::SameOperandsAndResultType")) {
38892a836daSRiver Riddle     // Check for a non-variable length operand to use as the type anchor.
38992a836daSRiver Riddle     auto *operandI = llvm::find_if(arguments, [](const Argument &arg) {
39068f58812STres Popp       NamedTypeConstraint *operand = llvm::dyn_cast_if_present<NamedTypeConstraint *>(arg);
39192a836daSRiver Riddle       return operand && !operand->isVariableLength();
39292a836daSRiver Riddle     });
39392a836daSRiver Riddle     if (operandI == arguments.end())
39431f40f60SJacques Pienaar       return;
39531f40f60SJacques Pienaar 
3961b60f0d7SJeff Niu     // All result types are inferred from the operand type.
39792a836daSRiver Riddle     int operandIdx = operandI - arguments.begin();
39892a836daSRiver Riddle     for (int i = 0; i < getNumResults(); ++i)
3991b60f0d7SJeff Niu       resultTypeMapping.emplace_back(operandIdx, "$_self");
40092a836daSRiver Riddle 
40192a836daSRiver Riddle     allResultsHaveKnownTypes = true;
40292a836daSRiver Riddle     traits.push_back(Trait::create(inferTrait->getDefInit()));
40392a836daSRiver Riddle     return;
40492a836daSRiver Riddle   }
40592a836daSRiver Riddle 
4061b60f0d7SJeff Niu   /// This struct represents a node in this operation's result type inferenece
4071b60f0d7SJeff Niu   /// graph. Each node has a list of incoming type inference edges `sources`.
4081b60f0d7SJeff Niu   /// Each edge represents a "source" from which the result type can be
4091b60f0d7SJeff Niu   /// inferred, either an operand (leaf) or another result (node). When a node
4101b60f0d7SJeff Niu   /// is known to have a fully-inferred type, `inferred` is set to true.
4111b60f0d7SJeff Niu   struct ResultTypeInference {
4121b60f0d7SJeff Niu     /// The list of incoming type inference edges.
4131b60f0d7SJeff Niu     SmallVector<InferredResultType> sources;
4141b60f0d7SJeff Niu     /// This flag is set to true when the result type is known to be inferrable.
4151b60f0d7SJeff Niu     bool inferred = false;
41631f40f60SJacques Pienaar   };
41731f40f60SJacques Pienaar 
4181b60f0d7SJeff Niu   // This vector represents the type inference graph, with one node for each
4191b60f0d7SJeff Niu   // operation result. The nth element is the node for the nth result.
4201b60f0d7SJeff Niu   SmallVector<ResultTypeInference> inference(getNumResults(), {});
4211b60f0d7SJeff Niu 
4221b60f0d7SJeff Niu   // For all results whose types are buildable, initialize their type inference
4231b60f0d7SJeff Niu   // nodes with an edge to themselves. Mark those nodes are fully-inferred.
4248c258fdaSJakub Kuderski   for (auto [idx, infer] : llvm::enumerate(inference)) {
4251b60f0d7SJeff Niu     if (getResult(idx).constraint.getBuilderCall()) {
4261b60f0d7SJeff Niu       infer.sources.emplace_back(InferredResultType::mapResultIndex(idx),
4271b60f0d7SJeff Niu                                  "$_self");
4281b60f0d7SJeff Niu       infer.inferred = true;
4291b60f0d7SJeff Niu     }
4301b60f0d7SJeff Niu   }
4311b60f0d7SJeff Niu 
4321b60f0d7SJeff Niu   // Use `AllTypesMatch` and `TypesMatchWith` operation traits to build the
4331b60f0d7SJeff Niu   // result type inference graph.
43494662ee0SRiver Riddle   for (const Trait &trait : traits) {
435659192b1SRahul Joshi     const Record &def = trait.getDef();
4361b60f0d7SJeff Niu 
43731f40f60SJacques Pienaar     // If the infer type op interface was manually added, then treat it as
43831f40f60SJacques Pienaar     // intention that the op needs special handling.
43931f40f60SJacques Pienaar     // TODO: Reconsider whether to always generate, this is more conservative
44031f40f60SJacques Pienaar     // and keeps existing behavior so starting that way for now.
44131f40f60SJacques Pienaar     if (def.isSubClassOf(
44231f40f60SJacques Pienaar             llvm::formatv("{0}::Trait", inferTypeOpInterface).str()))
44331f40f60SJacques Pienaar       return;
44494662ee0SRiver Riddle     if (const auto *traitDef = dyn_cast<InterfaceTrait>(&trait))
44594662ee0SRiver Riddle       if (&traitDef->getDef() == inferTrait)
44631f40f60SJacques Pienaar         return;
44731f40f60SJacques Pienaar 
4481b60f0d7SJeff Niu     // The `TypesMatchWith` trait represents a 1 -> 1 type inference edge with a
4491b60f0d7SJeff Niu     // type transformer.
4501b60f0d7SJeff Niu     if (def.isSubClassOf("TypesMatchWith")) {
4511b60f0d7SJeff Niu       int target = argumentsAndResultsIndex.lookup(def.getValueAsString("rhs"));
4521b60f0d7SJeff Niu       // Ignore operand type inference.
4531b60f0d7SJeff Niu       if (InferredResultType::isArgIndex(target))
4541b60f0d7SJeff Niu         continue;
4551b60f0d7SJeff Niu       int resultIndex = InferredResultType::unmapResultIndex(target);
4561b60f0d7SJeff Niu       ResultTypeInference &infer = inference[resultIndex];
4571b60f0d7SJeff Niu       // If the type of the result has already been inferred, do nothing.
4581b60f0d7SJeff Niu       if (infer.inferred)
4591b60f0d7SJeff Niu         continue;
4601b60f0d7SJeff Niu       int sourceIndex =
4611b60f0d7SJeff Niu           argumentsAndResultsIndex.lookup(def.getValueAsString("lhs"));
4621b60f0d7SJeff Niu       infer.sources.emplace_back(sourceIndex,
4631b60f0d7SJeff Niu                                  def.getValueAsString("transformer").str());
4641b60f0d7SJeff Niu       // Locally propagate inferredness.
4651b60f0d7SJeff Niu       infer.inferred =
4661b60f0d7SJeff Niu           InferredResultType::isArgIndex(sourceIndex) ||
4671b60f0d7SJeff Niu           inference[InferredResultType::unmapResultIndex(sourceIndex)].inferred;
4681b60f0d7SJeff Niu       continue;
4691b60f0d7SJeff Niu     }
4701b60f0d7SJeff Niu 
47131f40f60SJacques Pienaar     if (!def.isSubClassOf("AllTypesMatch"))
47231f40f60SJacques Pienaar       continue;
47331f40f60SJacques Pienaar 
47431f40f60SJacques Pienaar     auto values = def.getValueAsListOfStrings("values");
4751b60f0d7SJeff Niu     // The `AllTypesMatch` trait represents an N <-> N fanin and fanout. That
4761b60f0d7SJeff Niu     // is, every result type has an edge from every other type. However, if any
4771b60f0d7SJeff Niu     // one of the values refers to an operand or a result with a fully-inferred
4781b60f0d7SJeff Niu     // type, we can infer all other types from that value. Try to find a
4791b60f0d7SJeff Niu     // fully-inferred type in the list.
4801b60f0d7SJeff Niu     std::optional<int> fullyInferredIndex;
4811b60f0d7SJeff Niu     SmallVector<int> resultIndices;
4821b60f0d7SJeff Niu     for (StringRef name : values) {
4831b60f0d7SJeff Niu       int index = argumentsAndResultsIndex.lookup(name);
4841b60f0d7SJeff Niu       if (InferredResultType::isResultIndex(index))
4851b60f0d7SJeff Niu         resultIndices.push_back(InferredResultType::unmapResultIndex(index));
4861b60f0d7SJeff Niu       if (InferredResultType::isArgIndex(index) ||
4871b60f0d7SJeff Niu           inference[InferredResultType::unmapResultIndex(index)].inferred)
4881b60f0d7SJeff Niu         fullyInferredIndex = index;
4891b60f0d7SJeff Niu     }
4901b60f0d7SJeff Niu     if (fullyInferredIndex) {
4911b60f0d7SJeff Niu       // Make the fully-inferred type the only source for all results that
4921b60f0d7SJeff Niu       // aren't already inferred -- a 1 -> N fanout.
4931b60f0d7SJeff Niu       for (int resultIndex : resultIndices) {
4941b60f0d7SJeff Niu         ResultTypeInference &infer = inference[resultIndex];
4951b60f0d7SJeff Niu         if (!infer.inferred) {
4961b60f0d7SJeff Niu           infer.sources.assign(1, {*fullyInferredIndex, "$_self"});
4971b60f0d7SJeff Niu           infer.inferred = true;
4981b60f0d7SJeff Niu         }
4991b60f0d7SJeff Niu       }
5001b60f0d7SJeff Niu     } else {
5011b60f0d7SJeff Niu       // Add an edge between every result and every other type; N <-> N.
5021b60f0d7SJeff Niu       for (int resultIndex : resultIndices) {
5031b60f0d7SJeff Niu         for (int otherResultIndex : resultIndices) {
5041b60f0d7SJeff Niu           if (resultIndex == otherResultIndex)
5051b60f0d7SJeff Niu             continue;
506*7aebacbeSPhilipp Schilk           inference[resultIndex].sources.emplace_back(
507*7aebacbeSPhilipp Schilk               InferredResultType::unmapResultIndex(otherResultIndex), "$_self");
5081b60f0d7SJeff Niu         }
5091b60f0d7SJeff Niu       }
5101b60f0d7SJeff Niu     }
51131f40f60SJacques Pienaar   }
51231f40f60SJacques Pienaar 
5131b60f0d7SJeff Niu   // Propagate inferredness until a fixed point.
5141e60e7adSMarkus Böck   std::vector<ResultTypeInference *> worklist;
5151b60f0d7SJeff Niu   for (ResultTypeInference &infer : inference)
5161b60f0d7SJeff Niu     if (!infer.inferred)
5171b60f0d7SJeff Niu       worklist.push_back(&infer);
5181b60f0d7SJeff Niu   bool changed;
5191b60f0d7SJeff Niu   do {
5201b60f0d7SJeff Niu     changed = false;
5211e60e7adSMarkus Böck     for (auto cur = worklist.begin(); cur != worklist.end();) {
5221b60f0d7SJeff Niu       ResultTypeInference &infer = **cur;
5231e60e7adSMarkus Böck 
5241e60e7adSMarkus Böck       InferredResultType *iter =
5251e60e7adSMarkus Böck           llvm::find_if(infer.sources, [&](const InferredResultType &source) {
5261b60f0d7SJeff Niu             assert(InferredResultType::isResultIndex(source.getIndex()));
5271e60e7adSMarkus Böck             return inference[InferredResultType::unmapResultIndex(
5281e60e7adSMarkus Böck                                  source.getIndex())]
5291e60e7adSMarkus Böck                 .inferred;
5301e60e7adSMarkus Böck           });
5311e60e7adSMarkus Böck       if (iter == infer.sources.end()) {
5321e60e7adSMarkus Böck         ++cur;
5331e60e7adSMarkus Böck         continue;
5341e60e7adSMarkus Böck       }
5351e60e7adSMarkus Böck 
5361b60f0d7SJeff Niu       changed = true;
5371b60f0d7SJeff Niu       infer.inferred = true;
5381b60f0d7SJeff Niu       // Make this the only source for the result. This breaks any cycles.
5391e60e7adSMarkus Böck       infer.sources.assign(1, *iter);
5401e60e7adSMarkus Böck       cur = worklist.erase(cur);
5411b60f0d7SJeff Niu     }
5421b60f0d7SJeff Niu   } while (changed);
5431b60f0d7SJeff Niu 
5441b60f0d7SJeff Niu   allResultsHaveKnownTypes = worklist.empty();
54531f40f60SJacques Pienaar 
54631f40f60SJacques Pienaar   // If the types could be computed, then add type inference trait.
5471b60f0d7SJeff Niu   if (allResultsHaveKnownTypes) {
54894662ee0SRiver Riddle     traits.push_back(Trait::create(inferTrait->getDefInit()));
5491b60f0d7SJeff Niu     for (const ResultTypeInference &infer : inference)
5501b60f0d7SJeff Niu       resultTypeMapping.push_back(infer.sources.front());
5511b60f0d7SJeff Niu   }
55231f40f60SJacques Pienaar }
55331f40f60SJacques Pienaar 
55412d16de5SRahul Joshi void Operator::populateOpStructure() {
5552a463c36SJacques Pienaar   auto &recordKeeper = def.getRecords();
55631f40f60SJacques Pienaar   auto *typeConstraintClass = recordKeeper.getClass("TypeConstraint");
55731f40f60SJacques Pienaar   auto *attrClass = recordKeeper.getClass("Attr");
5585e118f93SMehdi Amini   auto *propertyClass = recordKeeper.getClass("Property");
55931f40f60SJacques Pienaar   auto *derivedAttrClass = recordKeeper.getClass("DerivedAttr");
56031f40f60SJacques Pienaar   auto *opVarClass = recordKeeper.getClass("OpVariable");
56195949a0dSAlex Zinenko   numNativeAttributes = 0;
5622a463c36SJacques Pienaar 
563e768b076SRahul Joshi   const DagInit *argumentValues = def.getValueAsDag("arguments");
564796ca609SLei Zhang   unsigned numArgs = argumentValues->getNumArgs();
565796ca609SLei Zhang 
56631f40f60SJacques Pienaar   // Mapping from name of to argument or result index. Arguments are indexed
56731f40f60SJacques Pienaar   // to match getArg index, while the results are negatively indexed.
56831f40f60SJacques Pienaar   llvm::StringMap<int> argumentsAndResultsIndex;
56931f40f60SJacques Pienaar 
57095949a0dSAlex Zinenko   // Handle operands and native attributes.
571796ca609SLei Zhang   for (unsigned i = 0; i != numArgs; ++i) {
57231f40f60SJacques Pienaar     auto *arg = argumentValues->getArg(i);
57366647a31SLei Zhang     auto givenName = argumentValues->getArgNameStr(i);
57431f40f60SJacques Pienaar     auto *argDefInit = dyn_cast<DefInit>(arg);
5752a463c36SJacques Pienaar     if (!argDefInit)
5762a463c36SJacques Pienaar       PrintFatalError(def.getLoc(),
577e0fc5038SLei Zhang                       Twine("undefined type for argument #") + Twine(i));
578d256b9e8SRahul Joshi     const Record *argDef = argDefInit->getDef();
57920dca522SRiver Riddle     if (argDef->isSubClassOf(opVarClass))
58020dca522SRiver Riddle       argDef = argDef->getValueAsDef("constraint");
58195949a0dSAlex Zinenko 
58295949a0dSAlex Zinenko     if (argDef->isSubClassOf(typeConstraintClass)) {
583509cd739SJacques Pienaar       operands.push_back(
58420dca522SRiver Riddle           NamedTypeConstraint{givenName, TypeConstraint(argDef)});
58595949a0dSAlex Zinenko     } else if (argDef->isSubClassOf(attrClass)) {
58666647a31SLei Zhang       if (givenName.empty())
5872a463c36SJacques Pienaar         PrintFatalError(argDef->getLoc(), "attributes must be named");
58895949a0dSAlex Zinenko       if (argDef->isSubClassOf(derivedAttrClass))
58995949a0dSAlex Zinenko         PrintFatalError(argDef->getLoc(),
590dde5bf23SJacques Pienaar                         "derived attributes not allowed in argument list");
5919b034f0bSLei Zhang       attributes.push_back({givenName, Attribute(argDef)});
59295949a0dSAlex Zinenko       ++numNativeAttributes;
5935e118f93SMehdi Amini     } else if (argDef->isSubClassOf(propertyClass)) {
5945e118f93SMehdi Amini       if (givenName.empty())
5955e118f93SMehdi Amini         PrintFatalError(argDef->getLoc(), "properties must be named");
5965e118f93SMehdi Amini       properties.push_back({givenName, Property(argDef)});
59795949a0dSAlex Zinenko     } else {
5985e118f93SMehdi Amini       PrintFatalError(def.getLoc(),
5995e118f93SMehdi Amini                       "unexpected def type; only defs deriving "
6005e118f93SMehdi Amini                       "from TypeConstraint or Attr or Property are allowed");
60195949a0dSAlex Zinenko     }
60231f40f60SJacques Pienaar     if (!givenName.empty())
60331f40f60SJacques Pienaar       argumentsAndResultsIndex[givenName] = i;
604dde5bf23SJacques Pienaar   }
605dde5bf23SJacques Pienaar 
606f8bbe5deSLei Zhang   // Handle derived attributes.
607dde5bf23SJacques Pienaar   for (const auto &val : def.getValues()) {
608dde5bf23SJacques Pienaar     if (auto *record = dyn_cast<llvm::RecordRecTy>(val.getType())) {
609dde5bf23SJacques Pienaar       if (!record->isSubClassOf(attrClass))
610dde5bf23SJacques Pienaar         continue;
611dde5bf23SJacques Pienaar       if (!record->isSubClassOf(derivedAttrClass))
612dde5bf23SJacques Pienaar         PrintFatalError(def.getLoc(),
613dde5bf23SJacques Pienaar                         "unexpected Attr where only DerivedAttr is allowed");
614dde5bf23SJacques Pienaar 
615dde5bf23SJacques Pienaar       if (record->getClasses().size() != 1) {
6162a463c36SJacques Pienaar         PrintFatalError(
6172a463c36SJacques Pienaar             def.getLoc(),
618dde5bf23SJacques Pienaar             "unsupported attribute modelling, only single class expected");
6192a463c36SJacques Pienaar       }
620659192b1SRahul Joshi       attributes.push_back({cast<StringInit>(val.getNameInit())->getValue(),
6219b034f0bSLei Zhang                             Attribute(cast<DefInit>(val.getValue()))});
622dde5bf23SJacques Pienaar     }
6232a463c36SJacques Pienaar   }
6241df6ca50SLei Zhang 
625796ca609SLei Zhang   // Populate `arguments`. This must happen after we've finalized `operands` and
626796ca609SLei Zhang   // `attributes` because we will put their elements' pointers in `arguments`.
627796ca609SLei Zhang   // SmallVector may perform re-allocation under the hood when adding new
628796ca609SLei Zhang   // elements.
6295e118f93SMehdi Amini   int operandIndex = 0, attrIndex = 0, propIndex = 0;
630796ca609SLei Zhang   for (unsigned i = 0; i != numArgs; ++i) {
631d256b9e8SRahul Joshi     const Record *argDef =
632d256b9e8SRahul Joshi         dyn_cast<DefInit>(argumentValues->getArg(i))->getDef();
63320dca522SRiver Riddle     if (argDef->isSubClassOf(opVarClass))
63420dca522SRiver Riddle       argDef = argDef->getValueAsDef("constraint");
635796ca609SLei Zhang 
636796ca609SLei Zhang     if (argDef->isSubClassOf(typeConstraintClass)) {
63771b9d89dSJacques Pienaar       attrOrOperandMapping.push_back(
63871b9d89dSJacques Pienaar           {OperandOrAttribute::Kind::Operand, operandIndex});
639796ca609SLei Zhang       arguments.emplace_back(&operands[operandIndex++]);
6405e118f93SMehdi Amini     } else if (argDef->isSubClassOf(attrClass)) {
64171b9d89dSJacques Pienaar       attrOrOperandMapping.push_back(
64271b9d89dSJacques Pienaar           {OperandOrAttribute::Kind::Attribute, attrIndex});
643796ca609SLei Zhang       arguments.emplace_back(&attributes[attrIndex++]);
6445e118f93SMehdi Amini     } else {
6455e118f93SMehdi Amini       assert(argDef->isSubClassOf(propertyClass));
6465e118f93SMehdi Amini       arguments.emplace_back(&properties[propIndex++]);
647796ca609SLei Zhang     }
648796ca609SLei Zhang   }
649796ca609SLei Zhang 
650e0fc5038SLei Zhang   auto *resultsDag = def.getValueAsDag("results");
651e0fc5038SLei Zhang   auto *outsOp = dyn_cast<DefInit>(resultsDag->getOperator());
652e0fc5038SLei Zhang   if (!outsOp || outsOp->getDef()->getName() != "outs") {
653e0fc5038SLei Zhang     PrintFatalError(def.getLoc(), "'results' must have 'outs' directive");
654e0fc5038SLei Zhang   }
655e0fc5038SLei Zhang 
656e0fc5038SLei Zhang   // Handle results.
657e0fc5038SLei Zhang   for (unsigned i = 0, e = resultsDag->getNumArgs(); i < e; ++i) {
658e0fc5038SLei Zhang     auto name = resultsDag->getArgNameStr(i);
65920dca522SRiver Riddle     auto *resultInit = dyn_cast<DefInit>(resultsDag->getArg(i));
66020dca522SRiver Riddle     if (!resultInit) {
661e0fc5038SLei Zhang       PrintFatalError(def.getLoc(),
662e0fc5038SLei Zhang                       Twine("undefined type for result #") + Twine(i));
663e0fc5038SLei Zhang     }
66420dca522SRiver Riddle     auto *resultDef = resultInit->getDef();
66520dca522SRiver Riddle     if (resultDef->isSubClassOf(opVarClass))
66620dca522SRiver Riddle       resultDef = resultDef->getValueAsDef("constraint");
6678f5fa566SLei Zhang     results.push_back({name, TypeConstraint(resultDef)});
66831f40f60SJacques Pienaar     if (!name.empty())
6691b60f0d7SJeff Niu       argumentsAndResultsIndex[name] = InferredResultType::mapResultIndex(i);
6704e103a12SRiver Riddle 
6714e103a12SRiver Riddle     // We currently only support VariadicOfVariadic operands.
6724e103a12SRiver Riddle     if (results.back().constraint.isVariadicOfVariadic()) {
6734e103a12SRiver Riddle       PrintFatalError(
6744e103a12SRiver Riddle           def.getLoc(),
6754e103a12SRiver Riddle           "'VariadicOfVariadic' results are currently not supported");
6764e103a12SRiver Riddle     }
677e0fc5038SLei Zhang   }
678e0fc5038SLei Zhang 
679b1de971bSRiver Riddle   // Handle successors
680b1de971bSRiver Riddle   auto *successorsDag = def.getValueAsDag("successors");
681b1de971bSRiver Riddle   auto *successorsOp = dyn_cast<DefInit>(successorsDag->getOperator());
682b1de971bSRiver Riddle   if (!successorsOp || successorsOp->getDef()->getName() != "successor") {
683b1de971bSRiver Riddle     PrintFatalError(def.getLoc(),
684b1de971bSRiver Riddle                     "'successors' must have 'successor' directive");
685b1de971bSRiver Riddle   }
686b1de971bSRiver Riddle 
687b1de971bSRiver Riddle   for (unsigned i = 0, e = successorsDag->getNumArgs(); i < e; ++i) {
688b1de971bSRiver Riddle     auto name = successorsDag->getArgNameStr(i);
689b1de971bSRiver Riddle     auto *successorInit = dyn_cast<DefInit>(successorsDag->getArg(i));
690b1de971bSRiver Riddle     if (!successorInit) {
691b1de971bSRiver Riddle       PrintFatalError(def.getLoc(),
692b1de971bSRiver Riddle                       Twine("undefined kind for successor #") + Twine(i));
693b1de971bSRiver Riddle     }
694b1de971bSRiver Riddle     Successor successor(successorInit->getDef());
695b1de971bSRiver Riddle 
696b1de971bSRiver Riddle     // Only support variadic successors if it is the last one for now.
697b1de971bSRiver Riddle     if (i != e - 1 && successor.isVariadic())
698b1de971bSRiver Riddle       PrintFatalError(def.getLoc(), "only the last successor can be variadic");
699b1de971bSRiver Riddle     successors.push_back({name, successor});
700b1de971bSRiver Riddle   }
701b1de971bSRiver Riddle 
702b6d54a1bSJacques Pienaar   // Create list of traits, skipping over duplicates: appending to lists in
703b6d54a1bSJacques Pienaar   // tablegen is easy, making them unique less so, so dedupe here.
70431f40f60SJacques Pienaar   if (auto *traitList = def.getValueAsListInit("traits")) {
705b6d54a1bSJacques Pienaar     // This is uniquing based on pointers of the trait.
706659192b1SRahul Joshi     SmallPtrSet<const Init *, 32> traitSet;
707b6d54a1bSJacques Pienaar     traits.reserve(traitSet.size());
7084b897de5SJacques Pienaar 
70991e8a63cSChia-hung Duan     // The declaration order of traits imply the verification order of traits.
71091e8a63cSChia-hung Duan     // Some traits may require other traits to be verified first then they can
71191e8a63cSChia-hung Duan     // do further verification based on those verified facts. If you see this
71291e8a63cSChia-hung Duan     // error, fix the traits declaration order by checking the `dependentTraits`
71391e8a63cSChia-hung Duan     // field.
714d256b9e8SRahul Joshi     auto verifyTraitValidity = [&](const Record *trait) {
71591e8a63cSChia-hung Duan       auto *dependentTraits = trait->getValueAsListInit("dependentTraits");
71691e8a63cSChia-hung Duan       for (auto *traitInit : *dependentTraits)
717ce14f7b1SKazu Hirata         if (!traitSet.contains(traitInit))
71891e8a63cSChia-hung Duan           PrintFatalError(
71991e8a63cSChia-hung Duan               def.getLoc(),
72091e8a63cSChia-hung Duan               trait->getValueAsString("trait") + " requires " +
72191e8a63cSChia-hung Duan                   cast<DefInit>(traitInit)->getDef()->getValueAsString(
72291e8a63cSChia-hung Duan                       "trait") +
72391e8a63cSChia-hung Duan                   " to precede it in traits list");
72491e8a63cSChia-hung Duan     };
72591e8a63cSChia-hung Duan 
726659192b1SRahul Joshi     std::function<void(const ListInit *)> insert;
727659192b1SRahul Joshi     insert = [&](const ListInit *traitList) {
72831f40f60SJacques Pienaar       for (auto *traitInit : *traitList) {
7294b897de5SJacques Pienaar         auto *def = cast<DefInit>(traitInit)->getDef();
730697a5036SSanjoy Das         if (def->isSubClassOf("TraitList")) {
7314b897de5SJacques Pienaar           insert(def->getValueAsListInit("traits"));
7324b897de5SJacques Pienaar           continue;
7334b897de5SJacques Pienaar         }
73491e8a63cSChia-hung Duan 
73583a635c0SRiver Riddle         // Ignore duplicates.
73683a635c0SRiver Riddle         if (!traitSet.insert(traitInit).second)
73783a635c0SRiver Riddle           continue;
73883a635c0SRiver Riddle 
73983a635c0SRiver Riddle         // If this is an interface with base classes, add the bases to the
74083a635c0SRiver Riddle         // trait list.
74183a635c0SRiver Riddle         if (def->isSubClassOf("Interface"))
74283a635c0SRiver Riddle           insert(def->getValueAsListInit("baseInterfaces"));
74383a635c0SRiver Riddle 
74491e8a63cSChia-hung Duan         // Verify if the trait has all the dependent traits declared before
74591e8a63cSChia-hung Duan         // itself.
74691e8a63cSChia-hung Duan         verifyTraitValidity(def);
74794662ee0SRiver Riddle         traits.push_back(Trait::create(traitInit));
748b6d54a1bSJacques Pienaar       }
7494b897de5SJacques Pienaar     };
7504b897de5SJacques Pienaar     insert(traitList);
751b6d54a1bSJacques Pienaar   }
752d4c8c8deSLei Zhang 
75331f40f60SJacques Pienaar   populateTypeInferenceInfo(argumentsAndResultsIndex);
75431f40f60SJacques Pienaar 
755d4c8c8deSLei Zhang   // Handle regions
7563650df50SLei Zhang   auto *regionsDag = def.getValueAsDag("regions");
7573650df50SLei Zhang   auto *regionsOp = dyn_cast<DefInit>(regionsDag->getOperator());
7583650df50SLei Zhang   if (!regionsOp || regionsOp->getDef()->getName() != "region") {
7593650df50SLei Zhang     PrintFatalError(def.getLoc(), "'regions' must have 'region' directive");
7603650df50SLei Zhang   }
7613650df50SLei Zhang 
7623650df50SLei Zhang   for (unsigned i = 0, e = regionsDag->getNumArgs(); i < e; ++i) {
7633650df50SLei Zhang     auto name = regionsDag->getArgNameStr(i);
7643650df50SLei Zhang     auto *regionInit = dyn_cast<DefInit>(regionsDag->getArg(i));
7653650df50SLei Zhang     if (!regionInit) {
7663650df50SLei Zhang       PrintFatalError(def.getLoc(),
7673650df50SLei Zhang                       Twine("undefined kind for region #") + Twine(i));
7683650df50SLei Zhang     }
7690359b86dSRiver Riddle     Region region(regionInit->getDef());
7700359b86dSRiver Riddle     if (region.isVariadic()) {
7710359b86dSRiver Riddle       // Only support variadic regions if it is the last one for now.
7720359b86dSRiver Riddle       if (i != e - 1)
7730359b86dSRiver Riddle         PrintFatalError(def.getLoc(), "only the last region can be variadic");
7740359b86dSRiver Riddle       if (name.empty())
7750359b86dSRiver Riddle         PrintFatalError(def.getLoc(), "variadic regions must be named");
7760359b86dSRiver Riddle     }
7770359b86dSRiver Riddle 
7780359b86dSRiver Riddle     regions.push_back({name, region});
7793650df50SLei Zhang   }
780796ca609SLei Zhang 
78120741773SRiver Riddle   // Populate the builders.
782659192b1SRahul Joshi   auto *builderList = dyn_cast_or_null<ListInit>(def.getValueInit("builders"));
78320741773SRiver Riddle   if (builderList && !builderList->empty()) {
784659192b1SRahul Joshi     for (const Init *init : builderList->getValues())
785659192b1SRahul Joshi       builders.emplace_back(cast<DefInit>(init)->getDef(), def.getLoc());
78620741773SRiver Riddle   } else if (skipDefaultBuilders()) {
78720741773SRiver Riddle     PrintFatalError(
78820741773SRiver Riddle         def.getLoc(),
78920741773SRiver Riddle         "default builders are skipped and no custom builders provided");
79020741773SRiver Riddle   }
79120741773SRiver Riddle 
792796ca609SLei Zhang   LLVM_DEBUG(print(llvm::dbgs()));
7932a463c36SJacques Pienaar }
7948f249438SJacques Pienaar 
7951b60f0d7SJeff Niu const InferredResultType &Operator::getInferredResultType(int index) const {
79631f40f60SJacques Pienaar   assert(allResultTypesKnown());
79731f40f60SJacques Pienaar   return resultTypeMapping[index];
79831f40f60SJacques Pienaar }
79931f40f60SJacques Pienaar 
8006842ec42SRiver Riddle ArrayRef<SMLoc> Operator::getLoc() const { return def.getLoc(); }
8012927297aSSmit Hinsu 
80212d16de5SRahul Joshi bool Operator::hasDescription() const {
803e4906862SAlex Rice   return !getDescription().trim().empty();
804a280e399SJacques Pienaar }
805a280e399SJacques Pienaar 
80612d16de5SRahul Joshi StringRef Operator::getDescription() const {
807a280e399SJacques Pienaar   return def.getValueAsString("description");
808a280e399SJacques Pienaar }
809a280e399SJacques Pienaar 
810e4906862SAlex Rice bool Operator::hasSummary() const { return !getSummary().trim().empty(); }
811a280e399SJacques Pienaar 
81212d16de5SRahul Joshi StringRef Operator::getSummary() const {
813a280e399SJacques Pienaar   return def.getValueAsString("summary");
814a280e399SJacques Pienaar }
815796ca609SLei Zhang 
81612d16de5SRahul Joshi bool Operator::hasAssemblyFormat() const {
8171a083f02SRiver Riddle   auto *valueInit = def.getValueInit("assemblyFormat");
818659192b1SRahul Joshi   return isa<StringInit>(valueInit);
8191a083f02SRiver Riddle }
8201a083f02SRiver Riddle 
82112d16de5SRahul Joshi StringRef Operator::getAssemblyFormat() const {
822659192b1SRahul Joshi   return TypeSwitch<const Init *, StringRef>(def.getValueInit("assemblyFormat"))
823659192b1SRahul Joshi       .Case<StringInit>([&](auto *init) { return init->getValue(); });
8241a083f02SRiver Riddle }
8251a083f02SRiver Riddle 
82612d16de5SRahul Joshi void Operator::print(llvm::raw_ostream &os) const {
827796ca609SLei Zhang   os << "op '" << getOperationName() << "'\n";
828796ca609SLei Zhang   for (Argument arg : arguments) {
82968f58812STres Popp     if (auto *attr = llvm::dyn_cast_if_present<NamedAttribute *>(arg))
830796ca609SLei Zhang       os << "[attribute] " << attr->name << '\n';
831796ca609SLei Zhang     else
83226d513d1SKazu Hirata       os << "[operand] " << cast<NamedTypeConstraint *>(arg)->name << '\n';
833796ca609SLei Zhang   }
834796ca609SLei Zhang }
83520dca522SRiver Riddle 
836659192b1SRahul Joshi auto Operator::VariableDecoratorIterator::unwrap(const Init *init)
83720dca522SRiver Riddle     -> VariableDecorator {
838659192b1SRahul Joshi   return VariableDecorator(cast<DefInit>(init)->getDef());
83920dca522SRiver Riddle }
84071b9d89dSJacques Pienaar 
84112d16de5SRahul Joshi auto Operator::getArgToOperandOrAttribute(int index) const
84271b9d89dSJacques Pienaar     -> OperandOrAttribute {
84371b9d89dSJacques Pienaar   return attrOrOperandMapping[index];
84471b9d89dSJacques Pienaar }
8456a994233SJacques Pienaar 
846b74192b7SRiver Riddle std::string Operator::getGetterName(StringRef name) const {
847b74192b7SRiver Riddle   return "get" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);
8486a994233SJacques Pienaar }
8496a994233SJacques Pienaar 
850b74192b7SRiver Riddle std::string Operator::getSetterName(StringRef name) const {
851b74192b7SRiver Riddle   return "set" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);
8526a994233SJacques Pienaar }
85315511c2eSJeff Niu 
85415511c2eSJeff Niu std::string Operator::getRemoverName(StringRef name) const {
85515511c2eSJeff Niu   return "remove" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);
85615511c2eSJeff Niu }
857bbfa7ef1SMarkus Böck 
858bbfa7ef1SMarkus Böck bool Operator::hasFolder() const { return def.getValueAsBit("hasFolder"); }
859cf0e8dcaSMatteo Franciolini 
860cf0e8dcaSMatteo Franciolini bool Operator::useCustomPropertiesEncoding() const {
861cf0e8dcaSMatteo Franciolini   return def.getValueAsBit("useCustomPropertiesEncoding");
862cf0e8dcaSMatteo Franciolini }
863