MLIR 23.0.0git
OperationSupport.h
Go to the documentation of this file.
1//===- OperationSupport.h ---------------------------------------*- C++ -*-===//
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 defines a number of support types that Operation and related
10// classes build on top of.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef MLIR_IR_OPERATIONSUPPORT_H
15#define MLIR_IR_OPERATIONSUPPORT_H
16
17#include "mlir/IR/Attributes.h"
20#include "mlir/IR/Diagnostics.h"
22#include "mlir/IR/Location.h"
23#include "mlir/IR/TypeRange.h"
24#include "mlir/IR/Types.h"
25#include "mlir/IR/Value.h"
27#include "llvm/ADT/BitmaskEnum.h"
28#include "llvm/ADT/PointerUnion.h"
29#include "llvm/ADT/STLFunctionalExtras.h"
30#include "llvm/Support/ErrorHandling.h"
31#include "llvm/Support/PointerLikeTypeTraits.h"
32#include "llvm/Support/TrailingObjects.h"
33#include <memory>
34#include <optional>
35
36namespace llvm {
37class BitVector;
38} // namespace llvm
39
40namespace mlir {
41class Dialect;
42class DictionaryAttr;
43class ElementsAttr;
44struct EmptyProperties;
46class NamedAttrList;
47class Operation;
48struct OperationState;
49class OpAsmParser;
50class OpAsmPrinter;
51class OperandRange;
53class OpFoldResult;
54class Pattern;
55class Region;
56class ResultRange;
57class RewritePattern;
59class Type;
60class Value;
61class ValueRange;
62template <typename ValueRangeT>
63class ValueTypeRange;
64
65//===----------------------------------------------------------------------===//
66// PropertyRef
67//===----------------------------------------------------------------------===//
68
69/// Type-safe wrapper around a void* for passing properties, including the
70/// properties structs of operations, generically through APIs. Pairs data with
71/// a TypeID for assert-based type checking. Note that the type in the type ID
72/// is the **storage** type of the property, and that the default object has a
73/// null data pointer and a type ID equal to the type ID for `void`.
75public:
76 PropertyRef() = default;
77 PropertyRef(TypeID typeID, void *data) : typeID(typeID), data(data) {}
78 operator bool() const { return data != nullptr; }
79 template <typename Dest>
80 Dest as() const {
81 static_assert(std::is_pointer_v<Dest>,
82 "PropertyRef::as<T>() requires T to be a pointer type");
83 using RawType = std::remove_cv_t<std::remove_pointer_t<Dest>>;
84 assert((typeID == TypeID::get<RawType>()) &&
85 "Property type mismatch: TypeID does not match requested type");
86 return static_cast<Dest>(data);
87 }
88 TypeID getTypeID() const { return typeID; }
89
90private:
91 TypeID typeID;
92 void *data = nullptr;
93};
94
95//===----------------------------------------------------------------------===//
96// OperationName
97//===----------------------------------------------------------------------===//
98
100public:
101 using FoldHookFn = llvm::unique_function<LogicalResult(
103 using HasTraitFn = llvm::unique_function<bool(TypeID) const>;
105 llvm::unique_function<ParseResult(OpAsmParser &, OperationState &)>;
106 // Note: RegisteredOperationName is passed as reference here as the derived
107 // class is defined below.
109 llvm::unique_function<void(const OperationName &, NamedAttrList &) const>;
111 llvm::unique_function<void(Operation *, OpAsmPrinter &, StringRef) const>;
113 llvm::unique_function<LogicalResult(Operation *) const>;
115 llvm::unique_function<LogicalResult(Operation *) const>;
116
117 /// This class represents a type erased version of an operation. It contains
118 /// all of the components necessary for opaquely interacting with an
119 /// operation. If the operation is not registered, some of these components
120 /// may not be populated.
122 virtual ~InterfaceConcept() = default;
123 virtual LogicalResult foldHook(Operation *, ArrayRef<Attribute>,
126 MLIRContext *) = 0;
127 virtual bool hasTrait(TypeID) = 0;
130 NamedAttrList &) = 0;
131 virtual void printAssembly(Operation *, OpAsmPrinter &, StringRef) = 0;
132 virtual LogicalResult verifyInvariants(Operation *) = 0;
133 virtual LogicalResult verifyRegionInvariants(Operation *) = 0;
134 /// Implementation for properties
135 virtual std::optional<Attribute> getInherentAttr(Operation *,
136 StringRef name) = 0;
137 virtual void setInherentAttr(Operation *op, StringAttr name,
138 Attribute value) = 0;
139 virtual void populateInherentAttrs(Operation *op, NamedAttrList &attrs) = 0;
140 virtual LogicalResult
143 virtual int getOpPropertyByteSize() = 0;
144 virtual void initProperties(OperationName opName, PropertyRef storage,
145 PropertyRef init) = 0;
146 virtual void deleteProperties(PropertyRef) = 0;
148 PropertyRef properties) = 0;
149 virtual LogicalResult
155 virtual llvm::hash_code hashProperties(PropertyRef) = 0;
156 };
157
158public:
159 class Impl : public InterfaceConcept {
160 public:
161 Impl(StringRef, Dialect *dialect, TypeID typeID,
167
168 /// Returns true if this is a registered operation.
169 bool isRegistered() const { return typeID != TypeID::get<void>(); }
171 Dialect *getDialect() const { return dialect; }
172 StringAttr getName() const { return name; }
173 TypeID getTypeID() const { return typeID; }
176
177 protected:
178 //===------------------------------------------------------------------===//
179 // Registered Operation Info
180
181 /// The name of the operation.
182 StringAttr name;
183
184 /// The unique identifier of the derived Op class.
186
187 /// The following fields are only populated when the operation is
188 /// registered.
189
190 /// This is the dialect that this operation belongs to.
192
193 /// A map of interfaces that were registered to this operation.
195
196 /// A list of attribute names registered to this operation in StringAttr
197 /// form. This allows for operation classes to use StringAttr for attribute
198 /// lookup/creation/etc., as opposed to raw strings.
200
201 /// The TypeID of the Properties struct for this operation.
203
205 };
206
207protected:
208 /// Default implementation for unregistered operations.
209 struct UnregisteredOpModel : public Impl {
215 LogicalResult foldHook(Operation *, ArrayRef<Attribute>,
218 bool hasTrait(TypeID) final;
220 void populateDefaultAttrs(const OperationName &, NamedAttrList &) final;
221 void printAssembly(Operation *, OpAsmPrinter &, StringRef) final;
222 LogicalResult verifyInvariants(Operation *) final;
223 LogicalResult verifyRegionInvariants(Operation *) final;
224 /// Implementation for properties
225 std::optional<Attribute> getInherentAttr(Operation *op,
226 StringRef name) final;
227 void setInherentAttr(Operation *op, StringAttr name, Attribute value) final;
228 void populateInherentAttrs(Operation *op, NamedAttrList &attrs) final;
229 LogicalResult
232 int getOpPropertyByteSize() final;
233 void initProperties(OperationName opName, PropertyRef storage,
234 PropertyRef init) final;
235 void deleteProperties(PropertyRef) final;
237 PropertyRef properties) final;
238 LogicalResult
244 llvm::hash_code hashProperties(PropertyRef) final;
245 };
246
247public:
248 OperationName(StringRef name, MLIRContext *context);
249
250 /// Return if this operation is registered.
251 bool isRegistered() const { return getImpl()->isRegistered(); }
252
253 /// Return the unique identifier of the derived Op class, or null if not
254 /// registered.
255 TypeID getTypeID() const { return getImpl()->getTypeID(); }
256
257 /// If this operation is registered, returns the registered information,
258 /// std::nullopt otherwise.
259 std::optional<RegisteredOperationName> getRegisteredInfo() const;
260
261 /// This hook implements a generalized folder for this operation. Operations
262 /// can implement this to provide simplifications rules that are applied by
263 /// the Builder::createOrFold API and the canonicalization pass.
264 ///
265 /// This is an intentionally limited interface - implementations of this
266 /// hook can only perform the following changes to the operation:
267 ///
268 /// 1. They can leave the operation alone and without changing the IR, and
269 /// return failure.
270 /// 2. They can mutate the operation in place, without changing anything
271 /// else in the IR. In this case, return success.
272 /// 3. They can return a list of existing values that can be used instead
273 /// of the operation. In this case, fill in the results list and return
274 /// success. The caller will remove the operation and use those results
275 /// instead.
276 ///
277 /// This allows expression of some simple in-place canonicalizations (e.g.
278 /// "x+0 -> x", "min(x,y,x,z) -> min(x,y,z)", "x+y-x -> y", etc), as well as
279 /// generalized constant folding.
280 LogicalResult foldHook(Operation *op, ArrayRef<Attribute> operands,
281 SmallVectorImpl<OpFoldResult> &results) const {
282 return getImpl()->foldHook(op, operands, results);
283 }
284
285 /// This hook returns any canonicalization pattern rewrites that the
286 /// operation supports, for use by the canonicalization pass.
288 MLIRContext *context) const {
289 return getImpl()->getCanonicalizationPatterns(results, context);
290 }
291
292 /// Returns true if the operation was registered with a particular trait, e.g.
293 /// hasTrait<OperandsAreSignlessIntegerLike>(). Returns false if the operation
294 /// is unregistered.
295 template <template <typename T> class Trait>
296 bool hasTrait() const {
298 }
299 bool hasTrait(TypeID traitID) const { return getImpl()->hasTrait(traitID); }
300
301 /// Returns true if the operation *might* have the provided trait. This
302 /// means that either the operation is unregistered, or it was registered with
303 /// the provide trait.
304 template <template <typename T> class Trait>
305 bool mightHaveTrait() const {
307 }
308 bool mightHaveTrait(TypeID traitID) const {
309 return !isRegistered() || getImpl()->hasTrait(traitID);
310 }
311
312 /// Return the static hook for parsing this operation assembly.
316
317 /// This hook implements the method to populate defaults attributes that are
318 /// unset.
320 getImpl()->populateDefaultAttrs(*this, attrs);
321 }
322
323 /// This hook implements the AsmPrinter for this operation.
325 StringRef defaultDialect) const {
326 return getImpl()->printAssembly(op, p, defaultDialect);
327 }
328
329 /// These hooks implement the verifiers for this operation. It should emits
330 /// an error message and returns failure if a problem is detected, or
331 /// returns success if everything is ok.
332 LogicalResult verifyInvariants(Operation *op) const {
333 return getImpl()->verifyInvariants(op);
334 }
335 LogicalResult verifyRegionInvariants(Operation *op) const {
336 return getImpl()->verifyRegionInvariants(op);
337 }
338
339 /// Return the list of cached attribute names registered to this operation.
340 /// The order of attributes cached here is unique to each type of operation,
341 /// and the interpretation of this attribute list should generally be driven
342 /// by the respective operation. In many cases, this caching removes the
343 /// need to use the raw string name of a known attribute.
344 ///
345 /// For example the ODS generator, with an op defining the following
346 /// attributes:
347 ///
348 /// let arguments = (ins I32Attr:$attr1, I32Attr:$attr2);
349 ///
350 /// ... may produce an order here of ["attr1", "attr2"]. This allows for the
351 /// ODS generator to directly access the cached name for a known attribute,
352 /// greatly simplifying the cost and complexity of attribute usage produced
353 /// by the generator.
354 ///
358
359 /// Returns an instance of the concept object for the given interface if it
360 /// was registered to this operation, null otherwise. This should not be used
361 /// directly.
362 template <typename T>
363 typename T::Concept *getInterface() const {
364 return getImpl()->getInterfaceMap().lookup<T>();
365 }
366
367 /// Attach the given models as implementations of the corresponding
368 /// interfaces for the concrete operation.
369 template <typename... Models>
371 // Handle the case where the models resolve a promised interface.
373 *getDialect(), getTypeID(), Models::Interface::getInterfaceID()),
374 ...);
375
376 getImpl()->getInterfaceMap().insertModels<Models...>();
377 }
378
379 /// Returns true if `InterfaceT` has been promised by the dialect or
380 /// implemented.
381 template <typename InterfaceT>
384 getDialect(), getTypeID(), InterfaceT::getInterfaceID()) ||
386 }
387
388 /// Returns true if this operation has the given interface registered to it.
389 template <typename T>
390 bool hasInterface() const {
392 }
393 bool hasInterface(TypeID interfaceID) const {
394 return getImpl()->getInterfaceMap().contains(interfaceID);
395 }
396
397 /// Returns true if the operation *might* have the provided interface. This
398 /// means that either the operation is unregistered, or it was registered with
399 /// the provide interface.
400 template <typename T>
401 bool mightHaveInterface() const {
403 }
404 bool mightHaveInterface(TypeID interfaceID) const {
405 return !isRegistered() || hasInterface(interfaceID);
406 }
407
408 /// Lookup an inherent attribute by name, this method isn't recommended
409 /// and may be removed in the future.
410 std::optional<Attribute> getInherentAttr(Operation *op,
411 StringRef name) const {
412 return getImpl()->getInherentAttr(op, name);
413 }
414
415 void setInherentAttr(Operation *op, StringAttr name, Attribute value) const {
416 return getImpl()->setInherentAttr(op, name, value);
417 }
418
420 return getImpl()->populateInherentAttrs(op, attrs);
421 }
422 /// This method exists for backward compatibility purpose when using
423 /// properties to store inherent attributes, it enables validating the
424 /// attributes when parsed from the older generic syntax pre-Properties.
425 LogicalResult
428 return getImpl()->verifyInherentAttrs(*this, attributes, emitError);
429 }
430 /// This hooks return the number of bytes to allocate for the op properties.
432 return getImpl()->getOpPropertyByteSize();
433 }
434
435 /// Return the TypeID of the op properties.
437 return getImpl()->getPropertiesTypeID();
438 }
439
440 /// This hooks destroy the op properties.
441 void destroyOpProperties(PropertyRef properties) const {
442 getImpl()->deleteProperties(properties);
443 }
444
445 /// Initialize the op properties.
446 void initOpProperties(PropertyRef storage, PropertyRef init) const {
447 getImpl()->initProperties(*this, storage, init);
448 }
449
450 /// Set the default values on the ODS attribute in the properties.
451 void populateDefaultProperties(PropertyRef properties) const {
452 getImpl()->populateDefaultProperties(*this, properties);
453 }
454
455 /// Return the op properties converted to an Attribute.
459
460 /// Define the op properties from the provided Attribute.
462 OperationName opName, PropertyRef properties, Attribute attr,
464 return getImpl()->setPropertiesFromAttr(opName, properties, attr,
465 emitError);
466 }
467
471
475
476 llvm::hash_code hashOpProperties(PropertyRef properties) const {
477 return getImpl()->hashProperties(properties);
478 }
479
480 /// Return the dialect this operation is registered to if the dialect is
481 /// loaded in the context, or nullptr if the dialect isn't loaded.
483 return isRegistered() ? getImpl()->getDialect()
484 : getImpl()->getName().getReferencedDialect();
485 }
486
487 /// Return the name of the dialect this operation is registered to.
488 StringRef getDialectNamespace() const;
489
490 /// Return the operation name with dialect name stripped, if it has one.
491 StringRef stripDialect() const { return getStringRef().split('.').second; }
492
493 /// Return the context this operation is associated with.
494 MLIRContext *getContext() { return getIdentifier().getContext(); }
495
496 /// Return the name of this operation. This always succeeds.
497 StringRef getStringRef() const { return getIdentifier(); }
498
499 /// Return the name of this operation as a StringAttr.
500 StringAttr getIdentifier() const { return getImpl()->getName(); }
501
502 void print(raw_ostream &os) const;
503 void dump() const;
504
505 /// Represent the operation name as an opaque pointer. (Used to support
506 /// PointerLikeTypeTraits).
507 void *getAsOpaquePointer() const { return const_cast<Impl *>(impl); }
508 static OperationName getFromOpaquePointer(const void *pointer) {
509 return OperationName(
510 const_cast<Impl *>(reinterpret_cast<const Impl *>(pointer)));
511 }
512
513 bool operator==(const OperationName &rhs) const { return impl == rhs.impl; }
514 bool operator!=(const OperationName &rhs) const { return !(*this == rhs); }
515
516protected:
518 Impl *getImpl() const { return impl; }
519 void setImpl(Impl *rhs) { impl = rhs; }
520
521private:
522 /// The internal implementation of the operation name.
523 Impl *impl = nullptr;
524
525 /// Allow access to the Impl struct.
526 friend MLIRContextImpl;
529};
530
532 info.print(os);
533 return os;
534}
535
536// Make operation names hashable.
537inline llvm::hash_code hash_value(OperationName arg) {
538 return llvm::hash_value(arg.getAsOpaquePointer());
539}
540
541//===----------------------------------------------------------------------===//
542// RegisteredOperationName
543//===----------------------------------------------------------------------===//
544
545/// This is a "type erased" representation of a registered operation. This
546/// should only be used by things like the AsmPrinter and other things that need
547/// to be parameterized by generic operation hooks. Most user code should use
548/// the concrete operation types.
549class RegisteredOperationName : public OperationName {
550public:
551 /// Implementation of the InterfaceConcept for operation APIs that forwarded
552 /// to a concrete op implementation.
553 template <typename ConcreteOp>
554 struct Model : public Impl {
555 using Properties = std::remove_reference_t<
556 decltype(std::declval<ConcreteOp>().getProperties())>;
558 : Impl(ConcreteOp::getOperationName(), dialect,
559 TypeID::get<ConcreteOp>(), ConcreteOp::getInterfaceMap()) {
561 }
562 LogicalResult foldHook(Operation *op, ArrayRef<Attribute> attrs,
563 SmallVectorImpl<OpFoldResult> &results) final {
564 return ConcreteOp::getFoldHookFn()(op, attrs, results);
565 }
567 MLIRContext *context) final {
568 ConcreteOp::getCanonicalizationPatterns(set, context);
569 }
570 bool hasTrait(TypeID id) final { return ConcreteOp::getHasTraitFn()(id); }
572 return ConcreteOp::parse;
573 }
574 void populateDefaultAttrs(const OperationName &name,
575 NamedAttrList &attrs) final {
576 ConcreteOp::populateDefaultAttrs(name, attrs);
577 }
579 StringRef name) final {
580 ConcreteOp::getPrintAssemblyFn()(op, printer, name);
581 }
582 LogicalResult verifyInvariants(Operation *op) final {
583 return ConcreteOp::getVerifyInvariantsFn()(op);
584 }
585 LogicalResult verifyRegionInvariants(Operation *op) final {
586 return ConcreteOp::getVerifyRegionInvariantsFn()(op);
587 }
588
589 /// Implementation for "Properties"
590
591 std::optional<Attribute> getInherentAttr(Operation *op,
592 StringRef name) final {
593 if constexpr (hasProperties) {
594 auto concreteOp = cast<ConcreteOp>(op);
595 return ConcreteOp::getInherentAttr(concreteOp->getContext(),
596 concreteOp.getProperties(), name);
597 }
598 return std::nullopt;
599 }
600 void setInherentAttr(Operation *op, StringAttr name,
601 Attribute value) final {
602 if constexpr (hasProperties) {
603 auto concreteOp = cast<ConcreteOp>(op);
604 return ConcreteOp::setInherentAttr(concreteOp.getProperties(), name,
605 value);
606 }
607 llvm_unreachable(
608 "Can't call setInherentAttr on operation with empty properties");
609 }
611 if constexpr (hasProperties) {
612 auto concreteOp = cast<ConcreteOp>(op);
613 ConcreteOp::populateInherentAttrs(concreteOp->getContext(),
614 concreteOp.getProperties(), attrs);
615 }
616 }
617 LogicalResult
618 verifyInherentAttrs(OperationName opName, NamedAttrList &attributes,
620 if constexpr (hasProperties)
621 return ConcreteOp::verifyInherentAttrs(opName, attributes, emitError);
622 return success();
623 }
624 // Detect if the concrete operation defined properties.
625 static constexpr bool hasProperties = !std::is_same_v<
626 typename ConcreteOp::template InferredProperties<ConcreteOp>,
628
630 if constexpr (hasProperties)
631 return sizeof(Properties);
632 return 0;
633 }
634 void initProperties(OperationName opName, PropertyRef storage,
635 PropertyRef init) final {
636 using Properties =
637 typename ConcreteOp::template InferredProperties<ConcreteOp>;
638 if (init)
639 new (storage.as<Properties *>()) Properties(*init.as<Properties *>());
640 else
641 new (storage.as<Properties *>()) Properties();
642 if constexpr (hasProperties)
643 ConcreteOp::populateDefaultProperties(opName,
644 *storage.as<Properties *>());
645 }
646 void deleteProperties(PropertyRef prop) final {
647 prop.as<Properties *>()->~Properties();
648 }
649 void populateDefaultProperties(OperationName opName,
650 PropertyRef properties) final {
651 if constexpr (hasProperties)
652 ConcreteOp::populateDefaultProperties(opName,
653 *properties.as<Properties *>());
654 }
655
656 LogicalResult
657 setPropertiesFromAttr(OperationName opName, PropertyRef properties,
658 Attribute attr,
660 if constexpr (hasProperties) {
661 auto p = properties.as<Properties *>();
662 return ConcreteOp::setPropertiesFromAttr(*p, attr, emitError);
663 }
664 emitError() << "this operation has empty properties";
665 return failure();
666 }
668 if constexpr (hasProperties) {
669 auto concreteOp = cast<ConcreteOp>(op);
670 return ConcreteOp::getPropertiesAsAttr(concreteOp->getContext(),
671 concreteOp.getProperties());
672 }
673 return {};
674 }
676 if constexpr (hasProperties)
677 return *lhs.as<Properties *>() == *rhs.as<Properties *>();
678 return true;
679 }
681 *lhs.as<Properties *>() = *rhs.as<Properties *>();
682 }
683 llvm::hash_code hashProperties(PropertyRef prop) final {
684 if constexpr (hasProperties)
685 return ConcreteOp::computePropertiesHash(*prop.as<Properties *>());
686
687 return {};
688 }
689 };
690
691 /// Lookup the registered operation information for the given operation.
692 /// Returns std::nullopt if the operation isn't registered.
693 static std::optional<RegisteredOperationName> lookup(StringRef name,
694 MLIRContext *ctx);
695
696 /// Lookup the registered operation information for the given operation.
697 /// Returns std::nullopt if the operation isn't registered.
698 static std::optional<RegisteredOperationName> lookup(TypeID typeID,
699 MLIRContext *ctx);
700
701 /// Register a new operation in a Dialect object.
702 /// This constructor is used by Dialect objects when they register the list
703 /// of operations they contain.
704 template <typename T>
705 static void insert(Dialect &dialect) {
706 insert(std::make_unique<Model<T>>(&dialect), T::getAttributeNames());
707 }
708 /// The use of this method is in general discouraged in favor of
709 /// 'insert<CustomOp>(dialect)'.
710 static void insert(std::unique_ptr<OperationName::Impl> ownedImpl,
711 ArrayRef<StringRef> attrNames);
712
713 /// Return the dialect this operation is registered to.
714 Dialect &getDialect() const { return *getImpl()->getDialect(); }
715
716 /// Represent the operation name as an opaque pointer. (Used to support
717 /// PointerLikeTypeTraits).
718 static RegisteredOperationName getFromOpaquePointer(const void *pointer) {
719 return RegisteredOperationName(
720 const_cast<Impl *>(reinterpret_cast<const Impl *>(pointer)));
721 }
722
723private:
725
726 /// Allow access to the constructor.
727 friend OperationName;
728};
729
730inline std::optional<RegisteredOperationName>
733 : std::optional<RegisteredOperationName>();
734}
735
736//===----------------------------------------------------------------------===//
737// Attribute Dictionary-Like Interface
738//===----------------------------------------------------------------------===//
739
740/// Attribute collections provide a dictionary-like interface. Define common
741/// lookup functions.
742namespace impl {
743
744/// Unsorted string search or identifier lookups are linear scans.
745template <typename IteratorT, typename NameT>
746std::pair<IteratorT, bool> findAttrUnsorted(IteratorT first, IteratorT last,
747 NameT name) {
748 for (auto it = first; it != last; ++it)
749 if (it->getName() == name)
750 return {it, true};
751 return {last, false};
752}
753
754/// Using llvm::lower_bound requires an extra string comparison to check whether
755/// the returned iterator points to the found element or whether it indicates
756/// the lower bound. Skip this redundant comparison by checking if `compare ==
757/// 0` during the binary search.
758template <typename IteratorT>
759std::pair<IteratorT, bool> findAttrSorted(IteratorT first, IteratorT last,
760 StringRef name) {
761 ptrdiff_t length = std::distance(first, last);
762
763 while (length > 0) {
764 ptrdiff_t half = length / 2;
765 IteratorT mid = first + half;
766 int compare = mid->getName().strref().compare(name);
767 if (compare < 0) {
768 first = mid + 1;
769 length = length - half - 1;
770 } else if (compare > 0) {
771 length = half;
772 } else {
773 return {mid, true};
774 }
775 }
776 return {first, false};
777}
778
779/// StringAttr lookups on large attribute lists will switch to string binary
780/// search. String binary searches become significantly faster than linear scans
781/// with the identifier when the attribute list becomes very large.
782template <typename IteratorT>
783std::pair<IteratorT, bool> findAttrSorted(IteratorT first, IteratorT last,
784 StringAttr name) {
785 constexpr unsigned kSmallAttributeList = 16;
786 if (std::distance(first, last) > kSmallAttributeList)
787 return findAttrSorted(first, last, name.strref());
788 return findAttrUnsorted(first, last, name);
789}
790
791/// Get an attribute from a sorted range of named attributes. Returns null if
792/// the attribute was not found.
793template <typename IteratorT, typename NameT>
794Attribute getAttrFromSortedRange(IteratorT first, IteratorT last, NameT name) {
795 std::pair<IteratorT, bool> result = findAttrSorted(first, last, name);
796 return result.second ? result.first->getValue() : Attribute();
797}
798
799/// Get an attribute from a sorted range of named attributes. Returns
800/// std::nullopt if the attribute was not found.
801template <typename IteratorT, typename NameT>
802std::optional<NamedAttribute>
803getNamedAttrFromSortedRange(IteratorT first, IteratorT last, NameT name) {
804 std::pair<IteratorT, bool> result = findAttrSorted(first, last, name);
805 return result.second ? *result.first : std::optional<NamedAttribute>();
806}
807
808} // namespace impl
809
810//===----------------------------------------------------------------------===//
811// NamedAttrList
812//===----------------------------------------------------------------------===//
813
814/// NamedAttrList is array of NamedAttributes that tracks whether it is sorted
815/// and does some basic work to remain sorted.
817public:
822 using size_type = size_t;
823
824 NamedAttrList() : dictionarySorted({}, true) {}
825 NamedAttrList(ArrayRef<NamedAttribute> attributes);
826 NamedAttrList(DictionaryAttr attributes);
827 NamedAttrList(const_iterator inStart, const_iterator inEnd);
828
829 template <typename Container>
830 NamedAttrList(const Container &vec)
832
833 bool operator!=(const NamedAttrList &other) const {
834 return !(*this == other);
835 }
836 bool operator==(const NamedAttrList &other) const {
837 return attrs == other.attrs;
838 }
839
840 /// Add an attribute with the specified name.
841 void append(StringRef name, Attribute attr) {
842 append(NamedAttribute(name, attr));
843 }
844
845 /// Add an attribute with the specified name.
846 void append(StringAttr name, Attribute attr) {
847 append(NamedAttribute(name, attr));
848 }
849
850 /// Append the given named attribute.
851 void append(NamedAttribute attr) { push_back(attr); }
852
853 /// Add an array of named attributes.
854 template <typename RangeT>
855 void append(RangeT &&newAttributes) {
856 append(std::begin(newAttributes), std::end(newAttributes));
857 }
858
859 /// Add a range of named attributes.
860 template <typename IteratorT,
861 typename = std::enable_if_t<std::is_convertible<
862 typename std::iterator_traits<IteratorT>::iterator_category,
863 std::input_iterator_tag>::value>>
864 void append(IteratorT inStart, IteratorT inEnd) {
865 // TODO: expand to handle case where values appended are in order & after
866 // end of current list.
867 dictionarySorted.setPointerAndInt(nullptr, false);
868 attrs.append(inStart, inEnd);
869 }
870
871 /// Replaces the attributes with new list of attributes.
872 void assign(const_iterator inStart, const_iterator inEnd);
873
874 /// Replaces the attributes with new list of attributes.
876 assign(range.begin(), range.end());
877 }
878
879 void clear() {
880 attrs.clear();
881 dictionarySorted.setPointerAndInt(nullptr, false);
882 }
883
884 bool empty() const { return attrs.empty(); }
885
886 void reserve(size_type N) { attrs.reserve(N); }
887
888 /// Add an attribute with the specified name.
889 void push_back(NamedAttribute newAttribute);
890
891 /// Pop last element from list.
892 void pop_back() { attrs.pop_back(); }
893
894 /// Returns an entry with a duplicate name the list, if it exists, else
895 /// returns std::nullopt.
896 std::optional<NamedAttribute> findDuplicate() const;
897
898 /// Return a dictionary attribute for the underlying dictionary. This will
899 /// return an empty dictionary attribute if empty rather than null.
900 DictionaryAttr getDictionary(MLIRContext *context) const;
901
902 /// Return all of the attributes on this operation.
903 ArrayRef<NamedAttribute> getAttrs() const;
904
905 /// Return the specified attribute if present, null otherwise.
906 Attribute get(StringAttr name) const;
907 Attribute get(StringRef name) const;
908
909 /// Return the specified named attribute if present, std::nullopt otherwise.
910 std::optional<NamedAttribute> getNamed(StringRef name) const;
911 std::optional<NamedAttribute> getNamed(StringAttr name) const;
912
913 /// If the an attribute exists with the specified name, change it to the new
914 /// value. Otherwise, add a new attribute with the specified name/value.
915 /// Returns the previous attribute value of `name`, or null if no
916 /// attribute previously existed with `name`.
917 Attribute set(StringAttr name, Attribute value);
918 Attribute set(StringRef name, Attribute value);
919
920 /// Erase the attribute with the given name from the list. Return the
921 /// attribute that was erased, or nullptr if there was no attribute with such
922 /// name.
923 Attribute erase(StringAttr name);
924 Attribute erase(StringRef name);
925
926 iterator begin() { return attrs.begin(); }
927 iterator end() { return attrs.end(); }
928 const_iterator begin() const { return attrs.begin(); }
929 const_iterator end() const { return attrs.end(); }
930
932 operator ArrayRef<NamedAttribute>() const;
933
934private:
935 /// Return whether the attributes are sorted.
936 bool isSorted() const { return dictionarySorted.getInt(); }
937
938 /// Erase the attribute at the given iterator position.
939 Attribute eraseImpl(SmallVectorImpl<NamedAttribute>::iterator it);
940
941 /// Lookup an attribute in the list.
942 template <typename AttrListT, typename NameT>
943 static auto findAttr(AttrListT &attrs, NameT name) {
944 return attrs.isSorted()
945 ? impl::findAttrSorted(attrs.begin(), attrs.end(), name)
946 : impl::findAttrUnsorted(attrs.begin(), attrs.end(), name);
947 }
948
949 // These are marked mutable as they may be modified (e.g., sorted)
950 mutable SmallVector<NamedAttribute, 4> attrs;
951 // Pair with cached DictionaryAttr and status of whether attrs is sorted.
952 // Note: just because sorted does not mean a DictionaryAttr has been created
953 // but the case where there is a DictionaryAttr but attrs isn't sorted should
954 // not occur.
955 mutable llvm::PointerIntPair<Attribute, 1, bool> dictionarySorted;
956};
957
958//===----------------------------------------------------------------------===//
959// OperationState
960//===----------------------------------------------------------------------===//
961
962/// This represents an operation in an abstracted form, suitable for use with
963/// the builder APIs. This object is a large and heavy weight object meant to
964/// be used as a temporary object on the stack. It is generally unwise to put
965/// this in a collection.
970 /// Types of the results of this operation.
973 /// Successors of this operation and their respective operands.
975 /// Regions that the op will hold.
977
978 /// This Attribute is used to opaquely construct the properties of the
979 /// operation. If we're creating an unregistered operation, the Attribute is
980 /// used as-is as the Properties storage of the operation. Otherwise, the
981 /// operation properties are constructed opaquely using its
982 /// `setPropertiesFromAttr` hook. Note that `getOrAddProperties` is the
983 /// preferred method to construct properties from C++.
985
986private:
987 PropertyRef properties;
988 llvm::function_ref<void(PropertyRef)> propertiesDeleter;
989 llvm::function_ref<void(PropertyRef, const PropertyRef)> propertiesSetter;
990 friend class Operation;
991
992public:
995
998 BlockRange successors = {},
999 MutableArrayRef<std::unique_ptr<Region>> regions = {});
1000 OperationState(Location location, StringRef name, ValueRange operands,
1001 TypeRange types, ArrayRef<NamedAttribute> attributes = {},
1002 BlockRange successors = {},
1003 MutableArrayRef<std::unique_ptr<Region>> regions = {});
1004 OperationState(OperationState &&other) = default;
1006 OperationState(const OperationState &other) = delete;
1007 OperationState &operator=(const OperationState &other) = delete;
1009
1010 /// Get (or create) the properties of the provided type to be set on the
1011 /// operation on creation.
1012 template <typename T>
1014 if (!properties) {
1015 T *p = new T{};
1016 properties = PropertyRef(TypeID::get<T>(), p);
1017#if defined(__clang__)
1018#if __has_warning("-Wdangling-assignment-gsl")
1019#pragma clang diagnostic push
1020// https://github.com/llvm/llvm-project/issues/126600
1021#pragma clang diagnostic ignored "-Wdangling-assignment-gsl"
1022#endif
1023#endif
1024 propertiesDeleter = [](PropertyRef prop) { delete prop.as<const T *>(); };
1025 propertiesSetter = [](PropertyRef newProp, const PropertyRef prop) {
1026 *newProp.as<T *>() = *prop.as<const T *>();
1027 };
1028#if defined(__clang__)
1029#if __has_warning("-Wdangling-assignment-gsl")
1030#pragma clang diagnostic pop
1031#endif
1032#endif
1033 }
1034 assert(properties.getTypeID() == TypeID::get<T>() &&
1035 "Inconsistent properties");
1036 return *properties.as<T *>();
1037 }
1038 PropertyRef getRawProperties() { return properties; }
1039
1040 // Set the properties defined on this OpState on the given operation,
1041 // optionally emit diagnostics on error through the provided diagnostic.
1042 LogicalResult
1043 setProperties(Operation *op,
1045
1046 // Make `newProperties` the source of the properties that will be copied into
1047 // the operation. The memory referenced by `newProperties` must remain live
1048 // until after the `Operation` is created, at which time it may be
1049 // deallocated. Calls to `getOrAddProperties<>()` will return references to
1050 // this memory.
1051 template <typename T>
1052 void useProperties(T &newProperties) {
1053 assert(!properties &&
1054 "Can't provide a properties struct when one has been allocated");
1055 properties = PropertyRef(TypeID::get<T>(), &newProperties);
1056#if defined(__clang__)
1057#if __has_warning("-Wdangling-assignment-gsl")
1058#pragma clang diagnostic push
1059// https://github.com/llvm/llvm-project/issues/126600
1060#pragma clang diagnostic ignored "-Wdangling-assignment-gsl"
1061#endif
1062#endif
1063 propertiesDeleter = [](PropertyRef) {};
1064 propertiesSetter = [](PropertyRef newProp, const PropertyRef prop) {
1065 *newProp.as<T *>() = *prop.as<const T *>();
1066 };
1067#if defined(__clang__)
1068#if __has_warning("-Wdangling-assignment-gsl")
1069#pragma clang diagnostic pop
1070#endif
1071#endif
1072 }
1073
1074 void addOperands(ValueRange newOperands);
1075
1076 void addTypes(ArrayRef<Type> newTypes) {
1077 types.append(newTypes.begin(), newTypes.end());
1078 }
1079 template <typename RangeT>
1080 std::enable_if_t<!std::is_convertible<RangeT, ArrayRef<Type>>::value>
1081 addTypes(RangeT &&newTypes) {
1082 types.append(newTypes.begin(), newTypes.end());
1083 }
1084
1085 /// Add an attribute with the specified name.
1086 void addAttribute(StringRef name, Attribute attr) {
1087 addAttribute(StringAttr::get(getContext(), name), attr);
1088 }
1089
1090 /// Add an attribute with the specified name. `name` and `attr` must not be
1091 /// null.
1092 void addAttribute(StringAttr name, Attribute attr) {
1093 assert(name && "attribute name cannot be null");
1094 assert(attr && "attribute cannot be null");
1095 attributes.append(name, attr);
1096 }
1097
1098 /// Add an array of named attributes.
1100 attributes.append(newAttributes);
1101 }
1102
1103 /// Adds a successor to the operation sate. `successor` must not be null.
1104 void addSuccessors(Block *successor) {
1105 assert(successor && "successor cannot be null");
1106 successors.push_back(successor);
1107 }
1108 void addSuccessors(BlockRange newSuccessors);
1109
1110 /// Create a region that should be attached to the operation. These regions
1111 /// can be filled in immediately without waiting for Operation to be
1112 /// created. When it is, the region bodies will be transferred.
1113 Region *addRegion();
1114
1115 /// Take a region that should be attached to the Operation. The body of the
1116 /// region will be transferred when the Operation is constructed. If the
1117 /// region is null, a new empty region will be attached to the Operation.
1118 void addRegion(std::unique_ptr<Region> &&region);
1119
1120 /// Take ownership of a set of regions that should be attached to the
1121 /// Operation.
1122 void addRegions(MutableArrayRef<std::unique_ptr<Region>> regions);
1123
1124 /// Get the context held by this operation state.
1125 MLIRContext *getContext() const { return location->getContext(); }
1126};
1127
1128//===----------------------------------------------------------------------===//
1129// OperandStorage
1130//===----------------------------------------------------------------------===//
1131
1132namespace detail {
1133/// This class handles the management of operation operands. Operands are
1134/// stored either in a trailing array, or a dynamically resizable vector.
1135class alignas(8) OperandStorage {
1136public:
1137 OperandStorage(Operation *owner, OpOperand *trailingOperands,
1138 ValueRange values);
1140
1141 /// Replace the operands contained in the storage with the ones provided in
1142 /// 'values'.
1143 void setOperands(Operation *owner, ValueRange values);
1144
1145 /// Replace the operands beginning at 'start' and ending at 'start' + 'length'
1146 /// with the ones provided in 'operands'. 'operands' may be smaller or larger
1147 /// than the range pointed to by 'start'+'length'.
1148 void setOperands(Operation *owner, unsigned start, unsigned length,
1149 ValueRange operands);
1150
1151 /// Erase the operands held by the storage within the given range.
1152 void eraseOperands(unsigned start, unsigned length);
1153
1154 /// Erase the operands held by the storage that have their corresponding bit
1155 /// set in `eraseIndices`.
1156 void eraseOperands(const BitVector &eraseIndices);
1157
1158 /// Get the operation operands held by the storage.
1159 MutableArrayRef<OpOperand> getOperands() { return {operandStorage, size()}; }
1160
1161 /// Return the number of operands held in the storage.
1162 unsigned size() { return numOperands; }
1163
1164private:
1165 /// Resize the storage to the given size. Returns the array containing the new
1166 /// operands.
1167 MutableArrayRef<OpOperand> resize(Operation *owner, unsigned newSize);
1168
1169 /// The total capacity number of operands that the storage can hold.
1170 unsigned capacity : 31;
1171 /// A flag indicating if the operand storage was dynamically allocated, as
1172 /// opposed to inlined into the owning operation.
1173 unsigned isStorageDynamic : 1;
1174 /// The number of operands within the storage.
1175 unsigned numOperands;
1176 /// A pointer to the operand storage.
1177 OpOperand *operandStorage;
1178};
1179} // namespace detail
1180
1181//===----------------------------------------------------------------------===//
1182// OpPrintingFlags
1183//===----------------------------------------------------------------------===//
1184
1185/// Set of flags used to control the behavior of the various IR print methods
1186/// (e.g. Operation::Print).
1188public:
1190
1191 /// Enables the elision of large elements attributes by printing a lexically
1192 /// valid but otherwise meaningless form instead of the element data. The
1193 /// `largeElementLimit` is used to configure what is considered to be a
1194 /// "large" ElementsAttr by providing an upper limit to the number of
1195 /// elements.
1196 OpPrintingFlags &elideLargeElementsAttrs(int64_t largeElementLimit = 16);
1197
1198 /// Enables the printing of large element attributes with a hex string. The
1199 /// `largeElementLimit` is used to configure what is considered to be a
1200 /// "large" ElementsAttr by providing an upper limit to the number of
1201 /// elements. Use -1 to disable the hex printing.
1203 printLargeElementsAttrWithHex(int64_t largeElementLimit = 100);
1204
1205 /// Enables the elision of large resources strings by omitting them from the
1206 /// `dialect_resources` section. The `largeResourceLimit` is used to configure
1207 /// what is considered to be a "large" resource by providing an upper limit to
1208 /// the string size.
1209 OpPrintingFlags &elideLargeResourceString(int64_t largeResourceLimit = 64);
1210
1211 /// Enable or disable printing of debug information (based on `enable`). If
1212 /// 'prettyForm' is set to true, debug information is printed in a more
1213 /// readable 'pretty' form. Note: The IR generated with 'prettyForm' is not
1214 /// parsable.
1215 OpPrintingFlags &enableDebugInfo(bool enable = true, bool prettyForm = false);
1216
1217 /// Always print operations in the generic form.
1218 OpPrintingFlags &printGenericOpForm(bool enable = true);
1219
1220 /// Skip printing regions.
1221 OpPrintingFlags &skipRegions(bool skip = true);
1222
1223 /// Do not verify the operation when using custom operation printers.
1224 OpPrintingFlags &assumeVerified(bool enable = true);
1225
1226 /// Use local scope when printing the operation. This allows for using the
1227 /// printer in a more localized and thread-safe setting, but may not
1228 /// necessarily be identical to what the IR will look like when dumping
1229 /// the full module.
1230 OpPrintingFlags &useLocalScope(bool enable = true);
1231
1232 /// Print users of values as comments.
1233 OpPrintingFlags &printValueUsers(bool enable = true);
1234
1235 /// Print unique SSA ID numbers for values, block arguments and naming
1236 /// conflicts across all regions
1237 OpPrintingFlags &printUniqueSSAIDs(bool enable = true);
1238
1239 /// Print SSA IDs using their NameLoc, if provided, as prefix.
1240 OpPrintingFlags &printNameLocAsPrefix(bool enable = true);
1241
1242 /// Return if the given ElementsAttr should be elided.
1243 bool shouldElideElementsAttr(ElementsAttr attr) const;
1244
1245 /// Return if the given ElementsAttr should be printed as hex string.
1246 bool shouldPrintElementsAttrWithHex(ElementsAttr attr) const;
1247
1248 /// Return the size limit for printing large ElementsAttr.
1249 std::optional<int64_t> getLargeElementsAttrLimit() const;
1250
1251 /// Return the size limit for printing large ElementsAttr as hex string.
1253
1254 /// Return the size limit in chars for printing large resources.
1255 std::optional<uint64_t> getLargeResourceStringLimit() const;
1256
1257 /// Return if debug information should be printed.
1258 bool shouldPrintDebugInfo() const;
1259
1260 /// Return if debug information should be printed in the pretty form.
1261 bool shouldPrintDebugInfoPrettyForm() const;
1262
1263 /// Return if operations should be printed in the generic form.
1264 bool shouldPrintGenericOpForm() const;
1265
1266 /// Return if regions should be skipped.
1267 bool shouldSkipRegions() const;
1268
1269 /// Return if operation verification should be skipped.
1270 bool shouldAssumeVerified() const;
1271
1272 /// Return if the printer should use local scope when dumping the IR.
1273 bool shouldUseLocalScope() const;
1274
1275 /// Return if the printer should print users of values.
1276 bool shouldPrintValueUsers() const;
1277
1278 /// Return if printer should use unique SSA IDs.
1279 bool shouldPrintUniqueSSAIDs() const;
1280
1281 /// Return if the printer should use NameLocs as prefixes when printing SSA
1282 /// IDs
1283 bool shouldUseNameLocAsPrefix() const;
1284
1285private:
1286 /// Elide large elements attributes if the number of elements is larger than
1287 /// the upper limit.
1288 std::optional<int64_t> elementsAttrElementLimit;
1289
1290 /// Elide printing large resources based on size of string.
1291 std::optional<uint64_t> resourceStringCharLimit;
1292
1293 /// Print large element attributes with hex strings if the number of elements
1294 /// is larger than the upper limit.
1295 int64_t elementsAttrHexElementLimit = 100;
1296
1297 /// Print debug information.
1298 bool printDebugInfoFlag : 1;
1299 bool printDebugInfoPrettyFormFlag : 1;
1300
1301 /// Print operations in the generic form.
1302 bool printGenericOpFormFlag : 1;
1303
1304 /// Always skip Regions.
1305 bool skipRegionsFlag : 1;
1306
1307 /// Skip operation verification.
1308 bool assumeVerifiedFlag : 1;
1309
1310 /// Print operations with numberings local to the current operation.
1311 bool printLocalScope : 1;
1312
1313 /// Print users of values.
1314 bool printValueUsersFlag : 1;
1315
1316 /// Print unique SSA IDs for values, block arguments and naming conflicts
1317 bool printUniqueSSAIDsFlag : 1;
1318
1319 /// Print SSA IDs using NameLocs as prefixes
1320 bool useNameLocAsPrefix : 1;
1321};
1322
1323//===----------------------------------------------------------------------===//
1324// Operation Equivalency
1325//===----------------------------------------------------------------------===//
1326
1327/// This class provides utilities for computing if two operations are
1328/// equivalent.
1330 enum Flags {
1331 None = 0,
1332
1333 // When provided, the location attached to the operation are ignored.
1335
1336 // When provided, the discardable attributes attached to the operation are
1337 // ignored.
1339
1340 // When provided, the properties attached to the operation are ignored.
1342
1343 // When provided, the commutativity of the operation is ignored, and
1344 // operands are compared in an order-sensitive way.
1346
1347 LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ IgnoreCommutativity)
1348 };
1349
1350 /// Compute a hash for the given operation.
1351 /// The `hashOperands` and `hashResults` callbacks are expected to return a
1352 /// unique hash_code for a given Value.
1353 static llvm::hash_code computeHash(
1354 Operation *op,
1355 function_ref<llvm::hash_code(Value)> hashOperands =
1356 [](Value v) { return hash_value(v); },
1357 function_ref<llvm::hash_code(Value)> hashResults =
1358 [](Value v) { return hash_value(v); },
1359 Flags flags = Flags::None);
1360
1361 /// Helper that can be used with `computeHash` above to ignore operation
1362 /// operands/result mapping.
1363 static llvm::hash_code ignoreHashValue(Value) { return llvm::hash_code{}; }
1364 /// Helper that can be used with `computeHash` to compute the hash value
1365 /// of operands/results directly.
1366 static llvm::hash_code directHashValue(Value v) { return hash_value(v); }
1367
1368 /// Compare two operations (including their regions) and return if they are
1369 /// equivalent.
1370 ///
1371 /// * `checkEquivalent` is a callback to check if two values are equivalent.
1372 /// For two operations to be equivalent, their operands must be the same SSA
1373 /// value or this callback must return `success`.
1374 /// * `markEquivalent` is a callback to inform the caller that the analysis
1375 /// determined that two values are equivalent.
1376 /// * `checkCommutativeEquivalent` is an optional callback to check for
1377 /// equivalence across two ranges for a commutative operation. If not passed
1378 /// in, then equivalence is checked pairwise. This callback is needed to be
1379 /// able to query the optional equivalence classes.
1380 ///
1381 /// Note: Additional information regarding value equivalence can be injected
1382 /// into the analysis via `checkEquivalent`. Typically, callers may want
1383 /// values that were determined to be equivalent as per `markEquivalent` to be
1384 /// reflected in `checkEquivalent`, unless `exactValueMatch` or a different
1385 /// equivalence relationship is desired.
1386 static bool
1387 isEquivalentTo(Operation *lhs, Operation *rhs,
1388 function_ref<LogicalResult(Value, Value)> checkEquivalent,
1389 function_ref<void(Value, Value)> markEquivalent = nullptr,
1390 Flags flags = Flags::None,
1391 function_ref<LogicalResult(ValueRange, ValueRange)>
1392 checkCommutativeEquivalent = nullptr);
1393
1394 /// Compare two operations and return if they are equivalent.
1395 static bool isEquivalentTo(Operation *lhs, Operation *rhs, Flags flags);
1396
1397 /// Compare two regions (including their subregions) and return if they are
1398 /// equivalent. See also `isEquivalentTo` for details.
1399 static bool isRegionEquivalentTo(
1400 Region *lhs, Region *rhs,
1401 function_ref<LogicalResult(Value, Value)> checkEquivalent,
1402 function_ref<void(Value, Value)> markEquivalent,
1404 function_ref<LogicalResult(ValueRange, ValueRange)>
1405 checkCommutativeEquivalent = nullptr);
1406
1407 /// Compare two regions and return if they are equivalent.
1408 static bool isRegionEquivalentTo(Region *lhs, Region *rhs,
1410
1411 /// Helper that can be used with `isEquivalentTo` above to consider ops
1412 /// equivalent even if their operands are not equivalent.
1413 static LogicalResult ignoreValueEquivalence(Value lhs, Value rhs) {
1414 return success();
1415 }
1416 /// Helper that can be used with `isEquivalentTo` above to consider ops
1417 /// equivalent only if their operands are the exact same SSA values.
1418 static LogicalResult exactValueMatch(Value lhs, Value rhs) {
1419 return success(lhs == rhs);
1420 }
1421};
1422
1423/// Enable Bitmask enums for OperationEquivalence::Flags.
1425
1426//===----------------------------------------------------------------------===//
1427// OperationFingerPrint
1428//===----------------------------------------------------------------------===//
1429
1430/// A unique fingerprint for a specific operation, and all of it's internal
1431/// operations (if `includeNested` is set).
1433public:
1434 OperationFingerPrint(Operation *topOp, bool includeNested = true);
1437
1438 bool operator==(const OperationFingerPrint &other) const {
1439 return hash == other.hash;
1440 }
1441 bool operator!=(const OperationFingerPrint &other) const {
1442 return !(*this == other);
1443 }
1444
1445private:
1446 std::array<uint8_t, 20> hash;
1447};
1448
1449} // namespace mlir
1450
1451namespace llvm {
1452template <>
1469template <>
1470struct DenseMapInfo<mlir::RegisteredOperationName>
1471 : public DenseMapInfo<mlir::OperationName> {
1480};
1481
1482template <>
1483struct PointerLikeTypeTraits<mlir::OperationName> {
1484 static inline void *getAsVoidPointer(mlir::OperationName I) {
1485 return const_cast<void *>(I.getAsOpaquePointer());
1486 }
1490 static constexpr int NumLowBitsAvailable =
1491 PointerLikeTypeTraits<void *>::NumLowBitsAvailable;
1492};
1493template <>
1494struct PointerLikeTypeTraits<mlir::RegisteredOperationName>
1495 : public PointerLikeTypeTraits<mlir::OperationName> {
1499};
1500
1501} // namespace llvm
1502
1503#endif
return success()
static size_t hash(const T &value)
Local helper to compute std::hash for a value.
Definition IRCore.cpp:55
lhs
b getContext())
static llvm::hash_code computeHash(SymbolOpInterface symbolOp)
Computes a hash code to represent symbolOp based on all its attributes except for the symbol name.
memberIdxs push_back(ArrayAttr::get(parser.getContext(), values))
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class provides an abstraction over the different types of ranges over Blocks.
Block represents an ordered list of Operations.
Definition Block.h:33
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition Dialect.h:38
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
This is the implementation of the MLIRContext class, using the pImpl idiom.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class represents a contiguous range of mutable operand ranges, e.g.
Definition ValueRange.h:211
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
void append(IteratorT inStart, IteratorT inEnd)
Add a range of named attributes.
void assign(ArrayRef< NamedAttribute > range)
Replaces the attributes with new list of attributes.
const_iterator begin() const
void assign(const_iterator inStart, const_iterator inEnd)
Replaces the attributes with new list of attributes.
NamedAttribute & reference
SmallVectorImpl< NamedAttribute >::const_iterator const_iterator
void append(NamedAttribute attr)
Append the given named attribute.
bool operator!=(const NamedAttrList &other) const
SmallVectorImpl< NamedAttribute >::iterator iterator
const_iterator end() const
NamedAttrList(const Container &vec)
void append(StringAttr name, Attribute attr)
Add an attribute with the specified name.
void pop_back()
Pop last element from list.
bool operator==(const NamedAttrList &other) const
void append(StringRef name, Attribute attr)
Add an attribute with the specified name.
void append(RangeT &&newAttributes)
Add an array of named attributes.
void reserve(size_type N)
const NamedAttribute & const_reference
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,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
This class represents a single result from folding an operation.
This class represents an operand of an operation.
Definition Value.h:254
bool shouldElideElementsAttr(ElementsAttr attr) const
Return if the given ElementsAttr should be elided.
std::optional< int64_t > getLargeElementsAttrLimit() const
Return the size limit for printing large ElementsAttr.
bool shouldUseNameLocAsPrefix() const
Return if the printer should use NameLocs as prefixes when printing SSA IDs.
bool shouldAssumeVerified() const
Return if operation verification should be skipped.
OpPrintingFlags & printLargeElementsAttrWithHex(int64_t largeElementLimit=100)
Enables the printing of large element attributes with a hex string.
bool shouldUseLocalScope() const
Return if the printer should use local scope when dumping the IR.
bool shouldPrintDebugInfoPrettyForm() const
Return if debug information should be printed in the pretty form.
bool shouldPrintElementsAttrWithHex(ElementsAttr attr) const
Return if the given ElementsAttr should be printed as hex string.
bool shouldPrintUniqueSSAIDs() const
Return if printer should use unique SSA IDs.
bool shouldPrintValueUsers() const
Return if the printer should print users of values.
int64_t getLargeElementsAttrHexLimit() const
Return the size limit for printing large ElementsAttr as hex string.
bool shouldPrintGenericOpForm() const
Return if operations should be printed in the generic form.
OpPrintingFlags & elideLargeResourceString(int64_t largeResourceLimit=64)
Enables the elision of large resources strings by omitting them from the dialect_resources section.
bool shouldPrintDebugInfo() const
Return if debug information should be printed.
OpPrintingFlags & elideLargeElementsAttrs(int64_t largeElementLimit=16)
Enables the elision of large elements attributes by printing a lexically valid but otherwise meaningl...
OpPrintingFlags & printNameLocAsPrefix(bool enable=true)
Print SSA IDs using their NameLoc, if provided, as prefix.
OpPrintingFlags & printValueUsers(bool enable=true)
Print users of values as comments.
OpPrintingFlags & enableDebugInfo(bool enable=true, bool prettyForm=false)
Enable or disable printing of debug information (based on enable).
OpPrintingFlags()
Initialize the printing flags with default supplied by the cl::opts above.
bool shouldSkipRegions() const
Return if regions should be skipped.
OpPrintingFlags & printGenericOpForm(bool enable=true)
Always print operations in the generic form.
OpPrintingFlags & useLocalScope(bool enable=true)
Use local scope when printing the operation.
std::optional< uint64_t > getLargeResourceStringLimit() const
Return the size limit in chars for printing large resources.
OpPrintingFlags & assumeVerified(bool enable=true)
Do not verify the operation when using custom operation printers.
OpPrintingFlags & skipRegions(bool skip=true)
Skip printing regions.
OpPrintingFlags & printUniqueSSAIDs(bool enable=true)
Print unique SSA ID numbers for values, block arguments and naming conflicts across all regions.
This class represents a contiguous range of operand ranges, e.g.
Definition ValueRange.h:85
This class implements the operand iterators for the Operation class.
Definition ValueRange.h:44
OperationFingerPrint & operator=(const OperationFingerPrint &)=default
OperationFingerPrint(Operation *topOp, bool includeNested=true)
bool operator!=(const OperationFingerPrint &other) const
bool operator==(const OperationFingerPrint &other) const
OperationFingerPrint(const OperationFingerPrint &)=default
TypeID propertiesTypeID
The TypeID of the Properties struct for this operation.
Impl(StringAttr name, Dialect *dialect, TypeID typeID, detail::InterfaceMap interfaceMap)
ArrayRef< StringAttr > attributeNames
A list of attribute names registered to this operation in StringAttr form.
StringAttr name
The name of the operation.
TypeID typeID
The unique identifier of the derived Op class.
ArrayRef< StringAttr > getAttributeNames() const
Impl(StringRef, Dialect *dialect, TypeID typeID, detail::InterfaceMap interfaceMap)
Dialect * dialect
The following fields are only populated when the operation is registered.
detail::InterfaceMap interfaceMap
A map of interfaces that were registered to this operation.
bool isRegistered() const
Returns true if this is a registered operation.
detail::InterfaceMap & getInterfaceMap()
void populateInherentAttrs(Operation *op, NamedAttrList &attrs) const
bool operator==(const OperationName &rhs) const
void destroyOpProperties(PropertyRef properties) const
This hooks destroy the op properties.
void dump() const
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
ArrayRef< StringAttr > getAttributeNames() const
Return the list of cached attribute names registered to this operation.
bool operator!=(const OperationName &rhs) const
StringRef stripDialect() const
Return the operation name with dialect name stripped, if it has one.
void setInherentAttr(Operation *op, StringAttr name, Attribute value) const
Attribute getOpPropertiesAsAttribute(Operation *op) const
Return the op properties converted to an Attribute.
bool hasTrait() const
Returns true if the operation was registered with a particular trait, e.g.
bool hasPromiseOrImplementsInterface() const
Returns true if InterfaceT has been promised by the dialect or implemented.
llvm::unique_function< bool(TypeID) const > HasTraitFn
StringAttr getIdentifier() const
Return the name of this operation as a StringAttr.
void getCanonicalizationPatterns(RewritePatternSet &results, MLIRContext *context) const
This hook returns any canonicalization pattern rewrites that the operation supports,...
ParseAssemblyFn getParseAssemblyFn() const
Return the static hook for parsing this operation assembly.
void copyOpProperties(PropertyRef lhs, PropertyRef rhs) 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.
Dialect * getDialect() const
Return the dialect this operation is registered to if the dialect is loaded in the context,...
OperationName(StringRef name, MLIRContext *context)
StringRef getDialectNamespace() const
Return the name of the dialect this operation is registered to.
void setImpl(Impl *rhs)
llvm::hash_code hashOpProperties(PropertyRef properties) const
std::optional< RegisteredOperationName > getRegisteredInfo() const
If this operation is registered, returns the registered information, std::nullopt otherwise.
bool mightHaveTrait() const
Returns true if the operation might have the provided trait.
llvm::unique_function< LogicalResult(Operation *) const > VerifyInvariantsFn
bool mightHaveTrait(TypeID traitID) const
bool hasInterface() const
Returns true if this operation has the given interface registered to it.
LogicalResult setOpPropertiesFromAttribute(OperationName opName, PropertyRef properties, Attribute attr, function_ref< InFlightDiagnostic()> emitError) const
Define the op properties from the provided Attribute.
LogicalResult verifyInherentAttrs(NamedAttrList &attributes, function_ref< InFlightDiagnostic()> emitError) const
This method exists for backward compatibility purpose when using properties to store inherent attribu...
void * getAsOpaquePointer() const
Represent the operation name as an opaque pointer.
llvm::unique_function< void(const OperationName &, NamedAttrList &) const > PopulateDefaultAttrsFn
llvm::unique_function< ParseResult(OpAsmParser &, OperationState &)> ParseAssemblyFn
void initOpProperties(PropertyRef storage, PropertyRef init) const
Initialize the op properties.
bool isRegistered() const
Return if this operation is registered.
bool mightHaveInterface() const
Returns true if the operation might have the provided interface.
T::Concept * getInterface() const
Returns an instance of the concept object for the given interface if it was registered to this operat...
llvm::unique_function< LogicalResult(Operation *) const > VerifyRegionInvariantsFn
LogicalResult foldHook(Operation *op, ArrayRef< Attribute > operands, SmallVectorImpl< OpFoldResult > &results) const
This hook implements a generalized folder for this operation.
bool mightHaveInterface(TypeID interfaceID) const
bool hasTrait(TypeID traitID) const
LogicalResult verifyRegionInvariants(Operation *op) const
bool compareOpProperties(PropertyRef lhs, PropertyRef rhs) const
TypeID getTypeID() const
Return the unique identifier of the derived Op class, or null if not registered.
TypeID getOpPropertiesTypeID() const
Return the TypeID of the op properties.
void populateDefaultAttrs(NamedAttrList &attrs) const
This hook implements the method to populate defaults attributes that are unset.
MLIRContext * getContext()
Return the context this operation is associated with.
llvm::unique_function< LogicalResult( Operation *, ArrayRef< Attribute >, SmallVectorImpl< OpFoldResult > &) const > FoldHookFn
void populateDefaultProperties(PropertyRef properties) const
Set the default values on the ODS attribute in the properties.
LogicalResult verifyInvariants(Operation *op) const
These hooks implement the verifiers for this operation.
int getOpPropertyByteSize() const
This hooks return the number of bytes to allocate for the op properties.
void printAssembly(Operation *op, OpAsmPrinter &p, StringRef defaultDialect) const
This hook implements the AsmPrinter for this operation.
void print(raw_ostream &os) const
bool hasInterface(TypeID interfaceID) const
static OperationName getFromOpaquePointer(const void *pointer)
llvm::unique_function< void(Operation *, OpAsmPrinter &, StringRef) const > PrintAssemblyFn
void attachInterface()
Attach the given models as implementations of the corresponding interfaces for the concrete operation...
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
This class contains all of the data related to a pattern, but does not contain any methods or logic f...
Type-safe wrapper around a void* for passing properties, including the properties structs of operatio...
TypeID getTypeID() const
PropertyRef(TypeID typeID, void *data)
PropertyRef()=default
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
This is a "type erased" representation of a registered operation.
static void insert(Dialect &dialect)
Register a new operation in a Dialect object.
static RegisteredOperationName getFromOpaquePointer(const void *pointer)
Represent the operation name as an opaque pointer.
static void insert(std::unique_ptr< OperationName::Impl > ownedImpl, ArrayRef< StringRef > attrNames)
The use of this method is in general discouraged in favor of 'insert<CustomOp>(dialect)'.
Dialect & getDialect() const
Return the dialect this operation is registered to.
This class implements the result iterators for the Operation class.
Definition ValueRange.h:248
RewritePattern is the common base class for all DAG to DAG replacements.
This class provides an efficient unique identifier for a specific C++ type.
Definition TypeID.h:107
static TypeID get()
Construct a type info object for the given type T.
Definition TypeID.h:245
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
This class implements iteration on the types of a given range of values.
Definition TypeRange.h:147
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
This class provides an efficient mapping between a given Interface type, and a particular implementat...
void insertModels()
Insert the given interface models.
T::Concept * lookup() const
Returns an instance of the concept object for the given interface if it was registered to this map,...
bool contains(TypeID interfaceID) const
Returns true if the interface map contains an interface for the given id.
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:229
bool hasPromisedInterface(Dialect &dialect, TypeID interfaceRequestorID, TypeID interfaceID)
Checks if a promise has been made for the interface/requestor pair.
Definition Dialect.cpp:163
void handleAdditionOfUndefinedPromisedInterface(Dialect &dialect, TypeID interfaceRequestorID, TypeID interfaceID)
Checks if the given interface, which is attempting to be attached, is a promised interface of this di...
Definition Dialect.cpp:157
std::optional< NamedAttribute > getNamedAttrFromSortedRange(IteratorT first, IteratorT last, NameT name)
Get an attribute from a sorted range of named attributes.
std::pair< IteratorT, bool > findAttrSorted(IteratorT first, IteratorT last, StringRef name)
Using llvm::lower_bound requires an extra string comparison to check whether the returned iterator po...
std::pair< IteratorT, bool > findAttrUnsorted(IteratorT first, IteratorT last, NameT name)
Unsorted string search or identifier lookups are linear scans.
Attribute getAttrFromSortedRange(IteratorT first, IteratorT last, NameT name)
Get an attribute from a sorted range of named attributes.
Include the generated interface declarations.
llvm::DenseMapInfo< T, Enable > DenseMapInfo
Definition LLVM.h:116
raw_ostream & operator<<(raw_ostream &os, const AliasResult &result)
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
inline ::llvm::hash_code hash_value(AffineExpr arg)
Make AffineExpr hashable.
Definition AffineExpr.h:247
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE()
Enable Bitmask enums for OperationEquivalence::Flags.
static mlir::OperationName getTombstoneKey()
static bool isEqual(mlir::OperationName lhs, mlir::OperationName rhs)
static unsigned getHashValue(mlir::OperationName val)
static mlir::RegisteredOperationName getEmptyKey()
static mlir::RegisteredOperationName getTombstoneKey()
static mlir::OperationName getFromVoidPointer(void *P)
static void * getAsVoidPointer(mlir::OperationName I)
static mlir::RegisteredOperationName getFromVoidPointer(void *P)
Structure used by default as a "marker" when no "Properties" are set on an Operation.
This class provides utilities for computing if two operations are equivalent.
static llvm::hash_code ignoreHashValue(Value)
Helper that can be used with computeHash above to ignore operation operands/result mapping.
static llvm::hash_code directHashValue(Value v)
Helper that can be used with computeHash to compute the hash value of operands/results directly.
static LogicalResult ignoreValueEquivalence(Value lhs, Value rhs)
Helper that can be used with isEquivalentTo above to consider ops equivalent even if their operands a...
static LogicalResult exactValueMatch(Value lhs, Value rhs)
Helper that can be used with isEquivalentTo above to consider ops equivalent only if their operands a...
This class represents a type erased version of an operation.
virtual void populateInherentAttrs(Operation *op, NamedAttrList &attrs)=0
virtual llvm::hash_code hashProperties(PropertyRef)=0
virtual void getCanonicalizationPatterns(RewritePatternSet &, MLIRContext *)=0
virtual OperationName::ParseAssemblyFn getParseAssemblyFn()=0
virtual void printAssembly(Operation *, OpAsmPrinter &, StringRef)=0
virtual void copyProperties(PropertyRef, PropertyRef)=0
virtual LogicalResult setPropertiesFromAttr(OperationName, PropertyRef, Attribute, function_ref< InFlightDiagnostic()> emitError)=0
virtual LogicalResult verifyInvariants(Operation *)=0
virtual void setInherentAttr(Operation *op, StringAttr name, Attribute value)=0
virtual Attribute getPropertiesAsAttr(Operation *)=0
virtual bool compareProperties(PropertyRef, PropertyRef)=0
virtual void populateDefaultProperties(OperationName opName, PropertyRef properties)=0
virtual LogicalResult foldHook(Operation *, ArrayRef< Attribute >, SmallVectorImpl< OpFoldResult > &)=0
virtual void populateDefaultAttrs(const OperationName &, NamedAttrList &)=0
virtual std::optional< Attribute > getInherentAttr(Operation *, StringRef name)=0
Implementation for properties.
virtual void initProperties(OperationName opName, PropertyRef storage, PropertyRef init)=0
virtual LogicalResult verifyInherentAttrs(OperationName opName, NamedAttrList &attributes, function_ref< InFlightDiagnostic()> emitError)=0
virtual LogicalResult verifyRegionInvariants(Operation *)=0
virtual void deleteProperties(PropertyRef)=0
LogicalResult foldHook(Operation *, ArrayRef< Attribute >, SmallVectorImpl< OpFoldResult > &) final
llvm::hash_code hashProperties(PropertyRef) final
void initProperties(OperationName opName, PropertyRef storage, PropertyRef init) final
OperationName::ParseAssemblyFn getParseAssemblyFn() final
void getCanonicalizationPatterns(RewritePatternSet &, MLIRContext *) final
LogicalResult setPropertiesFromAttr(OperationName, PropertyRef, Attribute, function_ref< InFlightDiagnostic()> emitError) final
UnregisteredOpModel(StringAttr name, Dialect *dialect, TypeID typeID, detail::InterfaceMap interfaceMap)
Attribute getPropertiesAsAttr(Operation *) final
bool compareProperties(PropertyRef, PropertyRef) final
void copyProperties(PropertyRef, PropertyRef) final
This represents an operation in an abstracted form, suitable for use with the builder APIs.
OperationState & operator=(const OperationState &other)=delete
SmallVector< Block *, 1 > successors
Successors of this operation and their respective operands.
T & getOrAddProperties()
Get (or create) the properties of the provided type to be set on the operation on creation.
OperationState & operator=(OperationState &&other)=default
SmallVector< Value, 4 > operands
std::enable_if_t<!std::is_convertible< RangeT, ArrayRef< Type > >::value > addTypes(RangeT &&newTypes)
void addAttributes(ArrayRef< NamedAttribute > newAttributes)
Add an array of named attributes.
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
void addAttribute(StringAttr 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 addTypes(ArrayRef< Type > newTypes)
MLIRContext * getContext() const
Get the context held by this operation state.
OperationState(OperationState &&other)=default
OperationState(const OperationState &other)=delete
SmallVector< std::unique_ptr< Region >, 1 > regions
Regions that the op will hold.
OperationState(Location location, StringRef name)
PropertyRef getRawProperties()
void useProperties(T &newProperties)
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.
Implementation of the InterfaceConcept for operation APIs that forwarded to a concrete op implementat...
std::optional< Attribute > getInherentAttr(Operation *op, StringRef name) final
Implementation for "Properties".
std::remove_reference_t< decltype(std::declval< ConcreteOp >().getProperties())> Properties
LogicalResult setPropertiesFromAttr(OperationName opName, PropertyRef properties, Attribute attr, function_ref< InFlightDiagnostic()> emitError) final
void populateDefaultProperties(OperationName opName, PropertyRef properties) final
void populateDefaultAttrs(const OperationName &name, NamedAttrList &attrs) final
OperationName::ParseAssemblyFn getParseAssemblyFn() final
bool compareProperties(PropertyRef lhs, PropertyRef rhs) final
void initProperties(OperationName opName, PropertyRef storage, PropertyRef init) final
LogicalResult verifyInvariants(Operation *op) final
void getCanonicalizationPatterns(RewritePatternSet &set, MLIRContext *context) final
void copyProperties(PropertyRef lhs, PropertyRef rhs) final
LogicalResult verifyRegionInvariants(Operation *op) final
void printAssembly(Operation *op, OpAsmPrinter &printer, StringRef name) final
void populateInherentAttrs(Operation *op, NamedAttrList &attrs) final
LogicalResult foldHook(Operation *op, ArrayRef< Attribute > attrs, SmallVectorImpl< OpFoldResult > &results) final
Attribute getPropertiesAsAttr(Operation *op) final
llvm::hash_code hashProperties(PropertyRef prop) final
LogicalResult verifyInherentAttrs(OperationName opName, NamedAttrList &attributes, function_ref< InFlightDiagnostic()> emitError) final
void setInherentAttr(Operation *op, StringAttr name, Attribute value) final
void deleteProperties(PropertyRef prop) final