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 CGF this Stack belong to
159 CIRGenFunction *cgf = nullptr;
160
161 /// The current set of branch fixups. A branch fixup is a jump to
162 /// an as-yet unemitted label, i.e. a label for which we don't yet
163 /// know the EH stack depth. Whenever we pop a cleanup, we have
164 /// to thread all the current branch fixups through it.
165 ///
166 /// Fixups are recorded as the Use of the respective branch or
167 /// switch statement. The use points to the final destination.
168 /// When popping out of a cleanup, these uses are threaded through
169 /// the cleanup and adjusted to point to the new cleanup.
170 ///
171 /// Note that branches are allowed to jump into protected scopes
172 /// in certain situations; e.g. the following code is legal:
173 /// struct A { ~A(); }; // trivial ctor, non-trivial dtor
174 /// goto foo;
175 /// A a;
176 /// foo:
177 /// bar();
179
180 // This class uses a custom allocator for maximum efficiency because cleanups
181 // are allocated and freed very frequently. It's basically a bump pointer
182 // allocator, but we can't use LLVM's BumpPtrAllocator because we use offsets
183 // into the buffer as stable iterators.
184 char *allocate(size_t size);
185 void deallocate(size_t size);
186
187 void *pushCleanup(CleanupKind kind, size_t dataSize);
188
189public:
190 EHScopeStack() = default;
191 ~EHScopeStack() = default;
192
193 /// Push a lazily-created cleanup on the stack.
194 template <class T, class... As> void pushCleanup(CleanupKind kind, As... a) {
195 static_assert(alignof(T) <= ScopeStackAlignment,
196 "Cleanup's alignment is too large.");
197 void *buffer = pushCleanup(kind, sizeof(T));
198 [[maybe_unused]] Cleanup *obj = new (buffer) T(a...);
199 }
200
201 void setCGF(CIRGenFunction *inCGF) { cgf = inCGF; }
202
203 /// Pops a cleanup scope off the stack. This is private to CIRGenCleanup.cpp.
205
206 /// Push a set of catch handlers on the stack. The catch is
207 /// uninitialized and will need to have the given number of handlers
208 /// set on it.
209 class EHCatchScope *pushCatch(unsigned numHandlers);
210
211 /// Pops a catch scope off the stack. This is private to CIRGenException.cpp.
212 void popCatch();
213
214 /// Determines whether the exception-scopes stack is empty.
215 bool empty() const { return startOfData == endOfBuffer; }
216
217 /// Determines whether there are any normal cleanups on the stack.
218 bool hasNormalCleanups() const {
219 return innermostNormalCleanup != stable_end();
220 }
221
222 /// Returns the innermost normal cleanup on the stack, or
223 /// stable_end() if there are no normal cleanups.
225 return innermostNormalCleanup;
226 }
228
229 /// An unstable reference to a scope-stack depth. Invalidated by
230 /// pushes but not pops.
231 class iterator;
232
233 /// Returns an iterator pointing to the innermost EH scope.
234 iterator begin() const;
235
236 /// Create a stable reference to the top of the EH stack. The
237 /// returned reference is valid until that scope is popped off the
238 /// stack.
240 return stable_iterator(endOfBuffer - startOfData);
241 }
242
243 /// Create a stable reference to the bottom of the EH stack.
245
246 /// Turn a stable reference to a scope depth into a unstable pointer
247 /// to the EH stack.
248 iterator find(stable_iterator savePoint) const;
249
250 /// Add a branch fixup to the current cleanup scope.
252 assert(hasNormalCleanups() && "adding fixup in scope without cleanups");
253 branchFixups.push_back(BranchFixup());
254 return branchFixups.back();
255 }
256
257 unsigned getNumBranchFixups() const { return branchFixups.size(); }
259 assert(i < getNumBranchFixups());
260 return branchFixups[i];
261 }
262
263 /// Pops lazily-removed fixups from the end of the list. This
264 /// should only be called by procedures which have just popped a
265 /// cleanup or resolved one or more fixups.
267};
268
269} // namespace clang::CIRGen
270
271#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 non-stable pointer into the scope stack.
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.
class EHCatchScope * pushCatch(unsigned numHandlers)
Push a set of catch handlers on the stack.
stable_iterator getInnermostActiveNormalCleanup() 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.