MLIR  22.0.0git
ControlFlowInterfaces.cpp
Go to the documentation of this file.
1 //===- ControlFlowInterfaces.cpp - ControlFlow Interfaces -----------------===//
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 #include <utility>
10 
11 #include "mlir/IR/BuiltinTypes.h"
12 #include "mlir/IR/Operation.h"
14 #include "llvm/Support/DebugLog.h"
15 
16 using namespace mlir;
17 
18 //===----------------------------------------------------------------------===//
19 // ControlFlowInterfaces
20 //===----------------------------------------------------------------------===//
21 
22 #include "mlir/Interfaces/ControlFlowInterfaces.cpp.inc"
23 
25  : producedOperandCount(0), forwardedOperands(std::move(forwardedOperands)) {
26 }
27 
28 SuccessorOperands::SuccessorOperands(unsigned int producedOperandCount,
29  MutableOperandRange forwardedOperands)
30  : producedOperandCount(producedOperandCount),
31  forwardedOperands(std::move(forwardedOperands)) {}
32 
33 //===----------------------------------------------------------------------===//
34 // BranchOpInterface
35 //===----------------------------------------------------------------------===//
36 
37 /// Returns the `BlockArgument` corresponding to operand `operandIndex` in some
38 /// successor if 'operandIndex' is within the range of 'operands', or
39 /// std::nullopt if `operandIndex` isn't a successor operand index.
40 std::optional<BlockArgument>
42  unsigned operandIndex, Block *successor) {
43  LDBG() << "Getting branch successor argument for operand index "
44  << operandIndex << " in successor block";
45 
46  OperandRange forwardedOperands = operands.getForwardedOperands();
47  // Check that the operands are valid.
48  if (forwardedOperands.empty()) {
49  LDBG() << "No forwarded operands, returning nullopt";
50  return std::nullopt;
51  }
52 
53  // Check to ensure that this operand is within the range.
54  unsigned operandsStart = forwardedOperands.getBeginOperandIndex();
55  if (operandIndex < operandsStart ||
56  operandIndex >= (operandsStart + forwardedOperands.size())) {
57  LDBG() << "Operand index " << operandIndex << " out of range ["
58  << operandsStart << ", "
59  << (operandsStart + forwardedOperands.size())
60  << "), returning nullopt";
61  return std::nullopt;
62  }
63 
64  // Index the successor.
65  unsigned argIndex =
66  operands.getProducedOperandCount() + operandIndex - operandsStart;
67  LDBG() << "Computed argument index " << argIndex << " for successor block";
68  return successor->getArgument(argIndex);
69 }
70 
71 /// Verify that the given operands match those of the given successor block.
72 LogicalResult
74  const SuccessorOperands &operands) {
75  LDBG() << "Verifying branch successor operands for successor #" << succNo
76  << " in operation " << op->getName();
77 
78  // Check the count.
79  unsigned operandCount = operands.size();
80  Block *destBB = op->getSuccessor(succNo);
81  LDBG() << "Branch has " << operandCount << " operands, target block has "
82  << destBB->getNumArguments() << " arguments";
83 
84  if (operandCount != destBB->getNumArguments())
85  return op->emitError() << "branch has " << operandCount
86  << " operands for successor #" << succNo
87  << ", but target block has "
88  << destBB->getNumArguments();
89 
90  // Check the types.
91  LDBG() << "Checking type compatibility for "
92  << (operandCount - operands.getProducedOperandCount())
93  << " forwarded operands";
94  for (unsigned i = operands.getProducedOperandCount(); i != operandCount;
95  ++i) {
96  Type operandType = operands[i].getType();
97  Type argType = destBB->getArgument(i).getType();
98  LDBG() << "Checking type compatibility: operand type " << operandType
99  << " vs argument type " << argType;
100 
101  if (!cast<BranchOpInterface>(op).areTypesCompatible(operandType, argType))
102  return op->emitError() << "type mismatch for bb argument #" << i
103  << " of successor #" << succNo;
104  }
105 
106  LDBG() << "Branch successor operand verification successful";
107  return success();
108 }
109 
110 //===----------------------------------------------------------------------===//
111 // WeightedBranchOpInterface
112 //===----------------------------------------------------------------------===//
113 
114 static LogicalResult verifyWeights(Operation *op,
115  llvm::ArrayRef<int32_t> weights,
116  std::size_t expectedWeightsNum,
117  llvm::StringRef weightAnchorName,
118  llvm::StringRef weightRefName) {
119  if (weights.empty())
120  return success();
121 
122  if (weights.size() != expectedWeightsNum)
123  return op->emitError() << "expects number of " << weightAnchorName
124  << " weights to match number of " << weightRefName
125  << ": " << weights.size() << " vs "
126  << expectedWeightsNum;
127 
128  if (llvm::all_of(weights, [](int32_t value) { return value == 0; }))
129  return op->emitError() << "branch weights cannot all be zero";
130 
131  return success();
132 }
133 
135  llvm::ArrayRef<int32_t> weights =
136  cast<WeightedBranchOpInterface>(op).getWeights();
137  return verifyWeights(op, weights, op->getNumSuccessors(), "branch",
138  "successors");
139 }
140 
141 //===----------------------------------------------------------------------===//
142 // WeightedRegionBranchOpInterface
143 //===----------------------------------------------------------------------===//
144 
146  llvm::ArrayRef<int32_t> weights =
147  cast<WeightedRegionBranchOpInterface>(op).getWeights();
148  return verifyWeights(op, weights, op->getNumRegions(), "region", "regions");
149 }
150 
151 //===----------------------------------------------------------------------===//
152 // RegionBranchOpInterface
153 //===----------------------------------------------------------------------===//
154 
156  RegionBranchPoint sourceNo,
157  RegionSuccessor succRegionNo) {
158  diag << "from ";
159  if (Operation *op = sourceNo.getTerminatorPredecessorOrNull())
160  diag << "Operation " << op->getName();
161  else
162  diag << "parent operands";
163 
164  diag << " to ";
165  if (Region *region = succRegionNo.getSuccessor())
166  diag << "Region #" << region->getRegionNumber();
167  else
168  diag << "parent results";
169  return diag;
170 }
171 
172 /// Verify that types match along all region control flow edges originating from
173 /// `sourcePoint`. `getInputsTypesForRegion` is a function that returns the
174 /// types of the inputs that flow to a successor region.
175 static LogicalResult
176 verifyTypesAlongAllEdges(RegionBranchOpInterface branchOp,
177  RegionBranchPoint sourcePoint,
178  function_ref<FailureOr<TypeRange>(RegionSuccessor)>
179  getInputsTypesForRegion) {
181  branchOp.getSuccessorRegions(sourcePoint, successors);
182 
183  for (RegionSuccessor &succ : successors) {
184  FailureOr<TypeRange> sourceTypes = getInputsTypesForRegion(succ);
185  if (failed(sourceTypes))
186  return failure();
187 
188  TypeRange succInputsTypes = succ.getSuccessorInputs().getTypes();
189  if (sourceTypes->size() != succInputsTypes.size()) {
191  branchOp->emitOpError("region control flow edge ");
192  std::string succStr;
193  llvm::raw_string_ostream os(succStr);
194  os << succ;
195  return printRegionEdgeName(diag, sourcePoint, succ)
196  << ": source has " << sourceTypes->size()
197  << " operands, but target successor " << os.str() << " needs "
198  << succInputsTypes.size();
199  }
200 
201  for (const auto &typesIdx :
202  llvm::enumerate(llvm::zip(*sourceTypes, succInputsTypes))) {
203  Type sourceType = std::get<0>(typesIdx.value());
204  Type inputType = std::get<1>(typesIdx.value());
205 
206  if (!branchOp.areTypesCompatible(sourceType, inputType)) {
208  branchOp->emitOpError("along control flow edge ");
209  return printRegionEdgeName(diag, sourcePoint, succ)
210  << ": source type #" << typesIdx.index() << " " << sourceType
211  << " should match input type #" << typesIdx.index() << " "
212  << inputType;
213  }
214  }
215  }
216 
217  return success();
218 }
219 
220 /// Verify that types match along control flow edges described the given op.
222  auto regionInterface = cast<RegionBranchOpInterface>(op);
223 
224  auto inputTypesFromParent = [&](RegionSuccessor successor) -> TypeRange {
225  return regionInterface.getEntrySuccessorOperands(successor).getTypes();
226  };
227 
228  // Verify types along control flow edges originating from the parent.
230  regionInterface, RegionBranchPoint::parent(), inputTypesFromParent)))
231  return failure();
232 
233  // Verify types along control flow edges originating from each region.
234  for (Region &region : op->getRegions()) {
235  // Collect all return-like terminators in the region.
237  for (Block &block : region)
238  if (!block.empty())
239  if (auto terminator =
240  dyn_cast<RegionBranchTerminatorOpInterface>(block.back()))
241  regionReturnOps.push_back(terminator);
242 
243  // If there is no return-like terminator, the op itself should verify
244  // type consistency.
245  if (regionReturnOps.empty())
246  continue;
247 
248  // Verify types along control flow edges originating from each return-like
249  // terminator.
250  for (RegionBranchTerminatorOpInterface regionReturnOp : regionReturnOps) {
251 
252  auto inputTypesForRegion =
253  [&](RegionSuccessor successor) -> FailureOr<TypeRange> {
254  OperandRange terminatorOperands =
255  regionReturnOp.getSuccessorOperands(successor);
256  return TypeRange(terminatorOperands.getTypes());
257  };
258  if (failed(verifyTypesAlongAllEdges(regionInterface, regionReturnOp,
259  inputTypesForRegion)))
260  return failure();
261  }
262  }
263 
264  return success();
265 }
266 
267 /// Stop condition for `traverseRegionGraph`. The traversal is interrupted if
268 /// this function returns "true" for a successor region. The first parameter is
269 /// the successor region. The second parameter indicates all already visited
270 /// regions.
272 
273 /// Traverse the region graph starting at `begin`. The traversal is interrupted
274 /// if `stopCondition` evaluates to "true" for a successor region. In that case,
275 /// this function returns "true". Otherwise, if the traversal was not
276 /// interrupted, this function returns "false".
277 static bool traverseRegionGraph(Region *begin,
278  StopConditionFn stopConditionFn) {
279  auto op = cast<RegionBranchOpInterface>(begin->getParentOp());
280  LDBG() << "Starting region graph traversal from region #"
281  << begin->getRegionNumber() << " in operation " << op->getName();
282 
283  SmallVector<bool> visited(op->getNumRegions(), false);
284  visited[begin->getRegionNumber()] = true;
285  LDBG() << "Initialized visited array with " << op->getNumRegions()
286  << " regions";
287 
288  // Retrieve all successors of the region and enqueue them in the worklist.
289  SmallVector<Region *> worklist;
290  auto enqueueAllSuccessors = [&](Region *region) {
291  LDBG() << "Enqueuing successors for region #" << region->getRegionNumber();
292  SmallVector<Attribute> operandAttributes(op->getNumOperands());
293  for (Block &block : *region) {
294  if (block.empty())
295  continue;
296  auto terminator =
297  dyn_cast<RegionBranchTerminatorOpInterface>(block.back());
298  if (!terminator)
299  continue;
300  SmallVector<RegionSuccessor> successors;
301  operandAttributes.resize(terminator->getNumOperands());
302  terminator.getSuccessorRegions(operandAttributes, successors);
303  LDBG() << "Found " << successors.size()
304  << " successors from terminator in block";
305  for (RegionSuccessor successor : successors) {
306  if (!successor.isParent()) {
307  worklist.push_back(successor.getSuccessor());
308  LDBG() << "Added region #"
309  << successor.getSuccessor()->getRegionNumber()
310  << " to worklist";
311  } else {
312  LDBG() << "Skipping parent successor";
313  }
314  }
315  }
316  };
317  enqueueAllSuccessors(begin);
318  LDBG() << "Initial worklist size: " << worklist.size();
319 
320  // Process all regions in the worklist via DFS.
321  while (!worklist.empty()) {
322  Region *nextRegion = worklist.pop_back_val();
323  LDBG() << "Processing region #" << nextRegion->getRegionNumber()
324  << " from worklist (remaining: " << worklist.size() << ")";
325 
326  if (stopConditionFn(nextRegion, visited)) {
327  LDBG() << "Stop condition met for region #"
328  << nextRegion->getRegionNumber() << ", returning true";
329  return true;
330  }
331  llvm::dbgs() << "Region: " << nextRegion << "\n";
332  if (!nextRegion->getParentOp()) {
333  llvm::errs() << "Region " << *nextRegion << " has no parent op\n";
334  return false;
335  }
336  if (visited[nextRegion->getRegionNumber()]) {
337  LDBG() << "Region #" << nextRegion->getRegionNumber()
338  << " already visited, skipping";
339  continue;
340  }
341  visited[nextRegion->getRegionNumber()] = true;
342  LDBG() << "Marking region #" << nextRegion->getRegionNumber()
343  << " as visited";
344  enqueueAllSuccessors(nextRegion);
345  }
346 
347  LDBG() << "Traversal completed, returning false";
348  return false;
349 }
350 
351 /// Return `true` if region `r` is reachable from region `begin` according to
352 /// the RegionBranchOpInterface (by taking a branch).
353 static bool isRegionReachable(Region *begin, Region *r) {
354  assert(begin->getParentOp() == r->getParentOp() &&
355  "expected that both regions belong to the same op");
356  return traverseRegionGraph(begin,
357  [&](Region *nextRegion, ArrayRef<bool> visited) {
358  // Interrupt traversal if `r` was reached.
359  return nextRegion == r;
360  });
361 }
362 
363 /// Return `true` if `a` and `b` are in mutually exclusive regions.
364 ///
365 /// 1. Find the first common of `a` and `b` (ancestor) that implements
366 /// RegionBranchOpInterface.
367 /// 2. Determine the regions `regionA` and `regionB` in which `a` and `b` are
368 /// contained.
369 /// 3. Check if `regionA` and `regionB` are mutually exclusive. They are
370 /// mutually exclusive if they are not reachable from each other as per
371 /// RegionBranchOpInterface::getSuccessorRegions.
373  LDBG() << "Checking if operations are in mutually exclusive regions: "
374  << a->getName() << " and " << b->getName();
375 
376  assert(a && "expected non-empty operation");
377  assert(b && "expected non-empty operation");
378 
379  auto branchOp = a->getParentOfType<RegionBranchOpInterface>();
380  while (branchOp) {
381  LDBG() << "Checking branch operation " << branchOp->getName();
382 
383  // Check if b is inside branchOp. (We already know that a is.)
384  if (!branchOp->isProperAncestor(b)) {
385  LDBG() << "Operation b is not inside branchOp, checking next ancestor";
386  // Check next enclosing RegionBranchOpInterface.
387  branchOp = branchOp->getParentOfType<RegionBranchOpInterface>();
388  continue;
389  }
390 
391  LDBG() << "Both operations are inside branchOp, finding their regions";
392 
393  // b is contained in branchOp. Retrieve the regions in which `a` and `b`
394  // are contained.
395  Region *regionA = nullptr, *regionB = nullptr;
396  for (Region &r : branchOp->getRegions()) {
397  if (r.findAncestorOpInRegion(*a)) {
398  assert(!regionA && "already found a region for a");
399  regionA = &r;
400  LDBG() << "Found region #" << r.getRegionNumber() << " for operation a";
401  }
402  if (r.findAncestorOpInRegion(*b)) {
403  assert(!regionB && "already found a region for b");
404  regionB = &r;
405  LDBG() << "Found region #" << r.getRegionNumber() << " for operation b";
406  }
407  }
408  assert(regionA && regionB && "could not find region of op");
409 
410  LDBG() << "Region A: #" << regionA->getRegionNumber() << ", Region B: #"
411  << regionB->getRegionNumber();
412 
413  // `a` and `b` are in mutually exclusive regions if both regions are
414  // distinct and neither region is reachable from the other region.
415  bool regionsAreDistinct = (regionA != regionB);
416  bool aNotReachableFromB = !isRegionReachable(regionA, regionB);
417  bool bNotReachableFromA = !isRegionReachable(regionB, regionA);
418 
419  LDBG() << "Regions distinct: " << regionsAreDistinct
420  << ", A not reachable from B: " << aNotReachableFromB
421  << ", B not reachable from A: " << bNotReachableFromA;
422 
423  bool mutuallyExclusive =
424  regionsAreDistinct && aNotReachableFromB && bNotReachableFromA;
425  LDBG() << "Operations are mutually exclusive: " << mutuallyExclusive;
426 
427  return mutuallyExclusive;
428  }
429 
430  // Could not find a common RegionBranchOpInterface among a's and b's
431  // ancestors.
432  LDBG() << "No common RegionBranchOpInterface found, operations are not "
433  "mutually exclusive";
434  return false;
435 }
436 
437 bool RegionBranchOpInterface::isRepetitiveRegion(unsigned index) {
438  LDBG() << "Checking if region #" << index << " is repetitive in operation "
439  << getOperation()->getName();
440 
441  Region *region = &getOperation()->getRegion(index);
442  bool isRepetitive = isRegionReachable(region, region);
443 
444  LDBG() << "Region #" << index << " is repetitive: " << isRepetitive;
445  return isRepetitive;
446 }
447 
448 bool RegionBranchOpInterface::hasLoop() {
449  LDBG() << "Checking if operation " << getOperation()->getName()
450  << " has loops";
451 
452  SmallVector<RegionSuccessor> entryRegions;
453  getSuccessorRegions(RegionBranchPoint::parent(), entryRegions);
454  LDBG() << "Found " << entryRegions.size() << " entry regions";
455 
456  for (RegionSuccessor successor : entryRegions) {
457  if (!successor.isParent()) {
458  LDBG() << "Checking entry region #"
459  << successor.getSuccessor()->getRegionNumber() << " for loops";
460 
461  bool hasLoop =
462  traverseRegionGraph(successor.getSuccessor(),
463  [](Region *nextRegion, ArrayRef<bool> visited) {
464  // Interrupt traversal if the region was already
465  // visited.
466  return visited[nextRegion->getRegionNumber()];
467  });
468 
469  if (hasLoop) {
470  LDBG() << "Found loop in entry region #"
471  << successor.getSuccessor()->getRegionNumber();
472  return true;
473  }
474  } else {
475  LDBG() << "Skipping parent successor";
476  }
477  }
478 
479  LDBG() << "No loops found in operation";
480  return false;
481 }
482 
484  LDBG() << "Finding enclosing repetitive region for operation "
485  << op->getName();
486 
487  while (Region *region = op->getParentRegion()) {
488  LDBG() << "Checking region #" << region->getRegionNumber()
489  << " in operation " << region->getParentOp()->getName();
490 
491  op = region->getParentOp();
492  if (auto branchOp = dyn_cast<RegionBranchOpInterface>(op)) {
493  LDBG()
494  << "Found RegionBranchOpInterface, checking if region is repetitive";
495  if (branchOp.isRepetitiveRegion(region->getRegionNumber())) {
496  LDBG() << "Found repetitive region #" << region->getRegionNumber();
497  return region;
498  }
499  } else {
500  LDBG() << "Parent operation does not implement RegionBranchOpInterface";
501  }
502  }
503 
504  LDBG() << "No enclosing repetitive region found";
505  return nullptr;
506 }
507 
509  LDBG() << "Finding enclosing repetitive region for value";
510 
511  Region *region = value.getParentRegion();
512  while (region) {
513  LDBG() << "Checking region #" << region->getRegionNumber()
514  << " in operation " << region->getParentOp()->getName();
515 
516  Operation *op = region->getParentOp();
517  if (auto branchOp = dyn_cast<RegionBranchOpInterface>(op)) {
518  LDBG()
519  << "Found RegionBranchOpInterface, checking if region is repetitive";
520  if (branchOp.isRepetitiveRegion(region->getRegionNumber())) {
521  LDBG() << "Found repetitive region #" << region->getRegionNumber();
522  return region;
523  }
524  } else {
525  LDBG() << "Parent operation does not implement RegionBranchOpInterface";
526  }
527  region = op->getParentRegion();
528  }
529 
530  LDBG() << "No enclosing repetitive region found for value";
531  return nullptr;
532 }
static bool isRepetitiveRegion(Region *region, const BufferizationOptions &options)
static LogicalResult verifyWeights(Operation *op, llvm::ArrayRef< int32_t > weights, std::size_t expectedWeightsNum, llvm::StringRef weightAnchorName, llvm::StringRef weightRefName)
static bool traverseRegionGraph(Region *begin, StopConditionFn stopConditionFn)
Traverse the region graph starting at begin.
static LogicalResult verifyTypesAlongAllEdges(RegionBranchOpInterface branchOp, RegionBranchPoint sourcePoint, function_ref< FailureOr< TypeRange >(RegionSuccessor)> getInputsTypesForRegion)
Verify that types match along all region control flow edges originating from sourcePoint.
static InFlightDiagnostic & printRegionEdgeName(InFlightDiagnostic &diag, RegionBranchPoint sourceNo, RegionSuccessor succRegionNo)
static bool isRegionReachable(Region *begin, Region *r)
Return true if region r is reachable from region begin according to the RegionBranchOpInterface (by t...
static std::string diag(const llvm::Value &value)
Block represents an ordered list of Operations.
Definition: Block.h:33
BlockArgument getArgument(unsigned i)
Definition: Block.h:129
unsigned getNumArguments()
Definition: Block.h:128
This class represents a diagnostic that is inflight and set to be reported.
Definition: Diagnostics.h:316
This class provides a mutable adaptor for a range of operands.
Definition: ValueRange.h:118
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.
type_range getTypes() const
Definition: ValueRange.cpp:28
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
Block * getSuccessor(unsigned index)
Definition: Operation.h:708
unsigned getNumSuccessors()
Definition: Operation.h:706
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition: Operation.h:674
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:268
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition: Operation.h:238
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition: Operation.h:677
OperationName getName()
The name of an operation is the key identifier for it.
Definition: Operation.h:119
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.
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
Operation * getTerminatorPredecessorOrNull() const
Returns the terminator if branching from a region.
This class represents a successor of a region.
Region * getSuccessor() const
Return the given region successor.
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
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition: Region.h:200
This class models how operands are forwarded to block arguments in control flow.
SuccessorOperands(MutableOperandRange forwardedOperands)
Constructs a SuccessorOperands with no produced operands that simply forwards operands to the success...
unsigned getProducedOperandCount() const
Returns the amount of operands that are produced internally by the operation.
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 various different ranges of value types.
Definition: TypeRange.h:37
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
Type getType() const
Return the type of this value.
Definition: Value.h:105
Region * getParentRegion()
Return the Region in which this Value is defined.
Definition: Value.cpp:39
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.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:344
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...
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:561
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...