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