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, PropertyRef properties,
54 BlockRange successors, RegionRange regions) {
55 unsigned numRegions = regions.size();
56 Operation *op =
57 create(location, name, resultTypes, operands, std::move(attributes),
58 properties, successors, numRegions);
59 for (unsigned i = 0; i < numRegions; ++i)
60 if (regions[i])
61 op->getRegion(i).takeBody(*regions[i]);
62 return op;
63}
64
65/// Create a new Operation with the specific fields.
66Operation *Operation::create(Location location, OperationName name,
67 TypeRange resultTypes, ValueRange operands,
68 NamedAttrList &&attributes, PropertyRef properties,
69 BlockRange successors, unsigned numRegions) {
70 // Populate default attributes.
71 name.populateDefaultAttrs(attributes);
72
73 return create(location, name, resultTypes, operands,
74 attributes.getDictionary(location.getContext()), properties,
75 successors, numRegions);
76}
77
78/// Overload of create that takes an existing DictionaryAttr to avoid
79/// unnecessarily uniquing a list of attributes.
80Operation *Operation::create(Location location, OperationName name,
81 TypeRange resultTypes, ValueRange operands,
82 DictionaryAttr attributes, PropertyRef properties,
83 BlockRange successors, unsigned numRegions) {
84 assert(llvm::all_of(resultTypes, [](Type t) { return t; }) &&
85 "unexpected null result type");
86
87 // We only need to allocate additional memory for a subset of results.
88 unsigned numTrailingResults = OpResult::getNumTrailing(resultTypes.size());
89 unsigned numInlineResults = OpResult::getNumInline(resultTypes.size());
90 unsigned numSuccessors = successors.size();
91 unsigned numOperands = operands.size();
92 unsigned numResults = resultTypes.size();
93 int opPropertiesAllocSize = llvm::alignTo<8>(name.getOpPropertyByteSize());
94
95 // If the operation is known to have no operands, don't allocate an operand
96 // storage.
97 bool needsOperandStorage =
98 operands.empty() ? !name.hasTrait<OpTrait::ZeroOperands>() : true;
99
100 // Compute the byte size for the operation and the operand storage. This takes
101 // into account the size of the operation, its trailing objects, and its
102 // prefixed objects.
103 size_t byteSize =
106 needsOperandStorage ? 1 : 0, opPropertiesAllocSize, numSuccessors,
107 numRegions, numOperands);
108 size_t prefixByteSize = llvm::alignTo(
109 Operation::prefixAllocSize(numTrailingResults, numInlineResults),
110 alignof(Operation));
111 char *mallocMem = reinterpret_cast<char *>(malloc(byteSize + prefixByteSize));
112 void *rawMem = mallocMem + prefixByteSize;
113
114 // Create the new Operation.
115 Operation *op = ::new (rawMem) Operation(
116 location, name, numResults, numSuccessors, numRegions,
117 opPropertiesAllocSize, attributes, properties, needsOperandStorage);
118
119 assert((numSuccessors == 0 || op->mightHaveTrait<OpTrait::IsTerminator>()) &&
120 "unexpected successors in a non-terminator operation");
121
122 // Initialize the results.
123 auto resultTypeIt = resultTypes.begin();
124 for (unsigned i = 0; i < numInlineResults; ++i, ++resultTypeIt)
125 new (op->getInlineOpResult(i)) detail::InlineOpResult(*resultTypeIt, i);
126 for (unsigned i = 0; i < numTrailingResults; ++i, ++resultTypeIt) {
127 new (op->getOutOfLineOpResult(i))
128 detail::OutOfLineOpResult(*resultTypeIt, i);
129 }
130
131 // Initialize the regions.
132 for (unsigned i = 0; i != numRegions; ++i)
133 new (&op->getRegion(i)) Region(op);
134
135 // Initialize the operands.
136 if (needsOperandStorage) {
137 new (&op->getOperandStorage()) detail::OperandStorage(
138 op, op->getTrailingObjects<OpOperand>(), operands);
139 }
140
141 // Initialize the successors.
142 auto blockOperands = op->getBlockOperands();
143 for (unsigned i = 0; i != numSuccessors; ++i)
144 new (&blockOperands[i]) BlockOperand(op, successors[i]);
145
146 // This must be done after properties are initialized.
147 op->setAttrs(attributes);
148
149 return op;
150}
151
152Operation::Operation(Location location, OperationName name, unsigned numResults,
153 unsigned numSuccessors, unsigned numRegions,
154 int fullPropertiesStorageSize, DictionaryAttr attributes,
155 PropertyRef properties, bool hasOperandStorage)
156 : location(location), numResults(numResults), numSuccs(numSuccessors),
157 numRegions(numRegions), hasOperandStorage(hasOperandStorage),
158 propertiesStorageSize((fullPropertiesStorageSize + 7) / 8), name(name) {
159 assert(attributes && "unexpected null attribute dictionary");
160 assert(fullPropertiesStorageSize <= propertiesCapacity &&
161 "Properties size overflow");
162#ifndef NDEBUG
163 if (!getDialect() && !getContext()->allowsUnregisteredDialects())
164 llvm::report_fatal_error(
165 name.getStringRef() +
166 " created with unregistered dialect. If this is intended, please call "
167 "allowUnregisteredDialects() on the MLIRContext, or use "
168 "-allow-unregistered-dialect with the MLIR tool used.");
169#endif
170 if (fullPropertiesStorageSize)
171 name.initOpProperties(getPropertiesStorage(), properties);
172}
173
174// Operations are deleted through the destroy() member because they are
175// allocated via malloc.
176Operation::~Operation() {
177 assert(block == nullptr && "operation destroyed but still in a block");
178#ifndef NDEBUG
179 if (!use_empty()) {
180 {
182 emitOpError("operation destroyed but still has uses");
183 for (Operation *user : getUsers())
184 diag.attachNote(user->getLoc()) << "- use: " << *user << "\n";
185 }
186 llvm::report_fatal_error("operation destroyed but still has uses");
187 }
188#endif
189 // Explicitly run the destructors for the operands.
190 if (hasOperandStorage)
191 getOperandStorage().~OperandStorage();
192
193 // Explicitly run the destructors for the successors.
194 for (auto &successor : getBlockOperands())
195 successor.~BlockOperand();
196
197 // Explicitly destroy the regions.
198 for (auto &region : getRegions())
199 region.~Region();
200 if (propertiesStorageSize)
201 name.destroyOpProperties(getPropertiesStorage());
202}
203
204/// Destroy this operation or one of its subclasses.
206 // Operations may have additional prefixed allocation, which needs to be
207 // accounted for here when computing the address to free.
208 char *rawMem = reinterpret_cast<char *>(this) -
209 llvm::alignTo(prefixAllocSize(), alignof(Operation));
210 this->~Operation();
211 free(rawMem);
212}
213
214/// Return true if this operation is a proper ancestor of the `other`
215/// operation.
216bool Operation::isProperAncestor(Operation *other) {
217 while ((other = other->getParentOp()))
218 if (this == other)
219 return true;
220 return false;
221}
222
223/// Replace any uses of 'from' with 'to' within this operation.
225 if (from == to)
226 return;
227 for (auto &operand : getOpOperands())
228 if (operand.get() == from)
229 operand.set(to);
230}
231
232/// Replace the current operands of this operation with the ones provided in
233/// 'operands'.
235 if (LLVM_LIKELY(hasOperandStorage))
236 return getOperandStorage().setOperands(this, operands);
237 assert(operands.empty() && "setting operands without an operand storage");
238}
239
240/// Replace the operands beginning at 'start' and ending at 'start' + 'length'
241/// with the ones provided in 'operands'. 'operands' may be smaller or larger
242/// than the range pointed to by 'start'+'length'.
243void Operation::setOperands(unsigned start, unsigned length,
244 ValueRange operands) {
245 assert((start + length) <= getNumOperands() &&
246 "invalid operand range specified");
247 if (LLVM_LIKELY(hasOperandStorage))
248 return getOperandStorage().setOperands(this, start, length, operands);
249 assert(operands.empty() && "setting operands without an operand storage");
250}
251
252/// Insert the given operands into the operand list at the given 'index'.
254 if (LLVM_LIKELY(hasOperandStorage))
255 return setOperands(index, /*length=*/0, operands);
256 assert(operands.empty() && "inserting operands without an operand storage");
257}
258
259//===----------------------------------------------------------------------===//
260// Diagnostics
261//===----------------------------------------------------------------------===//
262
263/// Emit an error about fatal conditions with this operation, reporting up to
264/// any diagnostic handlers that may be listening.
267 if (getContext()->shouldPrintOpOnDiagnostic()) {
268 diag.attachNote(getLoc())
269 .append("see current operation: ")
270 .appendOp(*this, OpPrintingFlags().printGenericOpForm());
271 }
272 return diag;
273}
274
275/// Emit a warning about this operation, reporting up to any diagnostic
276/// handlers that may be listening.
279 if (getContext()->shouldPrintOpOnDiagnostic())
280 diag.attachNote(getLoc()) << "see current operation: " << *this;
281 return diag;
282}
283
284/// Emit a remark about this operation, reporting up to any diagnostic
285/// handlers that may be listening.
288 if (getContext()->shouldPrintOpOnDiagnostic())
289 diag.attachNote(getLoc()) << "see current operation: " << *this;
290 return diag;
291}
292
295 NamedAttrList attrsList = attrs;
296 getName().populateInherentAttrs(this, attrsList);
297 return attrsList.getDictionary(getContext());
298 }
299 return attrs;
300}
301
302void Operation::setAttrs(DictionaryAttr newAttrs) {
303 assert(newAttrs && "expected valid attribute dictionary");
305 // We're spliting the providing DictionaryAttr by removing the inherentAttr
306 // which will be stored in the properties.
307 SmallVector<NamedAttribute> discardableAttrs;
308 discardableAttrs.reserve(newAttrs.size());
309 for (NamedAttribute attr : newAttrs) {
310 if (getInherentAttr(attr.getName()))
311 setInherentAttr(attr.getName(), attr.getValue());
312 else
313 discardableAttrs.push_back(attr);
314 }
315 if (discardableAttrs.size() != newAttrs.size())
316 newAttrs = DictionaryAttr::get(getContext(), discardableAttrs);
317 }
318 attrs = newAttrs;
319}
322 // We're spliting the providing array of attributes by removing the
323 // inherentAttr which will be stored in the properties.
324 SmallVector<NamedAttribute> discardableAttrs;
325 discardableAttrs.reserve(newAttrs.size());
326 for (NamedAttribute attr : newAttrs) {
327 if (getInherentAttr(attr.getName()))
328 setInherentAttr(attr.getName(), attr.getValue());
329 else
330 discardableAttrs.push_back(attr);
331 }
332 attrs = DictionaryAttr::get(getContext(), discardableAttrs);
333 return;
334 }
335 attrs = DictionaryAttr::get(getContext(), newAttrs);
336}
337
338std::optional<Attribute> Operation::getInherentAttr(StringRef name) {
339 return getName().getInherentAttr(this, name);
340}
341
342void Operation::setInherentAttr(StringAttr name, Attribute value) {
343 getName().setInherentAttr(this, name, value);
344}
345
347 std::optional<RegisteredOperationName> info = getRegisteredInfo();
348 if (LLVM_UNLIKELY(!info))
349 return *getPropertiesStorage().as<Attribute *>();
350 return info->getOpPropertiesAsAttribute(this);
351}
354 std::optional<RegisteredOperationName> info = getRegisteredInfo();
355 if (LLVM_UNLIKELY(!info)) {
356 *getPropertiesStorage().as<Attribute *>() = attr;
357 return success();
358 }
359 return info->setOpPropertiesFromAttribute(
360 this->getName(), this->getPropertiesStorage(), attr, emitError);
361}
362
364 name.copyOpProperties(getPropertiesStorage(), rhs);
365}
366
367llvm::hash_code Operation::hashProperties() {
368 return name.hashOpProperties(getPropertiesStorage());
369}
370
371//===----------------------------------------------------------------------===//
372// Operation Ordering
373//===----------------------------------------------------------------------===//
374
375/// Given an operation 'other' that is within the same parent block, return
376/// whether the current operation is before 'other' in the operation list
377/// of the parent block.
378/// Note: This function has an average complexity of O(1), but worst case may
379/// take O(N) where N is the number of operations within the parent block.
380bool Operation::isBeforeInBlock(Operation *other) {
381 assert(block && "Operations without parent blocks have no order.");
382 assert(other && other->block == block &&
383 "Expected other operation to have the same parent block.");
384 // If the order of the block is already invalid, directly recompute the
385 // parent.
386 if (!block->isOpOrderValid()) {
387 block->recomputeOpOrder();
388 } else {
389 // Update the order either operation if necessary.
390 updateOrderIfNecessary();
391 other->updateOrderIfNecessary();
392 }
393
394 return orderIndex < other->orderIndex;
395}
396
397/// Update the order index of this operation of this operation if necessary,
398/// potentially recomputing the order of the parent block.
399void Operation::updateOrderIfNecessary() {
400 assert(block && "expected valid parent");
401
402 // If the order is valid for this operation there is nothing to do.
403 if (hasValidOrder() || llvm::hasSingleElement(*block))
404 return;
405 Operation *blockFront = &block->front();
406 Operation *blockBack = &block->back();
407
408 // This method is expected to only be invoked on blocks with more than one
409 // operation.
410 assert(blockFront != blockBack && "expected more than one operation");
411
412 // If the operation is at the end of the block.
413 if (this == blockBack) {
414 Operation *prevNode = getPrevNode();
415 if (!prevNode->hasValidOrder())
416 return block->recomputeOpOrder();
417
418 // Add the stride to the previous operation.
419 orderIndex = prevNode->orderIndex + kOrderStride;
420 return;
421 }
422
423 // If this is the first operation try to use the next operation to compute the
424 // ordering.
425 if (this == blockFront) {
426 Operation *nextNode = getNextNode();
427 if (!nextNode->hasValidOrder())
428 return block->recomputeOpOrder();
429 // There is no order to give this operation.
430 if (nextNode->orderIndex == 0)
431 return block->recomputeOpOrder();
432
433 // If we can't use the stride, just take the middle value left. This is safe
434 // because we know there is at least one valid index to assign to.
435 if (nextNode->orderIndex <= kOrderStride)
436 orderIndex = (nextNode->orderIndex / 2);
437 else
438 orderIndex = kOrderStride;
439 return;
440 }
441
442 // Otherwise, this operation is between two others. Place this operation in
443 // the middle of the previous and next if possible.
444 Operation *prevNode = getPrevNode(), *nextNode = getNextNode();
445 if (!prevNode->hasValidOrder() || !nextNode->hasValidOrder())
446 return block->recomputeOpOrder();
447 unsigned prevOrder = prevNode->orderIndex, nextOrder = nextNode->orderIndex;
448
449 // Check to see if there is a valid order between the two.
450 if (prevOrder + 1 == nextOrder)
451 return block->recomputeOpOrder();
452 orderIndex = prevOrder + ((nextOrder - prevOrder) / 2);
453}
454
455//===----------------------------------------------------------------------===//
456// ilist_traits for Operation
457//===----------------------------------------------------------------------===//
458
459auto llvm::ilist_detail::SpecificNodeAccess<
460 llvm::ilist_detail::compute_node_options<::mlir::Operation>::type>::
461 getNodePtr(pointer n) -> node_type * {
462 return NodeAccess::getNodePtr<OptionsT>(n);
463}
464
465auto llvm::ilist_detail::SpecificNodeAccess<
466 llvm::ilist_detail::compute_node_options<::mlir::Operation>::type>::
467 getNodePtr(const_pointer n) -> const node_type * {
468 return NodeAccess::getNodePtr<OptionsT>(n);
469}
470
471auto llvm::ilist_detail::SpecificNodeAccess<
472 llvm::ilist_detail::compute_node_options<::mlir::Operation>::type>::
473 getValuePtr(node_type *n) -> pointer {
474 return NodeAccess::getValuePtr<OptionsT>(n);
475}
476
477auto llvm::ilist_detail::SpecificNodeAccess<
478 llvm::ilist_detail::compute_node_options<::mlir::Operation>::type>::
479 getValuePtr(const node_type *n) -> const_pointer {
480 return NodeAccess::getValuePtr<OptionsT>(n);
481}
482
486
487Block *llvm::ilist_traits<::mlir::Operation>::getContainingBlock() {
488 size_t offset(size_t(&((Block *)nullptr->*Block::getSublistAccess(nullptr))));
489 iplist<Operation> *anchor(static_cast<iplist<Operation> *>(this));
490 return reinterpret_cast<Block *>(reinterpret_cast<char *>(anchor) - offset);
491}
492
493/// This is a trait method invoked when an operation is added to a block. We
494/// keep the block pointer up to date.
496 assert(!op->getBlock() && "already in an operation block!");
497 op->block = getContainingBlock();
498
499 // Invalidate the order on the operation.
500 op->orderIndex = Operation::kInvalidOrderIdx;
501}
502
503/// This is a trait method invoked when an operation is removed from a block.
504/// We keep the block pointer up to date.
506 assert(op->block && "not already in an operation block!");
507 op->block = nullptr;
508}
509
510/// This is a trait method invoked when an operation is moved from one block
511/// to another. We keep the block pointer up to date.
513 ilist_traits<Operation> &otherList, op_iterator first, op_iterator last) {
514 Block *curParent = getContainingBlock();
515
516 // Invalidate the ordering of the parent block.
517 curParent->invalidateOpOrder();
518
519 // If we are transferring operations within the same block, the block
520 // pointer doesn't need to be updated.
521 if (curParent == otherList.getContainingBlock())
522 return;
523
524 // Update the 'block' member of each operation.
525 for (; first != last; ++first)
526 first->block = curParent;
527}
528
529/// Remove this operation (and its descendants) from its Block and delete
530/// all of them.
532 if (auto *parent = getBlock())
533 parent->getOperations().erase(this);
534 else
535 destroy();
536}
537
538/// Remove the operation from its parent block, but don't delete it.
540 if (Block *parent = getBlock())
541 parent->getOperations().remove(this);
542}
543
544/// Unlink this operation from its current block and insert it right before
545/// `existingOp` which may be in the same or another block in the same
546/// function.
547void Operation::moveBefore(Operation *existingOp) {
548 moveBefore(existingOp->getBlock(), existingOp->getIterator());
549}
550
551/// Unlink this operation from its current basic block and insert it right
552/// before `iterator` in the specified basic block.
554 llvm::iplist<Operation>::iterator iterator) {
555 assert(getBlock() &&
556 "cannot move an operation that isn't contained in a block");
557 block->getOperations().splice(iterator, getBlock()->getOperations(),
558 getIterator());
559}
560
561/// Unlink this operation from its current block and insert it right after
562/// `existingOp` which may be in the same or another block in the same function.
563void Operation::moveAfter(Operation *existingOp) {
564 moveAfter(existingOp->getBlock(), existingOp->getIterator());
565}
566
567/// Unlink this operation from its current block and insert it right after
568/// `iterator` in the specified block.
570 llvm::iplist<Operation>::iterator iterator) {
571 assert(iterator != block->end() && "cannot move after end of block");
572 moveBefore(block, std::next(iterator));
573}
574
575/// This drops all operand uses from this operation, which is an essential
576/// step in breaking cyclic dependences between references when they are to
577/// be deleted.
579 for (auto &op : getOpOperands())
580 op.drop();
581
582 for (auto &region : getRegions())
583 region.dropAllReferences();
584
585 for (auto &dest : getBlockOperands())
586 dest.drop();
587}
588
589/// This drops all uses of any values defined by this operation or its nested
590/// regions, wherever they are located.
592 dropAllUses();
593
594 for (auto &region : getRegions())
595 for (auto &block : region)
596 block.dropAllDefinedValueUses();
597}
598
599void Operation::setSuccessor(Block *block, unsigned index) {
600 assert(index < getNumSuccessors());
601 getBlockOperands()[index].set(block);
602}
603
604#ifndef NDEBUG
605/// Assert that the folded results (in case of values) have the same type as
606/// the results of the given op.
609 if (results.empty())
610 return;
611
612 for (auto [ofr, opResult] : llvm::zip_equal(results, op->getResults())) {
613 if (auto value = dyn_cast<Value>(ofr)) {
614 if (value.getType() != opResult.getType()) {
615 op->emitOpError() << "folder produced a value of incorrect type: "
616 << value.getType()
617 << ", expected: " << opResult.getType();
618 assert(false && "incorrect fold result type");
619 }
620 }
621 }
622}
623#endif // NDEBUG
624
625/// Attempt to fold this operation using the Op's registered foldHook.
628 // If we have a registered operation definition matching this one, use it to
629 // try to constant fold the operation.
630 if (succeeded(name.foldHook(this, operands, results))) {
631#ifndef NDEBUG
632 checkFoldResultTypes(this, results);
633#endif // NDEBUG
634 return success();
635 }
636
637 // Otherwise, fall back on the dialect hook to handle it.
638 Dialect *dialect = getDialect();
639 if (!dialect)
640 return failure();
641
642 auto *interface = dyn_cast<DialectFoldInterface>(dialect);
643 if (!interface)
644 return failure();
645
646 LogicalResult status = interface->fold(this, operands, results);
647#ifndef NDEBUG
648 if (succeeded(status))
649 checkFoldResultTypes(this, results);
650#endif // NDEBUG
651 return status;
652}
653
655 // Check if any operands are constants.
656 SmallVector<Attribute> constants;
657 constants.assign(getNumOperands(), Attribute());
658 for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
659 matchPattern(getOperand(i), m_Constant(&constants[i]));
660 return fold(constants, results);
661}
662
663/// Emit an error with the op name prefixed, like "'dim' op " which is
664/// convenient for verifiers.
666 return emitError() << "'" << getName() << "' op " << message;
667}
668
669//===----------------------------------------------------------------------===//
670// Operation Cloning
671//===----------------------------------------------------------------------===//
672
674 : cloneRegionsFlag(false), cloneOperandsFlag(false),
675 resultTypes(std::nullopt) {}
676
678 bool cloneRegions, bool cloneOperands,
679 std::optional<SmallVector<Type>> resultTypes)
680 : cloneRegionsFlag(cloneRegions), cloneOperandsFlag(cloneOperands),
681 resultTypes(resultTypes) {}
682
684 return CloneOptions().cloneRegions().cloneOperands().withResultTypes(
685 std::nullopt);
686}
687
689 cloneRegionsFlag = enable;
690 return *this;
691}
692
694 cloneOperandsFlag = enable;
695 return *this;
696}
697
699 std::optional<SmallVector<Type>> resultTypes) {
700 this->resultTypes = std::move(resultTypes);
701 return *this;
702}
703
704/// Create a deep copy of this operation but keep the operation regions empty.
705/// Operands are remapped using `mapper` (if present), and `mapper` is updated
706/// to contain the results. The `mapResults` flag specifies whether the results
707/// of the cloned operation should be added to the map.
709 return clone(mapper, CloneOptions::all().cloneRegions(false));
710}
711
713 IRMapping mapper;
714 return cloneWithoutRegions(mapper);
715}
716
717/// Create a deep copy of this operation, remapping any operands that use
718/// values outside of the operation using the map that is provided (leaving
719/// them alone if no entry is present). Replaces references to cloned
720/// sub-operations to the corresponding operation that is copied, and adds
721/// those mappings to the map.
722Operation *Operation::clone(IRMapping &mapper, const CloneOptions &options) {
723 SmallVector<Value, 8> operands;
724 SmallVector<Block *, 2> successors;
725
726 // Remap the operands.
727 if (options.shouldCloneOperands()) {
728 operands.reserve(getNumOperands());
729 for (auto opValue : getOperands())
730 operands.push_back(mapper.lookupOrDefault(opValue));
731 }
732
733 // Remap the successors.
734 successors.reserve(getNumSuccessors());
735 for (Block *successor : getSuccessors())
736 successors.push_back(mapper.lookupOrDefault(successor));
737
738 // Create the new operation.
739 auto *newOp = create(getLoc(), getName(),
740 options.resultTypesOr(getResultTypes()), operands, attrs,
741 getPropertiesStorage(), successors, getNumRegions());
742 mapper.map(this, newOp);
743
744 // Clone the regions.
745 if (options.shouldCloneRegions()) {
746 for (unsigned i = 0; i != numRegions; ++i)
747 getRegion(i).cloneInto(&newOp->getRegion(i), mapper);
748 }
749
750 // Remember the mapping of any results.
751 if (options.shouldCloneResults())
752 for (unsigned i = 0, e = getNumResults(); i != e; ++i)
753 mapper.map(getResult(i), newOp->getResult(i));
754
755 return newOp;
756}
757
759 IRMapping mapper;
760 return clone(mapper, options);
761}
762
763//===----------------------------------------------------------------------===//
764// OpState trait class.
765//===----------------------------------------------------------------------===//
766
767// The fallback for the parser is to try for a dialect operation parser.
768// Otherwise, reject the custom assembly form.
770 if (auto parseFn = result.name.getDialect()->getParseOperationHook(
771 result.name.getStringRef()))
772 return (*parseFn)(parser, result);
773 return parser.emitError(parser.getNameLoc(), "has no custom assembly form");
774}
775
776// The fallback for the printer is to try for a dialect operation printer.
777// Otherwise, it prints the generic form.
778void OpState::print(Operation *op, OpAsmPrinter &p, StringRef defaultDialect) {
779 if (auto printFn = op->getDialect()->getOperationPrinter(op)) {
780 printOpName(op, p, defaultDialect);
781 printFn(op, p);
782 } else {
783 p.printGenericOp(op);
784 }
785}
786
787/// Print an operation name, eliding the dialect prefix if necessary and doesn't
788/// lead to ambiguities.
790 StringRef defaultDialect) {
791 StringRef name = op->getName().getStringRef();
792 if (name.starts_with((defaultDialect + ".").str()) && name.count('.') == 1)
793 name = name.drop_front(defaultDialect.size() + 1);
794 p.getStream() << name;
795}
796
797/// Parse properties as a Attribute.
799 Attribute &result) {
800 if (succeeded(parser.parseOptionalLess())) { // The less is optional.
801 if (parser.parseAttribute(result) || parser.parseGreater())
802 return failure();
803 }
804 return success();
805}
806
807/// Print the properties as a Attribute with names not included within
808/// 'elidedProps'
810 ArrayRef<StringRef> elidedProps) {
811 if (!properties)
812 return;
813 auto dictAttr = dyn_cast_or_null<::mlir::DictionaryAttr>(properties);
814 if (dictAttr && !elidedProps.empty()) {
815 ArrayRef<NamedAttribute> attrs = dictAttr.getValue();
816 llvm::SmallDenseSet<StringRef> elidedAttrsSet(elidedProps.begin(),
817 elidedProps.end());
818 auto filteredAttrs =
819 llvm::make_filter_range(attrs, [&](NamedAttribute attr) {
820 return !elidedAttrsSet.contains(attr.getName().strref());
821 });
822 if (!filteredAttrs.empty()) {
823 p << "<{";
824 interleaveComma(filteredAttrs, p, [&](NamedAttribute attr) {
825 p.printNamedAttribute(attr);
826 });
827 p << "}>";
828 }
829 } else {
830 p << "<" << properties << ">";
831 }
832}
833
834/// Emit an error about fatal conditions with this operation, reporting up to
835/// any diagnostic handlers that may be listening.
837 return getOperation()->emitError(message);
838}
839
840/// Emit an error with the op name prefixed, like "'dim' op " which is
841/// convenient for verifiers.
843 return getOperation()->emitOpError(message);
844}
845
846/// Emit a warning about this operation, reporting up to any diagnostic
847/// handlers that may be listening.
849 return getOperation()->emitWarning(message);
850}
851
852/// Emit a remark about this operation, reporting up to any diagnostic
853/// handlers that may be listening.
855 return getOperation()->emitRemark(message);
856}
857
858//===----------------------------------------------------------------------===//
859// Op Trait implementations
860//===----------------------------------------------------------------------===//
861
862LogicalResult
865 // Nothing to fold if there are not at least 2 operands.
866 if (op->getNumOperands() < 2)
867 return failure();
868 // Move all constant operands to the end.
869 OpOperand *operandsBegin = op->getOpOperands().begin();
870 auto isNonConstant = [&](OpOperand &o) {
871 return !static_cast<bool>(operands[std::distance(operandsBegin, &o)]);
872 };
873 auto *firstConstantIt = llvm::find_if_not(op->getOpOperands(), isNonConstant);
874 auto *newConstantIt = std::stable_partition(
875 firstConstantIt, op->getOpOperands().end(), isNonConstant);
876 // Return success if the op was modified.
877 return success(firstConstantIt != newConstantIt);
878}
879
881 if (op->getNumOperands() == 1) {
882 auto *argumentOp = op->getOperand(0).getDefiningOp();
883 if (argumentOp && op->getName() == argumentOp->getName()) {
884 // Replace the outer operation output with the inner operation.
885 return op->getOperand(0);
886 }
887 } else if (op->getOperand(0) == op->getOperand(1)) {
888 return op->getOperand(0);
889 }
890
891 return {};
892}
893
895 auto *argumentOp = op->getOperand(0).getDefiningOp();
896 if (argumentOp && op->getName() == argumentOp->getName()) {
897 // Replace the outer involutions output with inner's input.
898 return argumentOp->getOperand(0);
899 }
900
901 return {};
902}
903
905 if (op->getNumOperands() != 0)
906 return op->emitOpError() << "requires zero operands";
907 return success();
908}
909
911 if (op->getNumOperands() != 1)
912 return op->emitOpError() << "requires a single operand";
913 return success();
914}
915
917 unsigned numOperands) {
918 if (op->getNumOperands() != numOperands) {
919 return op->emitOpError() << "expected " << numOperands
920 << " operands, but found " << op->getNumOperands();
921 }
922 return success();
923}
924
926 unsigned numOperands) {
927 if (op->getNumOperands() < numOperands)
928 return op->emitOpError()
929 << "expected " << numOperands << " or more operands, but found "
930 << op->getNumOperands();
931 return success();
932}
933
934/// If this is a vector type, or a tensor type, return the scalar element type
935/// that it is built around, otherwise return the type unmodified.
937 if (auto vec = llvm::dyn_cast<VectorType>(type))
938 return vec.getElementType();
939
940 // Look through tensor<vector<...>> to find the underlying element type.
941 if (auto tensor = llvm::dyn_cast<TensorType>(type))
942 return getTensorOrVectorElementType(tensor.getElementType());
943 return type;
944}
945
947 // FIXME: Add back check for no side effects on operation.
948 // Currently adding it would cause the shared library build
949 // to fail since there would be a dependency of IR on SideEffectInterfaces
950 // which is cyclical.
951 return success();
952}
953
955 // FIXME: Add back check for no side effects on operation.
956 // Currently adding it would cause the shared library build
957 // to fail since there would be a dependency of IR on SideEffectInterfaces
958 // which is cyclical.
959 return success();
960}
961
962LogicalResult
964 for (auto opType : op->getOperandTypes()) {
965 auto type = getTensorOrVectorElementType(opType);
966 if (!type.isSignlessIntOrIndex())
967 return op->emitOpError() << "requires an integer or index type";
968 }
969 return success();
970}
971
973 for (auto opType : op->getOperandTypes()) {
974 auto type = getTensorOrVectorElementType(opType);
975 if (!llvm::isa<FloatType>(type))
976 return op->emitOpError("requires a float type");
977 }
978 return success();
979}
980
982 // Zero or one operand always have the "same" type.
983 unsigned nOperands = op->getNumOperands();
984 if (nOperands < 2)
985 return success();
986
987 auto type = op->getOperand(0).getType();
988 for (auto opType : llvm::drop_begin(op->getOperandTypes(), 1))
989 if (opType != type)
990 return op->emitOpError() << "requires all operands to have the same type";
991 return success();
992}
993
995 if (op->getNumRegions() != 0)
996 return op->emitOpError() << "requires zero regions";
997 return success();
998}
999
1001 if (op->getNumRegions() != 1)
1002 return op->emitOpError() << "requires one region";
1003 return success();
1004}
1005
1007 unsigned numRegions) {
1008 if (op->getNumRegions() != numRegions)
1009 return op->emitOpError() << "expected " << numRegions << " regions";
1010 return success();
1011}
1012
1014 unsigned numRegions) {
1015 if (op->getNumRegions() < numRegions)
1016 return op->emitOpError() << "expected " << numRegions << " or more regions";
1017 return success();
1018}
1019
1021 if (op->getNumResults() != 0)
1022 return op->emitOpError() << "requires zero results";
1023 return success();
1024}
1025
1027 if (op->getNumResults() != 1)
1028 return op->emitOpError() << "requires one result";
1029 return success();
1030}
1031
1033 unsigned numOperands) {
1034 if (op->getNumResults() != numOperands)
1035 return op->emitOpError() << "expected " << numOperands << " results";
1036 return success();
1037}
1038
1040 unsigned numOperands) {
1041 if (op->getNumResults() < numOperands)
1042 return op->emitOpError()
1043 << "expected " << numOperands << " or more results";
1044 return success();
1045}
1046
1048 if (failed(verifyAtLeastNOperands(op, 1)))
1049 return failure();
1050
1051 if (failed(verifyCompatibleShapes(op->getOperandTypes())))
1052 return op->emitOpError() << "requires the same shape for all operands";
1053
1054 return success();
1055}
1056
1058 if (failed(verifyAtLeastNOperands(op, 1)) ||
1059 failed(verifyAtLeastNResults(op, 1)))
1060 return failure();
1061
1063 types.append(llvm::to_vector<4>(op->getResultTypes()));
1064
1065 if (failed(verifyCompatibleShapes(types)))
1066 return op->emitOpError()
1067 << "requires the same shape for all operands and results";
1068
1069 return success();
1070}
1071
1073 if (failed(verifyAtLeastNOperands(op, 1)))
1074 return failure();
1075 auto elementType = getElementTypeOrSelf(op->getOperand(0));
1076
1077 for (auto operand : llvm::drop_begin(op->getOperands(), 1)) {
1078 if (getElementTypeOrSelf(operand) != elementType)
1079 return op->emitOpError("requires the same element type for all operands");
1080 }
1081
1082 return success();
1083}
1084
1085LogicalResult
1087 if (failed(verifyAtLeastNOperands(op, 1)) ||
1088 failed(verifyAtLeastNResults(op, 1)))
1089 return failure();
1090
1091 auto elementType = getElementTypeOrSelf(op->getResult(0));
1092
1093 // Verify result element type matches first result's element type.
1094 for (auto result : llvm::drop_begin(op->getResults(), 1)) {
1095 if (getElementTypeOrSelf(result) != elementType)
1096 return op->emitOpError(
1097 "requires the same element type for all operands and results");
1098 }
1099
1100 // Verify operand's element type matches first result's element type.
1101 for (auto operand : op->getOperands()) {
1102 if (getElementTypeOrSelf(operand) != elementType)
1103 return op->emitOpError(
1104 "requires the same element type for all operands and results");
1105 }
1106
1107 return success();
1108}
1109
1111 if (failed(verifyAtLeastNOperands(op, 1)) ||
1112 failed(verifyAtLeastNResults(op, 1)))
1113 return failure();
1114
1115 auto type = op->getResult(0).getType();
1116 auto elementType = getElementTypeOrSelf(type);
1117 Attribute encoding = nullptr;
1118 if (auto rankedType = dyn_cast<RankedTensorType>(type))
1119 encoding = rankedType.getEncoding();
1120 for (auto resultType : llvm::drop_begin(op->getResultTypes())) {
1121 if (getElementTypeOrSelf(resultType) != elementType ||
1122 failed(verifyCompatibleShape(resultType, type)))
1123 return op->emitOpError()
1124 << "requires the same type for all operands and results";
1125 if (encoding)
1126 if (auto rankedType = dyn_cast<RankedTensorType>(resultType);
1127 encoding != rankedType.getEncoding())
1128 return op->emitOpError()
1129 << "requires the same encoding for all operands and results";
1130 }
1131 for (auto opType : op->getOperandTypes()) {
1132 if (getElementTypeOrSelf(opType) != elementType ||
1133 failed(verifyCompatibleShape(opType, type)))
1134 return op->emitOpError()
1135 << "requires the same type for all operands and results";
1136 if (encoding)
1137 if (auto rankedType = dyn_cast<RankedTensorType>(opType);
1138 encoding != rankedType.getEncoding())
1139 return op->emitOpError()
1140 << "requires the same encoding for all operands and results";
1141 }
1142 return success();
1143}
1144
1146 if (failed(verifyAtLeastNOperands(op, 1)))
1147 return failure();
1148
1149 // delegate function that returns true if type is a shaped type with known
1150 // rank
1151 auto hasRank = [](const Type type) {
1152 if (auto shapedType = dyn_cast<ShapedType>(type))
1153 return shapedType.hasRank();
1154
1155 return false;
1156 };
1157
1158 auto rankedOperandTypes =
1159 llvm::make_filter_range(op->getOperandTypes(), hasRank);
1160 auto rankedResultTypes =
1161 llvm::make_filter_range(op->getResultTypes(), hasRank);
1162
1163 // If all operands and results are unranked, then no further verification.
1164 if (rankedOperandTypes.empty() && rankedResultTypes.empty())
1165 return success();
1166
1167 // delegate function that returns rank of shaped type with known rank
1168 auto getRank = [](const Type type) {
1169 return cast<ShapedType>(type).getRank();
1170 };
1171
1172 auto rank = !rankedOperandTypes.empty() ? getRank(*rankedOperandTypes.begin())
1173 : getRank(*rankedResultTypes.begin());
1174
1175 for (const auto type : rankedOperandTypes) {
1176 if (rank != getRank(type)) {
1177 return op->emitOpError("operands don't have matching ranks");
1178 }
1179 }
1180
1181 for (const auto type : rankedResultTypes) {
1182 if (rank != getRank(type)) {
1183 return op->emitOpError("result type has different rank than operands");
1184 }
1185 }
1186
1187 return success();
1188}
1189
1191 Block *block = op->getBlock();
1192 // Verify that the operation is at the end of the respective parent block.
1193 if (!block || &block->back() != op)
1194 return op->emitOpError("must be the last operation in the parent block");
1195 return success();
1196}
1197
1198static LogicalResult verifyTerminatorSuccessors(Operation *op) {
1199 auto *parent = op->getParentRegion();
1200
1201 // Verify that the operands lines up with the BB arguments in the successor.
1202 for (Block *succ : op->getSuccessors())
1203 if (succ->getParent() != parent)
1204 return op->emitError("reference to block defined in another region");
1205 return success();
1206}
1207
1209 if (op->getNumSuccessors() != 0) {
1210 return op->emitOpError("requires 0 successors but found ")
1211 << op->getNumSuccessors();
1212 }
1213 return success();
1214}
1215
1217 if (op->getNumSuccessors() != 1) {
1218 return op->emitOpError("requires 1 successor but found ")
1219 << op->getNumSuccessors();
1220 }
1221 return verifyTerminatorSuccessors(op);
1222}
1224 unsigned numSuccessors) {
1225 if (op->getNumSuccessors() != numSuccessors) {
1226 return op->emitOpError("requires ")
1227 << numSuccessors << " successors but found "
1228 << op->getNumSuccessors();
1229 }
1230 return verifyTerminatorSuccessors(op);
1231}
1233 unsigned numSuccessors) {
1234 if (op->getNumSuccessors() < numSuccessors) {
1235 return op->emitOpError("requires at least ")
1236 << numSuccessors << " successors but found "
1237 << op->getNumSuccessors();
1238 }
1239 return verifyTerminatorSuccessors(op);
1240}
1241
1243 for (auto resultType : op->getResultTypes()) {
1244 auto elementType = getTensorOrVectorElementType(resultType);
1245 bool isBoolType = elementType.isInteger(1);
1246 if (!isBoolType)
1247 return op->emitOpError() << "requires a bool result type";
1248 }
1249
1250 return success();
1251}
1252
1254 for (auto resultType : op->getResultTypes())
1255 if (!llvm::isa<FloatType>(getTensorOrVectorElementType(resultType)))
1256 return op->emitOpError() << "requires a floating point type";
1257
1258 return success();
1259}
1260
1261LogicalResult
1263 for (auto resultType : op->getResultTypes())
1264 if (!getTensorOrVectorElementType(resultType).isSignlessIntOrIndex())
1265 return op->emitOpError() << "requires an integer or index type";
1266 return success();
1267}
1268
1270 StringRef attrName,
1271 StringRef valueGroupName,
1272 size_t expectedCount) {
1273 auto sizeAttr = op->getAttrOfType<DenseI32ArrayAttr>(attrName);
1274 if (!sizeAttr)
1275 return op->emitOpError("requires dense i32 array attribute '")
1276 << attrName << "'";
1277
1278 ArrayRef<int32_t> sizes = sizeAttr.asArrayRef();
1279 if (llvm::any_of(sizes, [](int32_t element) { return element < 0; }))
1280 return op->emitOpError("'")
1281 << attrName << "' attribute cannot have negative elements";
1282
1283 size_t totalCount = llvm::sum_of(sizes, size_t(0));
1284 if (totalCount != expectedCount)
1285 return op->emitOpError()
1286 << valueGroupName << " count (" << expectedCount
1287 << ") does not match with the total size (" << totalCount
1288 << ") specified in attribute '" << attrName << "'";
1289 return success();
1290}
1291
1293 StringRef attrName) {
1294 return verifyValueSizeAttr(op, attrName, "operand", op->getNumOperands());
1295}
1296
1298 StringRef attrName) {
1299 return verifyValueSizeAttr(op, attrName, "result", op->getNumResults());
1300}
1301
1303 for (Region &region : op->getRegions()) {
1304 if (region.empty())
1305 continue;
1306
1307 if (region.getNumArguments() != 0) {
1308 if (op->getNumRegions() > 1)
1309 return op->emitOpError("region #")
1310 << region.getRegionNumber() << " should have no arguments";
1311 return op->emitOpError("region should have no arguments");
1312 }
1313 }
1314 return success();
1315}
1316
1318 auto isMappableType = llvm::IsaPred<VectorType, TensorType>;
1319 auto resultMappableTypes =
1320 llvm::filter_to_vector<1>(op->getResultTypes(), isMappableType);
1321 auto operandMappableTypes =
1322 llvm::filter_to_vector<2>(op->getOperandTypes(), isMappableType);
1323
1324 // If the op only has scalar operand/result types, then we have nothing to
1325 // check.
1326 if (resultMappableTypes.empty() && operandMappableTypes.empty())
1327 return success();
1328
1329 if (!resultMappableTypes.empty() && operandMappableTypes.empty())
1330 return op->emitOpError("if a result is non-scalar, then at least one "
1331 "operand must be non-scalar");
1332
1333 assert(!operandMappableTypes.empty());
1334
1335 if (resultMappableTypes.empty())
1336 return op->emitOpError("if an operand is non-scalar, then there must be at "
1337 "least one non-scalar result");
1338
1339 if (resultMappableTypes.size() != op->getNumResults())
1340 return op->emitOpError(
1341 "if an operand is non-scalar, then all results must be non-scalar");
1342
1343 SmallVector<Type, 4> types = llvm::to_vector<2>(
1344 llvm::concat<Type>(operandMappableTypes, resultMappableTypes));
1345 TypeID expectedBaseTy = types.front().getTypeID();
1346 if (!llvm::all_of(types,
1347 [&](Type t) { return t.getTypeID() == expectedBaseTy; }) ||
1348 failed(verifyCompatibleShapes(types))) {
1349 return op->emitOpError() << "all non-scalar operands/results must have the "
1350 "same shape and base type";
1351 }
1352
1353 return success();
1354}
1355
1356/// Check for any values used by operations regions attached to the
1357/// specified "IsIsolatedFromAbove" operation defined outside of it.
1359 assert(isolatedOp->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
1360 "Intended to check IsolatedFromAbove ops");
1361
1362 // List of regions to analyze. Each region is processed independently, with
1363 // respect to the common `limit` region, so we can look at them in any order.
1364 // Therefore, use a simple vector and push/pop back the current region.
1365 SmallVector<Region *, 8> pendingRegions;
1366 for (auto &region : isolatedOp->getRegions()) {
1367 pendingRegions.push_back(&region);
1368
1369 // Traverse all operations in the region.
1370 while (!pendingRegions.empty()) {
1371 for (Operation &op : pendingRegions.pop_back_val()->getOps()) {
1372 for (Value operand : op.getOperands()) {
1373 // Check that any value that is used by an operation is defined in the
1374 // same region as either an operation result.
1375 auto *operandRegion = operand.getParentRegion();
1376 if (!operandRegion)
1377 return op.emitError("operation's operand is unlinked");
1378 if (!region.isAncestor(operandRegion)) {
1379 return op.emitOpError("using value defined outside the region")
1380 .attachNote(isolatedOp->getLoc())
1381 << "required by region isolation constraints";
1382 }
1383 }
1384
1385 // Schedule any regions in the operation for further checking. Don't
1386 // recurse into other IsolatedFromAbove ops, because they will check
1387 // themselves.
1388 if (op.getNumRegions() &&
1389 !op.hasTrait<OpTrait::IsIsolatedFromAbove>()) {
1390 for (Region &subRegion : op.getRegions())
1391 pendingRegions.push_back(&subRegion);
1392 }
1393 }
1394 }
1395 }
1396
1397 return success();
1398}
1399
1404
1405//===----------------------------------------------------------------------===//
1406// Misc. utils
1407//===----------------------------------------------------------------------===//
1408
1409/// Insert an operation, generated by `buildTerminatorOp`, at the end of the
1410/// region's only block if it does not have a terminator already. If the region
1411/// is empty, insert a new block first. `buildTerminatorOp` should return the
1412/// terminator operation to insert.
1414 Region &region, OpBuilder &builder, Location loc,
1415 function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp) {
1416 OpBuilder::InsertionGuard guard(builder);
1417 if (region.empty())
1418 builder.createBlock(&region);
1419
1420 Block &block = region.back();
1421 if (!block.empty() && block.back().hasTrait<OpTrait::IsTerminator>())
1422 return;
1423
1424 builder.setInsertionPointToEnd(&block);
1425 builder.insert(buildTerminatorOp(builder, loc));
1426}
1427
1428/// Create a simple OpBuilder and forward to the OpBuilder version of this
1429/// function.
1431 Region &region, Builder &builder, Location loc,
1432 function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp) {
1433 OpBuilder opBuilder(builder.getContext());
1434 ensureRegionTerminator(region, opBuilder, loc, buildTerminatorOp);
1435}
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:254
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.
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(PropertyRef storage, PropertyRef init) const
Initialize the op properties.
Class encompassing various options related to cloning an operation.
Definition Operation.h:141
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
PropertyRef getPropertiesStorage()
Return a generic (but typed) reference to the property type storage.
Definition Operation.h:927
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.
void copyProperties(PropertyRef rhs)
Copy properties from an existing other properties object.
MutableArrayRef< BlockOperand > getBlockOperands()
Definition Operation.h:721
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:238
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:712
bool use_empty()
Returns true if this operation has no uses.
Definition Operation.h:878
Value getOperand(unsigned idx)
Definition Operation.h:376
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:775
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:860
AttrClass getAttrOfType(StringAttr name)
Definition Operation.h:576
void setAttrs(DictionaryAttr newAttrs)
Set the attributes from a dictionary on this operation.
unsigned getNumSuccessors()
Definition Operation.h:732
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:783
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:231
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:433
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:700
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:241
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:252
MutableArrayRef< OpOperand > getOpOperands()
Definition Operation.h:409
std::optional< RegisteredOperationName > getRegisteredInfo()
If this operation has a registered operation description, return it.
Definition Operation.h:120
void dropAllDefinedValueUses()
Drop uses of all values defined by this operation or its nested regions.
unsigned getNumOperands()
Definition Operation.h:372
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
void populateDefaultAttrs()
Sets default attributes on unset attributes.
Definition Operation.h:689
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:116
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:423
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:703
static Operation * create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, PropertyRef properties, BlockRange successors, unsigned numRegions)
Create a new Operation with the specific fields.
Definition Operation.cpp:66
friend class Block
Definition Operation.h:1108
result_type_range getResultTypes()
Definition Operation.h:454
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:404
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:899
SuccessorRange getSuccessors()
Definition Operation.h:729
result_range getResults()
Definition Operation.h:441
int getPropertiesStorageSize() const
Returns the properties storage size.
Definition Operation.h:922
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:248
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:234
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:1111
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.
void erase()
Remove this operation from its parent block and delete it.
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:430
Type-safe wrapper around a void* for passing properties, including the properties structs of operatio...
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:40
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:389
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:401
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:147
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:384