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 None if `operandIndex` isn't a successor operand 71 index. 72 }], 73 "::llvm::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 (::llvm::Optional<::mlir::BlockArgument> arg = ::mlir::detail::getBranchSuccessorArgument( 78 $_op.getSuccessorOperands(i), operandIndex, 79 opaqueOp->getSuccessor(i))) 80 return arg; 81 } 82 return ::llvm::None; 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 contain 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 of 125 value propagation, where what occurs along control-flow edges is assumed to 126 be side-effect free. For example, corresponding successor operands and 127 successor block arguments may have different types. In such cases, 128 `areTypesCompatible` can be implemented to compare types along control-flow 129 edges. By default, type equality is used. 130 }]; 131 let cppNamespace = "::mlir"; 132 133 let methods = [ 134 InterfaceMethod<[{ 135 Returns the operands of this operation used as the entry arguments when 136 entering the region at `index`, which was specified as a successor of 137 this operation by `getSuccessorRegions`, or the operands forwarded to 138 the operation's results when it branches back to itself. These operands 139 should correspond 1-1 with the successor inputs specified in 140 `getSuccessorRegions`. 141 }], 142 "::mlir::OperandRange", "getSuccessorEntryOperands", 143 (ins "::llvm::Optional<unsigned>":$index), [{}], 144 /*defaultImplementation=*/[{ 145 auto operandEnd = this->getOperation()->operand_end(); 146 return ::mlir::OperandRange(operandEnd, operandEnd); 147 }] 148 >, 149 InterfaceMethod<[{ 150 Returns the viable successors of a region at `index`, or the possible 151 successors when branching from the parent op if `index` is None. These 152 are the regions that may be selected during the flow of control. If 153 `index` is None, `operands` is a set of optional attributes that 154 either correspond to a constant value for each operand of this 155 operation, or null if that operand is not a constant. If `index` is 156 valid, `operands` corresponds to the entry values of the region at 157 `index`. Only a region, i.e. a valid `index`, may use the parent 158 operation as a successor. This method allows for describing which 159 regions may be executed when entering an operation, and which regions 160 are executed after having executed another region of the parent op. The 161 successor region must be non-empty. 162 }], 163 "void", "getSuccessorRegions", 164 (ins "::llvm::Optional<unsigned>":$index, 165 "::llvm::ArrayRef<::mlir::Attribute>":$operands, 166 "::llvm::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions) 167 >, 168 InterfaceMethod<[{ 169 Populates `invocationBounds` with the minimum and maximum number of 170 times this operation will invoke the attached regions (assuming the 171 regions yield normally, i.e. do not abort or invoke an infinite loop). 172 The minimum number of invocations is at least 0. If the maximum number 173 of invocations cannot be statically determined, then it will not have a 174 value (i.e., it is set to `llvm::None`). 175 176 `operands` is a set of optional attributes that either correspond to 177 constant values for each operand of this operation or null if that 178 operand is not a constant. 179 180 This method may be called speculatively on operations where the provided 181 operands are not necessarily the same as the operation's current 182 operands. This may occur in analyses that wish to determine "what would 183 be the region invocations if these were the operands?" 184 }], 185 "void", "getRegionInvocationBounds", 186 (ins "::llvm::ArrayRef<::mlir::Attribute>":$operands, 187 "::llvm::SmallVectorImpl<::mlir::InvocationBounds> &" 188 :$invocationBounds), [{}], 189 [{ invocationBounds.append($_op->getNumRegions(), 190 ::mlir::InvocationBounds::getUnknown()); }] 191 >, 192 InterfaceMethod<[{ 193 This method is called to compare types along control-flow edges. By 194 default, the types are checked as equal. 195 }], 196 "bool", "areTypesCompatible", 197 (ins "::mlir::Type":$lhs, "::mlir::Type":$rhs), [{}], 198 [{ return lhs == rhs; }] 199 >, 200 ]; 201 202 let verify = [{ 203 static_assert(!ConcreteOp::template hasTrait<OpTrait::ZeroRegions>(), 204 "expected operation to have non-zero regions"); 205 return detail::verifyTypesAlongControlFlowEdges($_op); 206 }]; 207 let verifyWithRegions = 1; 208 209 let extraClassDeclaration = [{ 210 /// Convenience helper in case none of the operands is known. 211 void getSuccessorRegions(Optional<unsigned> index, 212 SmallVectorImpl<RegionSuccessor> ®ions); 213 214 /// Return `true` if control flow originating from the given region may 215 /// eventually branch back to the same region. (Maybe after passing through 216 /// other regions.) 217 bool isRepetitiveRegion(unsigned index); 218 }]; 219} 220 221//===----------------------------------------------------------------------===// 222// RegionBranchTerminatorOpInterface 223//===----------------------------------------------------------------------===// 224 225def RegionBranchTerminatorOpInterface : 226 OpInterface<"RegionBranchTerminatorOpInterface"> { 227 let description = [{ 228 This interface provides information for branching terminator operations 229 in the presence of a parent RegionBranchOpInterface implementation. It 230 specifies which operands are passed to which successor region. 231 }]; 232 let cppNamespace = "::mlir"; 233 234 let methods = [ 235 InterfaceMethod<[{ 236 Returns a mutable range of operands that are semantically "returned" by 237 passing them to the region successor given by `index`. If `index` is 238 None, this function returns the operands that are passed as a result to 239 the parent operation. 240 }], 241 "::mlir::MutableOperandRange", "getMutableSuccessorOperands", 242 (ins "::llvm::Optional<unsigned>":$index) 243 >, 244 InterfaceMethod<[{ 245 Returns a range of operands that are semantically "returned" by passing 246 them to the region successor given by `index`. If `index` is None, this 247 function returns the operands that are passed as a result to the parent 248 operation. 249 }], 250 "::mlir::OperandRange", "getSuccessorOperands", 251 (ins "::llvm::Optional<unsigned>":$index), [{}], 252 /*defaultImplementation=*/[{ 253 return $_op.getMutableSuccessorOperands(index); 254 }] 255 > 256 ]; 257 258 let verify = [{ 259 static_assert(ConcreteOp::template hasTrait<OpTrait::IsTerminator>(), 260 "expected operation to be a terminator"); 261 static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroResults>(), 262 "expected operation to have zero results"); 263 static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroSuccessors>(), 264 "expected operation to have zero successors"); 265 return success(); 266 }]; 267} 268 269//===----------------------------------------------------------------------===// 270// ControlFlow Traits 271//===----------------------------------------------------------------------===// 272 273// Op is "return-like". 274def ReturnLike : NativeOpTrait<"ReturnLike">; 275 276#endif // MLIR_INTERFACES_CONTROLFLOWINTERFACES 277