1 //===- ControlFlowInterfaces.h - ControlFlow Interfaces ---------*- C++ -*-===// 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 the definitions of the branch interfaces defined in 10 // `ControlFlowInterfaces.td`. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef MLIR_INTERFACES_CONTROLFLOWINTERFACES_H 15 #define MLIR_INTERFACES_CONTROLFLOWINTERFACES_H 16 17 #include "mlir/IR/OpDefinition.h" 18 19 namespace mlir { 20 class BranchOpInterface; 21 class RegionBranchOpInterface; 22 23 /// This class models how operands are forwarded to block arguments in control 24 /// flow. It consists of a number, denoting how many of the successors block 25 /// arguments are produced by the operation, followed by a range of operands 26 /// that are forwarded. The produced operands are passed to the first few 27 /// block arguments of the successor, followed by the forwarded operands. 28 /// It is unsupported to pass them in a different order. 29 /// 30 /// An example operation with both of these concepts would be a branch-on-error 31 /// operation, that internally produces an error object on the error path: 32 /// 33 /// invoke %function(%0) 34 /// label ^success ^error(%1 : i32) 35 /// 36 /// ^error(%e: !error, %arg0 : i32): 37 /// ... 38 /// 39 /// This operation would return an instance of SuccessorOperands with a produced 40 /// operand count of 1 (mapped to %e in the successor) and a forwarded 41 /// operands range consisting of %1 in the example above (mapped to %arg0 in the 42 /// successor). 43 class SuccessorOperands { 44 public: 45 /// Constructs a SuccessorOperands with no produced operands that simply 46 /// forwards operands to the successor. 47 explicit SuccessorOperands(MutableOperandRange forwardedOperands); 48 49 /// Constructs a SuccessorOperands with the given amount of produced operands 50 /// and forwarded operands. 51 SuccessorOperands(unsigned producedOperandCount, 52 MutableOperandRange forwardedOperands); 53 54 /// Returns the amount of operands passed to the successor. This consists both 55 /// of produced operands by the operation as well as forwarded ones. size()56 unsigned size() const { 57 return producedOperandCount + forwardedOperands.size(); 58 } 59 60 /// Returns true if there are no successor operands. empty()61 bool empty() const { return size() == 0; } 62 63 /// Returns the amount of operands that are produced internally by the 64 /// operation. These are passed to the first few block arguments. getProducedOperandCount()65 unsigned getProducedOperandCount() const { return producedOperandCount; } 66 67 /// Returns true if the successor operand denoted by `index` is produced by 68 /// the operation. isOperandProduced(unsigned index)69 bool isOperandProduced(unsigned index) const { 70 return index < producedOperandCount; 71 } 72 73 /// Returns the Value that is passed to the successors block argument denoted 74 /// by `index`. If it is produced by the operation, no such value exists and 75 /// a null Value is returned. 76 Value operator[](unsigned index) const { 77 if (isOperandProduced(index)) 78 return Value(); 79 return forwardedOperands[index - producedOperandCount]; 80 } 81 82 /// Get the range of operands that are simply forwarded to the successor. getForwardedOperands()83 OperandRange getForwardedOperands() const { return forwardedOperands; } 84 85 /// Get a slice of the operands forwarded to the successor. The given range 86 /// must not contain any operands produced by the operation. slice(unsigned subStart,unsigned subLen)87 MutableOperandRange slice(unsigned subStart, unsigned subLen) const { 88 assert(!isOperandProduced(subStart) && 89 "can't slice operands produced by the operation"); 90 return forwardedOperands.slice(subStart - producedOperandCount, subLen); 91 } 92 93 /// Erase operands forwarded to the successor. The given range must 94 /// not contain any operands produced by the operation. 95 void erase(unsigned subStart, unsigned subLen = 1) { 96 assert(!isOperandProduced(subStart) && 97 "can't erase operands produced by the operation"); 98 forwardedOperands.erase(subStart - producedOperandCount, subLen); 99 } 100 101 /// Add new operands that are forwarded to the successor. append(ValueRange valueRange)102 void append(ValueRange valueRange) { forwardedOperands.append(valueRange); } 103 104 /// Gets the index of the forwarded operand within the operation which maps 105 /// to the block argument denoted by `blockArgumentIndex`. The block argument 106 /// must be mapped to a forwarded operand. getOperandIndex(unsigned blockArgumentIndex)107 unsigned getOperandIndex(unsigned blockArgumentIndex) const { 108 assert(!isOperandProduced(blockArgumentIndex) && 109 "can't map operand produced by the operation"); 110 OperandRange operands = forwardedOperands; 111 return operands.getBeginOperandIndex() + 112 (blockArgumentIndex - producedOperandCount); 113 } 114 115 private: 116 /// Amount of operands that are produced internally within the operation and 117 /// passed to the first few block arguments. 118 unsigned producedOperandCount; 119 /// Range of operands that are forwarded to the remaining block arguments. 120 MutableOperandRange forwardedOperands; 121 }; 122 123 //===----------------------------------------------------------------------===// 124 // BranchOpInterface 125 //===----------------------------------------------------------------------===// 126 127 namespace detail { 128 /// Return the `BlockArgument` corresponding to operand `operandIndex` in some 129 /// successor if `operandIndex` is within the range of `operands`, or None if 130 /// `operandIndex` isn't a successor operand index. 131 Optional<BlockArgument> 132 getBranchSuccessorArgument(const SuccessorOperands &operands, 133 unsigned operandIndex, Block *successor); 134 135 /// Verify that the given operands match those of the given successor block. 136 LogicalResult verifyBranchSuccessorOperands(Operation *op, unsigned succNo, 137 const SuccessorOperands &operands); 138 } // namespace detail 139 140 //===----------------------------------------------------------------------===// 141 // RegionBranchOpInterface 142 //===----------------------------------------------------------------------===// 143 144 namespace detail { 145 /// Verify that types match along control flow edges described the given op. 146 LogicalResult verifyTypesAlongControlFlowEdges(Operation *op); 147 } // namespace detail 148 149 /// This class represents a successor of a region. A region successor can either 150 /// be another region, or the parent operation. If the successor is a region, 151 /// this class represents the destination region, as well as a set of arguments 152 /// from that region that will be populated when control flows into the region. 153 /// If the successor is the parent operation, this class represents an optional 154 /// set of results that will be populated when control returns to the parent 155 /// operation. 156 /// 157 /// This interface assumes that the values from the current region that are used 158 /// to populate the successor inputs are the operands of the return-like 159 /// terminator operations in the blocks within this region. 160 class RegionSuccessor { 161 public: 162 /// Initialize a successor that branches to another region of the parent 163 /// operation. 164 RegionSuccessor(Region *region, Block::BlockArgListType regionInputs = {}) region(region)165 : region(region), inputs(regionInputs) {} 166 /// Initialize a successor that branches back to/out of the parent operation. 167 RegionSuccessor(Optional<Operation::result_range> results = {}) 168 : inputs(results ? ValueRange(*results) : ValueRange()) {} 169 170 /// Return the given region successor. Returns nullptr if the successor is the 171 /// parent operation. getSuccessor()172 Region *getSuccessor() const { return region; } 173 174 /// Return true if the successor is the parent operation. isParent()175 bool isParent() const { return region == nullptr; } 176 177 /// Return the inputs to the successor that are remapped by the exit values of 178 /// the current region. getSuccessorInputs()179 ValueRange getSuccessorInputs() const { return inputs; } 180 181 private: 182 Region *region{nullptr}; 183 ValueRange inputs; 184 }; 185 186 /// This class represents upper and lower bounds on the number of times a region 187 /// of a `RegionBranchOpInterface` can be invoked. The lower bound is at least 188 /// zero, but the upper bound may not be known. 189 class InvocationBounds { 190 public: 191 /// Create invocation bounds. The lower bound must be at least 0 and only the 192 /// upper bound can be unknown. InvocationBounds(unsigned lb,Optional<unsigned> ub)193 InvocationBounds(unsigned lb, Optional<unsigned> ub) : lower(lb), upper(ub) { 194 assert((!ub || ub >= lb) && "upper bound cannot be less than lower bound"); 195 } 196 197 /// Return the lower bound. getLowerBound()198 unsigned getLowerBound() const { return lower; } 199 200 /// Return the upper bound. getUpperBound()201 Optional<unsigned> getUpperBound() const { return upper; } 202 203 /// Returns the unknown invocation bounds, i.e., there is no information on 204 /// how many times a region may be invoked. getUnknown()205 static InvocationBounds getUnknown() { return {0, llvm::None}; } 206 207 private: 208 /// The minimum number of times the successor region will be invoked. 209 unsigned lower; 210 /// The maximum number of times the successor region will be invoked or `None` 211 /// if an upper bound is not known. 212 Optional<unsigned> upper; 213 }; 214 215 /// Return `true` if `a` and `b` are in mutually exclusive regions as per 216 /// RegionBranchOpInterface. 217 bool insideMutuallyExclusiveRegions(Operation *a, Operation *b); 218 219 /// Return the first enclosing region of the given op that may be executed 220 /// repetitively as per RegionBranchOpInterface or `nullptr` if no such region 221 /// exists. 222 Region *getEnclosingRepetitiveRegion(Operation *op); 223 224 /// Return the first enclosing region of the given Value that may be executed 225 /// repetitively as per RegionBranchOpInterface or `nullptr` if no such region 226 /// exists. 227 Region *getEnclosingRepetitiveRegion(Value value); 228 229 //===----------------------------------------------------------------------===// 230 // RegionBranchTerminatorOpInterface 231 //===----------------------------------------------------------------------===// 232 233 /// Returns true if the given operation is either annotated with the 234 /// `ReturnLike` trait or implements the `RegionBranchTerminatorOpInterface`. 235 bool isRegionReturnLike(Operation *operation); 236 237 /// Returns the mutable operands that are passed to the region with the given 238 /// `regionIndex`. If the operation does not implement the 239 /// `RegionBranchTerminatorOpInterface` and is not marked as `ReturnLike`, the 240 /// result will be `llvm::None`. In all other cases, the resulting 241 /// `OperandRange` represents all operands that are passed to the specified 242 /// successor region. If `regionIndex` is `llvm::None`, all operands that are 243 /// passed to the parent operation will be returned. 244 Optional<MutableOperandRange> 245 getMutableRegionBranchSuccessorOperands(Operation *operation, 246 Optional<unsigned> regionIndex); 247 248 /// Returns the read only operands that are passed to the region with the given 249 /// `regionIndex`. See `getMutableRegionBranchSuccessorOperands` for more 250 /// information. 251 Optional<OperandRange> 252 getRegionBranchSuccessorOperands(Operation *operation, 253 Optional<unsigned> regionIndex); 254 255 //===----------------------------------------------------------------------===// 256 // ControlFlow Traits 257 //===----------------------------------------------------------------------===// 258 259 namespace OpTrait { 260 /// This trait indicates that a terminator operation is "return-like". This 261 /// means that it exits its current region and forwards its operands as "exit" 262 /// values to the parent region. Operations with this trait are not permitted to 263 /// contain successors or produce results. 264 template <typename ConcreteType> 265 struct ReturnLike : public TraitBase<ConcreteType, ReturnLike> { verifyTraitReturnLike266 static LogicalResult verifyTrait(Operation *op) { 267 static_assert(ConcreteType::template hasTrait<IsTerminator>(), 268 "expected operation to be a terminator"); 269 static_assert(ConcreteType::template hasTrait<ZeroResults>(), 270 "expected operation to have zero results"); 271 static_assert(ConcreteType::template hasTrait<ZeroSuccessors>(), 272 "expected operation to have zero successors"); 273 return success(); 274 } 275 }; 276 } // namespace OpTrait 277 278 } // namespace mlir 279 280 //===----------------------------------------------------------------------===// 281 // ControlFlow Interfaces 282 //===----------------------------------------------------------------------===// 283 284 /// Include the generated interface declarations. 285 #include "mlir/Interfaces/ControlFlowInterfaces.h.inc" 286 287 #endif // MLIR_INTERFACES_CONTROLFLOWINTERFACES_H 288