clang 23.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 /// Generation flags.
106 class Flags {
107 enum {
108 F_IsForEH = 0x1,
109 F_IsNormalCleanupKind = 0x2,
110 F_IsEHCleanupKind = 0x4,
111 F_HasExitSwitch = 0x8,
112 };
113 unsigned flags = 0;
114
115 public:
116 Flags() = default;
117
118 /// isForEH - true if the current emission is for an EH cleanup.
119 bool isForEHCleanup() const { return flags & F_IsForEH; }
120 bool isForNormalCleanup() const { return !isForEHCleanup(); }
121 void setIsForEHCleanup() { flags |= F_IsForEH; }
122
123 bool isNormalCleanupKind() const { return flags & F_IsNormalCleanupKind; }
124 void setIsNormalCleanupKind() { flags |= F_IsNormalCleanupKind; }
125
126 /// isEHCleanupKind - true if the cleanup was pushed as an EH
127 /// cleanup.
128 bool isEHCleanupKind() const { return flags & F_IsEHCleanupKind; }
129 void setIsEHCleanupKind() { flags |= F_IsEHCleanupKind; }
130
131 bool hasExitSwitch() const { return flags & F_HasExitSwitch; }
132 void setHasExitSwitch() { flags |= F_HasExitSwitch; }
133 };
134
135 /// Emit the cleanup. For normal cleanups, this is run in the
136 /// same EH context as when the cleanup was pushed, i.e. the
137 /// immediately-enclosing context of the cleanup scope. For
138 /// EH cleanups, this is run in a terminate context.
139 ///
140 // \param flags cleanup kind.
141 virtual void emit(CIRGenFunction &cgf, Flags flags) = 0;
142 };
143
144private:
145 // The implementation for this class is in CIRGenCleanup.h and
146 // CIRGenCleanup.cpp; the definition is here because it's used as a
147 // member of CIRGenFunction.
148
149 /// The start of the scope-stack buffer, i.e. the allocated pointer
150 /// for the buffer. All of these pointers are either simultaneously
151 /// null or simultaneously valid.
152 std::unique_ptr<char[]> startOfBuffer;
153
154 /// The end of the buffer.
155 char *endOfBuffer = nullptr;
156
157 /// The first valid entry in the buffer.
158 char *startOfData = nullptr;
159
160 /// The innermost normal cleanup on the stack.
161 stable_iterator innermostNormalCleanup = stable_end();
162
163 /// The innermost EH scope on the stack.
164 stable_iterator innermostEHScope = stable_end();
165
166 /// The CGF this Stack belong to
167 CIRGenFunction *cgf = nullptr;
168
169 // This class uses a custom allocator for maximum efficiency because cleanups
170 // are allocated and freed very frequently. It's basically a bump pointer
171 // allocator, but we can't use LLVM's BumpPtrAllocator because we use offsets
172 // into the buffer as stable iterators.
173 char *allocate(size_t size);
174 void deallocate(size_t size);
175
176 void *pushCleanup(CleanupKind kind, size_t dataSize);
177
178public:
179 EHScopeStack() = default;
180 ~EHScopeStack() = default;
181
182 /// Push a lazily-created cleanup on the stack.
183 template <class T, class... As> void pushCleanup(CleanupKind kind, As... a) {
184 static_assert(alignof(T) <= ScopeStackAlignment,
185 "Cleanup's alignment is too large.");
186 void *buffer = pushCleanup(kind, sizeof(T));
187 [[maybe_unused]] Cleanup *obj = new (buffer) T(a...);
188 }
189
190 /// Push a cleanup with non-constant storage requirements on the
191 /// stack. The cleanup type must provide an additional static method:
192 /// static size_t getExtraSize(size_t);
193 /// The argument to this method will be the value N, which will also
194 /// be passed as the first argument to the constructor.
195 ///
196 /// The data stored in the extra storage must obey the same
197 /// restrictions as normal cleanup member data.
198 ///
199 /// The pointer returned from this method is valid until the cleanup
200 /// stack is modified.
201 template <class T, class... As>
202 T *pushCleanupWithExtra(CleanupKind kind, size_t n, As... a) {
203 static_assert(alignof(T) <= ScopeStackAlignment,
204 "Cleanup's alignment is too large.");
205 void *buffer = pushCleanup(kind, sizeof(T) + T::getExtraSize(n));
206 return new (buffer) T(n, a...);
207 }
208
209 void setCGF(CIRGenFunction *inCGF) { cgf = inCGF; }
210
211 /// Pops a cleanup scope off the stack. This is private to CIRGenCleanup.cpp.
213
214 /// Determines whether the exception-scopes stack is empty.
215 bool empty() const { return startOfData == endOfBuffer; }
216
218
219 /// Determines whether there are any normal cleanups on the stack.
220 bool hasNormalCleanups() const {
221 return innermostNormalCleanup != stable_end();
222 }
223
224 /// Returns the innermost normal cleanup on the stack, or
225 /// stable_end() if there are no normal cleanups.
227 return innermostNormalCleanup;
228 }
230
231 stable_iterator getInnermostEHScope() const { return innermostEHScope; }
232
233 /// An unstable reference to a scope-stack depth. Invalidated by
234 /// pushes but not pops.
235 class iterator;
236
237 /// Returns an iterator pointing to the innermost EH scope.
238 iterator begin() const;
239
240 /// Returns an iterator pointing to the outermost EH scope.
241 iterator end() const;
242
243 /// Create a stable reference to the top of the EH stack. The
244 /// returned reference is valid until that scope is popped off the
245 /// stack.
247 return stable_iterator(endOfBuffer - startOfData);
248 }
249
250 /// Create a stable reference to the bottom of the EH stack.
252
253 /// Turn a stable reference to a scope depth into a unstable pointer
254 /// to the EH stack.
255 iterator find(stable_iterator savePoint) const;
256};
257
258} // namespace clang::CIRGen
259
260#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 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.
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...