diff options
Diffstat (limited to 'clang/lib/CodeGen/CGBlocks.cpp')
-rw-r--r-- | clang/lib/CodeGen/CGBlocks.cpp | 254 |
1 files changed, 198 insertions, 56 deletions
diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index 5d03477e9bd..b1dbb505b5d 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "CGBlocks.h" +#include "CGCXXABI.h" #include "CGDebugInfo.h" #include "CGObjCRuntime.h" #include "CGOpenCLRuntime.h" @@ -25,6 +26,7 @@ #include "llvm/IR/CallSite.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Module.h" +#include "llvm/Support/ScopedPrinter.h" #include <algorithm> #include <cstdio> @@ -34,8 +36,8 @@ using namespace CodeGen; CGBlockInfo::CGBlockInfo(const BlockDecl *block, StringRef name) : Name(name), CXXThisIndex(0), CanBeGlobal(false), NeedsCopyDispose(false), HasCXXObject(false), UsesStret(false), HasCapturedVariableLayout(false), - LocalAddress(Address::invalid()), StructureType(nullptr), Block(block), - DominatingIP(nullptr) { + CapturesNonExternalType(false), LocalAddress(Address::invalid()), + StructureType(nullptr), Block(block), DominatingIP(nullptr) { // Skip asm prefix, if any. 'name' is usually taken directly from // the mangled name of the enclosing function. @@ -464,6 +466,8 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF, } else if (CI.hasCopyExpr()) { info.NeedsCopyDispose = true; info.HasCXXObject = true; + if (!variable->getType()->getAsCXXRecordDecl()->isExternallyVisible()) + info.CapturesNonExternalType = true; // So do C structs that require non-trivial copy construction or // destruction. @@ -480,6 +484,8 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF, if (!record->hasTrivialDestructor()) { info.HasCXXObject = true; info.NeedsCopyDispose = true; + if (!record->isExternallyVisible()) + info.CapturesNonExternalType = true; } } } @@ -1522,13 +1528,17 @@ enum class BlockCaptureEntityKind { struct BlockCaptureManagedEntity { BlockCaptureEntityKind Kind; BlockFieldFlags Flags; - const BlockDecl::Capture &CI; - const CGBlockInfo::Capture &Capture; + const BlockDecl::Capture *CI; + const CGBlockInfo::Capture *Capture; BlockCaptureManagedEntity(BlockCaptureEntityKind Type, BlockFieldFlags Flags, const BlockDecl::Capture &CI, const CGBlockInfo::Capture &Capture) - : Kind(Type), Flags(Flags), CI(CI), Capture(Capture) {} + : Kind(Type), Flags(Flags), CI(&CI), Capture(&Capture) {} + + bool operator<(const BlockCaptureManagedEntity &Other) const { + return Capture->getOffset() < Other.Capture->getOffset(); + } }; } // end anonymous namespace @@ -1607,6 +1617,9 @@ static void findBlockCapturedManagedEntities( if (Info.first != BlockCaptureEntityKind::None) ManagedCaptures.emplace_back(Info.first, Info.second, CI, Capture); } + + // Sort the captures by offset. + llvm::sort(ManagedCaptures.begin(), ManagedCaptures.end()); } namespace { @@ -1614,10 +1627,12 @@ namespace { struct CallBlockRelease final : EHScopeStack::Cleanup { Address Addr; BlockFieldFlags FieldFlags; - bool LoadBlockVarAddr; + bool LoadBlockVarAddr, CanThrow; - CallBlockRelease(Address Addr, BlockFieldFlags Flags, bool LoadValue) - : Addr(Addr), FieldFlags(Flags), LoadBlockVarAddr(LoadValue) {} + CallBlockRelease(Address Addr, BlockFieldFlags Flags, bool LoadValue, + bool CT) + : Addr(Addr), FieldFlags(Flags), LoadBlockVarAddr(LoadValue), + CanThrow(CT) {} void Emit(CodeGenFunction &CGF, Flags flags) override { llvm::Value *BlockVarAddr; @@ -1628,15 +1643,112 @@ struct CallBlockRelease final : EHScopeStack::Cleanup { BlockVarAddr = Addr.getPointer(); } - CGF.BuildBlockRelease(BlockVarAddr, FieldFlags); + CGF.BuildBlockRelease(BlockVarAddr, FieldFlags, CanThrow); } }; } // end anonymous namespace +/// Check if \p T is a C++ class that has a destructor that can throw. +bool CodeGenFunction::cxxDestructorCanThrow(QualType T) { + if (const auto *RD = T->getAsCXXRecordDecl()) + if (const CXXDestructorDecl *DD = RD->getDestructor()) + return DD->getType()->getAs<FunctionProtoType>()->canThrow(); + return false; +} + +static std::string getCopyDestroyHelperFuncName( + const SmallVectorImpl<BlockCaptureManagedEntity> &Captures, + CharUnits BlockAlignment, bool IsCopyHelper, CodeGenModule &CGM) { + ASTContext &Ctx = CGM.getContext(); + std::unique_ptr<ItaniumMangleContext> MC( + ItaniumMangleContext::create(Ctx, Ctx.getDiagnostics())); + + std::string Name = + IsCopyHelper ? "__copy_helper_block_" : "__destroy_helper_block_"; + if (CGM.getLangOpts().Exceptions) + Name += "e"; + if (CGM.getCodeGenOpts().ObjCAutoRefCountExceptions) + Name += "a"; + Name += llvm::to_string(BlockAlignment.getQuantity()) + "_"; + + for (const BlockCaptureManagedEntity &E : Captures) { + const BlockDecl::Capture &CI = *E.CI; + BlockFieldFlags Flags = E.Flags; + QualType CaptureTy = CI.getVariable()->getType(); + Name += llvm::to_string(E.Capture->getOffset().getQuantity()); + + switch (E.Kind) { + case BlockCaptureEntityKind::CXXRecord: { + Name += "c"; + SmallString<256> Str; + llvm::raw_svector_ostream Out(Str); + MC->mangleTypeName(CaptureTy, Out); + Name += llvm::to_string(Str.size()) + Str.c_str(); + break; + } + case BlockCaptureEntityKind::ARCWeak: + Name += "w"; + break; + case BlockCaptureEntityKind::ARCStrong: + Name += "s"; + break; + case BlockCaptureEntityKind::BlockObject: { + const VarDecl *Var = CI.getVariable(); + unsigned F = Flags.getBitMask(); + if (F & BLOCK_FIELD_IS_BYREF) { + Name += "r"; + if (F & BLOCK_FIELD_IS_WEAK) + Name += "w"; + else { + if (IsCopyHelper) { + if (Ctx.getBlockVarCopyInit(Var).canThrow()) + Name += "c"; + } else { + if (CodeGenFunction::cxxDestructorCanThrow(CaptureTy)) + Name += "d"; + } + } + } else { + assert((F & BLOCK_FIELD_IS_OBJECT) && "unexpected flag value"); + if (F == BLOCK_FIELD_IS_BLOCK) + Name += "b"; + else + Name += "o"; + } + break; + } + case BlockCaptureEntityKind::NonTrivialCStruct: { + bool IsVolatile = CaptureTy.isVolatileQualified(); + CharUnits Alignment = + BlockAlignment.alignmentAtOffset(E.Capture->getOffset()); + + Name += "n"; + std::string Str; + if (IsCopyHelper) + Str = CodeGenFunction::getNonTrivialCopyConstructorStr( + CaptureTy, Alignment, IsVolatile, Ctx); + else + Str = CodeGenFunction::getNonTrivialDestructorStr(CaptureTy, Alignment, + IsVolatile, Ctx); + // The underscore is necessary here because non-trivial copy constructor + // and destructor strings can start with a number. + Name += llvm::to_string(Str.size()) + "_" + Str; + break; + } + case BlockCaptureEntityKind::None: + llvm_unreachable("unexpected block capture kind"); + } + } + + return Name; +} + static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind, Address Field, QualType CaptureType, - BlockFieldFlags Flags, bool EHOnly, - CodeGenFunction &CGF) { + BlockFieldFlags Flags, bool ForCopyHelper, + VarDecl *Var, CodeGenFunction &CGF) { + bool EHOnly = ForCopyHelper; + switch (CaptureKind) { case BlockCaptureEntityKind::CXXRecord: case BlockCaptureEntityKind::ARCWeak: @@ -1658,7 +1770,13 @@ static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind, case BlockCaptureEntityKind::BlockObject: { if (!EHOnly || CGF.getLangOpts().Exceptions) { CleanupKind Kind = EHOnly ? EHCleanup : NormalAndEHCleanup; - CGF.enterByrefCleanup(Kind, Field, Flags, /*LoadBlockVarAddr*/ true); + // Calls to _Block_object_dispose along the EH path in the copy helper + // function don't throw as newly-copied __block variables always have a + // reference count of 2. + bool CanThrow = + !ForCopyHelper && CGF.cxxDestructorCanThrow(CaptureType); + CGF.enterByrefCleanup(Kind, Field, Flags, /*LoadBlockVarAddr*/ true, + CanThrow); } break; } @@ -1667,6 +1785,19 @@ static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind, } } +static void setBlockHelperAttributesVisibility(bool CapturesNonExternalType, + llvm::Function *Fn, + const CGFunctionInfo &FI, + CodeGenModule &CGM) { + if (CapturesNonExternalType) { + CGM.SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); + } else { + Fn->setVisibility(llvm::GlobalValue::HiddenVisibility); + Fn->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); + CGM.SetLLVMFunctionAttributes(nullptr, FI, Fn); + CGM.SetLLVMFunctionAttributesForDefinition(nullptr, Fn); + } +} /// Generate the copy-helper function for a block closure object: /// static void block_copy_helper(block_t *dst, block_t *src); /// The runtime will have previously initialized 'dst' by doing a @@ -1677,6 +1808,16 @@ static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind, /// the contents of an individual __block variable to the heap. llvm::Constant * CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { + SmallVector<BlockCaptureManagedEntity, 4> CopiedCaptures; + findBlockCapturedManagedEntities(blockInfo, getLangOpts(), CopiedCaptures, + computeCopyInfoForBlockCapture); + std::string FuncName = + getCopyDestroyHelperFuncName(CopiedCaptures, blockInfo.BlockAlign, + /*IsCopyHelper*/ true, CGM); + + if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName)) + return Func; + ASTContext &C = getContext(); FunctionArgList args; @@ -1695,11 +1836,11 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI); llvm::Function *Fn = - llvm::Function::Create(LTy, llvm::GlobalValue::InternalLinkage, - "__copy_helper_block_", &CGM.getModule()); + llvm::Function::Create(LTy, llvm::GlobalValue::LinkOnceODRLinkage, + FuncName, &CGM.getModule()); IdentifierInfo *II - = &CGM.getContext().Idents.get("__copy_helper_block_"); + = &CGM.getContext().Idents.get(FuncName); FunctionDecl *FD = FunctionDecl::Create(C, C.getTranslationUnitDecl(), @@ -1709,8 +1850,8 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { false, false); - CGM.SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); - + setBlockHelperAttributesVisibility(blockInfo.CapturesNonExternalType, Fn, FI, + CGM); StartFunction(FD, C.VoidTy, Fn, FI, args); ApplyDebugLocation NL{*this, blockInfo.getBlockExpr()->getBeginLoc()}; llvm::Type *structPtrTy = blockInfo.StructureType->getPointerTo(); @@ -1723,13 +1864,9 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { dst = Address(Builder.CreateLoad(dst), blockInfo.BlockAlign); dst = Builder.CreateBitCast(dst, structPtrTy, "block.dest"); - SmallVector<BlockCaptureManagedEntity, 4> CopiedCaptures; - findBlockCapturedManagedEntities(blockInfo, getLangOpts(), CopiedCaptures, - computeCopyInfoForBlockCapture); - for (const auto &CopiedCapture : CopiedCaptures) { - const BlockDecl::Capture &CI = CopiedCapture.CI; - const CGBlockInfo::Capture &capture = CopiedCapture.Capture; + const BlockDecl::Capture &CI = *CopiedCapture.CI; + const CGBlockInfo::Capture &capture = *CopiedCapture.Capture; QualType captureType = CI.getVariable()->getType(); BlockFieldFlags flags = CopiedCapture.Flags; @@ -1783,28 +1920,17 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { dstAddr, srcValue, llvm::ConstantInt::get(Int32Ty, flags.getBitMask()) }; - const VarDecl *variable = CI.getVariable(); - bool copyCanThrow = false; - if (CI.isByRef() && variable->getType()->getAsCXXRecordDecl()) { - const Expr *copyExpr = - CGM.getContext().getBlockVarCopyInits(variable); - if (copyExpr) { - copyCanThrow = true; // FIXME: reuse the noexcept logic - } - } - - if (copyCanThrow) { + if (CI.isByRef() && C.getBlockVarCopyInit(CI.getVariable()).canThrow()) EmitRuntimeCallOrInvoke(CGM.getBlockObjectAssign(), args); - } else { + else EmitNounwindRuntimeCall(CGM.getBlockObjectAssign(), args); - } } } // Ensure that we destroy the copied object if an exception is thrown later // in the helper function. - pushCaptureCleanup(CopiedCapture.Kind, dstField, captureType, flags, /*EHOnly*/ true, - *this); + pushCaptureCleanup(CopiedCapture.Kind, dstField, captureType, flags, + /*ForCopyHelper*/ true, CI.getVariable(), *this); } FinishFunction(); @@ -1868,6 +1994,16 @@ computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, /// variable. llvm::Constant * CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { + SmallVector<BlockCaptureManagedEntity, 4> DestroyedCaptures; + findBlockCapturedManagedEntities(blockInfo, getLangOpts(), DestroyedCaptures, + computeDestroyInfoForBlockCapture); + std::string FuncName = + getCopyDestroyHelperFuncName(DestroyedCaptures, blockInfo.BlockAlign, + /*IsCopyHelper*/ false, CGM); + + if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName)) + return Func; + ASTContext &C = getContext(); FunctionArgList args; @@ -1883,11 +2019,11 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI); llvm::Function *Fn = - llvm::Function::Create(LTy, llvm::GlobalValue::InternalLinkage, - "__destroy_helper_block_", &CGM.getModule()); + llvm::Function::Create(LTy, llvm::GlobalValue::LinkOnceODRLinkage, + FuncName, &CGM.getModule()); IdentifierInfo *II - = &CGM.getContext().Idents.get("__destroy_helper_block_"); + = &CGM.getContext().Idents.get(FuncName); FunctionDecl *FD = FunctionDecl::Create(C, C.getTranslationUnitDecl(), SourceLocation(), @@ -1895,9 +2031,11 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { nullptr, SC_Static, false, false); - CGM.SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); - + setBlockHelperAttributesVisibility(blockInfo.CapturesNonExternalType, Fn, FI, + CGM); StartFunction(FD, C.VoidTy, Fn, FI, args); + markAsIgnoreThreadCheckingAtRuntime(Fn); + ApplyDebugLocation NL{*this, blockInfo.getBlockExpr()->getBeginLoc()}; llvm::Type *structPtrTy = blockInfo.StructureType->getPointerTo(); @@ -1908,20 +2046,17 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { CodeGenFunction::RunCleanupsScope cleanups(*this); - SmallVector<BlockCaptureManagedEntity, 4> DestroyedCaptures; - findBlockCapturedManagedEntities(blockInfo, getLangOpts(), DestroyedCaptures, - computeDestroyInfoForBlockCapture); - for (const auto &DestroyedCapture : DestroyedCaptures) { - const BlockDecl::Capture &CI = DestroyedCapture.CI; - const CGBlockInfo::Capture &capture = DestroyedCapture.Capture; + const BlockDecl::Capture &CI = *DestroyedCapture.CI; + const CGBlockInfo::Capture &capture = *DestroyedCapture.Capture; BlockFieldFlags flags = DestroyedCapture.Flags; Address srcField = Builder.CreateStructGEP(src, capture.getIndex(), capture.getOffset()); pushCaptureCleanup(DestroyedCapture.Kind, srcField, - CI.getVariable()->getType(), flags, /*EHOnly*/ false, *this); + CI.getVariable()->getType(), flags, + /*ForCopyHelper*/ false, CI.getVariable(), *this); } cleanups.ForceCleanup(); @@ -1961,7 +2096,7 @@ public: field = CGF.Builder.CreateBitCast(field, CGF.Int8PtrTy->getPointerTo(0)); llvm::Value *value = CGF.Builder.CreateLoad(field); - CGF.BuildBlockRelease(value, Flags | BLOCK_BYREF_CALLER); + CGF.BuildBlockRelease(value, Flags | BLOCK_BYREF_CALLER, false); } void profileImpl(llvm::FoldingSetNodeID &id) const override { @@ -2288,7 +2423,8 @@ CodeGenFunction::buildByrefHelpers(llvm::StructType &byrefType, byrefInfo.ByrefAlignment.alignmentAtOffset(byrefInfo.FieldOffset); if (const CXXRecordDecl *record = type->getAsCXXRecordDecl()) { - const Expr *copyExpr = CGM.getContext().getBlockVarCopyInits(&var); + const Expr *copyExpr = + CGM.getContext().getBlockVarCopyInit(&var).getCopyExpr(); if (!copyExpr && record->hasTrivialDestructor()) return nullptr; return ::buildByrefHelpers( @@ -2591,19 +2727,25 @@ void CodeGenFunction::emitByrefStructureInit(const AutoVarEmission &emission) { } } -void CodeGenFunction::BuildBlockRelease(llvm::Value *V, BlockFieldFlags flags) { +void CodeGenFunction::BuildBlockRelease(llvm::Value *V, BlockFieldFlags flags, + bool CanThrow) { llvm::Value *F = CGM.getBlockObjectDispose(); llvm::Value *args[] = { Builder.CreateBitCast(V, Int8PtrTy), llvm::ConstantInt::get(Int32Ty, flags.getBitMask()) }; - EmitNounwindRuntimeCall(F, args); // FIXME: throwing destructors? + + if (CanThrow) + EmitRuntimeCallOrInvoke(F, args); + else + EmitNounwindRuntimeCall(F, args); } void CodeGenFunction::enterByrefCleanup(CleanupKind Kind, Address Addr, BlockFieldFlags Flags, - bool LoadBlockVarAddr) { - EHStack.pushCleanup<CallBlockRelease>(Kind, Addr, Flags, LoadBlockVarAddr); + bool LoadBlockVarAddr, bool CanThrow) { + EHStack.pushCleanup<CallBlockRelease>(Kind, Addr, Flags, LoadBlockVarAddr, + CanThrow); } /// Adjust the declaration of something from the blocks API. |