From f0c11f7e6848f023ced6a5b51399ba787c7d4d0b Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 31 Mar 2011 08:03:29 +0000 Subject: [PATCH] After much contemplation, I've decided that we probably shouldn't "unique" __block object copy/dispose helpers for C++ objects with those for different variables with completely different semantics simply because they happen to both be no more aligned than a pointer. Found by inspection. Also, internalize most of the helper generation logic within CGBlocks.cpp, and refactor it to fit my peculiar aesthetic sense. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@128618 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGBlocks.cpp | 361 ++++++++++++++++++++-------------- lib/CodeGen/CodeGenFunction.h | 11 +- lib/CodeGen/CodeGenModule.h | 40 ++-- test/CodeGenCXX/blocks.cpp | 32 +++ 4 files changed, 279 insertions(+), 165 deletions(-) diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index c9edc97498..7e70e14119 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/lib/CodeGen/CGBlocks.cpp @@ -34,6 +34,9 @@ CGBlockInfo::CGBlockInfo(const BlockExpr *blockExpr, const char *N) ++Name; } +// Anchor the vtable to this translation unit. +CodeGenModule::ByrefHelpers::~ByrefHelpers() {} + /// Build the given block as a global block. static llvm::Constant *buildGlobalBlock(CodeGenModule &CGM, const CGBlockInfo &blockInfo, @@ -1218,87 +1221,158 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { return llvm::ConstantExpr::getBitCast(Fn, VoidPtrTy); } -llvm::Constant *CodeGenFunction:: -GeneratebyrefCopyHelperFunction(const llvm::Type *T, BlockFieldFlags flags, - const VarDecl *variable) { - QualType R = getContext().VoidTy; +namespace { + +/// Emits the copy/dispose helper functions for a __block object of id type. +class ObjectByrefHelpers : public CodeGenModule::ByrefHelpers { + BlockFieldFlags Flags; + +public: + ObjectByrefHelpers(CharUnits alignment, BlockFieldFlags flags) + : ByrefHelpers(alignment), Flags(flags) {} + + void emitCopy(CodeGenFunction &CGF, llvm::Value *srcField, + llvm::Value *destField) { + destField = CGF.Builder.CreateBitCast(destField, CGF.VoidPtrTy); + + srcField = CGF.Builder.CreateBitCast(srcField, CGF.VoidPtrPtrTy); + llvm::Value *srcValue = CGF.Builder.CreateLoad(srcField); + + unsigned flags = (Flags | BLOCK_BYREF_CALLER).getBitMask(); + + llvm::Value *flagsVal = llvm::ConstantInt::get(CGF.Int32Ty, flags); + llvm::Value *fn = CGF.CGM.getBlockObjectAssign(); + CGF.Builder.CreateCall3(fn, destField, srcValue, flagsVal); + } + + void emitDispose(CodeGenFunction &CGF, llvm::Value *field) { + field = CGF.Builder.CreateBitCast(field, CGF.Int8PtrTy->getPointerTo(0)); + llvm::Value *value = CGF.Builder.CreateLoad(field); + + CGF.BuildBlockRelease(value, Flags | BLOCK_BYREF_CALLER); + } + + void profileImpl(llvm::FoldingSetNodeID &id) const { + id.AddInteger(Flags.getBitMask()); + } +}; + +/// Emits the copy/dispose helpers for a __block variable with a +/// nontrivial copy constructor or destructor. +class CXXByrefHelpers : public CodeGenModule::ByrefHelpers { + QualType VarType; + const Expr *CopyExpr; + +public: + CXXByrefHelpers(CharUnits alignment, QualType type, + const Expr *copyExpr) + : ByrefHelpers(alignment), VarType(type), CopyExpr(copyExpr) {} + + bool needsCopy() const { return CopyExpr != 0; } + void emitCopy(CodeGenFunction &CGF, llvm::Value *destField, + llvm::Value *srcField) { + if (!CopyExpr) return; + CGF.EmitSynthesizedCXXCopyCtor(destField, srcField, CopyExpr); + } + + void emitDispose(CodeGenFunction &CGF, llvm::Value *field) { + EHScopeStack::stable_iterator cleanupDepth = CGF.EHStack.stable_begin(); + CGF.PushDestructorCleanup(VarType, field); + CGF.PopCleanupBlocks(cleanupDepth); + } + + void profileImpl(llvm::FoldingSetNodeID &id) const { + id.AddPointer(VarType.getCanonicalType().getAsOpaquePtr()); + } +}; +} // end anonymous namespace + +static llvm::Constant * +generateByrefCopyHelper(CodeGenFunction &CGF, + const llvm::StructType &byrefType, + CodeGenModule::ByrefHelpers &byrefInfo) { + ASTContext &Context = CGF.getContext(); + + QualType R = Context.VoidTy; FunctionArgList args; - ImplicitParamDecl dst(0, SourceLocation(), 0, getContext().VoidPtrTy); + ImplicitParamDecl dst(0, SourceLocation(), 0, Context.VoidPtrTy); args.push_back(&dst); - ImplicitParamDecl src(0, SourceLocation(), 0, getContext().VoidPtrTy); + ImplicitParamDecl src(0, SourceLocation(), 0, Context.VoidPtrTy); args.push_back(&src); const CGFunctionInfo &FI = - CGM.getTypes().getFunctionInfo(R, args, FunctionType::ExtInfo()); + CGF.CGM.getTypes().getFunctionInfo(R, args, FunctionType::ExtInfo()); - CodeGenTypes &Types = CGM.getTypes(); + CodeGenTypes &Types = CGF.CGM.getTypes(); const llvm::FunctionType *LTy = Types.GetFunctionType(FI, false); // FIXME: We'd like to put these into a mergable by content, with // internal linkage. llvm::Function *Fn = llvm::Function::Create(LTy, llvm::GlobalValue::InternalLinkage, - "__Block_byref_object_copy_", &CGM.getModule()); + "__Block_byref_object_copy_", &CGF.CGM.getModule()); IdentifierInfo *II - = &CGM.getContext().Idents.get("__Block_byref_object_copy_"); + = &Context.Idents.get("__Block_byref_object_copy_"); - FunctionDecl *FD = FunctionDecl::Create(getContext(), - getContext().getTranslationUnitDecl(), + FunctionDecl *FD = FunctionDecl::Create(Context, + Context.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), II, R, 0, SC_Static, SC_None, false, true); - StartFunction(FD, R, Fn, FI, args, SourceLocation()); - - // dst->x - llvm::Value *V = GetAddrOfLocalVar(&dst); - V = Builder.CreateBitCast(V, llvm::PointerType::get(T, 0)); - V = Builder.CreateLoad(V); - V = Builder.CreateStructGEP(V, 6, "x"); - llvm::Value *DstObj = V; - - // src->x - V = GetAddrOfLocalVar(&src); - V = Builder.CreateLoad(V); - V = Builder.CreateBitCast(V, T); - V = Builder.CreateStructGEP(V, 6, "x"); - - if (Expr *copyExpr = getContext().getBlockVarCopyInits(variable)) { - llvm::Value *SrcObj = V; - EmitSynthesizedCXXCopyCtor(DstObj, SrcObj, copyExpr); - } else { - DstObj = Builder.CreateBitCast(DstObj, VoidPtrTy); - V = Builder.CreateBitCast(V, VoidPtrPtrTy); - llvm::Value *SrcObj = Builder.CreateLoad(V); - flags |= BLOCK_BYREF_CALLER; - llvm::Value *N = llvm::ConstantInt::get(Int32Ty, flags.getBitMask()); - llvm::Value *F = CGM.getBlockObjectAssign(); - Builder.CreateCall3(F, DstObj, SrcObj, N); - } - - FinishFunction(); + CGF.StartFunction(FD, R, Fn, FI, args, SourceLocation()); - return llvm::ConstantExpr::getBitCast(Fn, Int8PtrTy); + if (byrefInfo.needsCopy()) { + const llvm::Type *byrefPtrType = byrefType.getPointerTo(0); + + // dst->x + llvm::Value *destField = CGF.GetAddrOfLocalVar(&dst); + destField = CGF.Builder.CreateLoad(destField); + destField = CGF.Builder.CreateBitCast(destField, byrefPtrType); + destField = CGF.Builder.CreateStructGEP(destField, 6, "x"); + + // src->x + llvm::Value *srcField = CGF.GetAddrOfLocalVar(&src); + srcField = CGF.Builder.CreateLoad(srcField); + srcField = CGF.Builder.CreateBitCast(srcField, byrefPtrType); + srcField = CGF.Builder.CreateStructGEP(srcField, 6, "x"); + + byrefInfo.emitCopy(CGF, destField, srcField); + } + + CGF.FinishFunction(); + + return llvm::ConstantExpr::getBitCast(Fn, CGF.Int8PtrTy); } -llvm::Constant * -CodeGenFunction::GeneratebyrefDestroyHelperFunction(const llvm::Type *T, - BlockFieldFlags flags, - const VarDecl *variable) { - QualType R = getContext().VoidTy; +/// Build the copy helper for a __block variable. +static llvm::Constant *buildByrefCopyHelper(CodeGenModule &CGM, + const llvm::StructType &byrefType, + CodeGenModule::ByrefHelpers &info) { + CodeGenFunction CGF(CGM); + return generateByrefCopyHelper(CGF, byrefType, info); +} + +/// Generate code for a __block variable's dispose helper. +static llvm::Constant * +generateByrefDisposeHelper(CodeGenFunction &CGF, + const llvm::StructType &byrefType, + CodeGenModule::ByrefHelpers &byrefInfo) { + ASTContext &Context = CGF.getContext(); + QualType R = Context.VoidTy; FunctionArgList args; - ImplicitParamDecl src(0, SourceLocation(), 0, getContext().VoidPtrTy); + ImplicitParamDecl src(0, SourceLocation(), 0, Context.VoidPtrTy); args.push_back(&src); const CGFunctionInfo &FI = - CGM.getTypes().getFunctionInfo(R, args, FunctionType::ExtInfo()); + CGF.CGM.getTypes().getFunctionInfo(R, args, FunctionType::ExtInfo()); - CodeGenTypes &Types = CGM.getTypes(); + CodeGenTypes &Types = CGF.CGM.getTypes(); const llvm::FunctionType *LTy = Types.GetFunctionType(FI, false); // FIXME: We'd like to put these into a mergable by content, with @@ -1306,82 +1380,96 @@ CodeGenFunction::GeneratebyrefDestroyHelperFunction(const llvm::Type *T, llvm::Function *Fn = llvm::Function::Create(LTy, llvm::GlobalValue::InternalLinkage, "__Block_byref_object_dispose_", - &CGM.getModule()); + &CGF.CGM.getModule()); IdentifierInfo *II - = &CGM.getContext().Idents.get("__Block_byref_object_dispose_"); + = &Context.Idents.get("__Block_byref_object_dispose_"); - FunctionDecl *FD = FunctionDecl::Create(getContext(), - getContext().getTranslationUnitDecl(), + FunctionDecl *FD = FunctionDecl::Create(Context, + Context.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), II, R, 0, SC_Static, SC_None, false, true); - StartFunction(FD, R, Fn, FI, args, SourceLocation()); + CGF.StartFunction(FD, R, Fn, FI, args, SourceLocation()); - llvm::Value *V = GetAddrOfLocalVar(&src); - V = Builder.CreateBitCast(V, llvm::PointerType::get(T, 0)); - V = Builder.CreateLoad(V); - V = Builder.CreateStructGEP(V, 6, "x"); + if (byrefInfo.needsDispose()) { + llvm::Value *V = CGF.GetAddrOfLocalVar(&src); + V = CGF.Builder.CreateLoad(V); + V = CGF.Builder.CreateBitCast(V, byrefType.getPointerTo(0)); + V = CGF.Builder.CreateStructGEP(V, 6, "x"); - // If it's not any kind of special object, it must have a destructor - // or something. - if (!flags.isSpecialPointer()) { - EHScopeStack::stable_iterator CleanupDepth = EHStack.stable_begin(); - PushDestructorCleanup(variable->getType(), V); - PopCleanupBlocks(CleanupDepth); - - // Otherwise, call _Block_object_dispose. - } else { - V = Builder.CreateBitCast(V, llvm::PointerType::get(Int8PtrTy, 0)); - V = Builder.CreateLoad(V); - - flags |= BLOCK_BYREF_CALLER; - BuildBlockRelease(V, flags); + byrefInfo.emitDispose(CGF, V); } - FinishFunction(); + CGF.FinishFunction(); - return llvm::ConstantExpr::getBitCast(Fn, Int8PtrTy); + return llvm::ConstantExpr::getBitCast(Fn, CGF.Int8PtrTy); } -llvm::Constant *CodeGenModule::BuildbyrefCopyHelper(const llvm::Type *T, - BlockFieldFlags flags, - unsigned align, - const VarDecl *var) { - // All alignments below pointer alignment are bumped up, as we - // always have at least that much alignment to begin with. - if (align < PointerAlignInBytes) align = PointerAlignInBytes; - - // As an optimization, we only generate a single function of each kind we - // might need. We need a different one for each alignment and for each - // setting of flags. We mix Align and flag to get the kind. - uint64_t Kind = (uint64_t)align*BLOCK_BYREF_CURRENT_MAX + flags.getBitMask(); - llvm::Constant *&Entry = AssignCache[Kind]; - if (!Entry) - Entry = CodeGenFunction(*this). - GeneratebyrefCopyHelperFunction(T, flags, var); - return Entry; +/// Build the dispose helper for a __block variable. +static llvm::Constant *buildByrefDisposeHelper(CodeGenModule &CGM, + const llvm::StructType &byrefType, + CodeGenModule::ByrefHelpers &info) { + CodeGenFunction CGF(CGM); + return generateByrefDisposeHelper(CGF, byrefType, info); } -llvm::Constant *CodeGenModule::BuildbyrefDestroyHelper(const llvm::Type *T, - BlockFieldFlags flags, - unsigned align, - const VarDecl *var) { - // All alignments below pointer alignment are bumped up, as we - // always have at least that much alignment to begin with. - if (align < PointerAlignInBytes) align = PointerAlignInBytes; - - // As an optimization, we only generate a single function of each kind we - // might need. We need a different one for each alignment and for each - // setting of flags. We mix Align and flag to get the kind. - uint64_t Kind = (uint64_t)align*BLOCK_BYREF_CURRENT_MAX + flags.getBitMask(); - llvm::Constant *&Entry = DestroyCache[Kind]; - if (!Entry) - Entry = CodeGenFunction(*this). - GeneratebyrefDestroyHelperFunction(T, flags, var); - return Entry; +/// +template static T *buildByrefHelpers(CodeGenModule &CGM, + const llvm::StructType &byrefTy, + T &byrefInfo) { + // Increase the field's alignment to be at least pointer alignment, + // since the layout of the byref struct will guarantee at least that. + byrefInfo.Alignment = std::max(byrefInfo.Alignment, + CharUnits::fromQuantity(CGM.PointerAlignInBytes)); + + llvm::FoldingSetNodeID id; + byrefInfo.Profile(id); + + void *insertPos; + CodeGenModule::ByrefHelpers *node + = CGM.ByrefHelpersCache.FindNodeOrInsertPos(id, insertPos); + if (node) return static_cast(node); + + byrefInfo.CopyHelper = buildByrefCopyHelper(CGM, byrefTy, byrefInfo); + byrefInfo.DisposeHelper = buildByrefDisposeHelper(CGM, byrefTy, byrefInfo); + + T *copy = new (CGM.getContext()) T(byrefInfo); + CGM.ByrefHelpersCache.InsertNode(copy, insertPos); + return copy; +} + +CodeGenModule::ByrefHelpers * +CodeGenFunction::buildByrefHelpers(const llvm::StructType &byrefType, + const AutoVarEmission &emission) { + const VarDecl &var = *emission.Variable; + QualType type = var.getType(); + + if (const CXXRecordDecl *record = type->getAsCXXRecordDecl()) { + const Expr *copyExpr = CGM.getContext().getBlockVarCopyInits(&var); + if (!copyExpr && record->hasTrivialDestructor()) return 0; + + CXXByrefHelpers byrefInfo(emission.Alignment, type, copyExpr); + return ::buildByrefHelpers(CGM, byrefType, byrefInfo); + } + + BlockFieldFlags flags; + if (type->isBlockPointerType()) { + flags |= BLOCK_FIELD_IS_BLOCK; + } else if (CGM.getContext().isObjCNSObjectType(type) || + type->isObjCObjectPointerType()) { + flags |= BLOCK_FIELD_IS_OBJECT; + } else { + return 0; + } + + if (type.isObjCGCWeak()) + flags |= BLOCK_FIELD_IS_WEAK; + + ObjectByrefHelpers byrefInfo(emission.Alignment, flags); + return ::buildByrefHelpers(CGM, byrefType, byrefInfo); } unsigned CodeGenFunction::getByRefValueLLVMField(const ValueDecl *VD) const { @@ -1495,37 +1583,25 @@ const llvm::Type *CodeGenFunction::BuildByRefType(const VarDecl *D) { /// Initialize the structural components of a __block variable, i.e. /// everything but the actual object. void CodeGenFunction::emitByrefStructureInit(const AutoVarEmission &emission) { - llvm::Value *V; + // Find the address of the local. + llvm::Value *addr = emission.Address; + + // That's an alloca of the byref structure type. + const llvm::StructType *byrefType = cast( + cast(addr->getType())->getElementType()); - BlockFieldFlags fieldFlags; - bool fieldNeedsCopyDispose = false; + // Build the byref helpers if necessary. This is null if we don't need any. + CodeGenModule::ByrefHelpers *helpers = + buildByrefHelpers(*byrefType, emission); const VarDecl &D = *emission.Variable; QualType type = D.getType(); - if (type->isBlockPointerType()) { - fieldFlags |= BLOCK_FIELD_IS_BLOCK; - fieldNeedsCopyDispose = true; - } else if (getContext().isObjCNSObjectType(type) || - type->isObjCObjectPointerType()) { - fieldFlags |= BLOCK_FIELD_IS_OBJECT; - fieldNeedsCopyDispose = true; - } else if (getLangOptions().CPlusPlus) { - if (getContext().getBlockVarCopyInits(&D)) - fieldNeedsCopyDispose = true; - else if (const CXXRecordDecl *record = type->getAsCXXRecordDecl()) - fieldNeedsCopyDispose = !record->hasTrivialDestructor(); - } - - llvm::Value *addr = emission.Address; - - // FIXME: Someone double check this. - if (type.isObjCGCWeak()) - fieldFlags |= BLOCK_FIELD_IS_WEAK; + llvm::Value *V; // Initialize the 'isa', which is just 0 or 1. int isa = 0; - if (fieldFlags & BLOCK_FIELD_IS_WEAK) + if (type.isObjCGCWeak()) isa = 1; V = Builder.CreateIntToPtr(Builder.getInt32(isa), Int8PtrTy, "isa"); Builder.CreateStore(V, Builder.CreateStructGEP(addr, 0, "byref.isa")); @@ -1538,29 +1614,20 @@ void CodeGenFunction::emitByrefStructureInit(const AutoVarEmission &emission) { // c) the flags field is set to either 0 if no helper functions are // needed or BLOCK_HAS_COPY_DISPOSE if they are, BlockFlags flags; - if (fieldNeedsCopyDispose) flags |= BLOCK_HAS_COPY_DISPOSE; + if (helpers) flags |= BLOCK_HAS_COPY_DISPOSE; Builder.CreateStore(llvm::ConstantInt::get(IntTy, flags.getBitMask()), Builder.CreateStructGEP(addr, 2, "byref.flags")); - const llvm::Type *V1; - V1 = cast(addr->getType())->getElementType(); - V = llvm::ConstantInt::get(IntTy, CGM.GetTargetTypeStoreSize(V1).getQuantity()); + CharUnits byrefSize = CGM.GetTargetTypeStoreSize(byrefType); + V = llvm::ConstantInt::get(IntTy, byrefSize.getQuantity()); Builder.CreateStore(V, Builder.CreateStructGEP(addr, 3, "byref.size")); - if (fieldNeedsCopyDispose) { - CharUnits alignment = emission.Alignment; - + if (helpers) { llvm::Value *copy_helper = Builder.CreateStructGEP(addr, 4); - Builder.CreateStore(CGM.BuildbyrefCopyHelper(addr->getType(), fieldFlags, - alignment.getQuantity(), &D), - copy_helper); + Builder.CreateStore(helpers->CopyHelper, copy_helper); llvm::Value *destroy_helper = Builder.CreateStructGEP(addr, 5); - Builder.CreateStore(CGM.BuildbyrefDestroyHelper(addr->getType(), - fieldFlags, - alignment.getQuantity(), - &D), - destroy_helper); + Builder.CreateStore(helpers->DisposeHelper, destroy_helper); } } diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 10f279e750..f3b00e3370 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1107,13 +1107,6 @@ public: llvm::Constant *GenerateCopyHelperFunction(const CGBlockInfo &blockInfo); llvm::Constant *GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo); - llvm::Constant *GeneratebyrefCopyHelperFunction(const llvm::Type *, - BlockFieldFlags flags, - const VarDecl *BD); - llvm::Constant *GeneratebyrefDestroyHelperFunction(const llvm::Type *T, - BlockFieldFlags flags, - const VarDecl *BD); - void BuildBlockRelease(llvm::Value *DeclPtr, BlockFieldFlags flags); class AutoVarEmission; @@ -2182,6 +2175,10 @@ private: } void EmitDeclMetadata(); + + CodeGenModule::ByrefHelpers * + buildByrefHelpers(const llvm::StructType &byrefType, + const AutoVarEmission &emission); }; /// Helper class with most of the code for saving a value for a diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index aa6a0535a1..0f86257757 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -247,9 +247,6 @@ class CodeGenModule : public CodeGenTypeCache { int GlobalUniqueCount; } Block; - llvm::DenseMap AssignCache; - llvm::DenseMap DestroyCache; - /// @} public: CodeGenModule(ASTContext &C, const CodeGenOptions &CodeGenOpts, @@ -385,14 +382,35 @@ public: CastExpr::path_const_iterator PathBegin, CastExpr::path_const_iterator PathEnd); - llvm::Constant *BuildbyrefCopyHelper(const llvm::Type *T, - BlockFieldFlags flags, - unsigned Align, - const VarDecl *variable); - llvm::Constant *BuildbyrefDestroyHelper(const llvm::Type *T, - BlockFieldFlags flags, - unsigned Align, - const VarDecl *variable); + /// A pair of helper functions for a __block variable. + class ByrefHelpers : public llvm::FoldingSetNode { + public: + llvm::Constant *CopyHelper; + llvm::Constant *DisposeHelper; + + /// The alignment of the field. This is important because + /// different offsets to the field within the byref struct need to + /// have different helper functions. + CharUnits Alignment; + + ByrefHelpers(CharUnits alignment) : Alignment(alignment) {} + virtual ~ByrefHelpers(); + + void Profile(llvm::FoldingSetNodeID &id) const { + id.AddInteger(Alignment.getQuantity()); + profileImpl(id); + } + virtual void profileImpl(llvm::FoldingSetNodeID &id) const = 0; + + virtual bool needsCopy() const { return true; } + virtual void emitCopy(CodeGenFunction &CGF, + llvm::Value *dest, llvm::Value *src) = 0; + + virtual bool needsDispose() const { return true; } + virtual void emitDispose(CodeGenFunction &CGF, llvm::Value *field) = 0; + }; + + llvm::FoldingSet ByrefHelpersCache; /// getUniqueBlockCount - Fetches the global unique block count. int getUniqueBlockCount() { return ++Block.GlobalUniqueCount; } diff --git a/test/CodeGenCXX/blocks.cpp b/test/CodeGenCXX/blocks.cpp index ea174b57e4..d66debea2b 100644 --- a/test/CodeGenCXX/blocks.cpp +++ b/test/CodeGenCXX/blocks.cpp @@ -55,3 +55,35 @@ namespace test1 { // ...or non-trivial copy constructors, but it's not clear how to do // that and still have a constant initializer in '03. } + +namespace test2 { + struct A { + A(); + A(const A &); + ~A(); + }; + + struct B { + B(); + B(const B &); + ~B(); + }; + + // CHECK: define void @_ZN5test24testEv() + void test() { + __block A a; + __block B b; + } + + // CHECK: define internal void @__Block_byref_object_copy + // CHECK: call void @_ZN5test21AC1ERKS0_( + + // CHECK: define internal void @__Block_byref_object_dispose + // CHECK: call void @_ZN5test21AD1Ev( + + // CHECK: define internal void @__Block_byref_object_copy + // CHECK: call void @_ZN5test21BC1ERKS0_( + + // CHECK: define internal void @__Block_byref_object_dispose + // CHECK: call void @_ZN5test21BD1Ev( +} -- 2.40.0