xref: /llvm-project/flang/lib/Optimizer/Transforms/MemoryAllocation.cpp (revision 31087c5e4c8ddfe08ab3ea6d3847e05c4738eeee)
13d092e31SEric Schweitz //===- MemoryAllocation.cpp -----------------------------------------------===//
23d092e31SEric Schweitz //
33d092e31SEric Schweitz // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43d092e31SEric Schweitz // See https://llvm.org/LICENSE.txt for license information.
53d092e31SEric Schweitz // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63d092e31SEric Schweitz //
73d092e31SEric Schweitz //===----------------------------------------------------------------------===//
83d092e31SEric Schweitz 
93d092e31SEric Schweitz #include "flang/Optimizer/Dialect/FIRDialect.h"
103d092e31SEric Schweitz #include "flang/Optimizer/Dialect/FIROps.h"
113d092e31SEric Schweitz #include "flang/Optimizer/Dialect/FIRType.h"
12*31087c5eSjeanPerier #include "flang/Optimizer/Transforms/MemoryUtils.h"
133d092e31SEric Schweitz #include "flang/Optimizer/Transforms/Passes.h"
1423aa5a74SRiver Riddle #include "mlir/Dialect/Func/IR/FuncOps.h"
153d092e31SEric Schweitz #include "mlir/IR/Diagnostics.h"
163d092e31SEric Schweitz #include "mlir/Pass/Pass.h"
173d092e31SEric Schweitz #include "mlir/Transforms/DialectConversion.h"
183d092e31SEric Schweitz #include "mlir/Transforms/Passes.h"
193d092e31SEric Schweitz #include "llvm/ADT/TypeSwitch.h"
203d092e31SEric Schweitz 
2167d0d7acSMichele Scuttari namespace fir {
2267d0d7acSMichele Scuttari #define GEN_PASS_DEF_MEMORYALLOCATIONOPT
2367d0d7acSMichele Scuttari #include "flang/Optimizer/Transforms/Passes.h.inc"
2467d0d7acSMichele Scuttari } // namespace fir
2567d0d7acSMichele Scuttari 
263d092e31SEric Schweitz #define DEBUG_TYPE "flang-memory-allocation-opt"
273d092e31SEric Schweitz 
283d092e31SEric Schweitz // Number of elements in an array does not determine where it is allocated.
294d53f88dSValentin Clement static constexpr std::size_t unlimitedArraySize = ~static_cast<std::size_t>(0);
303d092e31SEric Schweitz 
313d092e31SEric Schweitz /// Return `true` if this allocation is to remain on the stack (`fir.alloca`).
323d092e31SEric Schweitz /// Otherwise the allocation should be moved to the heap (`fir.allocmem`).
33213ab961STom Eccles static inline bool
34*31087c5eSjeanPerier keepStackAllocation(fir::AllocaOp alloca,
35213ab961STom Eccles                     const fir::MemoryAllocationOptOptions &options) {
36*31087c5eSjeanPerier   // Move all arrays and character with runtime determined size to the heap.
37*31087c5eSjeanPerier   if (options.dynamicArrayOnHeap && alloca.isDynamic())
383d092e31SEric Schweitz     return false;
39*31087c5eSjeanPerier   // TODO: use data layout to reason in terms of byte size to cover all "big"
40*31087c5eSjeanPerier   // entities, which may be scalar derived types.
41*31087c5eSjeanPerier   if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(alloca.getInType())) {
42*31087c5eSjeanPerier     if (!fir::hasDynamicSize(seqTy)) {
433d092e31SEric Schweitz       std::int64_t numberOfElements = 1;
443d092e31SEric Schweitz       for (std::int64_t i : seqTy.getShape()) {
453d092e31SEric Schweitz         numberOfElements *= i;
463d092e31SEric Schweitz         // If the count is suspicious, then don't change anything here.
473d092e31SEric Schweitz         if (numberOfElements <= 0)
483d092e31SEric Schweitz           return true;
493d092e31SEric Schweitz       }
503d092e31SEric Schweitz       // If the number of elements exceeds the threshold, move the allocation to
513d092e31SEric Schweitz       // the heap.
523d092e31SEric Schweitz       if (static_cast<std::size_t>(numberOfElements) >
533d092e31SEric Schweitz           options.maxStackArraySize) {
543d092e31SEric Schweitz         return false;
553d092e31SEric Schweitz       }
563d092e31SEric Schweitz     }
573d092e31SEric Schweitz   }
583d092e31SEric Schweitz   return true;
593d092e31SEric Schweitz }
603d092e31SEric Schweitz 
61*31087c5eSjeanPerier static mlir::Value genAllocmem(mlir::OpBuilder &builder, fir::AllocaOp alloca,
62*31087c5eSjeanPerier                                bool deallocPointsDominateAlloc) {
633d092e31SEric Schweitz   mlir::Type varTy = alloca.getInType();
64*31087c5eSjeanPerier   auto unpackName = [](std::optional<llvm::StringRef> opt) -> llvm::StringRef {
653d092e31SEric Schweitz     if (opt)
663d092e31SEric Schweitz       return *opt;
673d092e31SEric Schweitz     return {};
683d092e31SEric Schweitz   };
69*31087c5eSjeanPerier   llvm::StringRef uniqName = unpackName(alloca.getUniqName());
70*31087c5eSjeanPerier   llvm::StringRef bindcName = unpackName(alloca.getBindcName());
71*31087c5eSjeanPerier   auto heap = builder.create<fir::AllocMemOp>(alloca.getLoc(), varTy, uniqName,
72*31087c5eSjeanPerier                                               bindcName, alloca.getTypeparams(),
73149ad3d5SShraiysh Vaishay                                               alloca.getShape());
743d092e31SEric Schweitz   LLVM_DEBUG(llvm::dbgs() << "memory allocation opt: replaced " << alloca
753d092e31SEric Schweitz                           << " with " << heap << '\n');
76*31087c5eSjeanPerier   return heap;
773d092e31SEric Schweitz }
783d092e31SEric Schweitz 
79*31087c5eSjeanPerier static void genFreemem(mlir::Location loc, mlir::OpBuilder &builder,
80*31087c5eSjeanPerier                        mlir::Value allocmem) {
81*31087c5eSjeanPerier   [[maybe_unused]] auto free = builder.create<fir::FreeMemOp>(loc, allocmem);
82*31087c5eSjeanPerier   LLVM_DEBUG(llvm::dbgs() << "memory allocation opt: add free " << free
83*31087c5eSjeanPerier                           << " for " << allocmem << '\n');
84*31087c5eSjeanPerier }
853d092e31SEric Schweitz 
863d092e31SEric Schweitz /// This pass can reclassify memory allocations (fir.alloca, fir.allocmem) based
873d092e31SEric Schweitz /// on heuristics and settings. The intention is to allow better performance and
883d092e31SEric Schweitz /// workarounds for conditions such as environments with limited stack space.
893d092e31SEric Schweitz ///
903d092e31SEric Schweitz /// Currently, implements two conversions from stack to heap allocation.
913d092e31SEric Schweitz ///   1. If a stack allocation is an array larger than some threshold value
923d092e31SEric Schweitz ///      make it a heap allocation.
933d092e31SEric Schweitz ///   2. If a stack allocation is an array with a runtime evaluated size make
943d092e31SEric Schweitz ///      it a heap allocation.
95*31087c5eSjeanPerier namespace {
963d092e31SEric Schweitz class MemoryAllocationOpt
9767d0d7acSMichele Scuttari     : public fir::impl::MemoryAllocationOptBase<MemoryAllocationOpt> {
983d092e31SEric Schweitz public:
994d53f88dSValentin Clement   MemoryAllocationOpt() {
1004d53f88dSValentin Clement     // Set options with default values. (See Passes.td.) Note that the
1014d53f88dSValentin Clement     // command-line options, e.g. dynamicArrayOnHeap,  are not set yet.
1024d53f88dSValentin Clement     options = {dynamicArrayOnHeap, maxStackArraySize};
1034d53f88dSValentin Clement   }
1044d53f88dSValentin Clement 
1054d53f88dSValentin Clement   MemoryAllocationOpt(bool dynOnHeap, std::size_t maxStackSize) {
1064d53f88dSValentin Clement     // Set options with default values. (See Passes.td.)
1074d53f88dSValentin Clement     options = {dynOnHeap, maxStackSize};
1084d53f88dSValentin Clement   }
1094d53f88dSValentin Clement 
110213ab961STom Eccles   MemoryAllocationOpt(const fir::MemoryAllocationOptOptions &options)
111213ab961STom Eccles       : options{options} {}
112213ab961STom Eccles 
1134d53f88dSValentin Clement   /// Override `options` if command-line options have been set.
1144d53f88dSValentin Clement   inline void useCommandLineOptions() {
1154d53f88dSValentin Clement     if (dynamicArrayOnHeap)
1164d53f88dSValentin Clement       options.dynamicArrayOnHeap = dynamicArrayOnHeap;
1174d53f88dSValentin Clement     if (maxStackArraySize != unlimitedArraySize)
1184d53f88dSValentin Clement       options.maxStackArraySize = maxStackArraySize;
1194d53f88dSValentin Clement   }
1204d53f88dSValentin Clement 
1213d092e31SEric Schweitz   void runOnOperation() override {
1223d092e31SEric Schweitz     auto *context = &getContext();
1233d092e31SEric Schweitz     auto func = getOperation();
1249f85c198SRiver Riddle     mlir::RewritePatternSet patterns(context);
1253d092e31SEric Schweitz     mlir::ConversionTarget target(*context);
1264d53f88dSValentin Clement 
1274d53f88dSValentin Clement     useCommandLineOptions();
1284d53f88dSValentin Clement     LLVM_DEBUG(llvm::dbgs()
1294d53f88dSValentin Clement                << "dynamic arrays on heap: " << options.dynamicArrayOnHeap
1304d53f88dSValentin Clement                << "\nmaximum number of elements of array on stack: "
1314d53f88dSValentin Clement                << options.maxStackArraySize << '\n');
1323d092e31SEric Schweitz 
1333d092e31SEric Schweitz     // If func is a declaration, skip it.
1343d092e31SEric Schweitz     if (func.empty())
1353d092e31SEric Schweitz       return;
136*31087c5eSjeanPerier     auto tryReplacing = [&](fir::AllocaOp alloca) {
137*31087c5eSjeanPerier       bool res = !keepStackAllocation(alloca, options);
138*31087c5eSjeanPerier       if (res) {
139*31087c5eSjeanPerier         LLVM_DEBUG(llvm::dbgs()
140*31087c5eSjeanPerier                    << "memory allocation opt: found " << alloca << '\n');
1413d092e31SEric Schweitz       }
142*31087c5eSjeanPerier       return res;
143*31087c5eSjeanPerier     };
144*31087c5eSjeanPerier     mlir::IRRewriter rewriter(context);
145*31087c5eSjeanPerier     fir::replaceAllocas(rewriter, func.getOperation(), tryReplacing,
146*31087c5eSjeanPerier                         genAllocmem, genFreemem);
1473d092e31SEric Schweitz   }
1484d53f88dSValentin Clement 
1494d53f88dSValentin Clement private:
150213ab961STom Eccles   fir::MemoryAllocationOptOptions options;
1513d092e31SEric Schweitz };
1523d092e31SEric Schweitz } // namespace
153