MLIR 23.0.0git
Operation.cpp
Go to the documentation of this file.
1//===- Operation.cpp - Operation support code -----------------------------===//
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#include "mlir/IR/Operation.h"
10#include "mlir/IR/Attributes.h"
13#include "mlir/IR/Dialect.h"
14#include "mlir/IR/IRMapping.h"
15#include "mlir/IR/Matchers.h"
21#include "llvm/ADT/STLExtras.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/Support/ErrorHandling.h"
24#include <numeric>
25#include <optional>
26
27using namespace mlir;
28
29//===----------------------------------------------------------------------===//
30// Operation
31//===----------------------------------------------------------------------===//
32
33/// Create a new Operation from operation state.
34Operation *Operation::create(const OperationState &state) {
35 Operation *op =
36 create(state.location, state.name, state.types, state.operands,
37 state.attributes.getDictionary(state.getContext()),
38 state.properties, state.successors, state.regions);
39 if (LLVM_UNLIKELY(state.propertiesAttr)) {
40 assert(!state.properties);
41 LogicalResult result =
43 /*diagnostic=*/nullptr);
44 assert(result.succeeded() && "invalid properties in op creation");
45 (void)result;
46 }
47 return op;
48}
49
50/// Create a new Operation with the specific fields.
51Operation *Operation::create(Location location, OperationName name,
52 TypeRange resultTypes, ValueRange operands,
53 NamedAttrList &&attributes,
54 OpaqueProperties properties, BlockRange successors,
55 RegionRange regions) {
56 unsigned numRegions = regions.size();
57 Operation *op =
58 create(location, name, resultTypes, operands, std::move(attributes),
59 properties, successors, numRegions);
60 for (unsigned i = 0; i < numRegions; ++i)
61 if (regions[i])
62 op->getRegion(i).takeBody(*regions[i]);
63 return op;
64}
65
66/// Create a new Operation with the specific fields.
67Operation *Operation::create(Location location, OperationName name,
68 TypeRange resultTypes, ValueRange operands,
69 NamedAttrList &&attributes,
70 OpaqueProperties properties, BlockRange successors,
71 unsigned numRegions) {
72 // Populate default attributes.
73 name.populateDefaultAttrs(attributes);
74
75 return create(location, name, resultTypes, operands,
76 attributes.getDictionary(location.getContext()), properties,
77 successors, numRegions);
78}
79
80/// Overload of create that takes an existing DictionaryAttr to avoid
81/// unnecessarily uniquing a list of attributes.
82Operation *Operation::create(Location location, OperationName name,
83 TypeRange resultTypes, ValueRange operands,
84 DictionaryAttr attributes,
85 OpaqueProperties properties, BlockRange successors,
86 unsigned numRegions) {
87 assert(llvm::all_of(resultTypes, [](Type t) { return t; }) &&
88 "unexpected null result type");
89
90 // We only need to allocate additional memory for a subset of results.
91 unsigned numTrailingResults = OpResult::getNumTrailing(resultTypes.size());
92 unsigned numInlineResults = OpResult::getNumInline(resultTypes.size());
93 unsigned numSuccessors = successors.size();
94 unsigned numOperands = operands.size();
95 unsigned numResults = resultTypes.size();
96 int opPropertiesAllocSize = llvm::alignTo<8>(name.getOpPropertyByteSize());
97
98 // If the operation is known to have no operands, don't allocate an operand
99 // storage.
100 bool needsOperandStorage =
101 operands.empty() ? !name.hasTrait<OpTrait::ZeroOperands>() : true;
102
103 // Compute the byte size for the operation and the operand storage. This takes
104 // into account the size of the operation, its trailing objects, and its
105 // prefixed objects.
106 size_t byteSize =
109 needsOperandStorage ? 1 : 0, opPropertiesAllocSize, numSuccessors,
110 numRegions, numOperands);
111 size_t prefixByteSize = llvm::alignTo(
112 Operation::prefixAllocSize(numTrailingResults, numInlineResults),
113 alignof(Operation));
114 char *mallocMem = reinterpret_cast<char *>(malloc(byteSize + prefixByteSize));
115 void *rawMem = mallocMem + prefixByteSize;
116
117 // Create the new Operation.
118 Operation *op = ::new (rawMem) Operation(
119 location, name, numResults, numSuccessors, numRegions,
120 opPropertiesAllocSize, attributes, properties, needsOperandStorage);
121
122 assert((numSuccessors == 0 || op->mightHaveTrait<OpTrait::IsTerminator>()) &&
123 "unexpected successors in a non-terminator operation");
124
125 // Initialize the results.
126 auto resultTypeIt = resultTypes.begin();
127 for (unsigned i = 0; i < numInlineResults; ++i, ++resultTypeIt)
128 new (op->getInlineOpResult(i)) detail::InlineOpResult(*resultTypeIt, i);
129 for (unsigned i = 0; i < numTrailingResults; ++i, ++resultTypeIt) {
130 new (op->getOutOfLineOpResult(i))
131 detail::OutOfLineOpResult(*resultTypeIt, i);
132 }
133
134 // Initialize the regions.
135 for (unsigned i = 0; i != numRegions; ++i)
136 new (&op->getRegion(i)) Region(op);
137
138 // Initialize the operands.
139 if (needsOperandStorage) {
140 new (&op->getOperandStorage()) detail::OperandStorage(
141 op, op->getTrailingObjects<OpOperand>(), operands);
142 }
143
144 // Initialize the successors.
145 auto blockOperands = op->getBlockOperands();
146 for (unsigned i = 0; i != numSuccessors; ++i)
147 new (&blockOperands[i]) BlockOperand(op, successors[i]);
148
149 // This must be done after properties are initalized.
150 op->setAttrs(attributes);
151
152 return op;
153}
154
155Operation::Operation(Location location, OperationName name, unsigned numResults,
156 unsigned numSuccessors, unsigned numRegions,
157 int fullPropertiesStorageSize, DictionaryAttr attributes,
158 OpaqueProperties properties, bool hasOperandStorage)
159 : location(location), numResults(numResults), numSuccs(numSuccessors),
160 numRegions(numRegions), hasOperandStorage(hasOperandStorage),
161 propertiesStorageSize((fullPropertiesStorageSize + 7) / 8), name(name) {
162 assert(attributes && "unexpected null attribute dictionary");
163 assert(fullPropertiesStorageSize <= propertiesCapacity &&
164 "Properties size overflow");
165#ifndef NDEBUG
166 if (!getDialect() && !getContext()->allowsUnregisteredDialects())
167 llvm::report_fatal_error(
168 name.getStringRef() +
169 " created with unregistered dialect. If this is intended, please call "
170 "allowUnregisteredDialects() on the MLIRContext, or use "
171 "-allow-unregistered-dialect with the MLIR tool used.");
172#endif
173 if (fullPropertiesStorageSize)
174 name.initOpProperties(getPropertiesStorage(), properties);
175}
176
177// Operations are deleted through the destroy() member because they are
178// allocated via malloc.
179Operation::~Operation() {
180 assert(block == nullptr && "operation destroyed but still in a block");
181#ifndef NDEBUG
182 if (!use_empty()) {
183 {
185 emitOpError("operation destroyed but still has uses");
186 for (Operation *user : getUsers())
187 diag.attachNote(user->getLoc()) << "- use: " << *user << "\n";
188 }
189 llvm::report_fatal_error("operation destroyed but still has uses");
190 }
191#endif
192 // Explicitly run the destructors for the operands.
193 if (hasOperandStorage)
194 getOperandStorage().~OperandStorage();
195
196 // Explicitly run the destructors for the successors.
197 for (auto &successor : getBlockOperands())
198 successor.~BlockOperand();
199
200 // Explicitly destroy the regions.
201 for (auto &region : getRegions())
202 region.~Region();
203 if (propertiesStorageSize)
204 name.destroyOpProperties(getPropertiesStorage());
205}
206
207/// Destroy this operation or one of its subclasses.
209 // Operations may have additional prefixed allocation, which needs to be
210 // accounted for here when computing the address to free.
211 char *rawMem = reinterpret_cast<char *>(this) -
212 llvm::alignTo(prefixAllocSize(), alignof(Operation));
213 this->~Operation();
214 free(rawMem);
215}
216
217/// Return true if this operation is a proper ancestor of the `other`
218/// operation.
219bool Operation::isProperAncestor(Operation *other) {
220 while ((other = other->getParentOp()))
221 if (this == other)
222 return true;
223 return false;
224}
225
226/// Replace any uses of 'from' with 'to' within this operation.
228 if (from == to)
229 return;
230 for (auto &operand : getOpOperands())
231 if (operand.get() == from)
232 operand.set(to);
233}
234
235/// Replace the current operands of this operation with the ones provided in
236/// 'operands'.
238 if (LLVM_LIKELY(hasOperandStorage))
239 return getOperandStorage().setOperands(this, operands);
240 assert(operands.empty() && "setting operands without an operand storage");
241}
242
243/// Replace the operands beginning at 'start' and ending at 'start' + 'length'
244/// with the ones provided in 'operands'. 'operands' may be smaller or larger
245/// than the range pointed to by 'start'+'length'.
246void Operation::setOperands(unsigned start, unsigned length,
247 ValueRange operands) {
248 assert((start + length) <= getNumOperands() &&
249 "invalid operand range specified");
250 if (LLVM_LIKELY(hasOperandStorage))
251 return getOperandStorage().setOperands(this, start, length, operands);
252 assert(operands.empty() && "setting operands without an operand storage");
253}
254
255/// Insert the given operands into the operand list at the given 'index'.
257 if (LLVM_LIKELY(hasOperandStorage))
258 return setOperands(index, /*length=*/0, operands);
259 assert(operands.empty() && "inserting operands without an operand storage");
260}
261
262//===----------------------------------------------------------------------===//
263// Diagnostics
264//===----------------------------------------------------------------------===//
265
266/// Emit an error about fatal conditions with this operation, reporting up to
267/// any diagnostic handlers that may be listening.
270 if (getContext()->shouldPrintOpOnDiagnostic()) {
271 diag.attachNote(getLoc())
272 .append("see current operation: ")
273 .appendOp(*this, OpPrintingFlags().printGenericOpForm());
274 }
275 return diag;
276}
277
278/// Emit a warning about this operation, reporting up to any diagnostic
279/// handlers that may be listening.
282 if (getContext()->shouldPrintOpOnDiagnostic())
283 diag.attachNote(getLoc()) << "see current operation: " << *this;
284 return diag;
285}
286
287/// Emit a remark about this operation, reporting up to any diagnostic
288/// handlers that may be listening.
291 if (getContext()->shouldPrintOpOnDiagnostic())
292 diag.attachNote(getLoc()) << "see current operation: " << *this;
293 return diag;
294}
295
298 NamedAttrList attrsList = attrs;
299 getName().populateInherentAttrs(this, attrsList);
300 return attrsList.getDictionary(getContext());
301 }
302 return attrs;
303}
304
305void Operation::setAttrs(DictionaryAttr newAttrs) {
306 assert(newAttrs && "expected valid attribute dictionary");
308 // We're spliting the providing DictionaryAttr by removing the inherentAttr
309 // which will be stored in the properties.
310 SmallVector<NamedAttribute> discardableAttrs;
311 discardableAttrs.reserve(newAttrs.size());
312 for (NamedAttribute attr : newAttrs) {
313 if (getInherentAttr(attr.getName()))
314 setInherentAttr(attr.getName(), attr.getValue());
315 else
316 discardableAttrs.push_back(attr);
317 }
318 if (discardableAttrs.size() != newAttrs.size())
319 newAttrs = DictionaryAttr::get(getContext(), discardableAttrs);
320 }
321 attrs = newAttrs;
322}
325 // We're spliting the providing array of attributes by removing the
326 // inherentAttr which will be stored in the properties.
327 SmallVector<NamedAttribute> discardableAttrs;
328 discardableAttrs.reserve(newAttrs.size());
329 for (NamedAttribute attr : newAttrs) {
330 if (getInherentAttr(attr.getName()))
331 setInherentAttr(attr.getName(), attr.getValue());
332 else
333 discardableAttrs.push_back(attr);
334 }
335 attrs = DictionaryAttr::get(getContext(), discardableAttrs);
336 return;
337 }
338 attrs = DictionaryAttr::get(getContext(), newAttrs);
339}
340
341std::optional<Attribute> Operation::getInherentAttr(StringRef name) {
342 return getName().getInherentAttr(this, name);
343}
344
345void Operation::setInherentAttr(StringAttr name, Attribute value) {
346 getName().setInherentAttr(this, name, value);
347}
348
350 std::optional<RegisteredOperationName> info = getRegisteredInfo();
351 if (LLVM_UNLIKELY(!info))
352 return *getPropertiesStorage().as<Attribute *>();
353 return info->getOpPropertiesAsAttribute(this);
354}
357 std::optional<RegisteredOperationName> info = getRegisteredInfo();
358 if (LLVM_UNLIKELY(!info)) {
359 *getPropertiesStorage().as<Attribute *>() = attr;
360 return success();
361 }
362 return info->setOpPropertiesFromAttribute(
363 this->getName(), this->getPropertiesStorage(), attr, emitError);
364}
365
367 name.copyOpProperties(getPropertiesStorage(), rhs);
368}
369
370llvm::hash_code Operation::hashProperties() {
371 return name.hashOpProperties(getPropertiesStorage());
372}
373
374//===----------------------------------------------------------------------===//
375// Operation Ordering
376//===----------------------------------------------------------------------===//
377
378/// Given an operation 'other' that is within the same parent block, return
379/// whether the current operation is before 'other' in the operation list
380/// of the parent block.
381/// Note: This function has an average complexity of O(1), but worst case may
382/// take O(N) where N is the number of operations within the parent block.
383bool Operation::isBeforeInBlock(Operation *other) {
384 assert(block && "Operations without parent blocks have no order.");
385 assert(other && other->block == block &&
386 "Expected other operation to have the same parent block.");
387 // If the order of the block is already invalid, directly recompute the
388 // parent.
389 if (!block->isOpOrderValid()) {
390 block->recomputeOpOrder();
391 } else {
392 // Update the order either operation if necessary.
393 updateOrderIfNecessary();
394 other->updateOrderIfNecessary();
395 }
396
397 return orderIndex < other->orderIndex;
398}
399
400/// Update the order index of this operation of this operation if necessary,
401/// potentially recomputing the order of the parent block.
402void Operation::updateOrderIfNecessary() {
403 assert(block && "expected valid parent");
404
405 // If the order is valid for this operation there is nothing to do.
406 if (hasValidOrder() || llvm::hasSingleElement(*block))
407 return;
408 Operation *blockFront = &block->front();
409 Operation *blockBack = &block->back();
410
411 // This method is expected to only be invoked on blocks with more than one
412 // operation.
413 assert(blockFront != blockBack && "expected more than one operation");
414
415 // If the operation is at the end of the block.
416 if (this == blockBack) {
417 Operation *prevNode = getPrevNode();
418 if (!prevNode->hasValidOrder())
419 return block->recomputeOpOrder();
420
421 // Add the stride to the previous operation.
422 orderIndex = prevNode->orderIndex + kOrderStride;
423 return;
424 }
425
426 // If this is the first operation try to use the next operation to compute the
427 // ordering.
428 if (this == blockFront) {
429 Operation *nextNode = getNextNode();
430 if (!nextNode->hasValidOrder())
431 return block->recomputeOpOrder();
432 // There is no order to give this operation.
433 if (nextNode->orderIndex == 0)
434 return block->recomputeOpOrder();
435
436 // If we can't use the stride, just take the middle value left. This is safe
437 // because we know there is at least one valid index to assign to.
438 if (nextNode->orderIndex <= kOrderStride)
439 orderIndex = (nextNode->orderIndex / 2);
440 else
441 orderIndex = kOrderStride;
442 return;
443 }
444
445 // Otherwise, this operation is between two others. Place this operation in
446 // the middle of the previous and next if possible.
447 Operation *prevNode = getPrevNode(), *nextNode = getNextNode();
448 if (!prevNode->hasValidOrder() || !nextNode->hasValidOrder())
449 return block->recomputeOpOrder();
450 unsigned prevOrder = prevNode->orderIndex, nextOrder = nextNode->orderIndex;
451
452 // Check to see if there is a valid order between the two.
453 if (prevOrder + 1 == nextOrder)
454 return block->recomputeOpOrder();
455 orderIndex = prevOrder + ((nextOrder - prevOrder) / 2);
456}
457
458//===----------------------------------------------------------------------===//
459// ilist_traits for Operation
460//===----------------------------------------------------------------------===//
461
462auto llvm::ilist_detail::SpecificNodeAccess<
463 llvm::ilist_detail::compute_node_options<::mlir::Operation>::type>::
464 getNodePtr(pointer n) -> node_type * {
465 return NodeAccess::getNodePtr<OptionsT>(n);
466}
467
468auto llvm::ilist_detail::SpecificNodeAccess<
469 llvm::ilist_detail::compute_node_options<::mlir::Operation>::type>::
470 getNodePtr(const_pointer n) -> const node_type * {
471 return NodeAccess::getNodePtr<OptionsT>(n);
472}
473
474auto llvm::ilist_detail::SpecificNodeAccess<
475 llvm::ilist_detail::compute_node_options<::mlir::Operation>::type>::
476 getValuePtr(node_type *n) -> pointer {
477 return NodeAccess::getValuePtr<OptionsT>(n);
478}
479
480auto llvm::ilist_detail::SpecificNodeAccess<
481 llvm::ilist_detail::compute_node_options<::mlir::Operation>::type>::
482 getValuePtr(const node_type *n) -> const_pointer {
483 return NodeAccess::getValuePtr<OptionsT>(n);
484}
485
489
490Block *llvm::ilist_traits<::mlir::Operation>::getContainingBlock() {
491 size_t offset(size_t(&((Block *)nullptr->*Block::getSublistAccess(nullptr))));
492 iplist<Operation> *anchor(static_cast<iplist<Operation> *>(this));
493 return reinterpret_cast<Block *>(reinterpret_cast<char *>(anchor) - offset);
494}
495
496/// This is a trait method invoked when an operation is added to a block. We
497/// keep the block pointer up to date.
499 assert(!op->getBlock() && "already in an operation block!");
500 op->block = getContainingBlock();
501
502 // Invalidate the order on the operation.
503 op->orderIndex = Operation::kInvalidOrderIdx;
504}
505
506/// This is a trait method invoked when an operation is removed from a block.
507/// We keep the block pointer up to date.
509 assert(op->block && "not already in an operation block!");
510 op->block = nullptr;
511}
512
513/// This is a trait method invoked when an operation is moved from one block
514/// to another. We keep the block pointer up to date.
516 ilist_traits<Operation> &otherList, op_iterator first, op_iterator last) {
517 Block *curParent = getContainingBlock();
518
519 // Invalidate the ordering of the parent block.
520 curParent->invalidateOpOrder();
521
522 // If we are transferring operations within the same block, the block
523 // pointer doesn't need to be updated.
524 if (curParent == otherList.getContainingBlock())
525 return;
526
527 // Update the 'block' member of each operation.
528 for (; first != last; ++first)
529 first->block = curParent;
530}
531
532/// Remove this operation (and its descendants) from its Block and delete
533/// all of them.
535 if (auto *parent = getBlock())
536 parent->getOperations().erase(this);
537 else
538 destroy();
539}
540
541/// Remove the operation from its parent block, but don't delete it.
543 if (Block *parent = getBlock())
544 parent->getOperations().remove(this);
545}
546
547/// Unlink this operation from its current block and insert it right before
548/// `existingOp` which may be in the same or another block in the same
549/// function.
550void Operation::moveBefore(Operation *existingOp) {
551 moveBefore(existingOp->getBlock(), existingOp->getIterator());
552}
553
554/// Unlink this operation from its current basic block and insert it right
555/// before `iterator` in the specified basic block.
557 llvm::iplist<Operation>::iterator iterator) {
558 assert(getBlock() &&
559 "cannot move an operation that isn't contained in a block");
560 block->getOperations().splice(iterator, getBlock()->getOperations(),
561 getIterator());
562}
563
564/// Unlink this operation from its current block and insert it right after
565/// `existingOp` which may be in the same or another block in the same function.
566void Operation::moveAfter(Operation *existingOp) {
567 moveAfter(existingOp->getBlock(), existingOp->getIterator());
568}
569
570/// Unlink this operation from its current block and insert it right after
571/// `iterator` in the specified block.
573 llvm::iplist<Operation>::iterator iterator) {
574 assert(iterator != block->end() && "cannot move after end of block");
575 moveBefore(block, std::next(iterator));
576}
577
578/// This drops all operand uses from this operation, which is an essential
579/// step in breaking cyclic dependences between references when they are to
580/// be deleted.
582 for (auto &op : getOpOperands())
583 op.drop();
584
585 for (auto &region : getRegions())
586 region.dropAllReferences();
587
588 for (auto &dest : getBlockOperands())
589 dest.drop();
590}
591
592/// This drops all uses of any values defined by this operation or its nested
593/// regions, wherever they are located.
595 dropAllUses();
596
597 for (auto &region : getRegions())
598 for (auto &block : region)
599 block.dropAllDefinedValueUses();
600}
601
602void Operation::setSuccessor(Block *block, unsigned index) {
603 assert(index < getNumSuccessors());
604 getBlockOperands()[index].set(block);
605}
606
607#ifndef NDEBUG
608/// Assert that the folded results (in case of values) have the same type as
609/// the results of the given op.
612 if (results.empty())
613 return;
614
615 for (auto [ofr, opResult] : llvm::zip_equal(results, op->getResults())) {
616 if (auto value = dyn_cast<Value>(ofr)) {
617 if (value.getType() != opResult.getType()) {
618 op->emitOpError() << "folder produced a value of incorrect type: "
619 << value.getType()
620 << ", expected: " << opResult.getType();
621 assert(false && "incorrect fold result type");
622 }
623 }
624 }
625}
626#endif // NDEBUG
627
628/// Attempt to fold this operation using the Op's registered foldHook.
631 // If we have a registered operation definition matching this one, use it to
632 // try to constant fold the operation.
633 if (succeeded(name.foldHook(this, operands, results))) {
634#ifndef NDEBUG
635 checkFoldResultTypes(this, results);
636#endif // NDEBUG
637 return success();
638 }
639
640 // Otherwise, fall back on the dialect hook to handle it.
641 Dialect *dialect = getDialect();
642 if (!dialect)
643 return failure();
644
645 auto *interface = dyn_cast<DialectFoldInterface>(dialect);
646 if (!interface)
647 return failure();
648
649 LogicalResult status = interface->fold(this, operands, results);
650#ifndef NDEBUG
651 if (succeeded(status))
652 checkFoldResultTypes(this, results);
653#endif // NDEBUG
654 return status;
655}
656
658 // Check if any operands are constants.
659 SmallVector<Attribute> constants;
660 constants.assign(getNumOperands(), Attribute());
661 for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
662 matchPattern(getOperand(i), m_Constant(&constants[i]));
663 return fold(constants, results);
664}
665
666/// Emit an error with the op name prefixed, like "'dim' op " which is
667/// convenient for verifiers.
669 return emitError() << "'" << getName() << "' op " << message;
670}
671
672//===----------------------------------------------------------------------===//
673// Operation Cloning
674//===----------------------------------------------------------------------===//
675
677 : cloneRegionsFlag(false), cloneOperandsFlag(false),
678 resultTypes(std::nullopt) {}
679
681 bool cloneRegions, bool cloneOperands,
682 std::optional<SmallVector<Type>> resultTypes)
683 : cloneRegionsFlag(cloneRegions), cloneOperandsFlag(cloneOperands),
684 resultTypes(resultTypes) {}
685
687 return CloneOptions().cloneRegions().cloneOperands().withResultTypes(
688 std::nullopt);
689}
690
692 cloneRegionsFlag = enable;
693 return *this;
694}
695
697 cloneOperandsFlag = enable;
698 return *this;
699}
700
702 std::optional<SmallVector<Type>> resultTypes) {
703 this->resultTypes = std::move(resultTypes);
704 return *this;
705}
706
707/// Create a deep copy of this operation but keep the operation regions empty.
708/// Operands are remapped using `mapper` (if present), and `mapper` is updated
709/// to contain the results. The `mapResults` flag specifies whether the results
710/// of the cloned operation should be added to the map.
712 return clone(mapper, CloneOptions::all().cloneRegions(false));
713}
714
716 IRMapping mapper;
717 return cloneWithoutRegions(mapper);
718}
719
720/// Create a deep copy of this operation, remapping any operands that use
721/// values outside of the operation using the map that is provided (leaving
722/// them alone if no entry is present). Replaces references to cloned
723/// sub-operations to the corresponding operation that is copied, and adds
724/// those mappings to the map.
725Operation *Operation::clone(IRMapping &mapper, const CloneOptions &options) {
726 SmallVector<Value, 8> operands;
727 SmallVector<Block *, 2> successors;
728
729 // Remap the operands.
730 if (options.shouldCloneOperands()) {
731 operands.reserve(getNumOperands());
732 for (auto opValue : getOperands())
733 operands.push_back(mapper.lookupOrDefault(opValue));
734 }
735
736 // Remap the successors.
737 successors.reserve(getNumSuccessors());
738 for (Block *successor : getSuccessors())
739 successors.push_back(mapper.lookupOrDefault(successor));
740
741 // Create the new operation.
742 auto *newOp = create(getLoc(), getName(),
743 options.resultTypesOr(getResultTypes()), operands, attrs,
744 getPropertiesStorage(), successors, getNumRegions());
745 mapper.map(this, newOp);
746
747 // Clone the regions.
748 if (options.shouldCloneRegions()) {
749 for (unsigned i = 0; i != numRegions; ++i)
750 getRegion(i).cloneInto(&newOp->getRegion(i), mapper);
751 }
752
753 // Remember the mapping of any results.
754 if (options.shouldCloneResults())
755 for (unsigned i = 0, e = getNumResults(); i != e; ++i)
756 mapper.map(getResult(i), newOp->getResult(i));
757
758 return newOp;
759}
760
762 IRMapping mapper;
763 return clone(mapper, options);
764}
765
766//===----------------------------------------------------------------------===//
767// OpState trait class.
768//===----------------------------------------------------------------------===//
769
770// The fallback for the parser is to try for a dialect operation parser.
771// Otherwise, reject the custom assembly form.
773 if (auto parseFn = result.name.getDialect()->getParseOperationHook(
774 result.name.getStringRef()))
775 return (*parseFn)(parser, result);
776 return parser.emitError(parser.getNameLoc(), "has no custom assembly form");
777}
778
779// The fallback for the printer is to try for a dialect operation printer.
780// Otherwise, it prints the generic form.
781void OpState::print(Operation *op, OpAsmPrinter &p, StringRef defaultDialect) {
782 if (auto printFn = op->getDialect()->getOperationPrinter(op)) {
783 printOpName(op, p, defaultDialect);
784 printFn(op, p);
785 } else {
786 p.printGenericOp(op);
787 }
788}
789
790/// Print an operation name, eliding the dialect prefix if necessary and doesn't
791/// lead to ambiguities.
793 StringRef defaultDialect) {
794 StringRef name = op->getName().getStringRef();
795 if (name.starts_with((defaultDialect + ".").str()) && name.count('.') == 1)
796 name = name.drop_front(defaultDialect.size() + 1);
797 p.getStream() << name;
798}
799
800/// Parse properties as a Attribute.
802 Attribute &result) {
803 if (succeeded(parser.parseOptionalLess())) { // The less is optional.
804 if (parser.parseAttribute(result) || parser.parseGreater())
805 return failure();
806 }
807 return success();
808}
809
810/// Print the properties as a Attribute with names not included within
811/// 'elidedProps'
813 ArrayRef<StringRef> elidedProps) {
814 if (!properties)
815 return;
816 auto dictAttr = dyn_cast_or_null<::mlir::DictionaryAttr>(properties);
817 if (dictAttr && !elidedProps.empty()) {
818 ArrayRef<NamedAttribute> attrs = dictAttr.getValue();
819 llvm::SmallDenseSet<StringRef> elidedAttrsSet(elidedProps.begin(),
820 elidedProps.end());
821 auto filteredAttrs =
822 llvm::make_filter_range(attrs, [&](NamedAttribute attr) {
823 return !elidedAttrsSet.contains(attr.getName().strref());
824 });
825 if (!filteredAttrs.empty()) {
826 p << "<{";
827 interleaveComma(filteredAttrs, p, [&](NamedAttribute attr) {
828 p.printNamedAttribute(attr);
829 });
830 p << "}>";
831 }
832 } else {
833 p << "<" << properties << ">";
834 }
835}
836
837/// Emit an error about fatal conditions with this operation, reporting up to
838/// any diagnostic handlers that may be listening.
840 return getOperation()->emitError(message);
841}
842
843/// Emit an error with the op name prefixed, like "'dim' op " which is
844/// convenient for verifiers.
846 return getOperation()->emitOpError(message);
847}
848
849/// Emit a warning about this operation, reporting up to any diagnostic
850/// handlers that may be listening.
852 return getOperation()->emitWarning(message);
853}
854
855/// Emit a remark about this operation, reporting up to any diagnostic
856/// handlers that may be listening.
858 return getOperation()->emitRemark(message);
859}
860
861//===----------------------------------------------------------------------===//
862// Op Trait implementations
863//===----------------------------------------------------------------------===//
864
865LogicalResult
868 // Nothing to fold if there are not at least 2 operands.
869 if (op->getNumOperands() < 2)
870 return failure();
871 // Move all constant operands to the end.
872 OpOperand *operandsBegin = op->getOpOperands().begin();
873 auto isNonConstant = [&](OpOperand &o) {
874 return !static_cast<bool>(operands[std::distance(operandsBegin, &o)]);
875 };
876 auto *firstConstantIt = llvm::find_if_not(op->getOpOperands(), isNonConstant);
877 auto *newConstantIt = std::stable_partition(
878 firstConstantIt, op->getOpOperands().end(), isNonConstant);
879 // Return success if the op was modified.
880 return success(firstConstantIt != newConstantIt);
881}
882
884 if (op->getNumOperands() == 1) {
885 auto *argumentOp = op->getOperand(0).getDefiningOp();
886 if (argumentOp && op->getName() == argumentOp->getName()) {
887 // Replace the outer operation output with the inner operation.
888 return op->getOperand(0);
889 }
890 } else if (op->getOperand(0) == op->getOperand(1)) {
891 return op->getOperand(0);
892 }
893
894 return {};
895}
896
898 auto *argumentOp = op->getOperand(0).getDefiningOp();
899 if (argumentOp && op->getName() == argumentOp->getName()) {
900 // Replace the outer involutions output with inner's input.
901 return argumentOp->getOperand(0);
902 }
903
904 return {};
905}
906
908 if (op->getNumOperands() != 0)
909 return op->emitOpError() << "requires zero operands";
910 return success();
911}
912
914 if (op->getNumOperands() != 1)
915 return op->emitOpError() << "requires a single operand";
916 return success();
917}
918
920 unsigned numOperands) {
921 if (op->getNumOperands() != numOperands) {
922 return op->emitOpError() << "expected " << numOperands
923 << " operands, but found " << op->getNumOperands();
924 }
925 return success();
926}
927
929 unsigned numOperands) {
930 if (op->getNumOperands() < numOperands)
931 return op->emitOpError()
932 << "expected " << numOperands << " or more operands, but found "
933 << op->getNumOperands();
934 return success();
935}
936
937/// If this is a vector type, or a tensor type, return the scalar element type
938/// that it is built around, otherwise return the type unmodified.
940 if (auto vec = llvm::dyn_cast<VectorType>(type))
941 return vec.getElementType();
942
943 // Look through tensor<vector<...>> to find the underlying element type.
944 if (auto tensor = llvm::dyn_cast<TensorType>(type))
945 return getTensorOrVectorElementType(tensor.getElementType());
946 return type;
947}
948
950 // FIXME: Add back check for no side effects on operation.
951 // Currently adding it would cause the shared library build
952 // to fail since there would be a dependency of IR on SideEffectInterfaces
953 // which is cyclical.
954 return success();
955}
956
958 // FIXME: Add back check for no side effects on operation.
959 // Currently adding it would cause the shared library build
960 // to fail since there would be a dependency of IR on SideEffectInterfaces
961 // which is cyclical.
962 return success();
963}
964
965LogicalResult
967 for (auto opType : op->getOperandTypes()) {
968 auto type = getTensorOrVectorElementType(opType);
969 if (!type.isSignlessIntOrIndex())
970 return op->emitOpError() << "requires an integer or index type";
971 }
972 return success();
973}
974
976 for (auto opType : op->getOperandTypes()) {
977 auto type = getTensorOrVectorElementType(opType);
978 if (!llvm::isa<FloatType>(type))
979 return op->emitOpError("requires a float type");
980 }
981 return success();
982}
983
985 // Zero or one operand always have the "same" type.
986 unsigned nOperands = op->getNumOperands();
987 if (nOperands < 2)
988 return success();
989
990 auto type = op->getOperand(0).getType();
991 for (auto opType : llvm::drop_begin(op->getOperandTypes(), 1))
992 if (opType != type)
993 return op->emitOpError() << "requires all operands to have the same type";
994 return success();
995}
996
998 if (op->getNumRegions() != 0)
999 return op->emitOpError() << "requires zero regions";
1000 return success();
1001}
1002
1004 if (op->getNumRegions() != 1)
1005 return op->emitOpError() << "requires one region";
1006 return success();
1007}
1008
1010 unsigned numRegions) {
1011 if (op->getNumRegions() != numRegions)
1012 return op->emitOpError() << "expected " << numRegions << " regions";
1013 return success();
1014}
1015
1017 unsigned numRegions) {
1018 if (op->getNumRegions() < numRegions)
1019 return op->emitOpError() << "expected " << numRegions << " or more regions";
1020 return success();
1021}
1022
1024 if (op->getNumResults() != 0)
1025 return op->emitOpError() << "requires zero results";
1026 return success();
1027}
1028
1030 if (op->getNumResults() != 1)
1031 return op->emitOpError() << "requires one result";
1032 return success();
1033}
1034
1036 unsigned numOperands) {
1037 if (op->getNumResults() != numOperands)
1038 return op->emitOpError() << "expected " << numOperands << " results";
1039 return success();
1040}
1041
1043 unsigned numOperands) {
1044 if (op->getNumResults() < numOperands)
1045 return op->emitOpError()
1046 << "expected " << numOperands << " or more results";
1047 return success();
1048}
1049
1051 if (failed(verifyAtLeastNOperands(op, 1)))
1052 return failure();
1053
1054 if (failed(verifyCompatibleShapes(op->getOperandTypes())))
1055 return op->emitOpError() << "requires the same shape for all operands";
1056
1057 return success();
1058}
1059
1061 if (failed(verifyAtLeastNOperands(op, 1)) ||
1062 failed(verifyAtLeastNResults(op, 1)))
1063 return failure();
1064
1066 types.append(llvm::to_vector<4>(op->getResultTypes()));
1067
1068 if (failed(verifyCompatibleShapes(types)))
1069 return op->emitOpError()
1070 << "requires the same shape for all operands and results";
1071
1072 return success();
1073}
1074
1076 if (failed(verifyAtLeastNOperands(op, 1)))
1077 return failure();
1078 auto elementType = getElementTypeOrSelf(op->getOperand(0));
1079
1080 for (auto operand : llvm::drop_begin(op->getOperands(), 1)) {
1081 if (getElementTypeOrSelf(operand) != elementType)
1082 return op->emitOpError("requires the same element type for all operands");
1083 }
1084
1085 return success();
1086}
1087
1088LogicalResult
1090 if (failed(verifyAtLeastNOperands(op, 1)) ||
1091 failed(verifyAtLeastNResults(op, 1)))
1092 return failure();
1093
1094 auto elementType = getElementTypeOrSelf(op->getResult(0));
1095
1096 // Verify result element type matches first result's element type.
1097 for (auto result : llvm::drop_begin(op->getResults(), 1)) {
1098 if (getElementTypeOrSelf(result) != elementType)
1099 return op->emitOpError(
1100 "requires the same element type for all operands and results");
1101 }
1102
1103 // Verify operand's element type matches first result's element type.
1104 for (auto operand : op->getOperands()) {
1105 if (getElementTypeOrSelf(operand) != elementType)
1106 return op->emitOpError(
1107 "requires the same element type for all operands and results");
1108 }
1109
1110 return success();
1111}
1112
1114 if (failed(verifyAtLeastNOperands(op, 1)) ||
1115 failed(verifyAtLeastNResults(op, 1)))
1116 return failure();
1117
1118 auto type = op->getResult(0).getType();
1119 auto elementType = getElementTypeOrSelf(type);
1120 Attribute encoding = nullptr;
1121 if (auto rankedType = dyn_cast<RankedTensorType>(type))
1122 encoding = rankedType.getEncoding();
1123 for (auto resultType : llvm::drop_begin(op->getResultTypes())) {
1124 if (getElementTypeOrSelf(resultType) != elementType ||
1125 failed(verifyCompatibleShape(resultType, type)))
1126 return op->emitOpError()
1127 << "requires the same type for all operands and results";
1128 if (encoding)
1129 if (auto rankedType = dyn_cast<RankedTensorType>(resultType);
1130 encoding != rankedType.getEncoding())
1131 return op->emitOpError()
1132 << "requires the same encoding for all operands and results";
1133 }
1134 for (auto opType : op->getOperandTypes()) {
1135 if (getElementTypeOrSelf(opType) != elementType ||
1136 failed(verifyCompatibleShape(opType, type)))
1137 return op->emitOpError()
1138 << "requires the same type for all operands and results";
1139 if (encoding)
1140 if (auto rankedType = dyn_cast<RankedTensorType>(opType);
1141 encoding != rankedType.getEncoding())
1142 return op->emitOpError()
1143 << "requires the same encoding for all operands and results";
1144 }
1145 return success();
1146}
1147
1149 if (failed(verifyAtLeastNOperands(op, 1)))
1150 return failure();
1151
1152 // delegate function that returns true if type is a shaped type with known
1153 // rank
1154 auto hasRank = [](const Type type) {
1155 if (auto shapedType = dyn_cast<ShapedType>(type))
1156 return shapedType.hasRank();
1157
1158 return false;
1159 };
1160
1161 auto rankedOperandTypes =
1162 llvm::make_filter_range(op->getOperandTypes(), hasRank);
1163 auto rankedResultTypes =
1164 llvm::make_filter_range(op->getResultTypes(), hasRank);
1165
1166 // If all operands and results are unranked, then no further verification.
1167 if (rankedOperandTypes.empty() && rankedResultTypes.empty())
1168 return success();
1169
1170 // delegate function that returns rank of shaped type with known rank
1171 auto getRank = [](const Type type) {
1172 return cast<ShapedType>(type).getRank();
1173 };
1174
1175 auto rank = !rankedOperandTypes.empty() ? getRank(*rankedOperandTypes.begin())
1176 : getRank(*rankedResultTypes.begin());
1177
1178 for (const auto type : rankedOperandTypes) {
1179 if (rank != getRank(type)) {
1180 return op->emitOpError("operands don't have matching ranks");
1181 }
1182 }
1183
1184 for (const auto type : rankedResultTypes) {
1185 if (rank != getRank(type)) {
1186 return op->emitOpError("result type has different rank than operands");
1187 }
1188 }
1189
1190 return success();
1191}
1192
1194 Block *block = op->getBlock();
1195 // Verify that the operation is at the end of the respective parent block.
1196 if (!block || &block->back() != op)
1197 return op->emitOpError("must be the last operation in the parent block");
1198 return success();
1199}
1200
1201static LogicalResult verifyTerminatorSuccessors(Operation *op) {
1202 auto *parent = op->getParentRegion();
1203
1204 // Verify that the operands lines up with the BB arguments in the successor.
1205 for (Block *succ : op->getSuccessors())
1206 if (succ->getParent() != parent)
1207 return op->emitError("reference to block defined in another region");
1208 return success();
1209}
1210
1212 if (op->getNumSuccessors() != 0) {
1213 return op->emitOpError("requires 0 successors but found ")
1214 << op->getNumSuccessors();
1215 }
1216 return success();
1217}
1218
1220 if (op->getNumSuccessors() != 1) {
1221 return op->emitOpError("requires 1 successor but found ")
1222 << op->getNumSuccessors();
1223 }
1224 return verifyTerminatorSuccessors(op);
1225}
1227 unsigned numSuccessors) {
1228 if (op->getNumSuccessors() != numSuccessors) {
1229 return op->emitOpError("requires ")
1230 << numSuccessors << " successors but found "
1231 << op->getNumSuccessors();
1232 }
1233 return verifyTerminatorSuccessors(op);
1234}
1236 unsigned numSuccessors) {
1237 if (op->getNumSuccessors() < numSuccessors) {
1238 return op->emitOpError("requires at least ")
1239 << numSuccessors << " successors but found "
1240 << op->getNumSuccessors();
1241 }
1242 return verifyTerminatorSuccessors(op);
1243}
1244
1246 for (auto resultType : op->getResultTypes()) {
1247 auto elementType = getTensorOrVectorElementType(resultType);
1248 bool isBoolType = elementType.isInteger(1);
1249 if (!isBoolType)
1250 return op->emitOpError() << "requires a bool result type";
1251 }
1252
1253 return success();
1254}
1255
1257 for (auto resultType : op->getResultTypes())
1258 if (!llvm::isa<FloatType>(getTensorOrVectorElementType(resultType)))
1259 return op->emitOpError() << "requires a floating point type";
1260
1261 return success();
1262}
1263
1264LogicalResult
1266 for (auto resultType : op->getResultTypes())
1267 if (!getTensorOrVectorElementType(resultType).isSignlessIntOrIndex())
1268 return op->emitOpError() << "requires an integer or index type";
1269 return success();
1270}
1271
1273 StringRef attrName,
1274 StringRef valueGroupName,
1275 size_t expectedCount) {
1276 auto sizeAttr = op->getAttrOfType<DenseI32ArrayAttr>(attrName);
1277 if (!sizeAttr)
1278 return op->emitOpError("requires dense i32 array attribute '")
1279 << attrName << "'";
1280
1281 ArrayRef<int32_t> sizes = sizeAttr.asArrayRef();
1282 if (llvm::any_of(sizes, [](int32_t element) { return element < 0; }))
1283 return op->emitOpError("'")
1284 << attrName << "' attribute cannot have negative elements";
1285
1286 size_t totalCount = llvm::sum_of(sizes, size_t(0));
1287 if (totalCount != expectedCount)
1288 return op->emitOpError()
1289 << valueGroupName << " count (" << expectedCount
1290 << ") does not match with the total size (" << totalCount
1291 << ") specified in attribute '" << attrName << "'";
1292 return success();
1293}
1294
1296 StringRef attrName) {
1297 return verifyValueSizeAttr(op, attrName, "operand", op->getNumOperands());
1298}
1299
1301 StringRef attrName) {
1302 return verifyValueSizeAttr(op, attrName, "result", op->getNumResults());
1303}
1304
1306 for (Region &region : op->getRegions()) {
1307 if (region.empty())
1308 continue;
1309
1310 if (region.getNumArguments() != 0) {
1311 if (op->getNumRegions() > 1)
1312 return op->emitOpError("region #")
1313 << region.getRegionNumber() << " should have no arguments";
1314 return op->emitOpError("region should have no arguments");
1315 }
1316 }
1317 return success();
1318}
1319
1321 auto isMappableType = llvm::IsaPred<VectorType, TensorType>;
1322 auto resultMappableTypes =
1323 llvm::filter_to_vector<1>(op->getResultTypes(), isMappableType);
1324 auto operandMappableTypes =
1325 llvm::filter_to_vector<2>(op->getOperandTypes(), isMappableType);
1326
1327 // If the op only has scalar operand/result types, then we have nothing to
1328 // check.
1329 if (resultMappableTypes.empty() && operandMappableTypes.empty())
1330 return success();
1331
1332 if (!resultMappableTypes.empty() && operandMappableTypes.empty())
1333 return op->emitOpError("if a result is non-scalar, then at least one "
1334 "operand must be non-scalar");
1335
1336 assert(!operandMappableTypes.empty());
1337
1338 if (resultMappableTypes.empty())
1339 return op->emitOpError("if an operand is non-scalar, then there must be at "
1340 "least one non-scalar result");
1341
1342 if (resultMappableTypes.size() != op->getNumResults())
1343 return op->emitOpError(
1344 "if an operand is non-scalar, then all results must be non-scalar");
1345
1346 SmallVector<Type, 4> types = llvm::to_vector<2>(
1347 llvm::concat<Type>(operandMappableTypes, resultMappableTypes));
1348 TypeID expectedBaseTy = types.front().getTypeID();
1349 if (!llvm::all_of(types,
1350 [&](Type t) { return t.getTypeID() == expectedBaseTy; }) ||
1351 failed(verifyCompatibleShapes(types))) {
1352 return op->emitOpError() << "all non-scalar operands/results must have the "
1353 "same shape and base type";
1354 }
1355
1356 return success();
1357}
1358
1359/// Check for any values used by operations regions attached to the
1360/// specified "IsIsolatedFromAbove" operation defined outside of it.
1362 assert(isolatedOp->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
1363 "Intended to check IsolatedFromAbove ops");
1364
1365 // List of regions to analyze. Each region is processed independently, with
1366 // respect to the common `limit` region, so we can look at them in any order.
1367 // Therefore, use a simple vector and push/pop back the current region.
1368 SmallVector<Region *, 8> pendingRegions;
1369 for (auto &region : isolatedOp->getRegions()) {
1370 pendingRegions.push_back(&region);
1371
1372 // Traverse all operations in the region.
1373 while (!pendingRegions.empty()) {
1374 for (Operation &op : pendingRegions.pop_back_val()->getOps()) {
1375 for (Value operand : op.getOperands()) {
1376 // Check that any value that is used by an operation is defined in the
1377 // same region as either an operation result.
1378 auto *operandRegion = operand.getParentRegion();
1379 if (!operandRegion)
1380 return op.emitError("operation's operand is unlinked");
1381 if (!region.isAncestor(operandRegion)) {
1382 return op.emitOpError("using value defined outside the region")
1383 .attachNote(isolatedOp->getLoc())
1384 << "required by region isolation constraints";
1385 }
1386 }
1387
1388 // Schedule any regions in the operation for further checking. Don't
1389 // recurse into other IsolatedFromAbove ops, because they will check
1390 // themselves.
1391 if (op.getNumRegions() &&
1392 !op.hasTrait<OpTrait::IsIsolatedFromAbove>()) {
1393 for (Region &subRegion : op.getRegions())
1394 pendingRegions.push_back(&subRegion);
1395 }
1396 }
1397 }
1398 }
1399
1400 return success();
1401}
1402
1407
1408//===----------------------------------------------------------------------===//
1409// Misc. utils
1410//===----------------------------------------------------------------------===//
1411
1412/// Insert an operation, generated by `buildTerminatorOp`, at the end of the
1413/// region's only block if it does not have a terminator already. If the region
1414/// is empty, insert a new block first. `buildTerminatorOp` should return the
1415/// terminator operation to insert.
1417 Region &region, OpBuilder &builder, Location loc,
1418 function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp) {
1419 OpBuilder::InsertionGuard guard(builder);
1420 if (region.empty())
1421 builder.createBlock(&region);
1422
1423 Block &block = region.back();
1424 if (!block.empty() && block.back().hasTrait<OpTrait::IsTerminator>())
1425 return;
1426
1427 builder.setInsertionPointToEnd(&block);
1428 builder.insert(buildTerminatorOp(builder, loc));
1429}
1430
1431/// Create a simple OpBuilder and forward to the OpBuilder version of this
1432/// function.
1434 Region &region, Builder &builder, Location loc,
1435 function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp) {
1436 OpBuilder opBuilder(builder.getContext());
1437 ensureRegionTerminator(region, opBuilder, loc, buildTerminatorOp);
1438}
return success()
static LogicalResult verifyTerminatorSuccessors(Operation *op)
static Type getTensorOrVectorElementType(Type type)
If this is a vector type, or a tensor type, return the scalar element type that it is built around,...
static void checkFoldResultTypes(Operation *op, SmallVectorImpl< OpFoldResult > &results)
Assert that the folded results (in case of values) have the same type as the results of the given op.
static std::string diag(const llvm::Value &value)
false
Parses a map_entries map type from a string format back into its numeric value.
static llvm::ManagedStatic< PassManagerOptions > options
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseOptionalLess()=0
Parse a '<' token if present.
virtual ParseResult parseGreater()=0
Parse a '>' token.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
virtual raw_ostream & getStream() const
Return the raw output stream used by this printer.
virtual void printNamedAttribute(NamedAttribute attr)
Print the given named attribute.
Attributes are known-constant values of operations.
Definition Attributes.h:25
A block operand represents an operand that holds a reference to a Block, e.g.
This class provides an abstraction over the different types of ranges over Blocks.
Block represents an ordered list of Operations.
Definition Block.h:33
void recomputeOpOrder()
Recomputes the ordering of child operations within the block.
Definition Block.cpp:136
bool empty()
Definition Block.h:158
Operation & front()
Definition Block.h:163
Operation & back()
Definition Block.h:162
void invalidateOpOrder()
Invalidates the current ordering of operations.
Definition Block.cpp:107
static OpListType Block::* getSublistAccess(Operation *)
Returns pointer to member of operation list.
Definition Block.h:403
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
MLIRContext * getContext() const
Definition Builders.h:56
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition Dialect.h:38
virtual llvm::unique_function< void(Operation *, OpAsmPrinter &printer)> getOperationPrinter(Operation *op) const
Print an operation registered to this dialect.
Definition Dialect.cpp:87
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
Definition IRMapping.h:65
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
This class represents a diagnostic that is inflight and set to be reported.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
DictionaryAttr getDictionary(MLIRContext *context) const
Return a dictionary attribute for the underlying dictionary.
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
StringAttr getName() const
Return the name of the attribute.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printGenericOp(Operation *op, bool printOpName=true)=0
Print the entire operation with the default generic assembly form.
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:350
This class helps build Operations.
Definition Builders.h:209
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:434
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:438
Operation * insert(Operation *op)
Insert the given operation at the current insertion point and return it.
Definition Builders.cpp:425
This class represents a single result from folding an operation.
This class represents an operand of an operation.
Definition Value.h:257
Set of flags used to control the behavior of the various IR print methods (e.g.
Operation * getOperation()
Return the operation that this refers to.
static void genericPrintProperties(OpAsmPrinter &p, Attribute properties, ArrayRef< StringRef > elidedProps={})
Print the properties as a Attribute with names not included within 'elidedProps'.
void print(raw_ostream &os, OpPrintingFlags flags={})
Print the operation to the given stream.
static void printOpName(Operation *op, OpAsmPrinter &p, StringRef defaultDialect)
Print an operation name, eliding the dialect prefix if necessary.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
InFlightDiagnostic emitWarning(const Twine &message={})
Emit a warning about this operation, reporting up to any diagnostic handlers that may be listening.
static ParseResult genericParseProperties(OpAsmParser &parser, Attribute &result)
Parse properties as a Attribute.
static ParseResult parse(OpAsmParser &parser, OperationState &result)
Parse the custom form of an operation.
InFlightDiagnostic emitRemark(const Twine &message={})
Emit a remark about this operation, reporting up to any diagnostic handlers that may be listening.
This class provides the API for ops that are known to be isolated from above.
This class provides the API for ops that are known to be terminators.
This class provides the API for ops that are known to have no SSA operand.
Simple wrapper around a void* in order to express generically how to pass in op properties through AP...
void populateInherentAttrs(Operation *op, NamedAttrList &attrs) const
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
void setInherentAttr(Operation *op, StringAttr name, Attribute value) const
std::optional< Attribute > getInherentAttr(Operation *op, StringRef name) const
Lookup an inherent attribute by name, this method isn't recommended and may be removed in the future.
void initOpProperties(OpaqueProperties storage, OpaqueProperties init) const
Initialize the op properties.
Class encompassing various options related to cloning an operation.
Definition Operation.h:144
CloneOptions()
Default constructs an option with all flags set to false.
static CloneOptions all()
Returns an instance such that all elements of the operation are cloned.
CloneOptions & cloneRegions(bool enable=true)
Configures whether cloning should traverse into any of the regions of the operation.
CloneOptions & withResultTypes(std::optional< SmallVector< Type > > resultTypes)
Configures different result types to use for the cloned operation.
CloneOptions & cloneOperands(bool enable=true)
Configures whether operation' operands should be cloned.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
void setInherentAttr(StringAttr name, Attribute value)
Set an inherent attribute by name.
void replaceUsesOfWith(Value from, Value to)
Replace any uses of 'from' with 'to' within this operation.
MutableArrayRef< BlockOperand > getBlockOperands()
Definition Operation.h:724
DictionaryAttr getAttrDictionary()
Return all of the attributes on this operation as a DictionaryAttr.
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition Operation.h:241
LogicalResult fold(ArrayRef< Attribute > operands, SmallVectorImpl< OpFoldResult > &results)
Attempt to fold this operation with the specified constant operand values.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition Operation.h:715
bool use_empty()
Returns true if this operation has no uses.
Definition Operation.h:881
Value getOperand(unsigned idx)
Definition Operation.h:379
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:778
Operation * cloneWithoutRegions()
Create a partial copy of this operation without traversing into attached regions.
void insertOperands(unsigned index, ValueRange operands)
Insert the given operands into the operand list at the given 'index'.
void dropAllUses()
Drop all uses of results of this operation.
Definition Operation.h:863
AttrClass getAttrOfType(StringAttr name)
Definition Operation.h:579
void setAttrs(DictionaryAttr newAttrs)
Set the attributes from a dictionary on this operation.
unsigned getNumSuccessors()
Definition Operation.h:735
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
void dropAllReferences()
This drops all operand uses from this operation, which is an essential step in breaking cyclic depend...
InFlightDiagnostic emitWarning(const Twine &message={})
Emit a warning about this operation, reporting up to any diagnostic handlers that may be listening.
bool mightHaveTrait()
Returns true if the operation might have the provided trait.
Definition Operation.h:786
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:234
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:436
std::optional< Attribute > getInherentAttr(StringRef name)
Access an inherent attribute by name: returns an empty optional if there is no inherent attribute wit...
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:703
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:244
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:255
MutableArrayRef< OpOperand > getOpOperands()
Definition Operation.h:412
std::optional< RegisteredOperationName > getRegisteredInfo()
If this operation has a registered operation description, return it.
Definition Operation.h:123
void dropAllDefinedValueUses()
Drop uses of all values defined by this operation or its nested regions.
unsigned getNumOperands()
Definition Operation.h:375
static Operation * create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, OpaqueProperties properties, BlockRange successors, unsigned numRegions)
Create a new Operation with the specific fields.
Definition Operation.cpp:67
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
void populateDefaultAttrs()
Sets default attributes on unset attributes.
Definition Operation.h:692
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
void destroy()
Destroys this operation and its subclass data.
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:119
void remove()
Remove the operation from its parent block, but don't delete it.
LogicalResult setPropertiesFromAttribute(Attribute attr, function_ref< InFlightDiagnostic()> emitError)
Set the properties from the provided attribute.
operand_type_range getOperandTypes()
Definition Operation.h:426
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:706
friend class Block
Definition Operation.h:1105
result_type_range getResultTypes()
Definition Operation.h:457
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:407
void setSuccessor(Block *block, unsigned index)
void moveBefore(Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
void setOperands(ValueRange operands)
Replace the current operands of this operation with the ones provided in 'operands'.
user_range getUsers()
Returns a range of all users.
Definition Operation.h:902
SuccessorRange getSuccessors()
Definition Operation.h:732
result_range getResults()
Definition Operation.h:444
int getPropertiesStorageSize() const
Returns the properties storage size.
Definition Operation.h:925
Operation * clone(IRMapping &mapper, const CloneOptions &options=CloneOptions::all())
Create a deep copy of this operation, remapping any operands that use values outside of the operation...
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition Operation.h:251
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:237
InFlightDiagnostic emitRemark(const Twine &message={})
Emit a remark about this operation, reporting up to any diagnostic handlers that may be listening.
friend class Value
Definition Operation.h:1108
void moveAfter(Operation *existingOp)
Unlink this operation from its current block and insert it right after existingOp which may be in the...
llvm::hash_code hashProperties()
Compute a hash for the op properties (if any).
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
OpaqueProperties getPropertiesStorage()
Returns the properties storage.
Definition Operation.h:929
void erase()
Remove this operation from its parent block and delete it.
void copyProperties(OpaqueProperties rhs)
Copy properties from an existing other properties object.
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:433
This class provides an abstraction over the different types of ranges over Regions.
Definition Region.h:357
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & back()
Definition Region.h:64
bool empty()
Definition Region.h:60
void cloneInto(Region *dest, IRMapping &mapper)
Clone the internal blocks from this region into dest.
Definition Region.cpp:70
void takeBody(Region &other)
Takes body of another region (that region will have no body after this operation completes).
Definition Region.h:252
This class provides an efficient unique identifier for a specific C++ type.
Definition TypeID.h:107
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:37
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
TypeID getTypeID()
Return a unique identifier for the concrete type.
Definition Types.h:101
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:387
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
This class handles the management of operation operands.
This class provides the implementation for an operation result whose index cannot be represented "inl...
Definition Value.h:404
OpFoldResult foldIdempotent(Operation *op)
LogicalResult verifyResultsAreFloatLike(Operation *op)
LogicalResult verifyAtLeastNResults(Operation *op, unsigned numOperands)
LogicalResult verifyIsIdempotent(Operation *op)
LogicalResult verifyOperandsAreSignlessIntegerLike(Operation *op)
LogicalResult verifyNOperands(Operation *op, unsigned numOperands)
LogicalResult verifyNoRegionArguments(Operation *op)
LogicalResult verifyResultsAreSignlessIntegerLike(Operation *op)
LogicalResult verifyIsInvolution(Operation *op)
LogicalResult verifyOperandsAreFloatLike(Operation *op)
LogicalResult foldCommutative(Operation *op, ArrayRef< Attribute > operands, SmallVectorImpl< OpFoldResult > &results)
LogicalResult verifyZeroRegions(Operation *op)
LogicalResult verifyNSuccessors(Operation *op, unsigned numSuccessors)
LogicalResult verifyOperandSizeAttr(Operation *op, StringRef sizeAttrName)
LogicalResult verifyAtLeastNRegions(Operation *op, unsigned numRegions)
LogicalResult verifyValueSizeAttr(Operation *op, StringRef attrName, StringRef valueGroupName, size_t expectedCount)
LogicalResult verifyZeroResults(Operation *op)
LogicalResult verifySameOperandsAndResultType(Operation *op)
LogicalResult verifySameOperandsShape(Operation *op)
LogicalResult verifyAtLeastNSuccessors(Operation *op, unsigned numSuccessors)
LogicalResult verifyIsTerminator(Operation *op)
LogicalResult verifyAtLeastNOperands(Operation *op, unsigned numOperands)
LogicalResult verifyZeroOperands(Operation *op)
LogicalResult verifyElementwise(Operation *op)
LogicalResult verifyOneRegion(Operation *op)
LogicalResult verifySameOperandsAndResultRank(Operation *op)
LogicalResult verifyOneOperand(Operation *op)
LogicalResult verifyIsIsolatedFromAbove(Operation *op)
Check for any values used by operations regions attached to the specified "IsIsolatedFromAbove" opera...
LogicalResult verifyZeroSuccessors(Operation *op)
LogicalResult verifySameOperandsElementType(Operation *op)
LogicalResult verifyOneSuccessor(Operation *op)
LogicalResult verifySameOperandsAndResultElementType(Operation *op)
OpFoldResult foldInvolution(Operation *op)
LogicalResult verifyResultsAreBoolLike(Operation *op)
LogicalResult verifyNResults(Operation *op, unsigned numOperands)
LogicalResult verifyResultSizeAttr(Operation *op, StringRef sizeAttrName)
LogicalResult verifyNRegions(Operation *op, unsigned numRegions)
LogicalResult verifyOneResult(Operation *op)
LogicalResult verifySameTypeOperands(Operation *op)
LogicalResult verifySameOperandsAndResultShape(Operation *op)
bool hasElementwiseMappableTraits(Operation *op)
Together, Elementwise, Scalarizable, Vectorizable, and Tensorizable provide an easy way for scalar op...
OpProperties
This is a "tag" used for mapping the properties storage in llvm::TrailingObjects.
Definition Operation.h:28
void ensureRegionTerminator(Region &region, OpBuilder &builder, Location loc, function_ref< Operation *(OpBuilder &, Location)> buildTerminatorOp)
Insert an operation, generated by buildTerminatorOp, at the end of the region's only block if it does...
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
InFlightDiagnostic emitWarning(Location loc)
Utility method to emit a warning message using this location.
LogicalResult verifyCompatibleShapes(TypeRange types1, TypeRange types2)
Returns success if the given two arrays have the same number of elements and each pair wise entries h...
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
detail::DenseArrayAttrImpl< int32_t > DenseI32ArrayAttr
InFlightDiagnostic emitRemark(Location loc)
Utility method to emit a remark message using this location.
LogicalResult verifyCompatibleShape(ArrayRef< int64_t > shape1, ArrayRef< int64_t > shape2)
Returns success if the given two shapes are compatible.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
llvm::function_ref< Fn > function_ref
Definition LLVM.h:144
void removeNodeFromList(Operation *op)
This is a trait method invoked when an operation is removed from a block.
void transferNodesFromList(ilist_traits< Operation > &otherList, op_iterator first, op_iterator last)
This is a trait method invoked when an operation is moved from one block to another.
void addNodeToList(Operation *op)
This is a trait method invoked when an operation is added to a block.
static void deleteNode(Operation *op)
simple_ilist< Operation >::iterator op_iterator
This trait tags element-wise ops on vectors or tensors.
This trait tags Elementwise operatons that can be systematically scalarized.
This trait tags Elementwise operatons that can be systematically tensorized.
This trait tags Elementwise operatons that can be systematically vectorized.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
SmallVector< Block *, 1 > successors
Successors of this operation and their respective operands.
SmallVector< Value, 4 > operands
MLIRContext * getContext() const
Get the context held by this operation state.
SmallVector< std::unique_ptr< Region >, 1 > regions
Regions that the op will hold.
Attribute propertiesAttr
This Attribute is used to opaquely construct the properties of the operation.
SmallVector< Type, 4 > types
Types of the results of this operation.
This class provides the implementation for an operation result whose index can be represented "inline...
Definition Value.h:387