MLIR 24.0.0git
OperationSupport.cpp
Go to the documentation of this file.
1//===- OperationSupport.cpp -----------------------------------------------===//
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// This file contains out-of-line implementations of the support types that
10// Operation and related classes build on top of.
11//
12//===----------------------------------------------------------------------===//
13
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/Support/SHA1.h"
21#include <numeric>
22#include <optional>
23
24using namespace mlir;
25
27 llvm::SmallVectorImpl<NamedAttribute> &attrs, StringRef name,
28 Attribute attr) {
29 if (attr)
30 attrs.emplace_back(name, attr);
31}
32
34 OpAsmParser &parser,
38 if (!result.has_value())
39 return success();
40 if (failed(*result))
41 return failure();
42 operands.push_back(operand);
43 return success();
44}
45
47 SmallVectorImpl<Type> &types) {
48 Type type;
50 if (!result.has_value())
51 return success();
52 if (failed(*result))
53 return failure();
54 types.push_back(type);
55 return success();
56}
57
60 ArrayRef<StringRef> inherentNames,
61 llvm::function_ref<LogicalResult(DictionaryAttr)> setProperties) {
62 SmallVector<NamedAttribute> inherentAttributes;
63 for (const NamedAttribute &attr : attributes) {
64 StringRef name = attr.getName().getValue();
65 if (llvm::is_contained(inherentNames, name))
66 inherentAttributes.push_back(attr);
67 else
68 state.addAttribute(attr.getName(), attr.getValue());
69 }
70 if (inherentAttributes.empty())
71 return;
72 if (failed(setProperties(
73 DictionaryAttr::get(state.getContext(), inherentAttributes))))
74 llvm::report_fatal_error("Property conversion failed.");
75}
76
77//===----------------------------------------------------------------------===//
78// NamedAttrList
79//===----------------------------------------------------------------------===//
80
82 assign(attributes.begin(), attributes.end());
83}
84
85NamedAttrList::NamedAttrList(DictionaryAttr attributes)
86 : NamedAttrList(attributes ? attributes.getValue()
88 dictionarySorted.setPointerAndInt(attributes, true);
89}
90
92 assign(inStart, inEnd);
93}
94
96
97std::optional<NamedAttribute> NamedAttrList::findDuplicate() const {
98 std::optional<NamedAttribute> duplicate =
99 DictionaryAttr::findDuplicate(attrs, isSorted());
100 // DictionaryAttr::findDuplicate will sort the list, so reset the sorted
101 // state.
102 if (!isSorted())
103 dictionarySorted.setPointerAndInt(nullptr, true);
104 return duplicate;
105}
106
107DictionaryAttr NamedAttrList::getDictionary(MLIRContext *context) const {
108 if (!isSorted()) {
109 DictionaryAttr::sortInPlace(attrs);
110 dictionarySorted.setPointerAndInt(nullptr, true);
111 }
112 if (!dictionarySorted.getPointer())
113 dictionarySorted.setPointer(DictionaryAttr::getWithSorted(context, attrs));
114 return llvm::cast<DictionaryAttr>(dictionarySorted.getPointer());
115}
116
117/// Replaces the attributes with new list of attributes.
119 DictionaryAttr::sort(ArrayRef<NamedAttribute>{inStart, inEnd}, attrs);
120 dictionarySorted.setPointerAndInt(nullptr, true);
121}
122
124 if (isSorted())
125 dictionarySorted.setInt(attrs.empty() || attrs.back() < newAttribute);
126 dictionarySorted.setPointer(nullptr);
127 attrs.push_back(newAttribute);
128}
129
130/// Return the specified attribute if present, null otherwise.
131Attribute NamedAttrList::get(StringRef name) const {
132 auto it = findAttr(*this, name);
133 return it.second ? it.first->getValue() : Attribute();
134}
135Attribute NamedAttrList::get(StringAttr name) const {
136 auto it = findAttr(*this, name);
137 return it.second ? it.first->getValue() : Attribute();
138}
139
140/// Return the specified named attribute if present, std::nullopt otherwise.
141std::optional<NamedAttribute> NamedAttrList::getNamed(StringRef name) const {
142 auto it = findAttr(*this, name);
143 return it.second ? *it.first : std::optional<NamedAttribute>();
144}
145std::optional<NamedAttribute> NamedAttrList::getNamed(StringAttr name) const {
146 auto it = findAttr(*this, name);
147 return it.second ? *it.first : std::optional<NamedAttribute>();
148}
149
150/// If the an attribute exists with the specified name, change it to the new
151/// value. Otherwise, add a new attribute with the specified name/value.
152Attribute NamedAttrList::set(StringAttr name, Attribute value) {
153 assert(value && "attributes may never be null");
154
155 // Look for an existing attribute with the given name, and set its value
156 // in-place. Return the previous value of the attribute, if there was one.
157 auto it = findAttr(*this, name);
158 if (it.second) {
159 // Update the existing attribute by swapping out the old value for the new
160 // value. Return the old value.
161 Attribute oldValue = it.first->getValue();
162 if (it.first->getValue() != value) {
163 it.first->setValue(value);
164
165 // If the attributes have changed, the dictionary is invalidated.
166 dictionarySorted.setPointer(nullptr);
167 }
168 return oldValue;
169 }
170 // Perform a string lookup to insert the new attribute into its sorted
171 // position.
172 if (isSorted())
173 it = findAttr(*this, name.strref());
174 attrs.insert(it.first, {name, value});
175 // Invalidate the dictionary. Return null as there was no previous value.
176 dictionarySorted.setPointer(nullptr);
177 return Attribute();
178}
179
180Attribute NamedAttrList::set(StringRef name, Attribute value) {
181 assert(value && "attributes may never be null");
182 return set(mlir::StringAttr::get(value.getContext(), name), value);
183}
184
186NamedAttrList::eraseImpl(SmallVectorImpl<NamedAttribute>::iterator it) {
187 // Erasing does not affect the sorted property.
188 Attribute attr = it->getValue();
189 attrs.erase(it);
190 dictionarySorted.setPointer(nullptr);
191 return attr;
192}
193
195 auto it = findAttr(*this, name);
196 return it.second ? eraseImpl(it.first) : Attribute();
197}
198
200 auto it = findAttr(*this, name);
201 return it.second ? eraseImpl(it.first) : Attribute();
202}
203
206 assign(rhs.begin(), rhs.end());
207 return *this;
208}
209
210NamedAttrList::operator ArrayRef<NamedAttribute>() const { return attrs; }
211
212//===----------------------------------------------------------------------===//
213// OperationState
214//===----------------------------------------------------------------------===//
215
218
221
226 MutableArrayRef<std::unique_ptr<Region>> regions)
228 operands(operands.begin(), operands.end()),
229 types(types.begin(), types.end()),
230 attributes(attributes.begin(), attributes.end()),
231 successors(successors.begin(), successors.end()) {
232 for (std::unique_ptr<Region> &r : regions)
233 this->regions.push_back(std::move(r));
234}
242
244 if (properties)
245 propertiesDeleter(properties);
246}
247
250 if (LLVM_UNLIKELY(propertiesAttr)) {
251 assert(!properties);
253 }
254 if (properties)
255 propertiesSetter(op->getPropertiesStorage(), properties);
256 return success();
257}
258
260 operands.append(newOperands.begin(), newOperands.end());
261}
262
264 successors.append(newSuccessors.begin(), newSuccessors.end());
265}
266
268 regions.emplace_back(new Region);
269 return regions.back().get();
270}
271
272void OperationState::addRegion(std::unique_ptr<Region> &&region) {
273 regions.push_back(std::move(region));
274}
275
277 MutableArrayRef<std::unique_ptr<Region>> regions) {
278 for (std::unique_ptr<Region> &region : regions)
279 addRegion(std::move(region));
280}
281
282//===----------------------------------------------------------------------===//
283// OperandStorage
284//===----------------------------------------------------------------------===//
285
287 OpOperand *trailingOperands,
288 ValueRange values)
289 : isStorageDynamic(false), operandStorage(trailingOperands) {
290 numOperands = capacity = values.size();
291 for (unsigned i = 0; i < numOperands; ++i)
292 new (&operandStorage[i]) OpOperand(owner, values[i]);
293}
294
296 for (auto &operand : getOperands())
297 operand.~OpOperand();
298
299 // If the storage is dynamic, deallocate it.
300 if (isStorageDynamic)
301 free(operandStorage);
302}
303
304/// Replace the operands contained in the storage with the ones provided in
305/// 'values'.
307 MutableArrayRef<OpOperand> storageOperands = resize(owner, values.size());
308 for (unsigned i = 0, e = values.size(); i != e; ++i)
309 storageOperands[i].set(values[i]);
310}
311
312/// Replace the operands beginning at 'start' and ending at 'start' + 'length'
313/// with the ones provided in 'operands'. 'operands' may be smaller or larger
314/// than the range pointed to by 'start'+'length'.
316 unsigned length, ValueRange operands) {
317 // If the new size is the same, we can update inplace.
318 unsigned newSize = operands.size();
319 if (newSize == length) {
320 MutableArrayRef<OpOperand> storageOperands = getOperands();
321 for (unsigned i = 0, e = length; i != e; ++i)
322 storageOperands[start + i].set(operands[i]);
323 return;
324 }
325 // If the new size is greater, remove the extra operands and set the rest
326 // inplace.
327 if (newSize < length) {
328 eraseOperands(start + operands.size(), length - newSize);
329 setOperands(owner, start, newSize, operands);
330 return;
331 }
332 // Otherwise, the new size is greater so we need to grow the storage.
333 auto storageOperands = resize(owner, size() + (newSize - length));
334
335 // Shift operands to the right to make space for the new operands.
336 unsigned rotateSize = storageOperands.size() - (start + length);
337 auto rbegin = storageOperands.rbegin();
338 std::rotate(rbegin, std::next(rbegin, newSize - length), rbegin + rotateSize);
339
340 // Update the operands inplace.
341 for (unsigned i = 0, e = operands.size(); i != e; ++i)
342 storageOperands[start + i].set(operands[i]);
343}
344
345/// Erase an operand held by the storage.
346void detail::OperandStorage::eraseOperands(unsigned start, unsigned length) {
348 assert((start + length) <= operands.size());
349 numOperands -= length;
350
351 // Shift all operands down if the operand to remove is not at the end.
352 if (start != numOperands) {
353 auto *indexIt = std::next(operands.begin(), start);
354 std::rotate(indexIt, std::next(indexIt, length), operands.end());
355 }
356 for (unsigned i = 0; i != length; ++i)
357 operands[numOperands + i].~OpOperand();
358}
359
360void detail::OperandStorage::eraseOperands(const BitVector &eraseIndices) {
362 assert(eraseIndices.size() == operands.size());
363
364 // Check that at least one operand is erased.
365 int firstErasedIndice = eraseIndices.find_first();
366 if (firstErasedIndice == -1)
367 return;
368
369 // Shift all of the removed operands to the end, and destroy them.
370 numOperands = firstErasedIndice;
371 for (unsigned i = firstErasedIndice + 1, e = operands.size(); i < e; ++i)
372 if (!eraseIndices.test(i))
373 operands[numOperands++] = std::move(operands[i]);
374 for (OpOperand &operand : operands.drop_front(numOperands))
375 operand.~OpOperand();
376}
377
378/// Resize the storage to the given size. Returns the array containing the new
379/// operands.
380MutableArrayRef<OpOperand> detail::OperandStorage::resize(Operation *owner,
381 unsigned newSize) {
382 // If the number of operands is less than or equal to the current amount, we
383 // can just update in place.
384 MutableArrayRef<OpOperand> origOperands = getOperands();
385 if (newSize <= numOperands) {
386 // If the number of new size is less than the current, remove any extra
387 // operands.
388 for (unsigned i = newSize; i != numOperands; ++i)
389 origOperands[i].~OpOperand();
390 numOperands = newSize;
391 return origOperands.take_front(newSize);
392 }
393
394 // If the new size is within the original inline capacity, grow inplace.
395 if (newSize <= capacity) {
396 OpOperand *opBegin = origOperands.data();
397 for (unsigned e = newSize; numOperands != e; ++numOperands)
398 new (&opBegin[numOperands]) OpOperand(owner);
399 return MutableArrayRef<OpOperand>(opBegin, newSize);
400 }
401
402 // Otherwise, we need to allocate a new storage.
403 unsigned newCapacity =
404 std::max(unsigned(llvm::NextPowerOf2(capacity + 2)), newSize);
405 OpOperand *newOperandStorage =
406 reinterpret_cast<OpOperand *>(malloc(sizeof(OpOperand) * newCapacity));
407
408 // Move the current operands to the new storage.
409 MutableArrayRef<OpOperand> newOperands(newOperandStorage, newSize);
410 std::uninitialized_move(origOperands.begin(), origOperands.end(),
411 newOperands.begin());
412
413 // Destroy the original operands.
414 for (auto &operand : origOperands)
415 operand.~OpOperand();
416
417 // Initialize any new operands.
418 for (unsigned e = newSize; numOperands != e; ++numOperands)
419 new (&newOperands[numOperands]) OpOperand(owner);
420
421 // If the current storage is dynamic, free it.
422 if (isStorageDynamic)
423 free(operandStorage);
424
425 // Update the storage representation to use the new dynamic storage.
426 operandStorage = newOperandStorage;
427 capacity = newCapacity;
428 isStorageDynamic = true;
429 return newOperands;
430}
431
432//===----------------------------------------------------------------------===//
433// Operation Value-Iterators
434//===----------------------------------------------------------------------===//
435
436//===----------------------------------------------------------------------===//
437// OperandRange
438//===----------------------------------------------------------------------===//
439
441 assert(!empty() && "range must not be empty");
442 return base->getOperandNumber();
443}
444
446 return OperandRangeRange(*this, segmentSizes);
447}
448
449//===----------------------------------------------------------------------===//
450// OperandRangeRange
451//===----------------------------------------------------------------------===//
452
454 Attribute operandSegments)
455 : OperandRangeRange(OwnerT(operands.getBase(), operandSegments), 0,
456 llvm::cast<DenseI32ArrayAttr>(operandSegments).size()) {
457}
458
460 const OwnerT &owner = getBase();
461 ArrayRef<int32_t> sizeData = llvm::cast<DenseI32ArrayAttr>(owner.second);
462 return OperandRange(owner.first, llvm::sum_of(sizeData));
463}
464
465OperandRange OperandRangeRange::dereference(const OwnerT &object,
466 ptrdiff_t index) {
467 ArrayRef<int32_t> sizeData = llvm::cast<DenseI32ArrayAttr>(object.second);
468 uint32_t startIndex = llvm::sum_of(sizeData.take_front(index));
469 return OperandRange(object.first + startIndex, *(sizeData.begin() + index));
470}
471
472//===----------------------------------------------------------------------===//
473// MutableOperandRange
474//===----------------------------------------------------------------------===//
475
476/// Construct a new mutable range from the given operand, operand start index,
477/// and range length.
479 Operation *owner, unsigned start, unsigned length,
480 ArrayRef<OperandSegment> operandSegments)
481 : owner(owner), start(start), length(length),
482 operandSegments(operandSegments) {
483 assert((start + length) <= owner->getNumOperands() && "invalid range");
484}
487
488/// Construct a new mutable range for the given OpOperand.
490 : MutableOperandRange(opOperand.getOwner(),
491 /*start=*/opOperand.getOperandNumber(),
492 /*length=*/1) {}
493
494/// Slice this range into a sub range, with the additional operand segment.
496MutableOperandRange::slice(unsigned subStart, unsigned subLen,
497 std::optional<OperandSegment> segment) const {
498 assert((subStart + subLen) <= length && "invalid sub-range");
499 MutableOperandRange subSlice(owner, start + subStart, subLen,
500 operandSegments);
501 if (segment)
502 subSlice.operandSegments.push_back(*segment);
503 return subSlice;
504}
505
506/// Append the given values to the range.
508 if (values.empty())
509 return;
510 owner->insertOperands(start + length, values);
511 updateLength(length + values.size());
512}
513
514/// Assign this range to the given values.
516 owner->setOperands(start, length, values);
517 if (length != values.size())
518 updateLength(/*newLength=*/values.size());
519}
520
521/// Assign the range to the given value.
523 if (length == 1) {
524 owner->setOperand(start, value);
525 } else {
526 owner->setOperands(start, length, value);
527 updateLength(/*newLength=*/1);
528 }
529}
530
531/// Erase the operands within the given sub-range.
532void MutableOperandRange::erase(unsigned subStart, unsigned subLen) {
533 assert((subStart + subLen) <= length && "invalid sub-range");
534 if (length == 0)
535 return;
536 owner->eraseOperands(start + subStart, subLen);
537 updateLength(length - subLen);
538}
539
540/// Clear this range and erase all of the operands.
542 if (length != 0) {
543 owner->eraseOperands(start, length);
544 updateLength(/*newLength=*/0);
545 }
546}
547
548/// Explicit conversion to an OperandRange.
550 return owner->getOperands().slice(start, length);
551}
552
553/// Allow implicit conversion to an OperandRange.
554MutableOperandRange::operator OperandRange() const {
555 return getAsOperandRange();
556}
557
558MutableOperandRange::operator MutableArrayRef<OpOperand>() const {
559 return owner->getOpOperands().slice(start, length);
560}
561
564 return MutableOperandRangeRange(*this, segmentSizes);
565}
566
567/// Update the length of this range to the one provided.
568void MutableOperandRange::updateLength(unsigned newLength) {
569 int32_t diff = int32_t(newLength) - int32_t(length);
570 length = newLength;
571
572 // Update any of the provided segment attributes.
573 for (OperandSegment &segment : operandSegments) {
574 auto attr = llvm::cast<DenseI32ArrayAttr>(segment.second.getValue());
575 SmallVector<int32_t, 8> segments(attr.asArrayRef());
576 segments[segment.first] += diff;
577 segment.second.setValue(
578 DenseI32ArrayAttr::get(attr.getContext(), segments));
579 owner->setInherentAttr(segment.second.getName(), segment.second.getValue());
580 }
581}
582
584 assert(index < length && "index is out of bounds");
585 return owner->getOpOperand(start + index);
586}
587
589 return owner->getOpOperands().slice(start, length).begin();
590}
591
593 return owner->getOpOperands().slice(start, length).end();
594}
595
596//===----------------------------------------------------------------------===//
597// MutableOperandRangeRange
598//===----------------------------------------------------------------------===//
599
601 const MutableOperandRange &operands, NamedAttribute operandSegmentAttr)
603 OwnerT(operands, operandSegmentAttr), 0,
604 llvm::cast<DenseI32ArrayAttr>(operandSegmentAttr.getValue()).size()) {
605}
606
610
611MutableOperandRangeRange::operator OperandRangeRange() const {
612 return OperandRangeRange(getBase().first, getBase().second.getValue());
613}
614
615MutableOperandRange MutableOperandRangeRange::dereference(const OwnerT &object,
616 ptrdiff_t index) {
617 ArrayRef<int32_t> sizeData =
618 llvm::cast<DenseI32ArrayAttr>(object.second.getValue());
619 uint32_t startIndex = llvm::sum_of(sizeData.take_front(index));
620 return object.first.slice(
621 startIndex, *(sizeData.begin() + index),
623}
624
625//===----------------------------------------------------------------------===//
626// ResultRange
627//===----------------------------------------------------------------------===//
628
630 : ResultRange(static_cast<detail::OpResultImpl *>(Value(result).getImpl()),
631 1) {}
632
640 return use_iterator(*this, /*end=*/true);
641}
651
653 : it(end ? results.end() : results.begin()), endIt(results.end()) {
654 // Only initialize current use if there are results/can be uses.
655 if (it != endIt)
656 skipOverResultsWithNoUsers();
657}
658
660 // We increment over uses, if we reach the last use then move to next
661 // result.
662 if (use != (*it).use_end())
663 ++use;
664 if (use == (*it).use_end()) {
665 ++it;
666 skipOverResultsWithNoUsers();
667 }
668 return *this;
669}
670
671void ResultRange::UseIterator::skipOverResultsWithNoUsers() {
672 while (it != endIt && (*it).use_empty())
673 ++it;
674
675 // If we are at the last result, then set use to first use of
676 // first result (sentinel value used for end).
677 if (it == endIt)
678 use = {};
679 else
680 use = (*it).use_begin();
681}
682
686
688 Operation *op, function_ref<bool(OpOperand &)> shouldReplace) {
689 replaceUsesWithIf(op->getResults(), shouldReplace);
690}
691
692//===----------------------------------------------------------------------===//
693// ValueRange
694//===----------------------------------------------------------------------===//
695
697 : ValueRange(values.data(), values.size()) {}
699 : ValueRange(values.begin().getBase(), values.size()) {}
701 : ValueRange(values.getBase(), values.size()) {}
702
703/// See `llvm::detail::indexed_accessor_range_base` for details.
704ValueRange::OwnerT ValueRange::offset_base(const OwnerT &owner,
705 ptrdiff_t index) {
706 if (const auto *value = llvm::dyn_cast_if_present<const Value *>(owner))
707 return {value + index};
708 if (auto *operand = llvm::dyn_cast_if_present<OpOperand *>(owner))
709 return {operand + index};
710 // All elements are identical; the owner pointer never advances.
711 if (llvm::isa<const Repeated<Value> *>(owner))
712 return owner;
713 return cast<detail::OpResultImpl *>(owner)->getNextResultAtOffset(index);
714}
715/// See `llvm::detail::indexed_accessor_range_base` for details.
716Value ValueRange::dereference_iterator(const OwnerT &owner, ptrdiff_t index) {
717 if (const auto *value = llvm::dyn_cast_if_present<const Value *>(owner))
718 return value[index];
719 if (auto *operand = llvm::dyn_cast_if_present<OpOperand *>(owner))
720 return operand[index].get();
721 if (auto *repeated =
722 llvm::dyn_cast_if_present<const Repeated<Value> *>(owner))
723 return repeated->value();
724 return cast<detail::OpResultImpl *>(owner)->getNextResultAtOffset(index);
725}
726
727//===----------------------------------------------------------------------===//
728// Operation Equivalency
729//===----------------------------------------------------------------------===//
730
732 Operation *op, function_ref<llvm::hash_code(Value)> hashOperands,
733 function_ref<llvm::hash_code(Value)> hashResults, Flags flags) {
734 // Hash operations based upon their:
735 // - Operation Name
736 // - Attributes
737 // - Result Types
738 DictionaryAttr dictAttrs;
739 if (!(flags & Flags::IgnoreDiscardableAttrs))
740 dictAttrs = op->getRawDictionaryAttrs();
741 llvm::hash_code hash =
742 llvm::hash_combine(op->getName(), dictAttrs, op->getResultTypes());
743 if (!(flags & Flags::IgnoreProperties))
744 hash = llvm::hash_combine(hash, op->hashProperties());
745
746 // - Location if required
747 if (!(flags & Flags::IgnoreLocations))
748 hash = llvm::hash_combine(hash, op->getLoc());
749
750 // - Operands
751 if (!(flags & Flags::IgnoreCommutativity) &&
753 op->getNumOperands() > 0) {
754 size_t operandHash = hashOperands(op->getOperand(0));
755 for (auto operand : op->getOperands().drop_front())
756 operandHash += hashOperands(operand);
757 hash = llvm::hash_combine(hash, operandHash);
758 } else {
759 for (Value operand : op->getOperands())
760 hash = llvm::hash_combine(hash, hashOperands(operand));
761 }
762
763 // - Results
764 for (Value result : op->getResults())
765 hash = llvm::hash_combine(hash, hashResults(result));
766 return hash;
767}
768
770 Region *lhs, Region *rhs,
771 function_ref<LogicalResult(Value, Value)> checkEquivalent,
772 function_ref<void(Value, Value)> markEquivalent,
774 function_ref<LogicalResult(ValueRange, ValueRange)>
775 checkCommutativeEquivalent) {
777 auto blocksEquivalent = [&](Block &lBlock, Block &rBlock) {
778 // Check block arguments.
779 if (lBlock.getNumArguments() != rBlock.getNumArguments())
780 return false;
781
782 // Map the two blocks.
783 auto insertion = blocksMap.insert({&lBlock, &rBlock});
784 if (insertion.first->getSecond() != &rBlock)
785 return false;
786
787 for (auto argPair :
788 llvm::zip(lBlock.getArguments(), rBlock.getArguments())) {
789 Value curArg = std::get<0>(argPair);
790 Value otherArg = std::get<1>(argPair);
791 if (curArg.getType() != otherArg.getType())
792 return false;
794 curArg.getLoc() != otherArg.getLoc())
795 return false;
796 // Corresponding bbArgs are equivalent.
797 if (markEquivalent)
798 markEquivalent(curArg, otherArg);
799 }
800
801 auto opsEquivalent = [&](Operation &lOp, Operation &rOp) {
802 // Check for op equality (recursively).
803 if (!OperationEquivalence::isEquivalentTo(&lOp, &rOp, checkEquivalent,
804 markEquivalent, flags,
805 checkCommutativeEquivalent))
806 return false;
807 // Check successor mapping.
808 for (auto successorsPair :
809 llvm::zip(lOp.getSuccessors(), rOp.getSuccessors())) {
810 Block *curSuccessor = std::get<0>(successorsPair);
811 Block *otherSuccessor = std::get<1>(successorsPair);
812 auto insertion = blocksMap.insert({curSuccessor, otherSuccessor});
813 if (insertion.first->getSecond() != otherSuccessor)
814 return false;
815 }
816 return true;
817 };
818 return llvm::all_of_zip(lBlock, rBlock, opsEquivalent);
819 };
820 return llvm::all_of_zip(*lhs, *rhs, blocksEquivalent);
821}
822
823// Value equivalence cache to be used with `isRegionEquivalentTo` and
824// `isEquivalentTo`.
827 LogicalResult checkEquivalent(Value lhsValue, Value rhsValue) {
828 return success(lhsValue == rhsValue ||
829 equivalentValues.lookup(lhsValue) == rhsValue);
830 }
832 ValueRange rhsRange) {
833 // Handle simple case where sizes mismatch.
834 if (lhsRange.size() != rhsRange.size())
835 return failure();
836
837 // Handle where operands in order are equivalent.
838 auto lhsIt = lhsRange.begin();
839 auto rhsIt = rhsRange.begin();
840 for (; lhsIt != lhsRange.end(); ++lhsIt, ++rhsIt) {
841 if (failed(checkEquivalent(*lhsIt, *rhsIt)))
842 break;
843 }
844 if (lhsIt == lhsRange.end())
845 return success();
846
847 // Replace values with their entry in equivalentValues if they're in there
848 // that way, a sorted pointer comparison is enough to determine
849 // commutativity.
850 auto sortValues = [this](ValueRange values) {
851 SmallVector<Value> sortedValues = llvm::map_to_vector(
852 values, [this](Value a) { return equivalentValues.lookup_or(a, a); });
853 llvm::sort(sortedValues, [](Value a, Value b) {
854 return a.getAsOpaquePointer() < b.getAsOpaquePointer();
855 });
856 return sortedValues;
857 };
858 auto lhsSorted = sortValues({lhsIt, lhsRange.end()});
859 auto rhsSorted = sortValues({rhsIt, rhsRange.end()});
860 return success(lhsSorted == rhsSorted);
861 }
862 void markEquivalent(Value lhsResult, Value rhsResult) {
863 auto insertion = equivalentValues.insert({lhsResult, rhsResult});
864 // Make sure that the value was not already marked equivalent to some other
865 // value.
866 (void)insertion;
867 assert(insertion.first->second == rhsResult &&
868 "inconsistent OperationEquivalence state");
869 }
870};
871
872/*static*/ bool
877 lhs, rhs,
878 [&](Value lhsValue, Value rhsValue) -> LogicalResult {
879 return cache.checkEquivalent(lhsValue, rhsValue);
880 },
881 [&](Value lhsResult, Value rhsResult) {
882 cache.markEquivalent(lhsResult, rhsResult);
883 },
884 flags,
885 [&](ValueRange lhs, ValueRange rhs) -> LogicalResult {
886 return cache.checkCommutativeEquivalent(lhs, rhs);
887 });
888}
889
891 Operation *lhs, Operation *rhs,
892 function_ref<LogicalResult(Value, Value)> checkEquivalent,
893 function_ref<void(Value, Value)> markEquivalent, Flags flags,
894 function_ref<LogicalResult(ValueRange, ValueRange)>
895 checkCommutativeEquivalent) {
896 if (lhs == rhs)
897 return true;
898
899 // 1. Compare the operation properties.
900 if (!(flags & IgnoreDiscardableAttrs) &&
901 lhs->getRawDictionaryAttrs() != rhs->getRawDictionaryAttrs())
902 return false;
903
904 if (lhs->getName() != rhs->getName() ||
905 lhs->getNumRegions() != rhs->getNumRegions() ||
906 lhs->getNumSuccessors() != rhs->getNumSuccessors() ||
907 lhs->getNumOperands() != rhs->getNumOperands() ||
908 lhs->getNumResults() != rhs->getNumResults())
909 return false;
910 if (!(flags & IgnoreProperties) &&
911 !(lhs->getName().compareOpProperties(lhs->getPropertiesStorage(),
912 rhs->getPropertiesStorage())))
913 return false;
914 if (!(flags & IgnoreLocations) && lhs->getLoc() != rhs->getLoc())
915 return false;
916
917 // 2. Compare operands.
918 if (!(flags & IgnoreCommutativity) && checkCommutativeEquivalent &&
919 lhs->hasTrait<mlir::OpTrait::IsCommutative>()) {
920 auto lhsRange = lhs->getOperands();
921 auto rhsRange = rhs->getOperands();
922 if (failed(checkCommutativeEquivalent(lhsRange, rhsRange)))
923 return false;
924 } else {
925 // Check pair wise for equivalence.
926 for (auto operandPair : llvm::zip(lhs->getOperands(), rhs->getOperands())) {
927 Value curArg = std::get<0>(operandPair);
928 Value otherArg = std::get<1>(operandPair);
929 if (curArg == otherArg)
930 continue;
931 if (curArg.getType() != otherArg.getType())
932 return false;
933 if (failed(checkEquivalent(curArg, otherArg)))
934 return false;
935 }
936 }
937
938 // 3. Compare result types and mark results as equivalent.
939 for (auto resultPair : llvm::zip(lhs->getResults(), rhs->getResults())) {
940 Value curArg = std::get<0>(resultPair);
941 Value otherArg = std::get<1>(resultPair);
942 if (curArg.getType() != otherArg.getType())
943 return false;
944 if (markEquivalent)
945 markEquivalent(curArg, otherArg);
946 }
947
948 // 4. Compare regions.
949 for (auto regionPair : llvm::zip(lhs->getRegions(), rhs->getRegions()))
951 &std::get<0>(regionPair), &std::get<1>(regionPair), checkEquivalent,
952 markEquivalent, flags, checkCommutativeEquivalent))
953 return false;
954
955 return true;
956}
957
959 Operation *rhs,
960 Flags flags) {
963 lhs, rhs,
964 [&](Value lhsValue, Value rhsValue) -> LogicalResult {
965 return cache.checkEquivalent(lhsValue, rhsValue);
966 },
967 [&](Value lhsResult, Value rhsResult) {
968 cache.markEquivalent(lhsResult, rhsResult);
969 },
970 flags,
971 [&](ValueRange lhs, ValueRange rhs) -> LogicalResult {
972 return cache.checkCommutativeEquivalent(lhs, rhs);
973 });
974}
975
976//===----------------------------------------------------------------------===//
977// OperationFingerPrint
978//===----------------------------------------------------------------------===//
979
980template <typename T>
981static void addDataToHash(llvm::SHA1 &hasher, const T &data) {
982 hasher.update(
983 ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&data), sizeof(T)));
984}
985
987 bool includeNested) {
988 llvm::SHA1 hasher;
989
990 // Helper function that hashes an operation based on its mutable bits:
991 auto addOperationToHash = [&](Operation *op) {
992 // - Operation pointer
993 addDataToHash(hasher, op);
994 // - Parent operation pointer (to take into account the nesting structure)
995 if (op != topOp)
996 addDataToHash(hasher, op->getParentOp());
997 // - Attributes
999 // - Properties
1000 addDataToHash(hasher, op->hashProperties());
1001 // - Blocks in Regions
1002 for (Region &region : op->getRegions()) {
1003 for (Block &block : region) {
1004 addDataToHash(hasher, &block);
1005 for (BlockArgument arg : block.getArguments())
1006 addDataToHash(hasher, arg);
1007 }
1008 }
1009 // - Location
1010 addDataToHash(hasher, op->getLoc().getAsOpaquePointer());
1011 // - Operands
1012 for (Value operand : op->getOperands())
1013 addDataToHash(hasher, operand);
1014 // - Successors
1015 for (unsigned i = 0, e = op->getNumSuccessors(); i != e; ++i)
1016 addDataToHash(hasher, op->getSuccessor(i));
1017 // - Result types
1018 for (Type t : op->getResultTypes())
1019 addDataToHash(hasher, t);
1020 };
1021
1022 if (includeNested)
1023 topOp->walk(addOperationToHash);
1024 else
1025 addOperationToHash(topOp);
1026
1027 hash = hasher.result();
1028}
1029
return success()
getNumOperands() - 1))) return failure()
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
static Value getBase(Value v)
Looks through known "view-like" ops to find the base memref.
static size_t hash(const T &value)
Local helper to compute std::hash for a value.
Definition IRCore.cpp:57
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
false
Parses a map_entries map type from a string format back into its numeric value.
static void addDataToHash(llvm::SHA1 &hasher, const T &data)
#define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME)
Definition TypeID.h:323
This base class exposes generic asm parser hooks, usable across the various derived parsers.
virtual OptionalParseResult parseOptionalType(Type &result)=0
Parse an optional type.
Attributes are known-constant values of operations.
Definition Attributes.h:25
MLIRContext * getContext() const
Return the context this attribute belongs to.
This class represents an argument of a Block.
Definition Value.h:306
This class provides an abstraction over the different types of ranges over Blocks.
Block represents an ordered list of Operations.
Definition Block.h:34
unsigned getNumArguments()
Definition Block.h:153
BlockArgListType getArguments()
Definition Block.h:112
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
const void * getAsOpaquePointer() const
Methods for supporting PointerLikeTypeTraits.
Definition Location.h:103
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
This class represents a contiguous range of mutable operand ranges, e.g.
Definition ValueRange.h:211
MutableOperandRange join() const
Flatten all of the sub ranges into a single contiguous mutable operand range.
MutableOperandRangeRange(const MutableOperandRange &operands, NamedAttribute operandSegmentAttr)
Construct a range given a parent set of operands, and an I32 tensor elements attribute containing the...
This class provides a mutable adaptor for a range of operands.
Definition ValueRange.h:119
Operation * getOwner() const
Returns the owning operation.
Definition ValueRange.h:172
OperandRange getAsOperandRange() const
Explicit conversion to an OperandRange.
void assign(ValueRange values)
Assign this range to the given values.
MutableOperandRange slice(unsigned subStart, unsigned subLen, std::optional< OperandSegment > segment=std::nullopt) const
Slice this range into a sub range, with the additional operand segment.
MutableArrayRef< OpOperand >::iterator end() const
void erase(unsigned subStart, unsigned subLen=1)
Erase the operands within the given sub-range.
void append(ValueRange values)
Append the given values to the range.
void clear()
Clear this range and erase all of the operands.
MutableArrayRef< OpOperand >::iterator begin() const
Iterators enumerate OpOperands.
MutableOperandRange(Operation *owner, unsigned start, unsigned length, ArrayRef< OperandSegment > operandSegments={})
Construct a new mutable range from the given operand, operand start index, and range length.
std::pair< unsigned, NamedAttribute > OperandSegment
A pair of a named attribute corresponding to an operand segment attribute, and the index within that ...
Definition ValueRange.h:124
MutableOperandRangeRange split(NamedAttribute segmentSizes) const
Split this range into a set of contiguous subranges using the given elements attribute,...
OpOperand & operator[](unsigned index) const
Returns the OpOperand at the given index.
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
std::optional< NamedAttribute > getNamed(StringRef name) const
Return the specified named attribute if present, std::nullopt otherwise.
void assign(const_iterator inStart, const_iterator inEnd)
Replaces the attributes with new list of attributes.
SmallVectorImpl< NamedAttribute >::const_iterator const_iterator
ArrayRef< NamedAttribute > getAttrs() const
Return all of the attributes on this operation.
DictionaryAttr getDictionary(MLIRContext *context) const
Return a dictionary attribute for the underlying dictionary.
void push_back(NamedAttribute newAttribute)
Add an attribute with the specified name.
Attribute get(StringAttr name) const
Return the specified attribute if present, null otherwise.
Attribute erase(StringAttr name)
Erase the attribute with the given name from the list.
std::optional< NamedAttribute > findDuplicate() const
Returns an entry with a duplicate name the list, if it exists, else returns std::nullopt.
Attribute set(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
NamedAttrList & operator=(const SmallVectorImpl< NamedAttribute > &rhs)
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual OptionalParseResult parseOptionalOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single operand if present.
This class represents an operand of an operation.
Definition Value.h:254
This is a value defined by a result of an operation.
Definition Value.h:454
This class adds property that the operation is commutative.
This class represents a contiguous range of operand ranges, e.g.
Definition ValueRange.h:85
OperandRangeRange(OperandRange operands, Attribute operandSegments)
Construct a range given a parent set of operands, and an I32 elements attribute containing the sizes ...
OperandRange join() const
Flatten all of the sub ranges into a single contiguous operand range.
This class implements the operand iterators for the Operation class.
Definition ValueRange.h:44
unsigned getBeginOperandIndex() const
Return the operand index of the first element of this range.
OperandRangeRange split(DenseI32ArrayAttr segmentSizes) const
Split this range into a set of contiguous subranges using the given elements attribute,...
OperationFingerPrint(Operation *topOp, bool includeNested=true)
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:953
void setInherentAttr(StringAttr name, Attribute value)
Set an inherent attribute by name.
Value getOperand(unsigned idx)
Definition Operation.h:375
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:801
unsigned getNumSuccessors()
Definition Operation.h:758
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
DictionaryAttr getRawDictionaryAttrs()
Return all attributes that are not stored as properties.
Definition Operation.h:561
unsigned getNumOperands()
Definition Operation.h:371
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
LogicalResult setPropertiesFromAttribute(Attribute attr, function_ref< InFlightDiagnostic()> emitError)
Set the properties from the provided attribute.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:729
result_type_range getResultTypes()
Definition Operation.h:453
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition Operation.h:849
Block * getSuccessor(unsigned index)
Definition Operation.h:760
SuccessorRange getSuccessors()
Definition Operation.h:755
result_range getResults()
Definition Operation.h:440
llvm::hash_code hashProperties()
Compute a hash for the op properties (if any).
This class implements Optional functionality for ParseResult.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
This class implements a use iterator for a range of operation results.
Definition ValueRange.h:351
UseIterator(ResultRange results, bool end=false)
Initialize the UseIterator.
This class implements the result iterators for the Operation class.
Definition ValueRange.h:248
std::enable_if_t<!std::is_convertible< ValuesT, Operation * >::value > replaceUsesWithIf(ValuesT &&values, function_ref< bool(OpOperand &)> shouldReplace)
Replace uses of results of this range with the provided 'values' if the given callback returns true.
Definition ValueRange.h:304
iterator_range< user_iterator > user_range
Definition ValueRange.h:324
use_range getUses() const
Returns a range of all uses of results within this range, which is useful for iterating over all uses...
use_iterator use_begin() const
user_range getUsers()
Returns a range of all users.
ValueUserIterator< use_iterator, OpOperand > user_iterator
Definition ValueRange.h:323
ResultRange(OpResult result)
use_iterator use_end() const
user_iterator user_end()
user_iterator user_begin()
iterator_range< use_iterator > use_range
Definition ValueRange.h:269
UseIterator use_iterator
Definition ValueRange.h:268
std::enable_if_t<!std::is_convertible< ValuesT, Operation * >::value > replaceAllUsesWith(ValuesT &&values)
Replace all uses of results of this range with the provided 'values'.
Definition ValueRange.h:287
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
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
PointerUnion< const Value *, OpOperand *, detail::OpResultImpl *, const Repeated< Value > * > OwnerT
The type representing the owner of a ValueRange.
Definition ValueRange.h:393
ValueRange(Arg &&arg LLVM_LIFETIME_BOUND)
Definition ValueRange.h:402
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
void * getAsOpaquePointer() const
Methods for supporting PointerLikeTypeTraits.
Definition Value.h:233
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
static DenseArrayAttrImpl get(MLIRContext *context, ArrayRef< int32_t > content)
void eraseOperands(unsigned start, unsigned length)
Erase the operands held by the storage within the given range.
MutableArrayRef< OpOperand > getOperands()
Get the operation operands held by the storage.
unsigned size()
Return the number of operands held in the storage.
void setOperands(Operation *owner, ValueRange values)
Replace the operands contained in the storage with the ones provided in 'values'.
OperandStorage(Operation *owner, OpOperand *trailingOperands, ValueRange values)
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition CallGraph.h:227
AttrTypeReplacer.
void appendAttributeProperty(llvm::SmallVectorImpl< NamedAttribute > &attrs, StringRef name, Attribute attr)
Append a present attribute-backed property to a dictionary's attributes.
ParseResult parseOptionalTypeInto(AsmParser &parser, SmallVectorImpl< Type > &types)
void splitPropertiesAndDiscardableAttributes(OperationState &state, ArrayRef< NamedAttribute > attributes, ArrayRef< StringRef > inherentNames, llvm::function_ref< LogicalResult(DictionaryAttr)> setProperties)
Route legacy builder attributes to either the operation's properties or its discardable attribute dic...
ParseResult parseOptionalOperandInto(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &operands)
Parse an optional operand or type into a generated parser's storage.
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
detail::DenseArrayAttrImpl< int32_t > DenseI32ArrayAttr
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123
llvm::function_ref< Fn > function_ref
Definition LLVM.h:155
LogicalResult checkEquivalent(Value lhsValue, Value rhsValue)
void markEquivalent(Value lhsResult, Value rhsResult)
LogicalResult checkCommutativeEquivalent(ValueRange lhsRange, ValueRange rhsRange)
DenseMap< Value, Value > equivalentValues
Structure used by default as a "marker" when no "Properties" are set on an Operation.
This is the representation of an operand reference.
static bool isRegionEquivalentTo(Region *lhs, Region *rhs, function_ref< LogicalResult(Value, Value)> checkEquivalent, function_ref< void(Value, Value)> markEquivalent, OperationEquivalence::Flags flags, function_ref< LogicalResult(ValueRange, ValueRange)> checkCommutativeEquivalent=nullptr)
Compare two regions (including their subregions) and return if they are equivalent.
static bool isEquivalentTo(Operation *lhs, Operation *rhs, function_ref< LogicalResult(Value, Value)> checkEquivalent, function_ref< void(Value, Value)> markEquivalent=nullptr, Flags flags=Flags::None, function_ref< LogicalResult(ValueRange, ValueRange)> checkCommutativeEquivalent=nullptr)
Compare two operations (including their regions) and return if they are equivalent.
static llvm::hash_code computeHash(Operation *op, function_ref< llvm::hash_code(Value)> hashOperands=[](Value v) { return hash_value(v);}, function_ref< llvm::hash_code(Value)> hashResults=[](Value v) { return hash_value(v);}, Flags flags=Flags::None)
Compute a hash for the given operation.
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
void addOperands(ValueRange newOperands)
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
void addSuccessors(Block *successor)
Adds a successor to the operation sate. successor must not be null.
void addRegions(MutableArrayRef< std::unique_ptr< Region > > regions)
Take ownership of a set of regions that should be attached to the Operation.
MLIRContext * getContext() const
Get the context held by this operation state.
SmallVector< std::unique_ptr< Region >, 1 > regions
Regions that the op will hold.
OperationState(Location location, StringRef name)
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.
Region * addRegion()
Create a region that should be attached to the operation.
LogicalResult setProperties(Operation *op, function_ref< InFlightDiagnostic()> emitError) const