1//===-- ControlFlowInterfaces.td - ControlFlow Interfaces --*- 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// This file contains a set of interfaces that can be used to define information 10// about control flow operations, e.g. branches. 11// 12//===----------------------------------------------------------------------===// 13 14#ifndef MLIR_INTERFACES_CONTROLFLOWINTERFACES 15#define MLIR_INTERFACES_CONTROLFLOWINTERFACES 16 17include "mlir/IR/OpBase.td" 18 19//===----------------------------------------------------------------------===// 20// BranchOpInterface 21//===----------------------------------------------------------------------===// 22 23def BranchOpInterface : OpInterface<"BranchOpInterface"> { 24 let description = [{ 25 This interface provides information for branching terminator operations, 26 i.e. terminator operations with successors. 27 28 This interface is meant to model well-defined cases of control-flow of 29 value propagation, where what occurs along control-flow edges is assumed to 30 be side-effect free. For example, corresponding successor operands and 31 successor block arguments may have different types. In such cases, 32 `areTypesCompatible` can be implemented to compare types along control-flow 33 edges. By default, type equality is used. 34 }]; 35 let cppNamespace = "::mlir"; 36 37 let methods = [ 38 InterfaceMethod<[{ 39 Returns the operands that correspond to the arguments of the successor 40 at the given index. It consists of a number of operands that are 41 internally produced by the operation, followed by a range of operands 42 that are forwarded. An example operation making use of produced 43 operands would be: 44 45 ```mlir 46 invoke %function(%0) 47 label ^success ^error(%1 : i32) 48 49 ^error(%e: !error, %arg0: i32): 50 ... 51 ``` 52 53 The operand that would map to the `^error`s `%e` operand is produced 54 by the `invoke` operation, while `%1` is a forwarded operand that maps 55 to `%arg0` in the successor. 56 57 Produced operands always map to the first few block arguments of the 58 successor, followed by the forwarded operands. Mapping them in any 59 other order is not supported by the interface. 60 61 By having the forwarded operands last allows users of the interface 62 to append more forwarded operands to the branch operation without 63 interfering with other successor operands. 64 }], 65 "::mlir::SuccessorOperands", "getSuccessorOperands", 66 (ins "unsigned":$index) 67 >, 68 InterfaceMethod<[{ 69 Returns the `BlockArgument` corresponding to operand `operandIndex` in 70 some successor, or std::nullopt if `operandIndex` isn't a successor operand 71 index. 72 }], 73 "::std::optional<::mlir::BlockArgument>", "getSuccessorBlockArgument", 74 (ins "unsigned":$operandIndex), [{ 75 ::mlir::Operation *opaqueOp = $_op; 76 for (unsigned i = 0, e = opaqueOp->getNumSuccessors(); i != e; ++i) { 77 if (::std::optional<::mlir::BlockArgument> arg = ::mlir::detail::getBranchSuccessorArgument( 78 $_op.getSuccessorOperands(i), operandIndex, 79 opaqueOp->getSuccessor(i))) 80 return arg; 81 } 82 return ::std::nullopt; 83 }] 84 >, 85 InterfaceMethod<[{ 86 Returns the successor that would be chosen with the given constant 87 operands. Returns nullptr if a single successor could not be chosen. 88 }], 89 "::mlir::Block *", "getSuccessorForOperands", 90 (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands), [{}], 91 /*defaultImplementation=*/[{ return nullptr; }] 92 >, 93 InterfaceMethod<[{ 94 This method is called to compare types along control-flow edges. By 95 default, the types are checked as equal. 96 }], 97 "bool", "areTypesCompatible", 98 (ins "::mlir::Type":$lhs, "::mlir::Type":$rhs), [{}], 99 [{ return lhs == rhs; }] 100 >, 101 ]; 102 103 let verify = [{ 104 auto concreteOp = ::mlir::cast<ConcreteOp>($_op); 105 for (unsigned i = 0, e = $_op->getNumSuccessors(); i != e; ++i) { 106 ::mlir::SuccessorOperands operands = concreteOp.getSuccessorOperands(i); 107 if (::mlir::failed(::mlir::detail::verifyBranchSuccessorOperands($_op, i, operands))) 108 return ::mlir::failure(); 109 } 110 return ::mlir::success(); 111 }]; 112} 113 114//===----------------------------------------------------------------------===// 115// RegionBranchOpInterface 116//===----------------------------------------------------------------------===// 117 118def RegionBranchOpInterface : OpInterface<"RegionBranchOpInterface"> { 119 let description = [{ 120 This interface provides information for region operations that exhibit 121 branching behavior between held regions. I.e., this interface allows for 122 expressing control flow information for region holding operations. 123 124 This interface is meant to model well-defined cases of control-flow and 125 value propagation, where what occurs along control-flow edges is assumed to 126 be side-effect free. 127 128 A "region branch point" indicates a point from which a branch originates. It 129 can indicate either a region of this op or `RegionBranchPoint::parent()`. In 130 the latter case, the branch originates from outside of the op, i.e., when 131 first executing this op. 132 133 A "region successor" indicates the target of a branch. It can indicate 134 either a region of this op or this op. In the former case, the region 135 successor is a region pointer and a range of block arguments to which the 136 "successor operands" are forwarded to. In the latter case, the control flow 137 leaves this op and the region successor is a range of results of this op to 138 which the successor operands are forwarded to. 139 140 By default, successor operands and successor block arguments/successor 141 results must have the same type. `areTypesCompatible` can be implemented to 142 allow non-equal types. 143 144 Example: 145 146 ``` 147 %r = scf.for %iv = %lb to %ub step %step iter_args(%a = %b) 148 -> tensor<5xf32> { 149 ... 150 scf.yield %c : tensor<5xf32> 151 } 152 ``` 153 154 `scf.for` has one region. The region has two region successors: the region 155 itself and the `scf.for` op. %b is an entry successor operand. %c is a 156 successor operand. %a is a successor block argument. %r is a successor 157 result. 158 }]; 159 let cppNamespace = "::mlir"; 160 161 let methods = [ 162 InterfaceMethod<[{ 163 Returns the operands of this operation that are forwarded to the region 164 successor's block arguments or this operation's results when branching 165 to `point`. `point` is guaranteed to be among the successors that are 166 returned by `getEntrySuccessorRegions`/`getSuccessorRegions(parent())`. 167 168 Example: In the above example, this method returns the operand %b of the 169 `scf.for` op, regardless of the value of `point`. I.e., this op always 170 forwards the same operands, regardless of whether the loop has 0 or more 171 iterations. 172 }], 173 "::mlir::OperandRange", "getEntrySuccessorOperands", 174 (ins "::mlir::RegionBranchPoint":$point), [{}], 175 /*defaultImplementation=*/[{ 176 auto operandEnd = this->getOperation()->operand_end(); 177 return ::mlir::OperandRange(operandEnd, operandEnd); 178 }] 179 >, 180 InterfaceMethod<[{ 181 Returns the potential region successors when first executing the op. 182 183 Unlike `getSuccessorRegions`, this method also passes along the 184 constant operands of this op. Based on these, the implementation may 185 filter out certain successors. By default, simply dispatches to 186 `getSuccessorRegions`. `operands` contains an entry for every 187 operand of this op, with a null attribute if the operand has no constant 188 value. 189 190 Note: The control flow does not necessarily have to enter any region of 191 this op. 192 193 Example: In the above example, this method may return two region 194 region successors: the single region of the `scf.for` op and the 195 `scf.for` operation (that implements this interface). If %lb, %ub, %step 196 are constants and it can be determined the loop does not have any 197 iterations, this method may choose to return only this operation. 198 Similarly, if it can be determined that the loop has at least one 199 iteration, this method may choose to return only the region of the loop. 200 }], 201 "void", "getEntrySuccessorRegions", 202 (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands, 203 "::llvm::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions), [{}], 204 /*defaultImplementation=*/[{ 205 $_op.getSuccessorRegions(mlir::RegionBranchPoint::parent(), regions); 206 }] 207 >, 208 InterfaceMethod<[{ 209 Returns the potential region successors when branching from `point`. 210 These are the regions that may be selected during the flow of control. 211 212 When `point = RegionBranchPoint::parent()`, this method returns the 213 region successors when entering the operation. Otherwise, this method 214 returns the successor regions when branching from the region indicated 215 by `point`. 216 217 Example: In the above example, this method returns the region of the 218 `scf.for` and this operation for either region branch point (`parent` 219 and the region of the `scf.for`). An implementation may choose to filter 220 out region successors when it is statically known (e.g., by examining 221 the operands of this op) that those successors are not branched to. 222 }], 223 "void", "getSuccessorRegions", 224 (ins "::mlir::RegionBranchPoint":$point, 225 "::llvm::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions) 226 >, 227 InterfaceMethod<[{ 228 Populates `invocationBounds` with the minimum and maximum number of 229 times this operation will invoke the attached regions (assuming the 230 regions yield normally, i.e. do not abort or invoke an infinite loop). 231 The minimum number of invocations is at least 0. If the maximum number 232 of invocations cannot be statically determined, then it will be set to 233 `InvocationBounds::getUnknown()`. 234 235 This method also passes along the constant operands of this op. 236 `operands` contains an entry for every operand of this op, with a null 237 attribute if the operand has no constant value. 238 239 This method may be called speculatively on operations where the provided 240 operands are not necessarily the same as the operation's current 241 operands. This may occur in analyses that wish to determine "what would 242 be the region invocations if these were the operands?" 243 }], 244 "void", "getRegionInvocationBounds", 245 (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands, 246 "::llvm::SmallVectorImpl<::mlir::InvocationBounds> &" 247 :$invocationBounds), [{}], 248 /*defaultImplementation=*/[{ 249 invocationBounds.append($_op->getNumRegions(), 250 ::mlir::InvocationBounds::getUnknown()); 251 }] 252 >, 253 InterfaceMethod<[{ 254 This method is called to compare types along control-flow edges. By 255 default, the types are checked as equal. 256 }], 257 "bool", "areTypesCompatible", 258 (ins "::mlir::Type":$lhs, "::mlir::Type":$rhs), [{}], 259 /*defaultImplementation=*/[{ return lhs == rhs; }] 260 >, 261 ]; 262 263 let verify = [{ 264 static_assert(!ConcreteOp::template hasTrait<OpTrait::ZeroRegions>(), 265 "expected operation to have non-zero regions"); 266 return detail::verifyTypesAlongControlFlowEdges($_op); 267 }]; 268 let verifyWithRegions = 1; 269 270 let extraClassDeclaration = [{ 271 /// Return `true` if control flow originating from the given region may 272 /// eventually branch back to the same region. (Maybe after passing through 273 /// other regions.) 274 bool isRepetitiveRegion(unsigned index); 275 276 /// Return `true` if there is a loop in the region branching graph. Only 277 /// reachable regions (starting from the entry regions) are considered. 278 bool hasLoop(); 279 }]; 280} 281 282//===----------------------------------------------------------------------===// 283// RegionBranchTerminatorOpInterface 284//===----------------------------------------------------------------------===// 285 286def RegionBranchTerminatorOpInterface : 287 OpInterface<"RegionBranchTerminatorOpInterface"> { 288 let description = [{ 289 This interface provides information for branching terminator operations 290 in the presence of a parent `RegionBranchOpInterface` implementation. It 291 specifies which operands are passed to which successor region. 292 }]; 293 let cppNamespace = "::mlir"; 294 295 let methods = [ 296 InterfaceMethod<[{ 297 Returns a mutable range of operands that are semantically "returned" by 298 passing them to the region successor indicated by `point`. 299 }], 300 "::mlir::MutableOperandRange", "getMutableSuccessorOperands", 301 (ins "::mlir::RegionBranchPoint":$point) 302 >, 303 InterfaceMethod<[{ 304 Returns the potential region successors that are branched to after this 305 terminator based on the given constant operands. 306 307 This method also passes along the constant operands of this op. 308 `operands` contains an entry for every operand of this op, with a null 309 attribute if the operand has no constant value. 310 311 The default implementation simply dispatches to the parent 312 `RegionBranchOpInterface`'s `getSuccessorRegions` implementation. 313 }], 314 "void", "getSuccessorRegions", 315 (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands, 316 "::llvm::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions), [{}], 317 /*defaultImplementation=*/[{ 318 ::mlir::Operation *op = $_op; 319 ::llvm::cast<::mlir::RegionBranchOpInterface>(op->getParentOp()) 320 .getSuccessorRegions(op->getParentRegion(), regions); 321 }] 322 >, 323 ]; 324 325 let verify = [{ 326 static_assert(ConcreteOp::template hasTrait<OpTrait::IsTerminator>(), 327 "expected operation to be a terminator"); 328 static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroResults>(), 329 "expected operation to have zero results"); 330 static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroSuccessors>(), 331 "expected operation to have zero successors"); 332 return success(); 333 }]; 334 335 let extraClassDeclaration = [{ 336 // Returns a range of operands that are semantically "returned" by passing 337 // them to the region successor given by `index`. If `index` is None, this 338 // function returns the operands that are passed as a result to the parent 339 // operation. 340 ::mlir::OperandRange getSuccessorOperands(::mlir::RegionBranchPoint point) { 341 return getMutableSuccessorOperands(point); 342 } 343 }]; 344} 345 346def SelectLikeOpInterface : OpInterface<"SelectLikeOpInterface"> { 347 let description = [{ 348 This interface provides information for select-like operations, i.e., 349 operations that forward specific operands to the output, depending on a 350 binary condition. 351 352 If the value of the condition is 1, then the `true` operand is returned, 353 and the third operand is ignored, even if it was poison. 354 355 If the value of the condition is 0, then the `false` operand is returned, 356 and the second operand is ignored, even if it was poison. 357 358 If the condition is poison, then poison is returned. 359 360 Implementing operations can also accept shaped conditions, in which case 361 the operation works element-wise. 362 }]; 363 let cppNamespace = "::mlir"; 364 365 let methods = [ 366 InterfaceMethod<[{ 367 Returns the operand that would be chosen for a false condition. 368 }], "::mlir::Value", "getFalseValue", (ins)>, 369 InterfaceMethod<[{ 370 Returns the operand that would be chosen for a true condition. 371 }], "::mlir::Value", "getTrueValue", (ins)>, 372 InterfaceMethod<[{ 373 Returns the condition operand. 374 }], "::mlir::Value", "getCondition", (ins)> 375 ]; 376} 377 378//===----------------------------------------------------------------------===// 379// ControlFlow Traits 380//===----------------------------------------------------------------------===// 381 382// Op is "return-like". 383def ReturnLike : TraitList<[ 384 DeclareOpInterfaceMethods<RegionBranchTerminatorOpInterface>, 385 NativeOpTrait< 386 /*name=*/"ReturnLike", 387 /*traits=*/[], 388 /*extraOpDeclaration=*/"", 389 /*extraOpDefinition=*/[{ 390 ::mlir::MutableOperandRange $cppClass::getMutableSuccessorOperands( 391 ::mlir::RegionBranchPoint point) { 392 return ::mlir::MutableOperandRange(*this); 393 } 394 }] 395 > 396]>; 397 398#endif // MLIR_INTERFACES_CONTROLFLOWINTERFACES 399