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 a mutable range of operands that correspond to the arguments of 40 successor at the given index. Returns None if the operands to the 41 successor are non-materialized values, i.e. they are internal to the 42 operation. 43 }], 44 "::mlir::Optional<::mlir::MutableOperandRange>", "getMutableSuccessorOperands", 45 (ins "unsigned":$index) 46 >, 47 InterfaceMethod<[{ 48 Returns a range of operands that correspond to the arguments of 49 successor at the given index. Returns None if the operands to the 50 successor are non-materialized values, i.e. they are internal to the 51 operation. 52 }], 53 "::mlir::Optional<::mlir::OperandRange>", "getSuccessorOperands", 54 (ins "unsigned":$index), [{}], [{ 55 auto operands = $_op.getMutableSuccessorOperands(index); 56 return operands ? ::mlir::Optional<::mlir::OperandRange>(*operands) : ::llvm::None; 57 }] 58 >, 59 InterfaceMethod<[{ 60 Returns the `BlockArgument` corresponding to operand `operandIndex` in 61 some successor, or None if `operandIndex` isn't a successor operand 62 index. 63 }], 64 "::mlir::Optional<::mlir::BlockArgument>", "getSuccessorBlockArgument", 65 (ins "unsigned":$operandIndex), [{ 66 ::mlir::Operation *opaqueOp = $_op; 67 for (unsigned i = 0, e = opaqueOp->getNumSuccessors(); i != e; ++i) { 68 if (::mlir::Optional<::mlir::BlockArgument> arg = ::mlir::detail::getBranchSuccessorArgument( 69 $_op.getSuccessorOperands(i), operandIndex, 70 opaqueOp->getSuccessor(i))) 71 return arg; 72 } 73 return ::llvm::None; 74 }] 75 >, 76 InterfaceMethod<[{ 77 Returns the successor that would be chosen with the given constant 78 operands. Returns nullptr if a single successor could not be chosen. 79 }], 80 "::mlir::Block *", "getSuccessorForOperands", 81 (ins "::mlir::ArrayRef<::mlir::Attribute>":$operands), [{}], 82 /*defaultImplementation=*/[{ return nullptr; }] 83 >, 84 InterfaceMethod<[{ 85 This method is called to compare types along control-flow edges. By 86 default, the types are checked as equal. 87 }], 88 "bool", "areTypesCompatible", 89 (ins "::mlir::Type":$lhs, "::mlir::Type":$rhs), [{}], 90 [{ return lhs == rhs; }] 91 >, 92 ]; 93 94 let verify = [{ 95 auto concreteOp = ::mlir::cast<ConcreteOp>($_op); 96 for (unsigned i = 0, e = $_op->getNumSuccessors(); i != e; ++i) { 97 ::mlir::Optional<OperandRange> operands = concreteOp.getSuccessorOperands(i); 98 if (::mlir::failed(::mlir::detail::verifyBranchSuccessorOperands($_op, i, operands))) 99 return ::mlir::failure(); 100 } 101 return ::mlir::success(); 102 }]; 103} 104 105//===----------------------------------------------------------------------===// 106// RegionBranchOpInterface 107//===----------------------------------------------------------------------===// 108 109def RegionBranchOpInterface : OpInterface<"RegionBranchOpInterface"> { 110 let description = [{ 111 This interface provides information for region operations that contain 112 branching behavior between held regions, i.e. this interface allows for 113 expressing control flow information for region holding operations. 114 115 This interface is meant to model well-defined cases of control-flow of 116 value propagation, where what occurs along control-flow edges is assumed to 117 be side-effect free. For example, corresponding successor operands and 118 successor block arguments may have different types. In such cases, 119 `areTypesCompatible` can be implemented to compare types along control-flow 120 edges. By default, type equality is used. 121 }]; 122 let cppNamespace = "::mlir"; 123 124 let methods = [ 125 InterfaceMethod<[{ 126 Returns the operands of this operation used as the entry arguments when 127 entering the region at `index`, which was specified as a successor of 128 this operation by `getSuccessorRegions`. These operands should 129 correspond 1-1 with the successor inputs specified in 130 `getSuccessorRegions`. 131 }], 132 "::mlir::OperandRange", "getSuccessorEntryOperands", 133 (ins "unsigned":$index), [{}], /*defaultImplementation=*/[{ 134 auto operandEnd = this->getOperation()->operand_end(); 135 return ::mlir::OperandRange(operandEnd, operandEnd); 136 }] 137 >, 138 InterfaceMethod<[{ 139 Returns the viable successors of a region at `index`, or the possible 140 successors when branching from the parent op if `index` is None. These 141 are the regions that may be selected during the flow of control. If 142 `index` is None, `operands` is a set of optional attributes that 143 either correspond to a constant value for each operand of this 144 operation, or null if that operand is not a constant. If `index` is 145 valid, `operands` corresponds to the entry values of the region at 146 `index`. Only a region, i.e. a valid `index`, may use the parent 147 operation as a successor. This method allows for describing which 148 regions may be executed when entering an operation, and which regions 149 are executed after having executed another region of the parent op. The 150 successor region must be non-empty. 151 }], 152 "void", "getSuccessorRegions", 153 (ins "::mlir::Optional<unsigned>":$index, 154 "::mlir::ArrayRef<::mlir::Attribute>":$operands, 155 "::mlir::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions) 156 >, 157 InterfaceMethod<[{ 158 Populates `invocationBounds` with the minimum and maximum number of 159 times this operation will invoke the attached regions (assuming the 160 regions yield normally, i.e. do not abort or invoke an infinite loop). 161 The minimum number of invocations is at least 0. If the maximum number 162 of invocations cannot be statically determined, then it will not have a 163 value (i.e., it is set to `llvm::None`). 164 165 `operands` is a set of optional attributes that either correspond to 166 constant values for each operand of this operation or null if that 167 operand is not a constant. 168 169 This method may be called speculatively on operations where the provided 170 operands are not necessarily the same as the operation's current 171 operands. This may occur in analyses that wish to determine "what would 172 be the region invocations if these were the operands?" 173 }], 174 "void", "getRegionInvocationBounds", 175 (ins "::mlir::ArrayRef<::mlir::Attribute>":$operands, 176 "::llvm::SmallVectorImpl<::mlir::InvocationBounds> &" 177 :$invocationBounds), [{}], 178 [{ invocationBounds.append($_op->getNumRegions(), 179 ::mlir::InvocationBounds::getUnknown()); }] 180 >, 181 InterfaceMethod<[{ 182 This method is called to compare types along control-flow edges. By 183 default, the types are checked as equal. 184 }], 185 "bool", "areTypesCompatible", 186 (ins "::mlir::Type":$lhs, "::mlir::Type":$rhs), [{}], 187 [{ return lhs == rhs; }] 188 >, 189 ]; 190 191 let verify = [{ 192 static_assert(!ConcreteOp::template hasTrait<OpTrait::ZeroRegion>(), 193 "expected operation to have non-zero regions"); 194 return detail::verifyTypesAlongControlFlowEdges($_op); 195 }]; 196 let verifyWithRegions = 1; 197 198 let extraClassDeclaration = [{ 199 /// Convenience helper in case none of the operands is known. 200 void getSuccessorRegions(Optional<unsigned> index, 201 SmallVectorImpl<RegionSuccessor> ®ions) { 202 SmallVector<Attribute, 2> nullAttrs(getOperation()->getNumOperands()); 203 getSuccessorRegions(index, nullAttrs, regions); 204 } 205 }]; 206} 207 208//===----------------------------------------------------------------------===// 209// RegionBranchTerminatorOpInterface 210//===----------------------------------------------------------------------===// 211 212def RegionBranchTerminatorOpInterface : 213 OpInterface<"RegionBranchTerminatorOpInterface"> { 214 let description = [{ 215 This interface provides information for branching terminator operations 216 in the presence of a parent RegionBranchOpInterface implementation. It 217 specifies which operands are passed to which successor region. 218 }]; 219 let cppNamespace = "::mlir"; 220 221 let methods = [ 222 InterfaceMethod<[{ 223 Returns a mutable range of operands that are semantically "returned" by 224 passing them to the region successor given by `index`. If `index` is 225 None, this function returns the operands that are passed as a result to 226 the parent operation. 227 }], 228 "::mlir::MutableOperandRange", "getMutableSuccessorOperands", 229 (ins "::mlir::Optional<unsigned>":$index) 230 >, 231 InterfaceMethod<[{ 232 Returns a range of operands that are semantically "returned" by passing 233 them to the region successor given by `index`. If `index` is None, this 234 function returns the operands that are passed as a result to the parent 235 operation. 236 }], 237 "::mlir::OperandRange", "getSuccessorOperands", 238 (ins "::mlir::Optional<unsigned>":$index), [{}], 239 /*defaultImplementation=*/[{ 240 return $_op.getMutableSuccessorOperands(index); 241 }] 242 > 243 ]; 244 245 let verify = [{ 246 static_assert(ConcreteOp::template hasTrait<OpTrait::IsTerminator>(), 247 "expected operation to be a terminator"); 248 static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroResult>(), 249 "expected operation to have zero results"); 250 static_assert(ConcreteOp::template hasTrait<OpTrait::ZeroSuccessor>(), 251 "expected operation to have zero successors"); 252 return success(); 253 }]; 254} 255 256//===----------------------------------------------------------------------===// 257// ControlFlow Traits 258//===----------------------------------------------------------------------===// 259 260// Op is "return-like". 261def ReturnLike : NativeOpTrait<"ReturnLike">; 262 263#endif // MLIR_INTERFACES_CONTROLFLOWINTERFACES 264