
CodeGenFunction::EmitObjCForCollectionStmt currently emits lifetime markers for the loop variable in an inconsistent way: lifetime.start is emitted before the loop is entered, but lifetime.end is emitted inside the loop. AddressSanitizer uses these markers to track out-of-scope accesses to local variables, and we get false positives in Obj-C foreach loops (in the 2nd iteration of the loop). This patch keeps the loop variable alive for the whole loop by extending ForScope and registering the cleanup function inside EmitAutoVarAlloca. Differential Revision: https://reviews.llvm.org/D32029 llvm-svn: 300340
93 lines
3.1 KiB
Plaintext
93 lines
3.1 KiB
Plaintext
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-runtime-has-weak -fblocks -fobjc-arc -O2 -disable-llvm-passes -o - %s | FileCheck %s
|
|
|
|
@interface A
|
|
@end
|
|
|
|
id getObject();
|
|
void callee();
|
|
|
|
// Lifetime extension for binding a reference to an rvalue
|
|
// CHECK-LABEL: define void @_Z5test0v()
|
|
void test0() {
|
|
// CHECK: call i8* @_Z9getObjectv
|
|
// CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue
|
|
const __strong id &ref1 = getObject();
|
|
// CHECK: call void @_Z6calleev
|
|
callee();
|
|
// CHECK: call i8* @_Z9getObjectv
|
|
// CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue
|
|
// CHECK-NEXT: call i8* @objc_autorelease
|
|
const __autoreleasing id &ref2 = getObject();
|
|
// CHECK: call void @_Z6calleev
|
|
callee();
|
|
// CHECK: call void @objc_release
|
|
// CHECK: ret
|
|
}
|
|
|
|
// No lifetime extension when we're binding a reference to an lvalue.
|
|
// CHECK-LABEL: define void @_Z5test1RU8__strongP11objc_objectRU6__weakS0_
|
|
void test1(__strong id &x, __weak id &y) {
|
|
// CHECK-NOT: release
|
|
const __strong id &ref1 = x;
|
|
const __autoreleasing id &ref2 = x;
|
|
const __weak id &ref3 = y;
|
|
// CHECK: ret void
|
|
}
|
|
|
|
typedef __strong id strong_id;
|
|
|
|
//CHECK: define void @_Z5test3v
|
|
void test3() {
|
|
// CHECK: [[REF:%.*]] = alloca i8**, align 8
|
|
// CHECK: call i8* @objc_initWeak
|
|
// CHECK-NEXT: store i8**
|
|
const __weak id &ref = strong_id();
|
|
// CHECK-NEXT: call void @_Z6calleev()
|
|
callee();
|
|
// CHECK-NEXT: call void @objc_destroyWeak
|
|
// CHECK-NEXT: [[PTR:%.*]] = bitcast i8*** [[REF]] to i8*
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 8, i8* [[PTR]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
// CHECK-LABEL: define void @_Z5test4RU8__strongP11objc_object
|
|
void test4(__strong id &x) {
|
|
// CHECK: call i8* @objc_retain
|
|
__strong A* const &ar = x;
|
|
// CHECK: store i32 17, i32*
|
|
int i = 17;
|
|
// CHECK: call void @objc_release(
|
|
// CHECK: ret void
|
|
}
|
|
|
|
void sink(__strong A* &&);
|
|
|
|
// CHECK-LABEL: define void @_Z5test5RU8__strongP11objc_object
|
|
void test5(__strong id &x) {
|
|
// CHECK: [[REFTMP:%.*]] = alloca {{%.*}}*, align 8
|
|
// CHECK: [[I:%.*]] = alloca i32, align 4
|
|
// CHECK: [[OBJ_ID:%.*]] = call i8* @objc_retain(
|
|
// CHECK-NEXT: [[OBJ_A:%.*]] = bitcast i8* [[OBJ_ID]] to [[A:%[a-zA-Z0-9]+]]*
|
|
// CHECK-NEXT: store [[A]]* [[OBJ_A]], [[A]]** [[REFTMP:%[a-zA-Z0-9]+]]
|
|
// CHECK-NEXT: call void @_Z4sinkOU8__strongP1A
|
|
sink(x);
|
|
// CHECK-NEXT: [[OBJ_A:%[a-zA-Z0-9]+]] = load [[A]]*, [[A]]** [[REFTMP]]
|
|
// CHECK-NEXT: [[OBJ_ID:%[a-zA-Z0-9]+]] = bitcast [[A]]* [[OBJ_A]] to i8*
|
|
// CHECK-NEXT: call void @objc_release
|
|
// CHECK-NEXT: [[IPTR1:%.*]] = bitcast i32* [[I]] to i8*
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[IPTR1]])
|
|
// CHECK-NEXT: store i32 17, i32
|
|
int i = 17;
|
|
// CHECK-NEXT: [[IPTR2:%.*]] = bitcast i32* [[I]] to i8*
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* [[IPTR2]])
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
// CHECK-LABEL: define internal void @__cxx_global_var_init(
|
|
// CHECK: call i8* @_Z9getObjectv
|
|
// CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue
|
|
const __strong id &global_ref = getObject();
|
|
|
|
// Note: we intentionally don't release the object.
|
|
|