r343542 - Distinguish `__block` variables that are captured by escaping blocks

Akira Hatanaka via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 1 14:51:28 PDT 2018


Author: ahatanak
Date: Mon Oct  1 14:51:28 2018
New Revision: 343542

URL: http://llvm.org/viewvc/llvm-project?rev=343542&view=rev
Log:
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

Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/Sema/ScopeInfo.h
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/CodeGen/CGBlocks.cpp
    cfe/trunk/lib/CodeGen/CGClass.cpp
    cfe/trunk/lib/CodeGen/CGDecl.cpp
    cfe/trunk/lib/CodeGen/CGExpr.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/lib/Sema/ScopeInfo.cpp
    cfe/trunk/lib/Sema/Sema.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
    cfe/trunk/test/CodeGen/block-byref-aggr.c
    cfe/trunk/test/CodeGen/blocks-seq.c
    cfe/trunk/test/CodeGen/exceptions.c
    cfe/trunk/test/CodeGen/personality.c
    cfe/trunk/test/CodeGenCXX/block-capture.cpp
    cfe/trunk/test/CodeGenCXX/blocks.cpp
    cfe/trunk/test/CodeGenCXX/debug-info-blocks.cpp
    cfe/trunk/test/CodeGenCXX/noescape.cpp
    cfe/trunk/test/CodeGenObjC/arc-no-arc-exceptions.m
    cfe/trunk/test/CodeGenObjC/arc-unoptimized-byref-var.m
    cfe/trunk/test/CodeGenObjC/blocks-1.m
    cfe/trunk/test/CodeGenObjC/noescape.m
    cfe/trunk/test/CodeGenObjCXX/arc-blocks.mm
    cfe/trunk/test/PCH/block-helpers.cpp
    cfe/trunk/test/SemaObjCXX/blocks.mm
    cfe/trunk/test/SemaObjCXX/noescape.mm

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Mon Oct  1 14:51:28 2018
@@ -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; }

Modified: cfe/trunk/include/clang/Sema/ScopeInfo.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ScopeInfo.h?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/ScopeInfo.h (original)
+++ cfe/trunk/include/clang/Sema/ScopeInfo.h Mon Oct  1 14:51:28 2018
@@ -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 <algorithm>
@@ -202,6 +203,12 @@ public:
   /// function.
   SmallVector<CompoundScopeInfo, 4> CompoundScopes;
 
+  /// The set of blocks that are introduced in this function.
+  llvm::SmallPtrSet<const BlockDecl *, 1> Blocks;
+
+  /// The set of __block variables that are introduced in this function.
+  llvm::TinyPtrVector<VarDecl *> 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) {

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Mon Oct  1 14:51:28 2018
@@ -2361,6 +2361,14 @@ static DeclT *getDefinitionOrSelf(DeclT
   return D;
 }
 
+bool VarDecl::isEscapingByref() const {
+  return hasAttr<BlocksAttr>() && NonParmVarDeclBits.EscapingByref;
+}
+
+bool VarDecl::isNonEscapingByref() const {
+  return hasAttr<BlocksAttr>() && !NonParmVarDeclBits.EscapingByref;
+}
+
 VarDecl *VarDecl::getTemplateInstantiationPattern() const {
   // If it's a variable template specialization, find the template or partial
   // specialization from which it was instantiated.

Modified: cfe/trunk/lib/CodeGen/CGBlocks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGBlocks.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGBlocks.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGBlocks.cpp Mon Oct  1 14:51:28 2018
@@ -493,7 +493,11 @@ static QualType getCaptureFieldType(cons
     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(CodeGenModu
   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::EmitBlockL
       // 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::EmitBlockL
     // 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::EmitBlockCallExp
   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::GetAddrOfBlockD
     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::GetAddrOfBlockD
                                  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 Blo
     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(c
 static std::pair<BlockCaptureEntityKind, BlockFieldFlags>
 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);

Modified: cfe/trunk/lib/CodeGen/CGClass.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Mon Oct  1 14:51:28 2018
@@ -2839,7 +2839,7 @@ void CodeGenFunction::EmitLambdaBlockInv
   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.

Modified: cfe/trunk/lib/CodeGen/CGDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGDecl.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGDecl.cpp Mon Oct  1 14:51:28 2018
@@ -1125,8 +1125,8 @@ CodeGenFunction::EmitAutoVarAlloca(const
 
   AutoVarEmission emission(D);
 
-  bool isByRef = D.hasAttr<BlocksAttr>();
-  emission.IsByRef = isByRef;
+  bool isEscapingByRef = D.isEscapingByref();
+  emission.IsEscapingByRef = isEscapingByRef;
 
   CharUnits alignment = getContext().getDeclAlign(&D);
 
@@ -1165,8 +1165,8 @@ CodeGenFunction::EmitAutoVarAlloca(const
       // 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
     } 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(co
   }
 
   // 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(co
       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(co
   // 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::EmitAutoVarCleanup
   // 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;

Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExpr.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExpr.cpp Mon Oct  1 14:51:28 2018
@@ -2486,7 +2486,7 @@ LValue CodeGenFunction::EmitDeclRefLValu
       }
 
       assert(isa<BlockDecl>(CurCodeDecl));
-      Address addr = GetAddrOfBlockDecl(VD, VD->hasAttr<BlocksAttr>());
+      Address addr = GetAddrOfBlockDecl(VD);
       return MakeAddrLValue(addr, T, AlignmentSource::Decl);
     }
   }
@@ -2538,7 +2538,7 @@ LValue CodeGenFunction::EmitDeclRefLValu
     }
 
     // Drill into block byref variables.
-    bool isBlockByref = VD->hasAttr<BlocksAttr>();
+    bool isBlockByref = VD->isEscapingByref();
     if (isBlockByref) {
       addr = emitBlockByrefAddress(addr, VD);
     }

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Mon Oct  1 14:51:28 2018
@@ -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);
     }

