9
9
#include " flang/Optimizer/Dialect/FIRDialect.h"
10
10
#include " flang/Optimizer/Dialect/FIROps.h"
11
11
#include " flang/Optimizer/Dialect/FIRType.h"
12
+ #include " flang/Optimizer/Transforms/MemoryUtils.h"
12
13
#include " flang/Optimizer/Transforms/Passes.h"
13
14
#include " mlir/Dialect/Func/IR/FuncOps.h"
14
15
#include " mlir/IR/Diagnostics.h"
@@ -27,50 +28,18 @@ namespace fir {
27
28
// Number of elements in an array does not determine where it is allocated.
28
29
static constexpr std::size_t unlimitedArraySize = ~static_cast <std::size_t >(0 );
29
30
30
- namespace {
31
- class ReturnAnalysis {
32
- public:
33
- MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID (ReturnAnalysis)
34
-
35
- ReturnAnalysis (mlir::Operation *op) {
36
- if (auto func = mlir::dyn_cast<mlir::func::FuncOp>(op))
37
- for (mlir::Block &block : func)
38
- for (mlir::Operation &i : block)
39
- if (mlir::isa<mlir::func::ReturnOp>(i)) {
40
- returnMap[op].push_back (&i);
41
- break ;
42
- }
43
- }
44
-
45
- llvm::SmallVector<mlir::Operation *> getReturns (mlir::Operation *func) const {
46
- auto iter = returnMap.find (func);
47
- if (iter != returnMap.end ())
48
- return iter->second ;
49
- return {};
50
- }
51
-
52
- private:
53
- llvm::DenseMap<mlir::Operation *, llvm::SmallVector<mlir::Operation *>>
54
- returnMap;
55
- };
56
- } // namespace
57
-
58
31
// / Return `true` if this allocation is to remain on the stack (`fir.alloca`).
59
32
// / Otherwise the allocation should be moved to the heap (`fir.allocmem`).
60
33
static inline bool
61
- keepStackAllocation (fir::AllocaOp alloca, mlir::Block *entry,
34
+ keepStackAllocation (fir::AllocaOp alloca,
62
35
const fir::MemoryAllocationOptOptions &options) {
63
- // Limitation: only arrays allocated on the stack in the entry block are
64
- // considered for now.
65
- // TODO: Generalize the algorithm and placement of the freemem nodes.
66
- if (alloca-> getBlock () != entry)
67
- return true ;
36
+ // Move all arrays and character with runtime determined size to the heap.
37
+ if (options. dynamicArrayOnHeap && alloca. isDynamic ())
38
+ return false ;
39
+ // TODO: use data layout to reason in terms of byte size to cover all "big"
40
+ // entities, which may be scalar derived types.
68
41
if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(alloca.getInType ())) {
69
- if (fir::hasDynamicSize (seqTy)) {
70
- // Move all arrays with runtime determined size to the heap.
71
- if (options.dynamicArrayOnHeap )
72
- return false ;
73
- } else {
42
+ if (!fir::hasDynamicSize (seqTy)) {
74
43
std::int64_t numberOfElements = 1 ;
75
44
for (std::int64_t i : seqTy.getShape ()) {
76
45
numberOfElements *= i;
@@ -82,58 +51,37 @@ keepStackAllocation(fir::AllocaOp alloca, mlir::Block *entry,
82
51
// the heap.
83
52
if (static_cast <std::size_t >(numberOfElements) >
84
53
options.maxStackArraySize ) {
85
- LLVM_DEBUG (llvm::dbgs ()
86
- << " memory allocation opt: found " << alloca << ' \n ' );
87
54
return false ;
88
55
}
89
56
}
90
57
}
91
58
return true ;
92
59
}
93
60
94
- namespace {
95
- class AllocaOpConversion : public mlir ::OpRewritePattern<fir::AllocaOp> {
96
- public:
97
- using OpRewritePattern::OpRewritePattern;
98
-
99
- AllocaOpConversion (mlir::MLIRContext *ctx,
100
- llvm::ArrayRef<mlir::Operation *> rets)
101
- : OpRewritePattern(ctx), returnOps(rets) {}
102
-
103
- llvm::LogicalResult
104
- matchAndRewrite (fir::AllocaOp alloca,
105
- mlir::PatternRewriter &rewriter) const override {
106
- auto loc = alloca.getLoc ();
107
- mlir::Type varTy = alloca.getInType ();
108
- auto unpackName =
109
- [](std::optional<llvm::StringRef> opt) -> llvm::StringRef {
110
- if (opt)
111
- return *opt;
112
- return {};
113
- };
114
- auto uniqName = unpackName (alloca.getUniqName ());
115
- auto bindcName = unpackName (alloca.getBindcName ());
116
- auto heap = rewriter.create <fir::AllocMemOp>(
117
- loc, varTy, uniqName, bindcName, alloca.getTypeparams (),
118
- alloca.getShape ());
119
- auto insPt = rewriter.saveInsertionPoint ();
120
- for (mlir::Operation *retOp : returnOps) {
121
- rewriter.setInsertionPoint (retOp);
122
- [[maybe_unused]] auto free = rewriter.create <fir::FreeMemOp>(loc, heap);
123
- LLVM_DEBUG (llvm::dbgs () << " memory allocation opt: add free " << free
124
- << " for " << heap << ' \n ' );
125
- }
126
- rewriter.restoreInsertionPoint (insPt);
127
- rewriter.replaceOpWithNewOp <fir::ConvertOp>(
128
- alloca, fir::ReferenceType::get (varTy), heap);
129
- LLVM_DEBUG (llvm::dbgs () << " memory allocation opt: replaced " << alloca
130
- << " with " << heap << ' \n ' );
131
- return mlir::success ();
132
- }
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 {
65
+ if (opt)
66
+ return *opt;
67
+ return {};
68
+ };
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 (),
73
+ alloca.getShape ());
74
+ LLVM_DEBUG (llvm::dbgs () << " memory allocation opt: replaced " << alloca
75
+ << " with " << heap << ' \n ' );
76
+ return heap;
77
+ }
133
78
134
- private:
135
- llvm::ArrayRef<mlir::Operation *> returnOps;
136
- };
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 ' );
84
+ }
137
85
138
86
// / This pass can reclassify memory allocations (fir.alloca, fir.allocmem) based
139
87
// / on heuristics and settings. The intention is to allow better performance and
@@ -144,6 +92,7 @@ class AllocaOpConversion : public mlir::OpRewritePattern<fir::AllocaOp> {
144
92
// / make it a heap allocation.
145
93
// / 2. If a stack allocation is an array with a runtime evaluated size make
146
94
// / it a heap allocation.
95
+ namespace {
147
96
class MemoryAllocationOpt
148
97
: public fir::impl::MemoryAllocationOptBase<MemoryAllocationOpt> {
149
98
public:
@@ -184,23 +133,17 @@ class MemoryAllocationOpt
184
133
// If func is a declaration, skip it.
185
134
if (func.empty ())
186
135
return ;
187
-
188
- const auto &analysis = getAnalysis<ReturnAnalysis>();
189
-
190
- target.addLegalDialect <fir::FIROpsDialect, mlir::arith::ArithDialect,
191
- mlir::func::FuncDialect>();
192
- target.addDynamicallyLegalOp <fir::AllocaOp>([&](fir::AllocaOp alloca) {
193
- return keepStackAllocation (alloca, &func.front (), options);
194
- });
195
-
196
- llvm::SmallVector<mlir::Operation *> returnOps = analysis.getReturns (func);
197
- patterns.insert <AllocaOpConversion>(context, returnOps);
198
- if (mlir::failed (
199
- mlir::applyPartialConversion (func, target, std::move (patterns)))) {
200
- mlir::emitError (func.getLoc (),
201
- " error in memory allocation optimization\n " );
202
- signalPassFailure ();
203
- }
136
+ auto tryReplacing = [&](fir::AllocaOp alloca) {
137
+ bool res = !keepStackAllocation (alloca, options);
138
+ if (res) {
139
+ LLVM_DEBUG (llvm::dbgs ()
140
+ << " memory allocation opt: found " << alloca << ' \n ' );
141
+ }
142
+ return res;
143
+ };
144
+ mlir::IRRewriter rewriter (context);
145
+ fir::replaceAllocas (rewriter, func.getOperation (), tryReplacing,
146
+ genAllocmem, genFreemem);
204
147
}
205
148
206
149
private:
0 commit comments