//===- OptimizeAllocationLiveness.cpp - impl. optimize allocation liveness pass //-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements a pass for optimizing allocation liveness. // The pass moves the deallocation operation after the last user of the // allocated buffer. //===----------------------------------------------------------------------===// #include "mlir/Dialect/Bufferization/Transforms/BufferViewFlowAnalysis.h" #include "mlir/Dialect/Bufferization/Transforms/Passes.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/MemRef/IR/MemRef.h" #include "mlir/IR/Operation.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "optimize-allocation-liveness" #define DBGS() (llvm::dbgs() << '[' << DEBUG_TYPE << "] ") #define LDBG(X) LLVM_DEBUG(DBGS() << X << "\n") namespace mlir { namespace bufferization { #define GEN_PASS_DEF_OPTIMIZEALLOCATIONLIVENESS #include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc" } // namespace bufferization } // namespace mlir using namespace mlir; namespace { //===----------------------------------------------------------------------===// // Helper functions //===----------------------------------------------------------------------===// /// Return true if `a` happens before `b`, i.e., `a` or one of its ancestors /// properly dominates `b` and `b` is not inside `a`. static bool happensBefore(Operation *a, Operation *b) { do { if (a->isProperAncestor(b)) return false; if (Operation *bAncestor = a->getBlock()->findAncestorOpInBlock(*b)) { return a->isBeforeInBlock(bAncestor); } } while ((a = a->getParentOp())); return false; } /// This method searches for a user of value that is a dealloc operation. /// If multiple users with free effect are found, return nullptr. Operation *findUserWithFreeSideEffect(Value value) { Operation *freeOpUser = nullptr; for (Operation *user : value.getUsers()) { if (MemoryEffectOpInterface memEffectOp = dyn_cast(user)) { SmallVector effects; memEffectOp.getEffects(effects); for (const auto &effect : effects) { if (isa(effect.getEffect())) { if (freeOpUser) { LDBG("Multiple users with free effect found: " << *freeOpUser << " and " << *user); return nullptr; } freeOpUser = user; } } } } return freeOpUser; } /// Checks if the given op allocates memory. static bool hasMemoryAllocEffect(MemoryEffectOpInterface memEffectOp) { SmallVector effects; memEffectOp.getEffects(effects); for (const auto &effect : effects) { if (isa(effect.getEffect())) { return true; } } return false; } struct OptimizeAllocationLiveness : public bufferization::impl::OptimizeAllocationLivenessBase< OptimizeAllocationLiveness> { public: OptimizeAllocationLiveness() = default; void runOnOperation() override { func::FuncOp func = getOperation(); if (func.isExternal()) return; BufferViewFlowAnalysis analysis = BufferViewFlowAnalysis(func); func.walk([&](MemoryEffectOpInterface memEffectOp) -> WalkResult { if (!hasMemoryAllocEffect(memEffectOp)) return WalkResult::advance(); auto allocOp = memEffectOp; LDBG("Checking alloc op: " << allocOp); auto deallocOp = findUserWithFreeSideEffect(allocOp->getResult(0)); if (!deallocOp || (deallocOp->getBlock() != allocOp->getBlock())) { // The pass handles allocations that have a single dealloc op in the // same block. We also should not hoist the dealloc op out of // conditionals. return WalkResult::advance(); } Operation *lastUser = nullptr; const BufferViewFlowAnalysis::ValueSetT &deps = analysis.resolve(allocOp->getResult(0)); for (auto dep : llvm::make_early_inc_range(deps)) { for (auto user : dep.getUsers()) { // We are looking for a non dealloc op user. // check if user is the dealloc op itself. if (user == deallocOp) continue; // find the ancestor of user that is in the same block as the allocOp. auto topUser = allocOp->getBlock()->findAncestorOpInBlock(*user); if (!lastUser || happensBefore(lastUser, topUser)) { lastUser = topUser; } } } if (lastUser == nullptr) { return WalkResult::advance(); } LDBG("Last user found: " << *lastUser); assert(lastUser->getBlock() == allocOp->getBlock()); assert(lastUser->getBlock() == deallocOp->getBlock()); // Move the dealloc op after the last user. deallocOp->moveAfter(lastUser); LDBG("Moved dealloc op after: " << *lastUser); return WalkResult::advance(); }); } }; } // end anonymous namespace //===----------------------------------------------------------------------===// // OptimizeAllocatinliveness construction //===----------------------------------------------------------------------===// std::unique_ptr mlir::bufferization::createOptimizeAllocationLivenessPass() { return std::make_unique(); }