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