382 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8//9// These classes support the generation of CIR for cleanups, initially based10// on LLVM IR cleanup handling, but ought to change as CIR evolves.11//12//===----------------------------------------------------------------------===//13 14#ifndef CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H15#define CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H16 17#include "Address.h"18#include "CIRGenModule.h"19#include "EHScopeStack.h"20#include "mlir/IR/Value.h"21#include "clang/AST/StmtCXX.h"22 23namespace clang::CIRGen {24 25/// The MS C++ ABI needs a pointer to RTTI data plus some flags to describe the26/// type of a catch handler, so we use this wrapper.27struct CatchTypeInfo {28 mlir::TypedAttr rtti;29 unsigned flags;30};31 32/// A protected scope for zero-cost EH handling.33class EHScope {34 EHScopeStack::stable_iterator enclosingEHScope;35 36 class CommonBitFields {37 friend class EHScope;38 unsigned kind : 3;39 };40 enum { NumCommonBits = 3 };41 42 bool scopeMayThrow;43 44protected:45 class CatchBitFields {46 friend class EHCatchScope;47 unsigned : NumCommonBits;48 unsigned numHandlers : 32 - NumCommonBits;49 };50 51 class CleanupBitFields {52 friend class EHCleanupScope;53 unsigned : NumCommonBits;54 55 /// Whether this cleanup needs to be run along normal edges.56 unsigned isNormalCleanup : 1;57 58 /// Whether this cleanup needs to be run along exception edges.59 unsigned isEHCleanup : 1;60 61 /// Whether this cleanup is currently active.62 unsigned isActive : 1;63 64 /// Whether this cleanup is a lifetime marker65 unsigned isLifetimeMarker : 1;66 67 /// Whether the normal cleanup should test the activation flag.68 unsigned testFlagInNormalCleanup : 1;69 70 /// Whether the EH cleanup should test the activation flag.71 unsigned testFlagInEHCleanup : 1;72 73 /// The amount of extra storage needed by the Cleanup.74 /// Always a multiple of the scope-stack alignment.75 unsigned cleanupSize : 12;76 };77 78 union {79 CommonBitFields commonBits;80 CatchBitFields catchBits;81 CleanupBitFields cleanupBits;82 };83 84public:85 enum Kind { Cleanup, Catch, Terminate, Filter };86 87 EHScope(Kind kind, EHScopeStack::stable_iterator enclosingEHScope)88 : enclosingEHScope(enclosingEHScope) {89 commonBits.kind = kind;90 }91 92 Kind getKind() const { return static_cast<Kind>(commonBits.kind); }93 94 bool mayThrow() const {95 // Traditional LLVM codegen also checks for `!block->use_empty()`, but96 // in CIRGen the block content is not important, just used as a way to97 // signal `hasEHBranches`.98 return scopeMayThrow;99 }100 101 void setMayThrow(bool mayThrow) { scopeMayThrow = mayThrow; }102 103 EHScopeStack::stable_iterator getEnclosingEHScope() const {104 return enclosingEHScope;105 }106};107 108/// A scope which attempts to handle some, possibly all, types of109/// exceptions.110///111/// Objective C \@finally blocks are represented using a cleanup scope112/// after the catch scope.113 114class EHCatchScope : public EHScope {115 // In effect, we have a flexible array member116 // Handler Handlers[0];117 // But that's only standard in C99, not C++, so we have to do118 // annoying pointer arithmetic instead.119 120public:121 struct Handler {122 /// A type info value, or null MLIR attribute for a catch-all123 CatchTypeInfo type;124 125 /// The catch handler for this type.126 mlir::Region *region;127 128 /// The catch handler stmt.129 const CXXCatchStmt *stmt;130 131 bool isCatchAll() const { return type.rtti == nullptr; }132 };133 134private:135 friend class EHScopeStack;136 137 Handler *getHandlers() { return reinterpret_cast<Handler *>(this + 1); }138 139 const Handler *getHandlers() const {140 return reinterpret_cast<const Handler *>(this + 1);141 }142 143public:144 static size_t getSizeForNumHandlers(unsigned n) {145 return sizeof(EHCatchScope) + n * sizeof(Handler);146 }147 148 EHCatchScope(unsigned numHandlers,149 EHScopeStack::stable_iterator enclosingEHScope)150 : EHScope(Catch, enclosingEHScope) {151 catchBits.numHandlers = numHandlers;152 assert(catchBits.numHandlers == numHandlers && "NumHandlers overflow?");153 }154 155 unsigned getNumHandlers() const { return catchBits.numHandlers; }156 157 void setHandler(unsigned i, CatchTypeInfo type, mlir::Region *region,158 const CXXCatchStmt *stmt) {159 assert(i < getNumHandlers());160 Handler *handler = &getHandlers()[i];161 handler->type = type;162 handler->region = region;163 handler->stmt = stmt;164 }165 166 const Handler &getHandler(unsigned i) const {167 assert(i < getNumHandlers());168 return getHandlers()[i];169 }170 171 // Clear all handler blocks.172 // FIXME: it's better to always call clearHandlerBlocks in DTOR and have a173 // 'takeHandler' or some such function which removes ownership from the174 // EHCatchScope object if the handlers should live longer than EHCatchScope.175 void clearHandlerBlocks() {176 // The blocks are owned by TryOp, nothing to delete.177 }178 179 using iterator = const Handler *;180 iterator begin() const { return getHandlers(); }181 iterator end() const { return getHandlers() + getNumHandlers(); }182 183 static bool classof(const EHScope *scope) {184 return scope->getKind() == Catch;185 }186};187 188/// A cleanup scope which generates the cleanup blocks lazily.189class alignas(EHScopeStack::ScopeStackAlignment) EHCleanupScope190 : public EHScope {191 /// The nearest normal cleanup scope enclosing this one.192 EHScopeStack::stable_iterator enclosingNormal;193 194 /// The dual entry/exit block along the normal edge. This is lazily195 /// created if needed before the cleanup is popped.196 mlir::Block *normalBlock = nullptr;197 198 /// The number of fixups required by enclosing scopes (not including199 /// this one). If this is the top cleanup scope, all the fixups200 /// from this index onwards belong to this scope.201 unsigned fixupDepth = 0;202 203public:204 /// Gets the size required for a lazy cleanup scope with the given205 /// cleanup-data requirements.206 static size_t getSizeForCleanupSize(size_t size) {207 return sizeof(EHCleanupScope) + size;208 }209 210 size_t getAllocatedSize() const {211 return sizeof(EHCleanupScope) + cleanupBits.cleanupSize;212 }213 214 EHCleanupScope(unsigned cleanupSize, unsigned fixupDepth,215 EHScopeStack::stable_iterator enclosingNormal,216 EHScopeStack::stable_iterator enclosingEH)217 : EHScope(EHScope::Cleanup, enclosingEH),218 enclosingNormal(enclosingNormal), fixupDepth(fixupDepth) {219 // TODO(cir): When exception handling is upstreamed, isNormalCleanup and220 // isEHCleanup will be arguments to the constructor.221 cleanupBits.isNormalCleanup = true;222 cleanupBits.isEHCleanup = false;223 cleanupBits.isActive = true;224 cleanupBits.isLifetimeMarker = false;225 cleanupBits.testFlagInNormalCleanup = false;226 cleanupBits.testFlagInEHCleanup = false;227 cleanupBits.cleanupSize = cleanupSize;228 229 assert(cleanupBits.cleanupSize == cleanupSize && "cleanup size overflow");230 }231 232 void destroy() {}233 // Objects of EHCleanupScope are not destructed. Use destroy().234 ~EHCleanupScope() = delete;235 236 mlir::Block *getNormalBlock() const { return normalBlock; }237 void setNormalBlock(mlir::Block *bb) { normalBlock = bb; }238 239 bool isNormalCleanup() const { return cleanupBits.isNormalCleanup; }240 241 bool isActive() const { return cleanupBits.isActive; }242 void setActive(bool isActive) { cleanupBits.isActive = isActive; }243 244 bool isLifetimeMarker() const { return cleanupBits.isLifetimeMarker; }245 246 unsigned getFixupDepth() const { return fixupDepth; }247 EHScopeStack::stable_iterator getEnclosingNormalCleanup() const {248 return enclosingNormal;249 }250 251 size_t getCleanupSize() const { return cleanupBits.cleanupSize; }252 void *getCleanupBuffer() { return this + 1; }253 254 EHScopeStack::Cleanup *getCleanup() {255 return reinterpret_cast<EHScopeStack::Cleanup *>(getCleanupBuffer());256 }257 258 static bool classof(const EHScope *scope) {259 return (scope->getKind() == Cleanup);260 }261 262 void markEmitted() {}263};264 265/// A non-stable pointer into the scope stack.266class EHScopeStack::iterator {267 char *ptr = nullptr;268 269 friend class EHScopeStack;270 explicit iterator(char *ptr) : ptr(ptr) {}271 272public:273 iterator() = default;274 275 EHScope *get() const { return reinterpret_cast<EHScope *>(ptr); }276 277 EHScope *operator->() const { return get(); }278 EHScope &operator*() const { return *get(); }279 280 iterator &operator++() {281 size_t size;282 switch (get()->getKind()) {283 case EHScope::Catch:284 size = EHCatchScope::getSizeForNumHandlers(285 static_cast<const EHCatchScope *>(get())->getNumHandlers());286 break;287 288 case EHScope::Filter:289 llvm_unreachable("EHScopeStack::iterator Filter");290 break;291 292 case EHScope::Cleanup:293 llvm_unreachable("EHScopeStack::iterator Cleanup");294 break;295 296 case EHScope::Terminate:297 llvm_unreachable("EHScopeStack::iterator Terminate");298 break;299 }300 ptr += llvm::alignTo(size, ScopeStackAlignment);301 return *this;302 }303 304 bool operator==(iterator other) const { return ptr == other.ptr; }305 bool operator!=(iterator other) const { return ptr != other.ptr; }306};307 308inline EHScopeStack::iterator EHScopeStack::begin() const {309 return iterator(startOfData);310}311 312inline EHScopeStack::iterator EHScopeStack::end() const {313 return iterator(endOfBuffer);314}315 316inline EHScopeStack::iterator317EHScopeStack::find(stable_iterator savePoint) const {318 assert(savePoint.isValid() && "finding invalid savepoint");319 assert(savePoint.size <= stable_begin().size &&320 "finding savepoint after pop");321 return iterator(endOfBuffer - savePoint.size);322}323 324inline void EHScopeStack::popCatch() {325 assert(!empty() && "popping exception stack when not empty");326 327 EHCatchScope &scope = llvm::cast<EHCatchScope>(*begin());328 innermostEHScope = scope.getEnclosingEHScope();329 deallocate(EHCatchScope::getSizeForNumHandlers(scope.getNumHandlers()));330}331 332/// The exceptions personality for a function.333struct EHPersonality {334 const char *personalityFn = nullptr;335 336 // If this is non-null, this personality requires a non-standard337 // function for rethrowing an exception after a catchall cleanup.338 // This function must have prototype void(void*).339 const char *catchallRethrowFn = nullptr;340 341 static const EHPersonality &get(CIRGenModule &cgm,342 const clang::FunctionDecl *fd);343 static const EHPersonality &get(CIRGenFunction &cgf);344 345 static const EHPersonality GNU_C;346 static const EHPersonality GNU_C_SJLJ;347 static const EHPersonality GNU_C_SEH;348 static const EHPersonality GNU_ObjC;349 static const EHPersonality GNU_ObjC_SJLJ;350 static const EHPersonality GNU_ObjC_SEH;351 static const EHPersonality GNUstep_ObjC;352 static const EHPersonality GNU_ObjCXX;353 static const EHPersonality NeXT_ObjC;354 static const EHPersonality GNU_CPlusPlus;355 static const EHPersonality GNU_CPlusPlus_SJLJ;356 static const EHPersonality GNU_CPlusPlus_SEH;357 static const EHPersonality MSVC_except_handler;358 static const EHPersonality MSVC_C_specific_handler;359 static const EHPersonality MSVC_CxxFrameHandler3;360 static const EHPersonality GNU_Wasm_CPlusPlus;361 static const EHPersonality XL_CPlusPlus;362 static const EHPersonality ZOS_CPlusPlus;363 364 /// Does this personality use landingpads or the family of pad instructions365 /// designed to form funclets?366 bool usesFuncletPads() const {367 return isMSVCPersonality() || isWasmPersonality();368 }369 370 bool isMSVCPersonality() const {371 return this == &MSVC_except_handler || this == &MSVC_C_specific_handler ||372 this == &MSVC_CxxFrameHandler3;373 }374 375 bool isWasmPersonality() const { return this == &GNU_Wasm_CPlusPlus; }376 377 bool isMSVCXXPersonality() const { return this == &MSVC_CxxFrameHandler3; }378};379 380} // namespace clang::CIRGen381#endif // CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H382