1//===- TransformOps.td - Transform dialect operations ------*- tablegen -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef MLIR_DIALECT_TRANSFORM_IR_TRANSFORMOPS
10#define MLIR_DIALECT_TRANSFORM_IR_TRANSFORMOPS
11
12include "mlir/Interfaces/ControlFlowInterfaces.td"
13include "mlir/IR/OpAsmInterface.td"
14include "mlir/IR/SymbolInterfaces.td"
15include "mlir/Dialect/PDL/IR/PDLTypes.td"
16include "mlir/Dialect/Transform/IR/TransformDialect.td"
17include "mlir/Dialect/Transform/IR/TransformEffects.td"
18include "mlir/Dialect/Transform/IR/TransformInterfaces.td"
19
20def AlternativesOp : TransformDialectOp<"alternatives",
21    [DeclareOpInterfaceMethods<RegionBranchOpInterface,
22        ["getSuccessorEntryOperands", "getSuccessorRegions",
23         "getRegionInvocationBounds"]>,
24     DeclareOpInterfaceMethods<TransformOpInterface>,
25     FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
26     IsolatedFromAbove, PossibleTopLevelTransformOpTrait,
27     SingleBlockImplicitTerminator<"::mlir::transform::YieldOp">]> {
28  let summary = "Attempts sequences of transforms until one succeeds";
29  let description = [{
30    This op may have an arbitrary number of regions, each of which represents a
31    sequence of transform operations to be applied to the same payload IR. The
32    regions are visited in order of appearance, and transforms in them are
33    applied in their respective order of appearance. If one of these transforms
34    fails to apply, the remaining ops in the same region are skipped an the next
35    region is attempted. If all transformations in a region succeed, the
36    remaining regions are skipped and the entire "alternatives" transformation
37    succeeds. If all regions contained a failing transformation, the entire
38    "alternatives" transformation fails.
39
40    It is up to the nested operations to define which errors are "recoverable"
41    (or "silenceable") and allow another alternatives to be attempted, and which
42    errors should be propagated without attempting the other alternatives.
43
44    The single operand of this operation is the scope in which the alternative
45    transformation sequences are attempted, that is, an operation in the payload
46    IR that contains all the other operations that may be modified by the
47    transformations. The scope operation must be isolated from above. There is
48    no check that the transforms are indeed scoped as their "apply" methods can
49    be arbitrarily complex. Therefore it is the responsibility of the user to
50    ensure that the transforms are scoped correctly, or to produce an
51    irrecoverable error and thus abort the execution without attempting the
52    remaining alternatives. Note that the payload IR outside of the given scope
53    is not necessarily in the valid state, or even accessible to the
54    transformation.
55
56    The changes to the IR within the scope performed by transforms in the failed
57    alternative region are reverted before attempting the next region.
58    Practically, this is achieved by cloning the scope. Therefore it is advised
59    to limit the scope as much as possible and place the most likely
60    alternatives early in the region list. The operation is also isolated from
61    above and requires rediscovering the operations within the given scope to
62    avoid additional handle invalidation. The latter restriction may be lifted
63    in the future.
64
65    Each of the regions may yield transform IR handles. The handles of the first
66    successful alternative region are returned as the results of the
67    "alternatives" op. Therefore, each alternative region must yield the same
68    number of results, which should also match the number and the types of the
69    "alternatives" op results.
70
71    Remark: this op allows one to implement a simple "try" construct as follows:
72
73    ```mlir
74    %result = transform.alternatives %scope {
75    ^bb0(%arg0: !pdl.operation):
76      // Try a fallible transformation.
77      %0 = transform.fallible %arg0 // ...
78      // If succeeded, yield the the result of the transformation.
79      transform.yield %0 : !pdl.operation
80    }, {
81    ^bb0(%arg0: !pdl.operation):
82      // Otherwise, the second alternative is tried and it always succeeds by
83      // returning the original handle.
84      transform.yield %arg0 : !pdl.operation
85    }
86    ```
87  }];
88
89  let arguments = (ins Optional<PDL_Operation>:$scope);
90  let results = (outs Variadic<AnyType>:$results);
91  let regions = (region VariadicRegion<SizedRegion<1>>:$alternatives);
92
93  let assemblyFormat =
94    "($scope^)? (`->` type($results)^)? attr-dict-with-keyword regions";
95  let hasVerifier = 1;
96}
97
98def ForeachOp : TransformDialectOp<"foreach",
99    [DeclareOpInterfaceMethods<TransformOpInterface>,
100     DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
101     DeclareOpInterfaceMethods<RegionBranchOpInterface, [
102         "getSuccessorRegions", "getSuccessorEntryOperands"]>,
103     SingleBlockImplicitTerminator<"::mlir::transform::YieldOp">
104    ]> {
105  let summary = "Executes the body for each payload op";
106  let description = [{
107    This op has exactly one region with exactly one block ("body"). The body is
108    executed for each payload op that is associated to the target operand in an
109    unbatched fashion. I.e., the block argument ("iteration variable") is always
110    mapped to exactly one payload op.
111
112    This op always reads the target handle. Furthermore, it consumes the handle
113    if there is a transform op in the body that consumes the iteration variable.
114    This op does not return anything.
115
116    The transformations inside the body are applied in order of their
117    appearance. During application, if any transformation in the sequence fails,
118    the entire sequence fails immediately leaving the payload IR in potentially
119    invalid state, i.e., this operation offers no transformation rollback
120    capabilities.
121  }];
122
123  let arguments = (ins PDL_Operation:$target);
124  let results = (outs);
125  let regions = (region SizedRegion<1>:$body);
126  let assemblyFormat = "$target $body attr-dict";
127
128  let extraClassDeclaration = [{
129    /// Allow the dialect prefix to be omitted.
130    static StringRef getDefaultDialect() { return "transform"; }
131
132    BlockArgument getIterationVariable() {
133      return getBody().front().getArgument(0);
134    }
135  }];
136}
137
138def GetClosestIsolatedParentOp : TransformDialectOp<"get_closest_isolated_parent",
139    [DeclareOpInterfaceMethods<TransformOpInterface>,
140     NavigationTransformOpTrait, MemoryEffectsOpInterface]> {
141  let summary = "Gets handles to the closest isolated-from-above parents";
142  let description = [{
143    The handles defined by this Transform op correspond to the closest isolated
144    from above ancestor of the Payload IR operations associated with its
145    operand. If any of the given Payload IR ops has no such parent (unlikely as
146    there usually is a top-level ModuleOp), the transformation is considered to
147    have failed.
148
149    Ancestor ops follow the same order as the ops associated with the
150    operand, except for potential duplicates (multiple Payload IR ops associated
151    with the operand have the same parent) for which the ancestor will only be
152    listed once for the first time it occurs. For example, given the list
153    "(childof(A), childof(B), childof(B), childof(A), childof(B))", the
154    resulting list will be just "(A, B)". Note that no other semantic ordering
155    is applied, e.g., "B" may itself be a parent of "A". This may have an impact
156    on the further transformation applied to the handle produced here.
157  }];
158
159  let arguments = (ins PDL_Operation:$target);
160  let results = (outs PDL_Operation:$parent);
161  let assemblyFormat = "$target attr-dict";
162}
163
164def MergeHandlesOp : TransformDialectOp<"merge_handles",
165    [DeclareOpInterfaceMethods<TransformOpInterface>,
166     DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
167  let summary = "Merges handles into one pointing to the union of payload ops";
168  let description = [{
169    Creates a new Transform IR handle value that points to the same Payload IR
170    operations as the operand handles. The Payload IR operations are listed
171    in the same order as they are in the operand handles, grouped by operand
172    handle, e.g., all Payload IR operations associated with the first handle
173    come first, then all Payload IR operations associated with the second handle
174    and so on. If `deduplicate` is set, do not add the given Payload IR
175    operation more than once to the final list regardless of it coming from the
176    same or different handles. Consumes the operands and produces a new handle.
177  }];
178
179  let arguments = (ins Variadic<PDL_Operation>:$handles,
180                       UnitAttr:$deduplicate);
181  let results = (outs PDL_Operation:$result);
182  let assemblyFormat = "($deduplicate^)? $handles attr-dict";
183  let hasFolder = 1;
184}
185
186def PDLMatchOp : TransformDialectOp<"pdl_match",
187    [DeclareOpInterfaceMethods<TransformOpInterface>]> {
188  let summary = "Finds ops that match the named PDL pattern";
189  let description = [{
190    Find Payload IR ops nested within the Payload IR op associated with the
191    operand that match the PDL pattern identified by its name. The pattern is
192    expected to be defined in the closest surrounding `WithPDLPatternsOp`.
193
194    Produces a Transform IR value associated with the list of Payload IR ops
195    that matched the pattern. The order of results in the list is that of the
196    Operation::walk, clients are advised not to rely on a specific order though.
197    If the operand is associated with multiple Payload IR ops, finds matching
198    ops nested within each of those and produces a single list containing all
199    of the matched ops.
200
201    The transformation is considered successful regardless of whether some
202    Payload IR ops actually matched the pattern and only fails if the pattern
203    could not be looked up or compiled.
204  }];
205
206  let arguments = (ins
207    Arg<PDL_Operation, "Payload IR scope to match within",
208        [TransformMappingRead, PayloadIRRead]>:$root,
209    SymbolRefAttr:$pattern_name);
210  let results = (outs
211    Res<PDL_Operation, "Handle to the matched Payload IR ops",
212        [TransformMappingAlloc, TransformMappingWrite]>:$matched);
213
214  let assemblyFormat = "$pattern_name `in` $root attr-dict";
215}
216
217def ReplicateOp : TransformDialectOp<"replicate",
218    [DeclareOpInterfaceMethods<TransformOpInterface>,
219     DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
220  let summary = "Lists payload ops multiple times in the new handle";
221  let description = [{
222    Produces a new handle associated with a list of payload IR ops that is
223    computed by repeating the list of payload IR ops associated with the
224    operand handle as many times as the "pattern" handle has associated
225    operations. For example, if pattern is associated with [op1, op2] and the
226    operand handle is associated with [op3, op4, op5], the resulting handle
227    will be associated with [op3, op4, op5, op3, op4, op5].
228
229    This transformation is useful to "align" the sizes of payload IR lists
230    before a transformation that expects, e.g., identically-sized lists. For
231    example, a transformation may be parameterized by same notional per-target
232    size computed at runtime and supplied as another handle, the replication
233    allows this size to be computed only once and used for every target instead
234    of replicating the computation itself.
235
236    Note that it is undesirable to pass a handle with duplicate operations to
237    an operation that consumes the handle. Handle consumption often indicates
238    that the associated payload IR ops are destroyed, so having the same op
239    listed more than once will lead to double-free. Single-operand
240    MergeHandlesOp may be used to deduplicate the associated list of payload IR
241    ops when necessary. Furthermore, a combination of ReplicateOp and
242    MergeHandlesOp can be used to construct arbitrary lists with repetitions.
243  }];
244
245  let arguments = (ins PDL_Operation:$pattern,
246                       Variadic<PDL_Operation>:$handles);
247  let results = (outs Variadic<PDL_Operation>:$replicated);
248  let assemblyFormat =
249    "`num` `(` $pattern `)` $handles "
250    "custom<PDLOpTypedResults>(type($replicated), ref($handles)) attr-dict";
251}
252
253def SequenceOp : TransformDialectOp<"sequence",
254    [DeclareOpInterfaceMethods<RegionBranchOpInterface,
255        ["getSuccessorEntryOperands", "getSuccessorRegions",
256         "getRegionInvocationBounds"]>,
257     DeclareOpInterfaceMethods<TransformOpInterface>,
258     DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
259     OpAsmOpInterface, PossibleTopLevelTransformOpTrait,
260     SingleBlockImplicitTerminator<"::mlir::transform::YieldOp">]> {
261  let summary = "Contains a sequence of other transform ops to apply";
262  let description = [{
263    The transformations indicated by the sequence are applied in order of their
264    appearance. Each value produced by a transformation within the sequence
265    corresponds to an operation or a group of operations in the payload IR.
266    During application, if any transformation in the sequence fails, the entire
267    sequence fails immediately leaving the payload IR in potentially invalid
268    state, i.e., this operation offers no transformation rollback capabilities.
269
270    The entry block of this operation has a single argument that maps to either
271    the operand if provided or the top-level container operation of the payload
272    IR, typically the root operation of the pass interpreting the transform
273    dialect. Operand omission is only allowed for sequences not contained in
274    another sequence.
275  }];
276
277  let arguments = (ins Optional<PDL_Operation>:$root);
278  let results = (outs Variadic<AnyType>:$results);
279  let regions = (region SizedRegion<1>:$body);
280
281  let assemblyFormat =
282    "($root^)? attr-dict-with-keyword regions (`:` type($results)^)?";
283
284  let extraClassDeclaration = [{
285    /// Allow the dialect prefix to be omitted.
286    static StringRef getDefaultDialect() { return "transform"; }
287  }];
288
289  let hasVerifier = 1;
290}
291
292def WithPDLPatternsOp : TransformDialectOp<"with_pdl_patterns",
293    [DeclareOpInterfaceMethods<TransformOpInterface>, NoTerminator,
294     OpAsmOpInterface, PossibleTopLevelTransformOpTrait, RecursiveSideEffects,
295     SymbolTable]> {
296  let summary = "Contains PDL patterns available for use in transforms";
297  let description = [{
298    This op contains a set of named PDL patterns that are available for the
299    Transform dialect operations to be used for pattern matching. For example,
300    PDLMatchOp can be used to produce a Transform IR value associated with all
301    Payload IR operations that match the pattern as follows:
302
303    ```mlir
304    transform.with_pdl_patterns {
305    ^bb0(%arg0: !pdl.operation):
306      pdl.pattern @my_pattern : benefit(1) {
307        %0 = pdl.operation //...
308        // Regular PDL goes here.
309        pdl.rewrite %0 with "transform.dialect"
310      }
311
312      sequence %arg0 {
313      ^bb0(%arg1: !pdl.operation):
314        %1 = pdl_match @my_pattern in %arg1
315        // Use %1 as handle
316      }
317    }
318    ```
319
320    Note that the pattern is expected to finish with a `pdl.rewrite` terminator
321    that points to the custom rewriter named "transform.dialect". The rewriter
322    actually does nothing, but the transform application will keep track of the
323    operations that matched the pattern.
324
325    This op is expected to contain `pdl.pattern` operations and exactly one
326    another Transform dialect operation that gets executed with all patterns
327    available. This op is a possible top-level Transform IR op, the argument of
328    its entry block corresponds to either the root op of the payload IR or the
329    ops associated with its operand when provided.
330  }];
331
332  let arguments = (ins
333    Arg<Optional<PDL_Operation>, "Root operation of the Payload IR",
334        [TransformMappingRead]>:$root);
335  let regions = (region SizedRegion<1>:$body);
336  let assemblyFormat = "($root^)? attr-dict-with-keyword regions";
337
338  let hasVerifier = 1;
339
340  let extraClassDeclaration = [{
341    /// Allow the dialect prefix to be omitted.
342    static StringRef getDefaultDialect() { return "transform"; }
343  }];
344}
345
346def YieldOp : TransformDialectOp<"yield", [Terminator]> {
347  let summary = "Yields operation handles from a transform IR region";
348  let description = [{
349    This terminator operation yields operation handles from regions of the
350    transform IR ops back to the containing op. It is not itself associated with
351    any transformation on the payload IR and is used for flow purposes only.
352  }];
353
354  let arguments = (ins
355    Arg<Variadic<AnyType>, "Operation handles yielded back to the parent",
356        [TransformMappingRead]>:$operands);
357  let assemblyFormat = "operands attr-dict (`:` type($operands)^)?";
358
359  let builders = [
360    OpBuilder<(ins), [{
361      return build($_builder, $_state, ::mlir::ValueRange());
362    }]>
363  ];
364}
365
366#endif // MLIR_DIALECT_TRANSFORM_IR_TRANSFORMOPS
367