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