[clang] e5df9cc - [CodeGen] Treat ObjC `__unsafe_unretained` and class types as trivial

Akira Hatanaka via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 11 11:22:15 PST 2022


Author: Akira Hatanaka
Date: 2022-01-11T11:18:24-08:00
New Revision: e5df9cc098b554ebb066792e40cbde6feddc57bc

URL: https://github.com/llvm/llvm-project/commit/e5df9cc098b554ebb066792e40cbde6feddc57bc
DIFF: https://github.com/llvm/llvm-project/commit/e5df9cc098b554ebb066792e40cbde6feddc57bc.diff

LOG: [CodeGen] Treat ObjC `__unsafe_unretained` and class types as trivial
when generating copy/dispose helper functions

Analyze the block captures just once before generating copy/dispose
block helper functions and honor the inert `__unsafe_unretained`
qualifier. This refactor fixes a bug where captures of ObjC
`__unsafe_unretained` and class types were needlessly retained/released
by the copy/dispose helper functions.

Differential Revision: https://reviews.llvm.org/D116948

Added: 
    

Modified: 
    clang/lib/CodeGen/CGBlocks.cpp
    clang/lib/CodeGen/CGBlocks.h
    clang/lib/CodeGen/CGObjCMac.cpp
    clang/test/CodeGenObjC/arc-blocks.m
    clang/test/CodeGenObjC/blocks.m

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp
index 7bb6dbb8a8aca..1f1de3df857c9 100644
--- a/clang/lib/CodeGen/CGBlocks.cpp
+++ b/clang/lib/CodeGen/CGBlocks.cpp
@@ -33,10 +33,10 @@ using namespace clang;
 using namespace CodeGen;
 
 CGBlockInfo::CGBlockInfo(const BlockDecl *block, StringRef name)
