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
36namespace llvm {
37class BitVector;
38} // namespace llvm
39
40namespace mlir {
41class Dialect;
42class DictionaryAttr;
43class ElementsAttr;
44struct EmptyProperties;
45class MutableOperandRangeRange;
46class NamedAttrList;
47class Operation;
48struct OperationState;
49class OpAsmParser;
50class OpAsmPrinter;
51class OperandRange;
52class OperandRangeRange;
53class OpFoldResult;
54class Pattern;
55class Region;
56class ResultRange;
57class RewritePattern;
58class RewritePatternSet;
59class Type;
60class Value;
61class ValueRange;
62template <typename ValueRangeT>
63class 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.
71class OpaqueProperties {
72public:
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
80private:
81 void *properties;
82};
83
84//===----------------------------------------------------------------------===//
85// OperationName
86//===----------------------------------------------------------------------===//
87
88class OperationName {
89public:
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
147public:
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
192protected:
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
228public:
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 dialect&: *getDialect(), interfaceRequestorID: getTypeID(), interfaceID: 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: 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(Separator: '.').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
492protected:
493 OperationName(Impl *impl) : impl(impl) {}
494 Impl *getImpl() const { return impl; }
495 void setImpl(Impl *rhs) { impl = rhs; }
496
497private:
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
507inline raw_ostream &operator<<(raw_ostream &os, OperationName info) {
508 info.print(os);
509 return os;
510}
511
512// Make operation names hashable.
513inline llvm::hash_code hash_value(OperationName arg) {
514 return llvm::hash_value(ptr: 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.
525class RegisteredOperationName : public OperationName {
526public:
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
703private:
704 RegisteredOperationName(Impl *impl) : OperationName(impl) {}
705
706 /// Allow access to the constructor.
707 friend OperationName;
708};
709
710inline std::optional<RegisteredOperationName>
711OperationName::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.
722namespace impl {
723
724/// Unsorted string search or identifier lookups are linear scans.
725template <typename IteratorT, typename NameT>
726std::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.
738template <typename IteratorT>
739std::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.
762template <typename IteratorT>
763std::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.
773template <typename IteratorT, typename NameT>
774Attribute 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.
781template <typename IteratorT, typename NameT>
782std::optional<NamedAttribute>
783getNamedAttrFromSortedRange(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.
796class NamedAttrList {
797public:
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(attr: NamedAttribute(name, attr));
824 }
825
826 /// Add an attribute with the specified name.
827 void append(StringAttr name, Attribute attr) {
828 append(attr: NamedAttribute(name, attr));
829 }
830
831 /// Append the given named attribute.
832 void append(NamedAttribute attr) { push_back(newAttribute: 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(PtrVal: nullptr, IntVal: 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(inStart: range.begin(), inEnd: range.end());
858 }
859
860 void clear() {
861 attrs.clear();
862 dictionarySorted.setPointerAndInt(PtrVal: nullptr, IntVal: 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
915private:
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.
947struct 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
967private:
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
975public:
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 &operator=(OperationState &&other) = default;
989 OperationState(const OperationState &other) = delete;
990 OperationState &operator=(const OperationState &other) = delete;
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#if defined(__clang__)
1001#if __has_warning("-Wdangling-assignment-gsl")
1002#pragma clang diagnostic push
1003// https://github.com/llvm/llvm-project/issues/126600
1004#pragma clang diagnostic ignored "-Wdangling-assignment-gsl"
1005#endif
1006#endif
1007 propertiesDeleter = [](OpaqueProperties prop) {
1008 delete prop.as<const T *>();
1009 };
1010 propertiesSetter = [](OpaqueProperties newProp,
1011 const OpaqueProperties prop) {
1012 *newProp.as<T *>() = *prop.as<const T *>();
1013 };
1014#if defined(__clang__)
1015#if __has_warning("-Wdangling-assignment-gsl")
1016#pragma clang diagnostic pop
1017#endif
1018#endif
1019 propertiesId = TypeID::get<T>();
1020 }
1021 assert(propertiesId == TypeID::get<T>() && "Inconsistent properties");
1022 return *properties.as<T *>();
1023 }
1024 OpaqueProperties getRawProperties() { return properties; }
1025
1026 // Set the properties defined on this OpState on the given operation,
1027 // optionally emit diagnostics on error through the provided diagnostic.
1028 LogicalResult
1029 setProperties(Operation *op,
1030 function_ref<InFlightDiagnostic()> emitError) const;
1031
1032 // Make `newProperties` the source of the properties that will be copied into
1033 // the operation. The memory referenced by `newProperties` must remain live
1034 // until after the `Operation` is created, at which time it may be
1035 // deallocated. Calls to `getOrAddProperties<>() will return references to
1036 // this memory.
1037 template <typename T>
1038 void useProperties(T &newProperties) {
1039 assert(!properties &&
1040 "Can't provide a properties struct when one has been allocated");
1041 properties = &newProperties;
1042#if defined(__clang__)
1043#if __has_warning("-Wdangling-assignment-gsl")
1044#pragma clang diagnostic push
1045// https://github.com/llvm/llvm-project/issues/126600
1046#pragma clang diagnostic ignored "-Wdangling-assignment-gsl"
1047#endif
1048#endif
1049 propertiesDeleter = [](OpaqueProperties) {};
1050 propertiesSetter = [](OpaqueProperties newProp,
1051 const OpaqueProperties prop) {
1052 *newProp.as<T *>() = *prop.as<const T *>();
1053 };
1054#if defined(__clang__)
1055#if __has_warning("-Wdangling-assignment-gsl")
1056#pragma clang diagnostic pop
1057#endif
1058#endif
1059 propertiesId = TypeID::get<T>();
1060 }
1061
1062 void addOperands(ValueRange newOperands);
1063
1064 void addTypes(ArrayRef<Type> newTypes) {
1065 types.append(in_start: newTypes.begin(), in_end: newTypes.end());
1066 }
1067 template <typename RangeT>
1068 std::enable_if_t<!std::is_convertible<RangeT, ArrayRef<Type>>::value>
1069 addTypes(RangeT &&newTypes) {
1070 types.append(newTypes.begin(), newTypes.end());
1071 }
1072
1073 /// Add an attribute with the specified name.
1074 void addAttribute(StringRef name, Attribute attr) {
1075 addAttribute(StringAttr::get(getContext(), name), attr);
1076 }
1077
1078 /// Add an attribute with the specified name. `name` and `attr` must not be
1079 /// null.
1080 void addAttribute(StringAttr name, Attribute attr) {
1081 assert(name && "attribute name cannot be null");
1082 assert(attr && "attribute cannot be null");
1083 attributes.append(name, attr);
1084 }
1085
1086 /// Add an array of named attributes.
1087 void addAttributes(ArrayRef<NamedAttribute> newAttributes) {
1088 attributes.append(newAttributes);
1089 }
1090
1091 /// Adds a successor to the operation sate. `successor` must not be null.
1092 void addSuccessors(Block *successor) {
1093 assert(successor && "successor cannot be null");
1094 successors.push_back(Elt: successor);
1095 }
1096 void addSuccessors(BlockRange newSuccessors);
1097
1098 /// Create a region that should be attached to the operation. These regions
1099 /// can be filled in immediately without waiting for Operation to be
1100 /// created. When it is, the region bodies will be transferred.
1101 Region *addRegion();
1102
1103 /// Take a region that should be attached to the Operation. The body of the
1104 /// region will be transferred when the Operation is constructed. If the
1105 /// region is null, a new empty region will be attached to the Operation.
1106 void addRegion(std::unique_ptr<Region> &&region);
1107
1108 /// Take ownership of a set of regions that should be attached to the
1109 /// Operation.
1110 void addRegions(MutableArrayRef<std::unique_ptr<Region>> regions);
1111
1112 /// Get the context held by this operation state.
1113 MLIRContext *getContext() const { return location->getContext(); }
1114};
1115
1116//===----------------------------------------------------------------------===//
1117// OperandStorage
1118//===----------------------------------------------------------------------===//
1119
1120namespace detail {
1121/// This class handles the management of operation operands. Operands are
1122/// stored either in a trailing array, or a dynamically resizable vector.
1123class alignas(8) OperandStorage {
1124public:
1125 OperandStorage(Operation *owner, OpOperand *trailingOperands,
1126 ValueRange values);
1127 ~OperandStorage();
1128
1129 /// Replace the operands contained in the storage with the ones provided in
1130 /// 'values'.
1131 void setOperands(Operation *owner, ValueRange values);
1132
1133 /// Replace the operands beginning at 'start' and ending at 'start' + 'length'
1134 /// with the ones provided in 'operands'. 'operands' may be smaller or larger
1135 /// than the range pointed to by 'start'+'length'.
1136 void setOperands(Operation *owner, unsigned start, unsigned length,
1137 ValueRange operands);
1138
1139 /// Erase the operands held by the storage within the given range.
1140 void eraseOperands(unsigned start, unsigned length);
1141
1142 /// Erase the operands held by the storage that have their corresponding bit
1143 /// set in `eraseIndices`.
1144 void eraseOperands(const BitVector &eraseIndices);
1145
1146 /// Get the operation operands held by the storage.
1147 MutableArrayRef<OpOperand> getOperands() { return {operandStorage, size()}; }
1148
1149 /// Return the number of operands held in the storage.
1150 unsigned size() { return numOperands; }
1151
1152private:
1153 /// Resize the storage to the given size. Returns the array containing the new
1154 /// operands.
1155 MutableArrayRef<OpOperand> resize(Operation *owner, unsigned newSize);
1156
1157 /// The total capacity number of operands that the storage can hold.
1158 unsigned capacity : 31;
1159 /// A flag indicating if the operand storage was dynamically allocated, as
1160 /// opposed to inlined into the owning operation.
1161 unsigned isStorageDynamic : 1;
1162 /// The number of operands within the storage.
1163 unsigned numOperands;
1164 /// A pointer to the operand storage.
1165 OpOperand *operandStorage;
1166};
1167} // namespace detail
1168
1169//===----------------------------------------------------------------------===//
1170// OpPrintingFlags
1171//===----------------------------------------------------------------------===//
1172
1173/// Set of flags used to control the behavior of the various IR print methods
1174/// (e.g. Operation::Print).
1175class OpPrintingFlags {
1176public:
1177 OpPrintingFlags();
1178 OpPrintingFlags(std::nullopt_t) : OpPrintingFlags() {}
1179
1180 /// Enables the elision of large elements attributes by printing a lexically
1181 /// valid but otherwise meaningless form instead of the element data. The
1182 /// `largeElementLimit` is used to configure what is considered to be a
1183 /// "large" ElementsAttr by providing an upper limit to the number of
1184 /// elements.
1185 OpPrintingFlags &elideLargeElementsAttrs(int64_t largeElementLimit = 16);
1186
1187 /// Enables the printing of large element attributes with a hex string. The
1188 /// `largeElementLimit` is used to configure what is considered to be a
1189 /// "large" ElementsAttr by providing an upper limit to the number of
1190 /// elements. Use -1 to disable the hex printing.
1191 OpPrintingFlags &
1192 printLargeElementsAttrWithHex(int64_t largeElementLimit = 100);
1193
1194 /// Enables the elision of large resources strings by omitting them from the
1195 /// `dialect_resources` section. The `largeResourceLimit` is used to configure
1196 /// what is considered to be a "large" resource by providing an upper limit to
1197 /// the string size.
1198 OpPrintingFlags &elideLargeResourceString(int64_t largeResourceLimit = 64);
1199
1200 /// Enable or disable printing of debug information (based on `enable`). If
1201 /// 'prettyForm' is set to true, debug information is printed in a more
1202 /// readable 'pretty' form. Note: The IR generated with 'prettyForm' is not
1203 /// parsable.
1204 OpPrintingFlags &enableDebugInfo(bool enable = true, bool prettyForm = false);
1205
1206 /// Always print operations in the generic form.
1207 OpPrintingFlags &printGenericOpForm(bool enable = true);
1208
1209 /// Skip printing regions.
1210 OpPrintingFlags &skipRegions(bool skip = true);
1211
1212 /// Do not verify the operation when using custom operation printers.
1213 OpPrintingFlags &assumeVerified(bool enable = true);
1214
1215 /// Use local scope when printing the operation. This allows for using the
1216 /// printer in a more localized and thread-safe setting, but may not
1217 /// necessarily be identical to what the IR will look like when dumping
1218 /// the full module.
1219 OpPrintingFlags &useLocalScope(bool enable = true);
1220
1221 /// Print users of values as comments.
1222 OpPrintingFlags &printValueUsers(bool enable = true);
1223
1224 /// Print unique SSA ID numbers for values, block arguments and naming
1225 /// conflicts across all regions
1226 OpPrintingFlags &printUniqueSSAIDs(bool enable = true);
1227
1228 /// Print SSA IDs using their NameLoc, if provided, as prefix.
1229 OpPrintingFlags &printNameLocAsPrefix(bool enable = true);
1230
1231 /// Return if the given ElementsAttr should be elided.
1232 bool shouldElideElementsAttr(ElementsAttr attr) const;
1233
1234 /// Return if the given ElementsAttr should be printed as hex string.
1235 bool shouldPrintElementsAttrWithHex(ElementsAttr attr) const;
1236
1237 /// Return the size limit for printing large ElementsAttr.
1238 std::optional<int64_t> getLargeElementsAttrLimit() const;
1239
1240 /// Return the size limit for printing large ElementsAttr as hex string.
1241 int64_t getLargeElementsAttrHexLimit() const;
1242
1243 /// Return the size limit in chars for printing large resources.
1244 std::optional<uint64_t> getLargeResourceStringLimit() const;
1245
1246 /// Return if debug information should be printed.
1247 bool shouldPrintDebugInfo() const;
1248
1249 /// Return if debug information should be printed in the pretty form.
1250 bool shouldPrintDebugInfoPrettyForm() const;
1251
1252 /// Return if operations should be printed in the generic form.
1253 bool shouldPrintGenericOpForm() const;
1254
1255 /// Return if regions should be skipped.
1256 bool shouldSkipRegions() const;
1257
1258 /// Return if operation verification should be skipped.
1259 bool shouldAssumeVerified() const;
1260
1261 /// Return if the printer should use local scope when dumping the IR.
1262 bool shouldUseLocalScope() const;
1263
1264 /// Return if the printer should print users of values.
1265 bool shouldPrintValueUsers() const;
1266
1267 /// Return if printer should use unique SSA IDs.
1268 bool shouldPrintUniqueSSAIDs() const;
1269
1270 /// Return if the printer should use NameLocs as prefixes when printing SSA
1271 /// IDs
1272 bool shouldUseNameLocAsPrefix() const;
1273
1274private:
1275 /// Elide large elements attributes if the number of elements is larger than
1276 /// the upper limit.
1277 std::optional<int64_t> elementsAttrElementLimit;
1278
1279 /// Elide printing large resources based on size of string.
1280 std::optional<uint64_t> resourceStringCharLimit;
1281
1282 /// Print large element attributes with hex strings if the number of elements
1283 /// is larger than the upper limit.
1284 int64_t elementsAttrHexElementLimit = 100;
1285
1286 /// Print debug information.
1287 bool printDebugInfoFlag : 1;
1288 bool printDebugInfoPrettyFormFlag : 1;
1289
1290 /// Print operations in the generic form.
1291 bool printGenericOpFormFlag : 1;
1292
1293 /// Always skip Regions.
1294 bool skipRegionsFlag : 1;
1295
1296 /// Skip operation verification.
1297 bool assumeVerifiedFlag : 1;
1298
1299 /// Print operations with numberings local to the current operation.
1300 bool printLocalScope : 1;
1301
1302 /// Print users of values.
1303 bool printValueUsersFlag : 1;
1304
1305 /// Print unique SSA IDs for values, block arguments and naming conflicts
1306 bool printUniqueSSAIDsFlag : 1;
1307
1308 /// Print SSA IDs using NameLocs as prefixes
1309 bool useNameLocAsPrefix : 1;
1310};
1311
1312//===----------------------------------------------------------------------===//
1313// Operation Equivalency
1314//===----------------------------------------------------------------------===//
1315
1316/// This class provides utilities for computing if two operations are
1317/// equivalent.
1318struct OperationEquivalence {
1319 enum Flags {
1320 None = 0,
1321
1322 // When provided, the location attached to the operation are ignored.
1323 IgnoreLocations = 1,
1324
1325 // When provided, the discardable attributes attached to the operation are
1326 // ignored.
1327 IgnoreDiscardableAttrs = 2,
1328
1329 // When provided, the properties attached to the operation are ignored.
1330 IgnoreProperties = 4,
1331
1332 LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ IgnoreProperties)
1333 };
1334
1335 /// Compute a hash for the given operation.
1336 /// The `hashOperands` and `hashResults` callbacks are expected to return a
1337 /// unique hash_code for a given Value.
1338 static llvm::hash_code computeHash(
1339 Operation *op,
1340 function_ref<llvm::hash_code(Value)> hashOperands =
1341 [](Value v) { return hash_value(arg: v); },
1342 function_ref<llvm::hash_code(Value)> hashResults =
1343 [](Value v) { return hash_value(arg: v); },
1344 Flags flags = Flags::None);
1345
1346 /// Helper that can be used with `computeHash` above to ignore operation
1347 /// operands/result mapping.
1348 static llvm::hash_code ignoreHashValue(Value) { return llvm::hash_code{}; }
1349 /// Helper that can be used with `computeHash` above to ignore operation
1350 /// operands/result mapping.
1351 static llvm::hash_code directHashValue(Value v) { return hash_value(arg: v); }
1352
1353 /// Compare two operations (including their regions) and return if they are
1354 /// equivalent.
1355 ///
1356 /// * `checkEquivalent` is a callback to check if two values are equivalent.
1357 /// For two operations to be equivalent, their operands must be the same SSA
1358 /// value or this callback must return `success`.
1359 /// * `markEquivalent` is a callback to inform the caller that the analysis
1360 /// determined that two values are equivalent.
1361 /// * `checkCommutativeEquivalent` is an optional callback to check for
1362 /// equivalence across two ranges for a commutative operation. If not passed
1363 /// in, then equivalence is checked pairwise. This callback is needed to be
1364 /// able to query the optional equivalence classes.
1365 ///
1366 /// Note: Additional information regarding value equivalence can be injected
1367 /// into the analysis via `checkEquivalent`. Typically, callers may want
1368 /// values that were determined to be equivalent as per `markEquivalent` to be
1369 /// reflected in `checkEquivalent`, unless `exactValueMatch` or a different
1370 /// equivalence relationship is desired.
1371 static bool
1372 isEquivalentTo(Operation *lhs, Operation *rhs,
1373 function_ref<LogicalResult(Value, Value)> checkEquivalent,
1374 function_ref<void(Value, Value)> markEquivalent = nullptr,
1375 Flags flags = Flags::None,
1376 function_ref<LogicalResult(ValueRange, ValueRange)>
1377 checkCommutativeEquivalent = nullptr);
1378
1379 /// Compare two operations and return if they are equivalent.
1380 static bool isEquivalentTo(Operation *lhs, Operation *rhs, Flags flags);
1381
1382 /// Compare two regions (including their subregions) and return if they are
1383 /// equivalent. See also `isEquivalentTo` for details.
1384 static bool isRegionEquivalentTo(
1385 Region *lhs, Region *rhs,
1386 function_ref<LogicalResult(Value, Value)> checkEquivalent,
1387 function_ref<void(Value, Value)> markEquivalent,
1388 OperationEquivalence::Flags flags,
1389 function_ref<LogicalResult(ValueRange, ValueRange)>
1390 checkCommutativeEquivalent = nullptr);
1391
1392 /// Compare two regions and return if they are equivalent.
1393 static bool isRegionEquivalentTo(Region *lhs, Region *rhs,
1394 OperationEquivalence::Flags flags);
1395
1396 /// Helper that can be used with `isEquivalentTo` above to consider ops
1397 /// equivalent even if their operands are not equivalent.
1398 static LogicalResult ignoreValueEquivalence(Value lhs, Value rhs) {
1399 return success();
1400 }
1401 /// Helper that can be used with `isEquivalentTo` above to consider ops
1402 /// equivalent only if their operands are the exact same SSA values.
1403 static LogicalResult exactValueMatch(Value lhs, Value rhs) {
1404 return success(IsSuccess: lhs == rhs);
1405 }
1406};
1407
1408/// Enable Bitmask enums for OperationEquivalence::Flags.
1409LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
1410
1411//===----------------------------------------------------------------------===//
1412// OperationFingerPrint
1413//===----------------------------------------------------------------------===//
1414
1415/// A unique fingerprint for a specific operation, and all of it's internal
1416/// operations (if `includeNested` is set).
1417class OperationFingerPrint {
1418public:
1419 OperationFingerPrint(Operation *topOp, bool includeNested = true);
1420 OperationFingerPrint(const OperationFingerPrint &) = default;
1421 OperationFingerPrint &operator=(const OperationFingerPrint &) = default;
1422
1423 bool operator==(const OperationFingerPrint &other) const {
1424 return hash == other.hash;
1425 }
1426 bool operator!=(const OperationFingerPrint &other) const {
1427 return !(*this == other);
1428 }
1429
1430private:
1431 std::array<uint8_t, 20> hash;
1432};
1433
1434} // namespace mlir
1435
1436namespace llvm {
1437template <>
1438struct DenseMapInfo<mlir::OperationName> {
1439 static mlir::OperationName getEmptyKey() {
1440 void *pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
1441 return mlir::OperationName::getFromOpaquePointer(pointer);
1442 }
1443 static mlir::OperationName getTombstoneKey() {
1444 void *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
1445 return mlir::OperationName::getFromOpaquePointer(pointer);
1446 }
1447 static unsigned getHashValue(mlir::OperationName val) {
1448 return DenseMapInfo<void *>::getHashValue(PtrVal: val.getAsOpaquePointer());
1449 }
1450 static bool isEqual(mlir::OperationName lhs, mlir::OperationName rhs) {
1451 return lhs == rhs;
1452 }
1453};
1454template <>
1455struct DenseMapInfo<mlir::RegisteredOperationName>
1456 : public DenseMapInfo<mlir::OperationName> {
1457 static mlir::RegisteredOperationName getEmptyKey() {
1458 void *pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
1459 return mlir::RegisteredOperationName::getFromOpaquePointer(pointer);
1460 }
1461 static mlir::RegisteredOperationName getTombstoneKey() {
1462 void *pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
1463 return mlir::RegisteredOperationName::getFromOpaquePointer(pointer);
1464 }
1465};
1466
1467template <>
1468struct PointerLikeTypeTraits<mlir::OperationName> {
1469 static inline void *getAsVoidPointer(mlir::OperationName I) {
1470 return const_cast<void *>(I.getAsOpaquePointer());
1471 }
1472 static inline mlir::OperationName getFromVoidPointer(void *P) {
1473 return mlir::OperationName::getFromOpaquePointer(pointer: P);
1474 }
1475 static constexpr int NumLowBitsAvailable =
1476 PointerLikeTypeTraits<void *>::NumLowBitsAvailable;
1477};
1478template <>
1479struct PointerLikeTypeTraits<mlir::RegisteredOperationName>
1480 : public PointerLikeTypeTraits<mlir::OperationName> {
1481 static inline mlir::RegisteredOperationName getFromVoidPointer(void *P) {
1482 return mlir::RegisteredOperationName::getFromOpaquePointer(pointer: P);
1483 }
1484};
1485
1486} // namespace llvm
1487
1488#endif
1489

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of mlir/include/mlir/IR/OperationSupport.h