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