//===- DialectConversion.h - MLIR dialect conversion pass -------*- 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 declares a generic pass for converting between MLIR dialects. // //===----------------------------------------------------------------------===// #ifndef MLIR_TRANSFORMS_DIALECTCONVERSION_H_ #define MLIR_TRANSFORMS_DIALECTCONVERSION_H_ #include "mlir/Config/mlir-config.h" #include "mlir/Rewrite/FrozenRewritePatternSet.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringMap.h" #include namespace mlir { // Forward declarations. class Attribute; class Block; struct ConversionConfig; class ConversionPatternRewriter; class MLIRContext; class Operation; struct OperationConverter; class Type; class Value; //===----------------------------------------------------------------------===// // Type Conversion //===----------------------------------------------------------------------===// /// Type conversion class. Specific conversions and materializations can be /// registered using addConversion and addMaterialization, respectively. class TypeConverter { public: /// Type alias to allow derived classes to inherit constructors with /// `using Base::Base;`. using Base = TypeConverter; virtual ~TypeConverter() = default; TypeConverter() = default; // Copy the registered conversions, but not the caches TypeConverter(const TypeConverter &other) : conversions(other.conversions), sourceMaterializations(other.sourceMaterializations), targetMaterializations(other.targetMaterializations), typeAttributeConversions(other.typeAttributeConversions) {} TypeConverter &operator=(const TypeConverter &other) { conversions = other.conversions; sourceMaterializations = other.sourceMaterializations; targetMaterializations = other.targetMaterializations; typeAttributeConversions = other.typeAttributeConversions; return *this; } /// This class provides all of the information necessary to convert a type /// signature. class SignatureConversion { public: SignatureConversion(unsigned numOrigInputs) : remappedInputs(numOrigInputs) {} /// This struct represents a range of new types or a range of values that /// remaps an existing signature input. struct InputMapping { size_t inputNo, size; SmallVector replacementValues; /// Return "true" if this input was replaces with one or multiple values. bool replacedWithValues() const { return !replacementValues.empty(); } }; /// Return the argument types for the new signature. ArrayRef getConvertedTypes() const { return argTypes; } /// Get the input mapping for the given argument. std::optional getInputMapping(unsigned input) const { return remappedInputs[input]; } //===------------------------------------------------------------------===// // Conversion Hooks //===------------------------------------------------------------------===// /// Remap an input of the original signature with a new set of types. The /// new types are appended to the new signature conversion. void addInputs(unsigned origInputNo, ArrayRef types); /// Append new input types to the signature conversion, this should only be /// used if the new types are not intended to remap an existing input. void addInputs(ArrayRef types); /// Remap an input of the original signature to `replacements` /// values. This drops the original argument. void remapInput(unsigned origInputNo, ArrayRef replacements); private: /// Remap an input of the original signature with a range of types in the /// new signature. void remapInput(unsigned origInputNo, unsigned newInputNo, unsigned newInputCount = 1); /// The remapping information for each of the original arguments. SmallVector, 4> remappedInputs; /// The set of new argument types. SmallVector argTypes; }; /// The general result of a type attribute conversion callback, allowing /// for early termination. The default constructor creates the na case. class AttributeConversionResult { public: constexpr AttributeConversionResult() : impl() {} AttributeConversionResult(Attribute attr) : impl(attr, resultTag) {} static AttributeConversionResult result(Attribute attr); static AttributeConversionResult na(); static AttributeConversionResult abort(); bool hasResult() const; bool isNa() const; bool isAbort() const; Attribute getResult() const; private: AttributeConversionResult(Attribute attr, unsigned tag) : impl(attr, tag) {} llvm::PointerIntPair impl; // Note that na is 0 so that we can use PointerIntPair's default // constructor. static constexpr unsigned naTag = 0; static constexpr unsigned resultTag = 1; static constexpr unsigned abortTag = 2; }; /// Register a conversion function. A conversion function must be convertible /// to any of the following forms (where `T` is `Value` or a class derived /// from `Type`, including `Type` itself): /// /// * std::optional(T) /// - This form represents a 1-1 type conversion. It should return nullptr /// or `std::nullopt` to signify failure. If `std::nullopt` is returned, /// the converter is allowed to try another conversion function to /// perform the conversion. /// * std::optional(T, SmallVectorImpl &) /// - This form represents a 1-N type conversion. It should return /// `failure` or `std::nullopt` to signify a failed conversion. If the /// new set of types is empty, the type is removed and any usages of the /// existing value are expected to be removed during conversion. If /// `std::nullopt` is returned, the converter is allowed to try another /// conversion function to perform the conversion. /// /// Conversion functions that accept `Value` as the first argument are /// context-aware. I.e., they can take into account IR when converting the /// type of the given value. Context-unaware conversion functions accept /// `Type` or a derived class as the first argument. /// /// Note: Context-unaware conversions are cached, but context-aware /// conversions are not. /// /// Note: When attempting to convert a type, e.g. via 'convertType', the /// mostly recently added conversions will be invoked first. template >::template arg_t<0>> void addConversion(FnT &&callback) { registerConversion(wrapCallback(std::forward(callback))); } /// All of the following materializations require function objects that are /// convertible to the following form: /// `Value(OpBuilder &, T, ValueRange, Location)`, /// where `T` is any subclass of `Type`. This function is responsible for /// creating an operation, using the OpBuilder and Location provided, that /// "casts" a range of values into a single value of the given type `T`. It /// must return a Value of the type `T` on success and `nullptr` if /// it failed but other materialization should be attempted. Materialization /// functions must be provided when a type conversion may persist after the /// conversion has finished. /// /// Note: Target materializations may optionally accept an additional Type /// parameter, which is the original type of the SSA value. Furthermore, `T` /// can be a TypeRange; in that case, the function must return a /// SmallVector. /// This method registers a materialization that will be called when /// converting a replacement value back to its original source type. /// This is used when some uses of the original value persist beyond the main /// conversion. template >::template arg_t<1>> void addSourceMaterialization(FnT &&callback) { sourceMaterializations.emplace_back( wrapSourceMaterialization(std::forward(callback))); } /// This method registers a materialization that will be called when /// converting a value to a target type according to a pattern's type /// converter. /// /// Note: Target materializations can optionally inspect the "original" /// type. This type may be different from the type of the input value. /// For example, let's assume that a conversion pattern "P1" replaced an SSA /// value "v1" (type "t1") with "v2" (type "t2"). Then a different conversion /// pattern "P2" matches an op that has "v1" as an operand. Let's furthermore /// assume that "P2" determines that the converted target type of "t1" is /// "t3", which may be different from "t2". In this example, the target /// materialization will be invoked with: outputType = "t3", inputs = "v2", /// originalType = "t1". Note that the original type "t1" cannot be recovered /// from just "t3" and "v2"; that's why the originalType parameter exists. /// /// Note: During a 1:N conversion, the result types can be a TypeRange. In /// that case the materialization produces a SmallVector. template >::template arg_t<1>> void addTargetMaterialization(FnT &&callback) { targetMaterializations.emplace_back( wrapTargetMaterialization(std::forward(callback))); } /// Register a conversion function for attributes within types. Type /// converters may call this function in order to allow hooking into the /// translation of attributes that exist within types. For example, a type /// converter for the `memref` type could use these conversions to convert /// memory spaces or layouts in an extensible way. /// /// The conversion functions take a non-null Type or subclass of Type and a /// non-null Attribute (or subclass of Attribute), and returns a /// `AttributeConversionResult`. This result can either contain an /// `Attribute`, which may be `nullptr`, representing the conversion's /// success, `AttributeConversionResult::na()` (the default empty value), /// indicating that the conversion function did not apply and that further /// conversion functions should be checked, or /// `AttributeConversionResult::abort()` indicating that the conversion /// process should be aborted. /// /// Registered conversion functions are callled in the reverse of the order in /// which they were registered. template < typename FnT, typename T = typename llvm::function_traits>::template arg_t<0>, typename A = typename llvm::function_traits>::template arg_t<1>> void addTypeAttributeConversion(FnT &&callback) { registerTypeAttributeConversion( wrapTypeAttributeConversion(std::forward(callback))); } /// Convert the given type. This function returns failure if no valid /// conversion exists, success otherwise. If the new set of types is empty, /// the type is removed and any usages of the existing value are expected to /// be removed during conversion. /// /// Note: This overload invokes only context-unaware type conversion /// functions. Users should call the other overload if possible. LogicalResult convertType(Type t, SmallVectorImpl &results) const; /// Convert the type of the given value. This function returns failure if no /// valid conversion exists, success otherwise. If the new set of types is /// empty, the type is removed and any usages of the existing value are /// expected to be removed during conversion. /// /// Note: This overload invokes both context-aware and context-unaware type /// conversion functions. LogicalResult convertType(Value v, SmallVectorImpl &results) const; /// This hook simplifies defining 1-1 type conversions. This function returns /// the type to convert to on success, and a null type on failure. Type convertType(Type t) const; Type convertType(Value v) const; /// Attempts a 1-1 type conversion, expecting the result type to be /// `TargetType`. Returns the converted type cast to `TargetType` on success, /// and a null type on conversion or cast failure. template TargetType convertType(Type t) const { return dyn_cast_or_null(convertType(t)); } template TargetType convertType(Value v) const { return dyn_cast_or_null(convertType(v)); } /// Convert the given types, filling 'results' as necessary. This returns /// "failure" if the conversion of any of the types fails, "success" /// otherwise. LogicalResult convertTypes(TypeRange types, SmallVectorImpl &results) const; /// Convert the types of the given values, filling 'results' as necessary. /// This returns "failure" if the conversion of any of the types fails, /// "success" otherwise. LogicalResult convertTypes(ValueRange values, SmallVectorImpl &results) const; /// Return true if the given type is legal for this type converter, i.e. the /// type converts to itself. bool isLegal(Type type) const; bool isLegal(Value value) const; /// Return true if all of the given types are legal for this type converter. bool isLegal(TypeRange range) const { return llvm::all_of(range, [this](Type type) { return isLegal(type); }); } bool isLegal(ValueRange range) const { return llvm::all_of(range, [this](Value value) { return isLegal(value); }); } /// Return true if the given operation has legal operand and result types. bool isLegal(Operation *op) const; /// Return true if the types of block arguments within the region are legal. bool isLegal(Region *region) const; /// Return true if the inputs and outputs of the given function type are /// legal. bool isSignatureLegal(FunctionType ty) const; /// This method allows for converting a specific argument of a signature. It /// takes as inputs the original argument input number, type. /// On success, it populates 'result' with any new mappings. LogicalResult convertSignatureArg(unsigned inputNo, Type type, SignatureConversion &result) const; LogicalResult convertSignatureArgs(TypeRange types, SignatureConversion &result, unsigned origInputOffset = 0) const; LogicalResult convertSignatureArg(unsigned inputNo, Value value, SignatureConversion &result) const; LogicalResult convertSignatureArgs(ValueRange values, SignatureConversion &result, unsigned origInputOffset = 0) const; /// This function converts the type signature of the given block, by invoking /// 'convertSignatureArg' for each argument. This function should return a /// valid conversion for the signature on success, std::nullopt otherwise. std::optional convertBlockSignature(Block *block) const; /// Materialize a conversion from a set of types into one result type by /// generating a cast sequence of some kind. See the respective /// `add*Materialization` for more information on the context for these /// methods. Value materializeSourceConversion(OpBuilder &builder, Location loc, Type resultType, ValueRange inputs) const; Value materializeTargetConversion(OpBuilder &builder, Location loc, Type resultType, ValueRange inputs, Type originalType = {}) const; SmallVector materializeTargetConversion(OpBuilder &builder, Location loc, TypeRange resultType, ValueRange inputs, Type originalType = {}) const; /// Convert an attribute present `attr` from within the type `type` using /// the registered conversion functions. If no applicable conversion has been /// registered, return std::nullopt. Note that the empty attribute/`nullptr` /// is a valid return value for this function. std::optional convertTypeAttribute(Type type, Attribute attr) const; private: /// The signature of the callback used to convert a type. If the new set of /// types is empty, the type is removed and any usages of the existing value /// are expected to be removed during conversion. using ConversionCallbackFn = std::function( PointerUnion, SmallVectorImpl &)>; /// The signature of the callback used to materialize a source conversion. /// /// Arguments: builder, result type, inputs, location using SourceMaterializationCallbackFn = std::function; /// The signature of the callback used to materialize a target conversion. /// /// Arguments: builder, result types, inputs, location, original type using TargetMaterializationCallbackFn = std::function( OpBuilder &, TypeRange, ValueRange, Location, Type)>; /// The signature of the callback used to convert a type attribute. using TypeAttributeConversionCallbackFn = std::function; /// Generate a wrapper for the given callback. This allows for accepting /// different callback forms, that all compose into a single version. /// With callback of form: `std::optional(T)`, where `T` can be a /// `Value` or a `Type` (or a class derived from `Type`). template std::enable_if_t, ConversionCallbackFn> wrapCallback(FnT &&callback) { return wrapCallback([callback = std::forward(callback)]( T typeOrValue, SmallVectorImpl &results) { if (std::optional resultOpt = callback(typeOrValue)) { bool wasSuccess = static_cast(*resultOpt); if (wasSuccess) results.push_back(*resultOpt); return std::optional(success(wasSuccess)); } return std::optional(); }); } /// With callback of form: `std::optional( /// T, SmallVectorImpl &)`, where `T` is a type. template std::enable_if_t &> && std::is_base_of_v, ConversionCallbackFn> wrapCallback(FnT &&callback) const { return [callback = std::forward(callback)]( PointerUnion typeOrValue, SmallVectorImpl &results) -> std::optional { T derivedType; if (Type t = dyn_cast(typeOrValue)) { derivedType = dyn_cast(t); } else if (Value v = dyn_cast(typeOrValue)) { derivedType = dyn_cast(v.getType()); } else { llvm_unreachable("unexpected variant"); } if (!derivedType) return std::nullopt; return callback(derivedType, results); }; } /// With callback of form: `std::optional( /// T, SmallVectorImpl)`, where `T` is a `Value`. template std::enable_if_t &> && std::is_same_v, ConversionCallbackFn> wrapCallback(FnT &&callback) { contextAwareTypeConversionsIndex = conversions.size(); return [callback = std::forward(callback)]( PointerUnion typeOrValue, SmallVectorImpl &results) -> std::optional { if (Type t = dyn_cast(typeOrValue)) { // Context-aware type conversion was called with a type. return std::nullopt; } else if (Value v = dyn_cast(typeOrValue)) { return callback(v, results); } llvm_unreachable("unexpected variant"); return std::nullopt; }; } /// Register a type conversion. void registerConversion(ConversionCallbackFn callback) { conversions.emplace_back(std::move(callback)); cachedDirectConversions.clear(); cachedMultiConversions.clear(); } /// Generate a wrapper for the given source materialization callback. The /// callback may take any subclass of `Type` and the wrapper will check for /// the target type to be of the expected class before calling the callback. template SourceMaterializationCallbackFn wrapSourceMaterialization(FnT &&callback) const { return [callback = std::forward(callback)]( OpBuilder &builder, Type resultType, ValueRange inputs, Location loc) -> Value { if (T derivedType = dyn_cast(resultType)) return callback(builder, derivedType, inputs, loc); return Value(); }; } /// Generate a wrapper for the given target materialization callback. /// The callback may take any subclass of `Type` and the wrapper will check /// for the target type to be of the expected class before calling the /// callback. /// /// With callback of form: /// - Value(OpBuilder &, T, ValueRange, Location, Type) /// - SmallVector(OpBuilder &, TypeRange, ValueRange, Location, Type) template std::enable_if_t< std::is_invocable_v, TargetMaterializationCallbackFn> wrapTargetMaterialization(FnT &&callback) const { return [callback = std::forward(callback)]( OpBuilder &builder, TypeRange resultTypes, ValueRange inputs, Location loc, Type originalType) -> SmallVector { SmallVector result; if constexpr (std::is_same::value) { // This is a 1:N target materialization. Return the produces values // directly. result = callback(builder, resultTypes, inputs, loc, originalType); } else if constexpr (std::is_assignable::value) { // This is a 1:1 target materialization. Invoke the callback only if a // single SSA value is requested. if (resultTypes.size() == 1) { // Invoke the callback only if the type class of the callback matches // the requested result type. if (T derivedType = dyn_cast(resultTypes.front())) { // 1:1 materializations produce single values, but we store 1:N // target materialization functions in the type converter. Wrap the // result value in a SmallVector. Value val = callback(builder, derivedType, inputs, loc, originalType); if (val) result.push_back(val); } } } else { static_assert(sizeof(T) == 0, "T must be a Type or a TypeRange"); } return result; }; } /// With callback of form: /// - Value(OpBuilder &, T, ValueRange, Location) /// - SmallVector(OpBuilder &, TypeRange, ValueRange, Location) template std::enable_if_t< std::is_invocable_v, TargetMaterializationCallbackFn> wrapTargetMaterialization(FnT &&callback) const { return wrapTargetMaterialization( [callback = std::forward(callback)]( OpBuilder &builder, T resultTypes, ValueRange inputs, Location loc, Type originalType) { return callback(builder, resultTypes, inputs, loc); }); } /// Generate a wrapper for the given memory space conversion callback. The /// callback may take any subclass of `Attribute` and the wrapper will check /// for the target attribute to be of the expected class before calling the /// callback. template TypeAttributeConversionCallbackFn wrapTypeAttributeConversion(FnT &&callback) const { return [callback = std::forward(callback)]( Type type, Attribute attr) -> AttributeConversionResult { if (T derivedType = dyn_cast(type)) { if (A derivedAttr = dyn_cast_or_null(attr)) return callback(derivedType, derivedAttr); } return AttributeConversionResult::na(); }; } /// Register a memory space conversion, clearing caches. void registerTypeAttributeConversion(TypeAttributeConversionCallbackFn callback) { typeAttributeConversions.emplace_back(std::move(callback)); // Clear type conversions in case a memory space is lingering inside. cachedDirectConversions.clear(); cachedMultiConversions.clear(); } /// Internal implementation of the type conversion. LogicalResult convertTypeImpl(PointerUnion t, SmallVectorImpl &results) const; /// The set of registered conversion functions. SmallVector conversions; /// The list of registered materialization functions. SmallVector sourceMaterializations; SmallVector targetMaterializations; /// The list of registered type attribute conversion functions. SmallVector typeAttributeConversions; /// A set of cached conversions to avoid recomputing in the common case. /// Direct 1-1 conversions are the most common, so this cache stores the /// successful 1-1 conversions as well as all failed conversions. mutable DenseMap cachedDirectConversions; /// This cache stores the successful 1->N conversions, where N != 1. mutable DenseMap> cachedMultiConversions; /// A mutex used for cache access mutable llvm::sys::SmartRWMutex cacheMutex; /// Whether the type converter has context-aware type conversions. I.e., /// conversion rules that depend on the SSA value instead of just the type. /// We store here the index in the `conversions` vector of the last added /// context-aware conversion, if any. This is useful because we can't cache /// the result of type conversion happening after context-aware conversions, /// because the type converter may return different results for the same input /// type. This is why it is recommened to add context-aware conversions first, /// any context-free conversions after will benefit from caching. int contextAwareTypeConversionsIndex = -1; }; //===----------------------------------------------------------------------===// // Conversion Patterns //===----------------------------------------------------------------------===// /// Base class for the conversion patterns. This pattern class enables type /// conversions, and other uses specific to the conversion framework. As such, /// patterns of this type can only be used with the 'apply*' methods below. class ConversionPattern : public RewritePattern { public: using OpAdaptor = ArrayRef; using OneToNOpAdaptor = ArrayRef; /// Hook for derived classes to implement combined matching and rewriting. /// This overload supports only 1:1 replacements. The 1:N overload is called /// by the driver. By default, it calls this 1:1 overload or fails to match /// if 1:N replacements were found. virtual LogicalResult matchAndRewrite(Operation *op, ArrayRef operands, ConversionPatternRewriter &rewriter) const { llvm_unreachable("matchAndRewrite is not implemented"); } /// Hook for derived classes to implement combined matching and rewriting. /// This overload supports 1:N replacements. virtual LogicalResult matchAndRewrite(Operation *op, ArrayRef operands, ConversionPatternRewriter &rewriter) const { return dispatchTo1To1(*this, op, operands, rewriter); } /// Attempt to match and rewrite the IR root at the specified operation. LogicalResult matchAndRewrite(Operation *op, PatternRewriter &rewriter) const final; /// Return the type converter held by this pattern, or nullptr if the pattern /// does not require type conversion. const TypeConverter *getTypeConverter() const { return typeConverter; } template std::enable_if_t::value, const ConverterTy *> getTypeConverter() const { return static_cast(typeConverter); } protected: /// See `RewritePattern::RewritePattern` for information on the other /// available constructors. using RewritePattern::RewritePattern; /// Construct a conversion pattern with the given converter, and forward the /// remaining arguments to RewritePattern. template ConversionPattern(const TypeConverter &typeConverter, Args &&...args) : RewritePattern(std::forward(args)...), typeConverter(&typeConverter) {} /// Given an array of value ranges, which are the inputs to a 1:N adaptor, /// try to extract the single value of each range to construct a the inputs /// for a 1:1 adaptor. /// /// Returns failure if at least one range has 0 or more than 1 value. FailureOr> getOneToOneAdaptorOperands(ArrayRef operands) const; /// Overloaded method used to dispatch to the 1:1 'matchAndRewrite' method /// if possible and emit diagnostic with a failure return value otherwise. /// 'self' should be '*this' of the derived-pattern and is used to dispatch /// to the correct 'matchAndRewrite' method in the derived pattern. template static LogicalResult dispatchTo1To1(const SelfPattern &self, SourceOp op, ArrayRef operands, ConversionPatternRewriter &rewriter); /// Same as above, but accepts an adaptor as operand. template static LogicalResult dispatchTo1To1( const SelfPattern &self, SourceOp op, typename SourceOp::template GenericAdaptor> adaptor, ConversionPatternRewriter &rewriter); protected: /// An optional type converter for use by this pattern. const TypeConverter *typeConverter = nullptr; }; /// OpConversionPattern is a wrapper around ConversionPattern that allows for /// matching and rewriting against an instance of a derived operation class as /// opposed to a raw Operation. template class OpConversionPattern : public ConversionPattern { public: /// Type alias to allow derived classes to inherit constructors with /// `using Base::Base;`. using Base = OpConversionPattern; using OpAdaptor = typename SourceOp::Adaptor; using OneToNOpAdaptor = typename SourceOp::template GenericAdaptor>; OpConversionPattern(MLIRContext *context, PatternBenefit benefit = 1) : ConversionPattern(SourceOp::getOperationName(), benefit, context) {} OpConversionPattern(const TypeConverter &typeConverter, MLIRContext *context, PatternBenefit benefit = 1) : ConversionPattern(typeConverter, SourceOp::getOperationName(), benefit, context) {} /// Wrappers around the ConversionPattern methods that pass the derived op /// type. LogicalResult matchAndRewrite(Operation *op, ArrayRef operands, ConversionPatternRewriter &rewriter) const final { auto sourceOp = cast(op); return matchAndRewrite(sourceOp, OpAdaptor(operands, sourceOp), rewriter); } LogicalResult matchAndRewrite(Operation *op, ArrayRef operands, ConversionPatternRewriter &rewriter) const final { auto sourceOp = cast(op); return matchAndRewrite(sourceOp, OneToNOpAdaptor(operands, sourceOp), rewriter); } /// Methods that operate on the SourceOp type. One of these must be /// overridden by the derived pattern class. virtual LogicalResult matchAndRewrite(SourceOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const { llvm_unreachable("matchAndRewrite is not implemented"); } virtual LogicalResult matchAndRewrite(SourceOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter) const { return dispatchTo1To1(*this, op, adaptor, rewriter); } private: using ConversionPattern::matchAndRewrite; }; /// OpInterfaceConversionPattern is a wrapper around ConversionPattern that /// allows for matching and rewriting against an instance of an OpInterface /// class as opposed to a raw Operation. template class OpInterfaceConversionPattern : public ConversionPattern { public: /// Type alias to allow derived classes to inherit constructors with /// `using Base::Base;`. using Base = OpInterfaceConversionPattern; OpInterfaceConversionPattern(MLIRContext *context, PatternBenefit benefit = 1) : ConversionPattern(Pattern::MatchInterfaceOpTypeTag(), SourceOp::getInterfaceID(), benefit, context) {} OpInterfaceConversionPattern(const TypeConverter &typeConverter, MLIRContext *context, PatternBenefit benefit = 1) : ConversionPattern(typeConverter, Pattern::MatchInterfaceOpTypeTag(), SourceOp::getInterfaceID(), benefit, context) {} /// Wrappers around the ConversionPattern methods that pass the derived op /// type. LogicalResult matchAndRewrite(Operation *op, ArrayRef operands, ConversionPatternRewriter &rewriter) const final { return matchAndRewrite(cast(op), operands, rewriter); } LogicalResult matchAndRewrite(Operation *op, ArrayRef operands, ConversionPatternRewriter &rewriter) const final { return matchAndRewrite(cast(op), operands, rewriter); } /// Methods that operate on the SourceOp type. One of these must be /// overridden by the derived pattern class. virtual LogicalResult matchAndRewrite(SourceOp op, ArrayRef operands, ConversionPatternRewriter &rewriter) const { llvm_unreachable("matchAndRewrite is not implemented"); } virtual LogicalResult matchAndRewrite(SourceOp op, ArrayRef operands, ConversionPatternRewriter &rewriter) const { return dispatchTo1To1(*this, op, operands, rewriter); } private: using ConversionPattern::matchAndRewrite; }; /// OpTraitConversionPattern is a wrapper around ConversionPattern that allows /// for matching and rewriting against instances of an operation that possess a /// given trait. template