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. @@ -484,6 +486,11 @@ } } + if (CGM.getLangOpts().CPlusPlus) + if (const auto *RD = variable->getType()->getAsCXXRecordDecl()) + if (!RD->isExternallyVisible()) + info.CapturesNonExternalType = true; + QualType VT = getCaptureFieldType(*CGF, CI); CharUnits size = C.getTypeSizeInChars(VT); CharUnits align = C.getDeclAlign(variable); @@ -1498,13 +1505,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 +1594,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 { @@ -1609,6 +1623,88 @@ }; } // end anonymous namespace +static std::string getCopyDestroyHelperFuncName( + const SmallVectorImpl &Captures, + CharUnits BlockAlignment, bool IsCopyHelper, CodeGenModule &CGM) { + 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 += "_"; + + switch (E.Kind) { + case BlockCaptureEntityKind::CXXRecord: { + Name += "c"; + SmallString<256> Str; + if (IsCopyHelper) { + // If this is a copy helper function, use the mangled name of the + // constructor that is called to copy the captured object. + Expr *CE = CI.getCopyExpr(); + if (auto *EC = dyn_cast(CE)) + CE = EC->getSubExpr(); + Str = CGM.getAddrOfCXXStructor( + cast(CE)->getConstructor(), + StructorType::Complete) + ->getName(); + } else { + // If this is a destroy helper functions, use the mangled class name. + llvm::raw_svector_ostream Out(Str); + CGM.getCXXABI().getMangleContext().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: { + Name += "b" + llvm::to_string(Flags.getBitMask()); + const VarDecl *Var = CI.getVariable(); + // Check if copy can throw. + if (IsCopyHelper && CI.isByRef() && + Var->getType()->getAsCXXRecordDecl() && + CGM.getContext().getBlockVarCopyInits(Var)) + Name += "t"; + break; + } + case BlockCaptureEntityKind::NonTrivialCStruct: { + bool IsVolatile = CaptureTy.isVolatileQualified(); + CharUnits Alignment = + BlockAlignment.alignmentAtOffset(E.Capture->getOffset()); + ASTContext &Ctx = CGM.getContext(); + + 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"); + } + + Name += "_" + llvm::to_string(E.Capture->getOffset().getQuantity()); + } + + return Name; +} + static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind, Address Field, QualType CaptureType, BlockFieldFlags Flags, bool EHOnly, @@ -1653,6 +1749,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 +1777,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,7 +1791,14 @@ false, false); - CGM.SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); + if (blockInfo.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); + } StartFunction(FD, C.VoidTy, Fn, FI, args); ApplyDebugLocation NL{*this, blockInfo.getBlockExpr()->getLocStart()}; @@ -1699,13 +1812,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; @@ -1844,6 +1953,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 +1978,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 +1990,18 @@ nullptr, SC_Static, false, false); - CGM.SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); + if (blockInfo.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); + } StartFunction(FD, C.VoidTy, Fn, FI, args); + markAsIgnoreThreadCheckingAtRuntime(Fn); + ApplyDebugLocation NL{*this, blockInfo.getBlockExpr()->getLocStart()}; llvm::Type *structPtrTy = blockInfo.StructureType->getPointerTo(); @@ -1884,13 +2012,9 @@ 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 = 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 @@ -1799,6 +1799,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. @@ -3602,6 +3607,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: 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_b8_20, void (i8*)* @__destroy_helper_block_4_b8_20, 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_b8_20(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_b8_20(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_b3_32(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,28 @@ // 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_b8t_32_b8t_40( +// CHECK: call void @_Block_object_assign( +// CHECK: invoke void @_Block_object_assign( + +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_e8_b8_32_b8_40( +// CHECK: call {{.*}}void @_Block_object_dispose( +// CHECK: call 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 {{.*}} void @__copy_helper_block_ +// CHECK-NOT: define {{.*}} void @__destroy_helper_block_ // 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_c21_ZN6test101BC1ERVKS0__32( + // CHECK: call void @_ZN6test101BC1ERVKS0_( + // CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_8_c16_ZTSVN6test101BE_32( + // 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_c27_ZN12_GLOBAL__N_11BC1ERKS0__32 +// CHECK-LABEL: define internal void @__destroy_helper_block_8_c22_ZTSN12_GLOBAL__N_11BE_32( + +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_b8_32, void (i8*)* @__destroy_helper_block_8_b8_32, 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_s_32, void (i8*)* @__destroy_helper_block_8_s_32, 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_s_32, void (i8*)* @__destroy_helper_block_8_s_32, 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_s_32, void (i8*)* @__destroy_helper_block_8_s_32, 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_s_32, void (i8*)* @__destroy_helper_block_8_s_32, 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_s_32, void (i8*)* @__destroy_helper_block_8_s_32, 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_s_32, void (i8*)* @__destroy_helper_block_8_s_32, 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_b7_32, void (i8*)* @__destroy_helper_block_8_s_32, 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_s_32(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_s_32(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_b8_32(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_b8_32(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_w_32(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_w_32(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_b3_32_b8_40_b8_48" @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_b3_32" // 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_b3_32" // 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_n13_8_8_t0w16_s16_32(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_n5_8_s16_32( // 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,5 +1,6 @@ // 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 } @@ -55,10 +56,16 @@ // Check that copy/dispose helper functions are exception safe. -// CHECK-LABEL: define internal void @__copy_helper_block_( +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_ea8_s_32_b8_40_w_48_c20_ZN5test12S0C1ERKS0__56_c20_ZN5test12S0C1ERKS0__60( // 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: %[[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: %[[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: %[[BLOCKCOPY_SRC:.*]] = load i8*, i8** %[[V4]], align 8 @@ -69,12 +76,6 @@ // 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: 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: invoke void @_ZN5test12S0C1ERKS0_(%[[STRUCT_TEST1_S0]]* %[[V12]], %[[STRUCT_TEST1_S0]]* dereferenceable(4) %[[V11]]) @@ -100,10 +101,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,14 +113,15 @@ // 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_s_32_b8_40_w_48_c20_ZN5test12S0C1ERKS0__56_c20_ZN5test12S0C1ERKS0__60( // CHECK-O1: tail call void @objc_release({{.*}}) {{.*}} !clang.imprecise_release +// CHECK-NOEXCP: define linkonce_odr hidden void @__copy_helper_block_8_s_32_b8_40_w_48_c20_ZN5test12S0C1ERKS0__56_c20_ZN5test12S0C1ERKS0__60( -// CHECK: define internal void @__destroy_helper_block_( +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_ea8_s_32_b8_40_w_48_c15_ZTSN5test12S0E_56_c15_ZTSN5test12S0E_60( // CHECK: %[[BLOCK:.*]] = bitcast i8* %{{.*}} to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8*, i8*, %[[STRUCT_TEST1_S0]], %[[STRUCT_TEST1_S0]] }>* +// 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: %[[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: invoke void @_ZN5test12S0D1Ev(%[[STRUCT_TEST1_S0]]* %[[V6]]) @@ -130,10 +132,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 +149,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 +161,10 @@ // 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_s_32_b8_40_w_48_c15_ZTSN5test12S0E_56_c15_ZTSN5test12S0E_60( // 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_s_32_b8_40_w_48_c15_ZTSN5test12S0E_56_c15_ZTSN5test12S0E_60( struct S0 { S0(); 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 linkonce_odr hidden 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]; });