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