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-exception
6 //
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/Predicate.h"
15 #include "mlir/TableGen/Trait.h"
16 #include "mlir/TableGen/Type.h"
17 #include "llvm/ADT/EquivalenceClasses.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 
31 using namespace mlir;
32 using namespace mlir::tblgen;
33 
34 using llvm::DagInit;
35 using llvm::DefInit;
36 using llvm::Record;
37 
38 Operator::Operator(const llvm::Record &def)
39     : dialect(def.getValueAsDef("opDialect")), def(def) {
40   // The first `_` in the op's TableGen def name is treated as separating the
41   // dialect prefix and the op class name. The dialect prefix will be ignored if
42   // not empty. Otherwise, if def name starts with a `_`, the `_` is considered
43   // as part of the class name.
44   StringRef prefix;
45   std::tie(prefix, cppClassName) = def.getName().split('_');
46   if (prefix.empty()) {
47     // Class name with a leading underscore and without dialect prefix
48     cppClassName = def.getName();
49   } else if (cppClassName.empty()) {
50     // Class name without dialect prefix
51     cppClassName = prefix;
52   }
53 
54   cppNamespace = def.getValueAsString("cppNamespace");
55 
56   populateOpStructure();
57   assertInvariants();
58 }
59 
60 std::string Operator::getOperationName() const {
61   auto prefix = dialect.getName();
62   auto opName = def.getValueAsString("opName");
63   if (prefix.empty())
64     return std::string(opName);
65   return std::string(llvm::formatv("{0}.{1}", prefix, opName));
66 }
67 
68 std::string Operator::getAdaptorName() const {
69   return std::string(llvm::formatv("{0}Adaptor", getCppClassName()));
70 }
71 
72 void Operator::assertInvariants() const {
73   // Check that the name of arguments/results/regions/successors don't overlap.
74   DenseMap<StringRef, StringRef> existingNames;
75   auto checkName = [&](StringRef name, StringRef entity) {
76     if (name.empty())
77       return;
78     auto insertion = existingNames.insert({name, entity});
79     if (insertion.second)
80       return;
81     if (entity == insertion.first->second)
82       PrintFatalError(getLoc(), "op has a conflict with two " + entity +
83                                     " having the same name '" + name + "'");
84     PrintFatalError(getLoc(), "op has a conflict with " +
85                                   insertion.first->second + " and " + entity +
86                                   " both having an entry with the name '" +
87                                   name + "'");
88   };
89   // Check operands amongst themselves.
90   for (int i : llvm::seq<int>(0, getNumOperands()))
91     checkName(getOperand(i).name, "operands");
92 
93   // Check results amongst themselves and against operands.
94   for (int i : llvm::seq<int>(0, getNumResults()))
95     checkName(getResult(i).name, "results");
96 
97   // Check regions amongst themselves and against operands and results.
98   for (int i : llvm::seq<int>(0, getNumRegions()))
99     checkName(getRegion(i).name, "regions");
100 
101   // Check successors amongst themselves and against operands, results, and
102   // regions.
103   for (int i : llvm::seq<int>(0, getNumSuccessors()))
104     checkName(getSuccessor(i).name, "successors");
105 }
106 
107 StringRef Operator::getDialectName() const { return dialect.getName(); }
108 
109 StringRef Operator::getCppClassName() const { return cppClassName; }
110 
111 std::string Operator::getQualCppClassName() const {
112   if (cppNamespace.empty())
113     return std::string(cppClassName);
114   return std::string(llvm::formatv("{0}::{1}", cppNamespace, cppClassName));
115 }
116 
117 StringRef Operator::getCppNamespace() const { return cppNamespace; }
118 
119 int Operator::getNumResults() const {
120   DagInit *results = def.getValueAsDag("results");
121   return results->getNumArgs();
122 }
123 
124 StringRef Operator::getExtraClassDeclaration() const {
125   constexpr auto attr = "extraClassDeclaration";
126   if (def.isValueUnset(attr))
127     return {};
128   return def.getValueAsString(attr);
129 }
130 
131 StringRef Operator::getExtraClassDefinition() const {
132   constexpr auto attr = "extraClassDefinition";
133   if (def.isValueUnset(attr))
134     return {};
135   return def.getValueAsString(attr);
136 }
137 
138 const llvm::Record &Operator::getDef() const { return def; }
139 
140 bool Operator::skipDefaultBuilders() const {
141   return def.getValueAsBit("skipDefaultBuilders");
142 }
143 
144 auto Operator::result_begin() -> value_iterator { return results.begin(); }
145 
146 auto Operator::result_end() -> value_iterator { return results.end(); }
147 
148 auto Operator::getResults() -> value_range {
149   return {result_begin(), result_end()};
150 }
151 
152 TypeConstraint Operator::getResultTypeConstraint(int index) const {
153   DagInit *results = def.getValueAsDag("results");
154   return TypeConstraint(cast<DefInit>(results->getArg(index)));
155 }
156 
157 StringRef Operator::getResultName(int index) const {
158   DagInit *results = def.getValueAsDag("results");
159   return results->getArgNameStr(index);
160 }
161 
162 auto Operator::getResultDecorators(int index) const -> var_decorator_range {
163   Record *result =
164       cast<DefInit>(def.getValueAsDag("results")->getArg(index))->getDef();
165   if (!result->isSubClassOf("OpVariable"))
166     return var_decorator_range(nullptr, nullptr);
167   return *result->getValueAsListInit("decorators");
168 }
169 
170 unsigned Operator::getNumVariableLengthResults() const {
171   return llvm::count_if(results, [](const NamedTypeConstraint &c) {
172     return c.constraint.isVariableLength();
173   });
174 }
175 
176 unsigned Operator::getNumVariableLengthOperands() const {
177   return llvm::count_if(operands, [](const NamedTypeConstraint &c) {
178     return c.constraint.isVariableLength();
179   });
180 }
181 
182 bool Operator::hasSingleVariadicArg() const {
183   return getNumArgs() == 1 && getArg(0).is<NamedTypeConstraint *>() &&
184          getOperand(0).isVariadic();
185 }
186 
187 Operator::arg_iterator Operator::arg_begin() const { return arguments.begin(); }
188 
189 Operator::arg_iterator Operator::arg_end() const { return arguments.end(); }
190 
191 Operator::arg_range Operator::getArgs() const {
192   return {arg_begin(), arg_end()};
193 }
194 
195 StringRef Operator::getArgName(int index) const {
196   DagInit *argumentValues = def.getValueAsDag("arguments");
197   return argumentValues->getArgNameStr(index);
198 }
199 
200 auto Operator::getArgDecorators(int index) const -> var_decorator_range {
201   Record *arg =
202       cast<DefInit>(def.getValueAsDag("arguments")->getArg(index))->getDef();
203   if (!arg->isSubClassOf("OpVariable"))
204     return var_decorator_range(nullptr, nullptr);
205   return *arg->getValueAsListInit("decorators");
206 }
207 
208 const Trait *Operator::getTrait(StringRef trait) const {
209   for (const auto &t : traits) {
210     if (const auto *traitDef = dyn_cast<NativeTrait>(&t)) {
211       if (traitDef->getFullyQualifiedTraitName() == trait)
212         return traitDef;
213     } else if (const auto *traitDef = dyn_cast<InternalTrait>(&t)) {
214       if (traitDef->getFullyQualifiedTraitName() == trait)
215         return traitDef;
216     } else if (const auto *traitDef = dyn_cast<InterfaceTrait>(&t)) {
217       if (traitDef->getFullyQualifiedTraitName() == trait)
218         return traitDef;
219     }
220   }
221   return nullptr;
222 }
223 
224 auto Operator::region_begin() const -> const_region_iterator {
225   return regions.begin();
226 }
227 auto Operator::region_end() const -> const_region_iterator {
228   return regions.end();
229 }
230 auto Operator::getRegions() const
231     -> llvm::iterator_range<const_region_iterator> {
232   return {region_begin(), region_end()};
233 }
234 
235 unsigned Operator::getNumRegions() const { return regions.size(); }
236 
237 const NamedRegion &Operator::getRegion(unsigned index) const {
238   return regions[index];
239 }
240 
241 unsigned Operator::getNumVariadicRegions() const {
242   return llvm::count_if(regions,
243                         [](const NamedRegion &c) { return c.isVariadic(); });
244 }
245 
246 auto Operator::successor_begin() const -> const_successor_iterator {
247   return successors.begin();
248 }
249 auto Operator::successor_end() const -> const_successor_iterator {
250   return successors.end();
251 }
252 auto Operator::getSuccessors() const
253     -> llvm::iterator_range<const_successor_iterator> {
254   return {successor_begin(), successor_end()};
255 }
256 
257 unsigned Operator::getNumSuccessors() const { return successors.size(); }
258 
259 const NamedSuccessor &Operator::getSuccessor(unsigned index) const {
260   return successors[index];
261 }
262 
263 unsigned Operator::getNumVariadicSuccessors() const {
264   return llvm::count_if(successors,
265                         [](const NamedSuccessor &c) { return c.isVariadic(); });
266 }
267 
268 auto Operator::trait_begin() const -> const_trait_iterator {
269   return traits.begin();
270 }
271 auto Operator::trait_end() const -> const_trait_iterator {
272   return traits.end();
273 }
274 auto Operator::getTraits() const -> llvm::iterator_range<const_trait_iterator> {
275   return {trait_begin(), trait_end()};
276 }
277 
278 auto Operator::attribute_begin() const -> attribute_iterator {
279   return attributes.begin();
280 }
281 auto Operator::attribute_end() const -> attribute_iterator {
282   return attributes.end();
283 }
284 auto Operator::getAttributes() const
285     -> llvm::iterator_range<attribute_iterator> {
286   return {attribute_begin(), attribute_end()};
287 }
288 
289 auto Operator::operand_begin() -> value_iterator { return operands.begin(); }
290 auto Operator::operand_end() -> value_iterator { return operands.end(); }
291 auto Operator::getOperands() -> value_range {
292   return {operand_begin(), operand_end()};
293 }
294 
295 auto Operator::getArg(int index) const -> Argument { return arguments[index]; }
296 
297 // Mapping from result index to combined argument and result index. Arguments
298 // are indexed to match getArg index, while the result indexes are mapped to
299 // avoid overlap.
300 static int resultIndex(int i) { return -1 - i; }
301 
302 bool Operator::isVariadic() const {
303   return any_of(llvm::concat<const NamedTypeConstraint>(operands, results),
304                 [](const NamedTypeConstraint &op) { return op.isVariadic(); });
305 }
306 
307 void Operator::populateTypeInferenceInfo(
308     const llvm::StringMap<int> &argumentsAndResultsIndex) {
309   // If the type inference op interface is not registered, then do not attempt
310   // to determine if the result types an be inferred.
311   auto &recordKeeper = def.getRecords();
312   auto *inferTrait = recordKeeper.getDef(inferTypeOpInterface);
313   allResultsHaveKnownTypes = false;
314   if (!inferTrait)
315     return;
316 
317   // If there are no results, the skip this else the build method generated
318   // overlaps with another autogenerated builder.
319   if (getNumResults() == 0)
320     return;
321 
322   // Skip for ops with variadic operands/results.
323   // TODO: This can be relaxed.
324   if (isVariadic())
325     return;
326 
327   // Skip cases currently being custom generated.
328   // TODO: Remove special cases.
329   if (getTrait("::mlir::OpTrait::SameOperandsAndResultType"))
330     return;
331 
332   // We create equivalence classes of argument/result types where arguments
333   // and results are mapped into the same index space and indices corresponding
334   // to the same type are in the same equivalence class.
335   llvm::EquivalenceClasses<int> ecs;
336   resultTypeMapping.resize(getNumResults());
337   // Captures the argument whose type matches a given result type. Preference
338   // towards capturing operands first before attributes.
339   auto captureMapping = [&](int i) {
340     bool found = false;
341     ecs.insert(resultIndex(i));
342     auto mi = ecs.findLeader(resultIndex(i));
343     for (auto me = ecs.member_end(); mi != me; ++mi) {
344       if (*mi < 0) {
345         auto tc = getResultTypeConstraint(i);
346         if (tc.getBuilderCall().hasValue()) {
347           resultTypeMapping[i].emplace_back(tc);
348           found = true;
349         }
350         continue;
351       }
352 
353       if (getArg(*mi).is<NamedAttribute *>()) {
354         // TODO: Handle attributes.
355         continue;
356       }
357       resultTypeMapping[i].emplace_back(*mi);
358       found = true;
359     }
360     return found;
361   };
362 
363   for (const Trait &trait : traits) {
364     const llvm::Record &def = trait.getDef();
365     // If the infer type op interface was manually added, then treat it as
366     // intention that the op needs special handling.
367     // TODO: Reconsider whether to always generate, this is more conservative
368     // and keeps existing behavior so starting that way for now.
369     if (def.isSubClassOf(
370             llvm::formatv("{0}::Trait", inferTypeOpInterface).str()))
371       return;
372     if (const auto *traitDef = dyn_cast<InterfaceTrait>(&trait))
373       if (&traitDef->getDef() == inferTrait)
374         return;
375 
376     if (!def.isSubClassOf("AllTypesMatch"))
377       continue;
378 
379     auto values = def.getValueAsListOfStrings("values");
380     auto root = argumentsAndResultsIndex.lookup(values.front());
381     for (StringRef str : values)
382       ecs.unionSets(argumentsAndResultsIndex.lookup(str), root);
383   }
384 
385   // Verifies that all output types have a corresponding known input type
386   // and chooses matching operand or attribute (in that order) that
387   // matches it.
388   allResultsHaveKnownTypes =
389       all_of(llvm::seq<int>(0, getNumResults()), captureMapping);
390 
391   // If the types could be computed, then add type inference trait.
392   if (allResultsHaveKnownTypes)
393     traits.push_back(Trait::create(inferTrait->getDefInit()));
394 }
395 
396 void Operator::populateOpStructure() {
397   auto &recordKeeper = def.getRecords();
398   auto *typeConstraintClass = recordKeeper.getClass("TypeConstraint");
399   auto *attrClass = recordKeeper.getClass("Attr");
400   auto *derivedAttrClass = recordKeeper.getClass("DerivedAttr");
401   auto *opVarClass = recordKeeper.getClass("OpVariable");
402   numNativeAttributes = 0;
403 
404   DagInit *argumentValues = def.getValueAsDag("arguments");
405   unsigned numArgs = argumentValues->getNumArgs();
406 
407   // Mapping from name of to argument or result index. Arguments are indexed
408   // to match getArg index, while the results are negatively indexed.
409   llvm::StringMap<int> argumentsAndResultsIndex;
410 
411   // Handle operands and native attributes.
412   for (unsigned i = 0; i != numArgs; ++i) {
413     auto *arg = argumentValues->getArg(i);
414     auto givenName = argumentValues->getArgNameStr(i);
415     auto *argDefInit = dyn_cast<DefInit>(arg);
416     if (!argDefInit)
417       PrintFatalError(def.getLoc(),
418                       Twine("undefined type for argument #") + Twine(i));
419     Record *argDef = argDefInit->getDef();
420     if (argDef->isSubClassOf(opVarClass))
421       argDef = argDef->getValueAsDef("constraint");
422 
423     if (argDef->isSubClassOf(typeConstraintClass)) {
424       operands.push_back(
425           NamedTypeConstraint{givenName, TypeConstraint(argDef)});
426     } else if (argDef->isSubClassOf(attrClass)) {
427       if (givenName.empty())
428         PrintFatalError(argDef->getLoc(), "attributes must be named");
429       if (argDef->isSubClassOf(derivedAttrClass))
430         PrintFatalError(argDef->getLoc(),
431                         "derived attributes not allowed in argument list");
432       attributes.push_back({givenName, Attribute(argDef)});
433       ++numNativeAttributes;
434     } else {
435       PrintFatalError(def.getLoc(), "unexpected def type; only defs deriving "
436                                     "from TypeConstraint or Attr are allowed");
437     }
438     if (!givenName.empty())
439       argumentsAndResultsIndex[givenName] = i;
440   }
441 
442   // Handle derived attributes.
443   for (const auto &val : def.getValues()) {
444     if (auto *record = dyn_cast<llvm::RecordRecTy>(val.getType())) {
445       if (!record->isSubClassOf(attrClass))
446         continue;
447       if (!record->isSubClassOf(derivedAttrClass))
448         PrintFatalError(def.getLoc(),
449                         "unexpected Attr where only DerivedAttr is allowed");
450 
451       if (record->getClasses().size() != 1) {
452         PrintFatalError(
453             def.getLoc(),
454             "unsupported attribute modelling, only single class expected");
455       }
456       attributes.push_back(
457           {cast<llvm::StringInit>(val.getNameInit())->getValue(),
458            Attribute(cast<DefInit>(val.getValue()))});
459     }
460   }
461 
462   // Populate `arguments`. This must happen after we've finalized `operands` and
463   // `attributes` because we will put their elements' pointers in `arguments`.
464   // SmallVector may perform re-allocation under the hood when adding new
465   // elements.
466   int operandIndex = 0, attrIndex = 0;
467   for (unsigned i = 0; i != numArgs; ++i) {
468     Record *argDef = dyn_cast<DefInit>(argumentValues->getArg(i))->getDef();
469     if (argDef->isSubClassOf(opVarClass))
470       argDef = argDef->getValueAsDef("constraint");
471 
472     if (argDef->isSubClassOf(typeConstraintClass)) {
473       attrOrOperandMapping.push_back(
474           {OperandOrAttribute::Kind::Operand, operandIndex});
475       arguments.emplace_back(&operands[operandIndex++]);
476     } else {
477       assert(argDef->isSubClassOf(attrClass));
478       attrOrOperandMapping.push_back(
479           {OperandOrAttribute::Kind::Attribute, attrIndex});
480       arguments.emplace_back(&attributes[attrIndex++]);
481     }
482   }
483 
484   auto *resultsDag = def.getValueAsDag("results");
485   auto *outsOp = dyn_cast<DefInit>(resultsDag->getOperator());
486   if (!outsOp || outsOp->getDef()->getName() != "outs") {
487     PrintFatalError(def.getLoc(), "'results' must have 'outs' directive");
488   }
489 
490   // Handle results.
491   for (unsigned i = 0, e = resultsDag->getNumArgs(); i < e; ++i) {
492     auto name = resultsDag->getArgNameStr(i);
493     auto *resultInit = dyn_cast<DefInit>(resultsDag->getArg(i));
494     if (!resultInit) {
495       PrintFatalError(def.getLoc(),
496                       Twine("undefined type for result #") + Twine(i));
497     }
498     auto *resultDef = resultInit->getDef();
499     if (resultDef->isSubClassOf(opVarClass))
500       resultDef = resultDef->getValueAsDef("constraint");
501     results.push_back({name, TypeConstraint(resultDef)});
502     if (!name.empty())
503       argumentsAndResultsIndex[name] = resultIndex(i);
504 
505     // We currently only support VariadicOfVariadic operands.
506     if (results.back().constraint.isVariadicOfVariadic()) {
507       PrintFatalError(
508           def.getLoc(),
509           "'VariadicOfVariadic' results are currently not supported");
510     }
511   }
512 
513   // Handle successors
514   auto *successorsDag = def.getValueAsDag("successors");
515   auto *successorsOp = dyn_cast<DefInit>(successorsDag->getOperator());
516   if (!successorsOp || successorsOp->getDef()->getName() != "successor") {
517     PrintFatalError(def.getLoc(),
518                     "'successors' must have 'successor' directive");
519   }
520 
521   for (unsigned i = 0, e = successorsDag->getNumArgs(); i < e; ++i) {
522     auto name = successorsDag->getArgNameStr(i);
523     auto *successorInit = dyn_cast<DefInit>(successorsDag->getArg(i));
524     if (!successorInit) {
525       PrintFatalError(def.getLoc(),
526                       Twine("undefined kind for successor #") + Twine(i));
527     }
528     Successor successor(successorInit->getDef());
529 
530     // Only support variadic successors if it is the last one for now.
531     if (i != e - 1 && successor.isVariadic())
532       PrintFatalError(def.getLoc(), "only the last successor can be variadic");
533     successors.push_back({name, successor});
534   }
535 
536   // Create list of traits, skipping over duplicates: appending to lists in
537   // tablegen is easy, making them unique less so, so dedupe here.
538   if (auto *traitList = def.getValueAsListInit("traits")) {
539     // This is uniquing based on pointers of the trait.
540     SmallPtrSet<const llvm::Init *, 32> traitSet;
541     traits.reserve(traitSet.size());
542 
543     std::function<void(llvm::ListInit *)> insert;
544     insert = [&](llvm::ListInit *traitList) {
545       for (auto *traitInit : *traitList) {
546         auto *def = cast<DefInit>(traitInit)->getDef();
547         if (def->isSubClassOf("OpTraitList")) {
548           insert(def->getValueAsListInit("traits"));
549           continue;
550         }
551         // Keep traits in the same order while skipping over duplicates.
552         if (traitSet.insert(traitInit).second)
553           traits.push_back(Trait::create(traitInit));
554       }
555     };
556     insert(traitList);
557   }
558 
559   populateTypeInferenceInfo(argumentsAndResultsIndex);
560 
561   // Handle regions
562   auto *regionsDag = def.getValueAsDag("regions");
563   auto *regionsOp = dyn_cast<DefInit>(regionsDag->getOperator());
564   if (!regionsOp || regionsOp->getDef()->getName() != "region") {
565     PrintFatalError(def.getLoc(), "'regions' must have 'region' directive");
566   }
567 
568   for (unsigned i = 0, e = regionsDag->getNumArgs(); i < e; ++i) {
569     auto name = regionsDag->getArgNameStr(i);
570     auto *regionInit = dyn_cast<DefInit>(regionsDag->getArg(i));
571     if (!regionInit) {
572       PrintFatalError(def.getLoc(),
573                       Twine("undefined kind for region #") + Twine(i));
574     }
575     Region region(regionInit->getDef());
576     if (region.isVariadic()) {
577       // Only support variadic regions if it is the last one for now.
578       if (i != e - 1)
579         PrintFatalError(def.getLoc(), "only the last region can be variadic");
580       if (name.empty())
581         PrintFatalError(def.getLoc(), "variadic regions must be named");
582     }
583 
584     regions.push_back({name, region});
585   }
586 
587   // Populate the builders.
588   auto *builderList =
589       dyn_cast_or_null<llvm::ListInit>(def.getValueInit("builders"));
590   if (builderList && !builderList->empty()) {
591     for (llvm::Init *init : builderList->getValues())
592       builders.emplace_back(cast<llvm::DefInit>(init)->getDef(), def.getLoc());
593   } else if (skipDefaultBuilders()) {
594     PrintFatalError(
595         def.getLoc(),
596         "default builders are skipped and no custom builders provided");
597   }
598 
599   LLVM_DEBUG(print(llvm::dbgs()));
600 }
601 
602 auto Operator::getSameTypeAsResult(int index) const -> ArrayRef<ArgOrType> {
603   assert(allResultTypesKnown());
604   return resultTypeMapping[index];
605 }
606 
607 ArrayRef<llvm::SMLoc> Operator::getLoc() const { return def.getLoc(); }
608 
609 bool Operator::hasDescription() const {
610   return def.getValue("description") != nullptr;
611 }
612 
613 StringRef Operator::getDescription() const {
614   return def.getValueAsString("description");
615 }
616 
617 bool Operator::hasSummary() const { return def.getValue("summary") != nullptr; }
618 
619 StringRef Operator::getSummary() const {
620   return def.getValueAsString("summary");
621 }
622 
623 bool Operator::hasAssemblyFormat() const {
624   auto *valueInit = def.getValueInit("assemblyFormat");
625   return isa<llvm::StringInit>(valueInit);
626 }
627 
628 StringRef Operator::getAssemblyFormat() const {
629   return TypeSwitch<llvm::Init *, StringRef>(def.getValueInit("assemblyFormat"))
630       .Case<llvm::StringInit>([&](auto *init) { return init->getValue(); });
631 }
632 
633 void Operator::print(llvm::raw_ostream &os) const {
634   os << "op '" << getOperationName() << "'\n";
635   for (Argument arg : arguments) {
636     if (auto *attr = arg.dyn_cast<NamedAttribute *>())
637       os << "[attribute] " << attr->name << '\n';
638     else
639       os << "[operand] " << arg.get<NamedTypeConstraint *>()->name << '\n';
640   }
641 }
642 
643 auto Operator::VariableDecoratorIterator::unwrap(llvm::Init *init)
644     -> VariableDecorator {
645   return VariableDecorator(cast<llvm::DefInit>(init)->getDef());
646 }
647 
648 auto Operator::getArgToOperandOrAttribute(int index) const
649     -> OperandOrAttribute {
650   return attrOrOperandMapping[index];
651 }
652 
653 // Helper to return the names for accessor.
654 static SmallVector<std::string, 2>
655 getGetterOrSetterNames(bool isGetter, const Operator &op, StringRef name) {
656   Dialect::EmitPrefix prefixType = op.getDialect().getEmitAccessorPrefix();
657   std::string prefix;
658   if (prefixType != Dialect::EmitPrefix::Raw)
659     prefix = isGetter ? "get" : "set";
660 
661   SmallVector<std::string, 2> names;
662   bool rawToo = prefixType == Dialect::EmitPrefix::Both;
663 
664   // Whether to skip generating prefixed form for argument. This just does some
665   // basic checks.
666   //
667   // There are a little bit more invasive checks possible for cases where not
668   // all ops have the trait that would cause overlap. For many cases here,
669   // renaming would be better (e.g., we can only guard in limited manner against
670   // methods from traits and interfaces here, so avoiding these in op definition
671   // is safer).
672   auto skip = [&](StringRef newName) {
673     bool shouldSkip = newName == "getAttributeNames" ||
674                       newName == "getAttributes" || newName == "getOperation" ||
675                       newName == "getType";
676     if (newName == "getOperands") {
677       // To reduce noise, skip generating the prefixed form and the warning if
678       // $operands correspond to single variadic argument.
679       if (op.getNumOperands() == 1 && op.getNumVariableLengthOperands() == 1)
680         return true;
681       shouldSkip = true;
682     }
683     if (newName == "getRegions") {
684       if (op.getNumRegions() == 1 && op.getNumVariadicRegions() == 1)
685         return true;
686       shouldSkip = true;
687     }
688     if (!shouldSkip)
689       return false;
690 
691     // This note could be avoided where the final function generated would
692     // have been identical. But preferably in the op definition avoiding using
693     // the generic name and then getting a more specialize type is better.
694     PrintNote(op.getLoc(),
695               "Skipping generation of prefixed accessor `" + newName +
696                   "` as it overlaps with default one; generating raw form (`" +
697                   name + "`) still");
698     return true;
699   };
700 
701   if (!prefix.empty()) {
702     names.push_back(
703         prefix + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true));
704     // Skip cases which would overlap with default ones for now.
705     if (skip(names.back())) {
706       rawToo = true;
707       names.clear();
708     } else if (rawToo) {
709       LLVM_DEBUG(llvm::errs() << "WITH_GETTER(\"" << op.getQualCppClassName()
710                               << "::" << name << "\")\n"
711                               << "WITH_GETTER(\"" << op.getQualCppClassName()
712                               << "Adaptor::" << name << "\")\n";);
713     }
714   }
715 
716   if (prefix.empty() || rawToo)
717     names.push_back(name.str());
718   return names;
719 }
720 
721 SmallVector<std::string, 2> Operator::getGetterNames(StringRef name) const {
722   return getGetterOrSetterNames(/*isGetter=*/true, *this, name);
723 }
724 
725 SmallVector<std::string, 2> Operator::getSetterNames(StringRef name) const {
726   return getGetterOrSetterNames(/*isGetter=*/false, *this, name);
727 }
728