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