MLIR 24.0.0git
Operator.cpp
Go to the documentation of this file.
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
16#include "mlir/TableGen/Trait.h"
17#include "mlir/TableGen/Type.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/ADT/Sequence.h"
20#include "llvm/ADT/SmallPtrSet.h"
21#include "llvm/ADT/StringExtras.h"
22#include "llvm/ADT/TypeSwitch.h"
23#include "llvm/Support/Debug.h"
24#include "llvm/Support/ErrorHandling.h"
25#include "llvm/Support/FormatVariadic.h"
26#include "llvm/TableGen/Error.h"
27#include "llvm/TableGen/Record.h"
28
29#define DEBUG_TYPE "mlir-tblgen-operator"
30
31using namespace mlir;
32using namespace mlir::tblgen;
33
34using llvm::DagInit;
35using llvm::DefInit;
36using llvm::Init;
37using llvm::ListInit;
38using llvm::Record;
39using llvm::StringInit;
40
41Operator::Operator(const Record &def)
42 : dialect(def.getValueAsDef("opDialect")), def(def) {
43 // The first `_` in the op's TableGen def name is treated as separating the
44 // dialect prefix and the op class name. The dialect prefix will be ignored if
45 // not empty. Otherwise, if def name starts with a `_`, the `_` is considered
46 // as part of the class name.
47 StringRef prefix;
48 std::tie(prefix, cppClassName) = def.getName().split('_');
49 if (prefix.empty()) {
50 // Class name with a leading underscore and without dialect prefix
51 cppClassName = def.getName();
52 } else if (cppClassName.empty()) {
53 // Class name without dialect prefix
54 cppClassName = prefix;
55 }
56
57 cppNamespace = def.getValueAsString("cppNamespace");
58
59 populateOpStructure();
60 assertInvariants();
61}
62
63std::string Operator::getOperationName() const {
64 auto prefix = dialect.getName();
65 auto opName = def.getValueAsString("opName");
66 if (prefix.empty())
67 return std::string(opName);
68 return std::string(llvm::formatv("{0}.{1}", prefix, opName));
69}
70
71std::string Operator::getAdaptorName() const {
72 return std::string(llvm::formatv("{0}Adaptor", getCppClassName()));
73}
74
76 return std::string(llvm::formatv("{0}GenericAdaptor", getCppClassName()));
77}
78
79/// Assert the invariants of accessors generated for the given name.
80static void assertAccessorInvariants(const Operator &op, StringRef name) {
81 std::string accessorName =
82 convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);
83
84 // Functor used to detect when an accessor will cause an overlap with an
85 // operation API.
86 //
87 // There are a little bit more invasive checks possible for cases where not
88 // all ops have the trait that would cause overlap. For many cases here,
89 // renaming would be better (e.g., we can only guard in limited manner
90 // against methods from traits and interfaces here, so avoiding these in op
91 // definition is safer).
92 auto nameOverlapsWithOpAPI = [&](StringRef newName) {
93 if (newName == "AttributeNames" || newName == "Attributes" ||
94 newName == "Operation")
95 return true;
96 if (newName == "Operands")
97 return op.getNumOperands() != 1 || op.getNumVariableLengthOperands() != 1;
98 if (newName == "Regions")
99 return op.getNumRegions() != 1 || op.getNumVariadicRegions() != 1;
100 if (newName == "Type")
101 return op.getNumResults() != 1;
102 return false;
103 };
104 if (nameOverlapsWithOpAPI(accessorName)) {
105 // This error could be avoided in situations where the final function is
106 // identical, but preferably the op definition should avoid using generic
107 // names.
108 PrintFatalError(op.getLoc(), "generated accessor for `" + name +
109 "` overlaps with a default one; please "
110 "rename to avoid overlap");
111 }
112}
113
115 // Check that the name of arguments/results/regions/successors don't overlap.
116 DenseMap<StringRef, StringRef> existingNames;
117 auto checkName = [&](StringRef name, StringRef entity) {
118 if (name.empty())
119 return;
120 auto insertion = existingNames.insert({name, entity});
121 if (insertion.second) {
122 // Assert invariants for accessors generated for this name.
123 assertAccessorInvariants(*this, name);
124 return;
125 }
126 if (entity == insertion.first->second)
127 PrintFatalError(getLoc(), "op has a conflict with two " + entity +
128 " having the same name '" + name + "'");
129 PrintFatalError(getLoc(), "op has a conflict with " +
130 insertion.first->second + " and " + entity +
131 " both having an entry with the name '" +
132 name + "'");
133 };
134 // Check operands amongst themselves.
135 for (int i : llvm::seq<int>(0, getNumOperands()))
136 checkName(getOperand(i).name, "operands");
137
138 // Check results amongst themselves and against operands.
139 for (int i : llvm::seq<int>(0, getNumResults()))
140 checkName(getResult(i).name, "results");
141
142 // Check regions amongst themselves and against operands and results.
143 for (int i : llvm::seq<int>(0, getNumRegions()))
144 checkName(getRegion(i).name, "regions");
145
146 // Check successors amongst themselves and against operands, results, and
147 // regions.
148 for (int i : llvm::seq<int>(0, getNumSuccessors()))
149 checkName(getSuccessor(i).name, "successors");
150}
151
152StringRef Operator::getDialectName() const { return dialect.getName(); }
153
154StringRef Operator::getCppClassName() const { return cppClassName; }
155
156std::string Operator::getQualCppClassName() const {
157 if (cppNamespace.empty())
158 return std::string(cppClassName);
159 return std::string(llvm::formatv("{0}::{1}", cppNamespace, cppClassName));
160}
161
162StringRef Operator::getCppNamespace() const { return cppNamespace; }
163
165 const DagInit *results = def.getValueAsDag("results");
166 return results->getNumArgs();
167}
168
170 constexpr auto attr = "extraClassDeclaration";
171 if (def.isValueUnset(attr))
172 return {};
173 return def.getValueAsString(attr);
174}
175
177 constexpr auto attr = "extraClassDefinition";
178 if (def.isValueUnset(attr))
179 return {};
180 return def.getValueAsString(attr);
181}
182
183const Record &Operator::getDef() const { return def; }
184
186 return def.getValueAsBit("skipDefaultBuilders");
187}
188
190 return def.getValueAsBit("hasCustomPropertiesPrinter");
191}
192
194 return results.begin();
195}
196
198 return results.end();
199}
200
202 return {result_begin(), result_end()};
203}
204
206 const DagInit *results = def.getValueAsDag("results");
207 return TypeConstraint(cast<DefInit>(results->getArg(index)));
208}
209
210StringRef Operator::getResultName(int index) const {
211 const DagInit *results = def.getValueAsDag("results");
212 return results->getArgNameStr(index);
213}
214
216 const Record *result =
217 cast<DefInit>(def.getValueAsDag("results")->getArg(index))->getDef();
218 if (!result->isSubClassOf("OpVariable"))
219 return var_decorator_range(nullptr, nullptr);
220 return *result->getValueAsListInit("decorators");
221}
222
224 return llvm::count_if(results, [](const NamedTypeConstraint &c) {
225 return c.constraint.isVariableLength();
226 });
227}
228
230 return llvm::count_if(operands, [](const NamedTypeConstraint &c) {
231 return c.constraint.isVariableLength();
232 });
233}
234
236 return getNumArgs() == 1 && isa<NamedTypeConstraint *>(getArg(0)) &&
238}
239
240Operator::arg_iterator Operator::arg_begin() const { return arguments.begin(); }
241
242Operator::arg_iterator Operator::arg_end() const { return arguments.end(); }
243
245 return {arg_begin(), arg_end()};
246}
247
248StringRef Operator::getArgName(int index) const {
249 const DagInit *argumentValues = def.getValueAsDag("arguments");
250 return argumentValues->getArgNameStr(index);
251}
252
254 const Record *arg =
255 cast<DefInit>(def.getValueAsDag("arguments")->getArg(index))->getDef();
256 if (!arg->isSubClassOf("OpVariable"))
257 return var_decorator_range(nullptr, nullptr);
258 return *arg->getValueAsListInit("decorators");
259}
260
261const Trait *Operator::getTrait(StringRef trait) const {
262 for (const auto &t : traits) {
263 if (const auto *traitDef = dyn_cast<NativeTrait>(&t)) {
264 if (traitDef->getFullyQualifiedTraitName() == trait)
265 return traitDef;
266 } else if (const auto *traitDef = dyn_cast<InternalTrait>(&t)) {
267 if (traitDef->getFullyQualifiedTraitName() == trait)
268 return traitDef;
269 } else if (const auto *traitDef = dyn_cast<InterfaceTrait>(&t)) {
270 if (traitDef->getFullyQualifiedTraitName() == trait)
271 return traitDef;
272 }
273 }
274 return nullptr;
275}
276
278 if (!properties.empty())
279 return true;
280 if (getTrait("::mlir::OpTrait::AttrSizedOperandSegments") ||
281 getTrait("::mlir::OpTrait::AttrSizedResultSegments"))
282 return true;
283 return llvm::any_of(attributes, [](const NamedAttribute &attr) {
284 return !attr.attr.isDerivedAttr();
285 });
286}
287
290 for (const NamedAttribute &attr : attributes)
291 if (!attr.attr.isDerivedAttr())
292 names.push_back(attr.name);
293 for (const NamedProperty &property : properties)
294 names.push_back(property.name);
295 if (getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {
296 names.push_back(operandSegmentAttrName);
297 names.push_back(legacyOperandSegmentAttrName);
298 }
299 if (getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {
300 names.push_back(resultSegmentAttrName);
301 names.push_back(legacyResultSegmentAttrName);
302 }
303 return names;
304}
305
307 return regions.begin();
308}
310 return regions.end();
311}
316
317unsigned Operator::getNumRegions() const { return regions.size(); }
318
319const NamedRegion &Operator::getRegion(unsigned index) const {
320 return regions[index];
321}
322
324 return llvm::count_if(regions,
325 [](const NamedRegion &c) { return c.isVariadic(); });
326}
327
329 return successors.begin();
330}
332 return successors.end();
333}
338
339unsigned Operator::getNumSuccessors() const { return successors.size(); }
340
342 return successors[index];
343}
344
346 return llvm::count_if(successors,
347 [](const NamedSuccessor &c) { return c.isVariadic(); });
348}
349
351 return traits.begin();
352}
354 return traits.end();
355}
359
361 return attributes.begin();
362}
364 return attributes.end();
365}
371 return attributes.begin();
372}
374 return attributes.end();
375}
379
381 return operands.begin();
382}
384 return operands.end();
385}
387 return {operand_begin(), operand_end()};
388}
389
390auto Operator::getArg(int index) const -> Argument { return arguments[index]; }
391
393 return any_of(llvm::concat<const NamedTypeConstraint>(operands, results),
394 [](const NamedTypeConstraint &op) { return op.isVariadic(); });
395}
396
397void Operator::populateTypeInferenceInfo(
398 const llvm::StringMap<int> &argumentsAndResultsIndex) {
399 // If the type inference op interface is not registered, then do not attempt
400 // to determine if the result types an be inferred.
401 auto &recordKeeper = def.getRecords();
402 auto *inferTrait = recordKeeper.getDef(inferTypeOpInterface);
403 allResultsHaveKnownTypes = false;
404 if (!inferTrait)
405 return;
406
407 // If there are no results, the skip this else the build method generated
408 // overlaps with another autogenerated builder.
409 if (getNumResults() == 0)
410 return;
411
412 // Skip ops with variadic or optional results.
414 return;
415
416 // Skip cases currently being custom generated.
417 // TODO: Remove special cases.
418 if (getTrait("::mlir::OpTrait::SameOperandsAndResultType")) {
419 // Check for a non-variable length operand to use as the type anchor.
420 auto *operandI = llvm::find_if(arguments, [](const Argument &arg) {
421 NamedTypeConstraint *operand =
422 llvm::dyn_cast_if_present<NamedTypeConstraint *>(arg);
423 return operand && !operand->isVariableLength();
424 });
425 if (operandI == arguments.end())
426 return;
427
428 // All result types are inferred from the operand type.
429 int operandIdx = operandI - arguments.begin();
430 for (int i = 0; i < getNumResults(); ++i)
431 resultTypeMapping.emplace_back(operandIdx, "$_self");
432
433 allResultsHaveKnownTypes = true;
434 traits.push_back(Trait::create(inferTrait->getDefInit()));
435 return;
436 }
437
438 /// This struct represents a node in this operation's result type inferenece
439 /// graph. Each node has a list of incoming type inference edges `sources`.
440 /// Each edge represents a "source" from which the result type can be
441 /// inferred, either an operand (leaf) or another result (node). When a node
442 /// is known to have a fully-inferred type, `inferred` is set to true.
443 struct ResultTypeInference {
444 /// The list of incoming type inference edges.
446 /// This flag is set to true when the result type is known to be inferrable.
447 bool inferred = false;
448 };
449
450 // This vector represents the type inference graph, with one node for each
451 // operation result. The nth element is the node for the nth result.
452 SmallVector<ResultTypeInference> inference(getNumResults(),
453 llvm::for_overwrite);
454
455 // For all results whose types are buildable, initialize their type inference
456 // nodes with an edge to themselves. Mark those nodes are fully-inferred.
457 for (auto [idx, infer] : llvm::enumerate(inference)) {
458 if (getResult(idx).constraint.getBuilderCall()) {
459 infer.sources.emplace_back(InferredResultType::mapResultIndex(idx),
460 "$_self");
461 infer.inferred = true;
462 }
463 }
464
465 // Use `AllTypesMatch` and `TypesMatchWith` operation traits to build the
466 // result type inference graph.
467 for (const Trait &trait : traits) {
468 const Record &def = trait.getDef();
469
470 // If the infer type op interface was manually added, then treat it as
471 // intention that the op needs special handling.
472 // TODO: Reconsider whether to always generate, this is more conservative
473 // and keeps existing behavior so starting that way for now.
474 if (def.isSubClassOf(
475 llvm::formatv("{0}::Trait", inferTypeOpInterface).str()))
476 return;
477 if (const auto *traitDef = dyn_cast<InterfaceTrait>(&trait))
478 if (&traitDef->getDef() == inferTrait)
479 return;
480
481 // The `TypesMatchWith` trait represents a 1 -> 1 type inference edge with a
482 // type transformer.
483 if (def.isSubClassOf("TypesMatchWith")) {
484 int target = argumentsAndResultsIndex.lookup(def.getValueAsString("rhs"));
485 // Ignore operand type inference.
487 continue;
489 ResultTypeInference &infer = inference[resultIndex];
490 // If the type of the result has already been inferred, do nothing.
491 if (infer.inferred)
492 continue;
493 int sourceIndex =
494 argumentsAndResultsIndex.lookup(def.getValueAsString("lhs"));
495 infer.sources.emplace_back(sourceIndex,
496 def.getValueAsString("transformer").str());
497 // Locally propagate inferredness.
498 infer.inferred =
499 InferredResultType::isArgIndex(sourceIndex) ||
500 inference[InferredResultType::unmapResultIndex(sourceIndex)].inferred;
501 continue;
502 }
503
504 // The `ShapedTypeMatchesElementCountAndTypes` trait represents a 1 -> 1
505 // type inference edge where a shaped type matches element count and types
506 // of variadic elements.
507 if (def.isSubClassOf("ShapedTypeMatchesElementCountAndTypes")) {
508 StringRef shapedArg = def.getValueAsString("shaped");
509 StringRef elementsArg = def.getValueAsString("elements");
510
511 int shapedIndex = argumentsAndResultsIndex.lookup(shapedArg);
512 int elementsIndex = argumentsAndResultsIndex.lookup(elementsArg);
513
514 // Handle result type inference from shaped type to variadic elements.
515 if (InferredResultType::isResultIndex(elementsIndex) &&
516 InferredResultType::isArgIndex(shapedIndex)) {
517 int resultIndex = InferredResultType::unmapResultIndex(elementsIndex);
518 ResultTypeInference &infer = inference[resultIndex];
519 if (!infer.inferred) {
520 infer.sources.emplace_back(
521 shapedIndex,
522 "::llvm::SmallVector<::mlir::Type>(::llvm::cast<::mlir::"
523 "ShapedType>($_self).getNumElements(), "
524 "::llvm::cast<::mlir::ShapedType>($_self).getElementType())");
525 infer.inferred = true;
526 }
527 }
528
529 // Type inference in the opposite direction is not possible as the actual
530 // shaped type can't be inferred from the variadic elements.
531
532 continue;
533 }
534
535 if (!def.isSubClassOf("AllTypesMatch"))
536 continue;
537
538 auto values = def.getValueAsListOfStrings("values");
539 // The `AllTypesMatch` trait represents an N <-> N fanin and fanout. That
540 // is, every result type has an edge from every other type. However, if any
541 // one of the values refers to an operand or a result with a fully-inferred
542 // type, we can infer all other types from that value. Try to find a
543 // fully-inferred type in the list.
544 std::optional<int> fullyInferredIndex;
545 SmallVector<int> resultIndices;
546 for (StringRef name : values) {
547 int index = argumentsAndResultsIndex.lookup(name);
549 resultIndices.push_back(InferredResultType::unmapResultIndex(index));
551 inference[InferredResultType::unmapResultIndex(index)].inferred)
552 fullyInferredIndex = index;
553 }
554 if (fullyInferredIndex) {
555 // Make the fully-inferred type the only source for all results that
556 // aren't already inferred -- a 1 -> N fanout.
557 for (int resultIndex : resultIndices) {
558 ResultTypeInference &infer = inference[resultIndex];
559 if (!infer.inferred) {
560 infer.sources.assign(1, {*fullyInferredIndex, "$_self"});
561 infer.inferred = true;
562 }
563 }
564 } else {
565 // Add an edge between every result and every other type; N <-> N.
566 for (int resultIndex : resultIndices) {
567 for (int otherResultIndex : resultIndices) {
568 if (resultIndex == otherResultIndex)
569 continue;
570 inference[resultIndex].sources.emplace_back(
571 InferredResultType::unmapResultIndex(otherResultIndex), "$_self");
572 }
573 }
574 }
575 }
576
577 // Propagate inferredness until a fixed point.
578 std::vector<ResultTypeInference *> worklist;
579 for (ResultTypeInference &infer : inference)
580 if (!infer.inferred)
581 worklist.push_back(&infer);
582 bool changed;
583 do {
584 changed = false;
585 for (auto cur = worklist.begin(); cur != worklist.end();) {
586 ResultTypeInference &infer = **cur;
587
588 InferredResultType *iter =
589 llvm::find_if(infer.sources, [&](const InferredResultType &source) {
590 assert(InferredResultType::isResultIndex(source.getIndex()));
591 return inference[InferredResultType::unmapResultIndex(
592 source.getIndex())]
593 .inferred;
594 });
595 if (iter == infer.sources.end()) {
596 ++cur;
597 continue;
598 }
599
600 changed = true;
601 infer.inferred = true;
602 // Make this the only source for the result. This breaks any cycles.
603 infer.sources.assign(1, *iter);
604 cur = worklist.erase(cur);
605 }
606 } while (changed);
607
608 allResultsHaveKnownTypes = worklist.empty();
609
610 // If the types could be computed, then add type inference trait.
611 if (allResultsHaveKnownTypes) {
612 traits.push_back(Trait::create(inferTrait->getDefInit()));
613 for (const ResultTypeInference &infer : inference)
614 resultTypeMapping.push_back(infer.sources.front());
615 }
616}
617
618void Operator::populateOpStructure() {
619 auto &recordKeeper = def.getRecords();
620 auto *typeConstraintClass = recordKeeper.getClass("TypeConstraint");
621 auto *attrClass = recordKeeper.getClass("Attr");
622 auto *propertyClass = recordKeeper.getClass("Property");
623 auto *derivedAttrClass = recordKeeper.getClass("DerivedAttr");
624 auto *opVarClass = recordKeeper.getClass("OpVariable");
625 numNativeAttributes = 0;
626
627 const DagInit *argumentValues = def.getValueAsDag("arguments");
628 unsigned numArgs = argumentValues->getNumArgs();
629
630 // Mapping from name of to argument or result index. Arguments are indexed
631 // to match getArg index, while the results are negatively indexed.
632 llvm::StringMap<int> argumentsAndResultsIndex;
633
634 // Handle operands and native attributes.
635 for (unsigned i = 0; i != numArgs; ++i) {
636 auto *arg = argumentValues->getArg(i);
637 auto givenName = argumentValues->getArgNameStr(i);
638 auto *argDefInit = dyn_cast<DefInit>(arg);
639 if (!argDefInit)
640 PrintFatalError(def.getLoc(),
641 Twine("undefined type for argument #") + Twine(i));
642 const Record *argDef = argDefInit->getDef();
643 if (argDef->isSubClassOf(opVarClass))
644 argDef = argDef->getValueAsDef("constraint");
645
646 if (argDef->isSubClassOf(typeConstraintClass)) {
647 operands.push_back(
648 NamedTypeConstraint{givenName, TypeConstraint(argDef)});
649 } else if (argDef->isSubClassOf(attrClass)) {
650 if (givenName.empty())
651 PrintFatalError(argDef->getLoc(), "attributes must be named");
652 if (argDef->isSubClassOf(derivedAttrClass))
653 PrintFatalError(argDef->getLoc(),
654 "derived attributes not allowed in argument list");
655 attributes.push_back({givenName, Attribute(argDef)});
656 ++numNativeAttributes;
657 } else if (argDef->isSubClassOf(propertyClass)) {
658 if (givenName.empty())
659 PrintFatalError(argDef->getLoc(), "properties must be named");
660 properties.push_back({givenName, Property(argDef)});
661 } else {
662 PrintFatalError(def.getLoc(),
663 "unexpected def type; only defs deriving "
664 "from TypeConstraint or Attr or Property are allowed");
665 }
666 if (!givenName.empty())
667 argumentsAndResultsIndex[givenName] = i;
668 }
669
670 // Handle derived attributes.
671 for (const auto &val : def.getValues()) {
672 if (auto *record = dyn_cast<llvm::RecordRecTy>(val.getType())) {
673 if (!record->isSubClassOf(attrClass))
674 continue;
675 if (!record->isSubClassOf(derivedAttrClass))
676 PrintFatalError(def.getLoc(),
677 "unexpected Attr where only DerivedAttr is allowed");
678
679 if (record->getClasses().size() != 1) {
680 PrintFatalError(
681 def.getLoc(),
682 "unsupported attribute modelling, only single class expected");
683 }
684 attributes.push_back({cast<StringInit>(val.getNameInit())->getValue(),
685 Attribute(cast<DefInit>(val.getValue()))});
686 }
687 }
688
689 // Populate `arguments`. This must happen after we've finalized `operands` and
690 // `attributes` because we will put their elements' pointers in `arguments`.
691 // SmallVector may perform re-allocation under the hood when adding new
692 // elements.
693 int operandIndex = 0, attrIndex = 0, propIndex = 0;
694 for (unsigned i = 0; i != numArgs; ++i) {
695 const Record *argDef =
696 dyn_cast<DefInit>(argumentValues->getArg(i))->getDef();
697 if (argDef->isSubClassOf(opVarClass))
698 argDef = argDef->getValueAsDef("constraint");
699
700 if (argDef->isSubClassOf(typeConstraintClass)) {
701 attrPropOrOperandMapping.push_back(
702 {OperandAttrOrProp::Kind::Operand, operandIndex});
703 arguments.emplace_back(&operands[operandIndex++]);
704 } else if (argDef->isSubClassOf(attrClass)) {
705 attrPropOrOperandMapping.push_back(
707 arguments.emplace_back(&attributes[attrIndex++]);
708 } else {
709 assert(argDef->isSubClassOf(propertyClass));
710 attrPropOrOperandMapping.push_back(
712 arguments.emplace_back(&properties[propIndex++]);
713 }
714 }
715
716 auto *resultsDag = def.getValueAsDag("results");
717 auto *outsOp = dyn_cast<DefInit>(resultsDag->getOperator());
718 if (!outsOp || outsOp->getDef()->getName() != "outs") {
719 PrintFatalError(def.getLoc(), "'results' must have 'outs' directive");
720 }
721
722 // Handle results.
723 for (unsigned i = 0, e = resultsDag->getNumArgs(); i < e; ++i) {
724 auto name = resultsDag->getArgNameStr(i);
725 auto *resultInit = dyn_cast<DefInit>(resultsDag->getArg(i));
726 if (!resultInit) {
727 PrintFatalError(def.getLoc(),
728 Twine("undefined type for result #") + Twine(i));
729 }
730 auto *resultDef = resultInit->getDef();
731 if (resultDef->isSubClassOf(opVarClass))
732 resultDef = resultDef->getValueAsDef("constraint");
733 results.push_back({name, TypeConstraint(resultDef)});
734 if (!name.empty())
735 argumentsAndResultsIndex[name] = InferredResultType::mapResultIndex(i);
736
737 // We currently only support VariadicOfVariadic operands.
738 if (results.back().constraint.isVariadicOfVariadic()) {
739 PrintFatalError(
740 def.getLoc(),
741 "'VariadicOfVariadic' results are currently not supported");
742 }
743 }
744
745 // Handle successors
746 auto *successorsDag = def.getValueAsDag("successors");
747 auto *successorsOp = dyn_cast<DefInit>(successorsDag->getOperator());
748 if (!successorsOp || successorsOp->getDef()->getName() != "successor") {
749 PrintFatalError(def.getLoc(),
750 "'successors' must have 'successor' directive");
751 }
752
753 for (unsigned i = 0, e = successorsDag->getNumArgs(); i < e; ++i) {
754 auto name = successorsDag->getArgNameStr(i);
755 auto *successorInit = dyn_cast<DefInit>(successorsDag->getArg(i));
756 if (!successorInit) {
757 PrintFatalError(def.getLoc(),
758 Twine("undefined kind for successor #") + Twine(i));
759 }
760 Successor successor(successorInit->getDef());
761
762 // Only support variadic successors if it is the last one for now.
763 if (i != e - 1 && successor.isVariadic())
764 PrintFatalError(def.getLoc(), "only the last successor can be variadic");
765 successors.push_back({name, successor});
766 }
767
768 // Create list of traits, skipping over duplicates: appending to lists in
769 // tablegen is easy, making them unique less so, so dedupe here.
770 if (auto *traitList = def.getValueAsListInit("traits")) {
771 // This is uniquing based on pointers of the trait.
772 SmallPtrSet<const Init *, 32> traitSet;
773 traits.reserve(traitSet.size());
774
775 // The declaration order of traits imply the verification order of traits.
776 // Some traits may require other traits to be verified first then they can
777 // do further verification based on those verified facts. If you see this
778 // error, fix the traits declaration order by checking the `dependentTraits`
779 // field.
780 auto verifyTraitValidity = [&](const Record *trait) {
781 auto *dependentTraits = trait->getValueAsListInit("dependentTraits");
782 for (auto *traitInit : *dependentTraits)
783 if (!traitSet.contains(traitInit))
784 PrintFatalError(
785 def.getLoc(),
786 trait->getValueAsString("trait") + " requires " +
787 cast<DefInit>(traitInit)->getDef()->getValueAsString(
788 "trait") +
789 " to precede it in traits list");
790 };
791
792 std::function<void(const ListInit *)> insert;
793 insert = [&](const ListInit *traitList) {
794 for (auto *traitInit : *traitList) {
795 auto *def = cast<DefInit>(traitInit)->getDef();
796 if (def->isSubClassOf("TraitList")) {
797 insert(def->getValueAsListInit("traits"));
798 continue;
799 }
800
801 // Ignore duplicates.
802 if (!traitSet.insert(traitInit).second)
803 continue;
804
805 // If this is an interface with base classes, add the bases to the
806 // trait list.
807 if (def->isSubClassOf("Interface"))
808 insert(def->getValueAsListInit("baseInterfaces"));
809
810 // Verify if the trait has all the dependent traits declared before
811 // itself.
812 verifyTraitValidity(def);
813 traits.push_back(Trait::create(traitInit));
814 }
815 };
816 insert(traitList);
817 }
818
819 populateTypeInferenceInfo(argumentsAndResultsIndex);
820
821 // Handle regions
822 auto *regionsDag = def.getValueAsDag("regions");
823 auto *regionsOp = dyn_cast<DefInit>(regionsDag->getOperator());
824 if (!regionsOp || regionsOp->getDef()->getName() != "region") {
825 PrintFatalError(def.getLoc(), "'regions' must have 'region' directive");
826 }
827
828 for (unsigned i = 0, e = regionsDag->getNumArgs(); i < e; ++i) {
829 auto name = regionsDag->getArgNameStr(i);
830 auto *regionInit = dyn_cast<DefInit>(regionsDag->getArg(i));
831 if (!regionInit) {
832 PrintFatalError(def.getLoc(),
833 Twine("undefined kind for region #") + Twine(i));
834 }
835 Region region(regionInit->getDef());
836 if (region.isVariadic()) {
837 // Only support variadic regions if it is the last one for now.
838 if (i != e - 1)
839 PrintFatalError(def.getLoc(), "only the last region can be variadic");
840 if (name.empty())
841 PrintFatalError(def.getLoc(), "variadic regions must be named");
842 }
843
844 regions.push_back({name, region});
845 }
846
847 // Populate the builders.
848 auto *builderList = dyn_cast_or_null<ListInit>(def.getValueInit("builders"));
849 if (builderList && !builderList->empty()) {
850 for (const Init *init : builderList->getElements())
851 builders.emplace_back(cast<DefInit>(init)->getDef(), def.getLoc());
852 } else if (skipDefaultBuilders()) {
853 PrintFatalError(
854 def.getLoc(),
855 "default builders are skipped and no custom builders provided");
856 }
857
858 LLVM_DEBUG(print(llvm::dbgs()));
859}
860
862 assert(allResultTypesKnown());
863 return resultTypeMapping[index];
864}
865
866ArrayRef<SMLoc> Operator::getLoc() const { return def.getLoc(); }
867
869 return !getDescription().trim().empty();
870}
871
872StringRef Operator::getDescription() const {
873 return def.getValueAsString("description");
874}
875
876bool Operator::hasSummary() const { return !getSummary().trim().empty(); }
877
878StringRef Operator::getSummary() const {
879 return def.getValueAsString("summary");
880}
881
883 auto *valueInit = def.getValueInit("assemblyFormat");
884 return isa<StringInit>(valueInit);
885}
886
888 return TypeSwitch<const Init *, StringRef>(def.getValueInit("assemblyFormat"))
889 .Case([&](const StringInit *init) { return init->getValue(); });
890}
891
892void Operator::print(llvm::raw_ostream &os) const {
893 os << "op '" << getOperationName() << "'\n";
894 for (Argument arg : arguments) {
895 if (auto *attr = llvm::dyn_cast_if_present<NamedAttribute *>(arg))
896 os << "[attribute] " << attr->name << '\n';
897 else
898 os << "[operand] " << cast<NamedTypeConstraint *>(arg)->name << '\n';
899 }
900}
901
904 return VariableDecorator(cast<DefInit>(init)->getDef());
905}
906
908 return attrPropOrOperandMapping[index];
909}
910
911std::string Operator::getGetterName(StringRef name) const {
912 return "get" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);
913}
914
915std::string Operator::getSetterName(StringRef name) const {
916 return "set" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);
917}
918
919std::string Operator::getRemoverName(StringRef name) const {
920 return "remove" + convertToCamelFromSnakeCase(name, /*capitalizeFirst=*/true);
921}
922
923bool Operator::hasFolder() const { return def.getValueAsBit("hasFolder"); }
924
926 return def.getValueAsBit("useCustomPropertiesEncoding");
927}
static void assertAccessorInvariants(const Operator &op, StringRef name)
Assert the invariants of accessors generated for the given name.
Definition Operator.cpp:80
bool isDerivedAttr() const
Definition Attribute.cpp:44
This class represents an inferred result type.
Definition Operator.h:52
static int mapResultIndex(int i)
Definition Operator.h:67
static int unmapResultIndex(int i)
Definition Operator.h:68
static bool isResultIndex(int i)
Definition Operator.h:69
static bool isArgIndex(int i)
Definition Operator.h:70
Wrapper class that contains a MLIR op's information (e.g., operands, attributes) defined in TableGen ...
Definition Operator.h:85
std::string getQualCppClassName() const
Returns this op's C++ class name prefixed with namespaces.
Definition Operator.cpp:156
unsigned getNumSuccessors() const
Returns the number of successors.
Definition Operator.cpp:339
const NamedRegion & getRegion(unsigned index) const
Returns the index-th region.
Definition Operator.cpp:319
TypeConstraint getResultTypeConstraint(int index) const
Returns the index-th result's type constraint.
Definition Operator.cpp:205
ArrayRef< SMLoc > getLoc() const
Definition Operator.cpp:866
Operator(const llvm::Record &def)
const NamedTypeConstraint * const_value_iterator
Definition Operator.h:144
llvm::iterator_range< const_region_iterator > getRegions() const
Definition Operator.cpp:312
StringRef getCppNamespace() const
Returns this op's C++ namespace.
Definition Operator.cpp:162
const_attribute_iterator attribute_begin() const
Definition Operator.cpp:360
std::string getGetterName(StringRef name) const
Returns the getter name for the accessor of name.
Definition Operator.cpp:911
const_successor_iterator successor_end() const
Definition Operator.cpp:331
int getNumOperands() const
Definition Operator.h:233
StringRef getDescription() const
Definition Operator.cpp:872
const_value_range getResults() const
Definition Operator.cpp:201
arg_range getArgs() const
Definition Operator.cpp:244
const NamedAttribute * const_attribute_iterator
Op attribute iterators.
Definition Operator.h:182
const_value_range getOperands() const
Definition Operator.cpp:386
const_region_iterator region_begin() const
Definition Operator.cpp:306
bool useCustomPropertiesEncoding() const
Whether to generate the readProperty/writeProperty methods for bytecode emission.
Definition Operator.cpp:925
const NamedRegion * const_region_iterator
Regions.
Definition Operator.h:273
NamedTypeConstraint & getOperand(int index)
Definition Operator.h:234
StringRef getResultName(int index) const
Returns the index-th result's name.
Definition Operator.cpp:210
var_decorator_range getArgDecorators(int index) const
Definition Operator.cpp:253
const Argument * arg_iterator
Definition Operator.h:256
unsigned getNumVariableLengthOperands() const
Returns the number of variadic operands in this operation.
Definition Operator.cpp:229
OperandAttrOrProp getArgToOperandAttrOrProp(int index) const
Returns the OperandAttrOrProp corresponding to the index.
Definition Operator.cpp:907
var_decorator_range getResultDecorators(int index) const
Returns the index-th result's decorators.
Definition Operator.cpp:215
std::string getGenericAdaptorName() const
Returns the name of op's generic adaptor C++ class.
Definition Operator.cpp:75
StringRef getExtraClassDefinition() const
Returns this op's extra class definition code.
Definition Operator.cpp:176
const_value_iterator result_begin() const
Op result iterators.
Definition Operator.cpp:193
const Trait * const_trait_iterator
Trait.
Definition Operator.h:301
const_attribute_iterator attribute_end() const
Definition Operator.cpp:363
const_trait_iterator trait_end() const
Definition Operator.cpp:353
llvm::iterator_range< VariableDecoratorIterator > var_decorator_range
Definition Operator.h:141
std::string getAdaptorName() const
Returns the name of op's adaptor C++ class.
Definition Operator.cpp:71
bool hasNonEmptyProperties() const
Returns whether this operation has any non-empty properties.
Definition Operator.cpp:277
int getNumResults() const
Returns the number of results this op produces.
Definition Operator.cpp:164
llvm::iterator_range< const_attribute_iterator > getAttributes() const
Definition Operator.cpp:366
llvm::iterator_range< const_value_iterator > const_value_range
Definition Operator.h:146
bool hasFolder() const
Definition Operator.cpp:923
const_value_iterator operand_end() const
Definition Operator.cpp:383
arg_iterator arg_end() const
Definition Operator.cpp:242
int getNumArgs() const
Returns the total number of arguments.
Definition Operator.h:243
NamedTypeConstraint & getResult(int index)
Returns the op result at the given index.
Definition Operator.h:166
llvm::iterator_range< arg_iterator > arg_range
Definition Operator.h:257
const_value_iterator operand_begin() const
Op operand iterators.
Definition Operator.cpp:380
void assertInvariants() const
Check invariants (like no duplicated or conflicted names) and abort the process if any invariant is b...
Definition Operator.cpp:114
StringRef getArgName(int index) const
Definition Operator.cpp:248
StringRef getDialectName() const
Returns this op's dialect name.
Definition Operator.cpp:152
const_region_iterator region_end() const
Definition Operator.cpp:309
unsigned getNumVariableLengthResults() const
Returns the number of variable length results in this operation.
Definition Operator.cpp:223
bool hasSingleVariadicArg() const
Returns true of the operation has a single variadic arg.
Definition Operator.cpp:235
const NamedSuccessor * const_successor_iterator
Successors.
Definition Operator.h:287
unsigned getNumVariadicSuccessors() const
Returns the number of variadic successors in this operation.
Definition Operator.cpp:345
StringRef getSummary() const
Definition Operator.cpp:878
bool isVariadic() const
Returns true if this op has variable length operands or results.
Definition Operator.cpp:392
llvm::iterator_range< const_trait_iterator > getTraits() const
Definition Operator.cpp:356
bool hasCustomPropertiesPrinter() const
Returns true if the operation provides a custom properties printer.
Definition Operator.cpp:189
const Trait * getTrait(llvm::StringRef trait) const
Returns the trait wrapper for the given MLIR C++ trait.
Definition Operator.cpp:261
SmallVector< StringRef > getInherentAttrNames() const
Returns all accepted attribute spellings for this operation's inherent attributes and properties,...
Definition Operator.cpp:288
llvm::iterator_range< const_successor_iterator > getSuccessors() const
Definition Operator.cpp:334
bool hasSummary() const
Definition Operator.cpp:876
const_successor_iterator successor_begin() const
Definition Operator.cpp:328
void print(llvm::raw_ostream &os) const
Prints the contents in this operator to the given os.
Definition Operator.cpp:892
unsigned getNumRegions() const
Returns the number of regions.
Definition Operator.cpp:317
const_trait_iterator trait_begin() const
Definition Operator.cpp:350
NamedAttribute * attribute_iterator
Definition Operator.h:186
StringRef getExtraClassDeclaration() const
Returns this op's extra class declaration code.
Definition Operator.cpp:169
StringRef getAssemblyFormat() const
Definition Operator.cpp:887
std::string getSetterName(StringRef name) const
Returns the setter name for the accessor of name.
Definition Operator.cpp:915
std::string getOperationName() const
Returns the operation name.
Definition Operator.cpp:63
const NamedSuccessor & getSuccessor(unsigned index) const
Returns the index-th successor.
Definition Operator.cpp:341
StringRef getCppClassName() const
Returns this op's C++ class name.
Definition Operator.cpp:154
bool allResultTypesKnown() const
Return whether all the result types are known.
Definition Operator.h:338
bool hasAssemblyFormat() const
Query functions for the assembly format of the operator.
Definition Operator.cpp:882
unsigned getNumVariadicRegions() const
Returns the number of variadic regions in this operation.
Definition Operator.cpp:323
bool skipDefaultBuilders() const
Returns true if default builders should not be generated.
Definition Operator.cpp:185
arg_iterator arg_begin() const
Op argument (attribute or operand) iterators.
Definition Operator.cpp:240
const InferredResultType & getInferredResultType(int index) const
Return all arguments or type constraints with same type as result[index].
Definition Operator.cpp:861
const llvm::Record & getDef() const
Returns the Tablegen definition this operator was constructed from.
Definition Operator.cpp:183
const_value_iterator result_end() const
Definition Operator.cpp:197
std::string getRemoverName(StringRef name) const
Returns the remove name for the accessor of name.
Definition Operator.cpp:919
Argument getArg(int index) const
Op argument (attribute or operand) accessors.
Definition Operator.cpp:390
bool hasDescription() const
Query functions for the documentation of the operator.
Definition Operator.cpp:868
static Trait create(const llvm::Init *init)
Definition Trait.cpp:26
bool isVariableLength() const
Definition Type.h:53
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition CallGraph.h:227
constexpr StringLiteral legacyResultSegmentAttrName
Definition Operator.h:46
const char * inferTypeOpInterface
constexpr StringLiteral legacyOperandSegmentAttrName
Definition Operator.h:44
constexpr StringLiteral operandSegmentAttrName
The canonical and legacy names of the implicit segment-size properties.
Definition Operator.h:42
llvm::PointerUnion< NamedAttribute *, NamedProperty *, NamedTypeConstraint * > Argument
Definition Argument.h:63
constexpr StringLiteral resultSegmentAttrName
Definition Operator.h:43
Include the generated interface declarations.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:147
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123
bool isVariadic() const
Definition Region.h:33
Pair consisting kind of argument and index into operands, attributes, or properties.
Definition Operator.h:346
static VariableDecorator unwrap(const llvm::Init *init)
Definition Operator.cpp:902
A class used to represent the decorators of an operator variable, i.e.
Definition Operator.h:118