clang 24.0.0git
CIRGenCleanup.h
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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 support the generation of CIR for cleanups, initially based
10// on LLVM IR cleanup handling, but ought to change as CIR evolves.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H
15#define CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H
16
17#include "Address.h"
18#include "CIRGenModule.h"
19#include "EHScopeStack.h"
20#include "mlir/IR/Value.h"
21#include "clang/AST/StmtCXX.h"
23
24namespace clang::CIRGen {
25
26/// The MS C++ ABI needs a pointer to RTTI data plus some flags to describe the
27/// type of a catch handler, so we use this wrapper.
29 mlir::TypedAttr rtti;
30 unsigned flags;
31};
32
33/// A protected scope for zero-cost EH handling.
34class EHScope {
35 EHScopeStack::stable_iterator enclosingEHScope;
36
37 class CommonBitFields {
38 friend class EHScope;
39 unsigned kind : 3;
40 };
41 enum { NumCommonBits = 3 };
42
43 bool scopeMayThrow;
44
45protected:
47 friend class EHCleanupScope;
48 unsigned : NumCommonBits;
49
50 /// Whether this cleanup needs to be run along normal edges.
51 unsigned isNormalCleanup : 1;
52
53 /// Whether this cleanup needs to be run along exception edges.
54 unsigned isEHCleanup : 1;
55
56 /// Whether this cleanup is currently active.
57 unsigned isActive : 1;
58
59 /// Whether this cleanup is a lifetime marker
60 unsigned isLifetimeMarker : 1;
61
62 /// Whether the normal cleanup should test the activation flag.
63 unsigned testFlagInNormalCleanup : 1;
64
65 /// Whether the EH cleanup should test the activation flag.
66 unsigned testFlagInEHCleanup : 1;
67
68 /// The amount of extra storage needed by the Cleanup.
69 /// Always a multiple of the scope-stack alignment.
70 unsigned cleanupSize : 12;
71 };
72
73 union {
74 CommonBitFields commonBits;
76 };
77
78public:
80
82 : enclosingEHScope(enclosingEHScope) {
83 commonBits.kind = kind;
84 }
85
86 Kind getKind() const { return static_cast<Kind>(commonBits.kind); }
87
88 bool mayThrow() const {
89 // Traditional LLVM codegen also checks for `!block->use_empty()`, but
90 // in CIRGen the block content is not important, just used as a way to
91 // signal `hasEHBranches`.
92 return scopeMayThrow;
93 }
94
95 void setMayThrow(bool mayThrow) { scopeMayThrow = mayThrow; }
96
98 return enclosingEHScope;
99 }
100};
101
102/// A cleanup scope which generates the cleanup blocks lazily.
103class alignas(EHScopeStack::ScopeStackAlignment) EHCleanupScope
104 : public EHScope {
105 /// The nearest normal cleanup scope enclosing this one.
106 EHScopeStack::stable_iterator enclosingNormal;
107
108 /// The dual entry/exit block along the normal edge. This is lazily
109 /// created if needed before the cleanup is popped.
110 mlir::Block *normalBlock = nullptr;
111
112 /// An optional boolean variable indicating whether this cleanup has been
113 /// activated yet.
114 Address activeFlag = Address::invalid();
115
116 /// Cleanup scope op that represent the current scope in CIR
117 cir::CleanupScopeOp cleanupScopeOp;
118
119public:
120 /// Gets the size required for a lazy cleanup scope with the given
121 /// cleanup-data requirements.
122 static size_t getSizeForCleanupSize(size_t size) {
123 return sizeof(EHCleanupScope) + size;
124 }
125
126 size_t getAllocatedSize() const {
127 return sizeof(EHCleanupScope) + cleanupBits.cleanupSize;
128 }
129
130 EHCleanupScope(bool isNormal, bool isEH, unsigned cleanupSize,
131 cir::CleanupScopeOp cleanupScopeOp,
132 EHScopeStack::stable_iterator enclosingNormal,
134 : EHScope(EHScope::Cleanup, enclosingEH),
135 enclosingNormal(enclosingNormal), cleanupScopeOp(cleanupScopeOp) {
136 cleanupBits.isNormalCleanup = isNormal;
137 cleanupBits.isEHCleanup = isEH;
138 cleanupBits.isActive = true;
139 cleanupBits.isLifetimeMarker = false;
140 cleanupBits.testFlagInNormalCleanup = false;
141 cleanupBits.testFlagInEHCleanup = false;
142 cleanupBits.cleanupSize = cleanupSize;
143
144 assert(cleanupBits.cleanupSize == cleanupSize && "cleanup size overflow");
145 }
146
147 void destroy() {}
148 // Objects of EHCleanupScope are not destructed. Use destroy().
149 ~EHCleanupScope() = delete;
150
151 mlir::Block *getNormalBlock() const { return normalBlock; }
152 void setNormalBlock(mlir::Block *bb) { normalBlock = bb; }
153
154 bool isNormalCleanup() const { return cleanupBits.isNormalCleanup; }
155 bool isEHCleanup() const { return cleanupBits.isEHCleanup; }
156
157 bool isActive() const { return cleanupBits.isActive; }
158 void setActive(bool isActive) { cleanupBits.isActive = isActive; }
159
160 bool isLifetimeMarker() const { return cleanupBits.isLifetimeMarker; }
161 void setLifetimeMarker() { cleanupBits.isLifetimeMarker = true; }
162
163 bool hasActiveFlag() const { return activeFlag.isValid(); }
164 Address getActiveFlag() const { return activeFlag; }
165 void setActiveFlag(Address var) { activeFlag = var; }
166
167 void setTestFlagInNormalCleanup(bool value) {
168 cleanupBits.testFlagInNormalCleanup = value;
169 }
171 return cleanupBits.testFlagInNormalCleanup;
172 }
173
174 void setTestFlagInEHCleanup(bool value) {
175 cleanupBits.testFlagInEHCleanup = value;
176 }
178 return cleanupBits.testFlagInEHCleanup;
179 }
180
182 return enclosingNormal;
183 }
184
185 size_t getCleanupSize() const { return cleanupBits.cleanupSize; }
186 void *getCleanupBuffer() { return this + 1; }
187
189 return reinterpret_cast<EHScopeStack::Cleanup *>(getCleanupBuffer());
190 }
191
192 cir::CleanupScopeOp getCleanupScopeOp() { return cleanupScopeOp; }
193
194 static bool classof(const EHScope *scope) {
195 return (scope->getKind() == Cleanup);
196 }
197
198 void markEmitted() {}
199};
200
201/// A non-stable pointer into the scope stack.
203 char *ptr = nullptr;
204
205 friend class EHScopeStack;
206 explicit iterator(char *ptr) : ptr(ptr) {}
207
208public:
209 iterator() = default;
210
211 EHScope *get() const { return reinterpret_cast<EHScope *>(ptr); }
212
213 EHScope *operator->() const { return get(); }
214 EHScope &operator*() const { return *get(); }
215
216 iterator &operator++() {
217 size_t size;
218 switch (get()->getKind()) {
219 case EHScope::Filter:
220 llvm_unreachable("EHScopeStack::iterator Filter");
221 break;
222
223 case EHScope::Cleanup:
224 size = static_cast<const EHCleanupScope *>(get())->getAllocatedSize();
225 break;
226
228 llvm_unreachable("EHScopeStack::iterator Terminate");
229 break;
230 }
231 ptr += llvm::alignTo(size, ScopeStackAlignment);
232 return *this;
233 }
234
235 bool operator==(iterator other) const { return ptr == other.ptr; }
236 bool operator!=(iterator other) const { return ptr != other.ptr; }
237};
238
240 return iterator(startOfData);
241}
242
244 return iterator(endOfBuffer);
245}
246
249 assert(savePoint.isValid() && "finding invalid savepoint");
250 assert(savePoint.size <= stable_begin().size &&
251 "finding savepoint after pop");
252 return iterator(endOfBuffer - savePoint.size);
253}
254
256
257/// Selects the personality function to use for \p fd, or for the translation
258/// unit as a whole when \p fd is null.
260 const clang::FunctionDecl *fd);
261
262/// Selects the personality function to use for the function currently being
263/// emitted.
265
266} // namespace clang::CIRGen
267#endif // CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H
static Decl::Kind getKind(const Decl *D)
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
static Address invalid()
Definition Address.h:76
This class organizes the cross-function state that is used while generating CIR code.
A cleanup scope which generates the cleanup blocks lazily.
mlir::Block * getNormalBlock() const
EHScopeStack::Cleanup * getCleanup()
EHCleanupScope(bool isNormal, bool isEH, unsigned cleanupSize, cir::CleanupScopeOp cleanupScopeOp, EHScopeStack::stable_iterator enclosingNormal, EHScopeStack::stable_iterator enclosingEH)
void setTestFlagInEHCleanup(bool value)
void setTestFlagInNormalCleanup(bool value)
cir::CleanupScopeOp getCleanupScopeOp()
static bool classof(const EHScope *scope)
static size_t getSizeForCleanupSize(size_t size)
Gets the size required for a lazy cleanup scope with the given cleanup-data requirements.
void setActiveFlag(Address var)
EHScopeStack::stable_iterator getEnclosingNormalCleanup() const
bool shouldTestFlagInNormalCleanup() const
void setNormalBlock(mlir::Block *bb)
void setActive(bool isActive)
Information for lazily generating a cleanup.
A non-stable pointer into the scope stack.
bool operator!=(iterator other) const
bool operator==(iterator other) const
A saved depth on the scope 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.
iterator end() const
Returns an iterator pointing to the outermost EH scope.
iterator begin() const
Returns an iterator pointing to the innermost EH scope.
A protected scope for zero-cost EH handling.
CleanupBitFields cleanupBits
void setMayThrow(bool mayThrow)
EHScopeStack::stable_iterator getEnclosingEHScope() const
CommonBitFields commonBits
EHScope(Kind kind, EHScopeStack::stable_iterator enclosingEHScope)
Represents a function declaration or definition.
Definition Decl.h:2059
const EHPersonality & getEHPersonality(CIRGenModule &cgm, const clang::FunctionDecl *fd)
Selects the personality function to use for fd, or for the translation unit as a whole when fd is nul...
The MS C++ ABI needs a pointer to RTTI data plus some flags to describe the type of a catch handler,...
The exceptions personality for a function.