1//===- SCFOps.td - Structured Control Flow 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// Defines MLIR structured control flow operations.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef MLIR_DIALECT_SCF_SCFOPS
14#define MLIR_DIALECT_SCF_SCFOPS
15
16include "mlir/Interfaces/ControlFlowInterfaces.td"
17include "mlir/Interfaces/LoopLikeInterface.td"
18include "mlir/Interfaces/SideEffectInterfaces.td"
19
20def SCF_Dialect : Dialect {
21  let name = "scf";
22  let cppNamespace = "::mlir::scf";
23}
24
25// Base class for SCF dialect ops.
26class SCF_Op<string mnemonic, list<OpTrait> traits = []> :
27    Op<SCF_Dialect, mnemonic, traits> {
28  // For every standard op, there needs to be a:
29  //   * void print(OpAsmPrinter &p, ${C++ class of Op} op)
30  //   * LogicalResult verify(${C++ class of Op} op)
31  //   * ParseResult parse${C++ class of Op}(OpAsmParser &parser,
32  //                                         OperationState &result)
33  // functions.
34  let printer = [{ return ::print(p, *this); }];
35  let verifier = [{ return ::verify(*this); }];
36  let parser = [{ return ::parse$cppClass(parser, result); }];
37}
38
39def ConditionOp : SCF_Op<"condition",
40                         [HasParent<"WhileOp">, NoSideEffect, Terminator]> {
41  let summary = "loop continuation condition";
42  let description = [{
43    This operation accepts the continuation (i.e., inverse of exit) condition
44    of the `scf.while` construct. If its first argument is true, the "after"
45    region of `scf.while` is executed, with the remaining arguments forwarded
46    to the entry block of the region. Otherwise, the loop terminates.
47  }];
48
49  let arguments = (ins I1:$condition, Variadic<AnyType>:$args);
50
51  let assemblyFormat =
52      [{ `(` $condition `)` attr-dict ($args^ `:` type($args))? }];
53
54  // Override the default verifier, everything is checked by traits.
55  let verifier = ?;
56}
57
58def ForOp : SCF_Op<"for",
59      [DeclareOpInterfaceMethods<LoopLikeOpInterface>,
60       DeclareOpInterfaceMethods<RegionBranchOpInterface>,
61       SingleBlockImplicitTerminator<"scf::YieldOp">,
62       RecursiveSideEffects]> {
63  let summary = "for operation";
64  let description = [{
65    The "scf.for" operation represents a loop taking 3 SSA value as operands
66    that represent the lower bound, upper bound and step respectively.  The
67    operation defines an SSA value for its induction variable. It has one
68    region capturing the loop body. The induction variable is represented as an
69    argument of this region. This SSA value always has type index, which is the
70    size of the machine word. The step is a value of type index, required to be
71    positive.
72    The lower and upper bounds specify a half-open range: the range includes
73    the lower bound but does not include the upper bound.
74
75    The body region must contain exactly one block that terminates with
76    "scf.yield". Calling ForOp::build will create such a region and insert
77    the terminator implicitly if none is defined, so will the parsing even in
78    cases when it is absent from the custom format. For example:
79
80    ```mlir
81    scf.for %iv = %lb to %ub step %step {
82      ... // body
83    }
84    ```
85
86    `scf.for` can also operate on loop-carried variables and returns the final
87    values after loop termination. The initial values of the variables are
88    passed as additional SSA operands to the "scf.for" following the 3 loop
89    control SSA values mentioned above (lower bound, upper bound and step). The
90    operation region has equivalent arguments for each variable representing
91    the value of the variable at the current iteration.
92
93    The region must terminate with a "scf.yield" that passes all the current
94    iteration variables to the next iteration, or to the "scf.for" result, if
95    at the last iteration. Note, that when the loop-carried variables are
96    present, calling ForOp::build will not insert the terminator implicitly.
97    The caller must insert "scf.yield" in that case.
98
99    "scf.for" results hold the final values after the last iteration.
100    For example, to sum-reduce a memref:
101
102    ```mlir
103    func @reduce(%buffer: memref<1024xf32>, %lb: index,
104                 %ub: index, %step: index) -> (f32) {
105      // Initial sum set to 0.
106      %sum_0 = constant 0.0 : f32
107      // iter_args binds initial values to the loop's region arguments.
108      %sum = scf.for %iv = %lb to %ub step %step
109          iter_args(%sum_iter = %sum_0) -> (f32) {
110        %t = load %buffer[%iv] : memref<1024xf32>
111        %sum_next = addf %sum_iter, %t : f32
112        // Yield current iteration sum to next iteration %sum_iter or to %sum
113        // if final iteration.
114        scf.yield %sum_next : f32
115      }
116      return %sum : f32
117    }
118    ```
119
120    If the "scf.for" defines any values, a yield must be explicitly present.
121    The number and types of the "scf.for" results must match the initial
122    values in the "iter_args" binding and the yield operands.
123
124    Another example with a nested "scf.if" (see "scf.if" for details) to
125    perform conditional reduction:
126
127    ```mlir
128    func @conditional_reduce(%buffer: memref<1024xf32>, %lb: index,
129                             %ub: index, %step: index) -> (f32) {
130      %sum_0 = constant 0.0 : f32
131      %c0 = constant 0.0 : f32
132      %sum = scf.for %iv = %lb to %ub step %step
133          iter_args(%sum_iter = %sum_0) -> (f32) {
134        %t = load %buffer[%iv] : memref<1024xf32>
135        %cond = cmpf "ugt", %t, %c0 : f32
136        %sum_next = scf.if %cond -> (f32) {
137          %new_sum = addf %sum_iter, %t : f32
138          scf.yield %new_sum : f32
139        } else {
140          scf.yield %sum_iter : f32
141        }
142        scf.yield %sum_next : f32
143      }
144      return %sum : f32
145    }
146    ```
147  }];
148  let arguments = (ins Index:$lowerBound,
149                       Index:$upperBound,
150                       Index:$step,
151                       Variadic<AnyType>:$initArgs);
152  let results = (outs Variadic<AnyType>:$results);
153  let regions = (region SizedRegion<1>:$region);
154
155  let skipDefaultBuilders = 1;
156  let builders = [
157    OpBuilderDAG<(ins "Value":$lowerBound, "Value":$upperBound, "Value":$step,
158      CArg<"ValueRange", "llvm::None">:$iterArgs,
159      CArg<"function_ref<void(OpBuilder &, Location, Value, ValueRange)>",
160           "nullptr">)>
161  ];
162
163  let extraClassDeclaration = [{
164    using BodyBuilderFn =
165        function_ref<void(OpBuilder &, Location, Value, ValueRange)>;
166
167    Value getInductionVar() { return getBody()->getArgument(0); }
168    Block::BlockArgListType getRegionIterArgs() {
169      return getBody()->getArguments().drop_front();
170    }
171    Operation::operand_range getIterOperands() {
172      return getOperands().drop_front(getNumControlOperands());
173    }
174
175    void setLowerBound(Value bound) { getOperation()->setOperand(0, bound); }
176    void setUpperBound(Value bound) { getOperation()->setOperand(1, bound); }
177    void setStep(Value step) { getOperation()->setOperand(2, step); }
178
179    /// Number of region arguments for loop-carried values
180    unsigned getNumRegionIterArgs() {
181      return getBody()->getNumArguments() - 1;
182    }
183    /// Number of operands controlling the loop: lb, ub, step
184    unsigned getNumControlOperands() { return 3; }
185    /// Does the operation hold operands for loop-carried values
186    bool hasIterOperands() {
187      return getOperation()->getNumOperands() > getNumControlOperands();
188    }
189    /// Get Number of loop-carried values
190    unsigned getNumIterOperands() {
191      return getOperation()->getNumOperands() - getNumControlOperands();
192    }
193
194    /// Return operands used when entering the region at 'index'. These operands
195    /// correspond to the loop iterator operands, i.e., those exclusing the
196    /// induction variable. LoopOp only has one region, so 0 is the only valid
197    /// value for `index`.
198    OperandRange getSuccessorEntryOperands(unsigned index);
199
200    /// Returns the number of invocations of the body block if the loop bounds
201    /// are constants. Returns `kUnknownNumRegionInvocations` otherwise.
202    void getNumRegionInvocations(ArrayRef<Attribute> operands,
203                                 SmallVectorImpl<int64_t> &countPerRegion);
204  }];
205
206  let hasCanonicalizer = 1;
207}
208
209def IfOp : SCF_Op<"if",
210      [DeclareOpInterfaceMethods<RegionBranchOpInterface>,
211       SingleBlockImplicitTerminator<"scf::YieldOp">, RecursiveSideEffects,
212       NoRegionArguments]> {
213  let summary = "if-then-else operation";
214  let description = [{
215    The `scf.if` operation represents an if-then-else construct for
216    conditionally executing two regions of code. The operand to an if operation
217    is a boolean value. For example:
218
219    ```mlir
220    scf.if %b  {
221      ...
222    } else {
223      ...
224    }
225    ```
226
227    `scf.if` may also return results that are defined in its regions. The
228    values defined are determined by which execution path is taken.
229
230    Example:
231
232    ```mlir
233    %x, %y = scf.if %b -> (f32, f32) {
234      %x_true = ...
235      %y_true = ...
236      scf.yield %x_true, %y_true : f32, f32
237    } else {
238      %x_false = ...
239      %y_false = ...
240      scf.yield %x_false, %y_false : f32, f32
241    }
242    ```
243
244    `scf.if` regions are always terminated with "scf.yield". If "scf.if"
245    defines no values, the "scf.yield" can be left out, and will be inserted
246    implicitly. Otherwise, it must be explicit.
247    Also, if "scf.if" defines one or more values, the 'else' block cannot be
248    omitted.
249
250    Example:
251
252    ```mlir
253    scf.if %b  {
254      ...
255    }
256    ```
257  }];
258  let arguments = (ins I1:$condition);
259  let results = (outs Variadic<AnyType>:$results);
260  let regions = (region SizedRegion<1>:$thenRegion, AnyRegion:$elseRegion);
261
262  let skipDefaultBuilders = 1;
263  let builders = [
264    OpBuilderDAG<(ins "Value":$cond, "bool":$withElseRegion)>,
265    OpBuilderDAG<(ins "TypeRange":$resultTypes, "Value":$cond,
266      "bool":$withElseRegion)>,
267    OpBuilderDAG<(ins "TypeRange":$resultTypes, "Value":$cond,
268      CArg<"function_ref<void(OpBuilder &, Location)>",
269           "buildTerminatedBody">:$thenBuilder,
270      CArg<"function_ref<void(OpBuilder &, Location)>",
271           "nullptr">:$elseBuilder)>,
272    OpBuilderDAG<(ins "Value":$cond,
273      CArg<"function_ref<void(OpBuilder &, Location)>",
274           "buildTerminatedBody">:$thenBuilder,
275      CArg<"function_ref<void(OpBuilder &, Location)>",
276           "nullptr">:$elseBuilder)>
277  ];
278
279  let extraClassDeclaration = [{
280    OpBuilder getThenBodyBuilder(OpBuilder::Listener *listener = nullptr) {
281      Block* body = getBody(0);
282      return results().empty() ? OpBuilder::atBlockTerminator(body, listener)
283                               : OpBuilder::atBlockEnd(body, listener);
284    }
285    OpBuilder getElseBodyBuilder(OpBuilder::Listener *listener = nullptr) {
286      Block* body = getBody(1);
287      return results().empty() ? OpBuilder::atBlockTerminator(body, listener)
288                               : OpBuilder::atBlockEnd(body, listener);
289    }
290  }];
291
292  let hasCanonicalizer = 1;
293}
294
295def ParallelOp : SCF_Op<"parallel",
296    [AttrSizedOperandSegments,
297     DeclareOpInterfaceMethods<LoopLikeOpInterface>,
298     RecursiveSideEffects,
299     SingleBlockImplicitTerminator<"scf::YieldOp">]> {
300  let summary = "parallel for operation";
301  let description = [{
302    The "scf.parallel" operation represents a loop nest taking 4 groups of SSA
303    values as operands that represent the lower bounds, upper bounds, steps and
304    initial values, respectively. The operation defines a variadic number of
305    SSA values for its induction variables. It has one region capturing the
306    loop body. The induction variables are represented as an argument of this
307    region. These SSA values always have type index, which is the size of the
308    machine word. The steps are values of type index, required to be positive.
309    The lower and upper bounds specify a half-open range: the range includes
310    the lower bound but does not include the upper bound. The initial values
311    have the same types as results of "scf.parallel". If there are no results,
312    the keyword `init` can be omitted.
313
314    Semantically we require that the iteration space can be iterated in any
315    order, and the loop body can be executed in parallel. If there are data
316    races, the behavior is undefined.
317
318    The parallel loop operation supports reduction of values produced by
319    individual iterations into a single result. This is modeled using the
320    scf.reduce operation (see scf.reduce for details). Each result of a
321    scf.parallel operation is associated with an initial value operand and
322    reduce operation that is an immediate child. Reductions are matched to
323    result and initial values in order of their appearance in the body.
324    Consequently, we require that the body region has the same number of
325    results and initial values as it has reduce operations.
326
327    The body region must contain exactly one block that terminates with
328    "scf.yield" without operands. Parsing ParallelOp will create such a region
329    and insert the terminator when it is absent from the custom format.
330
331    Example:
332
333    ```mlir
334    %init = constant 0.0 : f32
335    scf.parallel (%iv) = (%lb) to (%ub) step (%step) init (%init) -> f32 {
336      %elem_to_reduce = load %buffer[%iv] : memref<100xf32>
337      scf.reduce(%elem_to_reduce) : f32 {
338        ^bb0(%lhs : f32, %rhs: f32):
339          %res = addf %lhs, %rhs : f32
340          scf.reduce.return %res : f32
341      }
342    }
343    ```
344  }];
345
346  let arguments = (ins Variadic<Index>:$lowerBound,
347                       Variadic<Index>:$upperBound,
348                       Variadic<Index>:$step,
349                       Variadic<AnyType>:$initVals);
350  let results = (outs Variadic<AnyType>:$results);
351  let regions = (region SizedRegion<1>:$region);
352
353  let skipDefaultBuilders = 1;
354  let builders = [
355    OpBuilderDAG<(ins "ValueRange":$lowerBounds, "ValueRange":$upperBounds,
356      "ValueRange":$steps, "ValueRange":$initVals,
357      CArg<"function_ref<void (OpBuilder &, Location, ValueRange, ValueRange)>",
358           "nullptr">:$bodyBuilderFn)>,
359    OpBuilderDAG<(ins "ValueRange":$lowerBounds, "ValueRange":$upperBounds,
360      "ValueRange":$steps,
361      CArg<"function_ref<void (OpBuilder &, Location, ValueRange)>",
362           "nullptr">:$bodyBuilderFn)>,
363  ];
364
365  let extraClassDeclaration = [{
366    ValueRange getInductionVars() {
367      return getBody()->getArguments();
368    }
369    unsigned getNumLoops() { return step().size(); }
370    unsigned getNumReductions() { return initVals().size(); }
371  }];
372
373  let hasCanonicalizer = 1;
374}
375
376def ReduceOp : SCF_Op<"reduce", [HasParent<"ParallelOp">]> {
377  let summary = "reduce operation for parallel for";
378  let description = [{
379    "scf.reduce" is an operation occurring inside "scf.parallel" operations.
380    It consists of one block with two arguments which have the same type as the
381    operand of "scf.reduce".
382
383    "scf.reduce" is used to model the value for reduction computations of a
384    "scf.parallel" operation. It has to appear as an immediate child of a
385    "scf.parallel" and is associated with a result value of its parent
386    operation.
387
388    Association is in the order of appearance in the body where the first
389    result of a parallel loop operation corresponds to the first "scf.reduce"
390    in the operation's body region. The reduce operation takes a single
391    operand, which is the value to be used in the reduction.
392
393    The reduce operation contains a region whose entry block expects two
394    arguments of the same type as the operand. As the iteration order of the
395    parallel loop and hence reduction order is unspecified, the result of
396    reduction may be non-deterministic unless the operation is associative and
397    commutative.
398
399    The result of the reduce operation's body must have the same type as the
400    operands and associated result value of the parallel loop operation.
401    Example:
402
403    ```mlir
404    %operand = constant 1.0 : f32
405    scf.reduce(%operand) : f32 {
406      ^bb0(%lhs : f32, %rhs: f32):
407        %res = addf %lhs, %rhs : f32
408        scf.reduce.return %res : f32
409    }
410    ```
411  }];
412
413  let skipDefaultBuilders = 1;
414  let builders = [
415    OpBuilderDAG<(ins "Value":$operand,
416      CArg<"function_ref<void (OpBuilder &, Location, Value, Value)>",
417           "nullptr">:$bodyBuilderFn)>
418  ];
419
420  let arguments = (ins AnyType:$operand);
421  let regions = (region SizedRegion<1>:$reductionOperator);
422}
423
424def ReduceReturnOp :
425    SCF_Op<"reduce.return", [HasParent<"ReduceOp">, NoSideEffect,
426                              Terminator]> {
427  let summary = "terminator for reduce operation";
428  let description = [{
429    "scf.reduce.return" is a special terminator operation for the block inside
430    "scf.reduce". It terminates the region. It should have the same type as
431    the operand of "scf.reduce". Example for the custom format:
432
433    ```mlir
434    scf.reduce.return %res : f32
435    ```
436  }];
437
438  let arguments = (ins AnyType:$result);
439  let assemblyFormat = "$result attr-dict `:` type($result)";
440}
441
442def WhileOp : SCF_Op<"while",
443    [DeclareOpInterfaceMethods<RegionBranchOpInterface>,
444     RecursiveSideEffects]> {
445  let summary = "a generic 'while' loop";
446  let description = [{
447    This operation represents a generic "while"/"do-while" loop that keeps
448    iterating as long as a condition is satisfied. There is no restriction on
449    the complexity of the condition. It consists of two regions (with single
450    block each): "before" region and "after" region. The names of regions
451    indicates whether they execute before or after the condition check.
452    Therefore, if the main loop payload is located in the "before" region, the
453    operation is a "do-while" loop. Otherwise, it is a "while" loop.
454
455    The "before" region terminates with a special operation, `scf.condition`,
456    that accepts as its first operand an `i1` value indicating whether to
457    proceed to the "after" region (value is `true`) or not. The two regions
458    communicate by means of region arguments. Initially, the "before" region
459    accepts as arguments the operands of the `scf.while` operation and uses them
460    to evaluate the condition. It forwards the trailing, non-condition operands
461    of the `scf.condition` terminator either to the "after" region if the
462    control flow is transferred there or to results of the `scf.while` operation
463    otherwise. The "after" region takes as arguments the values produced by the
464    "before" region and uses `scf.yield` to supply new arguments for the "after"
465    region, into which it transfers the control flow unconditionally.
466
467    A simple "while" loop can be represented as follows.
468
469    ```mlir
470    %res = scf.while (%arg1 = %init1) : (f32) -> f32 {
471      /* "Before" region.
472       * In a "while" loop, this region computes the condition. */
473      %condition = call @evaluate_condition(%arg1) : (f32) -> i1
474
475      /* Forward the argument (as result or "after" region argument). */
476      scf.condition(%condition) %arg1 : f32
477
478    } do {
479    ^bb0(%arg2: f32):
480      /* "After region.
481       * In a "while" loop, this region is the loop body. */
482      %next = call @payload(%arg2) : (f32) -> f32
483
484      /* Forward the new value to the "before" region.
485       * The operand types must match the types of the `scf.while` operands. */
486      scf.yield %next : f32
487    }
488    ```
489
490    A simple "do-while" loop can be represented by reducing the "after" block
491    to a simple forwarder.
492
493    ```mlir
494    %res = scf.while (%arg1 = %init1) : (f32) -> f32 {
495      /* "Before" region.
496       * In a "do-while" loop, this region contains the loop body. */
497      %next = call @payload(%arg1) : (f32) -> f32
498
499      /* And also evalutes the condition. */
500      %condition = call @evaluate_condition(%arg1) : (f32) -> i1
501
502      /* Loop through the "after" region. */
503      scf.condition(%condition) %next : f32
504
505    } do {
506    ^bb0(%arg2: f32):
507      /* "After" region.
508       * Forwards the values back to "before" region unmodified. */
509      scf.yield %arg2 : f32
510    }
511    ```
512
513    Note that the types of region arguments need not to match with each other.
514    The op expects the operand types to match with argument types of the
515    "before" region"; the result types to match with the trailing operand types
516    of the terminator of the "before" region, and with the argument types of the
517    "after" region. The following scheme can be used to share the results of
518    some operations executed in the "before" region with the "after" region,
519    avoiding the need to recompute them.
520
521    ```mlir
522    %res = scf.while (%arg1 = %init1) : (f32) -> i64 {
523      /* One can perform some computations, e.g., necessary to evaluate the
524       * condition, in the "before" region and forward their results to the
525       * "after" region. */
526      %shared = call @shared_compute(%arg1) : (f32) -> i64
527
528      /* Evalute the condition. */
529      %condition = call @evaluate_condition(%arg1, %shared) : (f32, i64) -> i1
530
531      /* Forward the result of the shared computation to the "after" region.
532       * The types must match the arguments of the "after" region as well as
533       * those of the `scf.while` results. */
534      scf.condition(%condition) %shared : i64
535
536    } do {
537    ^bb0(%arg2: i64) {
538      /* Use the partial result to compute the rest of the payload in the
539       * "after" region. */
540      %res = call @payload(%arg2) : (i64) -> f32
541
542      /* Forward the new value to the "before" region.
543       * The operand types must match the types of the `scf.while` operands. */
544      scf.yield %res : f32
545    }
546    ```
547
548    The custom syntax for this operation is as follows.
549
550    ```
551    op ::= `scf.while` assignments `:` function-type region `do` region
552           `attributes` attribute-dict
553    initializer ::= /* empty */ | `(` assignment-list `)`
554    assignment-list ::= assignment | assignment `,` assignment-list
555    assignment ::= ssa-value `=` ssa-value
556    ```
557  }];
558
559  let arguments = (ins Variadic<AnyType>:$inits);
560  let results = (outs Variadic<AnyType>:$results);
561  let regions = (region SizedRegion<1>:$before, SizedRegion<1>:$after);
562
563  let extraClassDeclaration = [{
564    OperandRange getSuccessorEntryOperands(unsigned index);
565  }];
566}
567
568def YieldOp : SCF_Op<"yield", [NoSideEffect, ReturnLike, Terminator,
569                               ParentOneOf<["IfOp, ForOp", "ParallelOp",
570                                            "WhileOp"]>]> {
571  let summary = "loop yield and termination operation";
572  let description = [{
573    "scf.yield" yields an SSA value from the SCF dialect op region and
574    terminates the regions. The semantics of how the values are yielded is
575    defined by the parent operation.
576    If "scf.yield" has any operands, the operands must match the parent
577    operation's results.
578    If the parent operation defines no values, then the "scf.yield" may be
579    left out in the custom syntax and the builders will insert one implicitly.
580    Otherwise, it has to be present in the syntax to indicate which values are
581    yielded.
582  }];
583
584  let arguments = (ins Variadic<AnyType>:$results);
585  let builders = [OpBuilderDAG<(ins), [{ /* nothing to do */ }]>];
586  // Override default verifier (defined in SCF_Op), no custom verification
587  // needed.
588  let verifier = ?;
589}
590
591#endif // MLIR_DIALECT_SCF_SCFOPS
592