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"
29#define DEBUG_TYPE "mlir-tblgen-operator"
39using llvm::StringInit;
42 : dialect(def.getValueAsDef(
"opDialect")), def(def) {
48 std::tie(prefix, cppClassName) = def.getName().split(
'_');
51 cppClassName = def.getName();
52 }
else if (cppClassName.empty()) {
54 cppClassName = prefix;
57 cppNamespace = def.getValueAsString(
"cppNamespace");
59 populateOpStructure();
64 auto prefix = dialect.getName();
65 auto opName = def.getValueAsString(
"opName");
67 return std::string(opName);
68 return std::string(llvm::formatv(
"{0}.{1}", prefix, opName));
76 return std::string(llvm::formatv(
"{0}GenericAdaptor",
getCppClassName()));
81 std::string accessorName =
82 convertToCamelFromSnakeCase(name,
true);
92 auto nameOverlapsWithOpAPI = [&](StringRef newName) {
93 if (newName ==
"AttributeNames" || newName ==
"Attributes" ||
94 newName ==
"Operation")
96 if (newName ==
"Operands")
98 if (newName ==
"Regions")
100 if (newName ==
"Type")
104 if (nameOverlapsWithOpAPI(accessorName)) {
108 PrintFatalError(op.
getLoc(),
"generated accessor for `" + name +
109 "` overlaps with a default one; please "
110 "rename to avoid overlap");
117 auto checkName = [&](StringRef name, StringRef entity) {
120 auto insertion = existingNames.insert({name, entity});
121 if (insertion.second) {
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 '" +
157 if (cppNamespace.empty())
158 return std::string(cppClassName);
159 return std::string(llvm::formatv(
"{0}::{1}", cppNamespace, cppClassName));
165 const DagInit *results = def.getValueAsDag(
"results");
166 return results->getNumArgs();
170 constexpr auto attr =
"extraClassDeclaration";
171 if (def.isValueUnset(attr))
173 return def.getValueAsString(attr);
177 constexpr auto attr =
"extraClassDefinition";
178 if (def.isValueUnset(attr))
180 return def.getValueAsString(attr);
186 return def.getValueAsBit(
"skipDefaultBuilders");
190 return def.getValueAsBit(
"hasCustomPropertiesPrinter");
194 return results.begin();
198 return results.end();
206 const DagInit *results = def.getValueAsDag(
"results");
211 const DagInit *results = def.getValueAsDag(
"results");
212 return results->getArgNameStr(
index);
217 cast<DefInit>(def.getValueAsDag(
"results")->getArg(
index))->getDef();
218 if (!
result->isSubClassOf(
"OpVariable"))
220 return *
result->getValueAsListInit(
"decorators");
249 const DagInit *argumentValues = def.getValueAsDag(
"arguments");
250 return argumentValues->getArgNameStr(
index);
255 cast<DefInit>(def.getValueAsDag(
"arguments")->getArg(
index))->getDef();
256 if (!arg->isSubClassOf(
"OpVariable"))
258 return *arg->getValueAsListInit(
"decorators");
262 for (
const auto &t : traits) {
263 if (
const auto *traitDef = dyn_cast<NativeTrait>(&t)) {
264 if (traitDef->getFullyQualifiedTraitName() == trait)
266 }
else if (
const auto *traitDef = dyn_cast<InternalTrait>(&t)) {
267 if (traitDef->getFullyQualifiedTraitName() == trait)
269 }
else if (
const auto *traitDef = dyn_cast<InterfaceTrait>(&t)) {
270 if (traitDef->getFullyQualifiedTraitName() == trait)
278 if (!properties.empty())
280 if (
getTrait(
"::mlir::OpTrait::AttrSizedOperandSegments") ||
281 getTrait(
"::mlir::OpTrait::AttrSizedResultSegments"))
291 if (!attr.attr.isDerivedAttr())
292 names.push_back(attr.name);
294 names.push_back(property.name);
295 if (
getTrait(
"::mlir::OpTrait::AttrSizedOperandSegments")) {
299 if (
getTrait(
"::mlir::OpTrait::AttrSizedResultSegments")) {
307 return regions.begin();
310 return regions.end();
320 return regions[
index];
324 return llvm::count_if(regions,
329 return successors.begin();
332 return successors.end();
342 return successors[
index];
346 return llvm::count_if(successors,
351 return traits.begin();
361 return attributes.begin();
364 return attributes.end();
371 return attributes.begin();
374 return attributes.end();
381 return operands.begin();
384 return operands.end();
393 return any_of(llvm::concat<const NamedTypeConstraint>(operands, results),
397void Operator::populateTypeInferenceInfo(
398 const llvm::StringMap<int> &argumentsAndResultsIndex) {
401 auto &recordKeeper = def.getRecords();
403 allResultsHaveKnownTypes =
false;
418 if (
getTrait(
"::mlir::OpTrait::SameOperandsAndResultType")) {
420 auto *operandI = llvm::find_if(arguments, [](
const Argument &arg) {
422 llvm::dyn_cast_if_present<NamedTypeConstraint *>(arg);
425 if (operandI == arguments.end())
429 int operandIdx = operandI - arguments.begin();
431 resultTypeMapping.emplace_back(operandIdx,
"$_self");
433 allResultsHaveKnownTypes =
true;
443 struct ResultTypeInference {
447 bool inferred =
false;
452 SmallVector<ResultTypeInference> inference(
getNumResults(), {});
456 for (
auto [idx, infer] : llvm::enumerate(inference)) {
457 if (
getResult(idx).constraint.getBuilderCall()) {
460 infer.inferred =
true;
466 for (
const Trait &trait : traits) {
467 const Record &def = trait.getDef();
473 if (def.isSubClassOf(
476 if (
const auto *traitDef = dyn_cast<InterfaceTrait>(&trait))
477 if (&traitDef->getDef() == inferTrait)
482 if (def.isSubClassOf(
"TypesMatchWith")) {
483 int target = argumentsAndResultsIndex.lookup(def.getValueAsString(
"rhs"));
488 ResultTypeInference &infer = inference[resultIndex];
493 argumentsAndResultsIndex.lookup(def.getValueAsString(
"lhs"));
494 infer.sources.emplace_back(sourceIndex,
495 def.getValueAsString(
"transformer").str());
506 if (def.isSubClassOf(
"ShapedTypeMatchesElementCountAndTypes")) {
507 StringRef shapedArg = def.getValueAsString(
"shaped");
508 StringRef elementsArg = def.getValueAsString(
"elements");
510 int shapedIndex = argumentsAndResultsIndex.lookup(shapedArg);
511 int elementsIndex = argumentsAndResultsIndex.lookup(elementsArg);
517 ResultTypeInference &infer = inference[resultIndex];
518 if (!infer.inferred) {
519 infer.sources.emplace_back(
521 "::llvm::SmallVector<::mlir::Type>(::llvm::cast<::mlir::"
522 "ShapedType>($_self).getNumElements(), "
523 "::llvm::cast<::mlir::ShapedType>($_self).getElementType())");
524 infer.inferred =
true;
534 if (!def.isSubClassOf(
"AllTypesMatch"))
537 auto values = def.getValueAsListOfStrings(
"values");
543 std::optional<int> fullyInferredIndex;
544 SmallVector<int> resultIndices;
545 for (StringRef name : values) {
546 int index = argumentsAndResultsIndex.lookup(name);
551 fullyInferredIndex = index;
553 if (fullyInferredIndex) {
556 for (
int resultIndex : resultIndices) {
557 ResultTypeInference &infer = inference[resultIndex];
558 if (!infer.inferred) {
559 infer.sources.assign(1, {*fullyInferredIndex,
"$_self"});
560 infer.inferred =
true;
565 for (
int resultIndex : resultIndices) {
566 for (
int otherResultIndex : resultIndices) {
567 if (resultIndex == otherResultIndex)
569 inference[resultIndex].sources.emplace_back(
577 std::vector<ResultTypeInference *> worklist;
578 for (ResultTypeInference &infer : inference)
580 worklist.push_back(&infer);
584 for (
auto cur = worklist.begin(); cur != worklist.end();) {
585 ResultTypeInference &infer = **cur;
587 InferredResultType *iter =
588 llvm::find_if(infer.sources, [&](
const InferredResultType &source) {
589 assert(InferredResultType::isResultIndex(source.getIndex()));
590 return inference[InferredResultType::unmapResultIndex(
594 if (iter == infer.sources.end()) {
600 infer.inferred =
true;
602 infer.sources.assign(1, *iter);
603 cur = worklist.erase(cur);
607 allResultsHaveKnownTypes = worklist.empty();
610 if (allResultsHaveKnownTypes) {
612 for (
const ResultTypeInference &infer : inference)
613 resultTypeMapping.push_back(infer.sources.front());
617void Operator::populateOpStructure() {
618 auto &recordKeeper = def.getRecords();
619 auto *typeConstraintClass = recordKeeper.getClass(
"TypeConstraint");
620 auto *attrClass = recordKeeper.getClass(
"Attr");
621 auto *propertyClass = recordKeeper.getClass(
"Property");
622 auto *derivedAttrClass = recordKeeper.getClass(
"DerivedAttr");
623 auto *opVarClass = recordKeeper.getClass(
"OpVariable");
624 numNativeAttributes = 0;
626 const DagInit *argumentValues = def.getValueAsDag(
"arguments");
627 unsigned numArgs = argumentValues->getNumArgs();
631 llvm::StringMap<int> argumentsAndResultsIndex;
634 for (
unsigned i = 0; i != numArgs; ++i) {
635 auto *arg = argumentValues->getArg(i);
636 auto givenName = argumentValues->getArgNameStr(i);
637 auto *argDefInit = dyn_cast<DefInit>(arg);
639 PrintFatalError(def.getLoc(),
640 Twine(
"undefined type for argument #") + Twine(i));
641 const Record *argDef = argDefInit->getDef();
642 if (argDef->isSubClassOf(opVarClass))
643 argDef = argDef->getValueAsDef(
"constraint");
645 if (argDef->isSubClassOf(typeConstraintClass)) {
647 NamedTypeConstraint{givenName, TypeConstraint(argDef)});
648 }
else if (argDef->isSubClassOf(attrClass)) {
649 if (givenName.empty())
650 PrintFatalError(argDef->getLoc(),
"attributes must be named");
651 if (argDef->isSubClassOf(derivedAttrClass))
652 PrintFatalError(argDef->getLoc(),
653 "derived attributes not allowed in argument list");
654 attributes.push_back({givenName, Attribute(argDef)});
655 ++numNativeAttributes;
656 }
else if (argDef->isSubClassOf(propertyClass)) {
657 if (givenName.empty())
658 PrintFatalError(argDef->getLoc(),
"properties must be named");
659 properties.push_back({givenName, Property(argDef)});
661 PrintFatalError(def.getLoc(),
662 "unexpected def type; only defs deriving "
663 "from TypeConstraint or Attr or Property are allowed");
665 if (!givenName.empty())
666 argumentsAndResultsIndex[givenName] = i;
670 for (
const auto &val : def.getValues()) {
671 if (
auto *record = dyn_cast<llvm::RecordRecTy>(val.getType())) {
672 if (!record->isSubClassOf(attrClass))
674 if (!record->isSubClassOf(derivedAttrClass))
675 PrintFatalError(def.getLoc(),
676 "unexpected Attr where only DerivedAttr is allowed");
678 if (record->getClasses().size() != 1) {
681 "unsupported attribute modelling, only single class expected");
683 attributes.push_back({cast<StringInit>(val.getNameInit())->getValue(),
684 Attribute(cast<DefInit>(val.getValue()))});
692 int operandIndex = 0, attrIndex = 0, propIndex = 0;
693 for (
unsigned i = 0; i != numArgs; ++i) {
694 const Record *argDef =
695 dyn_cast<DefInit>(argumentValues->getArg(i))->getDef();
696 if (argDef->isSubClassOf(opVarClass))
697 argDef = argDef->getValueAsDef(
"constraint");
699 if (argDef->isSubClassOf(typeConstraintClass)) {
700 attrPropOrOperandMapping.push_back(
702 arguments.emplace_back(&operands[operandIndex++]);
703 }
else if (argDef->isSubClassOf(attrClass)) {
704 attrPropOrOperandMapping.push_back(
706 arguments.emplace_back(&attributes[attrIndex++]);
708 assert(argDef->isSubClassOf(propertyClass));
709 attrPropOrOperandMapping.push_back(
711 arguments.emplace_back(&properties[propIndex++]);
715 auto *resultsDag = def.getValueAsDag(
"results");
716 auto *outsOp = dyn_cast<DefInit>(resultsDag->getOperator());
717 if (!outsOp || outsOp->getDef()->getName() !=
"outs") {
718 PrintFatalError(def.getLoc(),
"'results' must have 'outs' directive");
722 for (
unsigned i = 0, e = resultsDag->getNumArgs(); i < e; ++i) {
723 auto name = resultsDag->getArgNameStr(i);
724 auto *resultInit = dyn_cast<DefInit>(resultsDag->getArg(i));
726 PrintFatalError(def.getLoc(),
727 Twine(
"undefined type for result #") + Twine(i));
729 auto *resultDef = resultInit->getDef();
730 if (resultDef->isSubClassOf(opVarClass))
731 resultDef = resultDef->getValueAsDef(
"constraint");
732 results.push_back({name, TypeConstraint(resultDef)});
737 if (results.back().constraint.isVariadicOfVariadic()) {
740 "'VariadicOfVariadic' results are currently not supported");
745 auto *successorsDag = def.getValueAsDag(
"successors");
746 auto *successorsOp = dyn_cast<DefInit>(successorsDag->getOperator());
747 if (!successorsOp || successorsOp->getDef()->getName() !=
"successor") {
748 PrintFatalError(def.getLoc(),
749 "'successors' must have 'successor' directive");
752 for (
unsigned i = 0, e = successorsDag->getNumArgs(); i < e; ++i) {
753 auto name = successorsDag->getArgNameStr(i);
754 auto *successorInit = dyn_cast<DefInit>(successorsDag->getArg(i));
755 if (!successorInit) {
756 PrintFatalError(def.getLoc(),
757 Twine(
"undefined kind for successor #") + Twine(i));
759 Successor successor(successorInit->getDef());
762 if (i != e - 1 && successor.isVariadic())
763 PrintFatalError(def.getLoc(),
"only the last successor can be variadic");
764 successors.push_back({name, successor});
769 if (
auto *traitList = def.getValueAsListInit(
"traits")) {
771 SmallPtrSet<const Init *, 32> traitSet;
772 traits.reserve(traitSet.size());
779 auto verifyTraitValidity = [&](
const Record *trait) {
780 auto *dependentTraits = trait->getValueAsListInit(
"dependentTraits");
781 for (
auto *traitInit : *dependentTraits)
782 if (!traitSet.contains(traitInit))
785 trait->getValueAsString(
"trait") +
" requires " +
786 cast<DefInit>(traitInit)->getDef()->getValueAsString(
788 " to precede it in traits list");
791 std::function<void(
const ListInit *)> insert;
792 insert = [&](
const ListInit *traitList) {
793 for (
auto *traitInit : *traitList) {
794 auto *def = cast<DefInit>(traitInit)->getDef();
795 if (def->isSubClassOf(
"TraitList")) {
796 insert(def->getValueAsListInit(
"traits"));
801 if (!traitSet.insert(traitInit).second)
806 if (def->isSubClassOf(
"Interface"))
807 insert(def->getValueAsListInit(
"baseInterfaces"));
811 verifyTraitValidity(def);
818 populateTypeInferenceInfo(argumentsAndResultsIndex);
821 auto *regionsDag = def.getValueAsDag(
"regions");
822 auto *regionsOp = dyn_cast<DefInit>(regionsDag->getOperator());
823 if (!regionsOp || regionsOp->getDef()->getName() !=
"region") {
824 PrintFatalError(def.getLoc(),
"'regions' must have 'region' directive");
827 for (
unsigned i = 0, e = regionsDag->getNumArgs(); i < e; ++i) {
828 auto name = regionsDag->getArgNameStr(i);
829 auto *regionInit = dyn_cast<DefInit>(regionsDag->getArg(i));
831 PrintFatalError(def.getLoc(),
832 Twine(
"undefined kind for region #") + Twine(i));
834 Region region(regionInit->getDef());
835 if (region.isVariadic()) {
838 PrintFatalError(def.getLoc(),
"only the last region can be variadic");
840 PrintFatalError(def.getLoc(),
"variadic regions must be named");
843 regions.push_back({name, region});
847 auto *builderList = dyn_cast_or_null<ListInit>(def.getValueInit(
"builders"));
848 if (builderList && !builderList->empty()) {
849 for (
const Init *init : builderList->getElements())
850 builders.emplace_back(cast<DefInit>(init)->
getDef(), def.getLoc());
854 "default builders are skipped and no custom builders provided");
857 LLVM_DEBUG(
print(llvm::dbgs()));
862 return resultTypeMapping[
index];
872 return def.getValueAsString(
"description");
878 return def.getValueAsString(
"summary");
882 auto *valueInit = def.getValueInit(
"assemblyFormat");
883 return isa<StringInit>(valueInit);
888 .Case([&](
const StringInit *init) {
return init->getValue(); });
894 if (
auto *attr = llvm::dyn_cast_if_present<NamedAttribute *>(arg))
895 os <<
"[attribute] " << attr->name <<
'\n';
897 os <<
"[operand] " << cast<NamedTypeConstraint *>(arg)->name <<
'\n';
907 return attrPropOrOperandMapping[
index];
911 return "get" + convertToCamelFromSnakeCase(name,
true);
915 return "set" + convertToCamelFromSnakeCase(name,
true);
919 return "remove" + convertToCamelFromSnakeCase(name,
true);
925 return def.getValueAsBit(
"useCustomPropertiesEncoding");
static void assertAccessorInvariants(const Operator &op, StringRef name)
Assert the invariants of accessors generated for the given name.
bool isDerivedAttr() const
This class represents an inferred result type.
static int mapResultIndex(int i)
static int unmapResultIndex(int i)
static bool isResultIndex(int i)
static bool isArgIndex(int i)
Wrapper class that contains a MLIR op's information (e.g., operands, attributes) defined in TableGen ...
std::string getQualCppClassName() const
Returns this op's C++ class name prefixed with namespaces.
unsigned getNumSuccessors() const
Returns the number of successors.
const NamedRegion & getRegion(unsigned index) const
Returns the index-th region.
TypeConstraint getResultTypeConstraint(int index) const
Returns the index-th result's type constraint.
ArrayRef< SMLoc > getLoc() const
Operator(const llvm::Record &def)
const NamedTypeConstraint * const_value_iterator
llvm::iterator_range< const_region_iterator > getRegions() const
StringRef getCppNamespace() const
Returns this op's C++ namespace.
const_attribute_iterator attribute_begin() const
std::string getGetterName(StringRef name) const
Returns the getter name for the accessor of name.
const_successor_iterator successor_end() const
int getNumOperands() const
StringRef getDescription() const
const_value_range getResults() const
arg_range getArgs() const
const NamedAttribute * const_attribute_iterator
Op attribute iterators.
const_value_range getOperands() const
const_region_iterator region_begin() const
bool useCustomPropertiesEncoding() const
Whether to generate the readProperty/writeProperty methods for bytecode emission.
const NamedRegion * const_region_iterator
Regions.
NamedTypeConstraint & getOperand(int index)
StringRef getResultName(int index) const
Returns the index-th result's name.
var_decorator_range getArgDecorators(int index) const
const Argument * arg_iterator
unsigned getNumVariableLengthOperands() const
Returns the number of variadic operands in this operation.
OperandAttrOrProp getArgToOperandAttrOrProp(int index) const
Returns the OperandAttrOrProp corresponding to the index.
var_decorator_range getResultDecorators(int index) const
Returns the index-th result's decorators.
std::string getGenericAdaptorName() const
Returns the name of op's generic adaptor C++ class.
StringRef getExtraClassDefinition() const
Returns this op's extra class definition code.
const_value_iterator result_begin() const
Op result iterators.
const Trait * const_trait_iterator
Trait.
const_attribute_iterator attribute_end() const
const_trait_iterator trait_end() const
llvm::iterator_range< VariableDecoratorIterator > var_decorator_range
std::string getAdaptorName() const
Returns the name of op's adaptor C++ class.
bool hasNonEmptyProperties() const
Returns whether this operation has any non-empty properties.
int getNumResults() const
Returns the number of results this op produces.
llvm::iterator_range< const_attribute_iterator > getAttributes() const
llvm::iterator_range< const_value_iterator > const_value_range
const_value_iterator operand_end() const
arg_iterator arg_end() const
int getNumArgs() const
Returns the total number of arguments.
NamedTypeConstraint & getResult(int index)
Returns the op result at the given index.
llvm::iterator_range< arg_iterator > arg_range
const_value_iterator operand_begin() const
Op operand iterators.
void assertInvariants() const
Check invariants (like no duplicated or conflicted names) and abort the process if any invariant is b...
StringRef getArgName(int index) const
StringRef getDialectName() const
Returns this op's dialect name.
const_region_iterator region_end() const
unsigned getNumVariableLengthResults() const
Returns the number of variable length results in this operation.
bool hasSingleVariadicArg() const
Returns true of the operation has a single variadic arg.
const NamedSuccessor * const_successor_iterator
Successors.
unsigned getNumVariadicSuccessors() const
Returns the number of variadic successors in this operation.
StringRef getSummary() const
bool isVariadic() const
Returns true if this op has variable length operands or results.
llvm::iterator_range< const_trait_iterator > getTraits() const
bool hasCustomPropertiesPrinter() const
Returns true if the operation provides a custom properties printer.
const Trait * getTrait(llvm::StringRef trait) const
Returns the trait wrapper for the given MLIR C++ trait.
SmallVector< StringRef > getInherentAttrNames() const
Returns all accepted attribute spellings for this operation's inherent attributes and properties,...
llvm::iterator_range< const_successor_iterator > getSuccessors() const
const_successor_iterator successor_begin() const
void print(llvm::raw_ostream &os) const
Prints the contents in this operator to the given os.
unsigned getNumRegions() const
Returns the number of regions.
const_trait_iterator trait_begin() const
NamedAttribute * attribute_iterator
StringRef getExtraClassDeclaration() const
Returns this op's extra class declaration code.
StringRef getAssemblyFormat() const
std::string getSetterName(StringRef name) const
Returns the setter name for the accessor of name.
std::string getOperationName() const
Returns the operation name.
const NamedSuccessor & getSuccessor(unsigned index) const
Returns the index-th successor.
StringRef getCppClassName() const
Returns this op's C++ class name.
bool allResultTypesKnown() const
Return whether all the result types are known.
bool hasAssemblyFormat() const
Query functions for the assembly format of the operator.
unsigned getNumVariadicRegions() const
Returns the number of variadic regions in this operation.
bool skipDefaultBuilders() const
Returns true if default builders should not be generated.
arg_iterator arg_begin() const
Op argument (attribute or operand) iterators.
const InferredResultType & getInferredResultType(int index) const
Return all arguments or type constraints with same type as result[index].
const llvm::Record & getDef() const
Returns the Tablegen definition this operator was constructed from.
const_value_iterator result_end() const
std::string getRemoverName(StringRef name) const
Returns the remove name for the accessor of name.
Argument getArg(int index) const
Op argument (attribute or operand) accessors.
bool hasDescription() const
Query functions for the documentation of the operator.
static Trait create(const llvm::Init *init)
bool isVariableLength() const
The OpAsmOpInterface, see OpAsmInterface.td for more details.
constexpr StringLiteral legacyResultSegmentAttrName
const char * inferTypeOpInterface
constexpr StringLiteral legacyOperandSegmentAttrName
constexpr StringLiteral operandSegmentAttrName
The canonical and legacy names of the implicit segment-size properties.
llvm::PointerUnion< NamedAttribute *, NamedProperty *, NamedTypeConstraint * > Argument
constexpr StringLiteral resultSegmentAttrName
Include the generated interface declarations.
llvm::TypeSwitch< T, ResultT > TypeSwitch
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
bool isVariableLength() const
TypeConstraint constraint
Pair consisting kind of argument and index into operands, attributes, or properties.
static VariableDecorator unwrap(const llvm::Init *init)
A class used to represent the decorators of an operator variable, i.e.