Modified: cfe/trunk/lib/Sema/ScopeInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/ScopeInfo.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/ScopeInfo.cpp (original)
+++ cfe/trunk/lib/Sema/ScopeInfo.cpp Mon Oct  1 14:51:28 2018
@@ -54,6 +54,8 @@ void FunctionScopeInfo::Clear() {
   PossiblyUnreachableDiags.clear();
   WeakObjectUses.clear();
   ModifiedNonNullParams.clear();
+  Blocks.clear();
+  ByrefBlockVars.clear();
 }
 
 static const NamedDecl *getBestPropertyDecl(const ObjCPropertyRefExpr *PropE) {

Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Mon Oct  1 14:51:28 2018
@@ -1401,9 +1401,68 @@ void Sema::RecordParsingTemplateParamete
       "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<Expr>();
+    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<FunctionProtoType>();
+      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<BlocksAttr>())
+        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)

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Mon Oct  1 14:51:28 2018
@@ -11826,37 +11826,8 @@ void Sema::CheckCompleteVariableDeclarat
   QualType type = var->getType();
   if (type->isDependentType()) return;
 
-  // __block variables might require us to capture a copy-initializer.
-  if (var->hasAttr<BlocksAttr>()) {
-    // 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<Expr>();
-        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<FunctionProtoType>();
-          FPT = ResolveExceptionSpec(poi, FPT);
-        }
-    }
-  }
+  if (var->hasAttr<BlocksAttr>())
+    getCurFunction()->addByrefBlockVar(var);
 
   Expr *Init = var->getInit();
   bool IsGlobal = GlobalStorage && !var->isStaticLocal();

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Mon Oct  1 14:51:28 2018
@@ -13428,6 +13428,7 @@ ExprResult Sema::ActOnBlockStmtExpr(Sour
   PopExpressionEvaluationContext();
 
   BlockScopeInfo *BSI = cast<BlockScopeInfo>(FunctionScopes.back());
+  BlockDecl *BD = BSI->TheDecl;
 
   if (BSI->HasImplicitReturnType)
     deduceClosureReturnType(*BSI);
@@ -13438,7 +13439,7 @@ ExprResult Sema::ActOnBlockStmtExpr(Sour
   if (!BSI->ReturnType.isNull())
     RetTy = BSI->ReturnType;
 
-  bool NoReturn = BSI->TheDecl->hasAttr<NoReturnAttr>();
+  bool NoReturn = BD->hasAttr<NoReturnAttr>();
   QualType BlockTy;
 
   // Set the captured variables on the block.
@@ -13451,7 +13452,7 @@ ExprResult Sema::ActOnBlockStmtExpr(Sour
                               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(Sour
     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(Sour
       !PP.isCodeCompletionEnabled())
     DiagnoseInvalidJumps(cast<CompoundStmt>(Body));
 
-  BSI->TheDecl->setBody(cast<CompoundStmt>(Body));
+  BD->setBody(cast<CompoundStmt>(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(Sour
     }
   }
 
+  if (getCurFunction())
+    getCurFunction()->addBlock(BD);
+
   return Result;
 }
 

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Mon Oct  1 14:51:28 2018
@@ -1365,6 +1365,7 @@ ASTDeclReader::RedeclarableResult ASTDec
     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);

Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Mon Oct  1 14:51:28 2018
@@ -938,6 +938,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl
       Record.push_back(static_cast<unsigned>(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->isInitCapture() &&
       !D->isPreviousDeclInSameBlockScope() &&
       !(D->hasAttr<BlocksAttr>() && 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)

Modified: cfe/trunk/test/CodeGen/block-byref-aggr.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/block-byref-aggr.c?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGen/block-byref-aggr.c (original)
+++ cfe/trunk/test/CodeGen/block-byref-aggr.c Mon Oct  1 14:51:28 2018
@@ -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

Modified: cfe/trunk/test/CodeGen/blocks-seq.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/blocks-seq.c?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGen/blocks-seq.c (original)
+++ cfe/trunk/test/CodeGen/blocks-seq.c Mon Oct  1 14:51:28 2018
@@ -11,6 +11,7 @@ int rhs();
 
 void foo() {
   __block int i;
+  ^{ (void)i; };
   i = rhs();
   i += rhs();
 }

Modified: cfe/trunk/test/CodeGen/exceptions.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/exceptions.c?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGen/exceptions.c (original)
+++ cfe/trunk/test/CodeGen/exceptions.c Mon Oct  1 14:51:28 2018
@@ -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) {

Modified: cfe/trunk/test/CodeGen/personality.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/personality.c?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGen/personality.c (original)
+++ cfe/trunk/test/CodeGen/personality.c Mon Oct  1 14:51:28 2018
@@ -26,6 +26,7 @@ extern void i(void);
 
 void f(void) {
   __block int i;
+  ^{ (void)i; };
   g(^ { });
 }
 

Modified: cfe/trunk/test/CodeGenCXX/block-capture.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/block-capture.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/block-capture.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/block-capture.cpp Mon Oct  1 14:51:28 2018
@@ -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;
 }

Modified: cfe/trunk/test/CodeGenCXX/blocks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/blocks.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/blocks.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/blocks.cpp Mon Oct  1 14:51:28 2018
@@ -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

Modified: cfe/trunk/test/CodeGenCXX/debug-info-blocks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/debug-info-blocks.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/debug-info-blocks.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/debug-info-blocks.cpp Mon Oct  1 14:51:28 2018
@@ -9,6 +9,7 @@ struct A {
 
 void test() {
   __block A a;
+  ^{ (void)a; };
 }
 
 // CHECK: !DISubprogram(name: "__Block_byref_object_copy_",

Modified: cfe/trunk/test/CodeGenCXX/noescape.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/noescape.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/noescape.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/noescape.cpp Mon Oct  1 14:51:28 2018
@@ -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
 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; });
+}
+
+}

Modified: cfe/trunk/test/CodeGenObjC/arc-no-arc-exceptions.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/arc-no-arc-exceptions.m?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/arc-no-arc-exceptions.m (original)
+++ cfe/trunk/test/CodeGenObjC/arc-no-arc-exceptions.m Mon Oct  1 14:51:28 2018
@@ -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);
     }
 }

