//===- OpDefinition.h - Classes for defining concrete Op types --*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements helper classes for implementing the "Op" types. This // includes the Op type, which is the base class for Op class definitions, // as well as number of traits in the OpTrait namespace that provide a // declarative way to specify properties of Ops. // // The purpose of these types are to allow light-weight implementation of // concrete ops (like DimOp) with very little boilerplate. // //===----------------------------------------------------------------------===// #ifndef MLIR_IR_OPDEFINITION_H #define MLIR_IR_OPDEFINITION_H #include "mlir/IR/Dialect.h" #include "mlir/IR/ODSSupport.h" #include "mlir/IR/Operation.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include <optional> #include <type_traits> namespace mlir { class Builder; class OpBuilder; /// This class implements `Optional` functionality for ParseResult. We don't /// directly use Optional here, because it provides an implicit conversion /// to 'bool' which we want to avoid. This class is used to implement tri-state /// 'parseOptional' functions that may have a failure mode when parsing that /// shouldn't be attributed to "not present". class OptionalParseResult { … }; // These functions are out-of-line utilities, which avoids them being template // instantiated/duplicated. namespace impl { /// Insert an operation, generated by `buildTerminatorOp`, at the end of the /// region's only block if it does not have a terminator already. If the region /// is empty, insert a new block first. `buildTerminatorOp` should return the /// terminator operation to insert. void ensureRegionTerminator( Region ®ion, OpBuilder &builder, Location loc, function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp); void ensureRegionTerminator( Region ®ion, Builder &builder, Location loc, function_ref<Operation *(OpBuilder &, Location)> buildTerminatorOp); } // namespace impl /// Structure used by default as a "marker" when no "Properties" are set on an /// Operation. struct EmptyProperties { … }; /// Traits to detect whether an Operation defined a `Properties` type, otherwise /// it'll default to `EmptyProperties`. template <class Op, class = void> struct PropertiesSelector { … }; PropertiesSelector<Op, std::void_t<typename Op::Properties>>; /// This is the concrete base class that holds the operation pointer and has /// non-generic methods that only depend on State (to avoid having them /// instantiated on template types that don't affect them. /// /// This also has the fallback implementations of customization hooks for when /// they aren't customized. class OpState { … }; // Allow comparing operators. inline bool operator==(OpState lhs, OpState rhs) { … } inline bool operator!=(OpState lhs, OpState rhs) { … } raw_ostream &operator<<(raw_ostream &os, OpFoldResult ofr); /// This class represents a single result from folding an operation. class OpFoldResult : public PointerUnion<Attribute, Value> { … }; // Temporarily exit the MLIR namespace to add casting support as later code in // this uses it. The CastInfo must come after the OpFoldResult definition and // before any cast function calls depending on CastInfo. } // namespace mlir namespace llvm { // Allow llvm::cast style functions. CastInfo<To, mlir::OpFoldResult>; CastInfo<To, const mlir::OpFoldResult>; } // namespace llvm namespace mlir { /// Allow printing to a stream. inline raw_ostream &operator<<(raw_ostream &os, OpFoldResult ofr) { … } /// Allow printing to a stream. inline raw_ostream &operator<<(raw_ostream &os, OpState op) { … } //===----------------------------------------------------------------------===// // Operation Trait Types //===----------------------------------------------------------------------===// namespace OpTrait { // These functions are out-of-line implementations of the methods in the // corresponding trait classes. This avoids them being template // instantiated/duplicated. namespace impl { LogicalResult foldCommutative(Operation *op, ArrayRef<Attribute> operands, SmallVectorImpl<OpFoldResult> &results); OpFoldResult foldIdempotent(Operation *op); OpFoldResult foldInvolution(Operation *op); LogicalResult verifyZeroOperands(Operation *op); LogicalResult verifyOneOperand(Operation *op); LogicalResult verifyNOperands(Operation *op, unsigned numOperands); LogicalResult verifyIsIdempotent(Operation *op); LogicalResult verifyIsInvolution(Operation *op); LogicalResult verifyAtLeastNOperands(Operation *op, unsigned numOperands); LogicalResult verifyOperandsAreFloatLike(Operation *op); LogicalResult verifyOperandsAreSignlessIntegerLike(Operation *op); LogicalResult verifySameTypeOperands(Operation *op); LogicalResult verifyZeroRegions(Operation *op); LogicalResult verifyOneRegion(Operation *op); LogicalResult verifyNRegions(Operation *op, unsigned numRegions); LogicalResult verifyAtLeastNRegions(Operation *op, unsigned numRegions); LogicalResult verifyZeroResults(Operation *op); LogicalResult verifyOneResult(Operation *op); LogicalResult verifyNResults(Operation *op, unsigned numOperands); LogicalResult verifyAtLeastNResults(Operation *op, unsigned numOperands); LogicalResult verifySameOperandsShape(Operation *op); LogicalResult verifySameOperandsAndResultShape(Operation *op); LogicalResult verifySameOperandsElementType(Operation *op); LogicalResult verifySameOperandsAndResultElementType(Operation *op); LogicalResult verifySameOperandsAndResultType(Operation *op); LogicalResult verifySameOperandsAndResultRank(Operation *op); LogicalResult verifyResultsAreBoolLike(Operation *op); LogicalResult verifyResultsAreFloatLike(Operation *op); LogicalResult verifyResultsAreSignlessIntegerLike(Operation *op); LogicalResult verifyIsTerminator(Operation *op); LogicalResult verifyZeroSuccessors(Operation *op); LogicalResult verifyOneSuccessor(Operation *op); LogicalResult verifyNSuccessors(Operation *op, unsigned numSuccessors); LogicalResult verifyAtLeastNSuccessors(Operation *op, unsigned numSuccessors); LogicalResult verifyValueSizeAttr(Operation *op, StringRef attrName, StringRef valueGroupName, size_t expectedCount); LogicalResult verifyOperandSizeAttr(Operation *op, StringRef sizeAttrName); LogicalResult verifyResultSizeAttr(Operation *op, StringRef sizeAttrName); LogicalResult verifyNoRegionArguments(Operation *op); LogicalResult verifyElementwise(Operation *op); LogicalResult verifyIsIsolatedFromAbove(Operation *op); } // namespace impl /// Helper class for implementing traits. Clients are not expected to interact /// with this directly, so its members are all protected. template <typename ConcreteType, template <typename> class TraitType> class TraitBase { … }; //===----------------------------------------------------------------------===// // Operand Traits namespace detail { /// Utility trait base that provides accessors for derived traits that have /// multiple operands. template <typename ConcreteType, template <typename> class TraitType> struct MultiOperandTraitBase : public TraitBase<ConcreteType, TraitType> { … }; } // namespace detail /// `verifyInvariantsImpl` verifies the invariants like the types, attrs, .etc. /// It should be run after core traits and before any other user defined traits. /// In order to run it in the correct order, wrap it with OpInvariants trait so /// that tblgen will be able to put it in the right order. template <typename ConcreteType> class OpInvariants : public TraitBase<ConcreteType, OpInvariants> { … }; /// This class provides the API for ops that are known to have no /// SSA operand. template <typename ConcreteType> class ZeroOperands : public TraitBase<ConcreteType, ZeroOperands> { … }; /// This class provides the API for ops that are known to have exactly one /// SSA operand. template <typename ConcreteType> class OneOperand : public TraitBase<ConcreteType, OneOperand> { … }; /// This class provides the API for ops that are known to have a specified /// number of operands. This is used as a trait like this: /// /// class FooOp : public Op<FooOp, OpTrait::NOperands<2>::Impl> { /// template <unsigned N> class NOperands { … }; /// This class provides the API for ops that are known to have a at least a /// specified number of operands. This is used as a trait like this: /// /// class FooOp : public Op<FooOp, OpTrait::AtLeastNOperands<2>::Impl> { /// template <unsigned N> class AtLeastNOperands { … }; /// This class provides the API for ops which have an unknown number of /// SSA operands. template <typename ConcreteType> class VariadicOperands : public detail::MultiOperandTraitBase<ConcreteType, VariadicOperands> { … }; //===----------------------------------------------------------------------===// // Region Traits /// This class provides verification for ops that are known to have zero /// regions. template <typename ConcreteType> class ZeroRegions : public TraitBase<ConcreteType, ZeroRegions> { … }; namespace detail { /// Utility trait base that provides accessors for derived traits that have /// multiple regions. template <typename ConcreteType, template <typename> class TraitType> struct MultiRegionTraitBase : public TraitBase<ConcreteType, TraitType> { … }; } // namespace detail /// This class provides APIs for ops that are known to have a single region. template <typename ConcreteType> class OneRegion : public TraitBase<ConcreteType, OneRegion> { … }; /// This class provides the API for ops that are known to have a specified /// number of regions. template <unsigned N> class NRegions { … }; /// This class provides APIs for ops that are known to have at least a specified /// number of regions. template <unsigned N> class AtLeastNRegions { … }; /// This class provides the API for ops which have an unknown number of /// regions. template <typename ConcreteType> class VariadicRegions : public detail::MultiRegionTraitBase<ConcreteType, VariadicRegions> { … }; //===----------------------------------------------------------------------===// // Result Traits /// This class provides return value APIs for ops that are known to have /// zero results. template <typename ConcreteType> class ZeroResults : public TraitBase<ConcreteType, ZeroResults> { … }; namespace detail { /// Utility trait base that provides accessors for derived traits that have /// multiple results. template <typename ConcreteType, template <typename> class TraitType> struct MultiResultTraitBase : public TraitBase<ConcreteType, TraitType> { … }; } // namespace detail /// This class provides return value APIs for ops that are known to have a /// single result. ResultType is the concrete type returned by getType(). template <typename ConcreteType> class OneResult : public TraitBase<ConcreteType, OneResult> { … }; /// This trait is used for return value APIs for ops that are known to have a /// specific type other than `Type`. This allows the "getType()" member to be /// more specific for an op. This should be used in conjunction with OneResult, /// and occur in the trait list before OneResult. template <typename ResultType> class OneTypedResult { … }; /// This class provides the API for ops that are known to have a specified /// number of results. This is used as a trait like this: /// /// class FooOp : public Op<FooOp, OpTrait::NResults<2>::Impl> { /// template <unsigned N> class NResults { … }; /// This class provides the API for ops that are known to have at least a /// specified number of results. This is used as a trait like this: /// /// class FooOp : public Op<FooOp, OpTrait::AtLeastNResults<2>::Impl> { /// template <unsigned N> class AtLeastNResults { … }; /// This class provides the API for ops which have an unknown number of /// results. template <typename ConcreteType> class VariadicResults : public detail::MultiResultTraitBase<ConcreteType, VariadicResults> { … }; //===----------------------------------------------------------------------===// // Terminator Traits /// This class indicates that the regions associated with this op don't have /// terminators. template <typename ConcreteType> class NoTerminator : public TraitBase<ConcreteType, NoTerminator> { … }; /// This class provides the API for ops that are known to be terminators. template <typename ConcreteType> class IsTerminator : public TraitBase<ConcreteType, IsTerminator> { … }; /// This class provides verification for ops that are known to have zero /// successors. template <typename ConcreteType> class ZeroSuccessors : public TraitBase<ConcreteType, ZeroSuccessors> { … }; namespace detail { /// Utility trait base that provides accessors for derived traits that have /// multiple successors. template <typename ConcreteType, template <typename> class TraitType> struct MultiSuccessorTraitBase : public TraitBase<ConcreteType, TraitType> { … }; } // namespace detail /// This class provides APIs for ops that are known to have a single successor. template <typename ConcreteType> class OneSuccessor : public TraitBase<ConcreteType, OneSuccessor> { … }; /// This class provides the API for ops that are known to have a specified /// number of successors. template <unsigned N> class NSuccessors { … }; /// This class provides APIs for ops that are known to have at least a specified /// number of successors. template <unsigned N> class AtLeastNSuccessors { … }; /// This class provides the API for ops which have an unknown number of /// successors. template <typename ConcreteType> class VariadicSuccessors : public detail::MultiSuccessorTraitBase<ConcreteType, VariadicSuccessors> { … }; //===----------------------------------------------------------------------===// // SingleBlock /// This class provides APIs and verifiers for ops with regions having a single /// block. template <typename ConcreteType> struct SingleBlock : public TraitBase<ConcreteType, SingleBlock> { … }; //===----------------------------------------------------------------------===// // SingleBlockImplicitTerminator /// This class provides APIs and verifiers for ops with regions having a single /// block that must terminate with `TerminatorOpType`. template <typename TerminatorOpType> struct SingleBlockImplicitTerminator { … }; /// Check is an op defines the `ImplicitTerminatorOpT` member. This is intended /// to be used with `llvm::is_detected`. has_implicit_terminator_t; /// Support to check if an operation has the SingleBlockImplicitTerminator /// trait. We can't just use `hasTrait` because this class is templated on a /// specific terminator op. template <class Op, bool hasTerminator = llvm::is_detected<has_implicit_terminator_t, Op>::value> struct hasSingleBlockImplicitTerminator { … }; hasSingleBlockImplicitTerminator<Op, false>; //===----------------------------------------------------------------------===// // Misc Traits /// This class provides verification for ops that are known to have the same /// operand shape: all operands are scalars, vectors/tensors of the same /// shape. template <typename ConcreteType> class SameOperandsShape : public TraitBase<ConcreteType, SameOperandsShape> { … }; /// This class provides verification for ops that are known to have the same /// operand and result shape: both are scalars, vectors/tensors of the same /// shape. template <typename ConcreteType> class SameOperandsAndResultShape : public TraitBase<ConcreteType, SameOperandsAndResultShape> { … }; /// This class provides verification for ops that are known to have the same /// operand element type (or the type itself if it is scalar). /// template <typename ConcreteType> class SameOperandsElementType : public TraitBase<ConcreteType, SameOperandsElementType> { … }; /// This class provides verification for ops that are known to have the same /// operand and result element type (or the type itself if it is scalar). /// template <typename ConcreteType> class SameOperandsAndResultElementType : public TraitBase<ConcreteType, SameOperandsAndResultElementType> { … }; /// This class provides verification for ops that are known to have the same /// operand and result type. /// /// Note: this trait subsumes the SameOperandsAndResultShape and /// SameOperandsAndResultElementType traits. template <typename ConcreteType> class SameOperandsAndResultType : public TraitBase<ConcreteType, SameOperandsAndResultType> { … }; /// This class verifies that op has same ranks for all /// operands and results types, if known. template <typename ConcreteType> class SameOperandsAndResultRank : public TraitBase<ConcreteType, SameOperandsAndResultRank> { … }; /// This class verifies that any results of the specified op have a boolean /// type, a vector thereof, or a tensor thereof. template <typename ConcreteType> class ResultsAreBoolLike : public TraitBase<ConcreteType, ResultsAreBoolLike> { … }; /// This class verifies that any results of the specified op have a floating /// point type, a vector thereof, or a tensor thereof. template <typename ConcreteType> class ResultsAreFloatLike : public TraitBase<ConcreteType, ResultsAreFloatLike> { … }; /// This class verifies that any results of the specified op have a signless /// integer or index type, a vector thereof, or a tensor thereof. template <typename ConcreteType> class ResultsAreSignlessIntegerLike : public TraitBase<ConcreteType, ResultsAreSignlessIntegerLike> { … }; /// This class adds property that the operation is commutative. template <typename ConcreteType> class IsCommutative : public TraitBase<ConcreteType, IsCommutative> { … }; /// This class adds property that the operation is an involution. /// This means a unary to unary operation "f" that satisfies f(f(x)) = x template <typename ConcreteType> class IsInvolution : public TraitBase<ConcreteType, IsInvolution> { … }; /// This class adds property that the operation is idempotent. /// This means a unary to unary operation "f" that satisfies f(f(x)) = f(x), /// or a binary operation "g" that satisfies g(x, x) = x. template <typename ConcreteType> class IsIdempotent : public TraitBase<ConcreteType, IsIdempotent> { … }; /// This class verifies that all operands of the specified op have a float type, /// a vector thereof, or a tensor thereof. template <typename ConcreteType> class OperandsAreFloatLike : public TraitBase<ConcreteType, OperandsAreFloatLike> { … }; /// This class verifies that all operands of the specified op have a signless /// integer or index type, a vector thereof, or a tensor thereof. template <typename ConcreteType> class OperandsAreSignlessIntegerLike : public TraitBase<ConcreteType, OperandsAreSignlessIntegerLike> { … }; /// This class verifies that all operands of the specified op have the same /// type. template <typename ConcreteType> class SameTypeOperands : public TraitBase<ConcreteType, SameTypeOperands> { … }; /// This class provides the API for a sub-set of ops that are known to be /// constant-like. These are non-side effecting operations with one result and /// zero operands that can always be folded to a specific attribute value. template <typename ConcreteType> class ConstantLike : public TraitBase<ConcreteType, ConstantLike> { … }; /// This class provides the API for ops that are known to be isolated from /// above. template <typename ConcreteType> class IsIsolatedFromAbove : public TraitBase<ConcreteType, IsIsolatedFromAbove> { … }; /// A trait of region holding operations that defines a new scope for polyhedral /// optimization purposes. Any SSA values of 'index' type that either dominate /// such an operation or are used at the top-level of such an operation /// automatically become valid symbols for the polyhedral scope defined by that /// operation. For more details, see `Traits.md#AffineScope`. template <typename ConcreteType> class AffineScope : public TraitBase<ConcreteType, AffineScope> { … }; /// A trait of region holding operations that define a new scope for automatic /// allocations, i.e., allocations that are freed when control is transferred /// back from the operation's region. Any operations performing such allocations /// (for eg. memref.alloca) will have their allocations automatically freed at /// their closest enclosing operation with this trait. template <typename ConcreteType> class AutomaticAllocationScope : public TraitBase<ConcreteType, AutomaticAllocationScope> { … }; /// This class provides a verifier for ops that are expecting their parent /// to be one of the given parent ops template <typename... ParentOpTypes> struct HasParent { … }; /// A trait for operations that have an attribute specifying operand segments. /// /// Certain operations can have multiple variadic operands and their size /// relationship is not always known statically. For such cases, we need /// a per-op-instance specification to divide the operands into logical groups /// or segments. This can be modeled by attributes. The attribute will be named /// as `operandSegmentSizes`. /// /// This trait verifies the attribute for specifying operand segments has /// the correct type (1D vector) and values (non-negative), etc. template <typename ConcreteType> class AttrSizedOperandSegments : public TraitBase<ConcreteType, AttrSizedOperandSegments> { … }; /// Similar to AttrSizedOperandSegments but used for results. template <typename ConcreteType> class AttrSizedResultSegments : public TraitBase<ConcreteType, AttrSizedResultSegments> { … }; /// This trait provides a verifier for ops that are expecting their regions to /// not have any arguments template <typename ConcrentType> struct NoRegionArguments : public TraitBase<ConcrentType, NoRegionArguments> { … }; // This trait is used to flag operations that consume or produce // values of `MemRef` type where those references can be 'normalized'. // TODO: Right now, the operands of an operation are either all normalizable, // or not. In the future, we may want to allow some of the operands to be // normalizable. template <typename ConcrentType> struct MemRefsNormalizable : public TraitBase<ConcrentType, MemRefsNormalizable> { … }; /// This trait tags element-wise ops on vectors or tensors. /// /// NOTE: Not all ops that are "elementwise" in some abstract sense satisfy this /// trait. In particular, broadcasting behavior is not allowed. /// /// An `Elementwise` op must satisfy the following properties: /// /// 1. If any result is a vector/tensor then at least one operand must also be a /// vector/tensor. /// 2. If any operand is a vector/tensor then there must be at least one result /// and all results must be vectors/tensors. /// 3. All operand and result vector/tensor types must be of the same shape. The /// shape may be dynamic in which case the op's behaviour is undefined for /// non-matching shapes. /// 4. The operation must be elementwise on its vector/tensor operands and /// results. When applied to single-element vectors/tensors, the result must /// be the same per elememnt. /// /// TODO: Avoid hardcoding vector/tensor, and generalize this trait to a new /// interface `ElementwiseTypeInterface` that describes the container types for /// which the operation is elementwise. /// /// Rationale: /// - 1. and 2. guarantee a well-defined iteration space and exclude the cases /// of 0 non-scalar operands or 0 non-scalar results, which complicate a /// generic definition of the iteration space. /// - 3. guarantees that folding can be done across scalars/vectors/tensors with /// the same pattern, as otherwise lots of special handling for type /// mismatches would be needed. /// - 4. guarantees that no error handling is needed. Higher-level dialects /// should reify any needed guards or error handling code before lowering to /// an `Elementwise` op. template <typename ConcreteType> struct Elementwise : public TraitBase<ConcreteType, Elementwise> { … }; /// This trait tags `Elementwise` operatons that can be systematically /// scalarized. All vector/tensor operands and results are then replaced by /// scalars of the respective element type. Semantically, this is the operation /// on a single element of the vector/tensor. /// /// Rationale: /// Allow to define the vector/tensor semantics of elementwise operations based /// on the same op's behavior on scalars. This provides a constructive procedure /// for IR transformations to, e.g., create scalar loop bodies from tensor ops. /// /// Example: /// ``` /// %tensor_select = "arith.select"(%pred_tensor, %true_val, %false_val) /// : (tensor<?xi1>, tensor<?xf32>, tensor<?xf32>) /// -> tensor<?xf32> /// ``` /// can be scalarized to /// /// ``` /// %scalar_select = "arith.select"(%pred, %true_val_scalar, %false_val_scalar) /// : (i1, f32, f32) -> f32 /// ``` template <typename ConcreteType> struct Scalarizable : public TraitBase<ConcreteType, Scalarizable> { … }; /// This trait tags `Elementwise` operatons that can be systematically /// vectorized. All scalar operands and results are then replaced by vectors /// with the respective element type. Semantically, this is the operation on /// multiple elements simultaneously. See also `Tensorizable`. /// /// Rationale: /// Provide the reverse to `Scalarizable` which, when chained together, allows /// reasoning about the relationship between the tensor and vector case. /// Additionally, it permits reasoning about promoting scalars to vectors via /// broadcasting in cases like `%select_scalar_pred` below. template <typename ConcreteType> struct Vectorizable : public TraitBase<ConcreteType, Vectorizable> { … }; /// This trait tags `Elementwise` operatons that can be systematically /// tensorized. All scalar operands and results are then replaced by tensors /// with the respective element type. Semantically, this is the operation on /// multiple elements simultaneously. See also `Vectorizable`. /// /// Rationale: /// Provide the reverse to `Scalarizable` which, when chained together, allows /// reasoning about the relationship between the tensor and vector case. /// Additionally, it permits reasoning about promoting scalars to tensors via /// broadcasting in cases like `%select_scalar_pred` below. /// /// Examples: /// ``` /// %scalar = "arith.addf"(%a, %b) : (f32, f32) -> f32 /// ``` /// can be tensorized to /// ``` /// %tensor = "arith.addf"(%a, %b) : (tensor<?xf32>, tensor<?xf32>) /// -> tensor<?xf32> /// ``` /// /// ``` /// %scalar_pred = "arith.select"(%pred, %true_val, %false_val) /// : (i1, tensor<?xf32>, tensor<?xf32>) -> tensor<?xf32> /// ``` /// can be tensorized to /// ``` /// %tensor_pred = "arith.select"(%pred, %true_val, %false_val) /// : (tensor<?xi1>, tensor<?xf32>, tensor<?xf32>) /// -> tensor<?xf32> /// ``` template <typename ConcreteType> struct Tensorizable : public TraitBase<ConcreteType, Tensorizable> { … }; /// Together, `Elementwise`, `Scalarizable`, `Vectorizable`, and `Tensorizable` /// provide an easy way for scalar operations to conveniently generalize their /// behavior to vectors/tensors, and systematize conversion between these forms. bool hasElementwiseMappableTraits(Operation *op); } // namespace OpTrait //===----------------------------------------------------------------------===// // Internal Trait Utilities //===----------------------------------------------------------------------===// namespace op_definition_impl { //===----------------------------------------------------------------------===// // Trait Existence /// Returns true if this given Trait ID matches the IDs of any of the provided /// trait types `Traits`. template <template <typename T> class... Traits> inline bool hasTrait(TypeID traitID) { … } template <> inline bool hasTrait<>(TypeID traitID) { … } //===----------------------------------------------------------------------===// // Trait Folding /// Trait to check if T provides a 'foldTrait' method for single result /// operations. has_single_result_fold_trait; detect_has_single_result_fold_trait; /// Trait to check if T provides a general 'foldTrait' method. has_fold_trait; detect_has_fold_trait; /// Trait to check if T provides any `foldTrait` method. detect_has_any_fold_trait; /// Returns the result of folding a trait that implements a `foldTrait` function /// that is specialized for operations that have a single result. template <typename Trait> static std::enable_if_t<detect_has_single_result_fold_trait<Trait>::value, LogicalResult> foldTrait(Operation *op, ArrayRef<Attribute> operands, SmallVectorImpl<OpFoldResult> &results) { … } /// Returns the result of folding a trait that implements a generalized /// `foldTrait` function that is supports any operation type. template <typename Trait> static std::enable_if_t<detect_has_fold_trait<Trait>::value, LogicalResult> foldTrait(Operation *op, ArrayRef<Attribute> operands, SmallVectorImpl<OpFoldResult> &results) { … } template <typename Trait> static inline std::enable_if_t<!detect_has_any_fold_trait<Trait>::value, LogicalResult> foldTrait(Operation *, ArrayRef<Attribute>, SmallVectorImpl<OpFoldResult> &) { … } /// Given a tuple type containing a set of traits, return the result of folding /// the given operation. template <typename... Ts> static LogicalResult foldTraits(Operation *op, ArrayRef<Attribute> operands, SmallVectorImpl<OpFoldResult> &results) { … } //===----------------------------------------------------------------------===// // Trait Verification /// Trait to check if T provides a `verifyTrait` method. has_verify_trait; detect_has_verify_trait; /// Trait to check if T provides a `verifyTrait` method. has_verify_region_trait; detect_has_verify_region_trait; /// Verify the given trait if it provides a verifier. template <typename T> std::enable_if_t<detect_has_verify_trait<T>::value, LogicalResult> verifyTrait(Operation *op) { … } template <typename T> inline std::enable_if_t<!detect_has_verify_trait<T>::value, LogicalResult> verifyTrait(Operation *) { … } /// Given a set of traits, return the result of verifying the given operation. template <typename... Ts> LogicalResult verifyTraits(Operation *op) { … } /// Verify the given trait if it provides a region verifier. template <typename T> std::enable_if_t<detect_has_verify_region_trait<T>::value, LogicalResult> verifyRegionTrait(Operation *op) { … } template <typename T> inline std::enable_if_t<!detect_has_verify_region_trait<T>::value, LogicalResult> verifyRegionTrait(Operation *) { … } /// Given a set of traits, return the result of verifying the regions of the /// given operation. template <typename... Ts> LogicalResult verifyRegionTraits(Operation *op) { … } } // namespace op_definition_impl //===----------------------------------------------------------------------===// // Operation Definition classes //===----------------------------------------------------------------------===// /// This provides public APIs that all operations should have. The template /// argument 'ConcreteType' should be the concrete type by CRTP and the others /// are base classes by the policy pattern. template <typename ConcreteType, template <typename T> class... Traits> class Op : public OpState, public Traits<ConcreteType>... { … }; /// This class represents the base of an operation interface. See the definition /// of `detail::Interface` for requirements on the `Traits` type. template <typename ConcreteType, typename Traits> class OpInterface : public detail::Interface<ConcreteType, Operation *, Traits, Op<ConcreteType>, OpTrait::TraitBase> { … }; } // namespace mlir namespace llvm { DenseMapInfo<T, std::enable_if_t<std::is_base_of<mlir::OpState, T>::value && !mlir::detail::IsInterface<T>::value>>; } // namespace llvm #endif