| //===- TransformOps.td - Transform dialect operations ------*- tablegen -*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef MLIR_DIALECT_TRANSFORM_IR_TRANSFORMOPS |
| #define MLIR_DIALECT_TRANSFORM_IR_TRANSFORMOPS |
| |
| include "mlir/Interfaces/CallInterfaces.td" |
| include "mlir/Interfaces/CastInterfaces.td" |
| include "mlir/Interfaces/ControlFlowInterfaces.td" |
| include "mlir/Interfaces/InferTypeOpInterface.td" |
| include "mlir/Interfaces/SideEffectInterfaces.td" |
| include "mlir/Interfaces/FunctionInterfaces.td" |
| include "mlir/IR/OpAsmInterface.td" |
| include "mlir/IR/RegionKindInterface.td" |
| include "mlir/IR/SymbolInterfaces.td" |
| include "mlir/Dialect/Transform/Interfaces/MatchInterfaces.td" |
| include "mlir/Dialect/Transform/IR/TransformAttrs.td" |
| include "mlir/Dialect/Transform/IR/TransformDialect.td" |
| include "mlir/Dialect/Transform/Interfaces/TransformInterfaces.td" |
| |
| def AlternativesOp : TransformDialectOp<"alternatives", |
| [DeclareOpInterfaceMethods<RegionBranchOpInterface, |
| ["getEntrySuccessorOperands", "getSuccessorRegions", |
| "getRegionInvocationBounds"]>, |
| DeclareOpInterfaceMethods<TransformOpInterface>, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| IsolatedFromAbove, PossibleTopLevelTransformOpTrait, |
| SingleBlockImplicitTerminator<"::mlir::transform::YieldOp">]> { |
| let summary = "Attempts sequences of transforms until one succeeds"; |
| let description = [{ |
| This op may have an arbitrary number of regions, each of which represents a |
| sequence of transform operations to be applied to the same payload IR. The |
| regions are visited in order of appearance, and transforms in them are |
| applied in their respective order of appearance. If one of these transforms |
| fails to apply, the remaining ops in the same region are skipped an the next |
| region is attempted. If all transformations in a region succeed, the |
| remaining regions are skipped and the entire "alternatives" transformation |
| succeeds. If all regions contained a failing transformation, the entire |
| "alternatives" transformation fails. |
| |
| It is up to the nested operations to define which errors are "recoverable" |
| (or "silenceable") and allow another alternatives to be attempted, and which |
| errors should be propagated without attempting the other alternatives. |
| |
| The single operand of this operation is the scope in which the alternative |
| transformation sequences are attempted, that is, an operation in the payload |
| IR that contains all the other operations that may be modified by the |
| transformations. The scope operation must be isolated from above. There is |
| no check that the transforms are indeed scoped as their "apply" methods can |
| be arbitrarily complex. Therefore it is the responsibility of the user to |
| ensure that the transforms are scoped correctly, or to produce an |
| irrecoverable error and thus abort the execution without attempting the |
| remaining alternatives. Note that the payload IR outside of the given scope |
| is not necessarily in the valid state, or even accessible to the |
| transformation. |
| |
| The changes to the IR within the scope performed by transforms in the failed |
| alternative region are reverted before attempting the next region. |
| Practically, this is achieved by cloning the scope. Therefore it is advised |
| to limit the scope as much as possible and place the most likely |
| alternatives early in the region list. The operation is also isolated from |
| above and requires rediscovering the operations within the given scope to |
| avoid additional handle invalidation. The latter restriction may be lifted |
| in the future. |
| |
| Each of the regions may yield transform IR handles. The handles of the first |
| successful alternative region are returned as the results of the |
| "alternatives" op. Therefore, each alternative region must yield the same |
| number of results, which should also match the number and the types of the |
| "alternatives" op results. |
| |
| Remark: this op allows one to implement a simple "try" construct as follows: |
| |
| ```mlir |
| %result = transform.alternatives %scope { |
| ^bb0(%arg0: !transform.any_op): |
| // Try a fallible transformation. |
| %0 = transform.fallible %arg0 // ... |
| // If succeeded, yield the the result of the transformation. |
| transform.yield %0 : !transform.any_op |
| }, { |
| ^bb0(%arg0: !transform.any_op): |
| // Otherwise, the second alternative is tried and it always succeeds by |
| // returning the original handle. |
| transform.yield %arg0 : !transform.any_op |
| } |
| ``` |
| }]; |
| |
| let arguments = (ins Optional<TransformHandleTypeInterface>:$scope); |
| let results = (outs Variadic<TransformHandleTypeInterface>:$results); |
| let regions = (region VariadicRegion<SizedRegion<1>>:$alternatives); |
| |
| let assemblyFormat = |
| "($scope^ `:` type($scope))? (`->` type($results)^)? " |
| "attr-dict-with-keyword regions"; |
| let hasVerifier = 1; |
| } |
| |
| def AnnotateOp : TransformDialectOp<"annotate", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> { |
| let summary = "Annotates the target operation with an attribute by name"; |
| let description = [{ |
| Adds an attribute with the given `name` to the `target` operation. An |
| optional `param` handle can be provided to give the attribute a specific |
| value, else a UnitAttr is added. A single attribute will be broadcasted to |
| all target operations, otherwise the attributes will be mapped 1:1 based on |
| the order within the handles. |
| |
| Produces a silenceable failure if the length of the parameter payload does |
| not match the length of the target payload. Does not consume the provided |
| handles. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target, |
| StrAttr:$name, |
| Optional<TransformParamTypeInterface>:$param); |
| let results = (outs); |
| |
| let assemblyFormat = |
| "$target $name attr-dict (`=` $param^)?" |
| "`:` type($target) (`,` type($param)^)?"; |
| } |
| |
| def ApplyCommonSubexpressionEliminationOp : TransformDialectOp<"apply_cse", |
| [TransformOpInterface, TransformEachOpTrait, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| ReportTrackingListenerFailuresOpTrait]> { |
| let summary = "Eliminate common subexpressions in the body of the target op"; |
| let description = [{ |
| This transform applies common subexpression elimination (CSE) to the body |
| of the targeted op. |
| |
| This transform reads the target handle and modifies the payload. Existing |
| handles to operations inside of the targeted op are retained and updated if |
| necessary. Note that this can lead to situations where a handle, that was |
| previously mapped to multiple distinct (but equivalent) operations, is now |
| mapped to the same operation multiple times. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target); |
| let results = (outs); |
| let assemblyFormat = "`to` $target attr-dict `:` type($target)"; |
| |
| let extraClassDeclaration = [{ |
| ::mlir::DiagnosedSilenceableFailure applyToOne( |
| ::mlir::transform::TransformRewriter &rewriter, |
| ::mlir::Operation *target, |
| ::mlir::transform::ApplyToEachResultList &results, |
| ::mlir::transform::TransformState &state); |
| }]; |
| } |
| |
| def ApplyConversionPatternsOp : TransformDialectOp<"apply_conversion_patterns", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| ReportTrackingListenerFailuresOpTrait] |
| # GraphRegionNoTerminator.traits> { |
| let summary = "Applies conversion patterns to the body of the targeted op"; |
| let description = [{ |
| This transform applies the specified conversion patterns to the targeted op |
| and all nested ops. By default, this transform applies a "full" dialect |
| conversion. If the `partial_conversion` unit attribute is present, this |
| transform applies a partial dialect conversion. |
| |
| The patterns that should be applied are specified in the first graph region |
| of this op. They must implement the |
| `ConversionPatternDescriptorOpInterface`. The order in which patterns are |
| applied is unspecified; i.e., the ordering of ops in the region of this op |
| is irrelevant. |
| |
| The second, optional graph region contains exactly one op that specifies |
| default type converter that should be used with this dialect conversion. If |
| provided, this op must implement the `TypeConverterBuilderOpInterface`. |
| Type converters are a property of conversion patterns: each conversion |
| pattern stores the type converter that should be used in its C++ class. Each |
| conversion pattern descriptor can optionally specify a type converter in its |
| `getTypeConverter` interface method. If no type converter is specified in |
| this method, the default type converter of the dialect conversion is used. |
| Default type converters are useful if the same type converter should be used |
| for multiple sets of conversion patterns. (Patterns that should not use this |
| default type converter specify their own type converter.) |
| |
| The `legal_ops`, `illegal_ops`, `legal_dialects`, `illegal_dialects` |
| attributes specify the conversion target. |
| |
| This transform modifies the payload. By default, it consumes the `target` |
| handle. It does not produce any handles. |
| |
| If the `preserve_handles` attribute is set, this transform does not consume |
| the `target` handle and instead updates handles based on notifications from |
| a tracking listener that is attached to the dialect conversion, similar to |
| `transform.apply_patterns`. Only replacements via `RewriterBase::replaceOp` |
| or `replaceOpWithNewOp` are considered "payload op replacements". In |
| contrast to `transform.apply_patterns`, we allow replacement ops even if the |
| op name has changed. This is because conversion patterns are expected to |
| lower ops to different ops (from a different dialect). More details can be |
| found at the documentation site of `TrackingListener`. |
| |
| This transform produces a silenceable failure if the dialect conversion was |
| unsuccessful or the tracking listener failed to find a replacement op. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target, |
| OptionalAttr<StrArrayAttr>:$legal_ops, |
| OptionalAttr<StrArrayAttr>:$illegal_ops, |
| OptionalAttr<StrArrayAttr>:$legal_dialects, |
| OptionalAttr<StrArrayAttr>:$illegal_dialects, |
| UnitAttr:$partial_conversion, |
| UnitAttr:$preserve_handles); |
| let results = (outs); |
| let regions = (region |
| MaxSizedRegion<1>:$patterns, |
| VariadicRegion<MaxSizedRegion<1>>:$default_type_converter_region); |
| |
| let assemblyFormat = [{ |
| `to` $target $patterns |
| (`with` `type_converter` $default_type_converter_region^)? |
| attr-dict `:` type($target) |
| }]; |
| let hasVerifier = 1; |
| |
| let skipDefaultBuilders = 1; |
| let builders = [ |
| OpBuilder<(ins |
| "Value":$target, |
| CArg<"function_ref<void(OpBuilder &, Location)>", "nullptr">: |
| $patternsBodyBuilder, |
| CArg<"function_ref<void(OpBuilder &, Location)>", "nullptr">: |
| $typeConverterBodyBuilder)>, |
| ]; |
| |
| let extraClassDeclaration = [{ |
| ::mlir::transform::TypeConverterBuilderOpInterface getDefaultTypeConverter() { |
| if (getDefaultTypeConverterRegion().size() == 0) |
| return {}; |
| return ::llvm::cast<::mlir::transform::TypeConverterBuilderOpInterface>( |
| &getDefaultTypeConverterRegion()[0].front().front()); |
| } |
| }]; |
| } |
| |
| def ApplyToLLVMConversionPatternsOp : Op<Transform_Dialect, |
| "apply_conversion_patterns.dialect_to_llvm", |
| [DeclareOpInterfaceMethods<ConversionPatternDescriptorOpInterface, |
| ["verifyTypeConverter"]>]> { |
| let description = [{ |
| Collects patterns that convert ops from the specified dialect to LLVM |
| dialect ops. These patterns require an "LLVMTypeConverter". |
| |
| Note: Only dialects that implement the `ConvertToLLVMPatternInterface` are |
| supported. Any conversion target modifications by interface implementations |
| are currently ignored. The conversion target is fully specified by the |
| enclosing "apply_conversion_patterns" op. |
| }]; |
| |
| let arguments = (ins StrAttr:$dialect_name); |
| let assemblyFormat = "$dialect_name attr-dict"; |
| let hasVerifier = 1; |
| } |
| |
| def ApplyDeadCodeEliminationOp : TransformDialectOp<"apply_dce", |
| [TransformOpInterface, TransformEachOpTrait, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| ReportTrackingListenerFailuresOpTrait]> { |
| let summary = "Eliminate dead operations in the body of the target op"; |
| let description = [{ |
| This transform applies dead code elimination (DCE) to the body of the |
| targeted op. |
| |
| Note: "transform.apply_patterns" with an empty region can also be used to |
| remove dead ops. However, that op applies additional simplifications such as |
| op folding and region simplification. |
| |
| This transform reads the target handle and modifies the payload. Note that |
| this transform may silently remove payload ops from handles. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target); |
| let results = (outs); |
| let assemblyFormat = "`to` $target attr-dict `:` type($target)"; |
| |
| let extraClassDeclaration = [{ |
| ::mlir::DiagnosedSilenceableFailure applyToOne( |
| ::mlir::transform::TransformRewriter &rewriter, |
| ::mlir::Operation *target, |
| ::mlir::transform::ApplyToEachResultList &results, |
| ::mlir::transform::TransformState &state); |
| }]; |
| } |
| |
| def ApplyPatternsOp : TransformDialectOp<"apply_patterns", |
| [TransformOpInterface, TransformEachOpTrait, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| ReportTrackingListenerFailuresOpTrait] |
| # GraphRegionNoTerminator.traits> { |
| let summary = "Greedily applies patterns to the body of the targeted op"; |
| let description = [{ |
| This transform greedily applies the specified patterns to the body of the |
| targeted op until a fixpoint was reached. Patterns are not applied to the |
| targeted op itself. |
| |
| The patterns that should be applied are specified in the graph region of |
| this op. They must implement the `PatternDescriptorOpInterface`. The order |
| in which patterns are applied is unspecified; i.e., the ordering of ops in |
| the region of this op is irrelevant. |
| |
| If `apple_cse` is set, the greedy pattern rewrite is interleaved with |
| common subexpression elimination (CSE): both are repeated until a fixpoint |
| is reached. |
| |
| This transform only reads the target handle and modifies the payload. If a |
| pattern erases or replaces a tracked op, the mapping is updated accordingly. |
| |
| Only replacements via `RewriterBase::replaceOp` or `replaceOpWithNewOp` are |
| considered "payload op replacements". Furthermore, only if the replacement |
| values are defined by the same op and that op has the same type as the |
| original op, the mapping is updated. Otherwise, this transform produces a |
| silenceable failure. More details can be found at the documentation site of |
| `TrackingListener`. |
| |
| This transform also produces a silenceable failure if the pattern |
| application did not converge within the default number of |
| iterations/rewrites of the greedy pattern rewrite driver. |
| }]; |
| |
| let arguments = (ins |
| TransformHandleTypeInterface:$target, |
| UnitAttr:$apply_cse, |
| DefaultValuedAttr<I64Attr, "static_cast<uint64_t>(-1)">:$max_iterations, |
| DefaultValuedAttr<I64Attr, "static_cast<uint64_t>(-1)">:$max_num_rewrites); |
| let results = (outs); |
| let regions = (region MaxSizedRegion<1>:$patterns); |
| |
| let assemblyFormat = "`to` $target $patterns attr-dict `:` type($target)"; |
| let hasVerifier = 1; |
| |
| let skipDefaultBuilders = 1; |
| let builders = [ |
| OpBuilder<(ins |
| "Value":$target, |
| CArg<"function_ref<void(OpBuilder &, Location)>", "nullptr">: |
| $bodyBuilder)>, |
| ]; |
| |
| let extraClassDeclaration = [{ |
| ::mlir::DiagnosedSilenceableFailure applyToOne( |
| ::mlir::transform::TransformRewriter &rewriter, |
| ::mlir::Operation *target, |
| ::mlir::transform::ApplyToEachResultList &results, |
| ::mlir::transform::TransformState &state); |
| }]; |
| } |
| |
| def ApplyCanonicalizationPatternsOp |
| : TransformDialectOp<"apply_patterns.canonicalization", |
| [DeclareOpInterfaceMethods<PatternDescriptorOpInterface>]> { |
| let summary = "Populates canonicalization patterns"; |
| let description = [{ |
| This op populates all canonicalization patterns of all loaded dialects in |
| an `apply_patterns` transform. |
| }]; |
| let assemblyFormat = "attr-dict"; |
| } |
| |
| def ApplyLoopInvariantCodeMotionOp : TransformDialectOp<"apply_licm", |
| [TransformOpInterface, TransformEachOpTrait, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| ReportTrackingListenerFailuresOpTrait]> { |
| let summary = "Move loop-invariant code out of a loop-like op"; |
| let description = [{ |
| This transform moves side-effect free, loop invariant code out of the |
| targeted loop-like op. The targeted op must implement the |
| `LoopLikeOpInterface`. |
| |
| Note: To move invariant ops from a loop nest, this transform must be applied |
| to each loop of the loop nest, starting with the inner-most loop. |
| |
| This transform reads the target handle and modifies the payload. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target); |
| let results = (outs); |
| let assemblyFormat = "`to` $target attr-dict `:` type($target)"; |
| |
| let extraClassDeclaration = [{ |
| ::mlir::DiagnosedSilenceableFailure applyToOne( |
| ::mlir::transform::TransformRewriter &rewriter, |
| ::mlir::LoopLikeOpInterface target, |
| ::mlir::transform::ApplyToEachResultList &results, |
| ::mlir::transform::TransformState &state); |
| }]; |
| } |
| |
| def ApplyRegisteredPassOp : TransformDialectOp<"apply_registered_pass", |
| [TransformOpInterface, TransformEachOpTrait, |
| FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface]> { |
| let summary = "Applies the specified registered pass or pass pipeline"; |
| let description = [{ |
| This transform applies the specified pass or pass pipeline to the targeted |
| ops. The name of the pass/pipeline is specified as a string attribute, as |
| set during pass/pipeline registration. Optionally, pass options may be |
| specified as a string attribute. The pass options syntax is identical to the |
| one used with "mlir-opt". |
| |
| This op first looks for a pass pipeline with the specified name. If no such |
| pipeline exists, it looks for a pass with the specified name. If no such |
| pass exists either, this op fails definitely. |
| |
| This transform consumes the target handle and produces a new handle that is |
| mapped to the same op. Passes are not allowed to remove/modify the operation |
| that they operate on, so the target op is guaranteed to still exist. The |
| target handle is invalidated because a pass may arbitrarily modify the body |
| of targeted ops. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target, |
| StrAttr:$pass_name, |
| DefaultValuedAttr<StrAttr, "\"\"">:$options); |
| let results = (outs TransformHandleTypeInterface:$result); |
| let assemblyFormat = [{ |
| $pass_name `to` $target attr-dict `:` functional-type(operands, results) |
| }]; |
| |
| let extraClassDeclaration = [{ |
| ::mlir::DiagnosedSilenceableFailure applyToOne( |
| ::mlir::transform::TransformRewriter &rewriter, |
| ::mlir::Operation *target, |
| ::mlir::transform::ApplyToEachResultList &results, |
| ::mlir::transform::TransformState &state); |
| }]; |
| } |
| |
| def CastOp : TransformDialectOp<"cast", |
| [TransformOpInterface, TransformEachOpTrait, |
| DeclareOpInterfaceMethods<CastOpInterface>, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> { |
| let arguments = (ins TransformHandleTypeInterface:$input); |
| let results = (outs TransformHandleTypeInterface:$output); |
| let assemblyFormat = "$input attr-dict `:` type($input) `to` type($output)"; |
| |
| let extraClassDeclaration = [{ |
| ::mlir::DiagnosedSilenceableFailure applyToOne( |
| ::mlir::transform::TransformRewriter &rewriter, |
| ::mlir::Operation *target, |
| ::mlir::transform::ApplyToEachResultList &results, |
| ::mlir::transform::TransformState &state); |
| }]; |
| } |
| |
| def NumAssociationsOp : TransformDialectOp<"num_associations", |
| [MemoryEffectsOpInterface, ParamProducerTransformOpTrait, |
| DeclareOpInterfaceMethods<TransformOpInterface>, |
| MatchOpInterface]> { |
| let summary = |
| "Returns the number of payload objects associated with the argument"; |
| let description = [{ |
| Given an argument, handle or parameter, returns a new parameter associated |
| with a single 64-bit number that corresponds to the number of payload |
| objects (operations or values for a handle, attributes for a parameter) |
| associated with the argument. |
| |
| Always succeeds. |
| }]; |
| let arguments = (ins Transform_AnyHandleOrParamType:$handle); |
| let results = (outs TransformParamTypeInterface:$num); |
| let assemblyFormat = [{ |
| $handle attr-dict `:` functional-type(operands, results) |
| }]; |
| let hasVerifier = 1; |
| } |
| |
| def CollectMatchingOp : TransformDialectOp<"collect_matching", [ |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| DeclareOpInterfaceMethods<SymbolUserOpInterface>, |
| DeclareOpInterfaceMethods<TransformOpInterface>]> { |
| let summary = "Collects all payload ops that match the given named matcher"; |
| let description = [{ |
| Collects operations or other payload IR objects nested under `root` |
| (inclusive) that match the given matcher expressed as a named sequence. The |
| matcher sequence must accept exactly one argument that it is not allowed to |
| modify. It must yield as many values as this op has results. Each of the |
| yielded values must be associated with exactly one payload object. If any |
| operation in the matcher sequence produces a silenceable failure, the |
| matcher advances to the next payload operation in the walk order without |
| finishing the sequence. |
| |
| The i-th result of this operation is constructed by concatenating the i-th |
| yielded payload IR objects of all successful matcher sequence applications. |
| All results are guaranteed to be mapped to the same number of payload IR |
| objects. |
| |
| The operation succeeds unless the matcher sequence produced a definite |
| failure for any invocation. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$root, |
| SymbolRefAttr:$matcher); |
| let results = (outs Variadic<Transform_AnyHandleOrParamType>:$results); |
| |
| let assemblyFormat = [{ |
| $matcher `in` $root attr-dict `:` functional-type($root, $results) |
| }]; |
| } |
| |
| def ForeachMatchOp : TransformDialectOp<"foreach_match", [ |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| DeclareOpInterfaceMethods<SymbolUserOpInterface>, |
| DeclareOpInterfaceMethods<TransformOpInterface>]> { |
| let summary = "Applies named sequences when a named matcher succeeds"; |
| let description = [{ |
| Given a pair of co-indexed lists of transform dialect symbols (such as |
| `transform.named_sequence`), walks the payload IR associated with the root |
| handle and interprets the symbols as matcher/action pairs by applying the |
| body of the corresponding symbol definition. The symbol from the first list |
| is the matcher part: if it results in a silenceable error, the error is |
| silenced and the next matcher is attempted. Definite failures from any |
| matcher stop the application immediately and are propagated unconditionally. |
| If none of the matchers succeeds, the next payload operation in walk order |
| (post-order at the moment of writing, double check `Operation::walk`) is |
| matched. If a matcher succeeds, the co-indexed action symbol is applied and |
| the following matchers are not applied to the same payload operation. If the |
| action succeeds, the next payload operation in walk order is matched. If it |
| fails, both silenceable and definite errors are propagated as the result of |
| this op. |
| |
| The matcher symbol must take one operand of a type that implements the same |
| transform dialect interface as the `root` operand (a check is performed at |
| application time to see if the associated payload satisfies the constraints |
| of the actual type). It must not consume the operand as multiple matchers |
| may be applied. The matcher may produce any number of results. The action |
| symbol paired with the matcher must take the same number of arguments as the |
| matcher has results, and these arguments must implement the same transform |
| dialect interfaces, but not necessarily have the exact same type (again, a |
| check is performed at application time to see if the associated payload |
| satisfies the constraints of actual types on both sides). The action symbol |
| may not have results. The actions are expected to only modify payload |
| operations nested in the `root` payload operations associated with the |
| operand of this transform operation. Furhermore, the actions may not modify |
| operations outside of the currently matched payload operation, e.g., they |
| may not modify sibling or parent operations. If such behavior is desired, |
| the parent must be matched first and the nested operations obtained by |
| traversing the IR from the parent. This is due to the matching being |
| performed as a post-order IR walk. |
| |
| This operation consumes the operand and produces a new handle associated |
| with the same payload. This is necessary to trigger invalidation of handles |
| to any of the payload operations nested in the payload operations associated |
| with the operand, as those are likely to be modified by actions. |
| |
| By default, the root payload operation associated with the operand is not |
| matched. This is to support the conservative case where applied actions may |
| invalidate the root payload operation. If the optional `restrict_root` |
| attribute is set, the root operand is guaranteed to not be invalidated by any |
| of the applied actions. In such cases, the root payload operation is also |
| matched. This is useful because matching the root payload operation is a |
| common idiom, when e.g. matching a func.func directly and operations nested |
| under it. |
| |
| The operation succeeds if none of the matchers produced a definite failure |
| during application and if all of the applied actions produced success. Note |
| that it also succeeds if all the matchers failed on all payload operations, |
| i.e. failure to apply is not an error. The operation produces a silenceable |
| failure if any applied action produced a silenceable failure. In this case, |
| the resulting handle is associated with an empty payload. The operation |
| produces a definite failure if any of the applied matchers or actions |
| produced a definite failure. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$root, |
| UnitAttr:$restrict_root, |
| SymbolRefArrayAttr:$matchers, |
| SymbolRefArrayAttr:$actions); |
| let results = (outs TransformHandleTypeInterface:$updated); |
| |
| let assemblyFormat = [{ |
| (`restrict_root` $restrict_root^)? |
| `in` |
| $root |
| custom<ForeachMatchSymbols>($matchers, $actions) |
| attr-dict |
| `:` functional-type($root, $updated) |
| }]; |
| |
| let hasVerifier = 1; |
| } |
| |
| def ForeachOp : TransformDialectOp<"foreach", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| DeclareOpInterfaceMethods<RegionBranchOpInterface, [ |
| "getSuccessorRegions", "getEntrySuccessorOperands"]>, |
| SingleBlockImplicitTerminator<"::mlir::transform::YieldOp"> |
| ]> { |
| let summary = "Executes the body for each payload op"; |
| let description = [{ |
| This op has exactly one region with exactly one block ("body"). The body is |
| executed for each payload op that is associated to the target operand in an |
| unbatched fashion. I.e., the block argument ("iteration variable") is always |
| mapped to exactly one payload op. |
| |
| This op always reads the target handle. Furthermore, it consumes the handle |
| if there is a transform op in the body that consumes the iteration variable. |
| This op does not return anything. |
| |
| The transformations inside the body are applied in order of their |
| appearance. During application, if any transformation in the sequence fails, |
| the entire sequence fails immediately leaving the payload IR in potentially |
| invalid state, i.e., this operation offers no transformation rollback |
| capabilities. |
| |
| This op generates as many handles as the terminating YieldOp has operands. |
| For each result, the payload ops of the corresponding YieldOp operand are |
| merged and mapped to the same resulting handle. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target); |
| let results = (outs Variadic<TransformHandleTypeInterface>:$results); |
| let regions = (region SizedRegion<1>:$body); |
| let assemblyFormat = |
| "$target `:` type($target) (`->` type($results)^)? $body attr-dict"; |
| let hasVerifier = 1; |
| |
| let extraClassDeclaration = [{ |
| /// Allow the dialect prefix to be omitted. |
| static StringRef getDefaultDialect() { return "transform"; } |
| |
| BlockArgument getIterationVariable() { |
| return getBody().front().getArgument(0); |
| } |
| |
| transform::YieldOp getYieldOp(); |
| }]; |
| } |
| |
| def GetConsumersOfResult : TransformDialectOp<"get_consumers_of_result", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| NavigationTransformOpTrait, MemoryEffectsOpInterface]> { |
| let summary = "Get handle to the consumers of this operation's result number"; |
| let description = [{ |
| The handle defined by this Transform op corresponds to all operations that |
| consume the SSA value defined by the `target` and `result_number` |
| arguments. |
| This operation applies to a single payload operation, otherwise it produces |
| a definite failure. |
| The return handle points to the consuming operations operations, which can |
| be empty. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target, |
| I64Attr:$result_number); |
| let results = (outs TransformHandleTypeInterface:$consumers); |
| let assemblyFormat = "$target `[` $result_number `]` attr-dict `:` " |
| "functional-type(operands, results)"; |
| } |
| |
| def GetDefiningOp : TransformDialectOp<"get_defining_op", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| MatchOpInterface, |
| NavigationTransformOpTrait, MemoryEffectsOpInterface]> { |
| let summary = "Get handle to the defining op of a value"; |
| let description = [{ |
| The handle defined by this Transform op corresponds to the defining op of |
| the targeted value. |
| |
| This transform produces a silenceable failure if the targeted value is a |
| block argument. |
| }]; |
| |
| let arguments = (ins TransformValueHandleTypeInterface:$target); |
| let results = (outs TransformHandleTypeInterface:$result); |
| let assemblyFormat = "$target attr-dict `:` " |
| "functional-type(operands, results)"; |
| } |
| |
| def GetParentOp : TransformDialectOp<"get_parent_op", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| MatchOpInterface, |
| NavigationTransformOpTrait, MemoryEffectsOpInterface]> { |
| let summary = "Gets handles to the closest parent ops"; |
| let description = [{ |
| The handle defined by this Transform op corresponds to the parents of the |
| targeted payload ops (in the same order). |
| |
| Requirements that parent ops must fulfill can be optionally specified. In |
| that case for each target op, the closest parent op that fulfills all |
| requirements, is returned. |
| - `isolated_from_above`: the parent op must be isolated from above |
| - `allow_empty_results`: get_parent_op is allowed to return an empty list |
| and still succeeds. In such a case, if `get_parent_op` fails for any |
| operation in the list, the entire transform returns an empty handle. |
| - `op_name`: the parent op must have the specified name |
| - `nth_parent`: get the n-th parent of that satisfies the above requirements |
| |
| If `deduplicate` is set, the result handle does not contain any duplicate |
| ops. For example, given the list |
| "(childof(A), childof(B), childof(B), childof(A), childof(B))", the |
| resulting list will be just "(A, B)". Note that no other semantic ordering |
| is applied, e.g., "B" may itself be a parent of "A". This may have an impact |
| on the further transformation applied to the handle produced here. |
| |
| If any of the given Payload IR ops has no such suitable parent, then: |
| - if `allow_empty_results` is set, the result handle is empty |
| - otherwise, the transformation produces a silenceable failure. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target, |
| UnitAttr:$isolated_from_above, |
| UnitAttr:$allow_empty_results, |
| OptionalAttr<StrAttr>:$op_name, |
| UnitAttr:$deduplicate, |
| DefaultValuedAttr<ConfinedAttr<I64Attr, [IntPositive]>, |
| "1">:$nth_parent); |
| let results = (outs TransformHandleTypeInterface:$parent); |
| let assemblyFormat = |
| "$target attr-dict `:` functional-type(operands, results)"; |
| } |
| |
| def GetProducerOfOperand : TransformDialectOp<"get_producer_of_operand", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| NavigationTransformOpTrait, MatchOpInterface, MemoryEffectsOpInterface]> { |
| let summary = "Get handle to the producer of this operation's operand number"; |
| let description = [{ |
| The handle defined by this Transform op corresponds to operation that |
| produces the SSA value defined by the `target` and `operand_number` |
| arguments. If the origin of the SSA value is not an operations (i.e. it is |
| a block argument), the transform produces a silenceable failure. |
| The return handle points to only the subset of successfully produced |
| computational operations, which can be empty. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target, |
| I64Attr:$operand_number); |
| let results = (outs TransformHandleTypeInterface:$producer); |
| let assemblyFormat = "$target `[` $operand_number `]` attr-dict `:` " |
| "functional-type(operands, results)"; |
| } |
| |
| def GetOperandOp : TransformDialectOp<"get_operand", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| NavigationTransformOpTrait, MatchOpInterface, MemoryEffectsOpInterface]> { |
| let summary = "Get a handle to the operand(s) of the targeted op"; |
| let description = [{ |
| The handle defined by this Transform op corresponds to the operands of the |
| given `target` operation specified by the given set of positions. There are |
| three possible modes: |
| |
| - Position list directly, i.e. `%target[0, 1, 2]`. This will return the |
| operands at the specified positions. |
| - Inverted position list, i.e. `%target[except(0, 1, 2)]`. This will return |
| all operands except those at the given positions. |
| - All, i.e. `%target[all]`. This will return all operands of the operation. |
| |
| This transform produces a silenceable failure if any of the operand indices |
| exceeds the number of operands in the target. It reads the target handle and |
| produces the result handle. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target, |
| DenseI64ArrayAttr:$raw_position_list, |
| UnitAttr:$is_inverted, |
| UnitAttr:$is_all); |
| let results = (outs TransformValueHandleTypeInterface:$result); |
| let assemblyFormat = |
| "$target `[`" |
| "custom<TransformMatchDims>($raw_position_list, $is_inverted, $is_all)" |
| "`]` attr-dict `:` functional-type(operands, results)"; |
| let hasVerifier = 1; |
| } |
| |
| def GetResultOp : TransformDialectOp<"get_result", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| NavigationTransformOpTrait, MemoryEffectsOpInterface]> { |
| let summary = "Get a handle to the result(s) of the targeted op"; |
| let description = [{ |
| The handle defined by this Transform op correspond to the OpResults of the |
| given `target` operation. Optionally `result_number` can be specified to |
| select a specific result. |
| |
| This transform fails silently if the targeted operation does not have enough |
| results. It reads the target handle and produces the result handle. |
| |
| The handle defined by this Transform op corresponds to the results of the |
| given `target` operation specified by the given set of positions. There are |
| three possible modes: |
| |
| - Position list directly, i.e. `%target[0, 1, 2]`. This will return the |
| results at the specified positions. |
| - Inverted position list, i.e. `%target[except(0, 1, 2)]`. This will return |
| all results except those at the given positions. |
| - All, i.e. `%target[all]`. This will return all results of the operation. |
| |
| This transform produces a silenceable failure if any of the result indices |
| exceeds the number of results returned by the target. It reads the target |
| handle and produces the result handle. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target, |
| DenseI64ArrayAttr:$raw_position_list, |
| UnitAttr:$is_inverted, |
| UnitAttr:$is_all); |
| let results = (outs TransformValueHandleTypeInterface:$result); |
| let assemblyFormat = |
| "$target `[`" |
| "custom<TransformMatchDims>($raw_position_list, $is_inverted, $is_all)" |
| "`]` attr-dict `:` functional-type(operands, results)"; |
| let hasVerifier = 1; |
| } |
| |
| def GetTypeOp : TransformDialectOp<"get_type", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| MatchOpInterface, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> { |
| let summary = "Get a parameter containing the type of the given value"; |
| let description = [{ |
| This operation creates a new Transform parameter containing the |
| type(s) of the value(s) associated with the operand handle. |
| |
| This transform never fails. |
| }]; |
| |
| let arguments = (ins TransformValueHandleTypeInterface:$value, |
| UnitAttr:$elemental); |
| let results = (outs TransformParamTypeInterface:$type_param); |
| let assemblyFormat = "(`elemental` $elemental^)? $value attr-dict `:`" |
| "functional-type(operands, results)"; |
| } |
| |
| def IncludeOp : TransformDialectOp<"include", |
| [CallOpInterface, |
| MatchOpInterface, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| DeclareOpInterfaceMethods<SymbolUserOpInterface>, |
| DeclareOpInterfaceMethods<TransformOpInterface>]> { |
| let summary = "Includes a named transform sequence"; |
| let description = [{ |
| The application of this transform operation is equivalent to applying the |
| operations contained in the named transform sequence with operands being |
| remapped to block arguments. The behavior of the operation when a |
| transformation in the included named sequence produces a silenceable error |
| is controlled by the `failure_propagation_mode` attribute. When set to |
| `propagate`, the failure of any nested transformation in the sequence |
| implies immediate failure of the entire sequence with a silenceable error, |
| and no further transformation is attempted. When set to `suppress`, |
| silenceable errors in nested operations are ignored and further |
| transformations are applied. Beware that even silenceable errors may leave |
| the payload IR in a state unsuitable for further transformations. It is the |
| responsibility of the user to ensure the following transformations are |
| robust enough when errors are suppressed. Definite errors are propagated |
| immediately regardless of the mode. The objects associated with the results |
| of this operation are the same as those associated with the operands of the |
| `transform.yield` in the referenced named sequence. |
| }]; |
| |
| let arguments = (ins SymbolRefAttr:$target, |
| FailurePropagationMode:$failure_propagation_mode, |
| Variadic<Transform_AnyHandleOrParamType>:$operands); |
| let results = (outs Variadic<Transform_AnyHandleOrParamType>:$results); |
| |
| let assemblyFormat = |
| "$target `failures` `(` $failure_propagation_mode `)`" |
| "`(` $operands `)` attr-dict `:` functional-type($operands, $results)"; |
| |
| let extraClassDeclaration = [{ |
| ::mlir::CallInterfaceCallable getCallableForCallee() { |
| return getTarget(); |
| } |
| |
| void setCalleeFromCallable(::mlir::CallInterfaceCallable callee) { |
| setTargetAttr(callee.get<SymbolRefAttr>()); |
| } |
| |
| ::mlir::Operation::operand_range getArgOperands() { |
| return getOperands(); |
| } |
| |
| ::mlir::MutableOperandRange getArgOperandsMutable() { |
| return getOperandsMutable(); |
| } |
| }]; |
| } |
| |
| def MatchOperationEmptyOp : Op<Transform_Dialect, "match.operation_empty", [ |
| AtMostOneOpMatcher, |
| MatchOpInterface, |
| MemoryEffectsOpInterface]> { |
| let summary = |
| "Matches if the handle is not associated to any op"; |
| let description = [{ |
| Succeeds if the handle is not associated to any op. |
| }]; |
| let arguments = (ins TransformHandleTypeInterface:$operand_handle); |
| let assemblyFormat = |
| "$operand_handle attr-dict `:` type($operand_handle)"; |
| let extraClassDeclaration = AtMostOneOpMatcher.extraDeclaration; |
| } |
| |
| def MatchOperationNameOp : TransformDialectOp<"match.operation_name", |
| [SingleOpMatcher, |
| MatchOpInterface, |
| MemoryEffectsOpInterface]> { |
| let summary = "Matches a single operation of one of the given kinds"; |
| let description = [{ |
| Succeeds if the operation associated with the operand handle has one of the |
| given operation names. Produces a silenceable failure otherwise. |
| |
| If more than one payload operation is associated with the operand handle, |
| produces a definite failure. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$operand_handle, |
| StrArrayAttr:$op_names); |
| let assemblyFormat = |
| "$operand_handle $op_names attr-dict `:` type($operand_handle)"; |
| let extraClassDeclaration = SingleOpMatcher.extraDeclaration; |
| } |
| |
| def MatchParamCmpIOp : Op<Transform_Dialect, "match.param.cmpi", [ |
| DeclareOpInterfaceMethods<TransformOpInterface>, |
| MatchOpInterface, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| SameTypeOperands]> { |
| let summary = |
| "Matches if two parameter lists are associated with the same value"; |
| let description = [{ |
| Succeeds if all of the co-indexed values associated with the given |
| parameters relate as specified by the predicate (greater than, less than, |
| equal to, or their combinations). Comparison treats all values as signed. |
| Produces a silenceable failure otherwise. |
| }]; |
| let arguments = (ins TransformParamTypeInterface:$param, |
| TransformParamTypeInterface:$reference, |
| MatchCmpIPredicateAttr:$predicate); |
| let assemblyFormat = |
| "$predicate $param `,` $reference attr-dict `:` type($param)"; |
| } |
| |
| def MergeHandlesOp : TransformDialectOp<"merge_handles", |
| [DeclareOpInterfaceMethods<TransformOpInterface, ["allowsRepeatedHandleOperands"]>, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| MatchOpInterface, SameOperandsAndResultType]> { |
| let summary = "Merges handles into one pointing to the union of payload ops"; |
| let description = [{ |
| Creates a new Transform IR handle value that points to the same Payload IR |
| operations/values/parameters as the operand handles. The Payload IR elements |
| are listed in the same order as they are in the operand handles, grouped by |
| operand handle, e.g., all Payload IR associated with the first handle comes |
| first, then all Payload IR associated with the second handle and so on. If |
| `deduplicate` is set, do not add the given Payload IR operation, value, or |
| parameter more than once to the final list regardless of it coming from the |
| same or different handles. Consumes the operands and produces a new handle. |
| }]; |
| |
| let arguments = (ins Variadic<Transform_AnyHandleOrParamType>:$handles, |
| UnitAttr:$deduplicate); |
| let results = (outs Transform_AnyHandleOrParamType:$result); |
| let assemblyFormat = "(`deduplicate` $deduplicate^)? $handles attr-dict `:` type($result)"; |
| let hasFolder = 1; |
| } |
| |
| def NamedSequenceOp : TransformDialectOp<"named_sequence", |
| [FunctionOpInterface, |
| IsolatedFromAbove, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| DeclareOpInterfaceMethods<TransformOpInterface>]> { |
| let summary = "Named transform sequence that can be included elsewhere"; |
| let description = [{ |
| Defines a named (callable, function-like) sequence of other Transform |
| dialect operations that can be included using `transform.include` as part of |
| another Transform dialect construct. This sequence is not processed |
| immediately but rather dispatched to when the inclusion is processed. The |
| arguments and results can be used to communicate a subset of mapping into |
| the named sequence. The sequence must consist of a single block and end with |
| a `transform.yield` terminator. The operands of the terminator become the |
| results of the `transform.include`. |
| |
| When dispatched to, the operations in the named sequence are executed one by |
| one, similarly to the regular unnamed sequence. The failure propagation mode |
| is specified on the `transform.include`. Different inclusions may use |
| different failure propagation modes. This transform operation always |
| succeeds by itself, but the inclusion may fail if any of the operations |
| fail. |
| |
| Named sequences can only appear at the top-level of the Transform dialect |
| nesting structure. That is, they cannot be nested in other Transform dialect |
| operations. Furthermore, one of the ancestors must have the `SymbolTable` |
| trait and have the `transform.with_named_sequence` attribute attached. |
| |
| Named sequences may include other named sequences via `transform.include`, |
| but recursion is *not* allowed. |
| }]; |
| |
| let arguments = (ins |
| SymbolNameAttr:$sym_name, |
| TypeAttrBase<"::mlir::FunctionType", |
| "function type attribute">:$function_type, |
| OptionalAttr<StrAttr>:$sym_visibility, |
| OptionalAttr<DictArrayAttr>:$arg_attrs, |
| OptionalAttr<DictArrayAttr>:$res_attrs); |
| let regions = (region MaxSizedRegion<1>:$body); |
| |
| let hasCustomAssemblyFormat = 1; |
| let hasVerifier = 1; |
| |
| let builders = [ |
| // Build a named sequence. |
| OpBuilder<(ins |
| "StringRef":$symName, |
| "Type":$rootType, |
| "TypeRange":$resultType, |
| "SequenceBodyBuilderFn":$bodyBuilder, |
| CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs, |
| CArg<"ArrayRef<DictionaryAttr>", "{}">:$argAttrs)> |
| ]; |
| |
| let extraClassDeclaration = [{ |
| ::llvm::ArrayRef<::mlir::Type> getArgumentTypes() { |
| return getFunctionType().getInputs(); |
| } |
| ::llvm::ArrayRef<::mlir::Type> getResultTypes() { |
| return getFunctionType().getResults(); |
| } |
| ::mlir::Region *getCallableRegion() { |
| return &getBody(); |
| } |
| }]; |
| } |
| |
| def SplitHandleOp : TransformDialectOp<"split_handle", |
| [FunctionalStyleTransformOpTrait, |
| DeclareOpInterfaceMethods<TransformOpInterface>, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> { |
| let summary = "Splits a handle of payload ops into handles with a single op"; |
| let description = [{ |
| Splits `handle` into one or multiple handles, as specified by the number |
| of results of this operation. `handle` should be mapped to as many payload |
| ops as there are results. Otherwise, this transform will fail produces a |
| silenceable failure by default. Each result handle is mapped to exactly one |
| payload op. The order of the payload ops is preserved, i.e., the i-th |
| payload op is mapped to the i-th result handle. |
| |
| This operation is useful for ensuring a statically known number of |
| operations are tracked by the source `handle` and to extract them into |
| individual handles that can be further manipulated in isolation. |
| |
| If there are more payload ops than results, the remaining ops are mapped to |
| the result with index `overflow_result`. If no `overflow_result` is |
| specified, the transform produces a silenceable failure. |
| |
| If there are fewer payload ops than results, the transform produces a |
| silenceable failure if `fail_on_payload_too_small` is set to "true". |
| Otherwise, it succeeds and the remaining result handles are not mapped to |
| any op. It also succeeds if `handle` is empty and |
| `pass_through_empty_handle` is set to "true", regardless of |
| `fail_on_payload_too_small`. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$handle, |
| DefaultValuedAttr<BoolAttr, "true">:$pass_through_empty_handle, |
| DefaultValuedAttr<BoolAttr, "true">:$fail_on_payload_too_small, |
| OptionalAttr<I64Attr>:$overflow_result); |
| let results = (outs Variadic<TransformHandleTypeInterface>:$results); |
| let hasVerifier = 1; |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$handle, "int64_t":$numResultHandles)> |
| ]; |
| |
| let assemblyFormat = [{ |
| $handle attr-dict `:` functional-type(operands, results) |
| }]; |
| } |
| |
| def ParamConstantOp : Op<Transform_Dialect, "param.constant", [ |
| MatchOpInterface, |
| DeclareOpInterfaceMethods<TransformOpInterface>, |
| MemoryEffectsOpInterface, |
| ParamProducerTransformOpTrait]> { |
| let summary = "Produces a new transform dialect parameter value associated " |
| "with the given attribute"; |
| let description = [{ |
| Produces a new transform dialect parameter associated with the singleton |
| list containing the given attribute. The operation itself always succeeds, |
| but the general association check may fail if the parameter type does not |
| accept the given kind of attribute as valid. |
| }]; |
| let arguments = (ins AnyAttr:$value); |
| let results = (outs TransformParamTypeInterface:$param); |
| let assemblyFormat = "$value attr-dict `->` type($param)"; |
| } |
| |
| def PrintOp : TransformDialectOp<"print", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| MatchOpInterface]> { |
| let summary = "Dump each payload op"; |
| let description = [{ |
| Prints each payload op that is associated with the `target` operand to |
| `stdout`. It also prints the `name` string attribute. If no target is |
| specified, the top-level op is dumped. |
| |
| This op is useful for printf-style debugging. |
| |
| Supported printing flag attributes: |
| * `assume_verified` -- skips verification when the unit attribute is |
| specified. This improves performace but may lead to crashes and |
| unexpected behavior when the printed payload op is invalid. |
| * `use_local_scope` -- prints in local scope when the unit attribute is |
| specified. This improves performance but may not be identical to |
| printing within the full module. |
| * `skip_regions` -- does not print regions of operations when the unit |
| attribute is specified. |
| }]; |
| |
| let arguments = (ins Optional<TransformHandleTypeInterface>:$target, |
| OptionalAttr<StrAttr>:$name, |
| OptionalAttr<UnitAttr>:$assume_verified, |
| OptionalAttr<UnitAttr>:$use_local_scope, |
| OptionalAttr<UnitAttr>:$skip_regions); |
| let results = (outs); |
| |
| let builders = [ |
| OpBuilder<(ins CArg<"StringRef", "StringRef()">:$name)>, |
| OpBuilder<(ins "Value":$target, CArg<"StringRef", "StringRef()">:$name)> |
| ]; |
| |
| let assemblyFormat = "$target attr-dict (`:` type($target)^)?"; |
| } |
| |
| def ReplicateOp : TransformDialectOp<"replicate", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| AllTypesMatch<["handles", "replicated"]>]> { |
| let summary = "Lists payload ops multiple times in the new handle"; |
| let description = [{ |
| Produces a new handle associated with a list of payload IR ops that is |
| computed by repeating the list of payload IR ops associated with the |
| operand handle as many times as the "pattern" handle has associated |
| operations. For example, if pattern is associated with [op1, op2] and the |
| operand handle is associated with [op3, op4, op5], the resulting handle |
| will be associated with [op3, op4, op5, op3, op4, op5]. |
| |
| This transformation is useful to "align" the sizes of payload IR lists |
| before a transformation that expects, e.g., identically-sized lists. For |
| example, a transformation may be parameterized by same notional per-target |
| size computed at runtime and supplied as another handle, the replication |
| allows this size to be computed only once and used for every target instead |
| of replicating the computation itself. |
| |
| Note that it is undesirable to pass a handle with duplicate operations to |
| an operation that consumes the handle. Handle consumption often indicates |
| that the associated payload IR ops are destroyed, so having the same op |
| listed more than once will lead to double-free. Single-operand |
| MergeHandlesOp may be used to deduplicate the associated list of payload IR |
| ops when necessary. Furthermore, a combination of ReplicateOp and |
| MergeHandlesOp can be used to construct arbitrary lists with repetitions. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$pattern, |
| Variadic<Transform_AnyHandleOrParamType>:$handles); |
| let results = (outs Variadic<Transform_AnyHandleOrParamType>:$replicated); |
| let assemblyFormat = "`num` `(` $pattern `)` $handles attr-dict `:` " |
| "type($pattern) `,` type($handles)"; |
| } |
| |
| def SelectOp : TransformDialectOp<"select", |
| [DeclareOpInterfaceMethods<TransformOpInterface>, |
| NavigationTransformOpTrait, MemoryEffectsOpInterface]> { |
| let summary = "Select payload ops by name"; |
| let description = [{ |
| The handle defined by this Transform op corresponds to all operations among |
| `target` that have the specified properties. Currently the following |
| properties are supported: |
| |
| - `op_name`: The op must have the specified name. |
| |
| The result payload ops are in the same relative order as the targeted ops. |
| This transform op reads the `target` handle and produces the `result` |
| handle. It reads the payload, but does not modify it. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target, |
| StrAttr:$op_name); |
| let results = (outs TransformHandleTypeInterface:$result); |
| let assemblyFormat = [{ |
| $op_name `in` $target attr-dict `:` functional-type(operands, results) |
| }]; |
| } |
| |
| def SequenceOp : TransformDialectOp<"sequence", |
| [DeclareOpInterfaceMethods<RegionBranchOpInterface, |
| ["getEntrySuccessorOperands", "getSuccessorRegions", |
| "getRegionInvocationBounds"]>, |
| MatchOpInterface, |
| DeclareOpInterfaceMethods<TransformOpInterface>, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| OpAsmOpInterface, PossibleTopLevelTransformOpTrait, |
| SingleBlockImplicitTerminator<"::mlir::transform::YieldOp">, |
| AttrSizedOperandSegments]> { |
| let summary = "Contains a sequence of other transform ops to apply"; |
| let description = [{ |
| The transformations indicated by the sequence are applied in order of their |
| appearance. Each value produced by a transformation within the sequence |
| corresponds to a group of operations or values in the payload IR, or to a |
| group of parameters, depending on the type of the value. The behavior of the |
| operation when a nested transformation produces a silenceable error is |
| controlled by the `failure_propagation_mode` attribute. When set to |
| `propagate`, the failure of any nested transformation in the sequence |
| implies immediate failure of the entire sequence with a silenceable error, |
| and no further transformation is attempted. When set to `suppress`, |
| silenceable errors in nested operations are ignored and further |
| transformations are applied. Beware that even silenceable errors may leave |
| the payload IR in a state unsuitable for further transformations. It is the |
| responsibility of the caller to ensure the following transformations are |
| robust enough when errors are suppressed. Definite errors reported by nested |
| transformations abort the sequence regardless of the propagation mode. The |
| set of modes may be extended in the future, e.g., to collect silenceable |
| errors and report them after attempting all transformations in the sequence. |
| |
| The entry block of this operation has a single argument that maps to either |
| the operand if provided or the top-level container operation of the payload |
| IR, typically the root operation of the pass interpreting the transform |
| dialect. Operand omission is only allowed for sequences not contained in |
| another sequence. |
| |
| The type of the block argument must match the type of the operand. If the |
| sequence is a top-level transform (without an operand), it can be used for |
| matching operations if the specified type within the top-level container |
| payload IR (including the container op itself). E.g.: |
| |
| ```mlir |
| transform.sequence failures(propagate) { |
| ^bb1(%arg1: !transform.any_op): |
| // %arg1 is mapped to the top-level container of the payload IR, which is |
| // typically a module |
| } |
| |
| transform.sequence failures(propagate) { |
| ^bb1(%arg1: !transform.op<"func.func>"): |
| // %arg1 is mapped to all "func.func" ops within and including the |
| // top-level container of the payload IR. Nested operations that have the |
| // specified op type are not included. |
| } |
| ``` |
| |
| The body of the sequence terminates with an implicit or explicit |
| `transform.yield` op. The operands of the terminator are returned as the |
| results of the sequence op. |
| }]; |
| |
| let arguments = (ins FailurePropagationMode:$failure_propagation_mode, |
| Optional<TransformHandleTypeInterface>:$root, |
| Variadic<Transform_AnyHandleOrParamType>:$extra_bindings); |
| let results = (outs Variadic<TransformHandleTypeInterface>:$results); |
| let regions = (region SizedRegion<1>:$body); |
| |
| let assemblyFormat = |
| "custom<SequenceOpOperands>($root, type($root), $extra_bindings, type($extra_bindings))" |
| " (`->` type($results)^)? `failures` `(` " |
| "$failure_propagation_mode `)` attr-dict-with-keyword regions"; |
| |
| let builders = [ |
| // Build a sequence with a root. |
| OpBuilder<(ins |
| "::mlir::TypeRange":$resultTypes, |
| "::mlir::transform::FailurePropagationMode":$failure_propagation_mode, |
| "::mlir::Value":$root, "SequenceBodyBuilderFn":$bodyBuilder)>, |
| |
| // Build a sequence with a root and additional arguments. |
| OpBuilder<(ins |
| "::mlir::TypeRange":$resultTypes, |
| "::mlir::transform::FailurePropagationMode":$failure_propagation_mode, |
| "::mlir::Value":$root, "::mlir::ValueRange":$extraBindings, |
| "SequenceBodyBuilderArgsFn":$bodyBuilder)>, |
| |
| // Build a top-level sequence (no root). |
| OpBuilder<(ins |
| "::mlir::TypeRange":$resultTypes, |
| "::mlir::transform::FailurePropagationMode":$failure_propagation_mode, |
| "::mlir::Type":$bbArgType, "SequenceBodyBuilderFn":$bodyBuilder)>, |
| |
| // Build a top-level sequence (no root) with extra arguments. |
| OpBuilder<(ins |
| "::mlir::TypeRange":$resultTypes, |
| "::mlir::transform::FailurePropagationMode":$failure_propagation_mode, |
| "::mlir::Type":$bbArgType, "::mlir::TypeRange":$extraBindingTypes, |
| "SequenceBodyBuilderArgsFn":$bodyBuilder)> |
| ]; |
| |
| let extraClassDeclaration = [{ |
| /// Allow the dialect prefix to be omitted. |
| static StringRef getDefaultDialect() { return "transform"; } |
| }]; |
| |
| let hasVerifier = 1; |
| } |
| |
| def VerifyOp : TransformDialectOp<"verify", |
| [TransformOpInterface, TransformEachOpTrait, |
| DeclareOpInterfaceMethods<MemoryEffectsOpInterface>, |
| ReportTrackingListenerFailuresOpTrait]> { |
| let summary = "Verifies the targeted ops"; |
| let description = [{ |
| This transform verifies the targeted ops. If at least one op fails to |
| verify, the transform produces a definite failure. |
| |
| Note: This op was designed for debugging purposes and should be used like an |
| assertion. It is intentional that this op produces a definite failure and |
| not a silenceable one. Correctness of the program should not depend on this |
| op. |
| |
| This transform reads the target handle. |
| }]; |
| |
| let arguments = (ins TransformHandleTypeInterface:$target); |
| let results = (outs); |
| let assemblyFormat = "$target attr-dict `:` type($target)"; |
| |
| let extraClassDeclaration = [{ |
| ::mlir::DiagnosedSilenceableFailure applyToOne( |
| ::mlir::transform::TransformRewriter &rewriter, |
| ::mlir::Operation *target, |
| ::mlir::transform::ApplyToEachResultList &results, |
| ::mlir::transform::TransformState &state); |
| }]; |
| } |
| |
| def YieldOp : TransformDialectOp<"yield", |
| [Terminator, DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> { |
| let summary = "Yields operation handles from a transform IR region"; |
| let description = [{ |
| This terminator operation yields operation handles from regions of the |
| transform IR ops back to the containing op. It is not itself associated with |
| any transformation on the payload IR and is used for flow purposes only. |
| }]; |
| |
| let arguments = (ins |
| Arg<Variadic<Transform_AnyHandleOrParamType>, |
| "Transform values yielded back to the parent" |
| >:$operands); |
| let assemblyFormat = "operands attr-dict (`:` type($operands)^)?"; |
| |
| let builders = [ |
| OpBuilder<(ins), [{ |
| return build($_builder, $_state, ::mlir::ValueRange()); |
| }]> |
| ]; |
| } |
| |
| #endif // MLIR_DIALECT_TRANSFORM_IR_TRANSFORMOPS |