clang 24.0.0git
EHScopeStack.h
Go to the documentation of this file.
1//===-- EHScopeStack.h - Stack for cleanup CIR generation -------*- 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// These classes should be the minimum interface required for other parts of
10// CIR CodeGen to emit cleanups. The implementation is in CIRGenCleanup.cpp and
11// other implemenentation details that are not widely needed are in
12// CIRGenCleanup.h.
13//
14// TODO(cir): this header should be shared between LLVM and CIR codegen.
15//
16//===----------------------------------------------------------------------===//
17
18#ifndef CLANG_LIB_CIR_CODEGEN_EHSCOPESTACK_H
19#define CLANG_LIB_CIR_CODEGEN_EHSCOPESTACK_H
20
22#include "llvm/ADT/SmallVector.h"
23
24namespace clang::CIRGen {
25
26class CIRGenFunction;
27
28enum CleanupKind : unsigned {
29 /// Denotes a cleanup that should run when a scope is exited using exceptional
30 /// control flow (a throw statement leading to stack unwinding, ).
31 EHCleanup = 0x1,
32
33 /// Denotes a cleanup that should run when a scope is exited using normal
34 /// control flow (falling off the end of the scope, return, goto, ...).
36
38
41};
42
43/// A stack of scopes which respond to exceptions, including cleanups
44/// and catch blocks.
46 friend class CIRGenFunction;
47
48public:
49 // TODO(ogcg): Switch to alignof(uint64_t) instead of 8
50 enum { ScopeStackAlignment = 8 };
51
52 /// A saved depth on the scope stack. This is necessary because
53 /// pushing scopes onto the stack invalidates iterators.
54 class stable_iterator {
55 friend class EHScopeStack;
56
57 /// Offset from startOfData to endOfBuffer.
58 ptrdiff_t size = -1;
59
60 explicit stable_iterator(ptrdiff_t size) : size(size) {}
61
62 public:
63 static stable_iterator invalid() { return stable_iterator(-1); }
64 stable_iterator() = default;
65
66 bool isValid() const { return size >= 0; }
67
68 /// Returns true if this scope encloses I.
69 /// Returns false if I is invalid.
70 /// This scope must be valid.
71 bool encloses(stable_iterator other) const { return size <= other.size; }
72
73 /// Returns true if this scope strictly encloses I: that is,
74 /// if it encloses I and is not I.
75 /// Returns false is I is invalid.
76 /// This scope must be valid.
77 bool strictlyEncloses(stable_iterator I) const { return size < I.size; }
78
79 friend bool operator==(stable_iterator A, stable_iterator B) {
80 return A.size == B.size;
81 }
82 friend bool operator!=(stable_iterator A, stable_iterator B) {
83 return A.size != B.size;
84 }
85 };
86
87 /// Information for lazily generating a cleanup. Subclasses must be
88 /// POD-like: cleanups will not be destructed, and they will be
89 /// allocated on the cleanup stack and freely copied and moved
90 /// around.
91 ///
92 /// Cleanup implementations should generally be declared in an
93 /// anonymous namespace.
94 class LLVM_MOVABLE_POLYMORPHIC_TYPE Cleanup {
95 // Anchor the construction vtable.
96 virtual void anchor();
97
98 public:
99 Cleanup(const Cleanup &) = default;
101 Cleanup() = default;
102
103 virtual ~Cleanup() = default;
104
105 virtual bool isRedundantBeforeReturn() { return false; }
106
107 /// Generation flags.
108 class Flags {
109 enum {
110 F_IsForEH = 0x1,
111 F_IsNormalCleanupKind = 0x2,
112 F_IsEHCleanupKind = 0x4,
113 F_HasExitSwitch = 0x8,
114 };
115 unsigned flags = 0;
116
117 public:
118 Flags() = default;
119
120 /// isForEH - true if the current emission is for an EH cleanup.
121 bool isForEHCleanup() const { return flags & F_IsForEH; }
122 bool isForNormalCleanup() const { return !isForEHCleanup(); }
123 void setIsForEHCleanup() { flags |= F_IsForEH; }
124
125 bool isNormalCleanupKind() const { return flags & F_IsNormalCleanupKind; }
126 void setIsNormalCleanupKind() { flags |= F_IsNormalCleanupKind; }
127
128 /// isEHCleanupKind - true if the cleanup was pushed as an EH
129 /// cleanup.
130 bool isEHCleanupKind() const { return flags & F_IsEHCleanupKind; }
131 void setIsEHCleanupKind() { flags |= F_IsEHCleanupKind; }
132
133 bool hasExitSwitch() const { return flags & F_HasExitSwitch; }
134 void setHasExitSwitch() { flags |= F_HasExitSwitch; }
135 };
136
137 /// Emit the cleanup. For normal cleanups, this is run in the
138 /// same EH context as when the cleanup was pushed, i.e. the
139 /// immediately-enclosing context of the cleanup scope. For
140 /// EH cleanups, this is run in a terminate context.
141 ///
142 // \param flags cleanup kind.
143 virtual void emit(CIRGenFunction &cgf, Flags flags) = 0;
144 };
145
146private:
147 // The implementation for this class is in CIRGenCleanup.h and
148 // CIRGenCleanup.cpp; the definition is here because it's used as a
149 // member of CIRGenFunction.
150
151 /// The start of the scope-stack buffer, i.e. the allocated pointer
152 /// for the buffer. All of these pointers are either simultaneously
153 /// null or simultaneously valid.
154 std::unique_ptr<char[]> startOfBuffer;
155
156 /// The end of the buffer.
157 char *endOfBuffer = nullptr;
158
159 /// The first valid entry in the buffer.
160 char *startOfData = nullptr;
161
162 /// The innermost normal cleanup on the stack.
163 stable_iterator innermostNormalCleanup = stable_end();
164
165 /// The innermost EH scope on the stack.
166 stable_iterator innermostEHScope = stable_end();
167
168 /// The CGF this Stack belong to
169 CIRGenFunction *cgf = nullptr;
170
171 /// When true, pushCleanup() does not create a cir.cleanup.scope op for the
172 /// pushed cleanup. This is set while emitting a loop's condition variable so
173 /// its destructor can be captured on the stack and later emitted into the
174 /// loop op's per-iteration cleanup region.
175 bool capturingLoopConditionCleanups = false;
176
177 // This class uses a custom allocator for maximum efficiency because cleanups
178 // are allocated and freed very frequently. It's basically a bump pointer
179 // allocator, but we can't use LLVM's BumpPtrAllocator because we use offsets
180 // into the buffer as stable iterators.
181 char *allocate(size_t size);
182 void deallocate(size_t size);
183
184 void *pushCleanup(CleanupKind kind, size_t dataSize);
185
186public:
187 EHScopeStack() = default;
188 ~EHScopeStack() = default;
189
190 /// Push a lazily-created cleanup on the stack.
191 template <class T, class... As> void pushCleanup(CleanupKind kind, As... a) {
192 static_assert(alignof(T) <= ScopeStackAlignment,
193 "Cleanup's alignment is too large.");
194 void *buffer = pushCleanup(kind, sizeof(T));
195 [[maybe_unused]] Cleanup *obj = new (buffer) T(a...);
196 }
197
198 /// Push a cleanup with non-constant storage requirements on the
199 /// stack. The cleanup type must provide an additional static method:
200 /// static size_t getExtraSize(size_t);
201 /// The argument to this method will be the value N, which will also
202 /// be passed as the first argument to the constructor.
203 ///
204 /// The data stored in the extra storage must obey the same
205 /// restrictions as normal cleanup member data.
206 ///
207 /// The pointer returned from this method is valid until the cleanup
208 /// stack is modified.
209 template <class T, class... As>
210 T *pushCleanupWithExtra(CleanupKind kind, size_t n, As... a) {
211 static_assert(alignof(T) <= ScopeStackAlignment,
212 "Cleanup's alignment is too large.");
213 void *buffer = pushCleanup(kind, sizeof(T) + T::getExtraSize(n));
214 return new (buffer) T(n, a...);
215 }
216
217 /// Push a cleanup by copying a serialized cleanup object from the
218 /// LifetimeExtendedCleanupStack onto the EH scope stack. This is used when
219 /// a full-expression's RunCleanupsScope exits: cleanups that were deferred
220 /// for lifetime extension (e.g. destroying a temporary bound to a local
221 /// reference) are promoted from the byte buffer to the enclosing scope's
222 /// EH stack so they run when that scope ends.
223 ///
224 /// The memcpy is safe because Cleanup subclasses are required to be POD-like
225 /// (see the Cleanup class comment), and the vtable pointer is part of the
226 /// copied bytes, so the clone dispatches to the correct emit() override.
227 void pushCopyOfCleanup(CleanupKind kind, const void *cleanup, size_t size) {
228 void *buffer = pushCleanup(kind, size);
229 std::memcpy(buffer, cleanup, size);
230 }
231
232 void setCGF(CIRGenFunction *inCGF) { cgf = inCGF; }
233
235 return capturingLoopConditionCleanups;
236 }
238 capturingLoopConditionCleanups = value;
239 }
240
241 /// Pops a cleanup scope off the stack. This is private to CIRGenCleanup.cpp.
243
244 /// Determines whether the exception-scopes stack is empty.
245 bool empty() const { return startOfData == endOfBuffer; }
246
248
249 /// Determines whether there are any normal cleanups on the stack.
250 bool hasNormalCleanups() const {
251 return innermostNormalCleanup != stable_end();
252 }
253
254 /// Returns the innermost normal cleanup on the stack, or
255 /// stable_end() if there are no normal cleanups.
257 return innermostNormalCleanup;
258 }
260
261 stable_iterator getInnermostEHScope() const { return innermostEHScope; }
262
263 /// An unstable reference to a scope-stack depth. Invalidated by
264 /// pushes but not pops.
265 class iterator;
266
267 /// Returns an iterator pointing to the innermost EH scope.
268 iterator begin() const;
269
270 /// Returns an iterator pointing to the outermost EH scope.
271 iterator end() const;
272
273 /// Create a stable reference to the top of the EH stack. The
274 /// returned reference is valid until that scope is popped off the
275 /// stack.
277 return stable_iterator(endOfBuffer - startOfData);
278 }
279
280 /// Create a stable reference to the bottom of the EH stack.
282
283 /// Turn a stable reference to a scope depth into a unstable pointer
284 /// to the EH stack.
285 iterator find(stable_iterator savePoint) const;
286};
287
288} // namespace clang::CIRGen
289
290#endif // CLANG_LIB_CIR_CODEGEN_EHSCOPESTACK_H
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
__PTRDIFF_TYPE__ ptrdiff_t
A signed integer type that is the result of subtracting two pointers.
bool isEHCleanupKind() const
isEHCleanupKind - true if the cleanup was pushed as an EH cleanup.
bool isForEHCleanup() const
isForEH - true if the current emission is for an EH cleanup.
Information for lazily generating a cleanup.
Cleanup(const Cleanup &)=default
virtual void emit(CIRGenFunction &cgf, Flags flags)=0
Emit the cleanup.
A saved depth on the scope stack.
bool strictlyEncloses(stable_iterator I) const
Returns true if this scope strictly encloses I: that is, if it encloses I and is not I.
bool encloses(stable_iterator other) const
Returns true if this scope encloses I.
friend bool operator==(stable_iterator A, stable_iterator B)
friend bool operator!=(stable_iterator A, stable_iterator B)
void setCGF(CIRGenFunction *inCGF)
void popCleanup()
Pops a cleanup scope off the stack. This is private to CIRGenCleanup.cpp.
bool hasNormalCleanups() const
Determines whether there are any normal cleanups on the stack.
iterator find(stable_iterator savePoint) const
Turn a stable reference to a scope depth into a unstable pointer to the EH stack.
stable_iterator stable_begin() const
Create a stable reference to the top of the EH stack.
void pushCleanup(CleanupKind kind, As... a)
Push a lazily-created cleanup on the stack.
bool empty() const
Determines whether the exception-scopes stack is empty.
iterator end() const
Returns an iterator pointing to the outermost EH scope.
bool isCapturingLoopConditionCleanups() const
bool requiresCatchOrCleanup() const
stable_iterator getInnermostActiveNormalCleanup() const
stable_iterator getInnermostEHScope() const
static stable_iterator stable_end()
Create a stable reference to the bottom of the EH stack.
iterator begin() const
Returns an iterator pointing to the innermost EH scope.
stable_iterator getInnermostNormalCleanup() const
Returns the innermost normal cleanup on the stack, or stable_end() if there are no normal cleanups.
void pushCopyOfCleanup(CleanupKind kind, const void *cleanup, size_t size)
Push a cleanup by copying a serialized cleanup object from the LifetimeExtendedCleanupStack onto the ...
void setCapturingLoopConditionCleanups(bool value)
T * pushCleanupWithExtra(CleanupKind kind, size_t n, As... a)
Push a cleanup with non-constant storage requirements on the stack.
@ EHCleanup
Denotes a cleanup that should run when a scope is exited using exceptional control flow (a throw stat...
@ NormalCleanup
Denotes a cleanup that should run when a scope is exited using normal control flow (falling off the e...
const FunctionProtoType * T