Index: cfe/trunk/include/clang/AST/Decl.h =================================================================== --- cfe/trunk/include/clang/AST/Decl.h +++ cfe/trunk/include/clang/AST/Decl.h @@ -965,6 +965,8 @@ /// Defines kind of the ImplicitParamDecl: 'this', 'self', 'vtt', '_cmd' or /// something else. unsigned ImplicitParamKind : 3; + + unsigned EscapingByref : 1; }; union { @@ -1407,6 +1409,19 @@ NonParmVarDeclBits.PreviousDeclInSameBlockScope = Same; } + /// Indicates the capture is a __block variable that is captured by a block + /// that can potentially escape (a block for which BlockDecl::doesNotEscape + /// returns false). + bool isEscapingByref() const; + + /// Indicates the capture is a __block variable that is never captured by an + /// escaping block. + bool isNonEscapingByref() const; + + void setEscapingByref() { + NonParmVarDeclBits.EscapingByref = true; + } + /// Retrieve the variable declaration from which this variable could /// be instantiated, if it is an instantiation (rather than a non-template). VarDecl *getTemplateInstantiationPattern() const; @@ -3858,6 +3873,14 @@ /// variable. bool isByRef() const { return VariableAndFlags.getInt() & flag_isByRef; } + bool isEscapingByref() const { + return getVariable()->isEscapingByref(); + } + + bool isNonEscapingByref() const { + return getVariable()->isNonEscapingByref(); + } + /// Whether this is a nested capture, i.e. the variable captured /// is not from outside the immediately enclosing function/block. bool isNested() const { return VariableAndFlags.getInt() & flag_isNested; } Index: cfe/trunk/include/clang/Sema/ScopeInfo.h =================================================================== --- cfe/trunk/include/clang/Sema/ScopeInfo.h +++ cfe/trunk/include/clang/Sema/ScopeInfo.h @@ -31,6 +31,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include @@ -202,6 +203,12 @@ /// function. SmallVector CompoundScopes; + /// The set of blocks that are introduced in this function. + llvm::SmallPtrSet Blocks; + + /// The set of __block variables that are introduced in this function. + llvm::TinyPtrVector ByrefBlockVars; + /// A list of PartialDiagnostics created but delayed within the /// current function scope. These diagnostics are vetted for reachability /// prior to being emitted. @@ -426,6 +433,16 @@ (HasBranchProtectedScope && HasBranchIntoScope)); } + // Add a block introduced in this function. + void addBlock(const BlockDecl *BD) { + Blocks.insert(BD); + } + + // Add a __block variable introduced in this function. + void addByrefBlockVar(VarDecl *VD) { + ByrefBlockVars.push_back(VD); + } + bool isCoroutine() const { return !FirstCoroutineStmtLoc.isInvalid(); } void setFirstCoroutineStmt(SourceLocation Loc, StringRef Keyword) { Index: cfe/trunk/lib/AST/Decl.cpp =================================================================== --- cfe/trunk/lib/AST/Decl.cpp +++ cfe/trunk/lib/AST/Decl.cpp @@ -2361,6 +2361,14 @@ return D; } +bool VarDecl::isEscapingByref() const { + return hasAttr() && NonParmVarDeclBits.EscapingByref; +} + +bool VarDecl::isNonEscapingByref() const { + return hasAttr() && !NonParmVarDeclBits.EscapingByref; +} + VarDecl *VarDecl::getTemplateInstantiationPattern() const { // If it's a variable template specialization, find the template or partial // specialization from which it was instantiated. Index: cfe/trunk/lib/CodeGen/CGBlocks.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGBlocks.cpp +++ cfe/trunk/lib/CodeGen/CGBlocks.cpp @@ -493,7 +493,11 @@ return CGF.BlockInfo->getCapture(VD).fieldType(); if (auto *FD = CGF.LambdaCaptureFields.lookup(VD)) return FD->getType(); - return VD->getType(); + // If the captured variable is a non-escaping __block variable, the field + // type is the reference type. If the variable is a __block variable that + // already has a reference type, the field type is the variable's type. + return VD->isNonEscapingByref() ? + CGF.getContext().getLValueReferenceType(VD->getType()) : VD->getType(); } /// Compute the layout of the given block. Attempts to lay the block @@ -549,7 +553,7 @@ for (const auto &CI : block->captures()) { const VarDecl *variable = CI.getVariable(); - if (CI.isByRef()) { + if (CI.isEscapingByref()) { // We have to copy/dispose of the __block reference. info.NeedsCopyDispose = true; @@ -1032,7 +1036,7 @@ // The lambda capture in a lambda's conversion-to-block-pointer is // special; we'll simply emit it directly. src = Address::invalid(); - } else if (CI.isByRef()) { + } else if (CI.isEscapingByref()) { if (BlockInfo && CI.isNested()) { // We need to use the capture from the enclosing block. const CGBlockInfo::Capture &enclosingCapture = @@ -1060,7 +1064,7 @@ // the block field. There's no need to chase the forwarding // pointer at this point, since we're building something that will // live a shorter life than the stack byref anyway. - if (CI.isByRef()) { + if (CI.isEscapingByref()) { // Get a void* that points to the byref struct. llvm::Value *byrefPointer; if (CI.isNested()) @@ -1279,8 +1283,7 @@ return EmitCall(FnInfo, Callee, ReturnValue, Args); } -Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable, - bool isByRef) { +Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable) { assert(BlockInfo && "evaluating block ref without block information?"); const CGBlockInfo::Capture &capture = BlockInfo->getCapture(variable); @@ -1291,7 +1294,7 @@ Builder.CreateStructGEP(LoadBlockStruct(), capture.getIndex(), capture.getOffset(), "block.capture.addr"); - if (isByRef) { + if (variable->isEscapingByref()) { // addr should be a void** right now. Load, then cast the result // to byref*. @@ -1305,6 +1308,10 @@ variable->getName()); } + assert((!variable->isNonEscapingByref() || + capture.fieldType()->isReferenceType()) && + "the capture field of a non-escaping variable should have a " + "reference type"); if (capture.fieldType()->isReferenceType()) addr = EmitLoadOfReference(MakeAddrLValue(addr, capture.fieldType())); @@ -1656,7 +1663,7 @@ return std::make_pair(BlockCaptureEntityKind::CXXRecord, BlockFieldFlags()); } BlockFieldFlags Flags; - if (CI.isByRef()) { + if (CI.isEscapingByref()) { Flags = BLOCK_FIELD_IS_BYREF; if (T.isObjCGCWeak()) Flags |= BLOCK_FIELD_IS_WEAK; @@ -2102,7 +2109,7 @@ static std::pair computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, const LangOptions &LangOpts) { - if (CI.isByRef()) { + if (CI.isEscapingByref()) { BlockFieldFlags Flags = BLOCK_FIELD_IS_BYREF; if (T.isObjCGCWeak()) Flags |= BLOCK_FIELD_IS_WEAK; @@ -2564,6 +2571,9 @@ CodeGenFunction::buildByrefHelpers(llvm::StructType &byrefType, const AutoVarEmission &emission) { const VarDecl &var = *emission.Variable; + assert(var.isEscapingByref() && + "only escaping __block variables need byref helpers"); + QualType type = var.getType(); auto &byrefInfo = getBlockByrefInfo(&var); Index: cfe/trunk/lib/CodeGen/CGClass.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGClass.cpp +++ cfe/trunk/lib/CodeGen/CGClass.cpp @@ -2839,7 +2839,7 @@ CallArgList CallArgs; QualType ThisType = getContext().getPointerType(getContext().getRecordType(Lambda)); - Address ThisPtr = GetAddrOfBlockDecl(variable, false); + Address ThisPtr = GetAddrOfBlockDecl(variable); CallArgs.add(RValue::get(ThisPtr.getPointer()), ThisType); // Add the rest of the parameters. Index: cfe/trunk/lib/CodeGen/CGDecl.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGDecl.cpp +++ cfe/trunk/lib/CodeGen/CGDecl.cpp @@ -1212,8 +1212,8 @@ AutoVarEmission emission(D); - bool isByRef = D.hasAttr(); - emission.IsByRef = isByRef; + bool isEscapingByRef = D.isEscapingByref(); + emission.IsEscapingByRef = isEscapingByRef; CharUnits alignment = getContext().getDeclAlign(&D); @@ -1252,8 +1252,8 @@ // in OpenCL. if ((!getLangOpts().OpenCL || Ty.getAddressSpace() == LangAS::opencl_constant) && - (CGM.getCodeGenOpts().MergeAllConstants && !NRVO && !isByRef && - CGM.isTypeConstant(Ty, true))) { + (CGM.getCodeGenOpts().MergeAllConstants && !NRVO && + !isEscapingByRef && CGM.isTypeConstant(Ty, true))) { EmitStaticVarDecl(D, llvm::GlobalValue::InternalLinkage); // Signal this condition to later callbacks. @@ -1305,7 +1305,7 @@ } else { CharUnits allocaAlignment; llvm::Type *allocaTy; - if (isByRef) { + if (isEscapingByRef) { auto &byrefInfo = getBlockByrefInfo(&D); allocaTy = byrefInfo.Type; allocaAlignment = byrefInfo.ByrefAlignment; @@ -1505,7 +1505,7 @@ } // Initialize the structure of a __block variable. - if (emission.IsByRef) + if (emission.IsEscapingByRef) emitByrefStructureInit(emission); // Initialize the variable here if it doesn't have a initializer and it is a @@ -1515,7 +1515,7 @@ type.isNonTrivialToPrimitiveDefaultInitialize() == QualType::PDIK_Struct) { LValue Dst = MakeAddrLValue(emission.getAllocatedAddress(), type); - if (emission.IsByRef) + if (emission.IsEscapingByRef) drillIntoBlockVariable(*this, Dst, &D); defaultInitNonTrivialCStructVar(Dst); return; @@ -1527,7 +1527,7 @@ // Check whether this is a byref variable that's potentially // captured and moved by its own initializer. If so, we'll need to // emit the initializer first, then copy into the variable. - bool capturedByInit = emission.IsByRef && isCapturedBy(D, Init); + bool capturedByInit = emission.IsEscapingByRef && isCapturedBy(D, Init); Address Loc = capturedByInit ? emission.Addr : emission.getObjectAddress(*this); @@ -1721,7 +1721,8 @@ // If this is a block variable, call _Block_object_destroy // (on the unforwarded address). Don't enter this cleanup if we're in pure-GC // mode. - if (emission.IsByRef && CGM.getLangOpts().getGC() != LangOptions::GCOnly) { + if (emission.IsEscapingByRef && + CGM.getLangOpts().getGC() != LangOptions::GCOnly) { BlockFieldFlags Flags = BLOCK_FIELD_IS_BYREF; if (emission.Variable->getType().isObjCGCWeak()) Flags |= BLOCK_FIELD_IS_WEAK; Index: cfe/trunk/lib/CodeGen/CGExpr.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGExpr.cpp +++ cfe/trunk/lib/CodeGen/CGExpr.cpp @@ -2486,7 +2486,7 @@ } assert(isa(CurCodeDecl)); - Address addr = GetAddrOfBlockDecl(VD, VD->hasAttr()); + Address addr = GetAddrOfBlockDecl(VD); return MakeAddrLValue(addr, T, AlignmentSource::Decl); } } @@ -2538,7 +2538,7 @@ } // Drill into block byref variables. - bool isBlockByref = VD->hasAttr(); + bool isBlockByref = VD->isEscapingByref(); if (isBlockByref) { addr = emitBlockByrefAddress(addr, VD); } Index: cfe/trunk/lib/CodeGen/CodeGenFunction.h =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.h +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h @@ -1787,7 +1787,7 @@ llvm::Value *ptr); Address LoadBlockStruct(); - Address GetAddrOfBlockDecl(const VarDecl *var, bool ByRef); + Address GetAddrOfBlockDecl(const VarDecl *var); /// BuildBlockByrefAddress - Computes the location of the /// data in a variable which is declared as __block. @@ -2683,8 +2683,9 @@ llvm::Value *NRVOFlag; - /// True if the variable is a __block variable. - bool IsByRef; + /// True if the variable is a __block variable that is captured by an + /// escaping block. + bool IsEscapingByRef; /// True if the variable is of aggregate type and has a constant /// initializer. @@ -2704,7 +2705,7 @@ AutoVarEmission(const VarDecl &variable) : Variable(&variable), Addr(Address::invalid()), NRVOFlag(nullptr), - IsByRef(false), IsConstantAggregate(false), + IsEscapingByRef(false), IsConstantAggregate(false), SizeForLifetimeMarkers(nullptr), AllocaAddr(Address::invalid()) {} bool wasEmittedAsGlobal() const { return !Addr.isValid(); } @@ -2734,7 +2735,7 @@ /// Note that this does not chase the forwarding pointer for /// __block decls. Address getObjectAddress(CodeGenFunction &CGF) const { - if (!IsByRef) return Addr; + if (!IsEscapingByRef) return Addr; return CGF.emitBlockByrefAddress(Addr, Variable, /*forward*/ false); } Index: cfe/trunk/lib/Sema/ScopeInfo.cpp =================================================================== --- cfe/trunk/lib/Sema/ScopeInfo.cpp +++ cfe/trunk/lib/Sema/ScopeInfo.cpp @@ -54,6 +54,8 @@ PossiblyUnreachableDiags.clear(); WeakObjectUses.clear(); ModifiedNonNullParams.clear(); + Blocks.clear(); + ByrefBlockVars.clear(); } static const NamedDecl *getBestPropertyDecl(const ObjCPropertyRefExpr *PropE) { Index: cfe/trunk/lib/Sema/Sema.cpp =================================================================== --- cfe/trunk/lib/Sema/Sema.cpp +++ cfe/trunk/lib/Sema/Sema.cpp @@ -1398,11 +1398,65 @@ "Remove assertion if intentionally called in a non-lambda context."); } +// Check that the type of the VarDecl has an accessible copy constructor and +// resolve its destructor's exception spefication. +static void checkEscapingByref(VarDecl *VD, Sema &S) { + QualType T = VD->getType(); + EnterExpressionEvaluationContext scope( + S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated); + SourceLocation Loc = VD->getLocation(); + Expr *VarRef = new (S.Context) DeclRefExpr(VD, false, T, VK_LValue, Loc); + ExprResult Result = S.PerformMoveOrCopyInitialization( + InitializedEntity::InitializeBlock(Loc, T, false), VD, VD->getType(), + VarRef, /*AllowNRVO=*/true); + if (!Result.isInvalid()) { + Result = S.MaybeCreateExprWithCleanups(Result); + Expr *Init = Result.getAs(); + S.Context.setBlockVarCopyInit(VD, Init, S.canThrow(Init)); + } + + // The destructor's exception spefication is needed when IRGen generates + // block copy/destroy functions. Resolve it here. + if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl()) + if (CXXDestructorDecl *DD = RD->getDestructor()) { + auto *FPT = DD->getType()->getAs(); + S.ResolveExceptionSpec(Loc, FPT); + } +} + +static void markEscapingByrefs(const FunctionScopeInfo &FSI, Sema &S) { + // Set the EscapingByref flag of __block variables captured by + // escaping blocks. + for (const BlockDecl *BD : FSI.Blocks) { + if (BD->doesNotEscape()) + continue; + for (const BlockDecl::Capture &BC : BD->captures()) { + VarDecl *VD = BC.getVariable(); + if (VD->hasAttr()) + VD->setEscapingByref(); + } + } + + for (VarDecl *VD : FSI.ByrefBlockVars) { + // __block variables might require us to capture a copy-initializer. + if (!VD->isEscapingByref()) + continue; + // It's currently invalid to ever have a __block variable with an + // array type; should we diagnose that here? + // Regardless, we don't want to ignore array nesting when + // constructing this copy. + if (VD->getType()->isStructureOrClassType()) + checkEscapingByref(VD, S); + } +} + void Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, const Decl *D, const BlockExpr *blkExpr) { assert(!FunctionScopes.empty() && "mismatched push/pop!"); FunctionScopeInfo *Scope = FunctionScopes.pop_back_val(); + markEscapingByrefs(*Scope, *this); + if (LangOpts.OpenMP) popOpenMPFunctionRegion(Scope); Index: cfe/trunk/lib/Sema/SemaDecl.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp +++ cfe/trunk/lib/Sema/SemaDecl.cpp @@ -11790,37 +11790,8 @@ QualType type = var->getType(); if (type->isDependentType()) return; - // __block variables might require us to capture a copy-initializer. - if (var->hasAttr()) { - // It's currently invalid to ever have a __block variable with an - // array type; should we diagnose that here? - - // Regardless, we don't want to ignore array nesting when - // constructing this copy. - if (type->isStructureOrClassType()) { - EnterExpressionEvaluationContext scope( - *this, ExpressionEvaluationContext::PotentiallyEvaluated); - SourceLocation poi = var->getLocation(); - Expr *varRef =new (Context) DeclRefExpr(var, false, type, VK_LValue, poi); - ExprResult result - = PerformMoveOrCopyInitialization( - InitializedEntity::InitializeBlock(poi, type, false), - var, var->getType(), varRef, /*AllowNRVO=*/true); - if (!result.isInvalid()) { - result = MaybeCreateExprWithCleanups(result); - Expr *init = result.getAs(); - Context.setBlockVarCopyInit(var, init, canThrow(init)); - } - - // The destructor's exception spefication is needed when IRGen generates - // block copy/destroy functions. Resolve it here. - if (const CXXRecordDecl *RD = type->getAsCXXRecordDecl()) - if (CXXDestructorDecl *DD = RD->getDestructor()) { - auto *FPT = DD->getType()->getAs(); - FPT = ResolveExceptionSpec(poi, FPT); - } - } - } + if (var->hasAttr()) + getCurFunction()->addByrefBlockVar(var); Expr *Init = var->getInit(); bool IsGlobal = GlobalStorage && !var->isStaticLocal(); Index: cfe/trunk/lib/Sema/SemaExpr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp +++ cfe/trunk/lib/Sema/SemaExpr.cpp @@ -13507,6 +13507,9 @@ } } + if (getCurFunction()) + getCurFunction()->addBlock(BSI->TheDecl); + return Result; } Index: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp =================================================================== --- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp +++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp @@ -1363,6 +1363,7 @@ VD->NonParmVarDeclBits.IsInitCapture = Record.readInt(); VD->NonParmVarDeclBits.PreviousDeclInSameBlockScope = Record.readInt(); VD->NonParmVarDeclBits.ImplicitParamKind = Record.readInt(); + VD->NonParmVarDeclBits.EscapingByref = Record.readInt(); } auto VarLinkage = Linkage(Record.readInt()); VD->setCachedLinkage(VarLinkage); Index: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp =================================================================== --- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp +++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp @@ -936,6 +936,7 @@ Record.push_back(static_cast(IPD->getParameterKind())); else Record.push_back(0); + Record.push_back(D->isEscapingByref()); } Record.push_back(D->getLinkageInternal()); @@ -1006,6 +1007,7 @@ !D->isInitCapture() && !D->isPreviousDeclInSameBlockScope() && !(D->hasAttr() && D->getType()->getAsCXXRecordDecl()) && + !D->isEscapingByref() && D->getStorageDuration() != SD_Static && !D->getMemberSpecializationInfo()) AbbrevToUse = Writer.getDeclVarAbbrev(); @@ -2057,6 +2059,7 @@ Abv->Add(BitCodeAbbrevOp(0)); // isInitCapture Abv->Add(BitCodeAbbrevOp(0)); // isPrevDeclInSameScope Abv->Add(BitCodeAbbrevOp(0)); // ImplicitParamKind + Abv->Add(BitCodeAbbrevOp(0)); // EscapingByref Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // IsInitICE (local) Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // VarKind (local enum) Index: cfe/trunk/test/CodeGen/block-byref-aggr.c =================================================================== --- cfe/trunk/test/CodeGen/block-byref-aggr.c +++ cfe/trunk/test/CodeGen/block-byref-aggr.c @@ -9,11 +9,13 @@ // cause a block copy. rdar://9309454 void test0() { __block Agg a = {100}; + ^{ (void)a; }; a = makeAgg(); } // CHECK-LABEL: define void @test0() // CHECK: [[A:%.*]] = alloca [[BYREF:%.*]], align 8 +// CHECK-NEXT: alloca <{ i8*, i32, i32, i8*, %{{.*}}*, i8* }>, align 8 // CHECK-NEXT: [[TEMP:%.*]] = alloca [[AGG]], align 4 // CHECK: [[RESULT:%.*]] = call i32 @makeAgg() // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[TEMP]], i32 0, i32 0 @@ -35,11 +37,13 @@ // rdar://11757470 void test1() { __block Agg a, b; + ^{ (void)a; (void)b; }; a = b = makeAgg(); } // CHECK-LABEL: define void @test1() // CHECK: [[A:%.*]] = alloca [[A_BYREF:%.*]], align 8 // CHECK-NEXT: [[B:%.*]] = alloca [[B_BYREF:%.*]], align 8 +// CHECK-NEXT: alloca <{ i8*, i32, i32, i8*, %{{.*}}*, i8*, i8* }>, align 8 // CHECK-NEXT: [[TEMP:%.*]] = alloca [[AGG]], align 4 // CHECK: [[RESULT:%.*]] = call i32 @makeAgg() // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[TEMP]], i32 0, i32 0 Index: cfe/trunk/test/CodeGen/blocks-seq.c =================================================================== --- cfe/trunk/test/CodeGen/blocks-seq.c +++ cfe/trunk/test/CodeGen/blocks-seq.c @@ -11,6 +11,7 @@ void foo() { __block int i; + ^{ (void)i; }; i = rhs(); i += rhs(); } Index: cfe/trunk/test/CodeGen/exceptions.c =================================================================== --- cfe/trunk/test/CodeGen/exceptions.c +++ cfe/trunk/test/CodeGen/exceptions.c @@ -23,6 +23,7 @@ void test2_helper(); void test2() { __block int x = 10; + ^{ (void)x; }; test2_helper(5, 6, 7); } void test2_helper(int x, int y) { Index: cfe/trunk/test/CodeGen/personality.c =================================================================== --- cfe/trunk/test/CodeGen/personality.c +++ cfe/trunk/test/CodeGen/personality.c @@ -26,6 +26,7 @@ void f(void) { __block int i; + ^{ (void)i; }; g(^ { }); } Index: cfe/trunk/test/CodeGenCXX/block-capture.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/block-capture.cpp +++ cfe/trunk/test/CodeGenCXX/block-capture.cpp @@ -4,10 +4,12 @@ // CHECK: [[baz:%[0-9a-z_]*]] = alloca %struct.__block_byref_baz // CHECK: [[bazref:%[0-9a-z_\.]*]] = getelementptr inbounds %struct.__block_byref_baz, %struct.__block_byref_baz* [[baz]], i32 0, i32 1 // CHECK: store %struct.__block_byref_baz* [[baz]], %struct.__block_byref_baz** [[bazref]] +// CHECK: bitcast %struct.__block_byref_baz* [[baz]] to i8* // CHECK: [[disposable:%[0-9a-z_]*]] = bitcast %struct.__block_byref_baz* [[baz]] to i8* // CHECK: call void @_Block_object_dispose(i8* [[disposable]] int main() { __block int baz = [&]() { return 0; }(); + ^{ (void)baz; }; return 0; } Index: cfe/trunk/test/CodeGenCXX/blocks.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/blocks.cpp +++ cfe/trunk/test/CodeGenCXX/blocks.cpp @@ -76,6 +76,7 @@ void test() { __block A a; __block B b; + ^{ (void)a; (void)b; }; } // CHECK-LABEL: define internal void @__Block_byref_object_copy Index: cfe/trunk/test/CodeGenCXX/debug-info-blocks.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/debug-info-blocks.cpp +++ cfe/trunk/test/CodeGenCXX/debug-info-blocks.cpp @@ -9,6 +9,7 @@ void test() { __block A a; + ^{ (void)a; }; } // CHECK: !DISubprogram(name: "__Block_byref_object_copy_", Index: cfe/trunk/test/CodeGenCXX/noescape.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/noescape.cpp +++ cfe/trunk/test/CodeGenCXX/noescape.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -fblocks -o - %s | FileCheck %s struct S { int a[4]; @@ -65,3 +65,32 @@ void test3(NoEscapeFunc f, int *p) { f(p); } + +namespace TestByref { + +struct S { + S(); + ~S(); + S(const S &); + int a; +}; + +typedef void (^BlockTy)(void); +S &getS(); +void noescapefunc(__attribute__((noescape)) BlockTy); + +// Check that __block variables with reference types are handled correctly. + +// CHECK: define void @_ZN9TestByref4testEv( +// CHECK: %[[X:.*]] = alloca %[[STRUCT_TESTBYREF:.*]]*, align 8 +// CHECK: %[[BLOCK:.*]] = alloca <{ i8*, i32, i32, i8*, %{{.*}}*, %[[STRUCT_TESTBYREF]]* }>, align 8 +// CHECK: %[[BLOCK_CAPTURED:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %{{.*}}*, %[[STRUCT_TESTBYREF]]* }>, <{ i8*, i32, i32, i8*, %{{.*}}*, %[[STRUCT_TESTBYREF]]* }>* %[[BLOCK]], i32 0, i32 5 +// CHECK: %[[V0:.*]] = load %[[STRUCT_TESTBYREF]]*, %[[STRUCT_TESTBYREF]]** %[[X]], align 8 +// CHECK: store %[[STRUCT_TESTBYREF]]* %[[V0]], %[[STRUCT_TESTBYREF]]** %[[BLOCK_CAPTURED]], align 8 + +void test() { + __block S &x = getS(); + noescapefunc(^{ (void)x; }); +} + +} Index: cfe/trunk/test/CodeGenObjC/arc-no-arc-exceptions.m =================================================================== --- cfe/trunk/test/CodeGenObjC/arc-no-arc-exceptions.m +++ cfe/trunk/test/CodeGenObjC/arc-no-arc-exceptions.m @@ -42,6 +42,7 @@ void test2(void) { @autoreleasepool { __attribute__((__blocks__(byref))) int x; + ^{ (void)x; }; NSLog(@"Address of x outside of block: %p", &x); } } Index: cfe/trunk/test/CodeGenObjC/arc-unoptimized-byref-var.m =================================================================== --- cfe/trunk/test/CodeGenObjC/arc-unoptimized-byref-var.m +++ cfe/trunk/test/CodeGenObjC/arc-unoptimized-byref-var.m @@ -3,6 +3,7 @@ void test19() { __block id x; + ^{ (void)x; }; // CHECK-UNOPT-LABEL: define internal void @__Block_byref_object_copy // CHECK-UNOPT: [[X:%.*]] = getelementptr inbounds [[BYREF_T:%.*]], [[BYREF_T:%.*]]* [[VAR:%.*]], i32 0, i32 6 // CHECK-UNOPT: [[X2:%.*]] = getelementptr inbounds [[BYREF_T:%.*]], [[BYREF_T:%.*]]* [[VAR1:%.*]], i32 0, i32 6 Index: cfe/trunk/test/CodeGenObjC/blocks-1.m =================================================================== --- cfe/trunk/test/CodeGenObjC/blocks-1.m +++ cfe/trunk/test/CodeGenObjC/blocks-1.m @@ -1,23 +1,31 @@ -// RUN: %clang_cc1 %s -emit-llvm -o %t -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -// RUN: grep "_Block_object_dispose" %t | count 6 -// RUN: grep "__copy_helper_block_" %t | count 4 -// RUN: grep "__destroy_helper_block_" %t | count 4 -// RUN: grep "__Block_byref_object_copy_" %t | count 2 -// RUN: grep "__Block_byref_object_dispose_" %t | count 2 -// RUN: not grep "i32 135)" %t -// RUN: grep "_Block_object_assign" %t | count 4 -// RUN: grep "objc_read_weak" %t | count 2 -// RUN: grep "objc_assign_weak" %t | count 3 -// RUN: %clang_cc1 -x objective-c++ %s -emit-llvm -o %t -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -// RUN: grep "_Block_object_dispose" %t | count 6 -// RUN: grep "__copy_helper_block_" %t | count 4 -// RUN: grep "__destroy_helper_block_" %t | count 4 -// RUN: grep "__Block_byref_object_copy_" %t | count 2 -// RUN: grep "__Block_byref_object_dispose_" %t | count 2 -// RUN: not grep "i32 135)" %t -// RUN: grep "_Block_object_assign" %t | count 4 -// RUN: grep "objc_read_weak" %t | count 2 -// RUN: grep "objc_assign_weak" %t | count 3 +// RUN: %clang_cc1 %s -emit-llvm -o - -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 | FileCheck %s --check-prefix=CHECK --check-prefix=OBJC +// RUN: %clang_cc1 -x objective-c++ %s -emit-llvm -o - -fobjc-gc -fblocks -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 | FileCheck %s --check-prefix=CHECK --check-prefix=OBJCXX + +// OBJC-LABEL: define void @test1( +// OBJCXX-LABEL: define void @_Z5test1P12NSDictionary( + +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_ +// CHECK: call void @_Block_object_assign( + +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_ +// CHECK: call void @_Block_object_dispose( + +// OBJC-LABEL: define void @foo( +// OBJCXX-LABEL: define void @_Z3foov( +// CHECK: call i8* @objc_read_weak( +// CHECK: call i8* @objc_assign_weak( +// CHECK: call void @_Block_object_dispose( + +// OBJC-LABEL: define void @test2( +// OBJCXX-LABEL: define void @_Z5test2v( +// CHECK: call i8* @objc_assign_weak( +// CHECK: call void @_Block_object_dispose( + +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_ +// CHECK: call void @_Block_object_assign( + +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_ +// CHECK: call void @_Block_object_dispose( @interface NSDictionary @end @@ -30,6 +38,7 @@ void foo() { __block __weak D *weakSelf; + ^{ (void)weakSelf; }; D *l; l = weakSelf; weakSelf = l; Index: cfe/trunk/test/CodeGenObjC/noescape.m =================================================================== --- cfe/trunk/test/CodeGenObjC/noescape.m +++ cfe/trunk/test/CodeGenObjC/noescape.m @@ -8,6 +8,7 @@ long long *ll; } __attribute__((transparent_union)); +void escapingFunc0(BlockTy); void noescapeFunc0(id, __attribute__((noescape)) BlockTy); void noescapeFunc1(__attribute__((noescape)) int *); void noescapeFunc2(__attribute__((noescape)) id); @@ -21,6 +22,9 @@ // When the block is non-escaping, copy/dispose helpers aren't generated, so the // block layout string must include information about __strong captures. +// CHECK-NOARC: %[[STRUCT_BLOCK_BYREF_B0:.*]] = type { i8*, %[[STRUCT_BLOCK_BYREF_B0]]*, i32, i32, i8*, %[[STRUCT_S0:.*]] } +// CHECK-ARC: %[[STRUCT_BLOCK_BYREF_B0:.*]] = type { i8*, %[[STRUCT_BLOCK_BYREF_B0]]*, i32, i32, i8*, i8*, i8*, %[[STRUCT_S0:.*]] } +// CHECK: %[[STRUCT_S0]] = type { i8*, i8* } // CHECK: @[[BLOCK_DESCIPTOR_TMP_2:.*ls32l8"]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i64 } { i64 0, i64 40, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8 // CHECK-LABEL: define void @test0( @@ -102,7 +106,7 @@ // CHECK-NOARC: %[[V1:.*]] = load i8*, i8** %[[B_ADDR]], align 8 // CHECK-NOARC: store i8* %[[V1]], i8** %[[BLOCK_CAPTURED]], align 8 // CHECK-ARC: %[[V2:.*]] = load i8*, i8** %[[B_ADDR]], align 8 -// CHECK-ARC: %[[V3:.*]] = call i8* @objc_retain(i8* %[[V2]]) #3 +// CHECK-ARC: %[[V3:.*]] = call i8* @objc_retain(i8* %[[V2]]) // CHECK-ARC: store i8* %[[V3]], i8** %[[BLOCK_CAPTURED]], align 8 // CHECK: call void @noescapeFunc0( // CHECK-ARC: call void @objc_storeStrong(i8** %[[V0]], i8* null) @@ -118,3 +122,49 @@ void test6(id a, id b) { noescapeFunc0(a, ^{ func(b); }); } + +// We don't need either the byref helper functions or the byref structs for +// __block variables that are not captured by escaping blocks. + +// CHECK: define void @test7( +// CHECK: alloca i8*, align 8 +// CHECK: %[[B0:.*]] = alloca i8*, align 8 +// CHECK: %[[BLOCK:.*]] = alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8** }>, align 8 +// CHECK: %[[BLOCK_CAPTURED:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8** }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8** }>* %[[BLOCK]], i32 0, i32 5 +// CHECK: store i8** %[[B0]], i8*** %[[BLOCK_CAPTURED]], align 8 + +// CHECK-ARC-NOT: define internal void @__Block_byref_object_copy_ +// CHECK-ARC-NOT: define internal void @__Block_byref_object_dispose_ + +void test7() { + id a; + __block id b0; + noescapeFunc0(a, ^{ (void)b0; }); +} + +// __block variables captured by escaping blocks need byref helper functions. + +// CHECK: define void @test8( +// CHECK: %[[A:.*]] = alloca i8*, align 8 +// CHECK: %[[B0:.*]] = alloca %[[STRUCT_BLOCK_BYREF_B0]], align 8 +// CHECK: alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, align 8 +// CHECK: %[[BLOCK1:.*]] = alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, align 8 +// CHECK: %[[BLOCK_CAPTURED7:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %[[BLOCK1]], i32 0, i32 5 +// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_BLOCK_BYREF_B0]]* %[[B0]] to i8* +// CHECK: store i8* %[[V3]], i8** %[[BLOCK_CAPTURED7]], align 8 + +// CHECK-ARC: define internal void @__Block_byref_object_copy_ +// CHECK-ARC: define internal void @__Block_byref_object_dispose_ +// CHECK: define linkonce_odr hidden void @__copy_helper_block_ +// CHECK: define linkonce_odr hidden void @__destroy_helper_block_ + +struct S0 { + id a, b; +}; + +void test8() { + id a; + __block struct S0 b0; + noescapeFunc0(a, ^{ (void)b0; }); + escapingFunc0(^{ (void)b0; }); +} Index: cfe/trunk/test/CodeGenObjCXX/arc-blocks.mm =================================================================== --- cfe/trunk/test/CodeGenObjCXX/arc-blocks.mm +++ cfe/trunk/test/CodeGenObjCXX/arc-blocks.mm @@ -3,9 +3,9 @@ // 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_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 } // 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" @@ -20,6 +20,7 @@ void foo() { __block A v; + ^{ (void)v; }; } // CHECK-LABEL: define void @_ZN5test03fooEv() // CHECK: [[V:%.*]] = alloca [[BYREF_A:%.*]], align 8 @@ -32,7 +33,8 @@ // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[BYREF_A]], [[BYREF_A]]* [[V]], i32 0, i32 7 // CHECK-NEXT: call void @_ZN5test01AC1Ev([[A]]* [[T0]]) // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[BYREF_A]], [[BYREF_A]]* [[V]], i32 0, i32 7 - // CHECK-NEXT: [[T1:%.*]] = bitcast [[BYREF_A]]* [[V]] to i8* + // CHECK: bitcast [[BYREF_A]]* [[V]] to i8* + // CHECK: [[T1:%.*]] = bitcast [[BYREF_A]]* [[V]] to i8* // CHECK-NEXT: call void @_Block_object_dispose(i8* [[T1]], i32 8) // CHECK-NEXT: call void @_ZN5test01AD1Ev([[A]]* [[T0]]) // CHECK-NEXT: ret void @@ -53,6 +55,11 @@ // CHECK-NEXT: ret void } +// CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_ +// CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_ +// CHECK-LABEL-O1: define linkonce_odr hidden void @__copy_helper_block_ +// CHECK-LABEL-O1: define linkonce_odr hidden void @__destroy_helper_block_ + namespace test1 { // Check that copy/dispose helper functions are exception safe. Index: cfe/trunk/test/PCH/block-helpers.cpp =================================================================== --- cfe/trunk/test/PCH/block-helpers.cpp +++ cfe/trunk/test/PCH/block-helpers.cpp @@ -1,6 +1,26 @@ // 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 +// CHECK: %[[STRUCT_BLOCK_BYREF_X:.*]] = type { i8*, %[[STRUCT_BLOCK_BYREF_X]]*, i32, i32, i8*, i8*, %[[STRUCT_S0:.*]] } +// CHECK: %[[STRUCT_S0]] = type { i32 } +// CHECK: %[[STRUCT_BLOCK_BYREF_Y:.*]] = type { i8*, %[[STRUCT_BLOCK_BYREF_Y]]*, i32, i32, i8*, i8*, %[[STRUCT_S0]] } +// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 } + +// Check that byref structs are allocated for x and y. + +// CHECK-LABEL: define linkonce_odr void @_ZN1S1mEv( +// CHECK: %[[X:.*]] = alloca %[[STRUCT_BLOCK_BYREF_X]], align 8 +// CHECK: %[[Y:.*]] = alloca %[[STRUCT_BLOCK_BYREF_Y]], align 8 +// CHECK: %[[BLOCK:.*]] = alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>, align 8 +// CHECK: %[[BLOCK_CAPTURED:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>* %[[BLOCK]], i32 0, i32 5 +// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_BLOCK_BYREF_X]]* %[[X]] to i8* +// CHECK: store i8* %[[V0]], i8** %[[BLOCK_CAPTURED]], align 8 +// CHECK: %[[BLOCK_CAPTURED10:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i8* }>* %[[BLOCK]], i32 0, i32 6 +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_BLOCK_BYREF_Y]]* %[[Y]] to i8* +// CHECK: store i8* %[[V1]], i8** %[[BLOCK_CAPTURED10]], align 8 + +// CHECK-LABEL: define internal void @___ZN1S1mEv_block_invoke( + // The second call to block_object_assign should be an invoke. // CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_e8_32rc40rc( Index: cfe/trunk/test/SemaObjCXX/blocks.mm =================================================================== --- cfe/trunk/test/SemaObjCXX/blocks.mm +++ cfe/trunk/test/SemaObjCXX/blocks.mm @@ -76,21 +76,27 @@ } // Make sure we successfully instantiate the copy constructor of a -// __block variable's type. +// __block variable's type when the variable is captured by an escaping block. namespace N2 { template struct A { A() {} A(const A &other) { int invalid[-n]; // expected-error 2 {{array with a negative size}} } + void m() {} }; + typedef void (^BlockFnTy)(); + void func(BlockFnTy); + void test1() { __block A<1> x; // expected-note {{requested here}} + func(^{ x.m(); }); } template void test2() { __block A x; // expected-note {{requested here}} + func(^{ x.m(); }); } template void test2<2>(); } Index: cfe/trunk/test/SemaObjCXX/noescape.mm =================================================================== --- cfe/trunk/test/SemaObjCXX/noescape.mm +++ cfe/trunk/test/SemaObjCXX/noescape.mm @@ -8,6 +8,7 @@ void m(); }; +void escapingFunc0(BlockTy); void noescapeFunc0(id, __attribute__((noescape)) BlockTy); void noescapeFunc1(id, [[clang::noescape]] BlockTy); void noescapeFunc2(__attribute__((noescape)) int *); // expected-note {{previous declaration is here}} @@ -127,3 +128,27 @@ -(void) m1:(int*) p { } @end + +struct S6 { + S6(); + S6(const S6 &) = delete; // expected-note 3 {{'S6' has been explicitly marked deleted here}} + int f; +}; + +void test1() { + id a; + // __block variables that are not captured by escaping blocks don't + // necessitate having accessible copy constructors. + __block S6 b0; + __block S6 b1; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b2; // expected-error {{call to deleted constructor of 'S6'}} + __block S6 b3; // expected-error {{call to deleted constructor of 'S6'}} + + noescapeFunc0(a, ^{ (void)b0; }); + escapingFunc0(^{ (void)b1; }); + { + noescapeFunc0(a, ^{ (void)b0; (void)b1; }); + } + noescapeFunc0(a, ^{ escapingFunc0(^{ (void)b2; }); }); + escapingFunc0(^{ noescapeFunc0(a, ^{ (void)b3; }); }); +}