xref: /llvm-project/mlir/lib/Dialect/Bufferization/Transforms/OptimizeAllocationLiveness.cpp (revision 6de04e6fe8b1520ef3e4073ff222e623b7dc9cb9)
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