1*6de04e6fSDennis Filimonov //===- OptimizeAllocationLiveness.cpp - impl. optimize allocation liveness pass 2*6de04e6fSDennis Filimonov //-===// 3*6de04e6fSDennis Filimonov // 4*6de04e6fSDennis Filimonov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5*6de04e6fSDennis Filimonov // See https://llvm.org/LICENSE.txt for license information. 6*6de04e6fSDennis Filimonov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7*6de04e6fSDennis Filimonov // 8*6de04e6fSDennis Filimonov //===----------------------------------------------------------------------===// 9*6de04e6fSDennis Filimonov // 10*6de04e6fSDennis Filimonov // This file implements a pass for optimizing allocation liveness. 11*6de04e6fSDennis Filimonov // The pass moves the deallocation operation after the last user of the 12*6de04e6fSDennis Filimonov // allocated buffer. 13*6de04e6fSDennis Filimonov //===----------------------------------------------------------------------===// 14*6de04e6fSDennis Filimonov 15*6de04e6fSDennis Filimonov #include "mlir/Dialect/Bufferization/Transforms/BufferViewFlowAnalysis.h" 16*6de04e6fSDennis Filimonov #include "mlir/Dialect/Bufferization/Transforms/Passes.h" 17*6de04e6fSDennis Filimonov #include "mlir/Dialect/Func/IR/FuncOps.h" 18*6de04e6fSDennis Filimonov #include "mlir/Dialect/MemRef/IR/MemRef.h" 19*6de04e6fSDennis Filimonov #include "mlir/IR/Operation.h" 20*6de04e6fSDennis Filimonov #include "llvm/Support/Debug.h" 21*6de04e6fSDennis Filimonov 22*6de04e6fSDennis Filimonov #define DEBUG_TYPE "optimize-allocation-liveness" 23*6de04e6fSDennis Filimonov #define DBGS() (llvm::dbgs() << '[' << DEBUG_TYPE << "] ") 24*6de04e6fSDennis Filimonov #define LDBG(X) LLVM_DEBUG(DBGS() << X << "\n") 25*6de04e6fSDennis Filimonov 26*6de04e6fSDennis Filimonov namespace mlir { 27*6de04e6fSDennis Filimonov namespace bufferization { 28*6de04e6fSDennis Filimonov #define GEN_PASS_DEF_OPTIMIZEALLOCATIONLIVENESS 29*6de04e6fSDennis Filimonov #include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc" 30*6de04e6fSDennis Filimonov } // namespace bufferization 31*6de04e6fSDennis Filimonov } // namespace mlir 32*6de04e6fSDennis Filimonov 33*6de04e6fSDennis Filimonov using namespace mlir; 34*6de04e6fSDennis Filimonov 35*6de04e6fSDennis Filimonov namespace { 36*6de04e6fSDennis Filimonov 37*6de04e6fSDennis Filimonov //===----------------------------------------------------------------------===// 38*6de04e6fSDennis Filimonov // Helper functions 39*6de04e6fSDennis Filimonov //===----------------------------------------------------------------------===// 40*6de04e6fSDennis Filimonov 41*6de04e6fSDennis Filimonov /// Return true if `a` happens before `b`, i.e., `a` or one of its ancestors 42*6de04e6fSDennis Filimonov /// properly dominates `b` and `b` is not inside `a`. 43*6de04e6fSDennis Filimonov static bool happensBefore(Operation *a, Operation *b) { 44*6de04e6fSDennis Filimonov do { 45*6de04e6fSDennis Filimonov if (a->isProperAncestor(b)) 46*6de04e6fSDennis Filimonov return false; 47*6de04e6fSDennis Filimonov if (Operation *bAncestor = a->getBlock()->findAncestorOpInBlock(*b)) { 48*6de04e6fSDennis Filimonov return a->isBeforeInBlock(bAncestor); 49*6de04e6fSDennis Filimonov } 50*6de04e6fSDennis Filimonov } while ((a = a->getParentOp())); 51*6de04e6fSDennis Filimonov return false; 52*6de04e6fSDennis Filimonov } 53*6de04e6fSDennis Filimonov 54*6de04e6fSDennis Filimonov /// This method searches for a user of value that is a dealloc operation. 55*6de04e6fSDennis Filimonov /// If multiple users with free effect are found, return nullptr. 56*6de04e6fSDennis Filimonov Operation *findUserWithFreeSideEffect(Value value) { 57*6de04e6fSDennis Filimonov Operation *freeOpUser = nullptr; 58*6de04e6fSDennis Filimonov for (Operation *user : value.getUsers()) { 59*6de04e6fSDennis Filimonov if (MemoryEffectOpInterface memEffectOp = 60*6de04e6fSDennis Filimonov dyn_cast<MemoryEffectOpInterface>(user)) { 61*6de04e6fSDennis Filimonov SmallVector<MemoryEffects::EffectInstance, 2> effects; 62*6de04e6fSDennis Filimonov memEffectOp.getEffects(effects); 63*6de04e6fSDennis Filimonov 64*6de04e6fSDennis Filimonov for (const auto &effect : effects) { 65*6de04e6fSDennis Filimonov if (isa<MemoryEffects::Free>(effect.getEffect())) { 66*6de04e6fSDennis Filimonov if (freeOpUser) { 67*6de04e6fSDennis Filimonov LDBG("Multiple users with free effect found: " << *freeOpUser 68*6de04e6fSDennis Filimonov << " and " << *user); 69*6de04e6fSDennis Filimonov return nullptr; 70*6de04e6fSDennis Filimonov } 71*6de04e6fSDennis Filimonov freeOpUser = user; 72*6de04e6fSDennis Filimonov } 73*6de04e6fSDennis Filimonov } 74*6de04e6fSDennis Filimonov } 75*6de04e6fSDennis Filimonov } 76*6de04e6fSDennis Filimonov return freeOpUser; 77*6de04e6fSDennis Filimonov } 78*6de04e6fSDennis Filimonov 79*6de04e6fSDennis Filimonov /// Checks if the given op allocates memory. 80*6de04e6fSDennis Filimonov static bool hasMemoryAllocEffect(MemoryEffectOpInterface memEffectOp) { 81*6de04e6fSDennis Filimonov SmallVector<MemoryEffects::EffectInstance, 2> effects; 82*6de04e6fSDennis Filimonov memEffectOp.getEffects(effects); 83*6de04e6fSDennis Filimonov for (const auto &effect : effects) { 84*6de04e6fSDennis Filimonov if (isa<MemoryEffects::Allocate>(effect.getEffect())) { 85*6de04e6fSDennis Filimonov return true; 86*6de04e6fSDennis Filimonov } 87*6de04e6fSDennis Filimonov } 88*6de04e6fSDennis Filimonov return false; 89*6de04e6fSDennis Filimonov } 90*6de04e6fSDennis Filimonov 91*6de04e6fSDennis Filimonov struct OptimizeAllocationLiveness 92*6de04e6fSDennis Filimonov : public bufferization::impl::OptimizeAllocationLivenessBase< 93*6de04e6fSDennis Filimonov OptimizeAllocationLiveness> { 94*6de04e6fSDennis Filimonov public: 95*6de04e6fSDennis Filimonov OptimizeAllocationLiveness() = default; 96*6de04e6fSDennis Filimonov 97*6de04e6fSDennis Filimonov void runOnOperation() override { 98*6de04e6fSDennis Filimonov func::FuncOp func = getOperation(); 99*6de04e6fSDennis Filimonov 100*6de04e6fSDennis Filimonov if (func.isExternal()) 101*6de04e6fSDennis Filimonov return; 102*6de04e6fSDennis Filimonov 103*6de04e6fSDennis Filimonov BufferViewFlowAnalysis analysis = BufferViewFlowAnalysis(func); 104*6de04e6fSDennis Filimonov 105*6de04e6fSDennis Filimonov func.walk([&](MemoryEffectOpInterface memEffectOp) -> WalkResult { 106*6de04e6fSDennis Filimonov if (!hasMemoryAllocEffect(memEffectOp)) 107*6de04e6fSDennis Filimonov return WalkResult::advance(); 108*6de04e6fSDennis Filimonov 109*6de04e6fSDennis Filimonov auto allocOp = memEffectOp; 110*6de04e6fSDennis Filimonov LDBG("Checking alloc op: " << allocOp); 111*6de04e6fSDennis Filimonov 112*6de04e6fSDennis Filimonov auto deallocOp = findUserWithFreeSideEffect(allocOp->getResult(0)); 113*6de04e6fSDennis Filimonov if (!deallocOp || (deallocOp->getBlock() != allocOp->getBlock())) { 114*6de04e6fSDennis Filimonov // The pass handles allocations that have a single dealloc op in the 115*6de04e6fSDennis Filimonov // same block. We also should not hoist the dealloc op out of 116*6de04e6fSDennis Filimonov // conditionals. 117*6de04e6fSDennis Filimonov return WalkResult::advance(); 118*6de04e6fSDennis Filimonov } 119*6de04e6fSDennis Filimonov 120*6de04e6fSDennis Filimonov Operation *lastUser = nullptr; 121*6de04e6fSDennis Filimonov const BufferViewFlowAnalysis::ValueSetT &deps = 122*6de04e6fSDennis Filimonov analysis.resolve(allocOp->getResult(0)); 123*6de04e6fSDennis Filimonov for (auto dep : llvm::make_early_inc_range(deps)) { 124*6de04e6fSDennis Filimonov for (auto user : dep.getUsers()) { 125*6de04e6fSDennis Filimonov // We are looking for a non dealloc op user. 126*6de04e6fSDennis Filimonov // check if user is the dealloc op itself. 127*6de04e6fSDennis Filimonov if (user == deallocOp) 128*6de04e6fSDennis Filimonov continue; 129*6de04e6fSDennis Filimonov 130*6de04e6fSDennis Filimonov // find the ancestor of user that is in the same block as the allocOp. 131*6de04e6fSDennis Filimonov auto topUser = allocOp->getBlock()->findAncestorOpInBlock(*user); 132*6de04e6fSDennis Filimonov if (!lastUser || happensBefore(lastUser, topUser)) { 133*6de04e6fSDennis Filimonov lastUser = topUser; 134*6de04e6fSDennis Filimonov } 135*6de04e6fSDennis Filimonov } 136*6de04e6fSDennis Filimonov } 137*6de04e6fSDennis Filimonov if (lastUser == nullptr) { 138*6de04e6fSDennis Filimonov return WalkResult::advance(); 139*6de04e6fSDennis Filimonov } 140*6de04e6fSDennis Filimonov LDBG("Last user found: " << *lastUser); 141*6de04e6fSDennis Filimonov assert(lastUser->getBlock() == allocOp->getBlock()); 142*6de04e6fSDennis Filimonov assert(lastUser->getBlock() == deallocOp->getBlock()); 143*6de04e6fSDennis Filimonov // Move the dealloc op after the last user. 144*6de04e6fSDennis Filimonov deallocOp->moveAfter(lastUser); 145*6de04e6fSDennis Filimonov LDBG("Moved dealloc op after: " << *lastUser); 146*6de04e6fSDennis Filimonov 147*6de04e6fSDennis Filimonov return WalkResult::advance(); 148*6de04e6fSDennis Filimonov }); 149*6de04e6fSDennis Filimonov } 150*6de04e6fSDennis Filimonov }; 151*6de04e6fSDennis Filimonov 152*6de04e6fSDennis Filimonov } // end anonymous namespace 153*6de04e6fSDennis Filimonov 154*6de04e6fSDennis Filimonov //===----------------------------------------------------------------------===// 155*6de04e6fSDennis Filimonov // OptimizeAllocatinliveness construction 156*6de04e6fSDennis Filimonov //===----------------------------------------------------------------------===// 157*6de04e6fSDennis Filimonov 158*6de04e6fSDennis Filimonov std::unique_ptr<Pass> 159*6de04e6fSDennis Filimonov mlir::bufferization::createOptimizeAllocationLivenessPass() { 160*6de04e6fSDennis Filimonov return std::make_unique<OptimizeAllocationLiveness>(); 161*6de04e6fSDennis Filimonov } 162