From: Akira Hatanaka Date: Fri, 10 Aug 2018 15:09:24 +0000 (+0000) Subject: [CodeGen] Merge equivalent block copy/helper functions. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ffa70d529ceb983d6a71e12b666fd3be54793026;p=clang [CodeGen] Merge equivalent block copy/helper functions. Clang generates copy and dispose helper functions for each block literal on the stack. Often these functions are equivalent for different blocks. This commit makes changes to merge equivalent copy and dispose helper functions and reduce code size. To enable merging equivalent copy/dispose functions, the captured object infomation is encoded into the helper function name. This allows IRGen to check whether an equivalent helper function has already been emitted and reuse the function instead of generating a new helper function whenever a block is defined. In addition, the helper functions are marked as linkonce_odr to enable merging helper functions that have the same name across translation units and marked as unnamed_addr to enable the linker's deduplication pass to merge functions that have different names but the same content. rdar://problem/42640608 Differential Revision: https://reviews.llvm.org/D50152 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@339438 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 889857488f..2c51e39b34 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -22,6 +22,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExternalASTSource.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/PrettyPrinter.h" @@ -150,6 +151,22 @@ struct TypeInfo { /// Holds long-lived AST nodes (such as types and decls) that can be /// referred to throughout the semantic analysis of a file. class ASTContext : public RefCountedBase { +public: + /// Copy initialization expr of a __block variable and a boolean flag that + /// indicates whether the expression can throw. + struct BlockVarCopyInit { + BlockVarCopyInit() = default; + BlockVarCopyInit(Expr *CopyExpr, bool CanThrow) + : ExprAndFlag(CopyExpr, CanThrow) {} + void setExprAndFlag(Expr *CopyExpr, bool CanThrow) { + ExprAndFlag.setPointerAndInt(CopyExpr, CanThrow); + } + Expr *getCopyExpr() const { return ExprAndFlag.getPointer(); } + bool canThrow() const { return ExprAndFlag.getInt(); } + llvm::PointerIntPair ExprAndFlag; + }; + +private: friend class NestedNameSpecifier; mutable SmallVector Types; @@ -244,8 +261,8 @@ class ASTContext : public RefCountedBase { /// interface. llvm::DenseMap ObjCMethodRedecls; - /// Mapping from __block VarDecls to their copy initialization expr. - llvm::DenseMap BlockVarCopyInits; + /// Mapping from __block VarDecls to BlockVarCopyInit. + llvm::DenseMap BlockVarCopyInits; /// Mapping from class scope functions specialization to their /// template patterns. @@ -2664,12 +2681,13 @@ public: /// otherwise returns null. const ObjCInterfaceDecl *getObjContainingInterface(const NamedDecl *ND) const; - /// Set the copy inialization expression of a block var decl. - void setBlockVarCopyInits(VarDecl*VD, Expr* Init); + /// Set the copy inialization expression of a block var decl. \p CanThrow + /// indicates whether the copy expression can throw or not. + void setBlockVarCopyInit(const VarDecl* VD, Expr *CopyExpr, bool CanThrow); /// Get the copy initialization expression of the VarDecl \p VD, or /// nullptr if none exists. - Expr *getBlockVarCopyInits(const VarDecl* VD); + BlockVarCopyInit getBlockVarCopyInit(const VarDecl* VD) const; /// Allocate an uninitialized TypeSourceInfo. /// diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 5eb3e9ac02..7d9215802a 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2503,21 +2503,24 @@ const ObjCInterfaceDecl *ASTContext::getObjContainingInterface( /// Get the copy initialization expression of VarDecl, or nullptr if /// none exists. -Expr *ASTContext::getBlockVarCopyInits(const VarDecl*VD) { +ASTContext::BlockVarCopyInit +ASTContext::getBlockVarCopyInit(const VarDecl*VD) const { assert(VD && "Passed null params"); assert(VD->hasAttr() && "getBlockVarCopyInits - not __block var"); - llvm::DenseMap::iterator - I = BlockVarCopyInits.find(VD); - return (I != BlockVarCopyInits.end()) ? I->second : nullptr; + auto I = BlockVarCopyInits.find(VD); + if (I != BlockVarCopyInits.end()) + return I->second; + return {nullptr, false}; } /// Set the copy inialization expression of a block var decl. -void ASTContext::setBlockVarCopyInits(VarDecl*VD, Expr* Init) { - assert(VD && Init && "Passed null params"); +void ASTContext::setBlockVarCopyInit(const VarDecl*VD, Expr *CopyExpr, + bool CanThrow) { + assert(VD && CopyExpr && "Passed null params"); assert(VD->hasAttr() && "setBlockVarCopyInits - not __block var"); - BlockVarCopyInits[VD] = Init; + BlockVarCopyInits[VD].setExprAndFlag(CopyExpr, CanThrow); } TypeSourceInfo *ASTContext::CreateTypeSourceInfo(QualType T, @@ -5962,7 +5965,7 @@ LangAS ASTContext::getOpenCLTypeAddrSpace(const Type *T) const { bool ASTContext::BlockRequiresCopying(QualType Ty, const VarDecl *D) { if (const CXXRecordDecl *record = Ty->getAsCXXRecordDecl()) { - const Expr *copyExpr = getBlockVarCopyInits(D); + const Expr *copyExpr = getBlockVarCopyInit(D).getCopyExpr(); if (!copyExpr && record->hasTrivialDestructor()) return false; return true; diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index 5d03477e9b..b1dbb505b5 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/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 #include @@ -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()->canThrow(); + return false; +} + +static std::string getCopyDestroyHelperFuncName( + const SmallVectorImpl &Captures, + CharUnits BlockAlignment, bool IsCopyHelper, CodeGenModule &CGM) { + ASTContext &Ctx = CGM.getContext(); + std::unique_ptr 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 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 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 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 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(Kind, Addr, Flags, LoadBlockVarAddr); + bool LoadBlockVarAddr, bool CanThrow) { + EHStack.pushCleanup(Kind, Addr, Flags, LoadBlockVarAddr, + CanThrow); } /// Adjust the declaration of something from the blocks API. diff --git a/lib/CodeGen/CGBlocks.h b/lib/CodeGen/CGBlocks.h index 5abf82b3f6..402acea70e 100644 --- a/lib/CodeGen/CGBlocks.h +++ b/lib/CodeGen/CGBlocks.h @@ -231,6 +231,11 @@ public: /// and their layout meta-data has been generated. bool HasCapturedVariableLayout : 1; + /// Indicates whether an object of a non-external C++ class is captured. This + /// bit is used to determine the linkage of the block copy/destroy helper + /// functions. + bool CapturesNonExternalType : 1; + /// The mapping of allocated indexes within the block. llvm::DenseMap Captures; diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 3a5bc0e469..5c47212f1b 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -1726,7 +1726,8 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) { if (emission.Variable->getType().isObjCGCWeak()) Flags |= BLOCK_FIELD_IS_WEAK; enterByrefCleanup(NormalAndEHCleanup, emission.Addr, Flags, - /*LoadBlockVarAddr*/ false); + /*LoadBlockVarAddr*/ false, + cxxDestructorCanThrow(emission.Variable->getType())); } } diff --git a/lib/CodeGen/CGNonTrivialStruct.cpp b/lib/CodeGen/CGNonTrivialStruct.cpp index 922e0934b8..e9f60a9113 100644 --- a/lib/CodeGen/CGNonTrivialStruct.cpp +++ b/lib/CodeGen/CGNonTrivialStruct.cpp @@ -283,8 +283,9 @@ struct GenDefaultInitializeFuncName struct GenDestructorFuncName : GenUnaryFuncName, DestructedTypeVisitor { using Super = DestructedTypeVisitor; - GenDestructorFuncName(CharUnits DstAlignment, ASTContext &Ctx) - : GenUnaryFuncName("__destructor_", DstAlignment, + GenDestructorFuncName(const char *Prefix, CharUnits DstAlignment, + ASTContext &Ctx) + : GenUnaryFuncName(Prefix, DstAlignment, Ctx) {} void visitWithKind(QualType::DestructionKind DK, QualType FT, const FieldDecl *FD, CharUnits CurStructOffset) { @@ -824,11 +825,28 @@ void CodeGenFunction::callCStructDefaultConstructor(LValue Dst) { IsVolatile, *this, std::array({{DstPtr}})); } +std::string +CodeGenFunction::getNonTrivialCopyConstructorStr(QualType QT, + CharUnits Alignment, + bool IsVolatile, + ASTContext &Ctx) { + GenBinaryFuncName GenName("", Alignment, Alignment, Ctx); + return GenName.getName(QT, IsVolatile); +} + +std::string +CodeGenFunction::getNonTrivialDestructorStr(QualType QT, CharUnits Alignment, + bool IsVolatile, ASTContext &Ctx) { + GenDestructorFuncName GenName("", Alignment, Ctx); + return GenName.getName(QT, IsVolatile); +} + void CodeGenFunction::callCStructDestructor(LValue Dst) { bool IsVolatile = Dst.isVolatile(); Address DstPtr = Dst.getAddress(); QualType QT = Dst.getType(); - GenDestructorFuncName GenName(DstPtr.getAlignment(), getContext()); + GenDestructorFuncName GenName("__destructor_", DstPtr.getAlignment(), + getContext()); std::string FuncName = GenName.getName(QT, IsVolatile); callSpecialFunction(GenDestructor(getContext()), FuncName, QT, IsVolatile, *this, std::array({{DstPtr}})); diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 98975a8c75..030101d2f4 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -772,9 +772,11 @@ static bool endsWithReturn(const Decl* F) { return false; } -static void markAsIgnoreThreadCheckingAtRuntime(llvm::Function *Fn) { - Fn->addFnAttr("sanitize_thread_no_checking_at_run_time"); - Fn->removeFnAttr(llvm::Attribute::SanitizeThread); +void CodeGenFunction::markAsIgnoreThreadCheckingAtRuntime(llvm::Function *Fn) { + if (SanOpts.has(SanitizerKind::Thread)) { + Fn->addFnAttr("sanitize_thread_no_checking_at_run_time"); + Fn->removeFnAttr(llvm::Attribute::SanitizeThread); + } } static bool matchesStlAllocatorFn(const Decl *D, const ASTContext &Ctx) { @@ -887,10 +889,6 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, (OMD->getSelector().isUnarySelector() && II->isStr(".cxx_destruct"))) { markAsIgnoreThreadCheckingAtRuntime(Fn); } - } else if (const auto *FD = dyn_cast_or_null(D)) { - IdentifierInfo *II = FD->getIdentifier(); - if (II && II->isStr("__destroy_helper_block_")) - markAsIgnoreThreadCheckingAtRuntime(Fn); } } diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 878923a85b..a160c368b1 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1746,6 +1746,9 @@ public: bool IsLambdaConversionToBlock, bool BuildGlobalBlock); + /// Check if \p T is a C++ class that has a destructor that can throw. + static bool cxxDestructorCanThrow(QualType T); + llvm::Constant *GenerateCopyHelperFunction(const CGBlockInfo &blockInfo); llvm::Constant *GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo); llvm::Constant *GenerateObjCAtomicSetterCopyHelperFunction( @@ -1754,7 +1757,8 @@ public: const ObjCPropertyImplDecl *PID); llvm::Value *EmitBlockCopyAndAutorelease(llvm::Value *Block, QualType Ty); - void BuildBlockRelease(llvm::Value *DeclPtr, BlockFieldFlags flags); + void BuildBlockRelease(llvm::Value *DeclPtr, BlockFieldFlags flags, + bool CanThrow); class AutoVarEmission; @@ -1777,7 +1781,7 @@ public: /// \param LoadBlockVarAddr Indicates whether we need to emit a load from /// \p Addr to get the address of the __block structure. void enterByrefCleanup(CleanupKind Kind, Address Addr, BlockFieldFlags Flags, - bool LoadBlockVarAddr); + bool LoadBlockVarAddr, bool CanThrow); void setBlockContextParameter(const ImplicitParamDecl *D, unsigned argNum, llvm::Value *ptr); @@ -1800,6 +1804,11 @@ public: void GenerateCode(GlobalDecl GD, llvm::Function *Fn, const CGFunctionInfo &FnInfo); + + /// Annotate the function with an attribute that disables TSan checking at + /// runtime. + void markAsIgnoreThreadCheckingAtRuntime(llvm::Function *Fn); + /// Emit code for the start of a function. /// \param Loc The location to be associated with the function. /// \param StartLoc The location of the function body. @@ -3605,6 +3614,19 @@ public: CXXDtorType Type, const CXXRecordDecl *RD); + // Return the copy constructor name with the prefix "__copy_constructor_" + // removed. + static std::string getNonTrivialCopyConstructorStr(QualType QT, + CharUnits Alignment, + bool IsVolatile, + ASTContext &Ctx); + + // Return the destructor name with the prefix "__destructor_" removed. + static std::string getNonTrivialDestructorStr(QualType QT, + CharUnits Alignment, + bool IsVolatile, + ASTContext &Ctx); + // These functions emit calls to the special functions of non-trivial C // structs. void defaultInitNonTrivialCStructVar(LValue Dst); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index bc96689107..abdeba60be 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -11812,8 +11812,16 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { if (!result.isInvalid()) { result = MaybeCreateExprWithCleanups(result); Expr *init = result.getAs(); - Context.setBlockVarCopyInits(var, init); + Context.setBlockVarCopyInit(var, init, canThrow(init)); } + + // The destructor's exception spefication is needed when IRGen generates + // block copy/destroy functions. Resolve it here. + if (const CXXRecordDecl *RD = type->getAsCXXRecordDecl()) + if (CXXDestructorDecl *DD = RD->getDestructor()) { + auto *FPT = DD->getType()->getAs(); + FPT = ResolveExceptionSpec(poi, FPT); + } } } diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index c8ce1a1fcd..2b8cfac88d 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1381,6 +1381,12 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) { } } + if (VD->hasAttr() && VD->getType()->getAsCXXRecordDecl()) { + Expr *CopyExpr = Record.readExpr(); + if (CopyExpr) + Reader.getContext().setBlockVarCopyInit(VD, CopyExpr, Record.readInt()); + } + if (VD->getStorageDuration() == SD_Static && Record.readInt()) Reader.DefinitionSource[VD] = Loc.F->Kind == ModuleKind::MK_MainFile; diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 4633a9c18d..2d90c98faa 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -946,6 +946,13 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { Record.push_back(0); } + if (D->hasAttr() && D->getType()->getAsCXXRecordDecl()) { + ASTContext::BlockVarCopyInit Init = Writer.Context->getBlockVarCopyInit(D); + Record.AddStmt(Init.getCopyExpr()); + if (Init.getCopyExpr()) + Record.push_back(Init.canThrow()); + } + if (D->getStorageDuration() == SD_Static) { bool ModulesCodegen = false; if (Writer.WritingModule && @@ -998,6 +1005,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { !D->isConstexpr() && !D->isInitCapture() && !D->isPreviousDeclInSameBlockScope() && + !(D->hasAttr() && D->getType()->getAsCXXRecordDecl()) && D->getStorageDuration() != SD_Static && !D->getMemberSpecializationInfo()) AbbrevToUse = Writer.getDeclVarAbbrev(); diff --git a/test/CodeGen/blocks-1.c b/test/CodeGen/blocks-1.c index 350f7a3baf..8589a7bedf 100644 --- a/test/CodeGen/blocks-1.c +++ b/test/CodeGen/blocks-1.c @@ -1,11 +1,11 @@ // RUN: %clang_cc1 %s -emit-llvm -o %t -fblocks -// RUN: grep "_Block_object_dispose" %t | count 17 -// RUN: grep "__copy_helper_block_" %t | count 14 -// RUN: grep "__destroy_helper_block_" %t | count 14 +// RUN: grep "_Block_object_dispose" %t | count 12 +// RUN: grep "__copy_helper_block_" %t | count 9 +// RUN: grep "__destroy_helper_block_" %t | count 9 // RUN: grep "__Block_byref_object_copy_" %t | count 2 // RUN: grep "__Block_byref_object_dispose_" %t | count 2 // RUN: grep "i32 135)" %t | count 2 -// RUN: grep "_Block_object_assign" %t | count 10 +// RUN: grep "_Block_object_assign" %t | count 5 int printf(const char *, ...); diff --git a/test/CodeGen/blocks.c b/test/CodeGen/blocks.c index 911c63e41d..d70bb1d153 100644 --- a/test/CodeGen/blocks.c +++ b/test/CodeGen/blocks.c @@ -1,4 +1,9 @@ // RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - -fblocks | FileCheck %s + +// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i32, i32 } + +// CHECK: @[[BLOCK_DESCRIPTOR_TMP21:.*]] = internal constant { i32, i32, void (i8*, i8*)*, void (i8*)*, i8*, i8* } { i32 0, i32 24, void (i8*, i8*)* @__copy_helper_block_4_20r, void (i8*)* @__destroy_helper_block_4_20r, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str, i32 0, i32 0), i8* null }, align 4 + void (^f)(void) = ^{}; // rdar://6768379 @@ -27,6 +32,32 @@ void (^test1)(void) = ^(void) { ^ { i = 1; }(); }; +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_4_20r(i8*, i8*) unnamed_addr +// CHECK: %[[_ADDR:.*]] = alloca i8*, align 4 +// CHECK-NEXT: %[[_ADDR1:.*]] = alloca i8*, align 4 +// CHECK-NEXT: store i8* %0, i8** %[[_ADDR]], align 4 +// CHECK-NEXT: store i8* %1, i8** %[[_ADDR1]], align 4 +// CHECK-NEXT: %[[V2:.*]] = load i8*, i8** %[[_ADDR1]], align 4 +// CHECK-NEXT: %[[BLOCK_SOURCE:.*]] = bitcast i8* %[[V2]] to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* +// CHECK-NEXT: %[[V3:.*]] = load i8*, i8** %[[_ADDR]], align 4 +// CHECK-NEXT: %[[BLOCK_DEST:.*]] = bitcast i8* %[[V3]] to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* +// CHECK-NEXT: %[[V4:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK_SOURCE]], i32 0, i32 5 +// CHECK-NEXT: %[[V5:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK_DEST]], i32 0, i32 5 +// CHECK-NEXT: %[[BLOCKCOPY_SRC:.*]] = load i8*, i8** %[[V4]], align 4 +// CHECK-NEXT: %[[V6:.*]] = bitcast i8** %[[V5]] to i8* +// CHECK-NEXT: call void @_Block_object_assign(i8* %[[V6]], i8* %[[BLOCKCOPY_SRC]], i32 8) +// CHECK-NEXT: ret void + +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_4_20r(i8*) unnamed_addr +// CHECK: %[[_ADDR:.*]] = alloca i8*, align 4 +// CHECK-NEXT: store i8* %0, i8** %[[_ADDR]], align 4 +// CHECK-NEXT: %[[V1:.*]] = load i8*, i8** %[[_ADDR]], align 4 +// CHECK-NEXT: %[[BLOCK:.*]] = bitcast i8* %[[V1]] to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* +// CHECK-NEXT: %[[V2:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK]], i32 0, i32 5 +// CHECK-NEXT: %[[V3:.*]] = load i8*, i8** %[[V2]], align 4 +// CHECK-NEXT: call void @_Block_object_dispose(i8* %[[V3]], i32 8) +// CHECK-NEXT: ret void + typedef double ftype(double); // It's not clear that we *should* support this syntax, but until that decision // is made, we should support it properly and not crash. @@ -85,30 +116,8 @@ void testConstCaptureInCopyAndDestroyHelpers() { __block int i; (^ { i = x; })(); } -// CHECK-LABEL: testConstCaptureInCopyAndDestroyHelpers_block_invoke - -// CHECK: @__copy_helper_block -// CHECK: alloca -// CHECK-NEXT: alloca -// CHECK-NEXT: store -// CHECK-NEXT: store -// CHECK-NEXT: load -// CHECK-NEXT: bitcast -// CHECK-NEXT: load -// CHECK-NEXT: bitcast -// CHECK-NEXT: getelementptr -// CHECK-NEXT: getelementptr -// CHECK-NEXT: load -// CHECK-NEXT: bitcast -// CHECK-NEXT: call void @_Block_object_assign -// CHECK-NEXT: ret - -// CHECK: @__destroy_helper_block -// CHECK: alloca -// CHECK-NEXT: store -// CHECK-NEXT: load -// CHECK-NEXT: bitcast -// CHECK-NEXT: getelementptr -// CHECK-NEXT: load -// CHECK-NEXT: call void @_Block_object_dispose -// CHECK-NEXT: ret +// CHECK-LABEL: define void @testConstCaptureInCopyAndDestroyHelpers( +// CHECK: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %{{.*}}, i32 0, i32 4 +// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i32, i32, void (i8*, i8*)*, void (i8*)*, i8*, i8* }* @[[BLOCK_DESCRIPTOR_TMP21]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 4 + +// CHECK-LABEL: define internal void @__testConstCaptureInCopyAndDestroyHelpers_block_invoke diff --git a/test/CodeGen/sanitize-thread-no-checking-at-run-time.m b/test/CodeGen/sanitize-thread-no-checking-at-run-time.m index 3d862c0bf7..16d3bc931c 100644 --- a/test/CodeGen/sanitize-thread-no-checking-at-run-time.m +++ b/test/CodeGen/sanitize-thread-no-checking-at-run-time.m @@ -35,7 +35,7 @@ public: void test2(id x) { extern void test2_helper(id (^)(void)); test2_helper(^{ return x; }); -// TSAN: define internal void @__destroy_helper_block_(i8*) [[ATTR:#[0-9]+]] +// TSAN: define linkonce_odr hidden void @__destroy_helper_block_8_32o(i8*) unnamed_addr [[ATTR:#[0-9]+]] } // TSAN: attributes [[ATTR]] = { noinline nounwind {{.*}} "sanitize_thread_no_checking_at_run_time" {{.*}} } diff --git a/test/CodeGenCXX/block-byref-cxx-objc.cpp b/test/CodeGenCXX/block-byref-cxx-objc.cpp index ce1ebd615e..208a5c5160 100644 --- a/test/CodeGenCXX/block-byref-cxx-objc.cpp +++ b/test/CodeGenCXX/block-byref-cxx-objc.cpp @@ -1,17 +1,23 @@ -// RUN: %clang_cc1 %s -emit-llvm -triple %itanium_abi_triple -o - -fblocks | FileCheck %s +// RUN: %clang_cc1 %s -std=c++11 -emit-llvm -triple %itanium_abi_triple -o - -fblocks -fexceptions | FileCheck %s // rdar://8594790 struct A { int x; A(const A &); A(); - ~A(); + ~A() noexcept(false); }; -int main() -{ - __block A BYREF_VAR; - ^{ BYREF_VAR.x = 1234; }; +struct B { + int x; + B(const B &); + B(); + ~B(); +}; + +int testA() { + __block A a0, a1; + ^{ a0.x = 1234; a1.x = 5678; }; return 0; } @@ -19,10 +25,32 @@ int main() // CHECK: call {{.*}} @_ZN1AC1ERKS_ // CHECK-LABEL: define internal void @__Block_byref_object_dispose_ // CHECK: call {{.*}} @_ZN1AD1Ev -// CHECK-LABEL: define internal void @__copy_helper_block_ -// CHECK: call {{.*}}void @_Block_object_assign -// CHECK-LABEL: define internal void @__destroy_helper_block_ -// CHECK: call {{.*}}void @_Block_object_dispose + +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_e8_32rc40rc( +// CHECK: call void @_Block_object_assign( +// CHECK: invoke void @_Block_object_assign( +// CHECK: call void @_Block_object_dispose({{.*}}) #[[NOUNWIND_ATTR:[0-9]+]] + +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_e8_32rd40rd( +// CHECK: invoke void @_Block_object_dispose( +// CHECK: call void @_Block_object_dispose( +// CHECK: invoke void @_Block_object_dispose( + +int testB() { + __block B b0, b1; + ^{ b0.x = 1234; b1.x = 5678; }; + return 0; +} + +// CHECK-LABEL: define internal void @__Block_byref_object_copy_ +// CHECK: call {{.*}} @_ZN1BC1ERKS_ +// CHECK-LABEL: define internal void @__Block_byref_object_dispose_ +// CHECK: call {{.*}} @_ZN1BD1Ev + +// CHECK-NOT: define{{.*}}@__copy_helper_block +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_e8_32r40r( + +// CHECK: attributes #[[NOUNWIND_ATTR]] = {{{.*}}nounwind{{.*}}} // rdar://problem/11135650 namespace test1 { diff --git a/test/CodeGenCXX/blocks.cpp b/test/CodeGenCXX/blocks.cpp index 37219d3f7a..7955b00f6f 100644 --- a/test/CodeGenCXX/blocks.cpp +++ b/test/CodeGenCXX/blocks.cpp @@ -252,3 +252,51 @@ namespace test9 { }); } } + +namespace test10 { + // Check that 'v' is included in the copy helper function name to indicate + // the constructor taking a volatile parameter is called to copy the captured + // object. + + // CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_8_32c16_ZTSVN6test101BE( + // CHECK: call void @_ZN6test101BC1ERVKS0_( + // CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_8_32c16_ZTSVN6test101BE( + // CHECK: call void @_ZN6test101BD1Ev( + + struct B { + int a; + B(); + B(const B &); + B(const volatile B &); + ~B(); + }; + + void test() { + volatile B x; + ^{ (void)x; }; + } +} + +// Copy/dispose helper functions that capture objects of non-external types +// should have internal linkage. + +// CHECK-LABEL: define internal void @__copy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE( +// CHECK-LABEL: define internal void @__destroy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE( + +namespace { + struct B { + int a; + B(); + B(const B &); + ~B(); + }; + + void test() { + B x; + ^{ (void)x; }; + } +} + +void callTest() { + test(); +} diff --git a/test/CodeGenCXX/cxx-block-objects.cpp b/test/CodeGenCXX/cxx-block-objects.cpp index ff868fcdd0..d28bcb670f 100644 --- a/test/CodeGenCXX/cxx-block-objects.cpp +++ b/test/CodeGenCXX/cxx-block-objects.cpp @@ -25,9 +25,9 @@ main() return 0; } -// CHECK-LABEL: define internal void @__copy_helper_block_ +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_ // CHECK: call void @_ZN1AC1ERKS_ -// CHECK-LABEL:define internal void @__destroy_helper_block_ +// CHECK-LABEL:define linkonce_odr hidden void @__destroy_helper_block_ // CHECK: call void @_ZN1AD1Ev diff --git a/test/CodeGenObjC/arc-blocks.m b/test/CodeGenObjC/arc-blocks.m index b84d141037..4fa7eef7ab 100644 --- a/test/CodeGenObjC/arc-blocks.m +++ b/test/CodeGenObjC/arc-blocks.m @@ -1,6 +1,17 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-UNOPT %s +// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 } +// CHECK-UNOPT: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 } +// CHECK: @[[BLOCK_DESCRIPTOR_TMP9:.*]] = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_32r, void (i8*)* @__destroy_helper_block_8_32r, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 16 }, align 8 +// CHECK-UNOPT: @{{.*}} = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_32s, void (i8*)* @__destroy_helper_block_8_32s, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 +// CHECK-UNOPT: @{{.*}} = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_32s, void (i8*)* @__destroy_helper_block_8_32s, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 +// CHECK-UNOPT: @{{.*}} = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_32s, void (i8*)* @__destroy_helper_block_8_32s, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 +// CHECK-UNOPT: @{{.*}} = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_32s, void (i8*)* @__destroy_helper_block_8_32s, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 +// CHECK-UNOPT: @[[BLOCK_DESCRIPTOR_TMP44:.*]] = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_32s, void (i8*)* @__destroy_helper_block_8_32s, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 +// CHECK: @[[BLOCK_DESCRIPTOR_TMP46:.*]] = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i8* } { i64 0, i64 48, void (i8*, i8*)* @__copy_helper_block_8_32s, void (i8*)* @__destroy_helper_block_8_32s, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @{{.*}}, i32 0, i32 0) }, align 8 +// CHECK: @[[BLOCK_DESCRIPTOR_TMP48:.*]] = internal constant { i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 } { i64 0, i64 40, void (i8*, i8*)* @__copy_helper_block_8_32b, void (i8*)* @__destroy_helper_block_8_32s, i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.47, i32 0, i32 0), i64 256 }, align 8 + // This shouldn't crash. void test0(id (^maker)(void)) { maker(); @@ -43,7 +54,7 @@ void test2(id x) { extern void test2_helper(id (^)(void)); test2_helper(^{ return x; }); -// CHECK-LABEL: define internal void @__copy_helper_block_(i8*, i8*) #{{[0-9]+}} { +// CHECK: define linkonce_odr hidden void @__copy_helper_block_8_32s(i8*, i8*) unnamed_addr #{{[0-9]+}} { // CHECK: [[T0:%.*]] = load i8*, i8** // CHECK-NEXT: [[SRC:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]* // CHECK-NEXT: [[T0:%.*]] = load i8*, i8** @@ -53,7 +64,8 @@ void test2(id x) { // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) [[NUW]] // CHECK-NEXT: ret void -// CHECK-LABEL: define internal void @__destroy_helper_block_(i8*) #{{[0-9]+}} { + +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_8_32s(i8*) unnamed_addr #{{[0-9]+}} { // CHECK: [[T0:%.*]] = load i8*, i8** // CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]* // CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[T1]], i32 0, i32 5 @@ -155,10 +167,10 @@ void test4(void) { // CHECK-NEXT: call void @objc_release(i8* [[T0]]) // CHECK-NEXT: ret void - // CHECK-LABEL: define internal void @__copy_helper_block_.{{[0-9]+}}(i8*, i8*) #{{[0-9]+}} { + // CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_8_32r(i8*, i8*) unnamed_addr #{{[0-9]+}} { // CHECK: call void @_Block_object_assign(i8* {{%.*}}, i8* {{%.*}}, i32 8) - // CHECK-LABEL: define internal void @__destroy_helper_block_.{{[0-9]+}}(i8*) #{{[0-9]+}} { + // CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_8_32r(i8*) unnamed_addr #{{[0-9]+}} { // CHECK: call void @_Block_object_dispose(i8* {{%.*}}, i32 8) } @@ -211,6 +223,8 @@ void test6(void) { // CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]], [[BYREF_T]]* [[VAR]], i32 0, i32 6 // 0x42800000 - has signature, copy/dispose helpers, as well as BLOCK_HAS_EXTENDED_LAYOUT // CHECK: store i32 -1040187392, + // CHECK: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %{{.*}}, i32 0, i32 4 +// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 }* @[[BLOCK_DESCRIPTOR_TMP9]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 8 // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* // CHECK-NEXT: store i8* [[T0]], i8** // CHECK: call void @test6_helper( @@ -236,14 +250,6 @@ void test6(void) { // CHECK: [[SLOT:%.*]] = getelementptr inbounds {{.*}}, i32 0, i32 6 // CHECK-NEXT: call i8* @objc_storeWeak(i8** [[SLOT]], i8* null) // CHECK-NEXT: ret void - - // CHECK-LABEL: define internal void @__copy_helper_block_.{{[0-9]+}}(i8*, i8*) #{{[0-9]+}} { - // 0x8 - FIELD_IS_BYREF (no FIELD_IS_WEAK because clang in control) - // CHECK: call void @_Block_object_assign(i8* {{%.*}}, i8* {{%.*}}, i32 8) - - // CHECK-LABEL: define internal void @__destroy_helper_block_.{{[0-9]+}}(i8*) #{{[0-9]+}} { - // 0x8 - FIELD_IS_BYREF (no FIELD_IS_WEAK because clang in control) - // CHECK: call void @_Block_object_dispose(i8* {{%.*}}, i32 8) } void test7(void) { @@ -276,12 +282,12 @@ void test7(void) { // CHECK-NEXT: call void @objc_release(i8* [[T0]]) // CHECK: ret void - // CHECK-LABEL: define internal void @__copy_helper_block_.{{[0-9]+}}(i8*, i8*) #{{[0-9]+}} { + // CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_8_32w(i8*, i8*) unnamed_addr #{{[0-9]+}} { // CHECK: getelementptr // CHECK-NEXT: getelementptr // CHECK-NEXT: call void @objc_copyWeak( - // CHECK-LABEL: define internal void @__destroy_helper_block_.{{[0-9]+}}(i8*) #{{[0-9]+}} { + // CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_8_32w(i8*) unnamed_addr #{{[0-9]+}} { // CHECK: getelementptr // CHECK-NEXT: call void @objc_destroyWeak( } @@ -631,6 +637,8 @@ void test18(id x) { // CHECK-UNOPT-NEXT: store i8* null, i8** [[X]] // CHECK-UNOPT-NEXT: call void @objc_storeStrong(i8** [[X]], // CHECK-UNOPT-NEXT: [[SLOTREL:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK-UNOPT: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 4 +// CHECK-UNOPT: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 }* @[[BLOCK_DESCRIPTOR_TMP44]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 8 // CHECK-UNOPT: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 // CHECK-UNOPT-NEXT: [[T0:%.*]] = load i8*, i8** [[X]], // CHECK-UNOPT-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) @@ -642,25 +650,6 @@ void test18(id x) { // CHECK-UNOPT-NEXT: ret void extern void test18_helper(id (^)(void)); test18_helper(^{ return x; }); - -// CHECK-UNOPT-LABEL: define internal void @__copy_helper_block_.{{[0-9]+}}(i8*, i8*) #{{[0-9]+}} { -// CHECK-UNOPT: [[T0:%.*]] = load i8*, i8** -// CHECK-UNOPT-NEXT: [[SRC:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]* -// CHECK-UNOPT-NEXT: [[T0:%.*]] = load i8*, i8** -// CHECK-UNOPT-NEXT: [[DST:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]* -// CHECK-UNOPT-NEXT: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[SRC]], i32 0, i32 5 -// CHECK-UNOPT-NEXT: [[T1:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[DST]], i32 0, i32 5 -// CHECK-UNOPT-NEXT: [[T2:%.*]] = load i8*, i8** [[T0]] -// CHECK-UNOPT-NEXT: store i8* null, i8** [[T1]] -// CHECK-UNOPT-NEXT: call void @objc_storeStrong(i8** [[T1]], i8* [[T2]]) [[NUW]] -// CHECK-UNOPT-NEXT: ret void - -// CHECK-UNOPT-LABEL: define internal void @__destroy_helper_block_.{{[0-9]+}}(i8*) #{{[0-9]+}} { -// CHECK-UNOPT: [[T0:%.*]] = load i8*, i8** -// CHECK-UNOPT-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]* -// CHECK-UNOPT-NEXT: [[T2:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[T1]], i32 0, i32 5 -// CHECK-UNOPT-NEXT: call void @objc_storeStrong(i8** [[T2]], i8* null) -// CHECK-UNOPT-NEXT: ret void } // Ensure that we don't emit helper code in copy/dispose routines for variables @@ -670,33 +659,12 @@ void testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers(id x, id y) { (^ { testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers(x, unsafeObject); })(); } -// CHECK-LABEL: testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers_block_invoke -// CHECK-UNOPT-LABEL: testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers_block_invoke +// CHECK-LABEL: define void @testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers +// %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>* %{{.*}}, i32 0, i32 4 +// store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i8* }* @[[BLOCK_DESCRIPTOR_TMP46]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 8 -// CHECK-UNOPT: @__copy_helper_block -// CHECK-UNOPT: alloca -// CHECK-UNOPT-NEXT: alloca -// CHECK-UNOPT-NEXT: store -// CHECK-UNOPT-NEXT: store -// CHECK-UNOPT-NEXT: load -// CHECK-UNOPT-NEXT: bitcast -// CHECK-UNOPT-NEXT: load -// CHECK-UNOPT-NEXT: bitcast -// CHECK-UNOPT-NEXT: getelementptr -// CHECK-UNOPT-NEXT: getelementptr -// CHECK-UNOPT-NEXT: load -// CHECK-UNOPT-NEXT: store -// CHECK-UNOPT-NEXT: call void @objc_storeStrong -// CHECK-UNOPT-NEXT: ret - -// CHECK-UNOPT: @__destroy_helper_block -// CHECK-UNOPT: alloca -// CHECK-UNOPT-NEXT: store -// CHECK-UNOPT-NEXT: load -// CHECK-UNOPT-NEXT: bitcast -// CHECK-UNOPT-NEXT: getelementptr -// CHECK-UNOPT-NEXT: call void @objc_storeStrong -// CHECK-UNOPT-NEXT: ret +// CHECK-LABEL: define internal void @__testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers_block_invoke +// CHECK-UNOPT-LABEL: define internal void @__testUnsafeUnretainedLifetimeInCopyAndDestroyHelpers_block_invoke // rdar://13588325 void test19_sink(void (^)(int)); @@ -712,6 +680,8 @@ void test19(void (^b)(void)) { // Block setup. We skip most of this. Note the bare retain. // CHECK-NEXT: [[SLOTREL:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 4 +// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, void (i8*, i8*)*, void (i8*)*, i8*, i64 }* @[[BLOCK_DESCRIPTOR_TMP48]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]], align 8 // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]], [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 // CHECK-NEXT: [[T0:%.*]] = load void ()*, void ()** [[B]], // CHECK-NEXT: [[T1:%.*]] = bitcast void ()* [[T0]] to i8* @@ -762,12 +732,6 @@ void test19(void (^b)(void)) { // CHECK-UNOPT: store i8* [[RETAINED]], i8** [[BLOCKCAPTURED]] // CHECK-UNOPT: call void @objc_storeStrong(i8** [[CAPTUREFIELD]], i8* null) -// CHECK-LABEL: define internal void @__copy_helper_block -// CHECK: [[BLOCKSOURCE:%.*]] = bitcast i8* %{{.*}} to <[[BLOCKTY]]>* -// CHECK: [[CAPTUREFIELD:%.*]] = getelementptr inbounds <[[BLOCKTY]]>, <[[BLOCKTY]]>* [[BLOCKSOURCE]], i32 0, i32 5 -// CHECK: [[BLOCKCOPYSRC:%.*]] = load i8*, i8** [[CAPTUREFIELD]] -// CHECK: call i8* @objc_retain(i8* [[BLOCKCOPYSRC]]) - void test20_callee(void (^)()); void test20(const id x) { test20_callee(^{ (void)x; }); diff --git a/test/CodeGenObjC/debug-info-block-helper.m b/test/CodeGenObjC/debug-info-block-helper.m index 107830782c..1d37ea44ec 100644 --- a/test/CodeGenObjC/debug-info-block-helper.m +++ b/test/CodeGenObjC/debug-info-block-helper.m @@ -2,7 +2,7 @@ // RUN: %clang_cc1 -emit-llvm -fblocks -debug-info-kind=limited -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 %s -o - | FileCheck %s extern void foo(void(^)(void)); -// CHECK: !DISubprogram(name: "__destroy_helper_block_" +// CHECK: !DISubprogram(name: "__destroy_helper_block_8_32o40r48r" @interface NSObject { struct objc_object *isa; diff --git a/test/CodeGenObjC/debug-info-blocks.m b/test/CodeGenObjC/debug-info-blocks.m index 848e389f70..b6e5b42ec6 100644 --- a/test/CodeGenObjC/debug-info-blocks.m +++ b/test/CodeGenObjC/debug-info-blocks.m @@ -59,9 +59,9 @@ static void run(void (^block)(void)) if ((self = [super init])) { // CHECK-DAG: [[DBG_LINE]] = !DILocation(line: 0, scope: ![[COPY_SP:[0-9]+]]) // CHECK-DAG: [[COPY_LINE]] = !DILocation(line: [[@LINE+7]], scope: ![[COPY_SP:[0-9]+]]) - // CHECK-DAG: [[COPY_SP]] = distinct !DISubprogram(name: "__copy_helper_block_" + // CHECK-DAG: [[COPY_SP]] = distinct !DISubprogram(name: "__copy_helper_block_8_32o" // CHECK-DAG: [[DESTROY_LINE]] = !DILocation(line: [[@LINE+5]], scope: ![[DESTROY_SP:[0-9]+]]) - // CHECK-DAG: [[DESTROY_SP]] = distinct !DISubprogram(name: "__destroy_helper_block_" + // CHECK-DAG: [[DESTROY_SP]] = distinct !DISubprogram(name: "__destroy_helper_block_8_32o" // CHECK-DAG: !DILocalVariable(arg: 1, scope: ![[COPY_SP]], {{.*}}, flags: DIFlagArtificial) // CHECK-DAG: !DILocalVariable(arg: 2, scope: ![[COPY_SP]], {{.*}}, flags: DIFlagArtificial) // CHECK-DAG: !DILocalVariable(arg: 1, scope: ![[DESTROY_SP]], {{.*}}, flags: DIFlagArtificial) diff --git a/test/CodeGenObjC/mrc-weak.m b/test/CodeGenObjC/mrc-weak.m index e2c78f0733..f77b945617 100644 --- a/test/CodeGenObjC/mrc-weak.m +++ b/test/CodeGenObjC/mrc-weak.m @@ -139,10 +139,10 @@ void test7(void) { // CHECK: call void @use_block // CHECK: call void @objc_destroyWeak -// CHECK-LABEL: define internal void @__copy_helper_block +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block // CHECK: @objc_copyWeak -// CHECK-LABEL: define internal void @__destroy_helper_block +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block // CHECK: @objc_destroyWeak void test8(void) { @@ -162,16 +162,16 @@ void test8(void) { // CHECK: call void @objc_destroyWeak // CHECK-LABEL: define void @test9_baseline() -// CHECK: define internal void @__copy_helper -// CHECK: define internal void @__destroy_helper +// CHECK: define linkonce_odr hidden void @__copy_helper +// CHECK: define linkonce_odr hidden void @__destroy_helper void test9_baseline(void) { Foo *p = get_object(); use_block(^{ [p run]; }); } // CHECK-LABEL: define void @test9() -// CHECK-NOT: define internal void @__copy_helper -// CHECK-NOT: define internal void @__destroy_helper +// CHECK-NOT: define linkonce_odr hidden void @__copy_helper +// CHECK-NOT: define linkonce_odr hidden void @__destroy_helper // CHECK: define void @test9_fin() void test9(void) { __unsafe_unretained Foo *p = get_object(); @@ -180,8 +180,8 @@ void test9(void) { void test9_fin() {} // CHECK-LABEL: define void @test10() -// CHECK-NOT: define internal void @__copy_helper -// CHECK-NOT: define internal void @__destroy_helper +// CHECK-NOT: define linkonce_odr hidden void @__copy_helper +// CHECK-NOT: define linkonce_odr hidden void @__destroy_helper // CHECK: define void @test10_fin() void test10(void) { typedef __unsafe_unretained Foo *UnsafeFooPtr; diff --git a/test/CodeGenObjC/strong-in-c-struct.m b/test/CodeGenObjC/strong-in-c-struct.m index 36e9049635..9c284049b5 100644 --- a/test/CodeGenObjC/strong-in-c-struct.m +++ b/test/CodeGenObjC/strong-in-c-struct.m @@ -419,11 +419,11 @@ void test_copy_constructor_StrongVolatile1(Strong *s) { // CHECK: call void @__destructor_8_s16( // CHECK: ret void -// CHECK: define internal void @__copy_helper_block_.1(i8*, i8*) +// CHECK: define linkonce_odr hidden void @__copy_helper_block_8_32n13_8_8_t0w16_s16(i8*, i8*) // CHECK: call void @__copy_constructor_8_8_t0w16_s16( // CHECK: ret void -// CHECK: define internal void @__destroy_helper_block_.2( +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_8_32n5_8_s16( // CHECK: call void @__destructor_8_s16( // CHECK: ret void diff --git a/test/CodeGenObjCXX/arc-blocks.mm b/test/CodeGenObjCXX/arc-blocks.mm index 3a75576681..614ba5e0f5 100644 --- a/test/CodeGenObjCXX/arc-blocks.mm +++ b/test/CodeGenObjCXX/arc-blocks.mm @@ -1,8 +1,10 @@ // RUN: %clang_cc1 -std=gnu++98 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-runtime-has-weak -fblocks -fobjc-arc -fexceptions -fobjc-arc-exceptions -o - %s | FileCheck -check-prefix CHECK %s // RUN: %clang_cc1 -std=gnu++98 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-runtime-has-weak -fblocks -fobjc-arc -fexceptions -fobjc-arc-exceptions -O1 -o - %s | FileCheck -check-prefix CHECK-O1 %s +// RUN: %clang_cc1 -std=gnu++98 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-runtime-has-weak -fblocks -fobjc-arc -o - %s | FileCheck -check-prefix CHECK-NOEXCP %s // CHECK: [[A:.*]] = type { i64, [10 x i8*] } // CHECK: %[[STRUCT_TEST1_S0:.*]] = type { i32 } +// CHECK: %[[STRUCT_TRIVIAL_INTERNAL:.*]] = type { i32 } // CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 } // CHECK: [[LAYOUT0:@.*]] = private unnamed_addr constant [3 x i8] c" 9\00" @@ -55,34 +57,34 @@ namespace test1 { // Check that copy/dispose helper functions are exception safe. -// CHECK-LABEL: define internal void @__copy_helper_block_( -// CHECK: %[[BLOCK_SOURCE:.*]] = bitcast i8* %{{.*}} to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* -// CHECK: %[[BLOCK_DEST:.*]] = bitcast i8* %{{.*}} to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_ea8_32s40r48w56c15_ZTSN5test12S0E60c15_ZTSN5test12S0E( +// CHECK: %[[BLOCK_SOURCE:.*]] = bitcast i8* %{{.*}} to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* +// CHECK: %[[BLOCK_DEST:.*]] = bitcast i8* %{{.*}} to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* -// CHECK: %[[V4:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_SOURCE]], i32 0, i32 6 -// CHECK: %[[V5:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_DEST]], i32 0, i32 6 +// CHECK: %[[V9:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK_SOURCE]], i32 0, i32 5 +// CHECK: %[[V10:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK_DEST]], i32 0, i32 5 +// CHECK: %[[BLOCKCOPY_SRC2:.*]] = load i8*, i8** %[[V9]], align 8 +// CHECK: store i8* null, i8** %[[V10]], align 8 +// CHECK: call void @objc_storeStrong(i8** %[[V10]], i8* %[[BLOCKCOPY_SRC2]]) + +// CHECK: %[[V4:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK_SOURCE]], i32 0, i32 6 +// CHECK: %[[V5:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK_DEST]], i32 0, i32 6 // CHECK: %[[BLOCKCOPY_SRC:.*]] = load i8*, i8** %[[V4]], align 8 // CHECK: %[[V6:.*]] = bitcast i8** %[[V5]] to i8* // CHECK: call void @_Block_object_assign(i8* %[[V6]], i8* %[[BLOCKCOPY_SRC]], i32 8) -// CHECK: %[[V7:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_SOURCE]], i32 0, i32 7 -// CHECK: %[[V8:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_DEST]], i32 0, i32 7 +// CHECK: %[[V7:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK_SOURCE]], i32 0, i32 7 +// CHECK: %[[V8:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK_DEST]], i32 0, i32 7 // CHECK: call void @objc_copyWeak(i8** %[[V8]], i8** %[[V7]]) -// CHECK: %[[V9:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_SOURCE]], i32 0, i32 5 -// CHECK: %[[V10:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_DEST]], i32 0, i32 5 -// CHECK: %[[BLOCKCOPY_SRC2:.*]] = load i8*, i8** %[[V9]], align 8 -// CHECK: store i8* null, i8** %[[V10]], align 8 -// CHECK: call void @objc_storeStrong(i8** %[[V10]], i8* %[[BLOCKCOPY_SRC2]]) - -// CHECK: %[[V11:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_SOURCE]], i32 0, i32 8 -// CHECK: %[[V12:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_DEST]], i32 0, i32 8 +// CHECK: %[[V11:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK_SOURCE]], i32 0, i32 8 +// CHECK: %[[V12:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK_DEST]], i32 0, i32 8 // CHECK: invoke void @_ZN5test12S0C1ERKS0_(%[[STRUCT_TEST1_S0]]* %[[V12]], %[[STRUCT_TEST1_S0]]* dereferenceable(4) %[[V11]]) // CHECK: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]] // CHECK: [[INVOKE_CONT]]: -// CHECK: %[[V13:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_SOURCE]], i32 0, i32 9 -// CHECK: %[[V14:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK_DEST]], i32 0, i32 9 +// CHECK: %[[V13:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK_SOURCE]], i32 0, i32 9 +// CHECK: %[[V14:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK_DEST]], i32 0, i32 9 // CHECK: invoke void @_ZN5test12S0C1ERKS0_(%[[STRUCT_TEST1_S0]]* %[[V14]], %[[STRUCT_TEST1_S0]]* dereferenceable(4) %[[V13]]) // CHECK: to label %[[INVOKE_CONT4:.*]] unwind label %[[LPAD3:.*]] @@ -100,10 +102,10 @@ namespace test1 { // CHECK: br label %[[EHCLEANUP]] // CHECK: [[EHCLEANUP]]: -// CHECK: call void @objc_storeStrong(i8** %[[V10]], i8* null) // CHECK: call void @objc_destroyWeak(i8** %[[V8]]) // CHECK: %[[V21:.*]] = load i8*, i8** %[[V5]], align 8 // CHECK: call void @_Block_object_dispose(i8* %[[V21]], i32 8) +// CHECK: call void @objc_storeStrong(i8** %[[V10]], i8* null) // CHECK: br label %[[EH_RESUME:.*]] // CHECK: [[EH_RESUME]]: @@ -112,16 +114,17 @@ namespace test1 { // CHECK: [[TERMINATE_LPAD]]: // CHECK: call void @__clang_call_terminate( -// CHECK-O1-LABEL: define internal void @__copy_helper_block_( +// CHECK-O1-LABEL: define linkonce_odr hidden void @__copy_helper_block_ea8_32s40r48w56c15_ZTSN5test12S0E60c15_ZTSN5test12S0E( // CHECK-O1: tail call void @objc_release({{.*}}) {{.*}} !clang.imprecise_release - -// CHECK: define internal void @__destroy_helper_block_( -// CHECK: %[[BLOCK:.*]] = bitcast i8* %{{.*}} to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* -// CHECK: %[[V2:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK]], i32 0, i32 6 -// CHECK: %[[V3:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK]], i32 0, i32 7 -// CHECK: %[[V4:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK]], i32 0, i32 5 -// CHECK: %[[V5:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK]], i32 0, i32 8 -// CHECK: %[[V6:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* %[[BLOCK]], i32 0, i32 9 +// CHECK-NOEXCP: define linkonce_odr hidden void @__copy_helper_block_8_32s40r48w56c15_ZTSN5test12S0E60c15_ZTSN5test12S0E( + +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_ea8_32s40r48w56c15_ZTSN5test12S0E60c15_ZTSN5test12S0E( +// CHECK: %[[BLOCK:.*]] = bitcast i8* %{{.*}} to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* +// CHECK: %[[V4:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK]], i32 0, i32 5 +// CHECK: %[[V2:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK]], i32 0, i32 6 +// CHECK: %[[V3:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK]], i32 0, i32 7 +// CHECK: %[[V5:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK]], i32 0, i32 8 +// CHECK: %[[V6:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]], %[[STRUCT_TRIVIAL_INTERNAL]] }>* %[[BLOCK]], i32 0, i32 9 // CHECK: invoke void @_ZN5test12S0D1Ev(%[[STRUCT_TEST1_S0]]* %[[V6]]) // CHECK: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]] @@ -130,10 +133,10 @@ namespace test1 { // CHECK: to label %[[INVOKE_CONT2:.*]] unwind label %[[LPAD1:.*]] // CHECK: [[INVOKE_CONT2]]: -// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* null) // CHECK: call void @objc_destroyWeak(i8** %[[V3]]) // CHECK: %[[V7:.*]] = load i8*, i8** %[[V2]], align 8 // CHECK: call void @_Block_object_dispose(i8* %[[V7]], i32 8) +// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* null) // CHECK: ret void // CHECK: [[LPAD]]: @@ -147,10 +150,10 @@ namespace test1 { // CHECK: br label %[[EHCLEANUP]] // CHECK: [[EHCLEANUP]]: -// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* null) // CHECK: call void @objc_destroyWeak(i8** %[[V3]]) // CHECK: %[[V14:.*]] = load i8*, i8** %[[V2]], align 8 // CHECK: call void @_Block_object_dispose(i8* %[[V14]], i32 8) +// CHECK: call void @objc_storeStrong(i8** %[[V4]], i8* null) // CHECK: br label %[[EH_RESUME]] // CHECK: [[EH_RESUME]]: @@ -159,9 +162,16 @@ namespace test1 { // CHECK: [[TERMINATE_LPAD]]: // CHECK: call void @__clang_call_terminate( -// CHECK-O1-LABEL: define internal void @__destroy_helper_block_( +// CHECK-O1-LABEL: define linkonce_odr hidden void @__destroy_helper_block_ea8_32s40r48w56c15_ZTSN5test12S0E60c15_ZTSN5test12S0E( // CHECK-O1: tail call void @objc_release({{.*}}) {{.*}} !clang.imprecise_release // CHECK-O1: tail call void @objc_release({{.*}}) {{.*}} !clang.imprecise_release +// CHECK-NOEXCP: define linkonce_odr hidden void @__destroy_helper_block_8_32s40r48w56c15_ZTSN5test12S0E60c15_ZTSN5test12S0E( + +namespace { +struct TrivialInternal { + int a; +}; +} struct S0 { S0(); @@ -177,6 +187,10 @@ void foo1() { __weak id t1 = getObj(); id t2 = getObj(); S0 t3, t4; - ^{ (void)t0; (void)t1; (void)t2; (void)t3; (void)t4; }; + // Capturing a non-external type doesn't cause the copy/dispose helpers to be + // internal unless the captured type has a non-trivial copy constructor or + // destructor. + TrivialInternal t5; + ^{ (void)t0; (void)t1; (void)t2; (void)t3; (void)t4; (void)t5; }; } } diff --git a/test/CodeGenObjCXX/lambda-to-block.mm b/test/CodeGenObjCXX/lambda-to-block.mm index a8d0718b12..cbb4ce161d 100644 --- a/test/CodeGenObjCXX/lambda-to-block.mm +++ b/test/CodeGenObjCXX/lambda-to-block.mm @@ -12,7 +12,7 @@ struct Copyable { void hasLambda(Copyable x) { takesBlock([x] () { }); } -// CHECK-LABEL: define internal void @__copy_helper_block_ +// CHECK-LABEL: define internal void @"__copy_helper_block_ // CHECK: call void @"_ZZ9hasLambda8CopyableEN3$_0C1ERKS0_" // CHECK-LABEL: define internal void @"_ZZ9hasLambda8CopyableEN3$_0C2ERKS0_" // CHECK: call void @_ZN8CopyableC1ERKS_ diff --git a/test/CodeGenObjCXX/mrc-weak.mm b/test/CodeGenObjCXX/mrc-weak.mm index 17ceb31231..1cb191fe30 100644 --- a/test/CodeGenObjCXX/mrc-weak.mm +++ b/test/CodeGenObjCXX/mrc-weak.mm @@ -119,10 +119,10 @@ void test7(void) { // CHECK: call void @use_block // CHECK: call void @objc_destroyWeak -// CHECK-LABEL: define internal void @__copy_helper_block +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block // CHECK: @objc_copyWeak -// CHECK-LABEL: define internal void @__destroy_helper_block +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block // CHECK: @objc_destroyWeak void test8(void) { @@ -142,8 +142,8 @@ void test8(void) { // CHECK: call void @objc_destroyWeak // CHECK-LABEL: define void @_Z14test9_baselinev() -// CHECK: define internal void @__copy_helper -// CHECK: define internal void @__destroy_helper +// CHECK: define linkonce_odr hidden void @__copy_helper +// CHECK: define linkonce_odr hidden void @__destroy_helper void test9_baseline(void) { Foo *p = get_object(); use_block(^{ [p run]; }); diff --git a/test/PCH/block-helpers.cpp b/test/PCH/block-helpers.cpp new file mode 100644 index 0000000000..046bbb428c --- /dev/null +++ b/test/PCH/block-helpers.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -x c++-header -triple x86_64-apple-darwin11 -emit-pch -fblocks -fexceptions -o %t %S/block-helpers.h +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -include-pch %t -emit-llvm -fblocks -fexceptions -o - %s | FileCheck %s + +// The second call to block_object_assign should be an invoke. + +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_e8_32rc40rc( +// CHECK: call void @_Block_object_assign( +// CHECK: invoke void @_Block_object_assign( +// CHECK: call void @_Block_object_dispose( + +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_e8_32r40r( +void test() { + S s; + s.m(); +} diff --git a/test/PCH/block-helpers.h b/test/PCH/block-helpers.h new file mode 100644 index 0000000000..3614cc7be9 --- /dev/null +++ b/test/PCH/block-helpers.h @@ -0,0 +1,12 @@ +struct S0 { + S0(); + S0(const S0 &) noexcept(false); + int a; +}; + +struct S { + void m() { + __block S0 x, y; + ^{ (void)x; (void)y; }; + } +};