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