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#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
787void OpState::dump() { state->dump(); }
788#endif
789
790/// Print an operation name, eliding the dialect prefix if necessary and doesn't
791/// lead to ambiguities.
793 StringRef defaultDialect) {
794 StringRef name = op->getName().getStringRef();
795 if (name.starts_with((defaultDialect + ".").str()) && name.count('.') == 1)
796 name = name.drop_front(defaultDialect.size() + 1);
797 p.getStream() << name;
798}
799
800/// Parse properties as a Attribute.
802 Attribute &result) {
803 if (succeeded(parser.parseOptionalLess())) { // The less is optional.
804 if (parser.parseAttribute(result) || parser.parseGreater())
805 return failure();
806 }
807 return success();
808}
809
810/// Print the properties as a Attribute with names not included within
811/// 'elidedProps'. A leading space is printed as part of the properties, so
812/// that nothing is emitted at all when every property is elided.
814 ArrayRef<StringRef> elidedProps) {
815 if (!properties)
816 return;
817 auto dictAttr = dyn_cast_or_null<::mlir::DictionaryAttr>(properties);
818 if (dictAttr && !elidedProps.empty()) {
819 ArrayRef<NamedAttribute> attrs = dictAttr.getValue();
820 llvm::SmallDenseSet<StringRef> elidedAttrsSet(elidedProps.begin(),
821 elidedProps.end());
822 auto filteredAttrs =
823 llvm::make_filter_range(attrs, [&](NamedAttribute attr) {
824 return !elidedAttrsSet.contains(attr.getName().strref());
825 });
826 if (!filteredAttrs.empty()) {
827 p << " <{";
828 interleaveComma(filteredAttrs, p, [&](NamedAttribute attr) {
829 p.printNamedAttribute(attr);
830 });
831 p << "}>";
832 }
833 } else {
834 p << " <" << properties << ">";
835 }
836}
837
838/// Emit an error about fatal conditions with this operation, reporting up to
839/// any diagnostic handlers that may be listening.
841 return getOperation()->emitError(message);
842}
843
844/// Emit an error with the op name prefixed, like "'dim' op " which is
845/// convenient for verifiers.
847 return getOperation()->emitOpError(message);
848}
849
850/// Emit a warning about this operation, reporting up to any diagnostic
851/// handlers that may be listening.
853 return getOperation()->emitWarning(message);
854}
855
856/// Emit a remark about this operation, reporting up to any diagnostic
857/// handlers that may be listening.
859 return getOperation()->emitRemark(message);
860}
861
862//===----------------------------------------------------------------------===//
863// Op Trait implementations
864//===----------------------------------------------------------------------===//
865
866LogicalResult
869 // Nothing to fold if there are not at least 2 operands.
870 if (op->getNumOperands() < 2)
871 return failure();
872 // Move all constant operands to the end.
873 OpOperand *operandsBegin = op->getOpOperands().begin();
874 auto isNonConstant = [&](OpOperand &o) {
875 return !static_cast<bool>(operands[std::distance(operandsBegin, &o)]);
876 };
877 auto *firstConstantIt = llvm::find_if_not(op->getOpOperands(), isNonConstant);
878 auto *newConstantIt = std::stable_partition(
879 firstConstantIt, op->getOpOperands().end(), isNonConstant);
880 // Return success if the op was modified.
881 return success(firstConstantIt != newConstantIt);
882}
883
885 if (op->getNumOperands() == 1) {
886 auto *argumentOp = op->getOperand(0).getDefiningOp();
887 if (argumentOp && op->getName() == argumentOp->getName()) {
888 // Replace the outer operation output with the inner operation.
889 return op->getOperand(0);
890 }
891 } else if (op->getOperand(0) == op->getOperand(1)) {
892 return op->getOperand(0);
893 }
894
895 return {};
896}
897
899 auto *argumentOp = op->getOperand(0).getDefiningOp();
900 if (argumentOp && op->getName() == argumentOp->getName()) {
901 // Replace the outer involutions output with inner's input.
902 return argumentOp->getOperand(0);
903 }
904
905 return {};
906}
907
909 if (op->getNumOperands() != 0)
910 return op->emitOpError() << "requires zero operands";
911 return success();
912}
913
915 if (op->getNumOperands() != 1)
916 return op->emitOpError() << "requires a single operand";
917 return success();
918}
919
921 unsigned numOperands) {
922 if (op->getNumOperands() != numOperands) {
923 return op->emitOpError() << "expected " << numOperands
924 << " operands, but found " << op->getNumOperands();
925 }
926 return success();
927}
928
930 unsigned numOperands) {
931 if (op->getNumOperands() < numOperands)
932 return op->emitOpError()
933 << "expected " << numOperands << " or more operands, but found "
934 << op->getNumOperands();
935 return success();
936}
937
938/// If this is a vector type, or a tensor type, return the scalar element type
939/// that it is built around, otherwise return the type unmodified.
941 if (auto vec = llvm::dyn_cast<VectorType>(type))
942 return vec.getElementType();
943
944 // Look through tensor<vector<...>> to find the underlying element type.
945 if (auto tensor = llvm::dyn_cast<TensorType>(type))
946 return getTensorOrVectorElementType(tensor.getElementType());
947 return type;
948}
949
951 // FIXME: Add back check for no side effects on operation.
952 // Currently adding it would cause the shared library build
953 // to fail since there would be a dependency of IR on SideEffectInterfaces
954 // which is cyclical.
955 return success();
956}
957
959 // FIXME: Add back check for no side effects on operation.
960 // Currently adding it would cause the shared library build
961 // to fail since there would be a dependency of IR on SideEffectInterfaces
962 // which is cyclical.
963 return success();
964}
965
966LogicalResult
968 for (auto opType : op->getOperandTypes()) {
969 auto type = getTensorOrVectorElementType(opType);
970 if (!type.isSignlessIntOrIndex())
971 return op->emitOpError() << "requires an integer or index type";
972 }
973 return success();
974}
975
977 for (auto opType : op->getOperandTypes()) {
978 auto type = getTensorOrVectorElementType(opType);
979 if (!llvm::isa<FloatType>(type))
980 return op->emitOpError("requires a float type");
981 }
982 return success();
983}
984
986 // Zero or one operand always have the "same" type.
987 unsigned nOperands = op->getNumOperands();
988 if (nOperands < 2)
989 return success();
990
991 auto type = op->getOperand(0).getType();
992 for (auto opType : llvm::drop_begin(op->getOperandTypes(), 1))
993 if (opType != type)
994 return op->emitOpError() << "requires all operands to have the same type";
995 return success();
996}
997
999 if (op->getNumRegions() != 0)
1000 return op->emitOpError() << "requires zero regions";
1001 return success();
1002}
1003
1005 if (op->getNumRegions() != 1)
1006 return op->emitOpError() << "requires one region";
1007 return success();
1008}
1009
1011 unsigned numRegions) {
1012 if (op->getNumRegions() != numRegions)
1013 return op->emitOpError() << "expected " << numRegions << " regions";
1014 return success();
1015}
1016
1018 unsigned numRegions) {
1019 if (op->getNumRegions() < numRegions)
1020 return op->emitOpError() << "expected " << numRegions << " or more regions";
1021 return success();
1022}
1023
1025 if (op->getNumResults() != 0)
1026 return op->emitOpError() << "requires zero results";
1027 return success();
1028}
1029
1031 if (op->getNumResults() != 1)
1032 return op->emitOpError() << "requires one result";
1033 return success();
1034}
1035
1037 unsigned numOperands) {
1038 if (op->getNumResults() != numOperands)
1039 return op->emitOpError() << "expected " << numOperands << " results";
1040 return success();
1041}
1042
1044 unsigned numOperands) {
1045 if (op->getNumResults() < numOperands)
1046 return op->emitOpError()
1047 << "expected " << numOperands << " or more results";
1048 return success();
1049}
1050
1052 if (failed(verifyAtLeastNOperands(op, 1)))
1053 return failure();
1054
1055 if (failed(verifyCompatibleShapes(op->getOperandTypes())))
1056 return op->emitOpError() << "requires the same shape for all operands";
1057
1058 return success();
1059}
1060
1062 if (failed(verifyAtLeastNOperands(op, 1)) ||
1063 failed(verifyAtLeastNResults(op, 1)))
1064 return failure();
1065
1067 types.append(llvm::to_vector<4>(op->getResultTypes()));
1068
1069 if (failed(verifyCompatibleShapes(types)))
1070 return op->emitOpError()
1071 << "requires the same shape for all operands and results";
1072
1073 return success();
1074}
1075
1077 if (failed(verifyAtLeastNOperands(op, 1)))
1078 return failure();
1079 auto elementType = getElementTypeOrSelf(op->getOperand(0));
1080
1081 for (auto operand : llvm::drop_begin(op->getOperands(), 1)) {
1082 if (getElementTypeOrSelf(operand) != elementType)
1083 return op->emitOpError("requires the same element type for all operands");
1084 }
1085
1086 return success();
1087}
1088
1089LogicalResult
1091 if (failed(verifyAtLeastNOperands(op, 1)) ||
1092 failed(verifyAtLeastNResults(op, 1)))
1093 return failure();
1094
1095 auto elementType = getElementTypeOrSelf(op->getResult(0));
1096
1097 // Verify result element type matches first result's element type.
1098 for (auto result : llvm::drop_begin(op->getResults(), 1)) {
1099 if (getElementTypeOrSelf(result) != elementType)
1100 return op->emitOpError(
1101 "requires the same element type for all operands and results");
1102 }
1103
1104 // Verify operand's element type matches first result's element type.
1105 for (auto operand : op->getOperands()) {
1106 if (getElementTypeOrSelf(operand) != elementType)
1107 return op->emitOpError(
1108 "requires the same element type for all operands and results");
1109 }
1110
1111 return success();
1112}
1113
1115 if (failed(verifyAtLeastNOperands(op, 1)) ||
1116 failed(verifyAtLeastNResults(op, 1)))
1117 return failure();
1118
1119 auto type = op->getResult(0).getType();
1120 auto elementType = getElementTypeOrSelf(type);
1121 Attribute encoding = nullptr;
1122 if (auto rankedType = dyn_cast<RankedTensorType>(type))
1123 encoding = rankedType.getEncoding();
1124 for (auto resultType : llvm::drop_begin(op->getResultTypes())) {
1125 if (getElementTypeOrSelf(resultType) != elementType ||
1126 failed(verifyCompatibleShape(resultType, type)))
1127 return op->emitOpError()
1128 << "requires the same type for all operands and results";
1129 if (encoding)
1130 if (auto rankedType = dyn_cast<RankedTensorType>(resultType);
1131 encoding != rankedType.getEncoding())
1132 return op->emitOpError()
1133 << "requires the same encoding for all operands and results";
1134 }
1135 for (auto opType : op->getOperandTypes()) {
1136 if (getElementTypeOrSelf(opType) != elementType ||
1137 failed(verifyCompatibleShape(opType, type)))
1138 return op->emitOpError()
1139 << "requires the same type for all operands and results";
1140 if (encoding)
1141 if (auto rankedType = dyn_cast<RankedTensorType>(opType);
1142 encoding != rankedType.getEncoding())
1143 return op->emitOpError()
1144 << "requires the same encoding for all operands and results";
1145 }
1146 return success();
1147}
1148
1150 if (failed(verifyAtLeastNOperands(op, 1)))
1151 return failure();
1152
1153 // delegate function that returns true if type is a shaped type with known
1154 // rank
1155 auto hasRank = [](const Type type) {
1156 if (auto shapedType = dyn_cast<ShapedType>(type))
1157 return shapedType.hasRank();
1158
1159 return false;
1160 };
1161
1162 auto rankedOperandTypes =
1163 llvm::make_filter_range(op->getOperandTypes(), hasRank);
1164 auto rankedResultTypes =
1165 llvm::make_filter_range(op->getResultTypes(), hasRank);
1166
1167 // If all operands and results are unranked, then no further verification.
1168 if (rankedOperandTypes.empty() && rankedResultTypes.empty())
1169 return success();
1170
1171 // delegate function that returns rank of shaped type with known rank
1172 auto getRank = [](const Type type) {
1173 return cast<ShapedType>(type).getRank();
1174 };
1175
1176 auto rank = !rankedOperandTypes.empty() ? getRank(*rankedOperandTypes.begin())
1177 : getRank(*rankedResultTypes.begin());
1178
1179 for (const auto type : rankedOperandTypes) {
1180 if (rank != getRank(type)) {
1181 return op->emitOpError("operands don't have matching ranks");
1182 }
1183 }
1184
1185 for (const auto type : rankedResultTypes) {
1186 if (rank != getRank(type)) {
1187 return op->emitOpError("result type has different rank than operands");
1188 }
1189 }
1190
1191 return success();
1192}
1193
1195 Block *block = op->getBlock();
1196 // Verify that the operation is at the end of the respective parent block.
1197 if (!block || &block->back() != op)
1198 return op->emitOpError("must be the last operation in the parent block");
1199 return success();
1200}
1201
1202static LogicalResult verifyTerminatorSuccessors(Operation *op) {
1203 auto *parent = op->getParentRegion();
1204
1205 // Verify that the operands lines up with the BB arguments in the successor.
1206 for (Block *succ : op->getSuccessors())
1207 if (succ->getParent() != parent)
1208 return op->emitError("reference to block defined in another region");
1209 return success();
1210}
1211
1213 if (op->getNumSuccessors() != 0) {
1214 return op->emitOpError("requires 0 successors but found ")
1215 << op->getNumSuccessors();
1216 }
1217 return success();
1218}
1219
1221 if (op->getNumSuccessors() != 1) {
1222 return op->emitOpError("requires 1 successor but found ")
1223 << op->getNumSuccessors();
1224 }
1225 return verifyTerminatorSuccessors(op);
1226}
1228 unsigned numSuccessors) {
1229 if (op->getNumSuccessors() != numSuccessors) {
1230 return op->emitOpError("requires ")
1231 << numSuccessors << " successors but found "
1232 << op->getNumSuccessors();
1233 }
1234 return verifyTerminatorSuccessors(op);
1235}
1237 unsigned numSuccessors) {
1238 if (op->getNumSuccessors() < numSuccessors) {
1239 return op->emitOpError("requires at least ")
1240 << numSuccessors << " successors but found "
1241 << op->getNumSuccessors();
1242 }
1243 return verifyTerminatorSuccessors(op);
1244}
1245
1247 for (auto resultType : op->getResultTypes()) {
1248 auto elementType = getTensorOrVectorElementType(resultType);
1249 bool isBoolType = elementType.isInteger(1);
1250 if (!isBoolType)
1251 return op->emitOpError() << "requires a bool result type";
1252 }
1253
1254 return success();
1255}
1256
1258 for (auto resultType : op->getResultTypes())
1259 if (!llvm::isa<FloatType>(getTensorOrVectorElementType(resultType)))
1260 return op->emitOpError() << "requires a floating point type";
1261
1262 return success();
1263}
1264
1265LogicalResult
1267 for (auto resultType : op->getResultTypes())
1268 if (!getTensorOrVectorElementType(resultType).isSignlessIntOrIndex())
1269 return op->emitOpError() << "requires an integer or index type";
1270 return success();
1271}
1272
1274 StringRef attrName,
1275 StringRef valueGroupName,
1276 size_t expectedCount) {
1277 auto sizeAttr = dyn_cast_or_null<DenseI32ArrayAttr>(
1278 op->getInherentAttr(attrName).value_or(Attribute{}));
1279 if (!sizeAttr)
1280 return op->emitOpError("requires dense i32 array attribute '")
1281 << attrName << "'";
1282
1283 ArrayRef<int32_t> sizes = sizeAttr.asArrayRef();
1284 if (llvm::any_of(sizes, [](int32_t element) { return element < 0; }))
1285 return op->emitOpError("'")
1286 << attrName << "' attribute cannot have negative elements";
1287
1288 size_t totalCount = llvm::sum_of(sizes, size_t(0));
1289 if (totalCount != expectedCount)
1290 return op->emitOpError()
1291 << valueGroupName << " count (" << expectedCount
1292 << ") does not match with the total size (" << totalCount
1293 << ") specified in attribute '" << attrName << "'";
1294 return success();
1295}
1296
1298 StringRef attrName) {
1299 return verifyValueSizeAttr(op, attrName, "operand", op->getNumOperands());
1300}
1301
1303 StringRef attrName) {
1304 return verifyValueSizeAttr(op, attrName, "result", op->getNumResults());
1305}
1306
1308 for (Region &region : op->getRegions()) {
1309 if (region.empty())
1310 continue;
1311
1312 if (region.getNumArguments() != 0) {
1313 if (op->getNumRegions() > 1)
1314 return op->emitOpError("region #")
1315 << region.getRegionNumber() << " should have no arguments";
1316 return op->emitOpError("region should have no arguments");
1317 }
1318 }
1319 return success();
1320}
1321
1323 auto isMappableType = llvm::IsaPred<VectorType, TensorType>;
1324 auto resultMappableTypes =
1325 llvm::filter_to_vector<1>(op->getResultTypes(), isMappableType);
1326 auto operandMappableTypes =
1327 llvm::filter_to_vector<2>(op->getOperandTypes(), isMappableType);
1328
1329 // If the op only has scalar operand/result types, then we have nothing to
1330 // check.
1331 if (resultMappableTypes.empty() && operandMappableTypes.empty())
1332 return success();
1333
1334 if (!resultMappableTypes.empty() && operandMappableTypes.empty())
1335 return op->emitOpError("if a result is non-scalar, then at least one "
1336 "operand must be non-scalar");
1337
1338 assert(!operandMappableTypes.empty());
1339
1340 if (resultMappableTypes.empty())
1341 return op->emitOpError("if an operand is non-scalar, then there must be at "
1342 "least one non-scalar result");
1343
1344 if (resultMappableTypes.size() != op->getNumResults())
1345 return op->emitOpError(
1346 "if an operand is non-scalar, then all results must be non-scalar");
1347
1348 SmallVector<Type, 4> types = llvm::to_vector<2>(
1349 llvm::concat<Type>(operandMappableTypes, resultMappableTypes));
1350 TypeID expectedBaseTy = types.front().getTypeID();
1351 if (!llvm::all_of(types,
1352 [&](Type t) { return t.getTypeID() == expectedBaseTy; }) ||
1353 failed(verifyCompatibleShapes(types))) {
1354 return op->emitOpError() << "all non-scalar operands/results must have the "
1355 "same shape and base type";
1356 }
1357
1358 return success();
1359}
1360
1361/// Check for any values used by operations regions attached to the
1362/// specified "IsIsolatedFromAbove" operation defined outside of it.
1364 assert(isolatedOp->hasTrait<OpTrait::IsIsolatedFromAbove>() &&
1365 "Intended to check IsolatedFromAbove ops");
1366
1367 // List of regions to analyze. Each region is processed independently, with
1368 // respect to the common `limit` region, so we can look at them in any order.
1369 // Therefore, use a simple vector and push/pop back the current region.
1370 SmallVector<Region *, 8> pendingRegions;
1371 for (auto &region : isolatedOp->getRegions()) {
1372 pendingRegions.push_back(&region);
1373
1374 // Traverse all operations in the region.
1375 while (!pendingRegions.empty()) {
1376 for (Operation &op : pendingRegions.pop_back_val()->getOps()) {
1377 for (Value operand : op.getOperands()) {
1378 // Check that any value that is used by an operation is defined in the
1379 // same region as either an operation result.
1380 auto *operandRegion = operand.getParentRegion();
1381 if (!operandRegion)
1382 return op.emitError("operation's operand is unlinked");
1383 if (!region.isAncestor(operandRegion)) {
1384 return op.emitOpError("using value defined outside the region")
1385 .attachNote(isolatedOp->getLoc())
1386 << "required by region isolation constraints";
1387 }
1388 }
1389
1390 // Schedule any regions in the operation for further checking. Don't
1391 // recurse into other IsolatedFromAbove ops, because they will check
1392 // themselves.
1393 if (op.getNumRegions() &&
1394 !op.hasTrait<OpTrait::IsIsolatedFromAbove>()) {
1395 for (Region &subRegion : op.getRegions())
1396 pendingRegions.push_back(&subRegion);
1397 }
1398 }
1399 }
1400 }
1401
1402 return success();
1403}
1404
1409
1410//===----------------------------------------------------------------------===//
1411// Misc. utils
1412//===----------------------------------------------------------------------===//
1413
1414/// Insert an operation, generated by `buildTerminatorOp`, at the end of the
1415/// region's only block if it does not have a terminator already. If the region
1416/// is empty, insert a new block first. `buildTerminatorOp` should return the
1417/// terminator operation to insert.
1419 Region &region, OpBuilder &builder, Location loc,
1420 function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp) {
1421 OpBuilder::InsertionGuard guard(builder);
1422 if (region.empty())
1423 builder.createBlock(&region);
1424
1425 Block &block = region.back();
1426 if (!block.empty() && block.back().hasTrait<OpTrait::IsTerminator>())
1427 return;
1428
1429 builder.setInsertionPointToEnd(&block);
1430 builder.insert(buildTerminatorOp(builder, loc));
1431}
1432
1433/// Create a simple OpBuilder and forward to the OpBuilder version of this
1434/// function.
1436 Region &region, Builder &builder, Location loc,
1437 function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp) {
1438 OpBuilder opBuilder(builder.getContext());
1439 ensureRegionTerminator(region, opBuilder, loc, buildTerminatorOp);
1440}
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.
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:34
void recomputeOpOrder()
Recomputes the ordering of child operations within the block.
Definition Block.cpp:136
bool empty()
Definition Block.h:173
Operation & front()
Definition Block.h:178
Operation & back()
Definition Block.h:177
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:166
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:256
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.
LLVM_DUMP_METHOD void dump()
Dump this operation.
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
Append the inherent attributes stored in the properties of op to attrs.
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:955
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:749
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:740
bool use_empty()
Returns true if this operation has no uses.
Definition Operation.h:906
Value getOperand(unsigned idx)
Definition Operation.h:377
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:803
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:888
void setAttrs(DictionaryAttr newAttrs)
Set the attributes from a dictionary on this operation.
unsigned getNumSuccessors()
Definition Operation.h:760
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:811
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:434
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:728
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:410
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:373
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
void populateDefaultAttrs()
Sets default attributes on unset attributes.
Definition Operation.h:717
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:424
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:731
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:1136
result_type_range getResultTypes()
Definition Operation.h:455
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:405
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:927
SuccessorRange getSuccessors()
Definition Operation.h:757
result_range getResults()
Definition Operation.h:442
int getPropertiesStorageSize() const
Returns the properties storage size.
Definition Operation.h:950
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:1139
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:431
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:363
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:253
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:392
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:403
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.
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:155
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:386