Modified: cfe/trunk/test/CodeGenObjC/arc-unoptimized-byref-var.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/arc-unoptimized-byref-var.m?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/arc-unoptimized-byref-var.m (original)
+++ cfe/trunk/test/CodeGenObjC/arc-unoptimized-byref-var.m Mon Oct  1 14:51:28 2018
@@ -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

Modified: cfe/trunk/test/CodeGenObjC/blocks-1.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/blocks-1.m?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/blocks-1.m (original)
+++ cfe/trunk/test/CodeGenObjC/blocks-1.m Mon Oct  1 14:51:28 2018
@@ -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;

Modified: cfe/trunk/test/CodeGenObjC/noescape.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/noescape.m?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/noescape.m (original)
+++ cfe/trunk/test/CodeGenObjC/noescape.m Mon Oct  1 14:51:28 2018
@@ -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__((noesca
 // 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; });
+}

Modified: cfe/trunk/test/CodeGenObjCXX/arc-blocks.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjCXX/arc-blocks.mm?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjCXX/arc-blocks.mm (original)
+++ cfe/trunk/test/CodeGenObjCXX/arc-blocks.mm Mon Oct  1 14:51:28 2018
@@ -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.

Modified: cfe/trunk/test/PCH/block-helpers.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/block-helpers.cpp?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/PCH/block-helpers.cpp (original)
+++ cfe/trunk/test/PCH/block-helpers.cpp Mon Oct  1 14:51:28 2018
@@ -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(

Modified: cfe/trunk/test/SemaObjCXX/blocks.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/blocks.mm?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjCXX/blocks.mm (original)
+++ cfe/trunk/test/SemaObjCXX/blocks.mm Mon Oct  1 14:51:28 2018
@@ -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 <int n> 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 <int n> void test2() {
     __block A<n> x; // expected-note {{requested here}}
+    func(^{ x.m(); });
   }
   template void test2<2>();
 }

Modified: cfe/trunk/test/SemaObjCXX/noescape.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/noescape.mm?rev=343542&r1=343541&r2=343542&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjCXX/noescape.mm (original)
+++ cfe/trunk/test/SemaObjCXX/noescape.mm Mon Oct  1 14:51:28 2018
@@ -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; }); });
+}




More information about the cfe-commits mailing list