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