ahatanak created this revision. ahatanak added a reviewer: rjmccall. ahatanak added a subscriber: cfe-commits.
This patch fixes a bug where EmitObjCForCollectionStmt doesn't pop cleanups for captures. For example, in the following for-in loop, a block which captures self is passed to foo1: for (id x in [self foo1:^{ use(self); }]) { use(x); break; } The current code in EmitObjCForCollectionStmt doesn't pop the cleanup for the captured self before completing IR generation for the for-in loop. This causes code-gen to generate incorrect IR in which self gets released twice. rdar://problem/16865751 http://reviews.llvm.org/D18618 Files: lib/CodeGen/CGObjC.cpp test/CodeGenObjC/arc-foreach.m Index: test/CodeGenObjC/arc-foreach.m =================================================================== --- test/CodeGenObjC/arc-foreach.m +++ test/CodeGenObjC/arc-foreach.m @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-runtime-has-weak -triple x86_64-apple-darwin -emit-llvm %s -o %t-64.s // RUN: FileCheck -check-prefix CHECK-LP64 --input-file=%t-64.s %s +// REQUIRES: asserts // rdar://9503326 // rdar://9606600 @@ -170,4 +171,53 @@ // CHECK-LP64-NEXT: br label [[L]] } +@interface NSObject @end + +@interface I1 : NSObject +- (NSArray *) foo1:(void (^)(void))block; +- (void) foo2; +@end + +NSArray *array4; + +@implementation I1 : NSObject +- (NSArray *) foo1:(void (^)(void))block { + block(); + return array4; +} + +- (void) foo2 { + for (id x in [self foo1:^{ use(self); }]) { + use(x); + break; + } +} +@end + +// CHECK-LP64-LABEL: define internal void @"\01-[I1 foo2]"( +// CHECK-LP64: [[SELF_ADDR:%.*]] = alloca [[TY:%.*]]*, +// CHECK-LP64: [[BLOCK:%.*]] = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>, +// CHECK-LP64: store [[TY]]* %self, [[TY]]** [[SELF_ADDR]] +// CHECK-LP64: [[T0:%.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>* [[BLOCK]], i32 0, i32 5 +// CHECK-LP64: [[BC:%.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>* [[BLOCK]], i32 0, i32 5 +// CHECK-LP64: [[T1:%.*]] = load [[TY]]*, [[TY]]** [[SELF_ADDR]] +// CHECK-LP64: [[T2:%.*]] = bitcast [[TY]]* [[T1]] to i8* +// CHECK-LP64: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) +// CHECK-LP64: [[T4:%.*]] = bitcast i8* [[T3]] to [[TY]]* +// CHECK-LP64: store [[TY]]* [[T4]], [[TY]]** [[BC]] + +// CHECK-LP64: [[T5:%.*]] = bitcast [[TY]]** [[T0]] to i8** +// CHECK-LP64: call void @objc_storeStrong(i8** [[T5]], i8* null) +// CHECK-LP64: switch i32 {{%.*}}, label %unreachable [ +// CHECK-LP64-NEXT: i32 0, label %[[CLEANUP_CONT:.*]] +// CHECK-LP64-NEXT: i32 2, label %[[FORCOLL_END:.*]] +// CHECK-LP64-NEXT: ] + +// CHECK-LP64: {{^}}[[CLEANUP_CONT]]: +// CHECK-LP64-NEXT: br label %[[FORCOLL_END]] + +// CHECK-LP64: {{^}}[[FORCOLL_END]]: +// CHECK-LP64-NEXT: ret void + + // CHECK-LP64: attributes [[NUW]] = { nounwind } Index: lib/CodeGen/CGObjC.cpp =================================================================== --- lib/CodeGen/CGObjC.cpp +++ lib/CodeGen/CGObjC.cpp @@ -1727,7 +1727,8 @@ // Leave the cleanup we entered in ARC. if (getLangOpts().ObjCAutoRefCount) - PopCleanupBlock(); + while (LoopEnd.getScopeDepth().strictlyEncloses(EHStack.stable_begin())) + PopCleanupBlock(); EmitBlock(LoopEnd.getBlock()); }
Index: test/CodeGenObjC/arc-foreach.m =================================================================== --- test/CodeGenObjC/arc-foreach.m +++ test/CodeGenObjC/arc-foreach.m @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-runtime-has-weak -triple x86_64-apple-darwin -emit-llvm %s -o %t-64.s // RUN: FileCheck -check-prefix CHECK-LP64 --input-file=%t-64.s %s +// REQUIRES: asserts // rdar://9503326 // rdar://9606600 @@ -170,4 +171,53 @@ // CHECK-LP64-NEXT: br label [[L]] } +@interface NSObject @end + +@interface I1 : NSObject +- (NSArray *) foo1:(void (^)(void))block; +- (void) foo2; +@end + +NSArray *array4; + +@implementation I1 : NSObject +- (NSArray *) foo1:(void (^)(void))block { + block(); + return array4; +} + +- (void) foo2 { + for (id x in [self foo1:^{ use(self); }]) { + use(x); + break; + } +} +@end + +// CHECK-LP64-LABEL: define internal void @"\01-[I1 foo2]"( +// CHECK-LP64: [[SELF_ADDR:%.*]] = alloca [[TY:%.*]]*, +// CHECK-LP64: [[BLOCK:%.*]] = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>, +// CHECK-LP64: store [[TY]]* %self, [[TY]]** [[SELF_ADDR]] +// CHECK-LP64: [[T0:%.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>* [[BLOCK]], i32 0, i32 5 +// CHECK-LP64: [[BC:%.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>* [[BLOCK]], i32 0, i32 5 +// CHECK-LP64: [[T1:%.*]] = load [[TY]]*, [[TY]]** [[SELF_ADDR]] +// CHECK-LP64: [[T2:%.*]] = bitcast [[TY]]* [[T1]] to i8* +// CHECK-LP64: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) +// CHECK-LP64: [[T4:%.*]] = bitcast i8* [[T3]] to [[TY]]* +// CHECK-LP64: store [[TY]]* [[T4]], [[TY]]** [[BC]] + +// CHECK-LP64: [[T5:%.*]] = bitcast [[TY]]** [[T0]] to i8** +// CHECK-LP64: call void @objc_storeStrong(i8** [[T5]], i8* null) +// CHECK-LP64: switch i32 {{%.*}}, label %unreachable [ +// CHECK-LP64-NEXT: i32 0, label %[[CLEANUP_CONT:.*]] +// CHECK-LP64-NEXT: i32 2, label %[[FORCOLL_END:.*]] +// CHECK-LP64-NEXT: ] + +// CHECK-LP64: {{^}}[[CLEANUP_CONT]]: +// CHECK-LP64-NEXT: br label %[[FORCOLL_END]] + +// CHECK-LP64: {{^}}[[FORCOLL_END]]: +// CHECK-LP64-NEXT: ret void + + // CHECK-LP64: attributes [[NUW]] = { nounwind } Index: lib/CodeGen/CGObjC.cpp =================================================================== --- lib/CodeGen/CGObjC.cpp +++ lib/CodeGen/CGObjC.cpp @@ -1727,7 +1727,8 @@ // Leave the cleanup we entered in ARC. if (getLangOpts().ObjCAutoRefCount) - PopCleanupBlock(); + while (LoopEnd.getScopeDepth().strictlyEncloses(EHStack.stable_begin())) + PopCleanupBlock(); EmitBlock(LoopEnd.getBlock()); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits