MLIR 24.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'. A leading space is printed as part of the properties, so
808/// that nothing is emitted at all when every property is elided.
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:172
Operation & front()
Definition Block.h:177
Operation & back()
Definition Block.h:176
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:417
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:351
This class helps build Operations.
Definition Builders.h:210
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:439
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:439
Operation * insert(Operation *op)
Insert the given operation at the current insertion point and return it.
Definition Builders.cpp:430
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:946
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:740
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:731
bool use_empty()
Returns true if this operation has no uses.
Definition Operation.h:897
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:794
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:879
AttrClass getAttrOfType(StringAttr name)
Definition Operation.h:595
void setAttrs(DictionaryAttr newAttrs)
Set the attributes from a dictionary on this operation.
unsigned getNumSuccessors()
Definition Operation.h:751
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:802
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:719
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:708
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:722
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:1127
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:918
SuccessorRange getSuccessors()
Definition Operation.h:748
result_range getResults()
Definition Operation.h:440
int getPropertiesStorageSize() const
Returns the properties storage size.
Definition Operation.h:941
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:1130
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:378
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:268
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