1f89bb3c0SAlexander Belyaev //===- BufferDeallocation.cpp - the impl for buffer deallocation ----------===//
2f89bb3c0SAlexander Belyaev //
3f89bb3c0SAlexander Belyaev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f89bb3c0SAlexander Belyaev // See https://llvm.org/LICENSE.txt for license information.
5f89bb3c0SAlexander Belyaev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f89bb3c0SAlexander Belyaev //
7f89bb3c0SAlexander Belyaev //===----------------------------------------------------------------------===//
8f89bb3c0SAlexander Belyaev //
9f89bb3c0SAlexander Belyaev // This file implements logic for computing correct alloc and dealloc positions.
10f89bb3c0SAlexander Belyaev // Furthermore, buffer deallocation also adds required new clone operations to
11f89bb3c0SAlexander Belyaev // ensure that all buffers are deallocated. The main class is the
12f89bb3c0SAlexander Belyaev // BufferDeallocationPass class that implements the underlying algorithm. In
13f89bb3c0SAlexander Belyaev // order to put allocations and deallocations at safe positions, it is
14f89bb3c0SAlexander Belyaev // significantly important to put them into the correct blocks. However, the
15f89bb3c0SAlexander Belyaev // liveness analysis does not pay attention to aliases, which can occur due to
16f89bb3c0SAlexander Belyaev // branches (and their associated block arguments) in general. For this purpose,
17f89bb3c0SAlexander Belyaev // BufferDeallocation firstly finds all possible aliases for a single value
18f89bb3c0SAlexander Belyaev // (using the BufferViewFlowAnalysis class). Consider the following example:
19f89bb3c0SAlexander Belyaev //
20f89bb3c0SAlexander Belyaev // ^bb0(%arg0):
21ace01605SRiver Riddle //   cf.cond_br %cond, ^bb1, ^bb2
22f89bb3c0SAlexander Belyaev // ^bb1:
23ace01605SRiver Riddle //   cf.br ^exit(%arg0)
24f89bb3c0SAlexander Belyaev // ^bb2:
25f89bb3c0SAlexander Belyaev //   %new_value = ...
26ace01605SRiver Riddle //   cf.br ^exit(%new_value)
27f89bb3c0SAlexander Belyaev // ^exit(%arg1):
28f89bb3c0SAlexander Belyaev //   return %arg1;
29f89bb3c0SAlexander Belyaev //
30f89bb3c0SAlexander Belyaev // We should place the dealloc for %new_value in exit. However, we have to free
31f89bb3c0SAlexander Belyaev // the buffer in the same block, because it cannot be freed in the post
32f89bb3c0SAlexander Belyaev // dominator. However, this requires a new clone buffer for %arg1 that will
33f89bb3c0SAlexander Belyaev // contain the actual contents. Using the class BufferViewFlowAnalysis, we
34f89bb3c0SAlexander Belyaev // will find out that %new_value has a potential alias %arg1. In order to find
35f89bb3c0SAlexander Belyaev // the dealloc position we have to find all potential aliases, iterate over
36f89bb3c0SAlexander Belyaev // their uses and find the common post-dominator block (note that additional
37f89bb3c0SAlexander Belyaev // clones and buffers remove potential aliases and will influence the placement
38f89bb3c0SAlexander Belyaev // of the deallocs). In all cases, the computed block can be safely used to free
39f89bb3c0SAlexander Belyaev // the %new_value buffer (may be exit or bb2) as it will die and we can use
40f89bb3c0SAlexander Belyaev // liveness information to determine the exact operation after which we have to
41f89bb3c0SAlexander Belyaev // insert the dealloc. However, the algorithm supports introducing clone buffers
42f89bb3c0SAlexander Belyaev // and placing deallocs in safe locations to ensure that all buffers will be
43f89bb3c0SAlexander Belyaev // freed in the end.
44f89bb3c0SAlexander Belyaev //
45f89bb3c0SAlexander Belyaev // TODO:
46f89bb3c0SAlexander Belyaev // The current implementation does not support explicit-control-flow loops and
47f89bb3c0SAlexander Belyaev // the resulting code will be invalid with respect to program semantics.
48f89bb3c0SAlexander Belyaev // However, structured control-flow loops are fully supported. Furthermore, it
49f89bb3c0SAlexander Belyaev // doesn't accept functions which return buffers already.
50f89bb3c0SAlexander Belyaev //
51f89bb3c0SAlexander Belyaev //===----------------------------------------------------------------------===//
52f89bb3c0SAlexander Belyaev 
53f89bb3c0SAlexander Belyaev #include "PassDetail.h"
54f89bb3c0SAlexander Belyaev 
55f89bb3c0SAlexander Belyaev #include "mlir/Dialect/Bufferization/IR/AllocationOpInterface.h"
56f89bb3c0SAlexander Belyaev #include "mlir/Dialect/Bufferization/IR/Bufferization.h"
570e9a4a3bSRiver Riddle #include "mlir/Dialect/Bufferization/Transforms/BufferUtils.h"
58f89bb3c0SAlexander Belyaev #include "mlir/Dialect/Bufferization/Transforms/Passes.h"
59f89bb3c0SAlexander Belyaev #include "mlir/Dialect/MemRef/IR/MemRef.h"
60f89bb3c0SAlexander Belyaev #include "llvm/ADT/SetOperations.h"
61f89bb3c0SAlexander Belyaev 
62f89bb3c0SAlexander Belyaev using namespace mlir;
630e9a4a3bSRiver Riddle using namespace mlir::bufferization;
64f89bb3c0SAlexander Belyaev 
65f89bb3c0SAlexander Belyaev /// Walks over all immediate return-like terminators in the given region.
66f89bb3c0SAlexander Belyaev static LogicalResult
walkReturnOperations(Region * region,llvm::function_ref<LogicalResult (Operation *)> func)67f89bb3c0SAlexander Belyaev walkReturnOperations(Region *region,
681fc096afSMehdi Amini                      llvm::function_ref<LogicalResult(Operation *)> func) {
69f89bb3c0SAlexander Belyaev   for (Block &block : *region) {
70f89bb3c0SAlexander Belyaev     Operation *terminator = block.getTerminator();
71f89bb3c0SAlexander Belyaev     // Skip non region-return-like terminators.
72f89bb3c0SAlexander Belyaev     if (isRegionReturnLike(terminator)) {
73f89bb3c0SAlexander Belyaev       if (failed(func(terminator)))
74f89bb3c0SAlexander Belyaev         return failure();
75f89bb3c0SAlexander Belyaev     }
76f89bb3c0SAlexander Belyaev   }
77f89bb3c0SAlexander Belyaev   return success();
78f89bb3c0SAlexander Belyaev }
79f89bb3c0SAlexander Belyaev 
80c076fa1cSMatthias Springer /// Checks if all operations that have at least one attached region implement
81c076fa1cSMatthias Springer /// the RegionBranchOpInterface. This is not required in edge cases, where we
82c076fa1cSMatthias Springer /// have a single attached region and the parent operation has no results.
validateSupportedControlFlow(Operation * op)83c076fa1cSMatthias Springer static bool validateSupportedControlFlow(Operation *op) {
84c076fa1cSMatthias Springer   WalkResult result = op->walk([&](Operation *operation) {
85c076fa1cSMatthias Springer     // Only check ops that are inside a function.
8658ceae95SRiver Riddle     if (!operation->getParentOfType<func::FuncOp>())
87c076fa1cSMatthias Springer       return WalkResult::advance();
88c076fa1cSMatthias Springer 
89f89bb3c0SAlexander Belyaev     auto regions = operation->getRegions();
90f89bb3c0SAlexander Belyaev     // Walk over all operations in a region and check if the operation has at
91f89bb3c0SAlexander Belyaev     // least one region and implements the RegionBranchOpInterface. If there
92f89bb3c0SAlexander Belyaev     // is an operation that does not fulfill this condition, we cannot apply
93f89bb3c0SAlexander Belyaev     // the deallocation steps. Furthermore, we accept cases, where we have a
94f89bb3c0SAlexander Belyaev     // region that returns no results, since, in that case, the intra-region
95f89bb3c0SAlexander Belyaev     // control flow does not affect the transformation.
96f89bb3c0SAlexander Belyaev     size_t size = regions.size();
97f89bb3c0SAlexander Belyaev     if (((size == 1 && !operation->getResults().empty()) || size > 1) &&
98f89bb3c0SAlexander Belyaev         !dyn_cast<RegionBranchOpInterface>(operation)) {
99f89bb3c0SAlexander Belyaev       operation->emitError("All operations with attached regions need to "
100f89bb3c0SAlexander Belyaev                            "implement the RegionBranchOpInterface.");
101f89bb3c0SAlexander Belyaev     }
102c076fa1cSMatthias Springer 
103c076fa1cSMatthias Springer     return WalkResult::advance();
104f89bb3c0SAlexander Belyaev   });
105c076fa1cSMatthias Springer   return !result.wasSkipped();
106f89bb3c0SAlexander Belyaev }
107f89bb3c0SAlexander Belyaev 
108f89bb3c0SAlexander Belyaev namespace {
109f89bb3c0SAlexander Belyaev 
110f89bb3c0SAlexander Belyaev //===----------------------------------------------------------------------===//
111f89bb3c0SAlexander Belyaev // Backedges analysis
112f89bb3c0SAlexander Belyaev //===----------------------------------------------------------------------===//
113f89bb3c0SAlexander Belyaev 
114f89bb3c0SAlexander Belyaev /// A straight-forward program analysis which detects loop backedges induced by
115f89bb3c0SAlexander Belyaev /// explicit control flow.
116f89bb3c0SAlexander Belyaev class Backedges {
117f89bb3c0SAlexander Belyaev public:
118f89bb3c0SAlexander Belyaev   using BlockSetT = SmallPtrSet<Block *, 16>;
119f89bb3c0SAlexander Belyaev   using BackedgeSetT = llvm::DenseSet<std::pair<Block *, Block *>>;
120f89bb3c0SAlexander Belyaev 
121f89bb3c0SAlexander Belyaev public:
122f89bb3c0SAlexander Belyaev   /// Constructs a new backedges analysis using the op provided.
Backedges(Operation * op)123f89bb3c0SAlexander Belyaev   Backedges(Operation *op) { recurse(op); }
124f89bb3c0SAlexander Belyaev 
125f89bb3c0SAlexander Belyaev   /// Returns the number of backedges formed by explicit control flow.
size() const126f89bb3c0SAlexander Belyaev   size_t size() const { return edgeSet.size(); }
127f89bb3c0SAlexander Belyaev 
128f89bb3c0SAlexander Belyaev   /// Returns the start iterator to loop over all backedges.
begin() const129f89bb3c0SAlexander Belyaev   BackedgeSetT::const_iterator begin() const { return edgeSet.begin(); }
130f89bb3c0SAlexander Belyaev 
131f89bb3c0SAlexander Belyaev   /// Returns the end iterator to loop over all backedges.
end() const132f89bb3c0SAlexander Belyaev   BackedgeSetT::const_iterator end() const { return edgeSet.end(); }
133f89bb3c0SAlexander Belyaev 
134f89bb3c0SAlexander Belyaev private:
135f89bb3c0SAlexander Belyaev   /// Enters the current block and inserts a backedge into the `edgeSet` if we
136f89bb3c0SAlexander Belyaev   /// have already visited the current block. The inserted edge links the given
137f89bb3c0SAlexander Belyaev   /// `predecessor` with the `current` block.
enter(Block & current,Block * predecessor)138f89bb3c0SAlexander Belyaev   bool enter(Block &current, Block *predecessor) {
139f89bb3c0SAlexander Belyaev     bool inserted = visited.insert(&current).second;
140f89bb3c0SAlexander Belyaev     if (!inserted)
141f89bb3c0SAlexander Belyaev       edgeSet.insert(std::make_pair(predecessor, &current));
142f89bb3c0SAlexander Belyaev     return inserted;
143f89bb3c0SAlexander Belyaev   }
144f89bb3c0SAlexander Belyaev 
145f89bb3c0SAlexander Belyaev   /// Leaves the current block.
exit(Block & current)146f89bb3c0SAlexander Belyaev   void exit(Block &current) { visited.erase(&current); }
147f89bb3c0SAlexander Belyaev 
148f89bb3c0SAlexander Belyaev   /// Recurses into the given operation while taking all attached regions into
149f89bb3c0SAlexander Belyaev   /// account.
recurse(Operation * op)150f89bb3c0SAlexander Belyaev   void recurse(Operation *op) {
151f89bb3c0SAlexander Belyaev     Block *current = op->getBlock();
152f89bb3c0SAlexander Belyaev     // If the current op implements the `BranchOpInterface`, there can be
153f89bb3c0SAlexander Belyaev     // cycles in the scope of all successor blocks.
154f89bb3c0SAlexander Belyaev     if (isa<BranchOpInterface>(op)) {
155f89bb3c0SAlexander Belyaev       for (Block *succ : current->getSuccessors())
156f89bb3c0SAlexander Belyaev         recurse(*succ, current);
157f89bb3c0SAlexander Belyaev     }
158f89bb3c0SAlexander Belyaev     // Recurse into all distinct regions and check for explicit control-flow
159f89bb3c0SAlexander Belyaev     // loops.
160f89bb3c0SAlexander Belyaev     for (Region &region : op->getRegions()) {
161f89bb3c0SAlexander Belyaev       if (!region.empty())
162f89bb3c0SAlexander Belyaev         recurse(region.front(), current);
163f89bb3c0SAlexander Belyaev     }
164f89bb3c0SAlexander Belyaev   }
165f89bb3c0SAlexander Belyaev 
166f89bb3c0SAlexander Belyaev   /// Recurses into explicit control-flow structures that are given by
167f89bb3c0SAlexander Belyaev   /// the successor relation defined on the block level.
recurse(Block & block,Block * predecessor)168f89bb3c0SAlexander Belyaev   void recurse(Block &block, Block *predecessor) {
169f89bb3c0SAlexander Belyaev     // Try to enter the current block. If this is not possible, we are
170f89bb3c0SAlexander Belyaev     // currently processing this block and can safely return here.
171f89bb3c0SAlexander Belyaev     if (!enter(block, predecessor))
172f89bb3c0SAlexander Belyaev       return;
173f89bb3c0SAlexander Belyaev 
174f89bb3c0SAlexander Belyaev     // Recurse into all operations and successor blocks.
175f89bb3c0SAlexander Belyaev     for (Operation &op : block.getOperations())
176f89bb3c0SAlexander Belyaev       recurse(&op);
177f89bb3c0SAlexander Belyaev 
178f89bb3c0SAlexander Belyaev     // Leave the current block.
179f89bb3c0SAlexander Belyaev     exit(block);
180f89bb3c0SAlexander Belyaev   }
181f89bb3c0SAlexander Belyaev 
182f89bb3c0SAlexander Belyaev   /// Stores all blocks that are currently visited and on the processing stack.
183f89bb3c0SAlexander Belyaev   BlockSetT visited;
184f89bb3c0SAlexander Belyaev 
185f89bb3c0SAlexander Belyaev   /// Stores all backedges in the format (source, target).
186f89bb3c0SAlexander Belyaev   BackedgeSetT edgeSet;
187f89bb3c0SAlexander Belyaev };
188f89bb3c0SAlexander Belyaev 
189f89bb3c0SAlexander Belyaev //===----------------------------------------------------------------------===//
190f89bb3c0SAlexander Belyaev // BufferDeallocation
191f89bb3c0SAlexander Belyaev //===----------------------------------------------------------------------===//
192f89bb3c0SAlexander Belyaev 
193f89bb3c0SAlexander Belyaev /// The buffer deallocation transformation which ensures that all allocs in the
194f89bb3c0SAlexander Belyaev /// program have a corresponding de-allocation. As a side-effect, it might also
195f89bb3c0SAlexander Belyaev /// introduce clones that in turn leads to additional deallocations.
196f89bb3c0SAlexander Belyaev class BufferDeallocation : public BufferPlacementTransformationBase {
197f89bb3c0SAlexander Belyaev public:
198f89bb3c0SAlexander Belyaev   using AliasAllocationMapT =
199f89bb3c0SAlexander Belyaev       llvm::DenseMap<Value, bufferization::AllocationOpInterface>;
200f89bb3c0SAlexander Belyaev 
BufferDeallocation(Operation * op)201f89bb3c0SAlexander Belyaev   BufferDeallocation(Operation *op)
202f89bb3c0SAlexander Belyaev       : BufferPlacementTransformationBase(op), dominators(op),
203f89bb3c0SAlexander Belyaev         postDominators(op) {}
204f89bb3c0SAlexander Belyaev 
205f89bb3c0SAlexander Belyaev   /// Checks if all allocation operations either provide an already existing
206f89bb3c0SAlexander Belyaev   /// deallocation operation or implement the AllocationOpInterface. In
207f89bb3c0SAlexander Belyaev   /// addition, this method initializes the internal alias to
208f89bb3c0SAlexander Belyaev   /// AllocationOpInterface mapping in order to get compatible
209f89bb3c0SAlexander Belyaev   /// AllocationOpInterface implementations for aliases.
prepare()210f89bb3c0SAlexander Belyaev   LogicalResult prepare() {
211f89bb3c0SAlexander Belyaev     for (const BufferPlacementAllocs::AllocEntry &entry : allocs) {
212f89bb3c0SAlexander Belyaev       // Get the defining allocation operation.
213f89bb3c0SAlexander Belyaev       Value alloc = std::get<0>(entry);
214f89bb3c0SAlexander Belyaev       auto allocationInterface =
215f89bb3c0SAlexander Belyaev           alloc.getDefiningOp<bufferization::AllocationOpInterface>();
216f89bb3c0SAlexander Belyaev       // If there is no existing deallocation operation and no implementation of
217f89bb3c0SAlexander Belyaev       // the AllocationOpInterface, we cannot apply the BufferDeallocation pass.
218f89bb3c0SAlexander Belyaev       if (!std::get<1>(entry) && !allocationInterface) {
219f89bb3c0SAlexander Belyaev         return alloc.getDefiningOp()->emitError(
220f89bb3c0SAlexander Belyaev             "Allocation is not deallocated explicitly nor does the operation "
221f89bb3c0SAlexander Belyaev             "implement the AllocationOpInterface.");
222f89bb3c0SAlexander Belyaev       }
223f89bb3c0SAlexander Belyaev 
224f89bb3c0SAlexander Belyaev       // Register the current allocation interface implementation.
225f89bb3c0SAlexander Belyaev       aliasToAllocations[alloc] = allocationInterface;
226f89bb3c0SAlexander Belyaev 
227f89bb3c0SAlexander Belyaev       // Get the alias information for the current allocation node.
228*c730f9a1SKazu Hirata       for (Value alias : aliases.resolve(alloc)) {
229f89bb3c0SAlexander Belyaev         // TODO: check for incompatible implementations of the
230f89bb3c0SAlexander Belyaev         // AllocationOpInterface. This could be realized by promoting the
231f89bb3c0SAlexander Belyaev         // AllocationOpInterface to a DialectInterface.
232f89bb3c0SAlexander Belyaev         aliasToAllocations[alias] = allocationInterface;
233*c730f9a1SKazu Hirata       }
234f89bb3c0SAlexander Belyaev     }
235f89bb3c0SAlexander Belyaev     return success();
236f89bb3c0SAlexander Belyaev   }
237f89bb3c0SAlexander Belyaev 
238f89bb3c0SAlexander Belyaev   /// Performs the actual placement/creation of all temporary clone and dealloc
239f89bb3c0SAlexander Belyaev   /// nodes.
deallocate()240f89bb3c0SAlexander Belyaev   LogicalResult deallocate() {
241f89bb3c0SAlexander Belyaev     // Add additional clones that are required.
242f89bb3c0SAlexander Belyaev     if (failed(introduceClones()))
243f89bb3c0SAlexander Belyaev       return failure();
244f89bb3c0SAlexander Belyaev 
245f89bb3c0SAlexander Belyaev     // Place deallocations for all allocation entries.
246f89bb3c0SAlexander Belyaev     return placeDeallocs();
247f89bb3c0SAlexander Belyaev   }
248f89bb3c0SAlexander Belyaev 
249f89bb3c0SAlexander Belyaev private:
250f89bb3c0SAlexander Belyaev   /// Introduces required clone operations to avoid memory leaks.
introduceClones()251f89bb3c0SAlexander Belyaev   LogicalResult introduceClones() {
252f89bb3c0SAlexander Belyaev     // Initialize the set of values that require a dedicated memory free
253f89bb3c0SAlexander Belyaev     // operation since their operands cannot be safely deallocated in a post
254f89bb3c0SAlexander Belyaev     // dominator.
255f89bb3c0SAlexander Belyaev     SmallPtrSet<Value, 8> valuesToFree;
256f89bb3c0SAlexander Belyaev     llvm::SmallDenseSet<std::tuple<Value, Block *>> visitedValues;
257f89bb3c0SAlexander Belyaev     SmallVector<std::tuple<Value, Block *>, 8> toProcess;
258f89bb3c0SAlexander Belyaev 
259f89bb3c0SAlexander Belyaev     // Check dominance relation for proper dominance properties. If the given
260f89bb3c0SAlexander Belyaev     // value node does not dominate an alias, we will have to create a clone in
261f89bb3c0SAlexander Belyaev     // order to free all buffers that can potentially leak into a post
262f89bb3c0SAlexander Belyaev     // dominator.
263f89bb3c0SAlexander Belyaev     auto findUnsafeValues = [&](Value source, Block *definingBlock) {
264f89bb3c0SAlexander Belyaev       auto it = aliases.find(source);
265f89bb3c0SAlexander Belyaev       if (it == aliases.end())
266f89bb3c0SAlexander Belyaev         return;
267f89bb3c0SAlexander Belyaev       for (Value value : it->second) {
268f89bb3c0SAlexander Belyaev         if (valuesToFree.count(value) > 0)
269f89bb3c0SAlexander Belyaev           continue;
270f89bb3c0SAlexander Belyaev         Block *parentBlock = value.getParentBlock();
271f89bb3c0SAlexander Belyaev         // Check whether we have to free this particular block argument or
272f89bb3c0SAlexander Belyaev         // generic value. We have to free the current alias if it is either
273f89bb3c0SAlexander Belyaev         // defined in a non-dominated block or it is defined in the same block
274f89bb3c0SAlexander Belyaev         // but the current value is not dominated by the source value.
275f89bb3c0SAlexander Belyaev         if (!dominators.dominates(definingBlock, parentBlock) ||
276f89bb3c0SAlexander Belyaev             (definingBlock == parentBlock && value.isa<BlockArgument>())) {
277f89bb3c0SAlexander Belyaev           toProcess.emplace_back(value, parentBlock);
278f89bb3c0SAlexander Belyaev           valuesToFree.insert(value);
279f89bb3c0SAlexander Belyaev         } else if (visitedValues.insert(std::make_tuple(value, definingBlock))
280f89bb3c0SAlexander Belyaev                        .second)
281f89bb3c0SAlexander Belyaev           toProcess.emplace_back(value, definingBlock);
282f89bb3c0SAlexander Belyaev       }
283f89bb3c0SAlexander Belyaev     };
284f89bb3c0SAlexander Belyaev 
285f89bb3c0SAlexander Belyaev     // Detect possibly unsafe aliases starting from all allocations.
286f89bb3c0SAlexander Belyaev     for (BufferPlacementAllocs::AllocEntry &entry : allocs) {
287f89bb3c0SAlexander Belyaev       Value allocValue = std::get<0>(entry);
288f89bb3c0SAlexander Belyaev       findUnsafeValues(allocValue, allocValue.getDefiningOp()->getBlock());
289f89bb3c0SAlexander Belyaev     }
290f89bb3c0SAlexander Belyaev     // Try to find block arguments that require an explicit free operation
291f89bb3c0SAlexander Belyaev     // until we reach a fix point.
292f89bb3c0SAlexander Belyaev     while (!toProcess.empty()) {
293f89bb3c0SAlexander Belyaev       auto current = toProcess.pop_back_val();
294f89bb3c0SAlexander Belyaev       findUnsafeValues(std::get<0>(current), std::get<1>(current));
295f89bb3c0SAlexander Belyaev     }
296f89bb3c0SAlexander Belyaev 
297f89bb3c0SAlexander Belyaev     // Update buffer aliases to ensure that we free all buffers and block
298f89bb3c0SAlexander Belyaev     // arguments at the correct locations.
299f89bb3c0SAlexander Belyaev     aliases.remove(valuesToFree);
300f89bb3c0SAlexander Belyaev 
301f89bb3c0SAlexander Belyaev     // Add new allocs and additional clone operations.
302f89bb3c0SAlexander Belyaev     for (Value value : valuesToFree) {
303f89bb3c0SAlexander Belyaev       if (failed(value.isa<BlockArgument>()
304f89bb3c0SAlexander Belyaev                      ? introduceBlockArgCopy(value.cast<BlockArgument>())
305f89bb3c0SAlexander Belyaev                      : introduceValueCopyForRegionResult(value)))
306f89bb3c0SAlexander Belyaev         return failure();
307f89bb3c0SAlexander Belyaev 
308f89bb3c0SAlexander Belyaev       // Register the value to require a final dealloc. Note that we do not have
309f89bb3c0SAlexander Belyaev       // to assign a block here since we do not want to move the allocation node
310f89bb3c0SAlexander Belyaev       // to another location.
311f89bb3c0SAlexander Belyaev       allocs.registerAlloc(std::make_tuple(value, nullptr));
312f89bb3c0SAlexander Belyaev     }
313f89bb3c0SAlexander Belyaev     return success();
314f89bb3c0SAlexander Belyaev   }
315f89bb3c0SAlexander Belyaev 
316f89bb3c0SAlexander Belyaev   /// Introduces temporary clones in all predecessors and copies the source
317f89bb3c0SAlexander Belyaev   /// values into the newly allocated buffers.
introduceBlockArgCopy(BlockArgument blockArg)318f89bb3c0SAlexander Belyaev   LogicalResult introduceBlockArgCopy(BlockArgument blockArg) {
319f89bb3c0SAlexander Belyaev     // Allocate a buffer for the current block argument in the block of
320f89bb3c0SAlexander Belyaev     // the associated value (which will be a predecessor block by
321f89bb3c0SAlexander Belyaev     // definition).
322f89bb3c0SAlexander Belyaev     Block *block = blockArg.getOwner();
323f89bb3c0SAlexander Belyaev     for (auto it = block->pred_begin(), e = block->pred_end(); it != e; ++it) {
324f89bb3c0SAlexander Belyaev       // Get the terminator and the value that will be passed to our
325f89bb3c0SAlexander Belyaev       // argument.
326f89bb3c0SAlexander Belyaev       Operation *terminator = (*it)->getTerminator();
327f89bb3c0SAlexander Belyaev       auto branchInterface = cast<BranchOpInterface>(terminator);
3280c789db5SMarkus Böck       SuccessorOperands operands =
3290c789db5SMarkus Böck           branchInterface.getSuccessorOperands(it.getSuccessorIndex());
3300c789db5SMarkus Böck 
331f89bb3c0SAlexander Belyaev       // Query the associated source value.
3320c789db5SMarkus Böck       Value sourceValue = operands[blockArg.getArgNumber()];
3330c789db5SMarkus Böck       if (!sourceValue) {
3340c789db5SMarkus Böck         return failure();
335f89bb3c0SAlexander Belyaev       }
3360c789db5SMarkus Böck       // Wire new clone and successor operand.
337f89bb3c0SAlexander Belyaev       // Create a new clone at the current location of the terminator.
338f89bb3c0SAlexander Belyaev       auto clone = introduceCloneBuffers(sourceValue, terminator);
339f89bb3c0SAlexander Belyaev       if (failed(clone))
340f89bb3c0SAlexander Belyaev         return failure();
3410c789db5SMarkus Böck       operands.slice(blockArg.getArgNumber(), 1).assign(*clone);
342f89bb3c0SAlexander Belyaev     }
343f89bb3c0SAlexander Belyaev 
344f89bb3c0SAlexander Belyaev     // Check whether the block argument has implicitly defined predecessors via
345f89bb3c0SAlexander Belyaev     // the RegionBranchOpInterface. This can be the case if the current block
346f89bb3c0SAlexander Belyaev     // argument belongs to the first block in a region and the parent operation
347f89bb3c0SAlexander Belyaev     // implements the RegionBranchOpInterface.
348f89bb3c0SAlexander Belyaev     Region *argRegion = block->getParent();
349f89bb3c0SAlexander Belyaev     Operation *parentOp = argRegion->getParentOp();
350f89bb3c0SAlexander Belyaev     RegionBranchOpInterface regionInterface;
351ff8f7904SMehdi Amini     if (&argRegion->front() != block ||
352f89bb3c0SAlexander Belyaev         !(regionInterface = dyn_cast<RegionBranchOpInterface>(parentOp)))
353f89bb3c0SAlexander Belyaev       return success();
354f89bb3c0SAlexander Belyaev 
355f89bb3c0SAlexander Belyaev     if (failed(introduceClonesForRegionSuccessors(
356f89bb3c0SAlexander Belyaev             regionInterface, argRegion->getParentOp()->getRegions(), blockArg,
357f89bb3c0SAlexander Belyaev             [&](RegionSuccessor &successorRegion) {
358f89bb3c0SAlexander Belyaev               // Find a predecessor of our argRegion.
359f89bb3c0SAlexander Belyaev               return successorRegion.getSuccessor() == argRegion;
360f89bb3c0SAlexander Belyaev             })))
361f89bb3c0SAlexander Belyaev       return failure();
362f89bb3c0SAlexander Belyaev 
363f89bb3c0SAlexander Belyaev     // Check whether the block argument belongs to an entry region of the
364f89bb3c0SAlexander Belyaev     // parent operation. In this case, we have to introduce an additional clone
365f89bb3c0SAlexander Belyaev     // for buffer that is passed to the argument.
366f89bb3c0SAlexander Belyaev     SmallVector<RegionSuccessor, 2> successorRegions;
367f89bb3c0SAlexander Belyaev     regionInterface.getSuccessorRegions(/*index=*/llvm::None, successorRegions);
368f89bb3c0SAlexander Belyaev     auto *it =
369f89bb3c0SAlexander Belyaev         llvm::find_if(successorRegions, [&](RegionSuccessor &successorRegion) {
370f89bb3c0SAlexander Belyaev           return successorRegion.getSuccessor() == argRegion;
371f89bb3c0SAlexander Belyaev         });
372f89bb3c0SAlexander Belyaev     if (it == successorRegions.end())
373f89bb3c0SAlexander Belyaev       return success();
374f89bb3c0SAlexander Belyaev 
375f89bb3c0SAlexander Belyaev     // Determine the actual operand to introduce a clone for and rewire the
376f89bb3c0SAlexander Belyaev     // operand to point to the clone instead.
377d955ca49SBenjamin Kramer     auto operands =
378d955ca49SBenjamin Kramer         regionInterface.getSuccessorEntryOperands(argRegion->getRegionNumber());
379d955ca49SBenjamin Kramer     size_t operandIndex =
380d955ca49SBenjamin Kramer         llvm::find(it->getSuccessorInputs(), blockArg).getIndex() +
381d955ca49SBenjamin Kramer         operands.getBeginOperandIndex();
382d955ca49SBenjamin Kramer     Value operand = parentOp->getOperand(operandIndex);
383d955ca49SBenjamin Kramer     assert(operand ==
384d955ca49SBenjamin Kramer                operands[operandIndex - operands.getBeginOperandIndex()] &&
385d955ca49SBenjamin Kramer            "region interface operands don't match parentOp operands");
386f89bb3c0SAlexander Belyaev     auto clone = introduceCloneBuffers(operand, parentOp);
387f89bb3c0SAlexander Belyaev     if (failed(clone))
388f89bb3c0SAlexander Belyaev       return failure();
389f89bb3c0SAlexander Belyaev 
390d955ca49SBenjamin Kramer     parentOp->setOperand(operandIndex, *clone);
391f89bb3c0SAlexander Belyaev     return success();
392f89bb3c0SAlexander Belyaev   }
393f89bb3c0SAlexander Belyaev 
394f89bb3c0SAlexander Belyaev   /// Introduces temporary clones in front of all associated nested-region
395f89bb3c0SAlexander Belyaev   /// terminators and copies the source values into the newly allocated buffers.
introduceValueCopyForRegionResult(Value value)396f89bb3c0SAlexander Belyaev   LogicalResult introduceValueCopyForRegionResult(Value value) {
397f89bb3c0SAlexander Belyaev     // Get the actual result index in the scope of the parent terminator.
398f89bb3c0SAlexander Belyaev     Operation *operation = value.getDefiningOp();
399f89bb3c0SAlexander Belyaev     auto regionInterface = cast<RegionBranchOpInterface>(operation);
400f89bb3c0SAlexander Belyaev     // Filter successors that return to the parent operation.
401f89bb3c0SAlexander Belyaev     auto regionPredicate = [&](RegionSuccessor &successorRegion) {
402f89bb3c0SAlexander Belyaev       // If the RegionSuccessor has no associated successor, it will return to
403f89bb3c0SAlexander Belyaev       // its parent operation.
404f89bb3c0SAlexander Belyaev       return !successorRegion.getSuccessor();
405f89bb3c0SAlexander Belyaev     };
406f89bb3c0SAlexander Belyaev     // Introduce a clone for all region "results" that are returned to the
407f89bb3c0SAlexander Belyaev     // parent operation. This is required since the parent's result value has
408f89bb3c0SAlexander Belyaev     // been considered critical. Therefore, the algorithm assumes that a clone
409f89bb3c0SAlexander Belyaev     // of a previously allocated buffer is returned by the operation (like in
410f89bb3c0SAlexander Belyaev     // the case of a block argument).
411f89bb3c0SAlexander Belyaev     return introduceClonesForRegionSuccessors(
412f89bb3c0SAlexander Belyaev         regionInterface, operation->getRegions(), value, regionPredicate);
413f89bb3c0SAlexander Belyaev   }
414f89bb3c0SAlexander Belyaev 
415f89bb3c0SAlexander Belyaev   /// Introduces buffer clones for all terminators in the given regions. The
416f89bb3c0SAlexander Belyaev   /// regionPredicate is applied to every successor region in order to restrict
417f89bb3c0SAlexander Belyaev   /// the clones to specific regions.
418f89bb3c0SAlexander Belyaev   template <typename TPredicate>
introduceClonesForRegionSuccessors(RegionBranchOpInterface regionInterface,MutableArrayRef<Region> regions,Value argValue,const TPredicate & regionPredicate)419f89bb3c0SAlexander Belyaev   LogicalResult introduceClonesForRegionSuccessors(
420f89bb3c0SAlexander Belyaev       RegionBranchOpInterface regionInterface, MutableArrayRef<Region> regions,
421f89bb3c0SAlexander Belyaev       Value argValue, const TPredicate &regionPredicate) {
422f89bb3c0SAlexander Belyaev     for (Region &region : regions) {
423f89bb3c0SAlexander Belyaev       // Query the regionInterface to get all successor regions of the current
424f89bb3c0SAlexander Belyaev       // one.
425f89bb3c0SAlexander Belyaev       SmallVector<RegionSuccessor, 2> successorRegions;
426f89bb3c0SAlexander Belyaev       regionInterface.getSuccessorRegions(region.getRegionNumber(),
427f89bb3c0SAlexander Belyaev                                           successorRegions);
428f89bb3c0SAlexander Belyaev       // Try to find a matching region successor.
429f89bb3c0SAlexander Belyaev       RegionSuccessor *regionSuccessor =
430f89bb3c0SAlexander Belyaev           llvm::find_if(successorRegions, regionPredicate);
431f89bb3c0SAlexander Belyaev       if (regionSuccessor == successorRegions.end())
432f89bb3c0SAlexander Belyaev         continue;
433f89bb3c0SAlexander Belyaev       // Get the operand index in the context of the current successor input
434f89bb3c0SAlexander Belyaev       // bindings.
435f89bb3c0SAlexander Belyaev       size_t operandIndex =
436f89bb3c0SAlexander Belyaev           llvm::find(regionSuccessor->getSuccessorInputs(), argValue)
437f89bb3c0SAlexander Belyaev               .getIndex();
438f89bb3c0SAlexander Belyaev 
439f89bb3c0SAlexander Belyaev       // Iterate over all immediate terminator operations to introduce
440f89bb3c0SAlexander Belyaev       // new buffer allocations. Thereby, the appropriate terminator operand
441f89bb3c0SAlexander Belyaev       // will be adjusted to point to the newly allocated buffer instead.
442f89bb3c0SAlexander Belyaev       if (failed(walkReturnOperations(&region, [&](Operation *terminator) {
443f89bb3c0SAlexander Belyaev             // Get the actual mutable operands for this terminator op.
444f89bb3c0SAlexander Belyaev             auto terminatorOperands = *getMutableRegionBranchSuccessorOperands(
445f89bb3c0SAlexander Belyaev                 terminator, region.getRegionNumber());
446f89bb3c0SAlexander Belyaev             // Extract the source value from the current terminator.
447f89bb3c0SAlexander Belyaev             // This conversion needs to exist on a separate line due to a bug in
448f89bb3c0SAlexander Belyaev             // GCC conversion analysis.
449f89bb3c0SAlexander Belyaev             OperandRange immutableTerminatorOperands = terminatorOperands;
450f89bb3c0SAlexander Belyaev             Value sourceValue = immutableTerminatorOperands[operandIndex];
451f89bb3c0SAlexander Belyaev             // Create a new clone at the current location of the terminator.
452f89bb3c0SAlexander Belyaev             auto clone = introduceCloneBuffers(sourceValue, terminator);
453f89bb3c0SAlexander Belyaev             if (failed(clone))
454f89bb3c0SAlexander Belyaev               return failure();
455f89bb3c0SAlexander Belyaev             // Wire clone and terminator operand.
456f89bb3c0SAlexander Belyaev             terminatorOperands.slice(operandIndex, 1).assign(*clone);
457f89bb3c0SAlexander Belyaev             return success();
458f89bb3c0SAlexander Belyaev           })))
459f89bb3c0SAlexander Belyaev         return failure();
460f89bb3c0SAlexander Belyaev     }
461f89bb3c0SAlexander Belyaev     return success();
462f89bb3c0SAlexander Belyaev   }
463f89bb3c0SAlexander Belyaev 
464f89bb3c0SAlexander Belyaev   /// Creates a new memory allocation for the given source value and clones
465f89bb3c0SAlexander Belyaev   /// its content into the newly allocated buffer. The terminator operation is
466f89bb3c0SAlexander Belyaev   /// used to insert the clone operation at the right place.
introduceCloneBuffers(Value sourceValue,Operation * terminator)467f89bb3c0SAlexander Belyaev   FailureOr<Value> introduceCloneBuffers(Value sourceValue,
468f89bb3c0SAlexander Belyaev                                          Operation *terminator) {
469f89bb3c0SAlexander Belyaev     // Avoid multiple clones of the same source value. This can happen in the
470f89bb3c0SAlexander Belyaev     // presence of loops when a branch acts as a backedge while also having
471f89bb3c0SAlexander Belyaev     // another successor that returns to its parent operation. Note: that
472f89bb3c0SAlexander Belyaev     // copying copied buffers can introduce memory leaks since the invariant of
473f89bb3c0SAlexander Belyaev     // BufferDeallocation assumes that a buffer will be only cloned once into a
474f89bb3c0SAlexander Belyaev     // temporary buffer. Hence, the construction of clone chains introduces
475f89bb3c0SAlexander Belyaev     // additional allocations that are not tracked automatically by the
476f89bb3c0SAlexander Belyaev     // algorithm.
477f89bb3c0SAlexander Belyaev     if (clonedValues.contains(sourceValue))
478f89bb3c0SAlexander Belyaev       return sourceValue;
479f89bb3c0SAlexander Belyaev     // Create a new clone operation that copies the contents of the old
480f89bb3c0SAlexander Belyaev     // buffer to the new one.
481f89bb3c0SAlexander Belyaev     auto clone = buildClone(terminator, sourceValue);
482f89bb3c0SAlexander Belyaev     if (succeeded(clone)) {
483f89bb3c0SAlexander Belyaev       // Remember the clone of original source value.
484f89bb3c0SAlexander Belyaev       clonedValues.insert(*clone);
485f89bb3c0SAlexander Belyaev     }
486f89bb3c0SAlexander Belyaev     return clone;
487f89bb3c0SAlexander Belyaev   }
488f89bb3c0SAlexander Belyaev 
489f89bb3c0SAlexander Belyaev   /// Finds correct dealloc positions according to the algorithm described at
490f89bb3c0SAlexander Belyaev   /// the top of the file for all alloc nodes and block arguments that can be
491f89bb3c0SAlexander Belyaev   /// handled by this analysis.
placeDeallocs()492f89bb3c0SAlexander Belyaev   LogicalResult placeDeallocs() {
493f89bb3c0SAlexander Belyaev     // Move or insert deallocs using the previously computed information.
494f89bb3c0SAlexander Belyaev     // These deallocations will be linked to their associated allocation nodes
495f89bb3c0SAlexander Belyaev     // since they don't have any aliases that can (potentially) increase their
496f89bb3c0SAlexander Belyaev     // liveness.
497f89bb3c0SAlexander Belyaev     for (const BufferPlacementAllocs::AllocEntry &entry : allocs) {
498f89bb3c0SAlexander Belyaev       Value alloc = std::get<0>(entry);
499f89bb3c0SAlexander Belyaev       auto aliasesSet = aliases.resolve(alloc);
500f89bb3c0SAlexander Belyaev       assert(!aliasesSet.empty() && "must contain at least one alias");
501f89bb3c0SAlexander Belyaev 
502f89bb3c0SAlexander Belyaev       // Determine the actual block to place the dealloc and get liveness
503f89bb3c0SAlexander Belyaev       // information.
504f89bb3c0SAlexander Belyaev       Block *placementBlock =
505f89bb3c0SAlexander Belyaev           findCommonDominator(alloc, aliasesSet, postDominators);
506f89bb3c0SAlexander Belyaev       const LivenessBlockInfo *livenessInfo =
507f89bb3c0SAlexander Belyaev           liveness.getLiveness(placementBlock);
508f89bb3c0SAlexander Belyaev 
509f89bb3c0SAlexander Belyaev       // We have to ensure that the dealloc will be after the last use of all
510f89bb3c0SAlexander Belyaev       // aliases of the given value. We first assume that there are no uses in
511f89bb3c0SAlexander Belyaev       // the placementBlock and that we can safely place the dealloc at the
512f89bb3c0SAlexander Belyaev       // beginning.
513f89bb3c0SAlexander Belyaev       Operation *endOperation = &placementBlock->front();
514f89bb3c0SAlexander Belyaev 
515f89bb3c0SAlexander Belyaev       // Iterate over all aliases and ensure that the endOperation will point
516f89bb3c0SAlexander Belyaev       // to the last operation of all potential aliases in the placementBlock.
517f89bb3c0SAlexander Belyaev       for (Value alias : aliasesSet) {
518f89bb3c0SAlexander Belyaev         // Ensure that the start operation is at least the defining operation of
519f89bb3c0SAlexander Belyaev         // the current alias to avoid invalid placement of deallocs for aliases
520f89bb3c0SAlexander Belyaev         // without any uses.
521f89bb3c0SAlexander Belyaev         Operation *beforeOp = endOperation;
522f89bb3c0SAlexander Belyaev         if (alias.getDefiningOp() &&
523f89bb3c0SAlexander Belyaev             !(beforeOp = placementBlock->findAncestorOpInBlock(
524f89bb3c0SAlexander Belyaev                   *alias.getDefiningOp())))
525f89bb3c0SAlexander Belyaev           continue;
526f89bb3c0SAlexander Belyaev 
527f89bb3c0SAlexander Belyaev         Operation *aliasEndOperation =
528f89bb3c0SAlexander Belyaev             livenessInfo->getEndOperation(alias, beforeOp);
529f89bb3c0SAlexander Belyaev         // Check whether the aliasEndOperation lies in the desired block and
530f89bb3c0SAlexander Belyaev         // whether it is behind the current endOperation. If yes, this will be
531f89bb3c0SAlexander Belyaev         // the new endOperation.
532f89bb3c0SAlexander Belyaev         if (aliasEndOperation->getBlock() == placementBlock &&
533f89bb3c0SAlexander Belyaev             endOperation->isBeforeInBlock(aliasEndOperation))
534f89bb3c0SAlexander Belyaev           endOperation = aliasEndOperation;
535f89bb3c0SAlexander Belyaev       }
536f89bb3c0SAlexander Belyaev       // endOperation is the last operation behind which we can safely store
537f89bb3c0SAlexander Belyaev       // the dealloc taking all potential aliases into account.
538f89bb3c0SAlexander Belyaev 
539f89bb3c0SAlexander Belyaev       // If there is an existing dealloc, move it to the right place.
540f89bb3c0SAlexander Belyaev       Operation *deallocOperation = std::get<1>(entry);
541f89bb3c0SAlexander Belyaev       if (deallocOperation) {
542f89bb3c0SAlexander Belyaev         deallocOperation->moveAfter(endOperation);
543f89bb3c0SAlexander Belyaev       } else {
544f89bb3c0SAlexander Belyaev         // If the Dealloc position is at the terminator operation of the
545f89bb3c0SAlexander Belyaev         // block, then the value should escape from a deallocation.
546f89bb3c0SAlexander Belyaev         Operation *nextOp = endOperation->getNextNode();
547f89bb3c0SAlexander Belyaev         if (!nextOp)
548f89bb3c0SAlexander Belyaev           continue;
549f89bb3c0SAlexander Belyaev         // If there is no dealloc node, insert one in the right place.
550f89bb3c0SAlexander Belyaev         if (failed(buildDealloc(nextOp, alloc)))
551f89bb3c0SAlexander Belyaev           return failure();
552f89bb3c0SAlexander Belyaev       }
553f89bb3c0SAlexander Belyaev     }
554f89bb3c0SAlexander Belyaev     return success();
555f89bb3c0SAlexander Belyaev   }
556f89bb3c0SAlexander Belyaev 
557f89bb3c0SAlexander Belyaev   /// Builds a deallocation operation compatible with the given allocation
558f89bb3c0SAlexander Belyaev   /// value. If there is no registered AllocationOpInterface implementation for
559f89bb3c0SAlexander Belyaev   /// the given value (e.g. in the case of a function parameter), this method
560f89bb3c0SAlexander Belyaev   /// builds a memref::DeallocOp.
buildDealloc(Operation * op,Value alloc)561f89bb3c0SAlexander Belyaev   LogicalResult buildDealloc(Operation *op, Value alloc) {
562f89bb3c0SAlexander Belyaev     OpBuilder builder(op);
563f89bb3c0SAlexander Belyaev     auto it = aliasToAllocations.find(alloc);
564f89bb3c0SAlexander Belyaev     if (it != aliasToAllocations.end()) {
565f89bb3c0SAlexander Belyaev       // Call the allocation op interface to build a supported and
566f89bb3c0SAlexander Belyaev       // compatible deallocation operation.
567f89bb3c0SAlexander Belyaev       auto dealloc = it->second.buildDealloc(builder, alloc);
568f89bb3c0SAlexander Belyaev       if (!dealloc)
569f89bb3c0SAlexander Belyaev         return op->emitError()
570f89bb3c0SAlexander Belyaev                << "allocations without compatible deallocations are "
571f89bb3c0SAlexander Belyaev                   "not supported";
572f89bb3c0SAlexander Belyaev     } else {
573f89bb3c0SAlexander Belyaev       // Build a "default" DeallocOp for unknown allocation sources.
574f89bb3c0SAlexander Belyaev       builder.create<memref::DeallocOp>(alloc.getLoc(), alloc);
575f89bb3c0SAlexander Belyaev     }
576f89bb3c0SAlexander Belyaev     return success();
577f89bb3c0SAlexander Belyaev   }
578f89bb3c0SAlexander Belyaev 
579f89bb3c0SAlexander Belyaev   /// Builds a clone operation compatible with the given allocation value. If
580f89bb3c0SAlexander Belyaev   /// there is no registered AllocationOpInterface implementation for the given
581f89bb3c0SAlexander Belyaev   /// value (e.g. in the case of a function parameter), this method builds a
582f89bb3c0SAlexander Belyaev   /// bufferization::CloneOp.
buildClone(Operation * op,Value alloc)583f89bb3c0SAlexander Belyaev   FailureOr<Value> buildClone(Operation *op, Value alloc) {
584f89bb3c0SAlexander Belyaev     OpBuilder builder(op);
585f89bb3c0SAlexander Belyaev     auto it = aliasToAllocations.find(alloc);
586f89bb3c0SAlexander Belyaev     if (it != aliasToAllocations.end()) {
587f89bb3c0SAlexander Belyaev       // Call the allocation op interface to build a supported and
588f89bb3c0SAlexander Belyaev       // compatible clone operation.
589f89bb3c0SAlexander Belyaev       auto clone = it->second.buildClone(builder, alloc);
590f89bb3c0SAlexander Belyaev       if (clone)
591f89bb3c0SAlexander Belyaev         return *clone;
592f89bb3c0SAlexander Belyaev       return (LogicalResult)(op->emitError()
593f89bb3c0SAlexander Belyaev                              << "allocations without compatible clone ops "
594f89bb3c0SAlexander Belyaev                                 "are not supported");
595f89bb3c0SAlexander Belyaev     }
596f89bb3c0SAlexander Belyaev     // Build a "default" CloneOp for unknown allocation sources.
597f89bb3c0SAlexander Belyaev     return builder.create<bufferization::CloneOp>(alloc.getLoc(), alloc)
598f89bb3c0SAlexander Belyaev         .getResult();
599f89bb3c0SAlexander Belyaev   }
600f89bb3c0SAlexander Belyaev 
601f89bb3c0SAlexander Belyaev   /// The dominator info to find the appropriate start operation to move the
602f89bb3c0SAlexander Belyaev   /// allocs.
603f89bb3c0SAlexander Belyaev   DominanceInfo dominators;
604f89bb3c0SAlexander Belyaev 
605f89bb3c0SAlexander Belyaev   /// The post dominator info to move the dependent allocs in the right
606f89bb3c0SAlexander Belyaev   /// position.
607f89bb3c0SAlexander Belyaev   PostDominanceInfo postDominators;
608f89bb3c0SAlexander Belyaev 
609f89bb3c0SAlexander Belyaev   /// Stores already cloned buffers to avoid additional clones of clones.
610f89bb3c0SAlexander Belyaev   ValueSetT clonedValues;
611f89bb3c0SAlexander Belyaev 
612f89bb3c0SAlexander Belyaev   /// Maps aliases to their source allocation interfaces (inverse mapping).
613f89bb3c0SAlexander Belyaev   AliasAllocationMapT aliasToAllocations;
614f89bb3c0SAlexander Belyaev };
615f89bb3c0SAlexander Belyaev 
616f89bb3c0SAlexander Belyaev //===----------------------------------------------------------------------===//
617f89bb3c0SAlexander Belyaev // BufferDeallocationPass
618f89bb3c0SAlexander Belyaev //===----------------------------------------------------------------------===//
619f89bb3c0SAlexander Belyaev 
620f89bb3c0SAlexander Belyaev struct DefaultAllocationInterface
621f89bb3c0SAlexander Belyaev     : public bufferization::AllocationOpInterface::ExternalModel<
622f89bb3c0SAlexander Belyaev           DefaultAllocationInterface, memref::AllocOp> {
buildDealloc__anonf063894c0211::DefaultAllocationInterface623f89bb3c0SAlexander Belyaev   static Optional<Operation *> buildDealloc(OpBuilder &builder, Value alloc) {
624f89bb3c0SAlexander Belyaev     return builder.create<memref::DeallocOp>(alloc.getLoc(), alloc)
625f89bb3c0SAlexander Belyaev         .getOperation();
626f89bb3c0SAlexander Belyaev   }
buildClone__anonf063894c0211::DefaultAllocationInterface627f89bb3c0SAlexander Belyaev   static Optional<Value> buildClone(OpBuilder &builder, Value alloc) {
628f89bb3c0SAlexander Belyaev     return builder.create<bufferization::CloneOp>(alloc.getLoc(), alloc)
629f89bb3c0SAlexander Belyaev         .getResult();
630f89bb3c0SAlexander Belyaev   }
631f89bb3c0SAlexander Belyaev };
632f89bb3c0SAlexander Belyaev 
633f89bb3c0SAlexander Belyaev /// The actual buffer deallocation pass that inserts and moves dealloc nodes
634f89bb3c0SAlexander Belyaev /// into the right positions. Furthermore, it inserts additional clones if
635f89bb3c0SAlexander Belyaev /// necessary. It uses the algorithm described at the top of the file.
636f89bb3c0SAlexander Belyaev struct BufferDeallocationPass : BufferDeallocationBase<BufferDeallocationPass> {
getDependentDialects__anonf063894c0211::BufferDeallocationPass637f89bb3c0SAlexander Belyaev   void getDependentDialects(DialectRegistry &registry) const override {
638f89bb3c0SAlexander Belyaev     registry.insert<bufferization::BufferizationDialect>();
639f89bb3c0SAlexander Belyaev     registry.insert<memref::MemRefDialect>();
640c076fa1cSMatthias Springer     registerAllocationOpInterfaceExternalModels(registry);
641f89bb3c0SAlexander Belyaev   }
642f89bb3c0SAlexander Belyaev 
runOnOperation__anonf063894c0211::BufferDeallocationPass64341574554SRiver Riddle   void runOnOperation() override {
64458ceae95SRiver Riddle     func::FuncOp func = getOperation();
64541574554SRiver Riddle     if (func.isExternal())
64641574554SRiver Riddle       return;
64741574554SRiver Riddle 
648c076fa1cSMatthias Springer     if (failed(deallocateBuffers(func)))
649c076fa1cSMatthias Springer       signalPassFailure();
650f89bb3c0SAlexander Belyaev   }
651f89bb3c0SAlexander Belyaev };
652f89bb3c0SAlexander Belyaev 
653be0a7e9fSMehdi Amini } // namespace
654f89bb3c0SAlexander Belyaev 
deallocateBuffers(Operation * op)655c076fa1cSMatthias Springer LogicalResult bufferization::deallocateBuffers(Operation *op) {
656c076fa1cSMatthias Springer   if (isa<ModuleOp>(op)) {
65758ceae95SRiver Riddle     WalkResult result = op->walk([&](func::FuncOp funcOp) {
658c076fa1cSMatthias Springer       if (failed(deallocateBuffers(funcOp)))
659c076fa1cSMatthias Springer         return WalkResult::interrupt();
660c076fa1cSMatthias Springer       return WalkResult::advance();
661c076fa1cSMatthias Springer     });
662c076fa1cSMatthias Springer     return success(!result.wasInterrupted());
663c076fa1cSMatthias Springer   }
664c076fa1cSMatthias Springer 
665c076fa1cSMatthias Springer   // Ensure that there are supported loops only.
666c076fa1cSMatthias Springer   Backedges backedges(op);
667c076fa1cSMatthias Springer   if (backedges.size()) {
668c076fa1cSMatthias Springer     op->emitError("Only structured control-flow loops are supported.");
669c076fa1cSMatthias Springer     return failure();
670c076fa1cSMatthias Springer   }
671c076fa1cSMatthias Springer 
672c076fa1cSMatthias Springer   // Check that the control flow structures are supported.
673c076fa1cSMatthias Springer   if (!validateSupportedControlFlow(op))
674c076fa1cSMatthias Springer     return failure();
675c076fa1cSMatthias Springer 
676c076fa1cSMatthias Springer   // Gather all required allocation nodes and prepare the deallocation phase.
677c076fa1cSMatthias Springer   BufferDeallocation deallocation(op);
678c076fa1cSMatthias Springer 
679c076fa1cSMatthias Springer   // Check for supported AllocationOpInterface implementations and prepare the
680c076fa1cSMatthias Springer   // internal deallocation pass.
681c076fa1cSMatthias Springer   if (failed(deallocation.prepare()))
682c076fa1cSMatthias Springer     return failure();
683c076fa1cSMatthias Springer 
684c076fa1cSMatthias Springer   // Place all required temporary clone and dealloc nodes.
685c076fa1cSMatthias Springer   if (failed(deallocation.deallocate()))
686c076fa1cSMatthias Springer     return failure();
687c076fa1cSMatthias Springer 
688c076fa1cSMatthias Springer   return success();
689c076fa1cSMatthias Springer }
690c076fa1cSMatthias Springer 
registerAllocationOpInterfaceExternalModels(DialectRegistry & registry)691c076fa1cSMatthias Springer void bufferization::registerAllocationOpInterfaceExternalModels(
692c076fa1cSMatthias Springer     DialectRegistry &registry) {
69377eee579SRiver Riddle   registry.addExtension(+[](MLIRContext *ctx, memref::MemRefDialect *dialect) {
69477eee579SRiver Riddle     memref::AllocOp::attachInterface<DefaultAllocationInterface>(*ctx);
69577eee579SRiver Riddle   });
696c076fa1cSMatthias Springer }
697c076fa1cSMatthias Springer 
698f89bb3c0SAlexander Belyaev //===----------------------------------------------------------------------===//
699f89bb3c0SAlexander Belyaev // BufferDeallocationPass construction
700f89bb3c0SAlexander Belyaev //===----------------------------------------------------------------------===//
701f89bb3c0SAlexander Belyaev 
createBufferDeallocationPass()702f89bb3c0SAlexander Belyaev std::unique_ptr<Pass> mlir::bufferization::createBufferDeallocationPass() {
703f89bb3c0SAlexander Belyaev   return std::make_unique<BufferDeallocationPass>();
704f89bb3c0SAlexander Belyaev }
705