xref: /llvm-project/flang/include/flang/Lower/StatementContext.h (revision ff7fca7fa8646d73f884ab8a351e4178499c4d05)
1 //===-- StatementContext.h --------------------------------------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef FORTRAN_LOWER_STATEMENTCONTEXT_H
14 #define FORTRAN_LOWER_STATEMENTCONTEXT_H
15 
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/SmallVector.h"
18 #include <functional>
19 #include <optional>
20 
21 namespace mlir {
22 class Location;
23 class Region;
24 } // namespace mlir
25 
26 namespace fir {
27 class FirOpBuilder;
28 }
29 
30 namespace Fortran::lower {
31 
32 /// When lowering a statement, temporaries for intermediate results may be
33 /// allocated on the heap. A StatementContext enables their deallocation
34 /// with one of several explicit finalize calls, or with an implicit
35 /// call to finalizeAndPop() at the end of the context. A context may prohibit
36 /// temporary allocation. Otherwise, an initial "outer" context scope may have
37 /// nested context scopes, which must make explicit subscope finalize calls.
38 ///
39 /// In addition to being useful for individual action statement contexts, a
40 /// StatementContext is also useful for construct blocks delimited by a pair
41 /// of statements such as (block-stmt, end-block-stmt), or a program unit
42 /// delimited by a pair of statements such as (subroutine-stmt, end-subroutine-
43 /// stmt). Attached cleanup code for these contexts may include stack
44 /// management code, deallocation code, and finalization of derived type
45 /// entities in the context.
46 class StatementContext {
47 public:
48   explicit StatementContext(bool cleanupProhibited = false) {
49     if (cleanupProhibited)
50       return;
51     cufs.push_back({});
52   }
53 
54   ~StatementContext() {
55     if (!cufs.empty())
56       finalizeAndPop();
57     assert(cufs.empty() && "invalid StatementContext destructor call");
58   }
59 
60   using CleanupFunction = std::function<void()>;
61 
62   /// Push a context subscope.
63   void pushScope() {
64     assert(!cufs.empty() && "invalid pushScope statement context");
65     cufs.push_back({});
66   }
67 
68   /// Append a cleanup function to the "list" of cleanup functions.
69   void attachCleanup(CleanupFunction cuf) {
70     assert(!cufs.empty() && "invalid attachCleanup statement context");
71     if (cufs.back()) {
72       CleanupFunction oldCleanup = *cufs.back();
73       cufs.back() = [=]() {
74         cuf();
75         oldCleanup();
76       };
77     } else {
78       cufs.back() = cuf;
79     }
80   }
81 
82   /// Make cleanup calls. Retain the stack top list for a repeat call.
83   void finalizeAndKeep() {
84     assert(!cufs.empty() && "invalid finalize statement context");
85     if (cufs.back())
86       (*cufs.back())();
87   }
88 
89   /// Make cleanup calls. Clear the stack top list.
90   void finalizeAndReset() {
91     finalizeAndKeep();
92     cufs.back().reset();
93   }
94 
95   /// Pop the stack top list.
96   void pop() { cufs.pop_back(); }
97 
98   /// Make cleanup calls. Pop the stack top list.
99   void finalizeAndPop() {
100     finalizeAndKeep();
101     pop();
102   }
103 
104   bool hasCode() const {
105     return !cufs.empty() && llvm::any_of(cufs, [](auto &opt) -> bool {
106       return opt.has_value();
107     });
108   }
109 
110 private:
111   // A statement context should never be copied or moved.
112   StatementContext(const StatementContext &) = delete;
113   StatementContext &operator=(const StatementContext &) = delete;
114   StatementContext(StatementContext &&) = delete;
115 
116   // Stack of cleanup function "lists" (nested cleanup function calls).
117   llvm::SmallVector<std::optional<CleanupFunction>> cufs;
118 };
119 
120 /// If \p context contains any cleanups, ensure \p region has a block, and
121 /// generate the cleanup inside that block.
122 void genCleanUpInRegionIfAny(mlir::Location loc, fir::FirOpBuilder &builder,
123                              mlir::Region &region, StatementContext &context);
124 
125 } // namespace Fortran::lower
126 
127 #endif // FORTRAN_LOWER_STATEMENTCONTEXT_H
128