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/Optional.h" 17 #include "llvm/ADT/SmallVector.h" 18 #include <functional> 19 20 namespace Fortran::lower { 21 22 /// When lowering a statement, temporaries for intermediate results may be 23 /// allocated on the heap. A StatementContext enables their deallocation 24 /// either explicitly with finalize() calls, or implicitly at the end of 25 /// the context. A context may prohibit temporary allocation. Otherwise, 26 /// an initial "outer" context scope may have nested context scopes, which 27 /// must make explicit subscope finalize() calls. 28 class StatementContext { 29 public: 30 explicit StatementContext(bool cleanupProhibited = false) { 31 if (cleanupProhibited) 32 return; 33 cufs.push_back({}); 34 } 35 ~StatementContext()36 ~StatementContext() { 37 if (!cufs.empty()) 38 finalizeAndPop(); 39 assert(cufs.empty() && "invalid StatementContext destructor call"); 40 } 41 42 using CleanupFunction = std::function<void()>; 43 44 /// Push a context subscope. pushScope()45 void pushScope() { 46 assert(!cufs.empty() && "invalid pushScope statement context"); 47 cufs.push_back({}); 48 } 49 50 /// Append a cleanup function to the "list" of cleanup functions. attachCleanup(CleanupFunction cuf)51 void attachCleanup(CleanupFunction cuf) { 52 assert(!cufs.empty() && "invalid attachCleanup statement context"); 53 if (cufs.back()) { 54 CleanupFunction oldCleanup = *cufs.back(); 55 cufs.back() = [=]() { 56 cuf(); 57 oldCleanup(); 58 }; 59 } else { 60 cufs.back() = cuf; 61 } 62 } 63 64 /// Make cleanup calls. Retain the stack top list for a repeat call. finalizeAndKeep()65 void finalizeAndKeep() { 66 assert(!cufs.empty() && "invalid finalize statement context"); 67 if (cufs.back()) 68 (*cufs.back())(); 69 } 70 71 /// Make cleanup calls. Pop the stack top list. finalizeAndPop()72 void finalizeAndPop() { 73 finalizeAndKeep(); 74 cufs.pop_back(); 75 } 76 77 /// Make cleanup calls. Clear the stack top list. finalize()78 void finalize() { 79 finalizeAndKeep(); 80 cufs.back().reset(); 81 } 82 workListIsEmpty()83 bool workListIsEmpty() const { 84 return cufs.empty() || llvm::all_of(cufs, [](auto &opt) -> bool { 85 return !opt.has_value(); 86 }); 87 } 88 89 private: 90 // A statement context should never be copied or moved. 91 StatementContext(const StatementContext &) = delete; 92 StatementContext &operator=(const StatementContext &) = delete; 93 StatementContext(StatementContext &&) = delete; 94 95 // Stack of cleanup function "lists" (nested cleanup function calls). 96 llvm::SmallVector<llvm::Optional<CleanupFunction>> cufs; 97 }; 98 99 } // namespace Fortran::lower 100 101 #endif // FORTRAN_LOWER_STATEMENTCONTEXT_H 102