clang 22.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
28/// A branch fixup. These are required when emitting a goto to a
29/// label which hasn't been emitted yet. The goto is optimistically
30/// emitted as a branch to the basic block for the label, and (if it
31/// occurs in a scope with non-trivial cleanups) a fixup is added to
32/// the innermost cleanup. When a (normal) cleanup is popped, any
33/// unresolved fixups in that scope are threaded through the cleanup.
35 /// The block containing the terminator which needs to be modified
36 /// into a switch if this fixup is resolved into the current scope.
37 /// If null, LatestBranch points directly to the destination.
38 mlir::Block *optimisticBranchBlock = nullptr;
39
40 /// The ultimate destination of the branch.
41 ///
42 /// This can be set to null to indicate that this fixup was
43 /// successfully resolved.
44 mlir::Block *destination = nullptr;
45
46 /// The destination index value.
47 unsigned destinationIndex = 0;
48
49 /// The initial branch of the fixup.
50 cir::BrOp initialBranch = {};
51};
52
53enum CleanupKind : unsigned {
54 /// Denotes a cleanup that should run when a scope is exited using exceptional
55 /// control flow (a throw statement leading to stack unwinding, ).
56 EHCleanup = 0x1,
57
58 /// Denotes a cleanup that should run when a scope is exited using normal
59 /// control flow (falling off the end of the scope, return, goto, ...).
61
63
66};
67
68/// A stack of scopes which respond to exceptions, including cleanups
69/// and catch blocks.
71 friend class CIRGenFunction;
72
73public:
74 // TODO(ogcg): Switch to alignof(uint64_t) instead of 8
75 enum { ScopeStackAlignment = 8 };
76
77 /// A saved depth on the scope stack. This is necessary because
78 /// pushing scopes onto the stack invalidates iterators.
79 class stable_iterator {
80 friend class EHScopeStack;
81
82 /// Offset from startOfData to endOfBuffer.
83 ptrdiff_t size = -1;
84
85 explicit stable_iterator(ptrdiff_t size) : size(size) {}
86
87 public:
88 static stable_iterator invalid() { return stable_iterator(-1); }
89 stable_iterator() = default;
90
91 bool isValid() const { return size >= 0; }
92
93 /// Returns true if this scope encloses I.
94 /// Returns false if I is invalid.
95 /// This scope must be valid.
96 bool encloses(stable_iterator other) const { return size <= other.size; }
97
98 /// Returns true if this scope strictly encloses I: that is,
99 /// if it encloses I and is not I.
100 /// Returns false is I is invalid.
101 /// This scope must be valid.
102 bool strictlyEncloses(stable_iterator I) const { return size < I.size; }
103
104 friend bool operator==(stable_iterator A, stable_iterator B) {
105 return A.size == B.size;
106 }
107 friend bool operator!=(stable_iterator A, stable_iterator B) {
108 return A.size != B.size;
109 }
110 };
111
112 /// Information for lazily generating a cleanup. Subclasses must be
113 /// POD-like: cleanups will not be destructed, and they will be
114 /// allocated on the cleanup stack and freely copied and moved
115 /// around.
116 ///
117 /// Cleanup implementations should generally be declared in an
118 /// anonymous namespace.
119 class LLVM_MOVABLE_POLYMORPHIC_TYPE Cleanup {
120 // Anchor the construction vtable.
121 virtual void anchor();
122
123 public:
124 Cleanup(const Cleanup &) = default;
126 Cleanup() = default;
127
128 virtual ~Cleanup() = default;
129
130 /// Emit the cleanup. For normal cleanups, this is run in the
131 /// same EH context as when the cleanup was pushed, i.e. the
132 /// immediately-enclosing context of the cleanup scope. For
133 /// EH cleanups, this is run in a terminate context.
134 ///
135 // \param flags cleanup kind.
136 virtual void emit(CIRGenFunction &cgf) = 0;
137 };
138
139private:
140 // The implementation for this class is in CIRGenCleanup.h and
141 // CIRGenCleanup.cpp; the definition is here because it's used as a
142 // member of CIRGenFunction.
143
144 /// The start of the scope-stack buffer, i.e. the allocated pointer
145 /// for the buffer. All of these pointers are either simultaneously
146 /// null or simultaneously valid.
147 std::unique_ptr<char[]> startOfBuffer;
148
149 /// The end of the buffer.
150 char *endOfBuffer = nullptr;
151
152 /// The first valid entry in the buffer.
153 char *startOfData = nullptr;
154
155 /// The innermost normal cleanup on the stack.
156 stable_iterator innermostNormalCleanup = stable_end();
157
158 /// The innermost EH scope on the stack.
159 stable_iterator innermostEHScope = stable_end();
160
161 /// The CGF this Stack belong to
162 CIRGenFunction *cgf = nullptr;
163
164 /// The current set of branch fixups. A branch fixup is a jump to
165 /// an as-yet unemitted label, i.e. a label for which we don't yet
166 /// know the EH stack depth. Whenever we pop a cleanup, we have
167 /// to thread all the current branch fixups through it.
168 ///
169 /// Fixups are recorded as the Use of the respective branch or
170 /// switch statement. The use points to the final destination.
171 /// When popping out of a cleanup, these uses are threaded through
172 /// the cleanup and adjusted to point to the new cleanup.
173 ///
174 /// Note that branches are allowed to jump into protected scopes
175 /// in certain situations; e.g. the following code is legal:
176 /// struct A { ~A(); }; // trivial ctor, non-trivial dtor
177 /// goto foo;
178 /// A a;
179 /// foo:
180 /// bar();
182
183 // This class uses a custom allocator for maximum efficiency because cleanups
184 // are allocated and freed very frequently. It's basically a bump pointer
185 // allocator, but we can't use LLVM's BumpPtrAllocator because we use offsets
186 // into the buffer as stable iterators.
187 char *allocate(size_t size);
188 void deallocate(size_t size);
189
190 void *pushCleanup(CleanupKind kind, size_t dataSize);
191
192public:
193 EHScopeStack() = default;
194 ~EHScopeStack() = default;
195
196 /// Push a lazily-created cleanup on the stack.
197 template <class T, class... As> void pushCleanup(CleanupKind kind, As... a) {
198 static_assert(alignof(T) <= ScopeStackAlignment,
199 "Cleanup's alignment is too large.");
200 void *buffer = pushCleanup(kind, sizeof(T));
201 [[maybe_unused]] Cleanup *obj = new (buffer) T(a...);
202 }
203
204 void setCGF(CIRGenFunction *inCGF) { cgf = inCGF; }
205
206 /// Pops a cleanup scope off the stack. This is private to CIRGenCleanup.cpp.
208
209 /// Push a set of catch handlers on the stack. The catch is
210 /// uninitialized and will need to have the given number of handlers
211 /// set on it.
212 class EHCatchScope *pushCatch(unsigned numHandlers);
213
214 /// Pops a catch scope off the stack. This is private to CIRGenException.cpp.
215 void popCatch();
216
217 /// Determines whether the exception-scopes stack is empty.
218 bool empty() const { return startOfData == endOfBuffer; }
219
221
222 /// Determines whether there are any normal cleanups on the stack.
223 bool hasNormalCleanups() const {
224 return innermostNormalCleanup != stable_end();
225 }
226
227 /// Returns the innermost normal cleanup on the stack, or
228 /// stable_end() if there are no normal cleanups.
230 return innermostNormalCleanup;
231 }
233
234 stable_iterator getInnermostEHScope() const { return innermostEHScope; }
235
236 /// An unstable reference to a scope-stack depth. Invalidated by
237 /// pushes but not pops.
238 class iterator;
239
240 /// Returns an iterator pointing to the innermost EH scope.
241 iterator begin() const;
242
243 /// Returns an iterator pointing to the outermost EH scope.
244 iterator end() const;
245
246 /// Create a stable reference to the top of the EH stack. The
247 /// returned reference is valid until that scope is popped off the
248 /// stack.
250 return stable_iterator(endOfBuffer - startOfData);
251 }
252
253 /// Create a stable reference to the bottom of the EH stack.
255
256 /// Turn a stable reference to a scope depth into a unstable pointer
257 /// to the EH stack.
258 iterator find(stable_iterator savePoint) const;
259
260 /// Add a branch fixup to the current cleanup scope.
262 assert(hasNormalCleanups() && "adding fixup in scope without cleanups");
263 branchFixups.push_back(BranchFixup());
264 return branchFixups.back();
265 }
266
267 unsigned getNumBranchFixups() const { return branchFixups.size(); }
269 assert(i < getNumBranchFixups());
270 return branchFixups[i];
271 }
272
273 /// Pops lazily-removed fixups from the end of the list. This
274 /// should only be called by procedures which have just popped a
275 /// cleanup or resolved one or more fixups.
277};
278
279} // namespace clang::CIRGen
280
281#endif // CLANG_LIB_CIR_CODEGEN_EHSCOPESTACK_H
__PTRDIFF_TYPE__ ptrdiff_t
A signed integer type that is the result of subtracting two pointers.
A scope which attempts to handle some, possibly all, types of exceptions.
Information for lazily generating a cleanup.
Cleanup(const Cleanup &)=default
virtual void emit(CIRGenFunction &cgf)=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.
void popCatch()
Pops a catch scope off the stack. This is private to CIRGenException.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.
void popNullFixups()
Pops lazily-removed fixups from the end of the list.
iterator end() const
Returns an iterator pointing to the outermost EH scope.
class EHCatchScope * pushCatch(unsigned numHandlers)
Push a set of catch handlers on the stack.
bool requiresCatchOrCleanup() const
stable_iterator getInnermostActiveNormalCleanup() const
stable_iterator getInnermostEHScope() const
unsigned getNumBranchFixups() const
static stable_iterator stable_end()
Create a stable reference to the bottom of the EH stack.
BranchFixup & getBranchFixup(unsigned i)
BranchFixup & addBranchFixup()
Add a branch fixup to the current cleanup scope.
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.
@ 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
cir::BrOp initialBranch
The initial branch of the fixup.
mlir::Block * destination
The ultimate destination of the branch.
mlir::Block * optimisticBranchBlock
The block containing the terminator which needs to be modified into a switch if this fixup is resolve...
unsigned destinationIndex
The destination index value.