At -O0, prefer objc_storeStrong with a null new value to the

combination of a load+objc_release;  this is generally better
for tools that try to track why values are retained and
released.  Also use objc_storeStrong when copying a block
(again, only at -O0), which requires us to do a preliminary
store of null in order to compensate for objc_storeStrong's
assign semantics.

llvm-svn: 166085
This commit is contained in:
John McCall 2012-10-17 02:28:37 +00:00
parent 6f7206132f
commit e68b8f4dcc
14 changed files with 176 additions and 64 deletions

View File

@ -1241,7 +1241,8 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
const Expr *copyExpr = ci->getCopyExpr();
BlockFieldFlags flags;
bool isARCWeakCapture = false;
bool useARCWeakCopy = false;
bool useARCStrongCopy = false;
if (copyExpr) {
assert(!ci->isByRef());
@ -1254,21 +1255,35 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
} else if (type->isObjCRetainableType()) {
flags = BLOCK_FIELD_IS_OBJECT;
if (type->isBlockPointerType())
bool isBlockPointer = type->isBlockPointerType();
if (isBlockPointer)
flags = BLOCK_FIELD_IS_BLOCK;
// Special rules for ARC captures:
if (getLangOpts().ObjCAutoRefCount) {
Qualifiers qs = type.getQualifiers();
// Don't generate special copy logic for a captured object
// unless it's __strong or __weak.
if (!qs.hasStrongOrWeakObjCLifetime())
continue;
// We need to register __weak direct captures with the runtime.
if (qs.getObjCLifetime() == Qualifiers::OCL_Weak) {
useARCWeakCopy = true;
// Support __weak direct captures.
if (qs.getObjCLifetime() == Qualifiers::OCL_Weak)
isARCWeakCapture = true;
// We need to retain the copied value for __strong direct captures.
} else if (qs.getObjCLifetime() == Qualifiers::OCL_Strong) {
// If it's a block pointer, we have to copy the block and
// assign that to the destination pointer, so we might as
// well use _Block_object_assign. Otherwise we can avoid that.
if (!isBlockPointer)
useARCStrongCopy = true;
// Otherwise the memcpy is fine.
} else {
continue;
}
// Non-ARC captures of retainable pointers are strong and
// therefore require a call to _Block_object_assign.
} else {
// fall through
}
} else {
continue;
@ -1281,14 +1296,36 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
// If there's an explicit copy expression, we do that.
if (copyExpr) {
EmitSynthesizedCXXCopyCtor(dstField, srcField, copyExpr);
} else if (isARCWeakCapture) {
} else if (useARCWeakCopy) {
EmitARCCopyWeak(dstField, srcField);
} else {
llvm::Value *srcValue = Builder.CreateLoad(srcField, "blockcopy.src");
srcValue = Builder.CreateBitCast(srcValue, VoidPtrTy);
llvm::Value *dstAddr = Builder.CreateBitCast(dstField, VoidPtrTy);
Builder.CreateCall3(CGM.getBlockObjectAssign(), dstAddr, srcValue,
llvm::ConstantInt::get(Int32Ty, flags.getBitMask()));
if (useARCStrongCopy) {
// At -O0, store null into the destination field (so that the
// storeStrong doesn't over-release) and then call storeStrong.
// This is a workaround to not having an initStrong call.
if (CGM.getCodeGenOpts().OptimizationLevel == 0) {
llvm::PointerType *ty = cast<llvm::PointerType>(srcValue->getType());
llvm::Value *null = llvm::ConstantPointerNull::get(ty);
Builder.CreateStore(null, dstField);
EmitARCStoreStrongCall(dstField, srcValue, true);
// With optimization enabled, take advantage of the fact that
// the blocks runtime guarantees a memcpy of the block data, and
// just emit a retain of the src field.
} else {
EmitARCRetainNonBlock(srcValue);
// We don't need this anymore, so kill it. It's not quite
// worth the annoyance to avoid creating it in the first place.
cast<llvm::Instruction>(dstField)->eraseFromParent();
}
} else {
srcValue = Builder.CreateBitCast(srcValue, VoidPtrTy);
llvm::Value *dstAddr = Builder.CreateBitCast(dstField, VoidPtrTy);
Builder.CreateCall3(CGM.getBlockObjectAssign(), dstAddr, srcValue,
llvm::ConstantInt::get(Int32Ty, flags.getBitMask()));
}
}
}
@ -1353,7 +1390,8 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) {
BlockFieldFlags flags;
const CXXDestructorDecl *dtor = 0;
bool isARCWeakCapture = false;
bool useARCWeakDestroy = false;
bool useARCStrongDestroy = false;
if (ci->isByRef()) {
flags = BLOCK_FIELD_IS_BYREF;
@ -1379,7 +1417,11 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) {
// Support __weak direct captures.
if (qs.getObjCLifetime() == Qualifiers::OCL_Weak)
isARCWeakCapture = true;
useARCWeakDestroy = true;
// Tools really want us to use objc_storeStrong here.
else
useARCStrongDestroy = true;
}
} else {
continue;
@ -1393,9 +1435,13 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) {
PushDestructorCleanup(dtor, srcField);
// If this is a __weak capture, emit the release directly.
} else if (isARCWeakCapture) {
} else if (useARCWeakDestroy) {
EmitARCDestroyWeak(srcField);
// Destroy strong objects with a call if requested.
} else if (useARCStrongDestroy) {
EmitARCDestroyStrong(srcField, /*precise*/ false);
// Otherwise we call _Block_object_dispose. It wouldn't be too
// hard to just emit this as a cleanup if we wanted to make sure
// that things were done in reverse.
@ -1494,10 +1540,7 @@ public:
}
void emitDispose(CodeGenFunction &CGF, llvm::Value *field) {
llvm::LoadInst *value = CGF.Builder.CreateLoad(field);
value->setAlignment(Alignment.getQuantity());
CGF.EmitARCRelease(value, /*precise*/ false);
CGF.EmitARCDestroyStrong(field, /*precise*/ false);
}
void profileImpl(llvm::FoldingSetNodeID &id) const {
@ -1527,10 +1570,7 @@ public:
}
void emitDispose(CodeGenFunction &CGF, llvm::Value *field) {
llvm::LoadInst *value = CGF.Builder.CreateLoad(field);
value->setAlignment(Alignment.getQuantity());
CGF.EmitARCRelease(value, /*precise*/ false);
CGF.EmitARCDestroyStrong(field, /*precise*/ false);
}
void profileImpl(llvm::FoldingSetNodeID &id) const {

View File

@ -1949,6 +1949,28 @@ void CodeGenFunction::EmitARCRelease(llvm::Value *value, bool precise) {
}
}
/// Destroy a __strong variable.
///
/// At -O0, emit a call to store 'null' into the address;
/// instrumenting tools prefer this because the address is exposed,
/// but it's relatively cumbersome to optimize.
///
/// At -O1 and above, just load and call objc_release.
///
/// call void \@objc_storeStrong(i8** %addr, i8* null)
void CodeGenFunction::EmitARCDestroyStrong(llvm::Value *addr, bool precise) {
if (CGM.getCodeGenOpts().OptimizationLevel == 0) {
llvm::PointerType *addrTy = cast<llvm::PointerType>(addr->getType());
llvm::Value *null = llvm::ConstantPointerNull::get(
cast<llvm::PointerType>(addrTy->getElementType()));
EmitARCStoreStrongCall(addr, null, /*ignored*/ true);
return;
}
llvm::Value *value = Builder.CreateLoad(addr);
EmitARCRelease(value, precise);
}
/// Store into a strong object. Always calls this:
/// call void \@objc_storeStrong(i8** %addr, i8* %value)
llvm::Value *CodeGenFunction::EmitARCStoreStrongCall(llvm::Value *addr,
@ -2222,15 +2244,13 @@ void CodeGenFunction::EmitObjCMRRAutoreleasePoolPop(llvm::Value *Arg) {
void CodeGenFunction::destroyARCStrongPrecise(CodeGenFunction &CGF,
llvm::Value *addr,
QualType type) {
llvm::Value *ptr = CGF.Builder.CreateLoad(addr, "strongdestroy");
CGF.EmitARCRelease(ptr, /*precise*/ true);
CGF.EmitARCDestroyStrong(addr, /*precise*/ true);
}
void CodeGenFunction::destroyARCStrongImprecise(CodeGenFunction &CGF,
llvm::Value *addr,
QualType type) {
llvm::Value *ptr = CGF.Builder.CreateLoad(addr, "strongdestroy");
CGF.EmitARCRelease(ptr, /*precise*/ false);
CGF.EmitARCDestroyStrong(addr, /*precise*/ false);
}
void CodeGenFunction::destroyARCWeak(CodeGenFunction &CGF,

View File

@ -2353,6 +2353,7 @@ public:
llvm::Value *EmitARCRetain(QualType type, llvm::Value *value);
llvm::Value *EmitARCRetainNonBlock(llvm::Value *value);
llvm::Value *EmitARCRetainBlock(llvm::Value *value, bool mandatory);
void EmitARCDestroyStrong(llvm::Value *addr, bool precise);
void EmitARCRelease(llvm::Value *value, bool precise);
llvm::Value *EmitARCAutorelease(llvm::Value *value);
llvm::Value *EmitARCAutoreleaseReturnValue(llvm::Value *value);

View File

@ -13,8 +13,7 @@ void test1(void) {
// CHECK-NEXT: call void asm sideeffect "mov\09r7, r7
// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
// CHECK-NEXT: store i8* [[T1]],
// CHECK-NEXT: load
// CHECK-NEXT: call void @objc_release
// CHECK-NEXT: call void @objc_storeStrong(
// CHECK-NEXT: ret void
id x = test1_helper();
}

View File

@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-optzns -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-UNOPT %s
// This shouldn't crash.
void test0(id (^maker)(void)) {
@ -41,6 +42,24 @@ void test2(id x) {
// CHECK-NEXT: ret void
extern void test2_helper(id (^)(void));
test2_helper(^{ return x; });
// CHECK: define internal void @__copy_helper_block_
// CHECK: [[T0:%.*]] = load i8**
// CHECK-NEXT: [[SRC:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]*
// CHECK-NEXT: [[T0:%.*]] = load i8**
// CHECK-NEXT: [[DST:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]*
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[SRC]], i32 0, i32 5
// CHECK-NEXT: [[T1:%.*]] = load i8** [[T0]]
// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) nounwind
// CHECK-NEXT: ret void
// CHECK: define internal void @__destroy_helper_block_
// CHECK: [[T0:%.*]] = load i8**
// CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]*
// CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[T1]], i32 0, i32 5
// CHECK-NEXT: [[T3:%.*]] = load i8** [[T2]]
// CHECK-NEXT: call void @objc_release(i8* [[T3]])
// CHECK-NEXT: ret void
}
void test3(void (^sink)(id*)) {
@ -359,7 +378,7 @@ void test10a(void) {
// CHECK: [[T0:%.*]] = load i8** {{%.*}}
// CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[BYREF_T]]*
// CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds [[BYREF_T]]* [[T1]], i32 0, i32 6
// CHECK-NEXT: [[T3:%.*]] = load void ()** [[T2]], align 8
// CHECK-NEXT: [[T3:%.*]] = load void ()** [[T2]]
// CHECK-NEXT: [[T4:%.*]] = bitcast void ()* [[T3]] to i8*
// CHECK-NEXT: call void @objc_release(i8* [[T4]])
// CHECK-NEXT: ret void
@ -589,3 +608,42 @@ id (^test17(id self, int which))(void) {
// CHECK-NEXT: call void @objc_release(i8* [[T0]])
// CHECK-NEXT: store i32
// CHECK-NEXT: br label
void test18(id x) {
// CHECK-UNOPT: define void @test18(
// CHECK-UNOPT: [[X:%.*]] = alloca i8*,
// CHECK-UNOPT-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
// CHECK-UNOPT-NEXT: [[PARM:%.*]] = call i8* @objc_retain(i8* {{%.*}})
// CHECK-UNOPT-NEXT: store i8* [[PARM]], i8** [[X]]
// CHECK-UNOPT-NEXT: [[SLOTREL:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK-UNOPT: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5
// CHECK-UNOPT-NEXT: [[T0:%.*]] = load i8** [[X]],
// CHECK-UNOPT-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]])
// CHECK-UNOPT-NEXT: store i8* [[T1]], i8** [[SLOT]],
// CHECK-UNOPT-NEXT: bitcast
// CHECK-UNOPT-NEXT: call void @test18_helper(
// CHECK-UNOPT-NEXT: call void @objc_storeStrong(i8** [[SLOTREL]], i8* null) nounwind
// CHECK-UNOPT-NEXT: call void @objc_storeStrong(i8** [[X]], i8* null) nounwind
// CHECK-UNOPT-NEXT: ret void
extern void test18_helper(id (^)(void));
test18_helper(^{ return x; });
// CHECK-UNOPT: define internal void @__copy_helper_block_
// CHECK-UNOPT: [[T0:%.*]] = load i8**
// CHECK-UNOPT-NEXT: [[SRC:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]*
// CHECK-UNOPT-NEXT: [[T0:%.*]] = load i8**
// CHECK-UNOPT-NEXT: [[DST:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]*
// CHECK-UNOPT-NEXT: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[SRC]], i32 0, i32 5
// CHECK-UNOPT-NEXT: [[T1:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[DST]], i32 0, i32 5
// CHECK-UNOPT-NEXT: [[T2:%.*]] = load i8** [[T0]]
// CHECK-UNOPT-NEXT: store i8* null, i8** [[T1]]
// CHECK-UNOPT-NEXT: call void @objc_storeStrong(i8** [[T1]], i8* [[T2]]) nounwind
// CHECK-UNOPT-NEXT: ret void
// CHECK-UNOPT: define internal void @__destroy_helper_block_
// CHECK-UNOPT: [[T0:%.*]] = load i8**
// CHECK-UNOPT-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[BLOCK_T]]*
// CHECK-UNOPT-NEXT: [[T2:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[T1]], i32 0, i32 5
// CHECK-UNOPT-NEXT: call void @objc_storeStrong(i8** [[T2]], i8* null)
// CHECK-UNOPT-NEXT: ret void
}

View File

@ -20,9 +20,8 @@ void test0(void) {
// CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) nounwind
// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to [[ETY]]*
// CHECK-NEXT: store [[ETY]]* [[T4]], [[ETY]]** [[E]]
// CHECK-NEXT: [[T0:%.*]] = load [[ETY]]** [[E]]
// CHECK-NEXT: [[T1:%.*]] = bitcast [[ETY]]* [[T0]] to i8*
// CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind
// CHECK-NEXT: [[T0:%.*]] = bitcast [[ETY]]** [[E]] to i8**
// CHECK-NEXT: call void @objc_storeStrong(i8** [[T0]], i8* null) nounwind
// CHECK-NEXT: call void @objc_end_catch() nounwind
void test1_helper(void);

View File

@ -67,8 +67,7 @@ void test0(NSArray *array) {
// CHECK-LP64-NEXT: store i8* [[T2]], i8** [[T0]]
// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]]
// CHECK-LP64: call void @use_block(
// CHECK-LP64-NEXT: [[T1:%.*]] = load i8** [[D0]]
// CHECK-LP64-NEXT: call void @objc_release(i8* [[T1]])
// CHECK-LP64-NEXT: call void @objc_storeStrong(i8** [[D0]], i8* null)
// CHECK-LP64: [[T0:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_
// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[ARRAY_T]]* [[SAVED_ARRAY]] to i8*
@ -79,9 +78,8 @@ void test0(NSArray *array) {
// CHECK-LP64-NEXT: call void @objc_release(i8* [[T0]])
// Destroy 'array'.
// CHECK-LP64: [[T0:%.*]] = load [[ARRAY_T]]** [[ARRAY]]
// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[ARRAY_T]]* [[T0]] to i8*
// CHECK-LP64-NEXT: call void @objc_release(i8* [[T1]])
// CHECK-LP64: [[T0:%.*]] = bitcast [[ARRAY_T]]** [[ARRAY]] to i8**
// CHECK-LP64-NEXT: call void @objc_storeStrong(i8** [[T0]], i8* null)
// CHECK-LP64-NEXT: ret void
// CHECK-LP64: define internal void @__test0_block_invoke

View File

@ -1,9 +1,13 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc -emit-llvm %s -o - | FileCheck %s
// rdar://problem/9224855
id make(void) __attribute__((ns_returns_retained));
void test0() {
make();
id x = 0;
// CHECK: call void @objc_release(
// CHECK: call void @objc_storeStrong(
}
// CHECK: declare extern_weak void @objc_release(
// CHECK: declare extern_weak void @objc_storeStrong(

View File

@ -11,8 +11,8 @@ void test0(Test0 *t0, id value) {
// CHECK: call i8* @objc_retain(
// CHECK: call i8* @objc_retain(
// CHECK: @objc_msgSend
// CHECK: call void @objc_release(
// CHECK: call void @objc_release(
// CHECK: call void @objc_storeStrong(
// CHECK: call void @objc_storeStrong(
struct S1 { Class isa; };
@interface Test1

View File

@ -20,11 +20,9 @@ void test0(Test0 *val) {
// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]])
// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to [[TEST0]]*
// CHECK-NEXT: store [[TEST0]]* [[T2]], [[TEST0]]** [[X]]
// CHECK-NEXT: [[T0:%.*]] = load [[TEST0]]** [[X]]
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST0]]* [[T0]] to i8*
// CHECK-NEXT: call void @objc_release(i8* [[T1]])
// CHECK-NEXT: [[T0:%.*]] = load [[TEST0]]** [[VAL]]
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST0]]* [[T0]] to i8*
// CHECK-NEXT: call void @objc_release(i8* [[T1]])
// CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST0]]** [[X]] to i8**
// CHECK-NEXT: call void @objc_storeStrong(i8** [[T0]], i8* null)
// CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST0]]** [[VAL]] to i8**
// CHECK-NEXT: call void @objc_storeStrong(i8** [[T0]], i8* null)
// CHECK-NEXT: ret void
}

View File

@ -13,9 +13,8 @@ id func() {
// CHECK: [[call:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend
// CHECK: [[SIX:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[call]]) nounwind
// CHECK: [[ARRAY:%.*]] = load %0**
// CHECK: [[ARRAY_CASTED:%.*]] = bitcast{{.*}}[[ARRAY]] to i8*
// CHECK: call void @objc_release(i8* [[ARRAY_CASTED]])
// CHECK: [[ARRAY_CASTED:%.*]] = bitcast %0** {{%.*}} to i8**
// CHECK: call void @objc_storeStrong(i8** [[ARRAY_CASTED]], i8* null)
// CHECK: [[EIGHT:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[SIX]]) nounwind
// CHECK: ret i8* [[EIGHT]]

View File

@ -20,9 +20,8 @@ void test0(void) {
// CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) nounwind
// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to [[ETY]]*
// CHECK-NEXT: store [[ETY]]* [[T4]], [[ETY]]** [[E]]
// CHECK-NEXT: [[T0:%.*]] = load [[ETY]]** [[E]]
// CHECK-NEXT: [[T1:%.*]] = bitcast [[ETY]]* [[T0]] to i8*
// CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind
// CHECK-NEXT: [[T0:%.*]] = bitcast [[ETY]]** [[E]] to i8**
// CHECK-NEXT: call void @objc_storeStrong(i8** [[T0]], i8* null) nounwind
// CHECK-NEXT: call void @objc_end_catch() nounwind
void test1_helper(void);
@ -60,9 +59,8 @@ void test2(void) {
// CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) nounwind
// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to [[ETY]]*
// CHECK-NEXT: store [[ETY]]* [[T4]], [[ETY]]** [[E]]
// CHECK-NEXT: [[T0:%.*]] = load [[ETY]]** [[E]]
// CHECK-NEXT: [[T1:%.*]] = bitcast [[ETY]]* [[T0]] to i8*
// CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind
// CHECK-NEXT: [[T0:%.*]] = bitcast [[ETY]]** [[E]] to i8**
// CHECK-NEXT: call void @objc_storeStrong(i8** [[T0]], i8* null) nounwind
// CHECK-NEXT: call void @__cxa_end_catch() nounwind
void test3_helper(void);

View File

@ -35,7 +35,7 @@ void test_new(id invalue) {
// CHECK: call i8* @objc_initWeak
new __weak id(invalue);
// CHECK: call void @objc_release
// CHECK: call void @objc_storeStrong
// CHECK: ret void
}
@ -76,8 +76,7 @@ void test_array_delete(__strong id *sptr, __weak id *wptr) {
// CHECK-NEXT: icmp eq i8** [[BEGIN]], [[END]]
// CHECK: [[PAST:%.*]] = phi i8** [ [[END]], {{%.*}} ], [ [[CUR:%.*]],
// CHECK-NEXT: [[CUR]] = getelementptr inbounds i8** [[PAST]], i64 -1
// CHECK-NEXT: [[T0:%.*]] = load i8** [[CUR]]
// CHECK-NEXT: call void @objc_release(i8* [[T0]])
// CHECK-NEXT: call void @objc_storeStrong(i8** [[CUR]], i8* null)
// CHECK-NEXT: icmp eq i8** [[CUR]], [[BEGIN]]
// CHECK: call void @_ZdaPv
delete [] sptr;

View File

@ -111,7 +111,7 @@ void test_ObjCBlockMember_copy_assign(ObjCBlockMember m1, ObjCBlockMember m2) {
// Implicitly-generated destructor for ObjCBlockMember
// CHECK: define linkonce_odr void @_ZN15ObjCBlockMemberD2Ev
// CHECK: call void @objc_release(i8*
// CHECK: call void @objc_storeStrong(i8*
// CHECK: ret
// Implicitly-generated default constructor for ObjCBlockMember
@ -134,8 +134,7 @@ void test_ObjCBlockMember_copy_assign(ObjCBlockMember m1, ObjCBlockMember m2) {
// CHECK-NEXT: br label
// CHECK: [[PAST:%.*]] = phi i8** [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ]
// CHECK-NEXT: [[CUR]] = getelementptr inbounds i8** [[PAST]], i64 -1
// CHECK-NEXT: [[T0:%.*]] = load i8** [[CUR]]
// CHECK-NEXT: call void @objc_release(i8* [[T0]])
// CHECK-NEXT: call void @objc_storeStrong(i8** [[CUR]], i8* null)
// CHECK-NEXT: [[T1:%.*]] = icmp eq i8** [[CUR]], [[BEGIN]]
// CHECK-NEXT: br i1 [[T1]],
// CHECK: ret void
@ -154,7 +153,7 @@ void test_ObjCBlockMember_copy_assign(ObjCBlockMember m1, ObjCBlockMember m2) {
// Implicitly-generated destructor for ObjCMember
// CHECK: define linkonce_odr void @_ZN10ObjCMemberD2Ev
// CHECK: call void @objc_release
// CHECK: call void @objc_storeStrong
// CHECK: ret void
// Implicitly-generated default constructor for ObjCMember