From 8e57b07f668528fa4ed893093342ba8d899a1556 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Mon, 1 Oct 2018 21:51:28 +0000 Subject: [PATCH] Distinguish `__block` variables that are captured by escaping blocks from those that aren't. This patch changes the way __block variables that aren't captured by escaping blocks are handled: - Since non-escaping blocks on the stack never get copied to the heap (see https://reviews.llvm.org/D49303), Sema shouldn't error out when the type of a non-escaping __block variable doesn't have an accessible copy constructor. - IRGen doesn't have to use the specialized byref structure (see https://clang.llvm.org/docs/Block-ABI-Apple.html#id8) for a non-escaping __block variable anymore. Instead IRGen can emit the variable as a normal variable and copy the reference to the block literal. Byref copy/dispose helpers aren't needed either. This reapplies r343518 after fixing a use-after-free bug in function Sema::ActOnBlockStmtExpr where the BlockScopeInfo was dereferenced after it was popped and deleted. rdar://problem/39352313 Differential Revision: https://reviews.llvm.org/D51564 llvm-svn: 343542 --- clang/include/clang/AST/Decl.h | 23 +++++++++ clang/include/clang/Sema/ScopeInfo.h | 17 +++++++ clang/lib/AST/Decl.cpp | 8 +++ clang/lib/CodeGen/CGBlocks.cpp | 28 ++++++---- clang/lib/CodeGen/CGClass.cpp | 2 +- clang/lib/CodeGen/CGDecl.cpp | 19 +++---- clang/lib/CodeGen/CGExpr.cpp | 4 +- clang/lib/CodeGen/CodeGenFunction.h | 11 ++-- clang/lib/Sema/ScopeInfo.cpp | 2 + clang/lib/Sema/Sema.cpp | 59 ++++++++++++++++++++++ clang/lib/Sema/SemaDecl.cpp | 33 +----------- clang/lib/Sema/SemaExpr.cpp | 18 ++++--- clang/lib/Serialization/ASTReaderDecl.cpp | 1 + clang/lib/Serialization/ASTWriterDecl.cpp | 3 ++ clang/test/CodeGen/block-byref-aggr.c | 4 ++ clang/test/CodeGen/blocks-seq.c | 1 + clang/test/CodeGen/exceptions.c | 1 + clang/test/CodeGen/personality.c | 1 + clang/test/CodeGenCXX/block-capture.cpp | 2 + clang/test/CodeGenCXX/blocks.cpp | 1 + clang/test/CodeGenCXX/debug-info-blocks.cpp | 1 + clang/test/CodeGenCXX/noescape.cpp | 31 +++++++++++- clang/test/CodeGenObjC/arc-no-arc-exceptions.m | 1 + clang/test/CodeGenObjC/arc-unoptimized-byref-var.m | 1 + clang/test/CodeGenObjC/blocks-1.m | 49 ++++++++++-------- clang/test/CodeGenObjC/noescape.m | 52 ++++++++++++++++++- clang/test/CodeGenObjCXX/arc-blocks.mm | 11 +++- clang/test/PCH/block-helpers.cpp | 20 ++++++++ clang/test/SemaObjCXX/blocks.mm | 8 ++- clang/test/SemaObjCXX/noescape.mm | 25 +++++++++ 30 files changed, 348 insertions(+), 89 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 2b6e111..076bd6e 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -965,6 +965,8 @@ protected: /// Defines kind of the ImplicitParamDecl: 'this', 'self', 'vtt', '_cmd' or /// something else. unsigned ImplicitParamKind : 3; + + unsigned EscapingByref : 1; }; union { @@ -1407,6 +1409,19 @@ public: 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; @@ -3865,6 +3880,14 @@ public: /// 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; } diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 5925fd6..e09a68a 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/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 @@ public: /// 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 @@ public: (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) { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 272e49a..5a64c67 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2361,6 +2361,14 @@ static DeclT *getDefinitionOrSelf(DeclT *D) { 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. diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index dae148c..7230e4e 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -493,7 +493,11 @@ static QualType getCaptureFieldType(const CodeGenFunction &CGF, 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 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF, 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 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { // 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 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { // 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 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E, 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 @@ Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable, 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 @@ Address CodeGenFunction::GetAddrOfBlockDecl(const VarDecl *variable, 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 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T, 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 @@ getBlockFieldFlagsForObjCObjectPointer(const BlockDecl::Capture &CI, 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 @@ BlockByrefHelpers * 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); diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 468d81c..aca0fd6 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2839,7 +2839,7 @@ void CodeGenFunction::EmitLambdaBlockInvokeBody() { 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. diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index e74066e..e4f5d43 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -1125,8 +1125,8 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { AutoVarEmission emission(D); - bool isByRef = D.hasAttr(); - emission.IsByRef = isByRef; + bool isEscapingByRef = D.isEscapingByref(); + emission.IsEscapingByRef = isEscapingByRef; CharUnits alignment = getContext().getDeclAlign(&D); @@ -1165,8 +1165,8 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { // 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. @@ -1218,7 +1218,7 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { } else { CharUnits allocaAlignment; llvm::Type *allocaTy; - if (isByRef) { + if (isEscapingByRef) { auto &byrefInfo = getBlockByrefInfo(&D); allocaTy = byrefInfo.Type; allocaAlignment = byrefInfo.ByrefAlignment; @@ -1418,7 +1418,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { } // 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 @@ -1428,7 +1428,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { type.isNonTrivialToPrimitiveDefaultInitialize() == QualType::PDIK_Struct) { LValue Dst = MakeAddrLValue(emission.getAllocatedAddress(), type); - if (emission.IsByRef) + if (emission.IsEscapingByRef) drillIntoBlockVariable(*this, Dst, &D); defaultInitNonTrivialCStructVar(Dst); return; @@ -1440,7 +1440,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { // 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); @@ -1634,7 +1634,8 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) { // 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; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index b74937e..028aa96 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -2486,7 +2486,7 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { } assert(isa(CurCodeDecl)); - Address addr = GetAddrOfBlockDecl(VD, VD->hasAttr()); + Address addr = GetAddrOfBlockDecl(VD); return MakeAddrLValue(addr, T, AlignmentSource::Decl); } } @@ -2538,7 +2538,7 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { } // Drill into block byref variables. - bool isBlockByref = VD->hasAttr(); + bool isBlockByref = VD->isEscapingByref(); if (isBlockByref) { addr = emitBlockByrefAddress(addr, VD); } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 6ea2d75..54289cd 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -1787,7 +1787,7 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: /// 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); } diff --git a/clang/lib/Sema/ScopeInfo.cpp b/clang/lib/Sema/ScopeInfo.cpp index 62a83cc..bd8db6f 100644 --- a/clang/lib/Sema/ScopeInfo.cpp +++ b/clang/lib/Sema/ScopeInfo.cpp @@ -54,6 +54,8 @@ void FunctionScopeInfo::Clear() { PossiblyUnreachableDiags.clear(); WeakObjectUses.clear(); ModifiedNonNullParams.clear(); + Blocks.clear(); + ByrefBlockVars.clear(); } static const NamedDecl *getBestPropertyDecl(const ObjCPropertyRefExpr *PropE) { diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index d777afe..ccbbe50 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1401,9 +1401,68 @@ void Sema::RecordParsingTemplateParameterDepth(unsigned Depth) { "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!"); + + // This function shouldn't be called after popping the current function scope. + // markEscapingByrefs calls PerformMoveOrCopyInitialization, which can call + // PushFunctionScope, which can cause clearing out PreallocatedFunctionScope + // when FunctionScopes is empty. + markEscapingByrefs(*FunctionScopes.back(), *this); + FunctionScopeInfo *Scope = FunctionScopes.pop_back_val(); if (LangOpts.OpenMP) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index f5ba87f..441c54d 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11826,37 +11826,8 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { 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(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 26fb107..cb4b6f1 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -13428,6 +13428,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, PopExpressionEvaluationContext(); BlockScopeInfo *BSI = cast(FunctionScopes.back()); + BlockDecl *BD = BSI->TheDecl; if (BSI->HasImplicitReturnType) deduceClosureReturnType(*BSI); @@ -13438,7 +13439,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, if (!BSI->ReturnType.isNull()) RetTy = BSI->ReturnType; - bool NoReturn = BSI->TheDecl->hasAttr(); + bool NoReturn = BD->hasAttr(); QualType BlockTy; // Set the captured variables on the block. @@ -13451,7 +13452,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, Cap.isNested(), Cap.getInitExpr()); Captures.push_back(NewCap); } - BSI->TheDecl->setCaptures(Context, Captures, BSI->CXXThisCaptureIndex != 0); + BD->setCaptures(Context, Captures, BSI->CXXThisCaptureIndex != 0); // If the user wrote a function type in some form, try to use that. if (!BSI->FunctionType.isNull()) { @@ -13488,7 +13489,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, BlockTy = Context.getFunctionType(RetTy, None, EPI); } - DiagnoseUnusedParameters(BSI->TheDecl->parameters()); + DiagnoseUnusedParameters(BD->parameters()); BlockTy = Context.getBlockPointerType(BlockTy); // If needed, diagnose invalid gotos and switches in the block. @@ -13496,19 +13497,19 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, !PP.isCodeCompletionEnabled()) DiagnoseInvalidJumps(cast(Body)); - BSI->TheDecl->setBody(cast(Body)); + BD->setBody(cast(Body)); if (Body && getCurFunction()->HasPotentialAvailabilityViolations) - DiagnoseUnguardedAvailabilityViolations(BSI->TheDecl); + DiagnoseUnguardedAvailabilityViolations(BD); // Try to apply the named return value optimization. We have to check again // if we can do this, though, because blocks keep return statements around // to deduce an implicit return type. if (getLangOpts().CPlusPlus && RetTy->isRecordType() && - !BSI->TheDecl->isDependentContext()) + !BD->isDependentContext()) computeNRVO(Body, BSI); - BlockExpr *Result = new (Context) BlockExpr(BSI->TheDecl, BlockTy); + BlockExpr *Result = new (Context) BlockExpr(BD, BlockTy); AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy(); PopFunctionScopeInfo(&WP, Result->getBlockDecl(), Result); @@ -13530,6 +13531,9 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, } } + if (getCurFunction()) + getCurFunction()->addBlock(BD); + return Result; } diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 6d541aa..d22a98c 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1365,6 +1365,7 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) { 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); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index e120f14..5ec11dd 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -938,6 +938,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { Record.push_back(static_cast(IPD->getParameterKind())); else Record.push_back(0); + Record.push_back(D->isEscapingByref()); } Record.push_back(D->getLinkageInternal()); @@ -1008,6 +1009,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { !D->isInitCapture() && !D->isPreviousDeclInSameBlockScope() && !(D->hasAttr() && D->getType()->getAsCXXRecordDecl()) && + !D->isEscapingByref() && D->getStorageDuration() != SD_Static && !D->getMemberSpecializationInfo()) AbbrevToUse = Writer.getDeclVarAbbrev(); @@ -2072,6 +2074,7 @@ void ASTWriter::WriteDeclAbbrevs() { 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) diff --git a/clang/test/CodeGen/block-byref-aggr.c b/clang/test/CodeGen/block-byref-aggr.c index 962f1006..db10576 100644 --- a/clang/test/CodeGen/block-byref-aggr.c +++ b/clang/test/CodeGen/block-byref-aggr.c @@ -9,11 +9,13 @@ Agg makeAgg(void); // 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 @@ void test0() { // 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 diff --git a/clang/test/CodeGen/blocks-seq.c b/clang/test/CodeGen/blocks-seq.c index b3e6729..9b47fbf 100644 --- a/clang/test/CodeGen/blocks-seq.c +++ b/clang/test/CodeGen/blocks-seq.c @@ -11,6 +11,7 @@ int rhs(); void foo() { __block int i; + ^{ (void)i; }; i = rhs(); i += rhs(); } diff --git a/clang/test/CodeGen/exceptions.c b/clang/test/CodeGen/exceptions.c index 039ec84..57b8691 100644 --- a/clang/test/CodeGen/exceptions.c +++ b/clang/test/CodeGen/exceptions.c @@ -23,6 +23,7 @@ void test1() { void test2_helper(); void test2() { __block int x = 10; + ^{ (void)x; }; test2_helper(5, 6, 7); } void test2_helper(int x, int y) { diff --git a/clang/test/CodeGen/personality.c b/clang/test/CodeGen/personality.c index 1c533ce..59a6a9c 100644 --- a/clang/test/CodeGen/personality.c +++ b/clang/test/CodeGen/personality.c @@ -26,6 +26,7 @@ extern void i(void); void f(void) { __block int i; + ^{ (void)i; }; g(^ { }); } diff --git a/clang/test/CodeGenCXX/block-capture.cpp b/clang/test/CodeGenCXX/block-capture.cpp index 6238383..515d64d 100644 --- a/clang/test/CodeGenCXX/block-capture.cpp +++ b/clang/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; } diff --git a/clang/test/CodeGenCXX/blocks.cpp b/clang/test/CodeGenCXX/blocks.cpp index 32b1dd8..3b3363d 100644 --- a/clang/test/CodeGenCXX/blocks.cpp +++ b/clang/test/CodeGenCXX/blocks.cpp @@ -76,6 +76,7 @@ namespace test2 { void test() { __block A a; __block B b; + ^{ (void)a; (void)b; }; } // CHECK-LABEL: define internal void @__Block_byref_object_copy diff --git a/clang/test/CodeGenCXX/debug-info-blocks.cpp b/clang/test/CodeGenCXX/debug-info-blocks.cpp index fd2f0c9..f47ade7 100644 --- a/clang/test/CodeGenCXX/debug-info-blocks.cpp +++ b/clang/test/CodeGenCXX/debug-info-blocks.cpp @@ -9,6 +9,7 @@ struct A { void test() { __block A a; + ^{ (void)a; }; } // CHECK: !DISubprogram(name: "__Block_byref_object_copy_", diff --git a/clang/test/CodeGenCXX/noescape.cpp b/clang/test/CodeGenCXX/noescape.cpp index 90d24de..40bc839 100644 --- a/clang/test/CodeGenCXX/noescape.cpp +++ b/clang/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 @@ typedef void (*NoEscapeFunc)(__attribute__((noescape)) int *); 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; }); +} + +} diff --git a/clang/test/CodeGenObjC/arc-no-arc-exceptions.m b/clang/test/CodeGenObjC/arc-no-arc-exceptions.m index 3338ad8..31d1dd5 100644 --- a/clang/test/CodeGenObjC/arc-no-arc-exceptions.m +++ b/clang/test/CodeGenObjC/arc-no-arc-exceptions.m @@ -42,6 +42,7 @@ void NSLog(id, ...); void test2(void) { @autoreleasepool { __attribute__((__blocks__(byref))) int x; + ^{ (void)x; }; NSLog(@"Address of x outside of block: %p", &x); } } diff --git a/clang/test/CodeGenObjC/arc-unoptimized-byref-var.m b/clang/test/CodeGenObjC/arc-unoptimized-byref-var.m index 9d856d6..ffc73a4 100644 --- a/clang/test/CodeGenObjC/arc-unoptimized-byref-var.m +++ b/clang/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 diff --git a/clang/test/CodeGenObjC/blocks-1.m b/clang/test/CodeGenObjC/blocks-1.m index 0d2c350..cecb55d 100644 --- a/clang/test/CodeGenObjC/blocks-1.m +++ b/clang/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 test1(NSDictionary * dict) { void foo() { __block __weak D *weakSelf; + ^{ (void)weakSelf; }; D *l; l = weakSelf; weakSelf = l; diff --git a/clang/test/CodeGenObjC/noescape.m b/clang/test/CodeGenObjC/noescape.m index b4c8bf7..c7624f2 100644 --- a/clang/test/CodeGenObjC/noescape.m +++ b/clang/test/CodeGenObjC/noescape.m @@ -8,6 +8,7 @@ union U { 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 @@ void noescapeFunc3(__attribute__((noescape)) union U); // 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 @@ void test5(BlockTy2 b, int *p) { // 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 func(id); 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; }); +} diff --git a/clang/test/CodeGenObjCXX/arc-blocks.mm b/clang/test/CodeGenObjCXX/arc-blocks.mm index ef0561b..4791aff 100644 --- a/clang/test/CodeGenObjCXX/arc-blocks.mm +++ b/clang/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 @@ namespace test0 { void foo() { __block A v; + ^{ (void)v; }; } // CHECK-LABEL: define void @_ZN5test03fooEv() // CHECK: [[V:%.*]] = alloca [[BYREF_A:%.*]], align 8 @@ -32,7 +33,8 @@ namespace test0 { // 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 @@ namespace test0 { // 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. diff --git a/clang/test/PCH/block-helpers.cpp b/clang/test/PCH/block-helpers.cpp index 046bbb4..02d4cee 100644 --- a/clang/test/PCH/block-helpers.cpp +++ b/clang/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( diff --git a/clang/test/SemaObjCXX/blocks.mm b/clang/test/SemaObjCXX/blocks.mm index bdd5538..5f73524 100644 --- a/clang/test/SemaObjCXX/blocks.mm +++ b/clang/test/SemaObjCXX/blocks.mm @@ -76,21 +76,27 @@ namespace N1 { } // 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>(); } diff --git a/clang/test/SemaObjCXX/noescape.mm b/clang/test/SemaObjCXX/noescape.mm index 9432a3a..efa2a76 100644 --- a/clang/test/SemaObjCXX/noescape.mm +++ b/clang/test/SemaObjCXX/noescape.mm @@ -8,6 +8,7 @@ struct S { 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 @@ __attribute__((objc_root_class)) -(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; }); }); +} -- 2.7.4