MLIR  21.0.0git
AsyncRuntimeRefCounting.cpp
Go to the documentation of this file.
1 //===- AsyncRuntimeRefCounting.cpp - Async Runtime Ref Counting -----------===//
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 implements automatic reference counting for Async runtime
10 // operations and types.
11 //
12 //===----------------------------------------------------------------------===//
13 
15 
16 #include "mlir/Analysis/Liveness.h"
21 #include "mlir/IR/PatternMatch.h"
23 #include "llvm/ADT/SmallSet.h"
24 
25 namespace mlir {
26 #define GEN_PASS_DEF_ASYNCRUNTIMEREFCOUNTINGPASS
27 #define GEN_PASS_DEF_ASYNCRUNTIMEPOLICYBASEDREFCOUNTINGPASS
28 #include "mlir/Dialect/Async/Passes.h.inc"
29 } // namespace mlir
30 
31 #define DEBUG_TYPE "async-runtime-ref-counting"
32 
33 using namespace mlir;
34 using namespace mlir::async;
35 
36 //===----------------------------------------------------------------------===//
37 // Utility functions shared by reference counting passes.
38 //===----------------------------------------------------------------------===//
39 
40 // Drop the reference count immediately if the value has no uses.
41 static LogicalResult dropRefIfNoUses(Value value, unsigned count = 1) {
42  if (!value.getUses().empty())
43  return failure();
44 
45  OpBuilder b(value.getContext());
46 
47  // Set insertion point after the operation producing a value, or at the
48  // beginning of the block if the value defined by the block argument.
49  if (Operation *op = value.getDefiningOp())
51  else
53 
54  b.create<RuntimeDropRefOp>(value.getLoc(), value, b.getI64IntegerAttr(1));
55  return success();
56 }
57 
58 // Calls `addRefCounting` for every reference counted value defined by the
59 // operation `op` (block arguments and values defined in nested regions).
60 static LogicalResult walkReferenceCountedValues(
61  Operation *op, llvm::function_ref<LogicalResult(Value)> addRefCounting) {
62  // Check that we do not have high level async operations in the IR because
63  // otherwise reference counting will produce incorrect results after high
64  // level async operations will be lowered to `async.runtime`
65  WalkResult checkNoAsyncWalk = op->walk([&](Operation *op) -> WalkResult {
66  if (!isa<ExecuteOp, AwaitOp, AwaitAllOp, YieldOp>(op))
67  return WalkResult::advance();
68 
69  return op->emitError()
70  << "async operations must be lowered to async runtime operations";
71  });
72 
73  if (checkNoAsyncWalk.wasInterrupted())
74  return failure();
75 
76  // Add reference counting to block arguments.
77  WalkResult blockWalk = op->walk([&](Block *block) -> WalkResult {
78  for (BlockArgument arg : block->getArguments())
79  if (isRefCounted(arg.getType()))
80  if (failed(addRefCounting(arg)))
81  return WalkResult::interrupt();
82 
83  return WalkResult::advance();
84  });
85 
86  if (blockWalk.wasInterrupted())
87  return failure();
88 
89  // Add reference counting to operation results.
90  WalkResult opWalk = op->walk([&](Operation *op) -> WalkResult {
91  for (unsigned i = 0; i < op->getNumResults(); ++i)
92  if (isRefCounted(op->getResultTypes()[i]))
93  if (failed(addRefCounting(op->getResult(i))))
94  return WalkResult::interrupt();
95 
96  return WalkResult::advance();
97  });
98 
99  if (opWalk.wasInterrupted())
100  return failure();
101 
102  return success();
103 }
104 
105 //===----------------------------------------------------------------------===//
106 // Automatic reference counting based on the liveness analysis.
107 //===----------------------------------------------------------------------===//
108 
109 namespace {
110 
111 class AsyncRuntimeRefCountingPass
112  : public impl::AsyncRuntimeRefCountingPassBase<
113  AsyncRuntimeRefCountingPass> {
114 public:
115  AsyncRuntimeRefCountingPass() = default;
116  void runOnOperation() override;
117 
118 private:
119  /// Adds an automatic reference counting to the `value`.
120  ///
121  /// All values (token, group or value) are semantically created with a
122  /// reference count of +1 and it is the responsibility of the async value user
123  /// to place the `add_ref` and `drop_ref` operations to ensure that the value
124  /// is destroyed after the last use.
125  ///
126  /// The function returns failure if it can't deduce the locations where
127  /// to place the reference counting operations.
128  ///
129  /// Async values "semantically created" when:
130  /// 1. Operation returns async result (e.g. `async.runtime.create`)
131  /// 2. Async value passed in as a block argument (or function argument,
132  /// because function arguments are just entry block arguments)
133  ///
134  /// Passing async value as a function argument (or block argument) does not
135  /// really mean that a new async value is created, it only means that the
136  /// caller of a function transfered ownership of `+1` reference to the callee.
137  /// It is convenient to think that from the callee perspective async value was
138  /// "created" with `+1` reference by the block argument.
139  ///
140  /// Automatic reference counting algorithm outline:
141  ///
142  /// #1 Insert `drop_ref` operations after last use of the `value`.
143  /// #2 Insert `add_ref` operations before functions calls with reference
144  /// counted `value` operand (newly created `+1` reference will be
145  /// transferred to the callee).
146  /// #3 Verify that divergent control flow does not lead to leaked reference
147  /// counted objects.
148  ///
149  /// Async runtime reference counting optimization pass will optimize away
150  /// some of the redundant `add_ref` and `drop_ref` operations inserted by this
151  /// strategy (see `async-runtime-ref-counting-opt`).
152  LogicalResult addAutomaticRefCounting(Value value);
153 
154  /// (#1) Adds the `drop_ref` operation after the last use of the `value`
155  /// relying on the liveness analysis.
156  ///
157  /// If the `value` is in the block `liveIn` set and it is not in the block
158  /// `liveOut` set, it means that it "dies" in the block. We find the last
159  /// use of the value in such block and:
160  ///
161  /// 1. If the last user is a `ReturnLike` operation we do nothing, because
162  /// it forwards the ownership to the caller.
163  /// 2. Otherwise we add a `drop_ref` operation immediately after the last
164  /// use.
165  LogicalResult addDropRefAfterLastUse(Value value);
166 
167  /// (#2) Adds the `add_ref` operation before the function call taking `value`
168  /// operand to ensure that the value passed to the function entry block
169  /// has a `+1` reference count.
170  LogicalResult addAddRefBeforeFunctionCall(Value value);
171 
172  /// (#3) Adds the `drop_ref` operation to account for successor blocks with
173  /// divergent `liveIn` property: `value` is not in the `liveIn` set of all
174  /// successor blocks.
175  ///
176  /// Example:
177  ///
178  /// ^entry:
179  /// %token = async.runtime.create : !async.token
180  /// cf.cond_br %cond, ^bb1, ^bb2
181  /// ^bb1:
182  /// async.runtime.await %token
183  /// async.runtime.drop_ref %token
184  /// cf.br ^bb2
185  /// ^bb2:
186  /// return
187  ///
188  /// In this example ^bb2 does not have `value` in the `liveIn` set, so we have
189  /// to branch into a special "reference counting block" from the ^entry that
190  /// will have a `drop_ref` operation, and then branch into the ^bb2.
191  ///
192  /// After transformation:
193  ///
194  /// ^entry:
195  /// %token = async.runtime.create : !async.token
196  /// cf.cond_br %cond, ^bb1, ^reference_counting
197  /// ^bb1:
198  /// async.runtime.await %token
199  /// async.runtime.drop_ref %token
200  /// cf.br ^bb2
201  /// ^reference_counting:
202  /// async.runtime.drop_ref %token
203  /// cf.br ^bb2
204  /// ^bb2:
205  /// return
206  ///
207  /// An exception to this rule are blocks with `async.coro.suspend` terminator,
208  /// because in Async to LLVM lowering it is guaranteed that the control flow
209  /// will jump into the resume block, and then follow into the cleanup and
210  /// suspend blocks.
211  ///
212  /// Example:
213  ///
214  /// ^entry(%value: !async.value<f32>):
215  /// async.runtime.await_and_resume %value, %hdl : !async.value<f32>
216  /// async.coro.suspend %ret, ^suspend, ^resume, ^cleanup
217  /// ^resume:
218  /// %0 = async.runtime.load %value
219  /// cf.br ^cleanup
220  /// ^cleanup:
221  /// ...
222  /// ^suspend:
223  /// ...
224  ///
225  /// Although cleanup and suspend blocks do not have the `value` in the
226  /// `liveIn` set, it is guaranteed that execution will eventually continue in
227  /// the resume block (we never explicitly destroy coroutines).
228  LogicalResult addDropRefInDivergentLivenessSuccessor(Value value);
229 };
230 
231 } // namespace
232 
233 LogicalResult AsyncRuntimeRefCountingPass::addDropRefAfterLastUse(Value value) {
234  OpBuilder builder(value.getContext());
235  Location loc = value.getLoc();
236 
237  // Use liveness analysis to find the placement of `drop_ref`operation.
238  auto &liveness = getAnalysis<Liveness>();
239 
240  // We analyse only the blocks of the region that defines the `value`, and do
241  // not check nested blocks attached to operations.
242  //
243  // By analyzing only the `definingRegion` CFG we potentially loose an
244  // opportunity to drop the reference count earlier and can extend the lifetime
245  // of reference counted value longer then it is really required.
246  //
247  // We also assume that all nested regions finish their execution before the
248  // completion of the owner operation. The only exception to this rule is
249  // `async.execute` operation, and we verify that they are lowered to the
250  // `async.runtime` operations before adding automatic reference counting.
251  Region *definingRegion = value.getParentRegion();
252 
253  // Last users of the `value` inside all blocks where the value dies.
254  llvm::SmallSet<Operation *, 4> lastUsers;
255 
256  // Find blocks in the `definingRegion` that have users of the `value` (if
257  // there are multiple users in the block, which one will be selected is
258  // undefined). User operation might be not the actual user of the value, but
259  // the operation in the block that has a "real user" in one of the attached
260  // regions.
261  llvm::DenseMap<Block *, Operation *> usersInTheBlocks;
262 
263  for (Operation *user : value.getUsers()) {
264  Block *userBlock = user->getBlock();
265  Block *ancestor = definingRegion->findAncestorBlockInRegion(*userBlock);
266  usersInTheBlocks[ancestor] = ancestor->findAncestorOpInBlock(*user);
267  assert(ancestor && "ancestor block must be not null");
268  assert(usersInTheBlocks[ancestor] && "ancestor op must be not null");
269  }
270 
271  // Find blocks where the `value` dies: the value is in `liveIn` set and not
272  // in the `liveOut` set. We place `drop_ref` immediately after the last use
273  // of the `value` in such regions (after handling few special cases).
274  //
275  // We do not traverse all the blocks in the `definingRegion`, because the
276  // `value` can be in the live in set only if it has users in the block, or it
277  // is defined in the block.
278  //
279  // Values with zero users (only definition) handled explicitly above.
280  for (auto &blockAndUser : usersInTheBlocks) {
281  Block *block = blockAndUser.getFirst();
282  Operation *userInTheBlock = blockAndUser.getSecond();
283 
284  const LivenessBlockInfo *blockLiveness = liveness.getLiveness(block);
285 
286  // Value must be in the live input set or defined in the block.
287  assert(blockLiveness->isLiveIn(value) ||
288  blockLiveness->getBlock() == value.getParentBlock());
289 
290  // If value is in the live out set, it means it doesn't "die" in the block.
291  if (blockLiveness->isLiveOut(value))
292  continue;
293 
294  // At this point we proved that `value` dies in the `block`. Find the last
295  // use of the `value` inside the `block`, this is where it "dies".
296  Operation *lastUser = blockLiveness->getEndOperation(value, userInTheBlock);
297  assert(lastUsers.count(lastUser) == 0 && "last users must be unique");
298  lastUsers.insert(lastUser);
299  }
300 
301  // Process all the last users of the `value` inside each block where the value
302  // dies.
303  for (Operation *lastUser : lastUsers) {
304  // Return like operations forward reference count.
305  if (lastUser->hasTrait<OpTrait::ReturnLike>())
306  continue;
307 
308  // We can't currently handle other types of terminators.
309  if (lastUser->hasTrait<OpTrait::IsTerminator>())
310  return lastUser->emitError() << "async reference counting can't handle "
311  "terminators that are not ReturnLike";
312 
313  // Add a drop_ref immediately after the last user.
314  builder.setInsertionPointAfter(lastUser);
315  builder.create<RuntimeDropRefOp>(loc, value, builder.getI64IntegerAttr(1));
316  }
317 
318  return success();
319 }
320 
321 LogicalResult
322 AsyncRuntimeRefCountingPass::addAddRefBeforeFunctionCall(Value value) {
323  OpBuilder builder(value.getContext());
324  Location loc = value.getLoc();
325 
326  for (Operation *user : value.getUsers()) {
327  if (!isa<func::CallOp>(user))
328  continue;
329 
330  // Add a reference before the function call to pass the value at `+1`
331  // reference to the function entry block.
332  builder.setInsertionPoint(user);
333  builder.create<RuntimeAddRefOp>(loc, value, builder.getI64IntegerAttr(1));
334  }
335 
336  return success();
337 }
338 
339 LogicalResult
340 AsyncRuntimeRefCountingPass::addDropRefInDivergentLivenessSuccessor(
341  Value value) {
342  using BlockSet = llvm::SmallPtrSet<Block *, 4>;
343 
344  OpBuilder builder(value.getContext());
345 
346  // If a block has successors with different `liveIn` property of the `value`,
347  // record block successors that do not thave the `value` in the `liveIn` set.
348  llvm::SmallDenseMap<Block *, BlockSet> divergentLivenessBlocks;
349 
350  // Use liveness analysis to find the placement of `drop_ref`operation.
351  auto &liveness = getAnalysis<Liveness>();
352 
353  // Because we only add `drop_ref` operations to the region that defines the
354  // `value` we can only process CFG for the same region.
355  Region *definingRegion = value.getParentRegion();
356 
357  // Collect blocks with successors with mismatching `liveIn` sets.
358  for (Block &block : definingRegion->getBlocks()) {
359  const LivenessBlockInfo *blockLiveness = liveness.getLiveness(&block);
360 
361  // Skip the block if value is not in the `liveOut` set.
362  if (!blockLiveness || !blockLiveness->isLiveOut(value))
363  continue;
364 
365  BlockSet liveInSuccessors; // `value` is in `liveIn` set
366  BlockSet noLiveInSuccessors; // `value` is not in the `liveIn` set
367 
368  // Collect successors that do not have `value` in the `liveIn` set.
369  for (Block *successor : block.getSuccessors()) {
370  const LivenessBlockInfo *succLiveness = liveness.getLiveness(successor);
371  if (succLiveness && succLiveness->isLiveIn(value))
372  liveInSuccessors.insert(successor);
373  else
374  noLiveInSuccessors.insert(successor);
375  }
376 
377  // Block has successors with different `liveIn` property of the `value`.
378  if (!liveInSuccessors.empty() && !noLiveInSuccessors.empty())
379  divergentLivenessBlocks.try_emplace(&block, noLiveInSuccessors);
380  }
381 
382  // Try to insert `dropRef` operations to handle blocks with divergent liveness
383  // in successors blocks.
384  for (auto kv : divergentLivenessBlocks) {
385  Block *block = kv.getFirst();
386  BlockSet &successors = kv.getSecond();
387 
388  // Coroutine suspension is a special case terminator for wich we do not
389  // need to create additional reference counting (see details above).
390  Operation *terminator = block->getTerminator();
391  if (isa<CoroSuspendOp>(terminator))
392  continue;
393 
394  // We only support successor blocks with empty block argument list.
395  auto hasArgs = [](Block *block) { return !block->getArguments().empty(); };
396  if (llvm::any_of(successors, hasArgs))
397  return terminator->emitOpError()
398  << "successor have different `liveIn` property of the reference "
399  "counted value";
400 
401  // Make sure that `dropRef` operation is called when branched into the
402  // successor block without `value` in the `liveIn` set.
403  for (Block *successor : successors) {
404  // If successor has a unique predecessor, it is safe to create `dropRef`
405  // operations directly in the successor block.
406  //
407  // Otherwise we need to create a special block for reference counting
408  // operations, and branch from it to the original successor block.
409  Block *refCountingBlock = nullptr;
410 
411  if (successor->getUniquePredecessor() == block) {
412  refCountingBlock = successor;
413  } else {
414  refCountingBlock = &successor->getParent()->emplaceBlock();
415  refCountingBlock->moveBefore(successor);
416  OpBuilder builder = OpBuilder::atBlockEnd(refCountingBlock);
417  builder.create<cf::BranchOp>(value.getLoc(), successor);
418  }
419 
420  OpBuilder builder = OpBuilder::atBlockBegin(refCountingBlock);
421  builder.create<RuntimeDropRefOp>(value.getLoc(), value,
422  builder.getI64IntegerAttr(1));
423 
424  // No need to update the terminator operation.
425  if (successor == refCountingBlock)
426  continue;
427 
428  // Update terminator `successor` block to `refCountingBlock`.
429  for (const auto &pair : llvm::enumerate(terminator->getSuccessors()))
430  if (pair.value() == successor)
431  terminator->setSuccessor(refCountingBlock, pair.index());
432  }
433  }
434 
435  return success();
436 }
437 
438 LogicalResult
439 AsyncRuntimeRefCountingPass::addAutomaticRefCounting(Value value) {
440  // Short-circuit reference counting for values without uses.
441  if (succeeded(dropRefIfNoUses(value)))
442  return success();
443 
444  // Add `drop_ref` operations based on the liveness analysis.
445  if (failed(addDropRefAfterLastUse(value)))
446  return failure();
447 
448  // Add `add_ref` operations before function calls.
449  if (failed(addAddRefBeforeFunctionCall(value)))
450  return failure();
451 
452  // Add `drop_ref` operations to successors with divergent `value` liveness.
453  if (failed(addDropRefInDivergentLivenessSuccessor(value)))
454  return failure();
455 
456  return success();
457 }
458 
459 void AsyncRuntimeRefCountingPass::runOnOperation() {
460  auto functor = [&](Value value) { return addAutomaticRefCounting(value); };
461  if (failed(walkReferenceCountedValues(getOperation(), functor)))
462  signalPassFailure();
463 }
464 
465 //===----------------------------------------------------------------------===//
466 // Reference counting based on the user defined policy.
467 //===----------------------------------------------------------------------===//
468 
469 namespace {
470 
471 class AsyncRuntimePolicyBasedRefCountingPass
472  : public impl::AsyncRuntimePolicyBasedRefCountingPassBase<
473  AsyncRuntimePolicyBasedRefCountingPass> {
474 public:
475  AsyncRuntimePolicyBasedRefCountingPass() { initializeDefaultPolicy(); }
476 
477  void runOnOperation() override;
478 
479 private:
480  // Adds a reference counting operations for all uses of the `value` according
481  // to the reference counting policy.
482  LogicalResult addRefCounting(Value value);
483 
484  void initializeDefaultPolicy();
485 
487 };
488 
489 } // namespace
490 
491 LogicalResult
492 AsyncRuntimePolicyBasedRefCountingPass::addRefCounting(Value value) {
493  // Short-circuit reference counting for values without uses.
494  if (succeeded(dropRefIfNoUses(value)))
495  return success();
496 
497  OpBuilder b(value.getContext());
498 
499  // Consult the user defined policy for every value use.
500  for (OpOperand &operand : value.getUses()) {
501  Location loc = operand.getOwner()->getLoc();
502 
503  for (auto &func : policy) {
504  FailureOr<int> refCount = func(operand);
505  if (failed(refCount))
506  return failure();
507 
508  int cnt = *refCount;
509 
510  // Create `add_ref` operation before the operand owner.
511  if (cnt > 0) {
512  b.setInsertionPoint(operand.getOwner());
513  b.create<RuntimeAddRefOp>(loc, value, b.getI64IntegerAttr(cnt));
514  }
515 
516  // Create `drop_ref` operation after the operand owner.
517  if (cnt < 0) {
518  b.setInsertionPointAfter(operand.getOwner());
519  b.create<RuntimeDropRefOp>(loc, value, b.getI64IntegerAttr(-cnt));
520  }
521  }
522  }
523 
524  return success();
525 }
526 
527 void AsyncRuntimePolicyBasedRefCountingPass::initializeDefaultPolicy() {
528  policy.push_back([](OpOperand &operand) -> FailureOr<int> {
529  Operation *op = operand.getOwner();
530  Type type = operand.get().getType();
531 
532  bool isToken = isa<TokenType>(type);
533  bool isGroup = isa<GroupType>(type);
534  bool isValue = isa<ValueType>(type);
535 
536  // Drop reference after async token or group error check (coro await).
537  if (dyn_cast<RuntimeIsErrorOp>(op))
538  return (isToken || isGroup) ? -1 : 0;
539 
540  // Drop reference after async value load.
541  if (dyn_cast<RuntimeLoadOp>(op))
542  return isValue ? -1 : 0;
543 
544  // Drop reference after async token added to the group.
545  if (dyn_cast<RuntimeAddToGroupOp>(op))
546  return isToken ? -1 : 0;
547 
548  return 0;
549  });
550 }
551 
552 void AsyncRuntimePolicyBasedRefCountingPass::runOnOperation() {
553  auto functor = [&](Value value) { return addRefCounting(value); };
554  if (failed(walkReferenceCountedValues(getOperation(), functor)))
555  signalPassFailure();
556 }
static LogicalResult walkReferenceCountedValues(Operation *op, llvm::function_ref< LogicalResult(Value)> addRefCounting)
static LogicalResult dropRefIfNoUses(Value value, unsigned count=1)
This class represents an argument of a Block.
Definition: Value.h:295
Block represents an ordered list of Operations.
Definition: Block.h:33
Operation * findAncestorOpInBlock(Operation &op)
Returns 'op' if 'op' lies in this block, or otherwise finds the ancestor operation of 'op' that lies ...
Definition: Block.cpp:76
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition: Block.cpp:29
SuccessorRange getSuccessors()
Definition: Block.h:267
Operation * getTerminator()
Get the terminator operation of this block.
Definition: Block.cpp:246
BlockArgListType getArguments()
Definition: Block.h:87
void moveBefore(Block *block)
Unlink this block from its current region and insert it right before the specific block.
Definition: Block.cpp:56
IntegerAttr getI64IntegerAttr(int64_t value)
Definition: Builders.cpp:108
IRValueT get() const
Return the current value being used by this operand.
Definition: UseDefLists.h:160
This class represents liveness information on block level.
Definition: Liveness.h:99
Block * getBlock() const
Returns the underlying block.
Definition: Liveness.h:106
bool isLiveIn(Value value) const
Returns true if the given value is in the live-in set.
Definition: Liveness.cpp:353
bool isLiveOut(Value value) const
Returns true if the given value is in the live-out set.
Definition: Liveness.cpp:358
Operation * getEndOperation(Value value, Operation *startOperation) const
Gets the end operation for the given value using the start operation provided (must be referenced in ...
Definition: Liveness.cpp:375
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:66
This class helps build Operations.
Definition: Builders.h:205
static OpBuilder atBlockBegin(Block *block, Listener *listener=nullptr)
Create a builder and set the insertion point to before the first operation in the block but still ins...
Definition: Builders.h:238
static OpBuilder atBlockEnd(Block *block, Listener *listener=nullptr)
Create a builder and set the insertion point to after the last operation in the block but still insid...
Definition: Builders.h:244
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition: Builders.h:429
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:453
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition: Builders.h:410
This class represents an operand of an operation.
Definition: Value.h:243
This class provides the API for ops that are known to be terminators.
Definition: OpDefinition.h:772
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition: Operation.h:750
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition: Operation.h:407
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition: Operation.h:798
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
result_type_range getResultTypes()
Definition: Operation.h:428
void setSuccessor(Block *block, unsigned index)
Definition: Operation.cpp:607
SuccessorRange getSuccessors()
Definition: Operation.h:704
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Definition: Operation.cpp:673
unsigned getNumResults()
Return the number of results held by this operation.
Definition: Operation.h:404
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
BlockListType & getBlocks()
Definition: Region.h:45
Block * findAncestorBlockInRegion(Block &block)
Returns 'block' if 'block' lies in this region, or otherwise finds the ancestor of 'block' that lies ...
Definition: Region.cpp:154
Block & emplaceBlock()
Definition: Region.h:46
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
MLIRContext * getContext() const
Utility to get the associated MLIRContext that this value is defined in.
Definition: Value.h:108
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Definition: Value.h:188
Block * getParentBlock()
Return the Block in which this Value is defined.
Definition: Value.cpp:48
user_range getUsers() const
Definition: Value.h:204
Location getLoc() const
Return the location of this value.
Definition: Value.cpp:26
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition: Value.cpp:20
Region * getParentRegion()
Return the Region in which this Value is defined.
Definition: Value.cpp:41
A utility result that is used to signal how to proceed with an ongoing walk:
Definition: Visitors.h:33
static WalkResult advance()
Definition: Visitors.h:51
bool wasInterrupted() const
Returns true if the walk was interrupted.
Definition: Visitors.h:55
static WalkResult interrupt()
Definition: Visitors.h:50
Operation * getOwner() const
Return the owner of this operand.
Definition: UseDefLists.h:38
bool isRefCounted(Type type)
Returns true if the type is reference counted at runtime.
Definition: Async.h:52
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:344
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition: Utils.cpp:305
This trait indicates that a terminator operation is "return-like".