99#include " flang/Optimizer/Dialect/FIRDialect.h"
1010#include " flang/Optimizer/Dialect/FIROps.h"
1111#include " flang/Optimizer/Dialect/FIRType.h"
12+ #include " flang/Optimizer/Transforms/MemoryUtils.h"
1213#include " flang/Optimizer/Transforms/Passes.h"
1314#include " mlir/Dialect/Func/IR/FuncOps.h"
1415#include " mlir/IR/Diagnostics.h"
@@ -27,50 +28,18 @@ namespace fir {
2728// Number of elements in an array does not determine where it is allocated.
2829static constexpr std::size_t unlimitedArraySize = ~static_cast <std::size_t >(0 );
2930
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-
5831// / Return `true` if this allocation is to remain on the stack (`fir.alloca`).
5932// / Otherwise the allocation should be moved to the heap (`fir.allocmem`).
6033static inline bool
61- keepStackAllocation (fir::AllocaOp alloca, mlir::Block *entry,
34+ keepStackAllocation (fir::AllocaOp alloca,
6235 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.
6841 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)) {
7443 std::int64_t numberOfElements = 1 ;
7544 for (std::int64_t i : seqTy.getShape ()) {
7645 numberOfElements *= i;
@@ -82,58 +51,37 @@ keepStackAllocation(fir::AllocaOp alloca, mlir::Block *entry,
8251 // the heap.
8352 if (static_cast <std::size_t >(numberOfElements) >
8453 options.maxStackArraySize ) {
85- LLVM_DEBUG (llvm::dbgs ()
86- << " memory allocation opt: found " << alloca << ' \n ' );
8754 return false ;
8855 }
8956 }
9057 }
9158 return true ;
9259}
9360
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+ }
13378
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+ }
13785
13886// / This pass can reclassify memory allocations (fir.alloca, fir.allocmem) based
13987// / on heuristics and settings. The intention is to allow better performance and
@@ -144,6 +92,7 @@ class AllocaOpConversion : public mlir::OpRewritePattern<fir::AllocaOp> {
14492// / make it a heap allocation.
14593// / 2. If a stack allocation is an array with a runtime evaluated size make
14694// / it a heap allocation.
95+ namespace {
14796class MemoryAllocationOpt
14897 : public fir::impl::MemoryAllocationOptBase<MemoryAllocationOpt> {
14998public:
@@ -184,23 +133,17 @@ class MemoryAllocationOpt
184133 // If func is a declaration, skip it.
185134 if (func.empty ())
186135 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);
204147 }
205148
206149private:
0 commit comments