MLIR 22.0.0git
OptimizeAllocationLiveness.cpp
Go to the documentation of this file.
1//===- OptimizeAllocationLiveness.cpp - impl. optimize allocation liveness pass
2//-===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements a pass for optimizing allocation liveness.
11// The pass moves the deallocation operation after the last user of the
12// allocated buffer.
13//===----------------------------------------------------------------------===//
14
18#include "mlir/IR/Operation.h"
19#include "mlir/IR/Value.h"
21#include "llvm/Support/DebugLog.h"
22
23#define DEBUG_TYPE "optimize-allocation-liveness"
24
25namespace mlir {
26namespace bufferization {
27#define GEN_PASS_DEF_OPTIMIZEALLOCATIONLIVENESSPASS
28#include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"
29} // namespace bufferization
30} // namespace mlir
31
32using namespace mlir;
33
34namespace {
35
36//===----------------------------------------------------------------------===//
37// Helper functions
38//===----------------------------------------------------------------------===//
39
40/// Return true if `a` happens before `b`, i.e., `a` or one of its ancestors
41/// properly dominates `b` and `b` is not inside `a`.
42static bool happensBefore(Operation *a, Operation *b) {
43 do {
44 if (a->isProperAncestor(b))
45 return false;
46 if (Operation *bAncestor = a->getBlock()->findAncestorOpInBlock(*b)) {
47 return a->isBeforeInBlock(bAncestor);
48 }
49 } while ((a = a->getParentOp()));
50 return false;
51}
52
53/// This method searches for a user of value that is a dealloc operation.
54/// If multiple users with free effect are found, return nullptr.
55Operation *findUserWithFreeSideEffect(Value value) {
56 Operation *freeOpUser = nullptr;
57 for (Operation *user : value.getUsers()) {
58 if (MemoryEffectOpInterface memEffectOp =
59 dyn_cast<MemoryEffectOpInterface>(user)) {
61 memEffectOp.getEffects(effects);
62
63 for (const auto &effect : effects) {
64 if (isa<MemoryEffects::Free>(effect.getEffect())) {
65 if (freeOpUser) {
66 LDBG() << "Multiple users with free effect found: " << *freeOpUser
67 << " and " << *user;
68 return nullptr;
69 }
70 freeOpUser = user;
71 }
72 }
73 }
74 }
75 return freeOpUser;
76}
77
78/// Checks if the given op allocates memory.
79static bool hasMemoryAllocEffect(MemoryEffectOpInterface memEffectOp) {
81 memEffectOp.getEffects(effects);
82 for (const auto &effect : effects) {
83 if (isa<MemoryEffects::Allocate>(effect.getEffect())) {
84 return true;
85 }
86 }
87 return false;
88}
89
90/// Extracts OpResult's with Allocate effects from given op
92collectAllocations(MemoryEffectOpInterface allocOp) {
94 allocOp.getEffects(effects);
95 SmallVector<OpResult> allocResults;
96 for (const MemoryEffects::EffectInstance &it : effects)
97 if (isa<MemoryEffects::Allocate>(it.getEffect()))
98 if (auto val = it.getValue(); val && val.getDefiningOp() == allocOp)
99 allocResults.push_back(cast<OpResult>(val));
100 return allocResults;
101}
102
103struct OptimizeAllocationLiveness
105 OptimizeAllocationLiveness> {
106public:
107 OptimizeAllocationLiveness() = default;
108
109 void runOnOperation() override {
110 func::FuncOp func = getOperation();
111
112 if (func.isExternal())
113 return;
114
115 BufferViewFlowAnalysis analysis = BufferViewFlowAnalysis(func);
116
117 func.walk([&](MemoryEffectOpInterface memEffectOp) -> WalkResult {
118 if (!hasMemoryAllocEffect(memEffectOp))
119 return WalkResult::advance();
120
121 auto allocOp = memEffectOp;
122 LDBG() << "Checking alloc op: " << allocOp;
123
124 SmallVector<OpResult> allocationResults = collectAllocations(allocOp);
125 // Multiple allocations from a single op are not considered here yet.
126 if (allocationResults.size() != 1)
127 return WalkResult::advance();
128
129 OpResult allocResult = allocationResults[0];
130 LDBG() << "On allocation result: " << allocResult;
131
132 auto *deallocOp = findUserWithFreeSideEffect(allocResult);
133 if (!deallocOp || (deallocOp->getBlock() != allocOp->getBlock())) {
134 // The pass handles allocations that have a single dealloc op in the
135 // same block. We also should not hoist the dealloc op out of
136 // conditionals.
137 return WalkResult::advance();
138 }
139
140 Operation *lastUser = nullptr;
142 analysis.resolve(allocResult);
143 for (auto dep : llvm::make_early_inc_range(deps)) {
144 for (auto *user : dep.getUsers()) {
145 // We are looking for a non dealloc op user.
146 // check if user is the dealloc op itself.
147 if (user == deallocOp)
148 continue;
149
150 // find the ancestor of user that is in the same block as the allocOp.
151 auto *topUser = allocOp->getBlock()->findAncestorOpInBlock(*user);
152 if (!lastUser || happensBefore(lastUser, topUser)) {
153 lastUser = topUser;
154 }
155 }
156 }
157 if (lastUser == nullptr) {
158 return WalkResult::advance();
159 }
160 LDBG() << "Last user found: " << *lastUser;
161 assert(lastUser->getBlock() == allocOp->getBlock());
162 assert(lastUser->getBlock() == deallocOp->getBlock());
163 // Move the dealloc op after the last user.
164 deallocOp->moveAfter(lastUser);
165 LDBG() << "Moved dealloc op after: " << *lastUser;
166
167 return WalkResult::advance();
168 });
169 }
170};
171
172} // end anonymous namespace
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static bool happensBefore(Operation *a, Operation *b, const DominanceInfo &domInfo)
Return true if a happens before b, i.e., a or one of its ancestors properly dominates b and b is not ...
Operation * findAncestorOpInBlock(Operation &op)
Returns 'op' if 'op' lies in this block, or otherwise finds the ancestor operation of 'op' that lies ...
Definition Block.cpp:74
SmallPtrSet< Value, 16 > ValueSetT
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:213
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:234
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
user_range getUsers() const
Definition Value.h:218
static WalkResult advance()
Definition WalkResult.h:47
SideEffects::EffectInstance< Effect > EffectInstance
detail::InFlightRemark analysis(Location loc, RemarkOpts opts)
Report an optimization analysis remark.
Definition Remarks.h:567
Include the generated interface declarations.