MLIR  22.0.0git
ControlFlowInterfaces.h
Go to the documentation of this file.
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 #include "mlir/IR/Operation.h"
19 #include "llvm/ADT/PointerUnion.h"
20 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/Support/raw_ostream.h"
22 
23 namespace mlir {
24 class BranchOpInterface;
25 class RegionBranchOpInterface;
26 class RegionBranchTerminatorOpInterface;
27 
28 /// This class models how operands are forwarded to block arguments in control
29 /// flow. It consists of a number, denoting how many of the successors block
30 /// arguments are produced by the operation, followed by a range of operands
31 /// that are forwarded. The produced operands are passed to the first few
32 /// block arguments of the successor, followed by the forwarded operands.
33 /// It is unsupported to pass them in a different order.
34 ///
35 /// An example operation with both of these concepts would be a branch-on-error
36 /// operation, that internally produces an error object on the error path:
37 ///
38 /// invoke %function(%0)
39 /// label ^success ^error(%1 : i32)
40 ///
41 /// ^error(%e: !error, %arg0 : i32):
42 /// ...
43 ///
44 /// This operation would return an instance of SuccessorOperands with a produced
45 /// operand count of 1 (mapped to %e in the successor) and a forwarded
46 /// operands range consisting of %1 in the example above (mapped to %arg0 in the
47 /// successor).
49 public:
50  /// Constructs a SuccessorOperands with no produced operands that simply
51  /// forwards operands to the successor.
52  explicit SuccessorOperands(MutableOperandRange forwardedOperands);
53 
54  /// Constructs a SuccessorOperands with the given amount of produced operands
55  /// and forwarded operands.
56  SuccessorOperands(unsigned producedOperandCount,
57  MutableOperandRange forwardedOperands);
58 
59  /// Returns the amount of operands passed to the successor. This consists both
60  /// of produced operands by the operation as well as forwarded ones.
61  unsigned size() const {
62  return producedOperandCount + forwardedOperands.size();
63  }
64 
65  /// Returns true if there are no successor operands.
66  bool empty() const { return size() == 0; }
67 
68  /// Returns the amount of operands that are produced internally by the
69  /// operation. These are passed to the first few block arguments.
70  unsigned getProducedOperandCount() const { return producedOperandCount; }
71 
72  /// Returns true if the successor operand denoted by `index` is produced by
73  /// the operation.
74  bool isOperandProduced(unsigned index) const {
75  return index < producedOperandCount;
76  }
77 
78  /// Returns the Value that is passed to the successors block argument denoted
79  /// by `index`. If it is produced by the operation, no such value exists and
80  /// a null Value is returned.
81  Value operator[](unsigned index) const {
82  if (isOperandProduced(index))
83  return Value();
84  return forwardedOperands[index - producedOperandCount].get();
85  }
86 
87  /// Get the range of operands that are simply forwarded to the successor.
88  OperandRange getForwardedOperands() const { return forwardedOperands; }
89 
90  /// Get the range of operands that are simply forwarded to the successor.
92  return forwardedOperands;
93  }
94 
95  /// Get a slice of the operands forwarded to the successor. The given range
96  /// must not contain any operands produced by the operation.
97  MutableOperandRange slice(unsigned subStart, unsigned subLen) const {
98  assert(!isOperandProduced(subStart) &&
99  "can't slice operands produced by the operation");
100  return forwardedOperands.slice(subStart - producedOperandCount, subLen);
101  }
102 
103  /// Erase operands forwarded to the successor. The given range must
104  /// not contain any operands produced by the operation.
105  void erase(unsigned subStart, unsigned subLen = 1) {
106  assert(!isOperandProduced(subStart) &&
107  "can't erase operands produced by the operation");
108  forwardedOperands.erase(subStart - producedOperandCount, subLen);
109  }
110 
111  /// Add new operands that are forwarded to the successor.
112  void append(ValueRange valueRange) { forwardedOperands.append(valueRange); }
113 
114  /// Gets the index of the forwarded operand within the operation which maps
115  /// to the block argument denoted by `blockArgumentIndex`. The block argument
116  /// must be mapped to a forwarded operand.
117  unsigned getOperandIndex(unsigned blockArgumentIndex) const {
118  assert(!isOperandProduced(blockArgumentIndex) &&
119  "can't map operand produced by the operation");
120  OperandRange operands = forwardedOperands;
121  return operands.getBeginOperandIndex() +
122  (blockArgumentIndex - producedOperandCount);
123  }
124 
125 private:
126  /// Amount of operands that are produced internally within the operation and
127  /// passed to the first few block arguments.
128  unsigned producedOperandCount;
129  /// Range of operands that are forwarded to the remaining block arguments.
130  MutableOperandRange forwardedOperands;
131 };
132 
133 //===----------------------------------------------------------------------===//
134 // BranchOpInterface
135 //===----------------------------------------------------------------------===//
136 
137 namespace detail {
138 /// Return the `BlockArgument` corresponding to operand `operandIndex` in some
139 /// successor if `operandIndex` is within the range of `operands`, or
140 /// std::nullopt if `operandIndex` isn't a successor operand index.
141 std::optional<BlockArgument>
142 getBranchSuccessorArgument(const SuccessorOperands &operands,
143  unsigned operandIndex, Block *successor);
144 
145 /// Verify that the given operands match those of the given successor block.
146 LogicalResult verifyBranchSuccessorOperands(Operation *op, unsigned succNo,
147  const SuccessorOperands &operands);
148 } // namespace detail
149 
150 //===----------------------------------------------------------------------===//
151 // WeightedBranchOpInterface
152 //===----------------------------------------------------------------------===//
153 
154 namespace detail {
155 /// Verify that the branch weights attached to an operation
156 /// implementing WeightedBranchOpInterface are correct.
157 LogicalResult verifyBranchWeights(Operation *op);
158 } // namespace detail
159 
160 //===----------------------------------------------------------------------===//
161 // WeightedRegiobBranchOpInterface
162 //===----------------------------------------------------------------------===//
163 
164 namespace detail {
165 /// Verify that the region weights attached to an operation
166 /// implementing WeightedRegiobBranchOpInterface are correct.
167 LogicalResult verifyRegionBranchWeights(Operation *op);
168 } // namespace detail
169 
170 //===----------------------------------------------------------------------===//
171 // RegionBranchOpInterface
172 //===----------------------------------------------------------------------===//
173 
174 namespace detail {
175 /// Verify that types match along control flow edges described the given op.
176 LogicalResult verifyTypesAlongControlFlowEdges(Operation *op);
177 } // namespace detail
178 
179 /// This class represents a successor of a region. A region successor can either
180 /// be another region, or the parent operation. If the successor is a region,
181 /// this class represents the destination region, as well as a set of arguments
182 /// from that region that will be populated when control flows into the region.
183 /// If the successor is the parent operation, this class represents an optional
184 /// set of results that will be populated when control returns to the parent
185 /// operation.
186 ///
187 /// This interface assumes that the values from the current region that are used
188 /// to populate the successor inputs are the operands of the return-like
189 /// terminator operations in the blocks within this region.
191 public:
192  /// Initialize a successor that branches to another region of the parent
193  /// operation.
194  /// TODO: the default value for the regionInputs is somehow broken.
195  /// A region successor should have its input correctly set.
196  RegionSuccessor(Region *region, Block::BlockArgListType regionInputs = {})
197  : successor(region), inputs(regionInputs) {
198  assert(region && "Region must not be null");
199  }
200  /// Initialize a successor that branches back to/out of the parent operation.
201  /// The target must be one of the recursive parent operations.
203  : successor(successorOp), inputs(ValueRange(results)) {
204  assert(successorOp && "Successor op must not be null");
205  }
206 
207  /// Return the given region successor. Returns nullptr if the successor is the
208  /// parent operation.
209  Region *getSuccessor() const { return dyn_cast<Region *>(successor); }
210 
211  /// Return true if the successor is the parent operation.
212  bool isParent() const { return isa<Operation *>(successor); }
213 
214  /// Return the inputs to the successor that are remapped by the exit values of
215  /// the current region.
216  ValueRange getSuccessorInputs() const { return inputs; }
217 
218  bool operator==(RegionSuccessor rhs) const {
219  return successor == rhs.successor && inputs == rhs.inputs;
220  }
221 
223  return !(lhs == rhs);
224  }
225 
226 private:
228  ValueRange inputs;
229 };
230 
231 /// This class represents a point being branched from in the methods of the
232 /// `RegionBranchOpInterface`.
233 /// One can branch from one of two kinds of places:
234 /// * The parent operation (aka the `RegionBranchOpInterface` implementation)
235 /// * A RegionBranchTerminatorOpInterface inside a region within the parent
236 // operation.
238 public:
239  /// Returns an instance of `RegionBranchPoint` representing the parent
240  /// operation.
241  static constexpr RegionBranchPoint parent() { return RegionBranchPoint(); }
242 
243  /// Creates a `RegionBranchPoint` that branches from the given terminator.
244  inline RegionBranchPoint(RegionBranchTerminatorOpInterface predecessor);
245 
246  /// Explicitly stops users from constructing with `nullptr`.
247  RegionBranchPoint(std::nullptr_t) = delete;
248 
249  /// Returns true if branching from the parent op.
250  bool isParent() const { return predecessor == nullptr; }
251 
252  /// Returns the terminator if branching from a region.
253  /// A null pointer otherwise.
254  Operation *getTerminatorPredecessorOrNull() const { return predecessor; }
255 
256  /// Returns true if the two branch points are equal.
258  return lhs.predecessor == rhs.predecessor;
259  }
260 
261 private:
262  // Private constructor to encourage the use of `RegionBranchPoint::parent`.
263  constexpr RegionBranchPoint() = default;
264 
265  /// Internal encoding. Uses nullptr for representing branching from the parent
266  /// op and the region terminator being branched from otherwise.
267  Operation *predecessor = nullptr;
268 };
269 
271  return !(lhs == rhs);
272 }
273 
274 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
275  RegionBranchPoint point) {
276  if (point.isParent())
277  return os << "<from parent>";
278  return os << "<region #"
280  ->getParentRegion()
281  ->getRegionNumber()
282  << ", terminator "
284  OpPrintingFlags().skipRegions())
285  << ">";
286 }
287 
288 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
289  RegionSuccessor successor) {
290  if (successor.isParent())
291  return os << "<to parent>";
292  return os << "<to region #" << successor.getSuccessor()->getRegionNumber()
293  << " with " << successor.getSuccessorInputs().size() << " inputs>";
294 }
295 
296 /// This class represents upper and lower bounds on the number of times a region
297 /// of a `RegionBranchOpInterface` can be invoked. The lower bound is at least
298 /// zero, but the upper bound may not be known.
300 public:
301  /// Create invocation bounds. The lower bound must be at least 0 and only the
302  /// upper bound can be unknown.
303  InvocationBounds(unsigned lb, std::optional<unsigned> ub)
304  : lower(lb), upper(ub) {
305  assert((!ub || ub >= lb) && "upper bound cannot be less than lower bound");
306  }
307 
308  /// Return the lower bound.
309  unsigned getLowerBound() const { return lower; }
310 
311  /// Return the upper bound.
312  std::optional<unsigned> getUpperBound() const { return upper; }
313 
314  /// Returns the unknown invocation bounds, i.e., there is no information on
315  /// how many times a region may be invoked.
316  static InvocationBounds getUnknown() { return {0, std::nullopt}; }
317 
318 private:
319  /// The minimum number of times the successor region will be invoked.
320  unsigned lower;
321  /// The maximum number of times the successor region will be invoked or
322  /// `std::nullopt` if an upper bound is not known.
323  std::optional<unsigned> upper;
324 };
325 
326 /// Return `true` if `a` and `b` are in mutually exclusive regions as per
327 /// RegionBranchOpInterface.
328 bool insideMutuallyExclusiveRegions(Operation *a, Operation *b);
329 
330 /// Return the first enclosing region of the given op that may be executed
331 /// repetitively as per RegionBranchOpInterface or `nullptr` if no such region
332 /// exists.
333 Region *getEnclosingRepetitiveRegion(Operation *op);
334 
335 /// Return the first enclosing region of the given Value that may be executed
336 /// repetitively as per RegionBranchOpInterface or `nullptr` if no such region
337 /// exists.
338 Region *getEnclosingRepetitiveRegion(Value value);
339 
340 //===----------------------------------------------------------------------===//
341 // ControlFlow Traits
342 //===----------------------------------------------------------------------===//
343 
344 namespace OpTrait {
345 /// This trait indicates that a terminator operation is "return-like". This
346 /// means that it exits its current region and forwards its operands as "exit"
347 /// values to the parent region. Operations with this trait are not permitted to
348 /// contain successors or produce results.
349 template <typename ConcreteType>
350 struct ReturnLike : public TraitBase<ConcreteType, ReturnLike> {
351  static LogicalResult verifyTrait(Operation *op) {
352  static_assert(ConcreteType::template hasTrait<IsTerminator>(),
353  "expected operation to be a terminator");
354  static_assert(ConcreteType::template hasTrait<ZeroResults>(),
355  "expected operation to have zero results");
356  static_assert(ConcreteType::template hasTrait<ZeroSuccessors>(),
357  "expected operation to have zero successors");
358  return success();
359  }
360 };
361 } // namespace OpTrait
362 
363 } // namespace mlir
364 
365 //===----------------------------------------------------------------------===//
366 // ControlFlow Interfaces
367 //===----------------------------------------------------------------------===//
368 
369 /// Include the generated interface declarations.
370 #include "mlir/Interfaces/ControlFlowInterfaces.h.inc"
371 
372 namespace mlir {
373 inline RegionBranchPoint::RegionBranchPoint(
374  RegionBranchTerminatorOpInterface predecessor)
375  : predecessor(predecessor.getOperation()) {}
376 } // namespace mlir
377 
378 #endif // MLIR_INTERFACES_CONTROLFLOWINTERFACES_H
MutableArrayRef< BlockArgument > BlockArgListType
Definition: Block.h:85
This class represents upper and lower bounds on the number of times a region of a RegionBranchOpInter...
static InvocationBounds getUnknown()
Returns the unknown invocation bounds, i.e., there is no information on how many times a region may b...
std::optional< unsigned > getUpperBound() const
Return the upper bound.
InvocationBounds(unsigned lb, std::optional< unsigned > ub)
Create invocation bounds.
unsigned getLowerBound() const
Return the lower bound.
This class provides a mutable adaptor for a range of operands.
Definition: ValueRange.h:118
unsigned size() const
Returns the current size of the range.
Definition: ValueRange.h:156
MutableOperandRange slice(unsigned subStart, unsigned subLen, std::optional< OperandSegment > segment=std::nullopt) const
Slice this range into a sub range, with the additional operand segment.
void erase(unsigned subStart, unsigned subLen=1)
Erase the operands within the given sub-range.
void append(ValueRange values)
Append the given values to the range.
Set of flags used to control the behavior of the various IR print methods (e.g.
Helper class for implementing traits.
Definition: OpDefinition.h:378
A wrapper class that allows for printing an operation with a set of flags, useful to act as a "stream...
Definition: Operation.h:1111
This class implements the operand iterators for the Operation class.
Definition: ValueRange.h:43
unsigned getBeginOperandIndex() const
Return the operand index of the first element of this range.
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition: Operation.h:230
This class represents a point being branched from in the methods of the RegionBranchOpInterface.
RegionBranchPoint(std::nullptr_t)=delete
Explicitly stops users from constructing with nullptr.
bool isParent() const
Returns true if branching from the parent op.
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
friend bool operator==(RegionBranchPoint lhs, RegionBranchPoint rhs)
Returns true if the two branch points are equal.
Operation * getTerminatorPredecessorOrNull() const
Returns the terminator if branching from a region.
This class represents a successor of a region.
RegionSuccessor(Region *region, Block::BlockArgListType regionInputs={})
Initialize a successor that branches to another region of the parent operation.
bool operator==(RegionSuccessor rhs) const
ValueRange getSuccessorInputs() const
Return the inputs to the successor that are remapped by the exit values of the current region.
friend bool operator!=(RegionSuccessor lhs, RegionSuccessor rhs)
bool isParent() const
Return true if the successor is the parent operation.
Region * getSuccessor() const
Return the given region successor.
RegionSuccessor(Operation *successorOp, Operation::result_range results)
Initialize a successor that branches back to/out of the parent operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
unsigned getRegionNumber()
Return the number of this region in the parent operation.
Definition: Region.cpp:62
This class implements the result iterators for the Operation class.
Definition: ValueRange.h:247
This class models how operands are forwarded to block arguments in control flow.
SuccessorOperands(unsigned producedOperandCount, MutableOperandRange forwardedOperands)
Constructs a SuccessorOperands with the given amount of produced operands and forwarded operands.
MutableOperandRange slice(unsigned subStart, unsigned subLen) const
Get a slice of the operands forwarded to the successor.
void erase(unsigned subStart, unsigned subLen=1)
Erase operands forwarded to the successor.
MutableOperandRange getMutableForwardedOperands() const
Get the range of operands that are simply forwarded to the successor.
SuccessorOperands(MutableOperandRange forwardedOperands)
Constructs a SuccessorOperands with no produced operands that simply forwards operands to the success...
Value operator[](unsigned index) const
Returns the Value that is passed to the successors block argument denoted by index.
unsigned getOperandIndex(unsigned blockArgumentIndex) const
Gets the index of the forwarded operand within the operation which maps to the block argument denoted...
bool isOperandProduced(unsigned index) const
Returns true if the successor operand denoted by index is produced by the operation.
unsigned getProducedOperandCount() const
Returns the amount of operands that are produced internally by the operation.
bool empty() const
Returns true if there are no successor operands.
void append(ValueRange valueRange)
Add new operands that are forwarded to the successor.
unsigned size() const
Returns the amount of operands passed to the successor.
OperandRange getForwardedOperands() const
Get the range of operands that are simply forwarded to the successor.
This class provides an abstraction over the different types of ranges over Values.
Definition: ValueRange.h:387
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
std::optional< BlockArgument > getBranchSuccessorArgument(const SuccessorOperands &operands, unsigned operandIndex, Block *successor)
Return the BlockArgument corresponding to operand operandIndex in some successor if operandIndex is w...
LogicalResult verifyRegionBranchWeights(Operation *op)
Verify that the region weights attached to an operation implementing WeightedRegiobBranchOpInterface ...
LogicalResult verifyBranchSuccessorOperands(Operation *op, unsigned succNo, const SuccessorOperands &operands)
Verify that the given operands match those of the given successor block.
LogicalResult verifyTypesAlongControlFlowEdges(Operation *op)
Verify that types match along control flow edges described the given op.
LogicalResult verifyBranchWeights(Operation *op)
Verify that the branch weights attached to an operation implementing WeightedBranchOpInterface are co...
Include the generated interface declarations.
bool insideMutuallyExclusiveRegions(Operation *a, Operation *b)
Return true if a and b are in mutually exclusive regions as per RegionBranchOpInterface.
Region * getEnclosingRepetitiveRegion(Operation *op)
Return the first enclosing region of the given op that may be executed repetitively as per RegionBran...
bool operator!=(RegionBranchPoint lhs, RegionBranchPoint rhs)
raw_ostream & operator<<(raw_ostream &os, const AliasResult &result)
Definition: AliasAnalysis.h:78
This trait indicates that a terminator operation is "return-like".
static LogicalResult verifyTrait(Operation *op)