xref: /llvm-project/mlir/include/mlir/Dialect/SCF/TransformOps/SCFTransformOps.td (revision a9c417c28a25c153aa0fdbe2eb5453a93820a3b1)
1//===- SCFTransformOps.td - SCF (loop) transformation ops --*- 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 SCF_TRANSFORM_OPS
10#define SCF_TRANSFORM_OPS
11
12include "mlir/Dialect/Transform/IR/TransformDialect.td"
13include "mlir/Dialect/Transform/Interfaces/TransformInterfaces.td"
14include "mlir/Dialect/Transform/IR/TransformTypes.td"
15include "mlir/Interfaces/SideEffectInterfaces.td"
16include "mlir/IR/OpBase.td"
17
18def ApplyForLoopCanonicalizationPatternsOp : Op<Transform_Dialect,
19    "apply_patterns.scf.for_loop_canonicalization",
20    [DeclareOpInterfaceMethods<PatternDescriptorOpInterface>]> {
21  let description = [{
22    Collects patterns for canonicalizing operations inside SCF loop bodies.
23    At the moment, only affine.min/max computations with iteration variables,
24    loop bounds and loop steps are canonicalized.
25  }];
26
27  let assemblyFormat = "attr-dict";
28}
29
30def ApplySCFStructuralConversionPatternsOp : Op<Transform_Dialect,
31    "apply_conversion_patterns.scf.structural_conversions",
32    [DeclareOpInterfaceMethods<ConversionPatternDescriptorOpInterface,
33      ["populateConversionTargetRules"]>]> {
34  let description = [{
35    Collects patterns for performing structural conversions of SCF operations.
36  }];
37
38  let assemblyFormat = "attr-dict";
39}
40
41def ApplySCFToControlFlowPatternsOp : Op<Transform_Dialect,
42    "apply_conversion_patterns.scf.scf_to_control_flow",
43    [DeclareOpInterfaceMethods<ConversionPatternDescriptorOpInterface>]> {
44  let description = [{
45    Collects patterns that lower structured control flow ops to unstructured
46    control flow.
47  }];
48
49  let assemblyFormat = "attr-dict";
50}
51
52def Transform_ScfForOp : Transform_ConcreteOpType<"scf.for">;
53
54def ForallToForOp : Op<Transform_Dialect, "loop.forall_to_for",
55    [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
56     DeclareOpInterfaceMethods<TransformOpInterface>]> {
57  let summary = "Converts scf.forall into a nest of scf.for operations";
58  let description = [{
59    Converts the `scf.forall` operation pointed to by the given handle into a
60    set of nested `scf.for` operations. Each new operation corresponds to one
61    induction variable of the original "multifor" loop.
62
63    The operand handle must be associated with exactly one payload operation.
64
65    Loops with shared outputs are currently not supported.
66
67    #### Return Modes
68
69    Consumes the operand handle. Produces a silenceable failure if the operand
70    is not associated with a single `scf.forall` payload operation.
71    Returns as many handles as the given `forall` op has induction variables
72    that are associated with the generated `scf.for` loops.
73    Produces a silenceable failure if another number of resulting handles is
74    requested.
75  }];
76  let arguments = (ins TransformHandleTypeInterface:$target);
77  let results = (outs Variadic<TransformHandleTypeInterface>:$transformed);
78
79  let assemblyFormat = "$target attr-dict `:` functional-type(operands, results)";
80}
81
82def ForallToParallelOp : Op<Transform_Dialect, "loop.forall_to_parallel",
83    [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
84     DeclareOpInterfaceMethods<TransformOpInterface>]> {
85  let summary = "Converts scf.forall into a nest of scf.for operations";
86  let description = [{
87    Converts the `scf.forall` operation pointed to by the given handle into an
88    `scf.parallel` operation.
89
90    The operand handle must be associated with exactly one payload operation.
91
92    Loops with outputs are not supported.
93
94    #### Return Modes
95
96    Consumes the operand handle. Produces a silenceable failure if the operand
97    is not associated with a single `scf.forall` payload operation.
98    Returns a handle to the new `scf.parallel` operation.
99    Produces a silenceable failure if another number of resulting handles is
100    requested.
101  }];
102  let arguments = (ins TransformHandleTypeInterface:$target);
103  let results = (outs Variadic<TransformHandleTypeInterface>:$transformed);
104
105  let assemblyFormat = "$target attr-dict `:` functional-type(operands, results)";
106}
107
108def LoopOutlineOp : Op<Transform_Dialect, "loop.outline",
109    [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
110     DeclareOpInterfaceMethods<TransformOpInterface>]> {
111  let summary = "Outlines a loop into a named function";
112  let description = [{
113    Moves the loop into a separate function with the specified name and replaces
114    the loop in the Payload IR with a call to that function. Takes care of
115    forwarding values that are used in the loop as function arguments. If the
116    operand is associated with more than one loop, each loop will be outlined
117    into a separate function. The provided name is used as a _base_ for forming
118    actual function names following `SymbolTable` auto-renaming scheme to avoid
119    duplicate symbols. Expects that all ops in the Payload IR have a
120    `SymbolTable` ancestor (typically true because of the top-level module).
121
122    #### Return Modes
123
124    Returns a handle to the list of outlined functions and a handle to the
125    corresponding function call operations in the same order as the operand
126    handle.
127
128    Produces a definite failure if outlining failed for any of the targets.
129  }];
130
131  // Note that despite the name of the transform operation and related utility
132  // functions, the actual implementation does not require the operation to be
133  // a loop.
134  let arguments = (ins TransformHandleTypeInterface:$target,
135                   StrAttr:$func_name);
136  let results = (outs TransformHandleTypeInterface:$function,
137                      TransformHandleTypeInterface:$call);
138
139  let assemblyFormat =
140    "$target attr-dict `:` functional-type(operands, results)";
141}
142
143def LoopPeelOp : Op<Transform_Dialect, "loop.peel",
144    [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
145     TransformOpInterface, TransformEachOpTrait]> {
146  let summary = "Peels the first or last iteration of the loop";
147  let description = [{
148     Rewrite the given loop with a main loop and a partial (first or last) loop.
149     When the `peelFront` option is set to true, the first iteration is peeled off.
150     Otherwise, updates the given loop so that its step evenly divides its range and puts
151     the remaining iteration into a separate loop or a conditional.
152
153     In the absence of sufficient static information, this op may peel a loop,
154     even if the step always divides the range evenly at runtime.
155
156     #### Return modes
157
158     This operation ignores non-scf::ForOp ops and drops them in the return.
159     The op returns two loops, the peeled loop which has trip count divisible
160     by the step, and the remainder loop.
161
162     When `peelFront` is true, the first result (remainder loop) executes all
163     but the first iteration of the target loop. The second result (peeled
164     loop) corresponds to the first iteration of the loop which can be
165     canonicalized away in the following optimizations.
166
167     When `peelFront` is false, the first result (peeled loop) is the portion
168     of the target loop with the highest upper bound that is divisible by the
169     step. The second result (remainder loop) contains the remaining iterations.
170
171     Note that even though the Payload IR modification may be performed
172     in-place, this operation consumes the operand handle and produces a new one.
173
174     #### Return Modes
175
176     Produces a definite failure if peeling fails.
177  }];
178
179  let arguments =
180      (ins Transform_ScfForOp:$target,
181           DefaultValuedAttr<BoolAttr, "false">:$peel_front,
182           DefaultValuedAttr<BoolAttr, "false">:$fail_if_already_divisible);
183  let results = (outs TransformHandleTypeInterface:$peeled_loop,
184                      TransformHandleTypeInterface:$remainder_loop);
185
186  let assemblyFormat =
187    "$target attr-dict `:` functional-type(operands, results)";
188
189  let extraClassDeclaration = [{
190    ::mlir::DiagnosedSilenceableFailure applyToOne(
191        ::mlir::transform::TransformRewriter &rewriter,
192        ::mlir::scf::ForOp target,
193        ::mlir::transform::ApplyToEachResultList &results,
194        ::mlir::transform::TransformState &state);
195  }];
196}
197
198def LoopPipelineOp : Op<Transform_Dialect, "loop.pipeline",
199    [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
200     TransformOpInterface, TransformEachOpTrait]> {
201  let summary = "Applies software pipelining to the loop";
202  let description = [{
203    Transforms the given loops one by one to achieve software pipelining for
204    each of them. That is, performs some amount of reads from memory before the
205    loop rather than inside the loop, the same amount of writes into memory
206    after the loop, and updates each iteration to read the data for a following
207    iteration rather than the current one.
208
209    The amount is specified by the attributes.
210
211    The values read and about to be stored are transferred as loop iteration
212    arguments. Currently supports memref and vector transfer operations as
213    memory reads/writes.
214
215    #### Return modes
216
217    This operation ignores non-scf::For ops and drops them in the return.
218    If all the operations referred to by the `target` PDLOperation pipeline
219    properly, the transform succeeds. Otherwise the transform produces a
220    silenceable failure.  The return handle points to only the subset of
221    successfully produced pipelined loops, which can be empty.
222  }];
223
224  let arguments = (ins Transform_ScfForOp:$target,
225                   DefaultValuedAttr<I64Attr, "1">:$iteration_interval,
226                   DefaultValuedAttr<I64Attr, "10">:$read_latency);
227  let results = (outs TransformHandleTypeInterface:$transformed);
228
229  let assemblyFormat =
230    "$target attr-dict `:` functional-type(operands, results)";
231
232  let extraClassDeclaration = [{
233    ::mlir::DiagnosedSilenceableFailure applyToOne(
234        ::mlir::transform::TransformRewriter &rewriter,
235        ::mlir::scf::ForOp target,
236        ::mlir::transform::ApplyToEachResultList &results,
237        ::mlir::transform::TransformState &state);
238  }];
239}
240
241def LoopPromoteIfOneIterationOp : Op<Transform_Dialect,
242    "loop.promote_if_one_iteration", [
243        DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
244        TransformOpInterface, TransformEachOpTrait]> {
245  let summary = "Promote loop if it has one iteration";
246  let description = [{
247    Promotes the given target loop op if it has a single iteration. I.e., the
248    loop op is removed and only the body remains.
249
250    #### Return modes
251
252    This transform fails if the target is mapped to ops that are loops. Ops are
253    considered loops if they implement the `LoopLikeOpInterface`. Otherwise,
254    this transform always succeeds. The transform consumes the target handle and
255    modifies the payload.
256  }];
257
258  let arguments = (ins TransformHandleTypeInterface:$target);
259  let results = (outs);
260  let assemblyFormat = "$target attr-dict `:` type($target)";
261
262  let extraClassDeclaration = [{
263    ::mlir::DiagnosedSilenceableFailure applyToOne(
264        ::mlir::transform::TransformRewriter &rewriter,
265        ::mlir::LoopLikeOpInterface target,
266        ::mlir::transform::ApplyToEachResultList &results,
267        ::mlir::transform::TransformState &state);
268  }];
269}
270
271def LoopUnrollOp : Op<Transform_Dialect, "loop.unroll",
272    [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
273     TransformOpInterface, TransformEachOpTrait]> {
274  let summary = "Unrolls the given loop with the given unroll factor";
275  let description = [{
276    Unrolls each loop associated with the given handle to have up to the given
277    number of loop body copies per iteration. If the unroll factor is larger
278    than the loop trip count, the latter is used as the unroll factor instead.
279
280    #### Return modes
281
282    This operation ignores non-`scf.for`, non-`affine.for` ops and drops them
283    in the return. If all the operations referred to by the `target` operand
284    unroll properly, the transform succeeds. Otherwise the transform produces a
285    silenceable failure.
286
287    Does not return handles as the operation may result in the loop being
288    removed after a full unrolling.
289  }];
290
291  let arguments = (ins TransformHandleTypeInterface:$target,
292                       ConfinedAttr<I64Attr, [IntPositive]>:$factor);
293
294  let assemblyFormat = "$target attr-dict `:` type($target)";
295
296  let extraClassDeclaration = [{
297    ::mlir::DiagnosedSilenceableFailure applyToOne(
298        ::mlir::transform::TransformRewriter &rewriter,
299        ::mlir::Operation *target,
300        ::mlir::transform::ApplyToEachResultList &results,
301        ::mlir::transform::TransformState &state);
302  }];
303}
304
305def LoopUnrollAndJamOp : Op<Transform_Dialect, "loop.unroll_and_jam",
306    [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
307     TransformOpInterface, TransformEachOpTrait]> {
308  let summary = "Unrolls and jam the given loop with the given unroll factor";
309  let description = [{
310    Unrolls & jams each loop associated with the given handle to have up to the given
311    number of loop body copies per iteration. If the unroll factor is larger
312    than the loop trip count, the latter is used as the unroll factor instead.
313
314    #### Return modes
315
316    This operation ignores non-`scf.for`, non-`affine.for` ops and drops them
317    in the return. If all the operations referred to by the `target` operand
318    unroll properly, the transform succeeds. Otherwise the transform produces a
319    silenceable failure.
320
321    Does not return handles as the operation may result in the loop being
322    removed after a full unrolling.
323  }];
324
325  let arguments = (ins TransformHandleTypeInterface:$target,
326                       ConfinedAttr<I64Attr, [IntPositive]>:$factor);
327
328  let assemblyFormat = "$target attr-dict `:` type($target)";
329
330  let extraClassDeclaration = [{
331    ::mlir::DiagnosedSilenceableFailure applyToOne(
332        ::mlir::transform::TransformRewriter &rewriter,
333        ::mlir::Operation *target,
334        ::mlir::transform::ApplyToEachResultList &results,
335        ::mlir::transform::TransformState &state);
336  }];
337}
338
339def LoopCoalesceOp : Op<Transform_Dialect, "loop.coalesce", [
340  FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
341  TransformOpInterface, TransformEachOpTrait]> {
342  let summary = "Coalesces the perfect loop nest enclosed by a given loop";
343  let description = [{
344    Given a perfect loop nest identified by the outermost loop,
345    perform loop coalescing in a bottom-up one-by-one manner.
346
347    #### Return modes
348
349    The return handle points to the coalesced loop if coalescing happens, or
350    the given input loop if coalescing does not happen.
351  }];
352  let arguments = (ins TransformHandleTypeInterface:$target);
353  let results = (outs TransformHandleTypeInterface:$transformed);
354
355  let assemblyFormat =
356      "$target attr-dict `:` functional-type($target, $transformed)";
357
358  let extraClassDeclaration = [{
359    ::mlir::DiagnosedSilenceableFailure applyToOne(
360        ::mlir::transform::TransformRewriter &rewriter,
361        ::mlir::Operation *target,
362        ::mlir::transform::ApplyToEachResultList &results,
363        ::mlir::transform::TransformState &state);
364  }];
365}
366
367def TakeAssumedBranchOp : Op<Transform_Dialect, "scf.take_assumed_branch", [
368  DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
369  TransformOpInterface, TransformEachOpTrait]> {
370  let description = [{
371    Given an scf.if conditional, inject user-defined information that it is
372    always safe to execute only the if or else branch.
373
374    This is achieved by just replacing the scf.if by the content of one of its
375    branches.
376
377    This is particularly useful for user-controlled rewriting of conditionals
378    that exist solely to guard against out-of-bounds behavior.
379
380    At the moment, no assume or assert operation is emitted as it is not always
381    desirable. In the future, this may be controlled by a dedicated attribute.
382
383    #### Return modes
384
385    The transform only consumes its operand and does not produce any result.
386    The transform definitely fails if `take_else_branch` is specified and the
387    `else` region is empty.
388  }];
389  let arguments = (ins TransformHandleTypeInterface:$target,
390                       OptionalAttr<UnitAttr>:$take_else_branch);
391  let results = (outs);
392
393  let assemblyFormat = [{
394      $target
395      (`take_else_branch` $take_else_branch^)?
396      attr-dict
397       `:` functional-type(operands, results)
398  }];
399
400  let extraClassDeclaration = [{
401    ::mlir::DiagnosedSilenceableFailure applyToOne(
402        ::mlir::transform::TransformRewriter &rewriter,
403        ::mlir::scf::IfOp ifOp,
404        ::mlir::transform::ApplyToEachResultList &results,
405        ::mlir::transform::TransformState &state);
406  }];
407}
408
409def LoopFuseSiblingOp : Op<Transform_Dialect, "loop.fuse_sibling",
410  [FunctionalStyleTransformOpTrait, MemoryEffectsOpInterface,
411   DeclareOpInterfaceMethods<TransformOpInterface>]> {
412  let summary = "Fuse a loop into another loop, assuming the fusion is legal.";
413
414  let description = [{
415    Fuses the `target` loop into the `source` loop assuming they are
416    independent of each other. In the fused loop, the arguments, body and
417    results of `target` are placed _before_ those of `source`.
418
419    For fusion of two `scf.for` loops, the bounds and step size must match. For
420    fusion of two `scf.forall` loops, the bounds and the mapping must match.
421    Otherwise a silencable failure is produced.
422
423    The `target` and `source` handles must refer to exactly one operation,
424    otherwise a definite failure is produced. It is the responsibility of the
425    user to ensure that the `target` and `source` loops are independent of each
426    other -- this op will only perform rudimentary legality checks.
427
428    #### Return modes
429
430    This operation consumes the `target` and `source` handles and produces the
431    `fused_loop` handle, which points to the fused loop.
432  }];
433
434  let arguments = (ins TransformHandleTypeInterface:$target,
435                       TransformHandleTypeInterface:$source);
436  let results = (outs TransformHandleTypeInterface:$fused_loop);
437  let assemblyFormat = "$target `into` $source attr-dict "
438                       " `:` functional-type(operands, results)";
439}
440
441#endif // SCF_TRANSFORM_OPS
442