//===- BufferDeallocation.cpp - the impl for buffer deallocation ----------===// // // 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 logic for computing correct alloc and dealloc positions. // Furthermore, buffer deallocation also adds required new clone operations to // ensure that all buffers are deallocated. The main class is the // BufferDeallocationPass class that implements the underlying algorithm. In // order to put allocations and deallocations at safe positions, it is // significantly important to put them into the correct blocks. However, the // liveness analysis does not pay attention to aliases, which can occur due to // branches (and their associated block arguments) in general. For this purpose, // BufferDeallocation firstly finds all possible aliases for a single value // (using the BufferViewFlowAnalysis class). Consider the following example: // // ^bb0(%arg0): // cf.cond_br %cond, ^bb1, ^bb2 // ^bb1: // cf.br ^exit(%arg0) // ^bb2: // %new_value = ... // cf.br ^exit(%new_value) // ^exit(%arg1): // return %arg1; // // We should place the dealloc for %new_value in exit. However, we have to free // the buffer in the same block, because it cannot be freed in the post // dominator. However, this requires a new clone buffer for %arg1 that will // contain the actual contents. Using the class BufferViewFlowAnalysis, we // will find out that %new_value has a potential alias %arg1. In order to find // the dealloc position we have to find all potential aliases, iterate over // their uses and find the common post-dominator block (note that additional // clones and buffers remove potential aliases and will influence the placement // of the deallocs). In all cases, the computed block can be safely used to free // the %new_value buffer (may be exit or bb2) as it will die and we can use // liveness information to determine the exact operation after which we have to // insert the dealloc. However, the algorithm supports introducing clone buffers // and placing deallocs in safe locations to ensure that all buffers will be // freed in the end. // // TODO: // The current implementation does not support explicit-control-flow loops and // the resulting code will be invalid with respect to program semantics. // However, structured control-flow loops are fully supported. Furthermore, it // doesn't accept functions which return buffers already. // //===----------------------------------------------------------------------===// #include "mlir/Dialect/Bufferization/Transforms/Passes.h" #include "mlir/Dialect/Bufferization/IR/AllocationOpInterface.h" #include "mlir/Dialect/Bufferization/IR/Bufferization.h" #include "mlir/Dialect/Bufferization/Transforms/BufferUtils.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/MemRef/IR/MemRef.h" #include "llvm/ADT/SetOperations.h" namespace mlir { namespace bufferization { #define GEN_PASS_DEF_BUFFERDEALLOCATION #include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc" } // namespace bufferization } // namespace mlir usingnamespacemlir; usingnamespacemlir::bufferization; /// Walks over all immediate return-like terminators in the given region. static LogicalResult walkReturnOperations( Region *region, llvm::function_ref<LogicalResult(RegionBranchTerminatorOpInterface)> func) { … } /// Checks if all operations that have at least one attached region implement /// the RegionBranchOpInterface. This is not required in edge cases, where we /// have a single attached region and the parent operation has no results. static bool validateSupportedControlFlow(Operation *op) { … } namespace { //===----------------------------------------------------------------------===// // Backedges analysis //===----------------------------------------------------------------------===// /// A straight-forward program analysis which detects loop backedges induced by /// explicit control flow. class Backedges { … }; //===----------------------------------------------------------------------===// // BufferDeallocation //===----------------------------------------------------------------------===// /// The buffer deallocation transformation which ensures that all allocs in the /// program have a corresponding de-allocation. As a side-effect, it might also /// introduce clones that in turn leads to additional deallocations. class BufferDeallocation : public BufferPlacementTransformationBase { … }; //===----------------------------------------------------------------------===// // BufferDeallocationPass //===----------------------------------------------------------------------===// /// The actual buffer deallocation pass that inserts and moves dealloc nodes /// into the right positions. Furthermore, it inserts additional clones if /// necessary. It uses the algorithm described at the top of the file. struct BufferDeallocationPass : public bufferization::impl::BufferDeallocationBase< BufferDeallocationPass> { … }; } // namespace LogicalResult bufferization::deallocateBuffers(Operation *op) { … } //===----------------------------------------------------------------------===// // BufferDeallocationPass construction //===----------------------------------------------------------------------===// std::unique_ptr<Pass> mlir::bufferization::createBufferDeallocationPass() { … }