-  : Name(name), CXXThisIndex(0), CanBeGlobal(false), NeedsCopyDispose(false),
-    HasCXXObject(false), UsesStret(false), HasCapturedVariableLayout(false),
-    CapturesNonExternalType(false), LocalAddress(Address::invalid()),
-    StructureType(nullptr), Block(block) {
+    : Name(name), CXXThisIndex(0), CanBeGlobal(false), NeedsCopyDispose(false),
+      NoEscape(false), HasCXXObject(false), UsesStret(false),
+      HasCapturedVariableLayout(false), CapturesNonExternalType(false),
+      LocalAddress(Address::invalid()), StructureType(nullptr), Block(block) {
 
   // Skip asm prefix, if any.  'name' is usually taken directly from
   // the mangled name of the enclosing function.
@@ -66,17 +66,6 @@ static llvm::Constant *buildDisposeHelper(CodeGenModule &CGM,
 
 namespace {
 
-/// Represents a type of copy/destroy operation that should be performed for an
-/// entity that's captured by a block.
-enum class BlockCaptureEntityKind {
-  CXXRecord, // Copy or destroy
-  ARCWeak,
-  ARCStrong,
-  NonTrivialCStruct,
-  BlockObject, // Assign or release
-  None
-};
-
 /// Represents a captured entity that requires extra operations in order for
 /// this entity to be copied or destroyed correctly.
 struct BlockCaptureManagedEntity {
@@ -110,11 +99,7 @@ enum class CaptureStrKind {
 
 } // end anonymous namespace
 
-static void findBlockCapturedManagedEntities(
-    const CGBlockInfo &BlockInfo, const LangOptions &LangOpts,
-    SmallVectorImpl<BlockCaptureManagedEntity> &ManagedCaptures);
-
-static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
+static std::string getBlockCaptureStr(const CGBlockInfo::Capture &Cap,
                                       CaptureStrKind StrKind,
                                       CharUnits BlockAlignment,
                                       CodeGenModule &CGM);
@@ -124,34 +109,33 @@ static std::string getBlockDescriptorName(const CGBlockInfo &BlockInfo,
   std::string Name = "__block_descriptor_";
   Name += llvm::to_string(BlockInfo.BlockSize.getQuantity()) + "_";
 
-  if (BlockInfo.needsCopyDisposeHelpers()) {
+  if (BlockInfo.NeedsCopyDispose) {
     if (CGM.getLangOpts().Exceptions)
       Name += "e";
     if (CGM.getCodeGenOpts().ObjCAutoRefCountExceptions)
       Name += "a";
     Name += llvm::to_string(BlockInfo.BlockAlign.getQuantity()) + "_";
 
-    SmallVector<BlockCaptureManagedEntity, 4> ManagedCaptures;
-    findBlockCapturedManagedEntities(BlockInfo, CGM.getContext().getLangOpts(),
-                                     ManagedCaptures);
+    for (auto &Cap : BlockInfo.SortedCaptures) {
+      if (Cap.isConstantOrTrivial())
+        continue;
 
-    for (const BlockCaptureManagedEntity &E : ManagedCaptures) {
-      Name += llvm::to_string(E.Capture->getOffset().getQuantity());
+      Name += llvm::to_string(Cap.getOffset().getQuantity());
 
-      if (E.CopyKind == E.DisposeKind) {
+      if (Cap.CopyKind == Cap.DisposeKind) {
         // If CopyKind and DisposeKind are the same, merge the capture
         // information.
-        assert(E.CopyKind != BlockCaptureEntityKind::None &&
+        assert(Cap.CopyKind != BlockCaptureEntityKind::None &&
                "shouldn't see BlockCaptureManagedEntity that is None");
-        Name += getBlockCaptureStr(E, CaptureStrKind::Merged,
+        Name += getBlockCaptureStr(Cap, CaptureStrKind::Merged,
                                    BlockInfo.BlockAlign, CGM);
       } else {
         // If CopyKind and DisposeKind are not the same, which can happen when
         // either Kind is None or the captured object is a __strong block,
         // concatenate the copy and dispose strings.
-        Name += getBlockCaptureStr(E, CaptureStrKind::CopyHelper,
+        Name += getBlockCaptureStr(Cap, CaptureStrKind::CopyHelper,
                                    BlockInfo.BlockAlign, CGM);
-        Name += getBlockCaptureStr(E, CaptureStrKind::DisposeHelper,
+        Name += getBlockCaptureStr(Cap, CaptureStrKind::DisposeHelper,
                                    BlockInfo.BlockAlign, CGM);
       }
     }
@@ -223,7 +207,7 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM,
 
   // Optional copy/dispose helpers.
   bool hasInternalHelper = false;
-  if (blockInfo.needsCopyDisposeHelpers()) {
+  if (blockInfo.NeedsCopyDispose) {
     // copy_func_helper_decl
     llvm::Constant *copyHelper = buildCopyHelper(CGM, blockInfo);
     elements.add(copyHelper);
@@ -340,17 +324,21 @@ namespace {
   struct BlockLayoutChunk {
     CharUnits Alignment;
     CharUnits Size;
-    Qualifiers::ObjCLifetime Lifetime;
     const BlockDecl::Capture *Capture; // null for 'this'
     llvm::Type *Type;
     QualType FieldType;
+    BlockCaptureEntityKind CopyKind, DisposeKind;
+    BlockFieldFlags CopyFlags, DisposeFlags;
 
     BlockLayoutChunk(CharUnits align, CharUnits size,
-                     Qualifiers::ObjCLifetime lifetime,
-                     const BlockDecl::Capture *capture,
-                     llvm::Type *type, QualType fieldType)
-      : Alignment(align), Size(size), Lifetime(lifetime),
-        Capture(capture), Type(type), FieldType(fieldType) {}
+                     const BlockDecl::Capture *capture, llvm::Type *type,
+                     QualType fieldType, BlockCaptureEntityKind CopyKind,
+                     BlockFieldFlags CopyFlags,
+                     BlockCaptureEntityKind DisposeKind,
+                     BlockFieldFlags DisposeFlags)
+        : Alignment(align), Size(size), Capture(capture), Type(type),
+          FieldType(fieldType), CopyKind(CopyKind), DisposeKind(DisposeKind),
+          CopyFlags(CopyFlags), DisposeFlags(DisposeFlags) {}
 
     /// Tell the block info that this chunk has the given field index.
     void setIndex(CGBlockInfo &info, unsigned index, CharUnits offset) {
@@ -358,32 +346,93 @@ namespace {
         info.CXXThisIndex = index;
         info.CXXThisOffset = offset;
       } else {
-        auto C = CGBlockInfo::Capture::makeIndex(index, offset, FieldType);
-        info.Captures.insert({Capture->getVariable(), C});
+        info.SortedCaptures.push_back(CGBlockInfo::Capture::makeIndex(
+            index, offset, FieldType, CopyKind, CopyFlags, DisposeKind,
+            DisposeFlags, Capture));
       }
     }
+
+    bool isTrivial() const {
+      return CopyKind == BlockCaptureEntityKind::None &&
+             DisposeKind == BlockCaptureEntityKind::None;
+    }
   };
 
-  /// Order by 1) all __strong together 2) next, all byfref together 3) next,
-  /// all __weak together. Preserve descending alignment in all situations.
+  /// Order by 1) all __strong together 2) next, all block together 3) next,
+  /// all byref together 4) next, all __weak together. Preserve descending
+  /// alignment in all situations.
   bool operator<(const BlockLayoutChunk &left, const BlockLayoutChunk &right) {
     if (left.Alignment != right.Alignment)
       return left.Alignment > right.Alignment;
 
     auto getPrefOrder = [](const BlockLayoutChunk &chunk) {
-      if (chunk.Capture && chunk.Capture->isByRef())
-        return 1;
-      if (chunk.Lifetime == Qualifiers::OCL_Strong)
+      switch (chunk.CopyKind) {
+      case BlockCaptureEntityKind::ARCStrong:
         return 0;
-      if (chunk.Lifetime == Qualifiers::OCL_Weak)
-        return 2;
-      return 3;
+      case BlockCaptureEntityKind::BlockObject:
+        switch (chunk.CopyFlags.getBitMask()) {
+        case BLOCK_FIELD_IS_OBJECT:
+          return 0;
+        case BLOCK_FIELD_IS_BLOCK:
+          return 1;
+        case BLOCK_FIELD_IS_BYREF:
+          return 2;
+        default:
+          break;
+        }
+        break;
+      case BlockCaptureEntityKind::ARCWeak:
+        return 3;
+      default:
+        break;
+      }
+      return 4;
     };
 
     return getPrefOrder(left) < getPrefOrder(right);
   }
 } // end anonymous namespace
 
+static std::pair<BlockCaptureEntityKind, BlockFieldFlags>
+computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
+                               const LangOptions &LangOpts);
+
+static std::pair<BlockCaptureEntityKind, BlockFieldFlags>
+computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
+                                  const LangOptions &LangOpts);
+
+static void addBlockLayout(CharUnits align, CharUnits size,
+                           const BlockDecl::Capture *capture, llvm::Type *type,
+                           QualType fieldType,
+                           SmallVectorImpl<BlockLayoutChunk> &Layout,
+                           CGBlockInfo &Info, CodeGenModule &CGM) {
+  if (!capture) {
+    // 'this' capture.
+    Layout.push_back(BlockLayoutChunk(
+        align, size, capture, type, fieldType, BlockCaptureEntityKind::None,
+        BlockFieldFlags(), BlockCaptureEntityKind::None, BlockFieldFlags()));
+    return;
+  }
+
+  const LangOptions &LangOpts = CGM.getLangOpts();
+  BlockCaptureEntityKind CopyKind, DisposeKind;
+  BlockFieldFlags CopyFlags, DisposeFlags;
+
+  std::tie(CopyKind, CopyFlags) =
+      computeCopyInfoForBlockCapture(*capture, fieldType, LangOpts);
+  std::tie(DisposeKind, DisposeFlags) =
+      computeDestroyInfoForBlockCapture(*capture, fieldType, LangOpts);
+  Layout.push_back(BlockLayoutChunk(align, size, capture, type, fieldType,
+                                    CopyKind, CopyFlags, DisposeKind,
+                                    DisposeFlags));
+
+  if (Info.NoEscape)
+    return;
+
+  if (!Layout.back().isTrivial())
+    Info.NeedsCopyDispose = true;
+}
+
 /// Determines if the given type is safe for constant capture in C++.
 static bool isSafeForCXXConstantCapture(QualType type) {
   const RecordType *recordType =
@@ -541,6 +590,9 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
            CGM.getLangOpts().getGC() == LangOptions::NonGC)
     info.HasCapturedVariableLayout = true;
 
+  if (block->doesNotEscape())
+    info.NoEscape = true;
+
   // Collect the layout chunks.
   SmallVector<BlockLayoutChunk, 16> layout;
   layout.reserve(block->capturesCXXThis() +
@@ -560,9 +612,8 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
     auto TInfo = CGM.getContext().getTypeInfoInChars(thisType);
     maxFieldAlign = std::max(maxFieldAlign, TInfo.Align);
 
-    layout.push_back(BlockLayoutChunk(TInfo.Align, TInfo.Width,
-                                      Qualifiers::OCL_None,
-                                      nullptr, llvmType, thisType));
+    addBlockLayout(TInfo.Align, TInfo.Width, nullptr, llvmType, thisType,
+                   layout, info, CGM);
   }
 
   // Next, all the block captures.
@@ -570,9 +621,6 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
     const VarDecl *variable = CI.getVariable();
 
     if (CI.isEscapingByref()) {
-      // We have to copy/dispose of the __block reference.
-      info.NeedsCopyDispose = true;
-
       // Just use void* instead of a pointer to the byref type.
       CharUnits align = CGM.getPointerAlign();
       maxFieldAlign = std::max(maxFieldAlign, align);
@@ -581,72 +629,28 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
       // the capture field type should always match.
       assert(CGF && getCaptureFieldType(*CGF, CI) == variable->getType() &&
              "capture type 
diff ers from the variable type");
-      layout.push_back(BlockLayoutChunk(align, CGM.getPointerSize(),
-                                        Qualifiers::OCL_None, &CI,
-                                        CGM.VoidPtrTy, variable->getType()));
+      addBlockLayout(align, CGM.getPointerSize(), &CI, CGM.VoidPtrTy,
+                     variable->getType(), layout, info, CGM);
       continue;
     }
 
     // Otherwise, build a layout chunk with the size and alignment of
     // the declaration.
     if (llvm::Constant *constant = tryCaptureAsConstant(CGM, CGF, variable)) {
-      info.Captures[variable] = CGBlockInfo::Capture::makeConstant(constant);
+      info.SortedCaptures.push_back(
+          CGBlockInfo::Capture::makeConstant(constant, &CI));
       continue;
     }
 
     QualType VT = getCaptureFieldType(*CGF, CI);
 
-    // If we have a lifetime qualifier, honor it for capture purposes.
-    // That includes *not* copying it if it's __unsafe_unretained.
-    Qualifiers::ObjCLifetime lifetime = VT.getObjCLifetime();
-    if (lifetime) {
-      switch (lifetime) {
-      case Qualifiers::OCL_None: llvm_unreachable("impossible");
-      case Qualifiers::OCL_ExplicitNone:
-      case Qualifiers::OCL_Autoreleasing:
-        break;
-
-      case Qualifiers::OCL_Strong:
-      case Qualifiers::OCL_Weak:
-        info.NeedsCopyDispose = true;
-      }
-
-    // Block pointers require copy/dispose.  So do Objective-C pointers.
-    } else if (VT->isObjCRetainableType()) {
-      // But honor the inert __unsafe_unretained qualifier, which doesn't
-      // actually make it into the type system.
-       if (VT->isObjCInertUnsafeUnretainedType()) {
-        lifetime = Qualifiers::OCL_ExplicitNone;
-      } else {
-        info.NeedsCopyDispose = true;
-        // used for mrr below.
-        lifetime = Qualifiers::OCL_Strong;
-      }
-
-    // So do types that require non-trivial copy construction.
-    } else if (CI.hasCopyExpr()) {
-      info.NeedsCopyDispose = true;
-      info.HasCXXObject = true;
-      if (!VT->getAsCXXRecordDecl()->isExternallyVisible())
-        info.CapturesNonExternalType = true;
-
-    // So do C structs that require non-trivial copy construction or
-    // destruction.
-    } else if (VT.isNonTrivialToPrimitiveCopy() == QualType::PCK_Struct ||
-               VT.isDestructedType() == QualType::DK_nontrivial_c_struct) {
-      info.NeedsCopyDispose = true;
-
-    // And so do types with destructors.
-    } else if (CGM.getLangOpts().CPlusPlus) {
-      if (const CXXRecordDecl *record = VT->getAsCXXRecordDecl()) {
-        if (!record->hasTrivialDestructor()) {
+    if (CGM.getLangOpts().CPlusPlus)
+      if (const CXXRecordDecl *record = VT->getAsCXXRecordDecl())
+        if (CI.hasCopyExpr() || !record->hasTrivialDestructor()) {
           info.HasCXXObject = true;
-          info.NeedsCopyDispose = true;
           if (!record->isExternallyVisible())
             info.CapturesNonExternalType = true;
         }
-      }
-    }
 
     CharUnits size = C.getTypeSizeInChars(VT);
     CharUnits align = C.getDeclAlign(variable);
@@ -656,8 +660,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
     llvm::Type *llvmType =
       CGM.getTypes().ConvertTypeForMem(VT);
 
-    layout.push_back(
-        BlockLayoutChunk(align, size, lifetime, &CI, llvmType, VT));
+    addBlockLayout(align, size, &CI, llvmType, VT, layout, info, CGM);
   }
 
   // If that was everything, we're done here.
@@ -665,6 +668,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
     info.StructureType =
       llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true);
     info.CanBeGlobal = true;
+    info.buildCaptureMap();
     return;
   }
 
@@ -718,6 +722,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
 
         // ...until we get to the alignment of the maximum field.
         if (endAlign >= maxFieldAlign) {
+          ++li;
           break;
         }
       }
@@ -770,6 +775,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CodeGenFunction *CGF,
     endAlign = getLowBit(blockSize);
   }
 
+  info.buildCaptureMap();
   info.StructureType =
     llvm::StructType::get(CGM.getLLVMContext(), elementTypes, true);
 }
@@ -826,7 +832,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
     // If the block is non-escaping, set field 'isa 'to NSConcreteGlobalBlock
     // and set the BLOCK_IS_GLOBAL bit of field 'flags'. Copying a non-escaping
     // block just returns the original block and releasing it is a no-op.
-    llvm::Constant *blockISA = blockInfo.getBlockDecl()->doesNotEscape()
+    llvm::Constant *blockISA = blockInfo.NoEscape
                                    ? CGM.getNSConcreteGlobalBlock()
                                    : CGM.getNSConcreteStackBlock();
     isa = llvm::ConstantExpr::getBitCast(blockISA, VoidPtrTy);
@@ -838,13 +844,13 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
     flags = BLOCK_HAS_SIGNATURE;
     if (blockInfo.HasCapturedVariableLayout)
       flags |= BLOCK_HAS_EXTENDED_LAYOUT;
-    if (blockInfo.needsCopyDisposeHelpers())
+    if (blockInfo.NeedsCopyDispose)
       flags |= BLOCK_HAS_COPY_DISPOSE;
     if (blockInfo.HasCXXObject)
       flags |= BLOCK_HAS_CXX_OBJ;
     if (blockInfo.UsesStret)
       flags |= BLOCK_USE_STRET;
-    if (blockInfo.getBlockDecl()->doesNotEscape())
+    if (blockInfo.NoEscape)
       flags |= BLOCK_IS_NOESCAPE | BLOCK_IS_GLOBAL;
   }
 
@@ -1033,7 +1039,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
     }
 
     // Push a cleanup for the capture if necessary.
-    if (!blockInfo.NeedsCopyDispose)
+    if (!blockInfo.NoEscape && !blockInfo.NeedsCopyDispose)
       continue;
 
     // Ignore __block captures; there's nothing special in the on-stack block
@@ -1654,6 +1660,11 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
       // For all other types, the memcpy is fine.
       return std::make_pair(BlockCaptureEntityKind::None, BlockFieldFlags());
 
+    // Honor the inert __unsafe_unretained qualifier, which doesn't actually
+    // make it into the type system.
+    if (T->isObjCInertUnsafeUnretainedType())
+      return std::make_pair(BlockCaptureEntityKind::None, BlockFieldFlags());
+
     // Special rules for ARC captures:
     Qualifiers QS = T.getQualifiers();
 
@@ -1669,34 +1680,6 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
   llvm_unreachable("after exhaustive PrimitiveCopyKind switch");
 }
 
-static std::pair<BlockCaptureEntityKind, BlockFieldFlags>
-computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
-                                  const LangOptions &LangOpts);
-
-/// Find the set of block captures that need to be explicitly copied or destroy.
-static void findBlockCapturedManagedEntities(
-    const CGBlockInfo &BlockInfo, const LangOptions &LangOpts,
-    SmallVectorImpl<BlockCaptureManagedEntity> &ManagedCaptures) {
-  for (const auto &CI : BlockInfo.getBlockDecl()->captures()) {
-    const VarDecl *Variable = CI.getVariable();
-    const CGBlockInfo::Capture &Capture = BlockInfo.getCapture(Variable);
-    if (Capture.isConstant())
-      continue;
-
-    QualType VT = Capture.fieldType();
-    auto CopyInfo = computeCopyInfoForBlockCapture(CI, VT, LangOpts);
-    auto DisposeInfo = computeDestroyInfoForBlockCapture(CI, VT, LangOpts);
-    if (CopyInfo.first != BlockCaptureEntityKind::None ||
-        DisposeInfo.first != BlockCaptureEntityKind::None)
-      ManagedCaptures.emplace_back(CopyInfo.first, DisposeInfo.first,
-                                   CopyInfo.second, DisposeInfo.second, CI,
-                                   Capture);
-  }
-
-  // Sort the captures by offset.
-  llvm::sort(ManagedCaptures);
-}
-
 namespace {
 /// Release a __block variable.
 struct CallBlockRelease final : EHScopeStack::Cleanup {
@@ -1732,13 +1715,13 @@ bool CodeGenFunction::cxxDestructorCanThrow(QualType T) {
 }
 
 // Return a string that has the information about a capture.
-static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
+static std::string getBlockCaptureStr(const CGBlockInfo::Capture &Cap,
                                       CaptureStrKind StrKind,
                                       CharUnits BlockAlignment,
                                       CodeGenModule &CGM) {
   std::string Str;
   ASTContext &Ctx = CGM.getContext();
-  const BlockDecl::Capture &CI = *E.CI;
+  const BlockDecl::Capture &CI = *Cap.Cap;
   QualType CaptureTy = CI.getVariable()->getType();
 
   BlockCaptureEntityKind Kind;
@@ -1747,15 +1730,16 @@ static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
   // CaptureStrKind::Merged should be passed only when the operations and the
   // flags are the same for copy and dispose.
   assert((StrKind != CaptureStrKind::Merged ||
-          (E.CopyKind == E.DisposeKind && E.CopyFlags == E.DisposeFlags)) &&
+          (Cap.CopyKind == Cap.DisposeKind &&
+           Cap.CopyFlags == Cap.DisposeFlags)) &&
          "
diff erent operations and flags");
 
   if (StrKind == CaptureStrKind::DisposeHelper) {
-    Kind = E.DisposeKind;
-    Flags = E.DisposeFlags;
+    Kind = Cap.DisposeKind;
+    Flags = Cap.DisposeFlags;
   } else {
-    Kind = E.CopyKind;
-    Flags = E.CopyFlags;
+    Kind = Cap.CopyKind;
+    Flags = Cap.CopyFlags;
   }
 
   switch (Kind) {
@@ -1803,8 +1787,7 @@ static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
   }
   case BlockCaptureEntityKind::NonTrivialCStruct: {
     bool IsVolatile = CaptureTy.isVolatileQualified();
-    CharUnits Alignment =
-        BlockAlignment.alignmentAtOffset(E.Capture->getOffset());
+    CharUnits Alignment = BlockAlignment.alignmentAtOffset(Cap.getOffset());
 
     Str += "n";
     std::string FuncStr;
@@ -1829,7 +1812,7 @@ static std::string getBlockCaptureStr(const BlockCaptureManagedEntity &E,
 }
 
 static std::string getCopyDestroyHelperFuncName(
-    const SmallVectorImpl<BlockCaptureManagedEntity> &Captures,
+    const SmallVectorImpl<CGBlockInfo::Capture> &Captures,
     CharUnits BlockAlignment, CaptureStrKind StrKind, CodeGenModule &CGM) {
   assert((StrKind == CaptureStrKind::CopyHelper ||
           StrKind == CaptureStrKind::DisposeHelper) &&
@@ -1843,9 +1826,11 @@ static std::string getCopyDestroyHelperFuncName(
     Name += "a";
   Name += llvm::to_string(BlockAlignment.getQuantity()) + "_";
 
-  for (const BlockCaptureManagedEntity &E : Captures) {
-    Name += llvm::to_string(E.Capture->getOffset().getQuantity());
-    Name += getBlockCaptureStr(E, StrKind, BlockAlignment, CGM);
+  for (auto &Cap : Captures) {
+    if (Cap.isConstantOrTrivial())
+      continue;
+    Name += llvm::to_string(Cap.getOffset().getQuantity());
+    Name += getBlockCaptureStr(Cap, StrKind, BlockAlignment, CGM);
   }
 
   return Name;
@@ -1916,11 +1901,9 @@ static void setBlockHelperAttributesVisibility(bool CapturesNonExternalType,
 /// the contents of an individual __block variable to the heap.
 llvm::Constant *
 CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
-  SmallVector<BlockCaptureManagedEntity, 4> CopiedCaptures;
-  findBlockCapturedManagedEntities(blockInfo, getLangOpts(), CopiedCaptures);
-  std::string FuncName =
-      getCopyDestroyHelperFuncName(CopiedCaptures, blockInfo.BlockAlign,
-                                   CaptureStrKind::CopyHelper, CGM);
+  std::string FuncName = getCopyDestroyHelperFuncName(
+      blockInfo.SortedCaptures, blockInfo.BlockAlign,
+      CaptureStrKind::CopyHelper, CGM);
 
   if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName))
     return llvm::ConstantExpr::getBitCast(Func, VoidPtrTy);
@@ -1967,17 +1950,19 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
   dst = Address(Builder.CreateLoad(dst), blockInfo.BlockAlign);
   dst = Builder.CreateBitCast(dst, structPtrTy, "block.dest");
 
-  for (const auto &CopiedCapture : CopiedCaptures) {
-    const BlockDecl::Capture &CI = *CopiedCapture.CI;
-    const CGBlockInfo::Capture &capture = *CopiedCapture.Capture;
+  for (auto &capture : blockInfo.SortedCaptures) {
+    if (capture.isConstantOrTrivial())
+      continue;
+
+    const BlockDecl::Capture &CI = *capture.Cap;
     QualType captureType = CI.getVariable()->getType();
-    BlockFieldFlags flags = CopiedCapture.CopyFlags;
+    BlockFieldFlags flags = capture.CopyFlags;
 
     unsigned index = capture.getIndex();
     Address srcField = Builder.CreateStructGEP(src, index);
     Address dstField = Builder.CreateStructGEP(dst, index);
 
-    switch (CopiedCapture.CopyKind) {
+    switch (capture.CopyKind) {
     case BlockCaptureEntityKind::CXXRecord:
       // If there's an explicit copy expression, we do that.
       assert(CI.getCopyExpr() && "copy expression for variable is missing");
@@ -2040,7 +2025,7 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
 
     // Ensure that we destroy the copied object if an exception is thrown later
     // in the helper function.
-    pushCaptureCleanup(CopiedCapture.CopyKind, dstField, captureType, flags,
+    pushCaptureCleanup(capture.CopyKind, dstField, captureType, flags,
                        /*ForCopyHelper*/ true, CI.getVariable(), *this);
   }
 
@@ -2085,8 +2070,10 @@ computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
                           BlockFieldFlags());
   case QualType::DK_none: {
     // Non-ARC captures are strong, and we need to use _Block_object_dispose.
+    // But honor the inert __unsafe_unretained qualifier, which doesn't actually
+    // make it into the type system.
     if (T->isObjCRetainableType() && !T.getQualifiers().hasObjCLifetime() &&
-        !LangOpts.ObjCAutoRefCount)
+        !LangOpts.ObjCAutoRefCount && !T->isObjCInertUnsafeUnretainedType())
       return std::make_pair(BlockCaptureEntityKind::BlockObject,
                             getBlockFieldFlagsForObjCObjectPointer(CI, T));
     // Otherwise, we have nothing to do.
@@ -2105,11 +2092,9 @@ computeDestroyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
 /// variable.
 llvm::Constant *
 CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) {
-  SmallVector<BlockCaptureManagedEntity, 4> DestroyedCaptures;
-  findBlockCapturedManagedEntities(blockInfo, getLangOpts(), DestroyedCaptures);
-  std::string FuncName =
-      getCopyDestroyHelperFuncName(DestroyedCaptures, blockInfo.BlockAlign,
-                                   CaptureStrKind::DisposeHelper, CGM);
+  std::string FuncName = getCopyDestroyHelperFuncName(
+      blockInfo.SortedCaptures, blockInfo.BlockAlign,
+      CaptureStrKind::DisposeHelper, CGM);
 
   if (llvm::GlobalValue *Func = CGM.getModule().getNamedValue(FuncName))
     return llvm::ConstantExpr::getBitCast(Func, VoidPtrTy);
@@ -2153,14 +2138,16 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) {
 
   CodeGenFunction::RunCleanupsScope cleanups(*this);
 
-  for (const auto &DestroyedCapture : DestroyedCaptures) {
-    const BlockDecl::Capture &CI = *DestroyedCapture.CI;
-    const CGBlockInfo::Capture &capture = *DestroyedCapture.Capture;
-    BlockFieldFlags flags = DestroyedCapture.DisposeFlags;
+  for (auto &capture : blockInfo.SortedCaptures) {
+    if (capture.isConstantOrTrivial())
+      continue;
+
+    const BlockDecl::Capture &CI = *capture.Cap;
+    BlockFieldFlags flags = capture.DisposeFlags;
 
     Address srcField = Builder.CreateStructGEP(src, capture.getIndex());
 
-    pushCaptureCleanup(DestroyedCapture.DisposeKind, srcField,
+    pushCaptureCleanup(capture.DisposeKind, srcField,
                        CI.getVariable()->getType(), flags,
                        /*ForCopyHelper*/ false, CI.getVariable(), *this);
   }

diff  --git a/clang/lib/CodeGen/CGBlocks.h b/clang/lib/CodeGen/CGBlocks.h
index 552d720b0a1d9..e8857d98894f9 100644
--- a/clang/lib/CodeGen/CGBlocks.h
+++ b/clang/lib/CodeGen/CGBlocks.h
@@ -141,6 +141,17 @@ class BlockByrefInfo {
   CharUnits FieldOffset;
 };
 
+/// Represents a type of copy/destroy operation that should be performed for an
+/// entity that's captured by a block.
+enum class BlockCaptureEntityKind {
+  None,
+  CXXRecord, // Copy or destroy
+  ARCWeak,
+  ARCStrong,
+  NonTrivialCStruct,
+  BlockObject, // Assign or release
+};
+
 /// CGBlockInfo - Information to generate a block literal.
 class CGBlockInfo {
 public:
@@ -190,20 +201,40 @@ class CGBlockInfo {
       return FieldType;
     }
 
-    static Capture makeIndex(unsigned index, CharUnits offset,
-                             QualType FieldType) {
+    static Capture
+    makeIndex(unsigned index, CharUnits offset, QualType FieldType,
+              BlockCaptureEntityKind CopyKind, BlockFieldFlags CopyFlags,
+              BlockCaptureEntityKind DisposeKind, BlockFieldFlags DisposeFlags,
+              const BlockDecl::Capture *Cap) {
       Capture v;
       v.Data = (index << 1) | 1;
       v.Offset = offset.getQuantity();
       v.FieldType = FieldType;
+      v.CopyKind = CopyKind;
+      v.CopyFlags = CopyFlags;
+      v.DisposeKind = DisposeKind;
+      v.DisposeFlags = DisposeFlags;
+      v.Cap = Cap;
       return v;
     }
 
-    static Capture makeConstant(llvm::Value *value) {
+    static Capture makeConstant(llvm::Value *value,
+                                const BlockDecl::Capture *Cap) {
       Capture v;
       v.Data = reinterpret_cast<uintptr_t>(value);
+      v.Cap = Cap;
       return v;
     }
+
+    bool isConstantOrTrivial() const {
+      return CopyKind == BlockCaptureEntityKind::None &&
+             DisposeKind == BlockCaptureEntityKind::None;
+    }
+
+    BlockCaptureEntityKind CopyKind = BlockCaptureEntityKind::None,
+                           DisposeKind = BlockCaptureEntityKind::None;
+    BlockFieldFlags CopyFlags, DisposeFlags;
+    const BlockDecl::Capture *Cap;
   };
 
   /// CanBeGlobal - True if the block can be global, i.e. it has
@@ -214,6 +245,9 @@ class CGBlockInfo {
   /// dispose helper functions if the block were escaping.
   bool NeedsCopyDispose : 1;
 
+  /// Indicates whether the block is non-escaping.
+  bool NoEscape : 1;
+
   /// HasCXXObject - True if the block's custom copy/dispose functions
   /// need to be run even in GC mode.
   bool HasCXXObject : 1;
@@ -231,8 +265,11 @@ class CGBlockInfo {
   /// functions.
   bool CapturesNonExternalType : 1;
 
-  /// The mapping of allocated indexes within the block.
-  llvm::DenseMap<const VarDecl*, Capture> Captures;
+  /// Mapping from variables to pointers to captures in SortedCaptures.
+  llvm::DenseMap<const VarDecl *, Capture *> Captures;
+
+  /// The block's captures. Non-constant captures are sorted by their offsets.
+  llvm::SmallVector<Capture, 4> SortedCaptures;
 
   Address LocalAddress;
   llvm::StructType *StructureType;
@@ -256,14 +293,18 @@ class CGBlockInfo {
   /// has been encountered.
   CGBlockInfo *NextBlockInfo;
 
+  void buildCaptureMap() {
+    for (auto &C : SortedCaptures)
+      Captures[C.Cap->getVariable()] = &C;
+  }
+
   const Capture &getCapture(const VarDecl *var) const {
     return const_cast<CGBlockInfo*>(this)->getCapture(var);
   }
   Capture &getCapture(const VarDecl *var) {
-    llvm::DenseMap<const VarDecl*, Capture>::iterator
-      it = Captures.find(var);
+    auto it = Captures.find(var);
     assert(it != Captures.end() && "no entry for variable!");
-    return it->second;
+    return *it->second;
   }
 
   const BlockDecl *getBlockDecl() const { return Block; }
@@ -274,11 +315,6 @@ class CGBlockInfo {
   }
 
   CGBlockInfo(const BlockDecl *blockDecl, StringRef Name);
-
-  // Indicates whether the block needs a custom copy or dispose function.
-  bool needsCopyDisposeHelpers() const {
-    return NeedsCopyDispose && !Block->doesNotEscape();
-  }
 };
 
 }  // end namespace CodeGen

diff  --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp
index d07524be127bb..a11a9ca7d8c0f 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -2935,8 +2935,7 @@ CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM,
 std::string CGObjCCommonMac::getRCBlockLayoutStr(CodeGenModule &CGM,
                                                  const CGBlockInfo &blockInfo) {
   fillRunSkipBlockVars(CGM, blockInfo);
-  return getBlockLayoutInfoString(RunSkipBlockVars,
-                                  blockInfo.needsCopyDisposeHelpers());
+  return getBlockLayoutInfoString(RunSkipBlockVars, blockInfo.NeedsCopyDispose);
 }
 
 llvm::Constant *CGObjCCommonMac::BuildByrefLayout(CodeGen::CodeGenModule &CGM,

diff  --git a/clang/test/CodeGenObjC/arc-blocks.m b/clang/test/CodeGenObjC/arc-blocks.m
index c939efdc2b5b7..1fb767a009b94 100644
--- a/clang/test/CodeGenObjC/arc-blocks.m
+++ b/clang/test/CodeGenObjC/arc-blocks.m
@@ -8,6 +8,10 @@
 // CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP46:.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8*, i8*, i8* } { i64 0, i64 48, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32s to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @{{.*}}, i32 0, i32 0) }, align 8
 // CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP48:.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32b to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([9 x i8], [9 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8
 
+// Check that no copy/dispose helpers are emitted for this block.
+
+// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP10:.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8* } { i64 0, i64 40, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @{{.*}}, i32 0, i32 0) }, align 8
+
 // This shouldn't crash.
 void test0(id (^maker)(void)) {
   maker();
@@ -769,5 +773,20 @@ void test23(id x, Test23 *t) {
   [t m:123, ^{ (void)x; }];
 }
 
+// CHECK-COMMON-LABEL: define internal void @"\01+[Test24 m]"(
+// CHECK-COMMON: %[[BLOCK_DESCRIPTOR:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8* }>* %{{.*}}, i32 0, i32 4
+// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, i8*, i8* }* @[[BLOCK_DESCRIPTOR_TMP10]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[BLOCK_DESCRIPTOR]],
+
+ at interface Test24
+ at property (class) void (^block)(void);
++(void)m;
+ at end
+
+ at implementation Test24
++(void)m {
+  self.block = ^{ (void)self; };
+}
+ at end
+
 // CHECK: attributes [[NUW]] = { nounwind }
 // CHECK-UNOPT: attributes [[NUW]] = { nounwind }

diff  --git a/clang/test/CodeGenObjC/blocks.m b/clang/test/CodeGenObjC/blocks.m
index ab2e237a28dab..3373e947f2ced 100644
--- a/clang/test/CodeGenObjC/blocks.m
+++ b/clang/test/CodeGenObjC/blocks.m
@@ -1,5 +1,14 @@
 // RUN: %clang_cc1 -triple i386-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fblocks -o - %s | FileCheck %s
 
+// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i32, i32 }
+
+// Check that there is only one capture (20o) in the copy/dispose function
+// names.
+
+// CHECK: @[[BLOCK_DESCRIPTOR0:.*]] = linkonce_odr hidden unnamed_addr constant { i32, i32, i8*, i8*, i8*, i32 } { i32 0, i32 28, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_4_20o to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_4_20o to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i32 512 },
+
+void (^gb0)(void);
+
 // test1.  All of this is somehow testing rdar://6676764
 struct S {
   void (^F)(struct S*);
@@ -132,3 +141,21 @@ void test4(void (^block)()) {
 // CHECK-NEXT: [[T5:%.*]] = bitcast i8* [[T4]] to void (i8*, i32, i32, i32, i32)*
 // CHECK-NEXT: call void [[T5]](i8* [[T3]], i32 0, i32 1, i32 2, i32 3)
 // CHECK-NEXT: ret void
+
+void test5(A *a) {
+  __unsafe_unretained A *t = a;
+  gb0 = ^{ (void)a; (void)t; };
+}
+
+// CHECK-LABEL: define void @test5(
+// CHECK: %[[V0:.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, {{.*}}*, {{.*}}* }>, <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, {{.*}}*, {{.*}}* }>* %{{.*}}, i32 0, i32 4
+// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i32, i32, i8*, i8*, i8*, i32 }* @[[BLOCK_DESCRIPTOR0]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %[[V0]],
+
+void test6(id a, long long b) {
+  void (^block)() = ^{ (void)b; (void)a; };
+}
+
+// Check that the block literal doesn't have two fields for capture 'a'.
+
+// CHECK-LABEL: define void @test6(
+// CHECK: alloca <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]*, i8*, i64 }>,


        


More information about the cfe-commits mailing list