1a6628e59SEugene Zhulenev //===- AsyncRuntimeRefCounting.cpp - Async Runtime Ref Counting -----------===//
2a6628e59SEugene Zhulenev //
3a6628e59SEugene Zhulenev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a6628e59SEugene Zhulenev // See https://llvm.org/LICENSE.txt for license information.
5a6628e59SEugene Zhulenev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a6628e59SEugene Zhulenev //
7a6628e59SEugene Zhulenev //===----------------------------------------------------------------------===//
8a6628e59SEugene Zhulenev //
9a6628e59SEugene Zhulenev // This file implements automatic reference counting for Async runtime
10a6628e59SEugene Zhulenev // operations and types.
11a6628e59SEugene Zhulenev //
12a6628e59SEugene Zhulenev //===----------------------------------------------------------------------===//
13a6628e59SEugene Zhulenev 
14a6628e59SEugene Zhulenev #include "PassDetail.h"
15a6628e59SEugene Zhulenev #include "mlir/Analysis/Liveness.h"
16a6628e59SEugene Zhulenev #include "mlir/Dialect/Async/IR/Async.h"
17a6628e59SEugene Zhulenev #include "mlir/Dialect/Async/Passes.h"
18a6628e59SEugene Zhulenev #include "mlir/Dialect/StandardOps/IR/Ops.h"
19a6628e59SEugene Zhulenev #include "mlir/IR/ImplicitLocOpBuilder.h"
20a6628e59SEugene Zhulenev #include "mlir/IR/PatternMatch.h"
21a6628e59SEugene Zhulenev #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
22a6628e59SEugene Zhulenev #include "llvm/ADT/SmallSet.h"
23a6628e59SEugene Zhulenev 
24a6628e59SEugene Zhulenev using namespace mlir;
25a6628e59SEugene Zhulenev using namespace mlir::async;
26a6628e59SEugene Zhulenev 
27a6628e59SEugene Zhulenev #define DEBUG_TYPE "async-runtime-ref-counting"
28a6628e59SEugene Zhulenev 
29a6628e59SEugene Zhulenev namespace {
30a6628e59SEugene Zhulenev 
31a6628e59SEugene Zhulenev class AsyncRuntimeRefCountingPass
32a6628e59SEugene Zhulenev     : public AsyncRuntimeRefCountingBase<AsyncRuntimeRefCountingPass> {
33a6628e59SEugene Zhulenev public:
34a6628e59SEugene Zhulenev   AsyncRuntimeRefCountingPass() = default;
35*8a316b00SEugene Zhulenev   void runOnOperation() override;
36a6628e59SEugene Zhulenev 
37a6628e59SEugene Zhulenev private:
38a6628e59SEugene Zhulenev   /// Adds an automatic reference counting to the `value`.
39a6628e59SEugene Zhulenev   ///
40a6628e59SEugene Zhulenev   /// All values (token, group or value) are semantically created with a
41a6628e59SEugene Zhulenev   /// reference count of +1 and it is the responsibility of the async value user
42a6628e59SEugene Zhulenev   /// to place the `add_ref` and `drop_ref` operations to ensure that the value
43a6628e59SEugene Zhulenev   /// is destroyed after the last use.
44a6628e59SEugene Zhulenev   ///
45a6628e59SEugene Zhulenev   /// The function returns failure if it can't deduce the locations where
46a6628e59SEugene Zhulenev   /// to place the reference counting operations.
47a6628e59SEugene Zhulenev   ///
48a6628e59SEugene Zhulenev   /// Async values "semantically created" when:
49a6628e59SEugene Zhulenev   ///   1. Operation returns async result (e.g. `async.runtime.create`)
50a6628e59SEugene Zhulenev   ///   2. Async value passed in as a block argument (or function argument,
51a6628e59SEugene Zhulenev   ///      because function arguments are just entry block arguments)
52a6628e59SEugene Zhulenev   ///
53a6628e59SEugene Zhulenev   /// Passing async value as a function argument (or block argument) does not
54a6628e59SEugene Zhulenev   /// really mean that a new async value is created, it only means that the
55a6628e59SEugene Zhulenev   /// caller of a function transfered ownership of `+1` reference to the callee.
56a6628e59SEugene Zhulenev   /// It is convenient to think that from the callee perspective async value was
57a6628e59SEugene Zhulenev   /// "created" with `+1` reference by the block argument.
58a6628e59SEugene Zhulenev   ///
59a6628e59SEugene Zhulenev   /// Automatic reference counting algorithm outline:
60a6628e59SEugene Zhulenev   ///
61a6628e59SEugene Zhulenev   /// #1 Insert `drop_ref` operations after last use of the `value`.
62a6628e59SEugene Zhulenev   /// #2 Insert `add_ref` operations before functions calls with reference
63a6628e59SEugene Zhulenev   ///    counted `value` operand (newly created `+1` reference will be
64a6628e59SEugene Zhulenev   ///    transferred to the callee).
65a6628e59SEugene Zhulenev   /// #3 Verify that divergent control flow does not lead to leaked reference
66a6628e59SEugene Zhulenev   ///    counted objects.
67a6628e59SEugene Zhulenev   ///
68a6628e59SEugene Zhulenev   /// Async runtime reference counting optimization pass will optimize away
69a6628e59SEugene Zhulenev   /// some of the redundant `add_ref` and `drop_ref` operations inserted by this
70a6628e59SEugene Zhulenev   /// strategy (see `async-runtime-ref-counting-opt`).
71a6628e59SEugene Zhulenev   LogicalResult addAutomaticRefCounting(Value value);
72a6628e59SEugene Zhulenev 
73a6628e59SEugene Zhulenev   /// (#1) Adds the `drop_ref` operation after the last use of the `value`
74a6628e59SEugene Zhulenev   /// relying on the liveness analysis.
75a6628e59SEugene Zhulenev   ///
76a6628e59SEugene Zhulenev   /// If the `value` is in the block `liveIn` set and it is not in the block
77a6628e59SEugene Zhulenev   /// `liveOut` set, it means that it "dies" in the block. We find the last
78a6628e59SEugene Zhulenev   /// use of the value in such block and:
79a6628e59SEugene Zhulenev   ///
80a6628e59SEugene Zhulenev   ///   1. If the last user is a `ReturnLike` operation we do nothing, because
81a6628e59SEugene Zhulenev   ///      it forwards the ownership to the caller.
82a6628e59SEugene Zhulenev   ///   2. Otherwise we add a `drop_ref` operation immediately after the last
83a6628e59SEugene Zhulenev   ///      use.
84a6628e59SEugene Zhulenev   LogicalResult addDropRefAfterLastUse(Value value);
85a6628e59SEugene Zhulenev 
86a6628e59SEugene Zhulenev   /// (#2) Adds the `add_ref` operation before the function call taking `value`
87a6628e59SEugene Zhulenev   /// operand to ensure that the value passed to the function entry block
88a6628e59SEugene Zhulenev   /// has a `+1` reference count.
89a6628e59SEugene Zhulenev   LogicalResult addAddRefBeforeFunctionCall(Value value);
90a6628e59SEugene Zhulenev 
91a6628e59SEugene Zhulenev   /// (#3) Verifies that if a block has a value in the `liveOut` set, then the
92a6628e59SEugene Zhulenev   /// value is in `liveIn` set in all successors.
93a6628e59SEugene Zhulenev   ///
94a6628e59SEugene Zhulenev   /// Example:
95a6628e59SEugene Zhulenev   ///
96a6628e59SEugene Zhulenev   ///   ^entry:
97a6628e59SEugene Zhulenev   ///     %token = async.runtime.create : !async.token
98a6628e59SEugene Zhulenev   ///     cond_br %cond, ^bb1, ^bb2
99a6628e59SEugene Zhulenev   ///   ^bb1:
100a6628e59SEugene Zhulenev   ///     async.runtime.await %token
101a6628e59SEugene Zhulenev   ///     return
102a6628e59SEugene Zhulenev   ///   ^bb2:
103a6628e59SEugene Zhulenev   ///     return
104a6628e59SEugene Zhulenev   ///
105a6628e59SEugene Zhulenev   /// This CFG will be rejected because ^bb2 does not have `value` in the
106a6628e59SEugene Zhulenev   /// `liveIn` set, and it will leak a reference counted object.
107a6628e59SEugene Zhulenev   ///
108a6628e59SEugene Zhulenev   /// An exception to this rule are blocks with `async.coro.suspend` terminator,
109a6628e59SEugene Zhulenev   /// because in Async to LLVM lowering it is guaranteed that the control flow
110a6628e59SEugene Zhulenev   /// will jump into the resume block, and then follow into the cleanup and
111a6628e59SEugene Zhulenev   /// suspend blocks.
112a6628e59SEugene Zhulenev   ///
113a6628e59SEugene Zhulenev   /// Example:
114a6628e59SEugene Zhulenev   ///
115a6628e59SEugene Zhulenev   ///  ^entry(%value: !async.value<f32>):
116a6628e59SEugene Zhulenev   ///     async.runtime.await_and_resume %value, %hdl : !async.value<f32>
117a6628e59SEugene Zhulenev   ///     async.coro.suspend %ret, ^suspend, ^resume, ^cleanup
118a6628e59SEugene Zhulenev   ///   ^resume:
119a6628e59SEugene Zhulenev   ///     %0 = async.runtime.load %value
120a6628e59SEugene Zhulenev   ///     br ^cleanup
121a6628e59SEugene Zhulenev   ///   ^cleanup:
122a6628e59SEugene Zhulenev   ///     ...
123a6628e59SEugene Zhulenev   ///   ^suspend:
124a6628e59SEugene Zhulenev   ///     ...
125a6628e59SEugene Zhulenev   ///
126a6628e59SEugene Zhulenev   /// Although cleanup and suspend blocks do not have the `value` in the
127a6628e59SEugene Zhulenev   /// `liveIn` set, it is guaranteed that execution will eventually continue in
128a6628e59SEugene Zhulenev   /// the resume block (we never explicitly destroy coroutines).
129a6628e59SEugene Zhulenev   LogicalResult verifySuccessors(Value value);
130a6628e59SEugene Zhulenev };
131a6628e59SEugene Zhulenev 
132a6628e59SEugene Zhulenev } // namespace
133a6628e59SEugene Zhulenev 
134a6628e59SEugene Zhulenev LogicalResult AsyncRuntimeRefCountingPass::addDropRefAfterLastUse(Value value) {
135a6628e59SEugene Zhulenev   OpBuilder builder(value.getContext());
136a6628e59SEugene Zhulenev   Location loc = value.getLoc();
137a6628e59SEugene Zhulenev 
138a6628e59SEugene Zhulenev   // Use liveness analysis to find the placement of `drop_ref`operation.
139a6628e59SEugene Zhulenev   auto &liveness = getAnalysis<Liveness>();
140a6628e59SEugene Zhulenev 
141a6628e59SEugene Zhulenev   // We analyse only the blocks of the region that defines the `value`, and do
142a6628e59SEugene Zhulenev   // not check nested blocks attached to operations.
143a6628e59SEugene Zhulenev   //
144a6628e59SEugene Zhulenev   // By analyzing only the `definingRegion` CFG we potentially loose an
145a6628e59SEugene Zhulenev   // opportunity to drop the reference count earlier and can extend the lifetime
146a6628e59SEugene Zhulenev   // of reference counted value longer then it is really required.
147a6628e59SEugene Zhulenev   //
148a6628e59SEugene Zhulenev   // We also assume that all nested regions finish their execution before the
149a6628e59SEugene Zhulenev   // completion of the owner operation. The only exception to this rule is
150a6628e59SEugene Zhulenev   // `async.execute` operation, and we verify that they are lowered to the
151a6628e59SEugene Zhulenev   // `async.runtime` operations before adding automatic reference counting.
152a6628e59SEugene Zhulenev   Region *definingRegion = value.getParentRegion();
153a6628e59SEugene Zhulenev 
154a6628e59SEugene Zhulenev   // Last users of the `value` inside all blocks where the value dies.
155a6628e59SEugene Zhulenev   llvm::SmallSet<Operation *, 4> lastUsers;
156a6628e59SEugene Zhulenev 
157a6628e59SEugene Zhulenev   // Find blocks in the `definingRegion` that have users of the `value` (if
158a6628e59SEugene Zhulenev   // there are multiple users in the block, which one will be selected is
159a6628e59SEugene Zhulenev   // undefined). User operation might be not the actual user of the value, but
160a6628e59SEugene Zhulenev   // the operation in the block that has a "real user" in one of the attached
161a6628e59SEugene Zhulenev   // regions.
162a6628e59SEugene Zhulenev   llvm::DenseMap<Block *, Operation *> usersInTheBlocks;
163a6628e59SEugene Zhulenev 
164a6628e59SEugene Zhulenev   for (Operation *user : value.getUsers()) {
165a6628e59SEugene Zhulenev     Block *userBlock = user->getBlock();
166a6628e59SEugene Zhulenev     Block *ancestor = definingRegion->findAncestorBlockInRegion(*userBlock);
167a6628e59SEugene Zhulenev     usersInTheBlocks[ancestor] = ancestor->findAncestorOpInBlock(*user);
168a6628e59SEugene Zhulenev     assert(ancestor && "ancestor block must be not null");
169a6628e59SEugene Zhulenev     assert(usersInTheBlocks[ancestor] && "ancestor op must be not null");
170a6628e59SEugene Zhulenev   }
171a6628e59SEugene Zhulenev 
172a6628e59SEugene Zhulenev   // Find blocks where the `value` dies: the value is in `liveIn` set and not
173a6628e59SEugene Zhulenev   // in the `liveOut` set. We place `drop_ref` immediately after the last use
174a6628e59SEugene Zhulenev   // of the `value` in such regions (after handling few special cases).
175a6628e59SEugene Zhulenev   //
176a6628e59SEugene Zhulenev   // We do not traverse all the blocks in the `definingRegion`, because the
177a6628e59SEugene Zhulenev   // `value` can be in the live in set only if it has users in the block, or it
178a6628e59SEugene Zhulenev   // is defined in the block.
179a6628e59SEugene Zhulenev   //
180a6628e59SEugene Zhulenev   // Values with zero users (only definition) handled explicitly above.
181a6628e59SEugene Zhulenev   for (auto &blockAndUser : usersInTheBlocks) {
182a6628e59SEugene Zhulenev     Block *block = blockAndUser.getFirst();
183a6628e59SEugene Zhulenev     Operation *userInTheBlock = blockAndUser.getSecond();
184a6628e59SEugene Zhulenev 
185a6628e59SEugene Zhulenev     const LivenessBlockInfo *blockLiveness = liveness.getLiveness(block);
186a6628e59SEugene Zhulenev 
187a6628e59SEugene Zhulenev     // Value must be in the live input set or defined in the block.
188a6628e59SEugene Zhulenev     assert(blockLiveness->isLiveIn(value) ||
189a6628e59SEugene Zhulenev            blockLiveness->getBlock() == value.getParentBlock());
190a6628e59SEugene Zhulenev 
191a6628e59SEugene Zhulenev     // If value is in the live out set, it means it doesn't "die" in the block.
192a6628e59SEugene Zhulenev     if (blockLiveness->isLiveOut(value))
193a6628e59SEugene Zhulenev       continue;
194a6628e59SEugene Zhulenev 
195a6628e59SEugene Zhulenev     // At this point we proved that `value` dies in the `block`. Find the last
196a6628e59SEugene Zhulenev     // use of the `value` inside the `block`, this is where it "dies".
197a6628e59SEugene Zhulenev     Operation *lastUser = blockLiveness->getEndOperation(value, userInTheBlock);
198a6628e59SEugene Zhulenev     assert(lastUsers.count(lastUser) == 0 && "last users must be unique");
199a6628e59SEugene Zhulenev     lastUsers.insert(lastUser);
200a6628e59SEugene Zhulenev   }
201a6628e59SEugene Zhulenev 
202a6628e59SEugene Zhulenev   // Process all the last users of the `value` inside each block where the value
203a6628e59SEugene Zhulenev   // dies.
204a6628e59SEugene Zhulenev   for (Operation *lastUser : lastUsers) {
205a6628e59SEugene Zhulenev     // Return like operations forward reference count.
206a6628e59SEugene Zhulenev     if (lastUser->hasTrait<OpTrait::ReturnLike>())
207a6628e59SEugene Zhulenev       continue;
208a6628e59SEugene Zhulenev 
209a6628e59SEugene Zhulenev     // We can't currently handle other types of terminators.
210a6628e59SEugene Zhulenev     if (lastUser->hasTrait<OpTrait::IsTerminator>())
211a6628e59SEugene Zhulenev       return lastUser->emitError() << "async reference counting can't handle "
212a6628e59SEugene Zhulenev                                       "terminators that are not ReturnLike";
213a6628e59SEugene Zhulenev 
214a6628e59SEugene Zhulenev     // Add a drop_ref immediately after the last user.
215a6628e59SEugene Zhulenev     builder.setInsertionPointAfter(lastUser);
216a6628e59SEugene Zhulenev     builder.create<RuntimeDropRefOp>(loc, value, builder.getI32IntegerAttr(1));
217a6628e59SEugene Zhulenev   }
218a6628e59SEugene Zhulenev 
219a6628e59SEugene Zhulenev   return success();
220a6628e59SEugene Zhulenev }
221a6628e59SEugene Zhulenev 
222a6628e59SEugene Zhulenev LogicalResult
223a6628e59SEugene Zhulenev AsyncRuntimeRefCountingPass::addAddRefBeforeFunctionCall(Value value) {
224a6628e59SEugene Zhulenev   OpBuilder builder(value.getContext());
225a6628e59SEugene Zhulenev   Location loc = value.getLoc();
226a6628e59SEugene Zhulenev 
227a6628e59SEugene Zhulenev   for (Operation *user : value.getUsers()) {
228a6628e59SEugene Zhulenev     if (!isa<CallOp>(user))
229a6628e59SEugene Zhulenev       continue;
230a6628e59SEugene Zhulenev 
231a6628e59SEugene Zhulenev     // Add a reference before the function call to pass the value at `+1`
232a6628e59SEugene Zhulenev     // reference to the function entry block.
233a6628e59SEugene Zhulenev     builder.setInsertionPoint(user);
234a6628e59SEugene Zhulenev     builder.create<RuntimeAddRefOp>(loc, value, builder.getI32IntegerAttr(1));
235a6628e59SEugene Zhulenev   }
236a6628e59SEugene Zhulenev 
237a6628e59SEugene Zhulenev   return success();
238a6628e59SEugene Zhulenev }
239a6628e59SEugene Zhulenev 
240a6628e59SEugene Zhulenev LogicalResult AsyncRuntimeRefCountingPass::verifySuccessors(Value value) {
241a6628e59SEugene Zhulenev   OpBuilder builder(value.getContext());
242a6628e59SEugene Zhulenev 
243a6628e59SEugene Zhulenev   // Blocks with successfors with different `liveIn` properties of the `value`.
244a6628e59SEugene Zhulenev   llvm::SmallSet<Block *, 4> divergentLivenessBlocks;
245a6628e59SEugene Zhulenev 
246a6628e59SEugene Zhulenev   // Use liveness analysis to find the placement of `drop_ref`operation.
247a6628e59SEugene Zhulenev   auto &liveness = getAnalysis<Liveness>();
248a6628e59SEugene Zhulenev 
249a6628e59SEugene Zhulenev   // Because we only add `drop_ref` operations to the region that defines the
250a6628e59SEugene Zhulenev   // `value` we can only process CFG for the same region.
251a6628e59SEugene Zhulenev   Region *definingRegion = value.getParentRegion();
252a6628e59SEugene Zhulenev 
253a6628e59SEugene Zhulenev   // Collect blocks with successors with mismatching `liveIn` sets.
254a6628e59SEugene Zhulenev   for (Block &block : definingRegion->getBlocks()) {
255a6628e59SEugene Zhulenev     const LivenessBlockInfo *blockLiveness = liveness.getLiveness(&block);
256a6628e59SEugene Zhulenev 
257a6628e59SEugene Zhulenev     // Skip the block if value is not in the `liveOut` set.
258a6628e59SEugene Zhulenev     if (!blockLiveness->isLiveOut(value))
259a6628e59SEugene Zhulenev       continue;
260a6628e59SEugene Zhulenev 
261a6628e59SEugene Zhulenev     // Sucessors with value in `liveIn` set and not value in `liveIn` set.
262a6628e59SEugene Zhulenev     llvm::SmallSet<Block *, 4> liveInSuccessors;
263a6628e59SEugene Zhulenev     llvm::SmallSet<Block *, 4> noLiveInSuccessors;
264a6628e59SEugene Zhulenev 
265a6628e59SEugene Zhulenev     // Collect successors that do not have `value` in the `liveIn` set.
266a6628e59SEugene Zhulenev     for (Block *successor : block.getSuccessors()) {
267a6628e59SEugene Zhulenev       const LivenessBlockInfo *succLiveness = liveness.getLiveness(successor);
268a6628e59SEugene Zhulenev       if (succLiveness->isLiveIn(value))
269a6628e59SEugene Zhulenev         liveInSuccessors.insert(successor);
270a6628e59SEugene Zhulenev       else
271a6628e59SEugene Zhulenev         noLiveInSuccessors.insert(successor);
272a6628e59SEugene Zhulenev     }
273a6628e59SEugene Zhulenev 
274a6628e59SEugene Zhulenev     // Block has successors with different `liveIn` property of the `value`.
275a6628e59SEugene Zhulenev     if (!liveInSuccessors.empty() && !noLiveInSuccessors.empty())
276a6628e59SEugene Zhulenev       divergentLivenessBlocks.insert(&block);
277a6628e59SEugene Zhulenev   }
278a6628e59SEugene Zhulenev 
279a6628e59SEugene Zhulenev   // Verify that divergent `liveIn` property only present in blocks with
280a6628e59SEugene Zhulenev   // async.coro.suspend terminator.
281a6628e59SEugene Zhulenev   for (Block *block : divergentLivenessBlocks) {
282a6628e59SEugene Zhulenev     Operation *terminator = block->getTerminator();
283a6628e59SEugene Zhulenev     if (isa<CoroSuspendOp>(terminator))
284a6628e59SEugene Zhulenev       continue;
285a6628e59SEugene Zhulenev 
286a6628e59SEugene Zhulenev     return terminator->emitOpError("successor have different `liveIn` property "
287a6628e59SEugene Zhulenev                                    "of the reference counted value: ");
288a6628e59SEugene Zhulenev   }
289a6628e59SEugene Zhulenev 
290a6628e59SEugene Zhulenev   return success();
291a6628e59SEugene Zhulenev }
292a6628e59SEugene Zhulenev 
293a6628e59SEugene Zhulenev LogicalResult
294a6628e59SEugene Zhulenev AsyncRuntimeRefCountingPass::addAutomaticRefCounting(Value value) {
295a6628e59SEugene Zhulenev   OpBuilder builder(value.getContext());
296a6628e59SEugene Zhulenev   Location loc = value.getLoc();
297a6628e59SEugene Zhulenev 
298a6628e59SEugene Zhulenev   // Set inserton point after the operation producing a value, or at the
299a6628e59SEugene Zhulenev   // beginning of the block if the value defined by the block argument.
300a6628e59SEugene Zhulenev   if (Operation *op = value.getDefiningOp())
301a6628e59SEugene Zhulenev     builder.setInsertionPointAfter(op);
302a6628e59SEugene Zhulenev   else
303a6628e59SEugene Zhulenev     builder.setInsertionPointToStart(value.getParentBlock());
304a6628e59SEugene Zhulenev 
305a6628e59SEugene Zhulenev   // Drop the reference count immediately if the value has no uses.
306a6628e59SEugene Zhulenev   if (value.getUses().empty()) {
307a6628e59SEugene Zhulenev     builder.create<RuntimeDropRefOp>(loc, value, builder.getI32IntegerAttr(1));
308a6628e59SEugene Zhulenev     return success();
309a6628e59SEugene Zhulenev   }
310a6628e59SEugene Zhulenev 
311a6628e59SEugene Zhulenev   // Add `drop_ref` operations based on the liveness analysis.
312a6628e59SEugene Zhulenev   if (failed(addDropRefAfterLastUse(value)))
313a6628e59SEugene Zhulenev     return failure();
314a6628e59SEugene Zhulenev 
315a6628e59SEugene Zhulenev   // Add `add_ref` operations before function calls.
316a6628e59SEugene Zhulenev   if (failed(addAddRefBeforeFunctionCall(value)))
317a6628e59SEugene Zhulenev     return failure();
318a6628e59SEugene Zhulenev 
319a6628e59SEugene Zhulenev   // Verify that the `value` is in `liveIn` set of all successors.
320a6628e59SEugene Zhulenev   if (failed(verifySuccessors(value)))
321a6628e59SEugene Zhulenev     return failure();
322a6628e59SEugene Zhulenev 
323a6628e59SEugene Zhulenev   return success();
324a6628e59SEugene Zhulenev }
325a6628e59SEugene Zhulenev 
326*8a316b00SEugene Zhulenev void AsyncRuntimeRefCountingPass::runOnOperation() {
327*8a316b00SEugene Zhulenev   Operation *op = getOperation();
328a6628e59SEugene Zhulenev 
329a6628e59SEugene Zhulenev   // Check that we do not have high level async operations in the IR because
330a6628e59SEugene Zhulenev   // otherwise automatic reference counting will produce incorrect results after
331a6628e59SEugene Zhulenev   // execute operations will be lowered to `async.runtime`
332*8a316b00SEugene Zhulenev   WalkResult executeOpWalk = op->walk([&](Operation *op) -> WalkResult {
333a6628e59SEugene Zhulenev     if (!isa<ExecuteOp, AwaitOp, AwaitAllOp, YieldOp>(op))
334a6628e59SEugene Zhulenev       return WalkResult::advance();
335a6628e59SEugene Zhulenev 
336a6628e59SEugene Zhulenev     return op->emitError()
337a6628e59SEugene Zhulenev            << "async operations must be lowered to async runtime operations";
338a6628e59SEugene Zhulenev   });
339a6628e59SEugene Zhulenev 
340a6628e59SEugene Zhulenev   if (executeOpWalk.wasInterrupted()) {
341a6628e59SEugene Zhulenev     signalPassFailure();
342a6628e59SEugene Zhulenev     return;
343a6628e59SEugene Zhulenev   }
344a6628e59SEugene Zhulenev 
345a6628e59SEugene Zhulenev   // Add reference counting to block arguments.
346*8a316b00SEugene Zhulenev   WalkResult blockWalk = op->walk([&](Block *block) -> WalkResult {
347a6628e59SEugene Zhulenev     for (BlockArgument arg : block->getArguments())
348a6628e59SEugene Zhulenev       if (isRefCounted(arg.getType()))
349a6628e59SEugene Zhulenev         if (failed(addAutomaticRefCounting(arg)))
350a6628e59SEugene Zhulenev           return WalkResult::interrupt();
351a6628e59SEugene Zhulenev 
352a6628e59SEugene Zhulenev     return WalkResult::advance();
353a6628e59SEugene Zhulenev   });
354a6628e59SEugene Zhulenev 
355a6628e59SEugene Zhulenev   if (blockWalk.wasInterrupted()) {
356a6628e59SEugene Zhulenev     signalPassFailure();
357a6628e59SEugene Zhulenev     return;
358a6628e59SEugene Zhulenev   }
359a6628e59SEugene Zhulenev 
360a6628e59SEugene Zhulenev   // Add reference counting to operation results.
361*8a316b00SEugene Zhulenev   WalkResult opWalk = op->walk([&](Operation *op) -> WalkResult {
362a6628e59SEugene Zhulenev     for (unsigned i = 0; i < op->getNumResults(); ++i)
363a6628e59SEugene Zhulenev       if (isRefCounted(op->getResultTypes()[i]))
364a6628e59SEugene Zhulenev         if (failed(addAutomaticRefCounting(op->getResult(i))))
365a6628e59SEugene Zhulenev           return WalkResult::interrupt();
366a6628e59SEugene Zhulenev 
367a6628e59SEugene Zhulenev     return WalkResult::advance();
368a6628e59SEugene Zhulenev   });
369a6628e59SEugene Zhulenev 
370a6628e59SEugene Zhulenev   if (opWalk.wasInterrupted())
371a6628e59SEugene Zhulenev     signalPassFailure();
372a6628e59SEugene Zhulenev }
373a6628e59SEugene Zhulenev 
374*8a316b00SEugene Zhulenev std::unique_ptr<Pass> mlir::createAsyncRuntimeRefCountingPass() {
375a6628e59SEugene Zhulenev   return std::make_unique<AsyncRuntimeRefCountingPass>();
376a6628e59SEugene Zhulenev }
377