1 //===- MemoryAllocation.cpp -----------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "flang/Optimizer/Dialect/FIRDialect.h"
10 #include "flang/Optimizer/Dialect/FIROps.h"
11 #include "flang/Optimizer/Dialect/FIRType.h"
12 #include "flang/Optimizer/Transforms/MemoryUtils.h"
13 #include "flang/Optimizer/Transforms/Passes.h"
14 #include "mlir/Dialect/Func/IR/FuncOps.h"
15 #include "mlir/IR/Diagnostics.h"
16 #include "mlir/Pass/Pass.h"
17 #include "mlir/Transforms/DialectConversion.h"
18 #include "mlir/Transforms/Passes.h"
19 #include "llvm/ADT/TypeSwitch.h"
22 #define GEN_PASS_DEF_MEMORYALLOCATIONOPT
23 #include "flang/Optimizer/Transforms/Passes.h.inc"
26 #define DEBUG_TYPE "flang-memory-allocation-opt"
28 // Number of elements in an array does not determine where it is allocated.
29 static constexpr std::size_t unlimitedArraySize
= ~static_cast<std::size_t>(0);
31 /// Return `true` if this allocation is to remain on the stack (`fir.alloca`).
32 /// Otherwise the allocation should be moved to the heap (`fir.allocmem`).
34 keepStackAllocation(fir::AllocaOp alloca
,
35 const fir::MemoryAllocationOptOptions
&options
) {
36 // Move all arrays and character with runtime determined size to the heap.
37 if (options
.dynamicArrayOnHeap
&& alloca
.isDynamic())
39 // TODO: use data layout to reason in terms of byte size to cover all "big"
40 // entities, which may be scalar derived types.
41 if (auto seqTy
= mlir::dyn_cast
<fir::SequenceType
>(alloca
.getInType())) {
42 if (!fir::hasDynamicSize(seqTy
)) {
43 std::int64_t numberOfElements
= 1;
44 for (std::int64_t i
: seqTy
.getShape()) {
45 numberOfElements
*= i
;
46 // If the count is suspicious, then don't change anything here.
47 if (numberOfElements
<= 0)
50 // If the number of elements exceeds the threshold, move the allocation to
52 if (static_cast<std::size_t>(numberOfElements
) >
53 options
.maxStackArraySize
) {
61 static mlir::Value
genAllocmem(mlir::OpBuilder
&builder
, fir::AllocaOp alloca
,
62 bool deallocPointsDominateAlloc
) {
63 mlir::Type varTy
= alloca
.getInType();
64 auto unpackName
= [](std::optional
<llvm::StringRef
> opt
) -> llvm::StringRef
{
69 llvm::StringRef uniqName
= unpackName(alloca
.getUniqName());
70 llvm::StringRef bindcName
= unpackName(alloca
.getBindcName());
71 auto heap
= builder
.create
<fir::AllocMemOp
>(alloca
.getLoc(), varTy
, uniqName
,
72 bindcName
, alloca
.getTypeparams(),
74 LLVM_DEBUG(llvm::dbgs() << "memory allocation opt: replaced " << alloca
75 << " with " << heap
<< '\n');
79 static void genFreemem(mlir::Location loc
, mlir::OpBuilder
&builder
,
80 mlir::Value allocmem
) {
81 [[maybe_unused
]] auto free
= builder
.create
<fir::FreeMemOp
>(loc
, allocmem
);
82 LLVM_DEBUG(llvm::dbgs() << "memory allocation opt: add free " << free
83 << " for " << allocmem
<< '\n');
86 /// This pass can reclassify memory allocations (fir.alloca, fir.allocmem) based
87 /// on heuristics and settings. The intention is to allow better performance and
88 /// workarounds for conditions such as environments with limited stack space.
90 /// Currently, implements two conversions from stack to heap allocation.
91 /// 1. If a stack allocation is an array larger than some threshold value
92 /// make it a heap allocation.
93 /// 2. If a stack allocation is an array with a runtime evaluated size make
94 /// it a heap allocation.
96 class MemoryAllocationOpt
97 : public fir::impl::MemoryAllocationOptBase
<MemoryAllocationOpt
> {
99 MemoryAllocationOpt() {
100 // Set options with default values. (See Passes.td.) Note that the
101 // command-line options, e.g. dynamicArrayOnHeap, are not set yet.
102 options
= {dynamicArrayOnHeap
, maxStackArraySize
};
105 MemoryAllocationOpt(bool dynOnHeap
, std::size_t maxStackSize
) {
106 // Set options with default values. (See Passes.td.)
107 options
= {dynOnHeap
, maxStackSize
};
110 MemoryAllocationOpt(const fir::MemoryAllocationOptOptions
&options
)
111 : options
{options
} {}
113 /// Override `options` if command-line options have been set.
114 inline void useCommandLineOptions() {
115 if (dynamicArrayOnHeap
)
116 options
.dynamicArrayOnHeap
= dynamicArrayOnHeap
;
117 if (maxStackArraySize
!= unlimitedArraySize
)
118 options
.maxStackArraySize
= maxStackArraySize
;
121 void runOnOperation() override
{
122 auto *context
= &getContext();
123 auto func
= getOperation();
124 mlir::RewritePatternSet
patterns(context
);
125 mlir::ConversionTarget
target(*context
);
127 useCommandLineOptions();
128 LLVM_DEBUG(llvm::dbgs()
129 << "dynamic arrays on heap: " << options
.dynamicArrayOnHeap
130 << "\nmaximum number of elements of array on stack: "
131 << options
.maxStackArraySize
<< '\n');
133 // If func is a declaration, skip it.
136 auto tryReplacing
= [&](fir::AllocaOp alloca
) {
137 bool res
= !keepStackAllocation(alloca
, options
);
139 LLVM_DEBUG(llvm::dbgs()
140 << "memory allocation opt: found " << alloca
<< '\n');
144 mlir::IRRewriter
rewriter(context
);
145 fir::replaceAllocas(rewriter
, func
.getOperation(), tryReplacing
,
146 genAllocmem
, genFreemem
);
150 fir::MemoryAllocationOptOptions options
;