Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -244,8 +244,12 @@ /// interface. llvm::DenseMap ObjCMethodRedecls; - /// Mapping from __block VarDecls to their copy initialization expr. - llvm::DenseMap BlockVarCopyInits; + /// Mapping from __block VarDecls to their copy initialization expr. The + /// boolean flag indicates whether the expression can throw. + typedef llvm::DenseMap> + BlockVarCopyInitMap; + BlockVarCopyInitMap BlockVarCopyInits; /// Mapping from class scope functions specialization to their /// template patterns. @@ -2671,6 +2675,14 @@ /// nullptr if none exists. Expr *getBlockVarCopyInits(const VarDecl* VD); + /// Set the boolean flag if the copy initilization expression of \p VD can + /// throw. + void setBlockVarCopyExprCanThrow(const VarDecl *VD); + + /// Get the boolean flag that indicates whether the copy initilization + /// expression of \p VD can throw. + bool getBlockVarCopyExprCanThrow(const VarDecl *VD) const; + /// Allocate an uninitialized TypeSourceInfo. /// /// The caller should initialize the memory held by TypeSourceInfo using Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -2507,9 +2507,8 @@ 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; + BlockVarCopyInitMap::const_iterator I = BlockVarCopyInits.find(VD); + return (I != BlockVarCopyInits.end()) ? I->second.getPointer() : nullptr; } /// Set the copy inialization expression of a block var decl. @@ -2517,7 +2516,22 @@ assert(VD && Init && "Passed null params"); assert(VD->hasAttr() && "setBlockVarCopyInits - not __block var"); - BlockVarCopyInits[VD] = Init; + BlockVarCopyInits[VD].setPointer(Init); +} + +void ASTContext::setBlockVarCopyExprCanThrow(const VarDecl *VD) { + assert(VD && "Passed null params"); + assert(VD->hasAttr() && + "setBlockVarCopyExprCanThrow - not __block var"); + BlockVarCopyInits[VD].setInt(true); +} + +bool ASTContext::getBlockVarCopyExprCanThrow(const VarDecl *VD) const { + assert(VD && "Passed null params"); + assert(VD->hasAttr() && + "getBlockVarCopyExprCanThrow - not __block var"); + BlockVarCopyInitMap::const_iterator I = BlockVarCopyInits.find(VD); + return (I != BlockVarCopyInits.end()) ? I->second.getInt() : false; } TypeSourceInfo *ASTContext::CreateTypeSourceInfo(QualType T, Index: lib/CodeGen/CGBlocks.h =================================================================== --- lib/CodeGen/CGBlocks.h +++ lib/CodeGen/CGBlocks.h @@ -231,6 +231,11 @@ /// 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; Index: lib/CodeGen/CGBlocks.cpp =================================================================== --- lib/CodeGen/CGBlocks.cpp +++ 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 @@ 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 @@ } 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 @@ if (!record->hasTrivialDestructor()) { info.HasCXXObject = true; info.NeedsCopyDispose = true; + if (!record->isExternallyVisible()) + info.CapturesNonExternalType = true; } } } @@ -1498,13 +1504,17 @@ 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 @@ -1583,6 +1593,9 @@ 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 { @@ -1590,10 +1603,12 @@ 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; @@ -1604,15 +1619,104 @@ 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 += "b"; + if (F & BLOCK_FIELD_IS_WEAK) + Name += "w"; + else { + if (CodeGenFunction::cxxDestructorCanThrow(CaptureTy)) + Name += "d"; + if (IsCopyHelper && Ctx.getBlockVarCopyExprCanThrow(Var)) + Name += "c"; + } + } else { + assert(F & BLOCK_FIELD_IS_OBJECT); + Name += "r"; + if (F == BLOCK_FIELD_IS_BLOCK) + Name += "b"; + } + 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); + 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) { + VarDecl *Var, CodeGenFunction &CGF) { switch (CaptureKind) { case BlockCaptureEntityKind::CXXRecord: case BlockCaptureEntityKind::ARCWeak: @@ -1634,7 +1738,9 @@ case BlockCaptureEntityKind::BlockObject: { if (!EHOnly || CGF.getLangOpts().Exceptions) { CleanupKind Kind = EHOnly ? EHCleanup : NormalAndEHCleanup; - CGF.enterByrefCleanup(Kind, Field, Flags, /*LoadBlockVarAddr*/ true); + bool CanThrow = CGF.cxxDestructorCanThrow(CaptureType); + CGF.enterByrefCleanup(Kind, Field, Flags, /*LoadBlockVarAddr*/ true, + CanThrow); } break; } @@ -1643,6 +1749,19 @@ } } +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 @@ -1653,6 +1772,16 @@ /// 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; @@ -1671,11 +1800,11 @@ 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(), @@ -1685,8 +1814,8 @@ 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()->getLocStart()}; llvm::Type *structPtrTy = blockInfo.StructureType->getPointerTo(); @@ -1699,13 +1828,9 @@ 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; @@ -1759,28 +1884,17 @@ 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.getBlockVarCopyExprCanThrow(CI.getVariable())) 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); + CI.getVariable(), *this); } FinishFunction(); @@ -1844,6 +1958,16 @@ /// 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; @@ -1859,11 +1983,11 @@ 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(), @@ -1871,9 +1995,11 @@ 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()->getLocStart()}; llvm::Type *structPtrTy = blockInfo.StructureType->getPointerTo(); @@ -1884,20 +2010,17 @@ 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, /*EHOnly*/ false, + CI.getVariable(), *this); } cleanups.ForceCleanup(); @@ -1937,7 +2060,7 @@ 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 { @@ -2567,19 +2690,25 @@ } } -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. Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -1726,7 +1726,8 @@ if (emission.Variable->getType().isObjCGCWeak()) Flags |= BLOCK_FIELD_IS_WEAK; enterByrefCleanup(NormalAndEHCleanup, emission.Addr, Flags, - /*LoadBlockVarAddr*/ false); + /*LoadBlockVarAddr*/ false, + cxxDestructorCanThrow(emission.Variable->getType())); } } Index: lib/CodeGen/CGNonTrivialStruct.cpp =================================================================== --- lib/CodeGen/CGNonTrivialStruct.cpp +++ lib/CodeGen/CGNonTrivialStruct.cpp @@ -283,8 +283,9 @@ 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 @@ 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}})); Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1746,6 +1746,9 @@ 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 @@ 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 @@ /// \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 @@ 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. @@ -3603,6 +3612,19 @@ 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); Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -772,9 +772,11 @@ 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 @@ (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); } } Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -11847,7 +11847,17 @@ result = MaybeCreateExprWithCleanups(result); Expr *init = result.getAs(); Context.setBlockVarCopyInits(var, init); + if (canThrow(init)) + Context.setBlockVarCopyExprCanThrow(var); } + + // 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); + } } } Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -1381,6 +1381,15 @@ } } + if (VD->hasAttr() && VD->getType()->getAsCXXRecordDecl()) { + Expr *CopyExpr = Record.readExpr(); + if (CopyExpr) { + Reader.getContext().setBlockVarCopyInits(VD, CopyExpr); + if (Record.readInt()) + Reader.getContext().setBlockVarCopyExprCanThrow(VD); + } + } + if (VD->getStorageDuration() == SD_Static && Record.readInt()) Reader.DefinitionSource[VD] = Loc.F->Kind == ModuleKind::MK_MainFile; Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -946,6 +946,13 @@ Record.push_back(0); } + if (D->hasAttr() && D->getType()->getAsCXXRecordDecl()) { + Expr *CopyExpr = Writer.Context->getBlockVarCopyInits(D); + Record.AddStmt(CopyExpr); + if (CopyExpr) + Record.push_back(Writer.Context->getBlockVarCopyExprCanThrow(D)); + } + if (D->getStorageDuration() == SD_Static) { bool ModulesCodegen = false; if (Writer.WritingModule && @@ -998,6 +1005,7 @@ !D->isConstexpr() && !D->isInitCapture() && !D->isPreviousDeclInSameBlockScope() && + !(D->hasAttr() && D->getType()->getAsCXXRecordDecl()) && D->getStorageDuration() != SD_Static && !D->getMemberSpecializationInfo()) AbbrevToUse = Writer.getDeclVarAbbrev(); Index: test/CodeGen/blocks-1.c =================================================================== --- test/CodeGen/blocks-1.c +++ 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 *, ...); Index: test/CodeGen/blocks.c =================================================================== --- test/CodeGen/blocks.c +++ 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_20b, void (i8*)* @__destroy_helper_block_4_20b, 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 @@ ^ { i = 1; }(); }; +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_4_20b(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_20b(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 @@ __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 Index: test/CodeGen/sanitize-thread-no-checking-at-run-time.m =================================================================== --- test/CodeGen/sanitize-thread-no-checking-at-run-time.m +++ test/CodeGen/sanitize-thread-no-checking-at-run-time.m @@ -35,7 +35,7 @@ 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_32r(i8*) unnamed_addr [[ATTR:#[0-9]+]] } // TSAN: attributes [[ATTR]] = { noinline nounwind {{.*}} "sanitize_thread_no_checking_at_run_time" {{.*}} } Index: test/CodeGenCXX/block-byref-cxx-objc.cpp =================================================================== --- test/CodeGenCXX/block-byref-cxx-objc.cpp +++ 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,30 @@ // 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_32bdc40bdc( +// CHECK: call void @_Block_object_assign( +// CHECK: invoke void @_Block_object_assign( +// CHECK: invoke void @_Block_object_dispose( + +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_e8_32bd40bd( +// 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: define linkonce_odr hidden void @__copy_helper_block_e8_32bc40bc( +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_e8_32b40b( // rdar://problem/11135650 namespace test1 { Index: test/CodeGenCXX/blocks.cpp =================================================================== --- test/CodeGenCXX/blocks.cpp +++ test/CodeGenCXX/blocks.cpp @@ -252,3 +252,51 @@ }); } } + +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(); +} Index: test/CodeGenCXX/cxx-block-objects.cpp =================================================================== --- test/CodeGenCXX/cxx-block-objects.cpp +++ test/CodeGenCXX/cxx-block-objects.cpp @@ -25,9 +25,9 @@ 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 Index: test/CodeGenObjC/arc-blocks.m =================================================================== --- test/CodeGenObjC/arc-blocks.m +++ 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_32b, void (i8*)* @__destroy_helper_block_8_32b, 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_32rb, 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 @@ 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 @@ // 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 @@ // 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_32b(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_32b(i8*) unnamed_addr #{{[0-9]+}} { // CHECK: call void @_Block_object_dispose(i8* {{%.*}}, i32 8) } @@ -211,6 +223,8 @@ // 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 @@ // 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 @@ // 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 @@ // 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 @@ // 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 @@ (^ { 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 @@ // 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 @@ // 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; }); Index: test/CodeGenObjC/debug-info-block-helper.m =================================================================== --- test/CodeGenObjC/debug-info-block-helper.m +++ 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_32r40b48b" @interface NSObject { struct objc_object *isa; Index: test/CodeGenObjC/debug-info-blocks.m =================================================================== --- test/CodeGenObjC/debug-info-blocks.m +++ test/CodeGenObjC/debug-info-blocks.m @@ -59,9 +59,9 @@ 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_32r" // 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_32r" // 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) Index: test/CodeGenObjC/mrc-weak.m =================================================================== --- test/CodeGenObjC/mrc-weak.m +++ test/CodeGenObjC/mrc-weak.m @@ -139,10 +139,10 @@ // 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 @@ // 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_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; Index: test/CodeGenObjC/strong-in-c-struct.m =================================================================== --- test/CodeGenObjC/strong-in-c-struct.m +++ test/CodeGenObjC/strong-in-c-struct.m @@ -419,11 +419,11 @@ // 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 Index: test/CodeGenObjCXX/arc-blocks.mm =================================================================== --- test/CodeGenObjCXX/arc-blocks.mm +++ 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 @@ // 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_32s40b48w56c15_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 @@ // 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 @@ // 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_32s40b48w56c15_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_32s40b48w56c15_ZTSN5test12S0E60c15_ZTSN5test12S0E( + +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_ea8_32s40b48w56c15_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 @@ // 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 @@ // 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 @@ // 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_32s40b48w56c15_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_32s40b48w56c15_ZTSN5test12S0E60c15_ZTSN5test12S0E( + +namespace { +struct TrivialInternal { + int a; +}; +} struct S0 { S0(); @@ -177,6 +187,10 @@ __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; }; } } Index: test/CodeGenObjCXX/lambda-to-block.mm =================================================================== --- test/CodeGenObjCXX/lambda-to-block.mm +++ test/CodeGenObjCXX/lambda-to-block.mm @@ -12,7 +12,7 @@ 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_ Index: test/CodeGenObjCXX/mrc-weak.mm =================================================================== --- test/CodeGenObjCXX/mrc-weak.mm +++ test/CodeGenObjCXX/mrc-weak.mm @@ -119,10 +119,10 @@ // 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 @@ // 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]; }); Index: test/PCH/block-helpers.h =================================================================== --- /dev/null +++ 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; }; + } +}; Index: test/PCH/block-helpers.cpp =================================================================== --- /dev/null +++ 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_32bc40bc( +// 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_32b40b( +void test() { + S s; + s.m(); +}