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