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