Path: blob/main/contrib/llvm-project/clang/lib/CodeGen/CGCleanup.h
35233 views
//===-- CGCleanup.h - Classes for cleanups IR generation --------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// These classes support the generation of LLVM IR for cleanups.9//10//===----------------------------------------------------------------------===//1112#ifndef LLVM_CLANG_LIB_CODEGEN_CGCLEANUP_H13#define LLVM_CLANG_LIB_CODEGEN_CGCLEANUP_H1415#include "EHScopeStack.h"1617#include "Address.h"18#include "llvm/ADT/STLExtras.h"19#include "llvm/ADT/SetVector.h"20#include "llvm/ADT/SmallPtrSet.h"21#include "llvm/ADT/SmallVector.h"22#include "llvm/IR/Instruction.h"2324namespace llvm {25class BasicBlock;26class Value;27class ConstantInt;28}2930namespace clang {31class FunctionDecl;32namespace CodeGen {33class CodeGenModule;34class CodeGenFunction;3536/// The MS C++ ABI needs a pointer to RTTI data plus some flags to describe the37/// type of a catch handler, so we use this wrapper.38struct CatchTypeInfo {39llvm::Constant *RTTI;40unsigned Flags;41};4243/// A protected scope for zero-cost EH handling.44class EHScope {45public:46enum Kind { Cleanup, Catch, Terminate, Filter };4748private:49llvm::BasicBlock *CachedLandingPad;50llvm::BasicBlock *CachedEHDispatchBlock;5152EHScopeStack::stable_iterator EnclosingEHScope;5354class CommonBitFields {55friend class EHScope;56LLVM_PREFERRED_TYPE(Kind)57unsigned Kind : 3;58};59enum { NumCommonBits = 3 };6061protected:62class CatchBitFields {63friend class EHCatchScope;64unsigned : NumCommonBits;6566unsigned NumHandlers : 32 - NumCommonBits;67};6869class CleanupBitFields {70friend class EHCleanupScope;71unsigned : NumCommonBits;7273/// Whether this cleanup needs to be run along normal edges.74LLVM_PREFERRED_TYPE(bool)75unsigned IsNormalCleanup : 1;7677/// Whether this cleanup needs to be run along exception edges.78LLVM_PREFERRED_TYPE(bool)79unsigned IsEHCleanup : 1;8081/// Whether this cleanup is currently active.82LLVM_PREFERRED_TYPE(bool)83unsigned IsActive : 1;8485/// Whether this cleanup is a lifetime marker86LLVM_PREFERRED_TYPE(bool)87unsigned IsLifetimeMarker : 1;8889/// Whether the normal cleanup should test the activation flag.90LLVM_PREFERRED_TYPE(bool)91unsigned TestFlagInNormalCleanup : 1;9293/// Whether the EH cleanup should test the activation flag.94LLVM_PREFERRED_TYPE(bool)95unsigned TestFlagInEHCleanup : 1;9697/// The amount of extra storage needed by the Cleanup.98/// Always a multiple of the scope-stack alignment.99unsigned CleanupSize : 12;100};101102class FilterBitFields {103friend class EHFilterScope;104unsigned : NumCommonBits;105106unsigned NumFilters : 32 - NumCommonBits;107};108109union {110CommonBitFields CommonBits;111CatchBitFields CatchBits;112CleanupBitFields CleanupBits;113FilterBitFields FilterBits;114};115116public:117EHScope(Kind kind, EHScopeStack::stable_iterator enclosingEHScope)118: CachedLandingPad(nullptr), CachedEHDispatchBlock(nullptr),119EnclosingEHScope(enclosingEHScope) {120CommonBits.Kind = kind;121}122123Kind getKind() const { return static_cast<Kind>(CommonBits.Kind); }124125llvm::BasicBlock *getCachedLandingPad() const {126return CachedLandingPad;127}128129void setCachedLandingPad(llvm::BasicBlock *block) {130CachedLandingPad = block;131}132133llvm::BasicBlock *getCachedEHDispatchBlock() const {134return CachedEHDispatchBlock;135}136137void setCachedEHDispatchBlock(llvm::BasicBlock *block) {138CachedEHDispatchBlock = block;139}140141bool hasEHBranches() const {142if (llvm::BasicBlock *block = getCachedEHDispatchBlock())143return !block->use_empty();144return false;145}146147EHScopeStack::stable_iterator getEnclosingEHScope() const {148return EnclosingEHScope;149}150};151152/// A scope which attempts to handle some, possibly all, types of153/// exceptions.154///155/// Objective C \@finally blocks are represented using a cleanup scope156/// after the catch scope.157class EHCatchScope : public EHScope {158// In effect, we have a flexible array member159// Handler Handlers[0];160// But that's only standard in C99, not C++, so we have to do161// annoying pointer arithmetic instead.162163public:164struct Handler {165/// A type info value, or null (C++ null, not an LLVM null pointer)166/// for a catch-all.167CatchTypeInfo Type;168169/// The catch handler for this type.170llvm::BasicBlock *Block;171172bool isCatchAll() const { return Type.RTTI == nullptr; }173};174175private:176friend class EHScopeStack;177178Handler *getHandlers() {179return reinterpret_cast<Handler*>(this+1);180}181182const Handler *getHandlers() const {183return reinterpret_cast<const Handler*>(this+1);184}185186public:187static size_t getSizeForNumHandlers(unsigned N) {188return sizeof(EHCatchScope) + N * sizeof(Handler);189}190191EHCatchScope(unsigned numHandlers,192EHScopeStack::stable_iterator enclosingEHScope)193: EHScope(Catch, enclosingEHScope) {194CatchBits.NumHandlers = numHandlers;195assert(CatchBits.NumHandlers == numHandlers && "NumHandlers overflow?");196}197198unsigned getNumHandlers() const {199return CatchBits.NumHandlers;200}201202void setCatchAllHandler(unsigned I, llvm::BasicBlock *Block) {203setHandler(I, CatchTypeInfo{nullptr, 0}, Block);204}205206void setHandler(unsigned I, llvm::Constant *Type, llvm::BasicBlock *Block) {207assert(I < getNumHandlers());208getHandlers()[I].Type = CatchTypeInfo{Type, 0};209getHandlers()[I].Block = Block;210}211212void setHandler(unsigned I, CatchTypeInfo Type, llvm::BasicBlock *Block) {213assert(I < getNumHandlers());214getHandlers()[I].Type = Type;215getHandlers()[I].Block = Block;216}217218const Handler &getHandler(unsigned I) const {219assert(I < getNumHandlers());220return getHandlers()[I];221}222223// Clear all handler blocks.224// FIXME: it's better to always call clearHandlerBlocks in DTOR and have a225// 'takeHandler' or some such function which removes ownership from the226// EHCatchScope object if the handlers should live longer than EHCatchScope.227void clearHandlerBlocks() {228for (unsigned I = 0, N = getNumHandlers(); I != N; ++I)229delete getHandler(I).Block;230}231232typedef const Handler *iterator;233iterator begin() const { return getHandlers(); }234iterator end() const { return getHandlers() + getNumHandlers(); }235236static bool classof(const EHScope *Scope) {237return Scope->getKind() == Catch;238}239};240241/// A cleanup scope which generates the cleanup blocks lazily.242class alignas(8) EHCleanupScope : public EHScope {243/// The nearest normal cleanup scope enclosing this one.244EHScopeStack::stable_iterator EnclosingNormal;245246/// The nearest EH scope enclosing this one.247EHScopeStack::stable_iterator EnclosingEH;248249/// The dual entry/exit block along the normal edge. This is lazily250/// created if needed before the cleanup is popped.251llvm::BasicBlock *NormalBlock;252253/// An optional i1 variable indicating whether this cleanup has been254/// activated yet.255Address ActiveFlag;256257/// Extra information required for cleanups that have resolved258/// branches through them. This has to be allocated on the side259/// because everything on the cleanup stack has be trivially260/// movable.261struct ExtInfo {262/// The destinations of normal branch-afters and branch-throughs.263llvm::SmallPtrSet<llvm::BasicBlock*, 4> Branches;264265/// Normal branch-afters.266SmallVector<std::pair<llvm::BasicBlock*,llvm::ConstantInt*>, 4>267BranchAfters;268};269mutable struct ExtInfo *ExtInfo;270271/// Erases auxillary allocas and their usages for an unused cleanup.272/// Cleanups should mark these allocas as 'used' if the cleanup is273/// emitted, otherwise these instructions would be erased.274struct AuxillaryAllocas {275SmallVector<llvm::Instruction *, 1> AuxAllocas;276bool used = false;277278// Records a potentially unused instruction to be erased later.279void Add(llvm::AllocaInst *Alloca) { AuxAllocas.push_back(Alloca); }280281// Mark all recorded instructions as used. These will not be erased later.282void MarkUsed() {283used = true;284AuxAllocas.clear();285}286287~AuxillaryAllocas() {288if (used)289return;290llvm::SetVector<llvm::Instruction *> Uses;291for (auto *Inst : llvm::reverse(AuxAllocas))292CollectUses(Inst, Uses);293// Delete uses in the reverse order of insertion.294for (auto *I : llvm::reverse(Uses))295I->eraseFromParent();296}297298private:299void CollectUses(llvm::Instruction *I,300llvm::SetVector<llvm::Instruction *> &Uses) {301if (!I || !Uses.insert(I))302return;303for (auto *User : I->users())304CollectUses(cast<llvm::Instruction>(User), Uses);305}306};307mutable struct AuxillaryAllocas *AuxAllocas;308309AuxillaryAllocas &getAuxillaryAllocas() {310if (!AuxAllocas) {311AuxAllocas = new struct AuxillaryAllocas();312}313return *AuxAllocas;314}315316/// The number of fixups required by enclosing scopes (not including317/// this one). If this is the top cleanup scope, all the fixups318/// from this index onwards belong to this scope.319unsigned FixupDepth;320321struct ExtInfo &getExtInfo() {322if (!ExtInfo) ExtInfo = new struct ExtInfo();323return *ExtInfo;324}325326const struct ExtInfo &getExtInfo() const {327if (!ExtInfo) ExtInfo = new struct ExtInfo();328return *ExtInfo;329}330331public:332/// Gets the size required for a lazy cleanup scope with the given333/// cleanup-data requirements.334static size_t getSizeForCleanupSize(size_t Size) {335return sizeof(EHCleanupScope) + Size;336}337338size_t getAllocatedSize() const {339return sizeof(EHCleanupScope) + CleanupBits.CleanupSize;340}341342EHCleanupScope(bool isNormal, bool isEH, unsigned cleanupSize,343unsigned fixupDepth,344EHScopeStack::stable_iterator enclosingNormal,345EHScopeStack::stable_iterator enclosingEH)346: EHScope(EHScope::Cleanup, enclosingEH),347EnclosingNormal(enclosingNormal), NormalBlock(nullptr),348ActiveFlag(Address::invalid()), ExtInfo(nullptr), AuxAllocas(nullptr),349FixupDepth(fixupDepth) {350CleanupBits.IsNormalCleanup = isNormal;351CleanupBits.IsEHCleanup = isEH;352CleanupBits.IsActive = true;353CleanupBits.IsLifetimeMarker = false;354CleanupBits.TestFlagInNormalCleanup = false;355CleanupBits.TestFlagInEHCleanup = false;356CleanupBits.CleanupSize = cleanupSize;357358assert(CleanupBits.CleanupSize == cleanupSize && "cleanup size overflow");359}360361void Destroy() {362if (AuxAllocas)363delete AuxAllocas;364delete ExtInfo;365}366void AddAuxAllocas(llvm::SmallVector<llvm::AllocaInst *> Allocas) {367for (auto *Alloca : Allocas)368getAuxillaryAllocas().Add(Alloca);369}370void MarkEmitted() { getAuxillaryAllocas().MarkUsed(); }371// Objects of EHCleanupScope are not destructed. Use Destroy().372~EHCleanupScope() = delete;373374bool isNormalCleanup() const { return CleanupBits.IsNormalCleanup; }375llvm::BasicBlock *getNormalBlock() const { return NormalBlock; }376void setNormalBlock(llvm::BasicBlock *BB) { NormalBlock = BB; }377378bool isEHCleanup() const { return CleanupBits.IsEHCleanup; }379380bool isActive() const { return CleanupBits.IsActive; }381void setActive(bool A) { CleanupBits.IsActive = A; }382383bool isLifetimeMarker() const { return CleanupBits.IsLifetimeMarker; }384void setLifetimeMarker() { CleanupBits.IsLifetimeMarker = true; }385386bool hasActiveFlag() const { return ActiveFlag.isValid(); }387Address getActiveFlag() const {388return ActiveFlag;389}390void setActiveFlag(RawAddress Var) {391assert(Var.getAlignment().isOne());392ActiveFlag = Var;393}394395void setTestFlagInNormalCleanup() {396CleanupBits.TestFlagInNormalCleanup = true;397}398bool shouldTestFlagInNormalCleanup() const {399return CleanupBits.TestFlagInNormalCleanup;400}401402void setTestFlagInEHCleanup() {403CleanupBits.TestFlagInEHCleanup = true;404}405bool shouldTestFlagInEHCleanup() const {406return CleanupBits.TestFlagInEHCleanup;407}408409unsigned getFixupDepth() const { return FixupDepth; }410EHScopeStack::stable_iterator getEnclosingNormalCleanup() const {411return EnclosingNormal;412}413414size_t getCleanupSize() const { return CleanupBits.CleanupSize; }415void *getCleanupBuffer() { return this + 1; }416417EHScopeStack::Cleanup *getCleanup() {418return reinterpret_cast<EHScopeStack::Cleanup*>(getCleanupBuffer());419}420421/// True if this cleanup scope has any branch-afters or branch-throughs.422bool hasBranches() const { return ExtInfo && !ExtInfo->Branches.empty(); }423424/// Add a branch-after to this cleanup scope. A branch-after is a425/// branch from a point protected by this (normal) cleanup to a426/// point in the normal cleanup scope immediately containing it.427/// For example,428/// for (;;) { A a; break; }429/// contains a branch-after.430///431/// Branch-afters each have their own destination out of the432/// cleanup, guaranteed distinct from anything else threaded through433/// it. Therefore branch-afters usually force a switch after the434/// cleanup.435void addBranchAfter(llvm::ConstantInt *Index,436llvm::BasicBlock *Block) {437struct ExtInfo &ExtInfo = getExtInfo();438if (ExtInfo.Branches.insert(Block).second)439ExtInfo.BranchAfters.push_back(std::make_pair(Block, Index));440}441442/// Return the number of unique branch-afters on this scope.443unsigned getNumBranchAfters() const {444return ExtInfo ? ExtInfo->BranchAfters.size() : 0;445}446447llvm::BasicBlock *getBranchAfterBlock(unsigned I) const {448assert(I < getNumBranchAfters());449return ExtInfo->BranchAfters[I].first;450}451452llvm::ConstantInt *getBranchAfterIndex(unsigned I) const {453assert(I < getNumBranchAfters());454return ExtInfo->BranchAfters[I].second;455}456457/// Add a branch-through to this cleanup scope. A branch-through is458/// a branch from a scope protected by this (normal) cleanup to an459/// enclosing scope other than the immediately-enclosing normal460/// cleanup scope.461///462/// In the following example, the branch through B's scope is a463/// branch-through, while the branch through A's scope is a464/// branch-after:465/// for (;;) { A a; B b; break; }466///467/// All branch-throughs have a common destination out of the468/// cleanup, one possibly shared with the fall-through. Therefore469/// branch-throughs usually don't force a switch after the cleanup.470///471/// \return true if the branch-through was new to this scope472bool addBranchThrough(llvm::BasicBlock *Block) {473return getExtInfo().Branches.insert(Block).second;474}475476/// Determines if this cleanup scope has any branch throughs.477bool hasBranchThroughs() const {478if (!ExtInfo) return false;479return (ExtInfo->BranchAfters.size() != ExtInfo->Branches.size());480}481482static bool classof(const EHScope *Scope) {483return (Scope->getKind() == Cleanup);484}485};486// NOTE: there's a bunch of different data classes tacked on after an487// EHCleanupScope. It is asserted (in EHScopeStack::pushCleanup*) that488// they don't require greater alignment than ScopeStackAlignment. So,489// EHCleanupScope ought to have alignment equal to that -- not more490// (would be misaligned by the stack allocator), and not less (would491// break the appended classes).492static_assert(alignof(EHCleanupScope) == EHScopeStack::ScopeStackAlignment,493"EHCleanupScope expected alignment");494495/// An exceptions scope which filters exceptions thrown through it.496/// Only exceptions matching the filter types will be permitted to be497/// thrown.498///499/// This is used to implement C++ exception specifications.500class EHFilterScope : public EHScope {501// Essentially ends in a flexible array member:502// llvm::Value *FilterTypes[0];503504llvm::Value **getFilters() {505return reinterpret_cast<llvm::Value**>(this+1);506}507508llvm::Value * const *getFilters() const {509return reinterpret_cast<llvm::Value* const *>(this+1);510}511512public:513EHFilterScope(unsigned numFilters)514: EHScope(Filter, EHScopeStack::stable_end()) {515FilterBits.NumFilters = numFilters;516assert(FilterBits.NumFilters == numFilters && "NumFilters overflow");517}518519static size_t getSizeForNumFilters(unsigned numFilters) {520return sizeof(EHFilterScope) + numFilters * sizeof(llvm::Value*);521}522523unsigned getNumFilters() const { return FilterBits.NumFilters; }524525void setFilter(unsigned i, llvm::Value *filterValue) {526assert(i < getNumFilters());527getFilters()[i] = filterValue;528}529530llvm::Value *getFilter(unsigned i) const {531assert(i < getNumFilters());532return getFilters()[i];533}534535static bool classof(const EHScope *scope) {536return scope->getKind() == Filter;537}538};539540/// An exceptions scope which calls std::terminate if any exception541/// reaches it.542class EHTerminateScope : public EHScope {543public:544EHTerminateScope(EHScopeStack::stable_iterator enclosingEHScope)545: EHScope(Terminate, enclosingEHScope) {}546static size_t getSize() { return sizeof(EHTerminateScope); }547548static bool classof(const EHScope *scope) {549return scope->getKind() == Terminate;550}551};552553/// A non-stable pointer into the scope stack.554class EHScopeStack::iterator {555char *Ptr;556557friend class EHScopeStack;558explicit iterator(char *Ptr) : Ptr(Ptr) {}559560public:561iterator() : Ptr(nullptr) {}562563EHScope *get() const {564return reinterpret_cast<EHScope*>(Ptr);565}566567EHScope *operator->() const { return get(); }568EHScope &operator*() const { return *get(); }569570iterator &operator++() {571size_t Size;572switch (get()->getKind()) {573case EHScope::Catch:574Size = EHCatchScope::getSizeForNumHandlers(575static_cast<const EHCatchScope *>(get())->getNumHandlers());576break;577578case EHScope::Filter:579Size = EHFilterScope::getSizeForNumFilters(580static_cast<const EHFilterScope *>(get())->getNumFilters());581break;582583case EHScope::Cleanup:584Size = static_cast<const EHCleanupScope *>(get())->getAllocatedSize();585break;586587case EHScope::Terminate:588Size = EHTerminateScope::getSize();589break;590}591Ptr += llvm::alignTo(Size, ScopeStackAlignment);592return *this;593}594595iterator next() {596iterator copy = *this;597++copy;598return copy;599}600601iterator operator++(int) {602iterator copy = *this;603operator++();604return copy;605}606607bool encloses(iterator other) const { return Ptr >= other.Ptr; }608bool strictlyEncloses(iterator other) const { return Ptr > other.Ptr; }609610bool operator==(iterator other) const { return Ptr == other.Ptr; }611bool operator!=(iterator other) const { return Ptr != other.Ptr; }612};613614inline EHScopeStack::iterator EHScopeStack::begin() const {615return iterator(StartOfData);616}617618inline EHScopeStack::iterator EHScopeStack::end() const {619return iterator(EndOfBuffer);620}621622inline void EHScopeStack::popCatch() {623assert(!empty() && "popping exception stack when not empty");624625EHCatchScope &scope = cast<EHCatchScope>(*begin());626InnermostEHScope = scope.getEnclosingEHScope();627deallocate(EHCatchScope::getSizeForNumHandlers(scope.getNumHandlers()));628}629630inline void EHScopeStack::popTerminate() {631assert(!empty() && "popping exception stack when not empty");632633EHTerminateScope &scope = cast<EHTerminateScope>(*begin());634InnermostEHScope = scope.getEnclosingEHScope();635deallocate(EHTerminateScope::getSize());636}637638inline EHScopeStack::iterator EHScopeStack::find(stable_iterator sp) const {639assert(sp.isValid() && "finding invalid savepoint");640assert(sp.Size <= stable_begin().Size && "finding savepoint after pop");641return iterator(EndOfBuffer - sp.Size);642}643644inline EHScopeStack::stable_iterator645EHScopeStack::stabilize(iterator ir) const {646assert(StartOfData <= ir.Ptr && ir.Ptr <= EndOfBuffer);647return stable_iterator(EndOfBuffer - ir.Ptr);648}649650/// The exceptions personality for a function.651struct EHPersonality {652const char *PersonalityFn;653654// If this is non-null, this personality requires a non-standard655// function for rethrowing an exception after a catchall cleanup.656// This function must have prototype void(void*).657const char *CatchallRethrowFn;658659static const EHPersonality &get(CodeGenModule &CGM, const FunctionDecl *FD);660static const EHPersonality &get(CodeGenFunction &CGF);661662static const EHPersonality GNU_C;663static const EHPersonality GNU_C_SJLJ;664static const EHPersonality GNU_C_SEH;665static const EHPersonality GNU_ObjC;666static const EHPersonality GNU_ObjC_SJLJ;667static const EHPersonality GNU_ObjC_SEH;668static const EHPersonality GNUstep_ObjC;669static const EHPersonality GNU_ObjCXX;670static const EHPersonality NeXT_ObjC;671static const EHPersonality GNU_CPlusPlus;672static const EHPersonality GNU_CPlusPlus_SJLJ;673static const EHPersonality GNU_CPlusPlus_SEH;674static const EHPersonality MSVC_except_handler;675static const EHPersonality MSVC_C_specific_handler;676static const EHPersonality MSVC_CxxFrameHandler3;677static const EHPersonality GNU_Wasm_CPlusPlus;678static const EHPersonality XL_CPlusPlus;679static const EHPersonality ZOS_CPlusPlus;680681/// Does this personality use landingpads or the family of pad instructions682/// designed to form funclets?683bool usesFuncletPads() const {684return isMSVCPersonality() || isWasmPersonality();685}686687bool isMSVCPersonality() const {688return this == &MSVC_except_handler || this == &MSVC_C_specific_handler ||689this == &MSVC_CxxFrameHandler3;690}691692bool isWasmPersonality() const { return this == &GNU_Wasm_CPlusPlus; }693694bool isMSVCXXPersonality() const { return this == &MSVC_CxxFrameHandler3; }695};696}697}698699#endif700701702