From e9341ab11eeb35845181f2834e989bac39ec5d96 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 9 Sep 2015 23:37:17 +0000 Subject: [PATCH] ARC: Fix the precise-lifetime suppression of returns_inner_pointer receiver extension for message sends via property syntax. rdar://22172983 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@247209 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGObjC.cpp | 13 ++ test/CodeGenObjC/arc-precise-lifetime.m | 155 ++++++++++++++++++++++-- 2 files changed, 155 insertions(+), 13 deletions(-) diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index 242b3d5a73..a0b92c9938 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -273,10 +273,23 @@ shouldExtendReceiverForInnerPointerMessage(const ObjCMessageExpr *message) { // receiver is loaded from a variable with precise lifetime. case ObjCMessageExpr::Instance: { const Expr *receiver = message->getInstanceReceiver(); + + // Look through OVEs. + if (auto opaque = dyn_cast(receiver)) { + if (opaque->getSourceExpr()) + receiver = opaque->getSourceExpr()->IgnoreParens(); + } + const ImplicitCastExpr *ice = dyn_cast(receiver); if (!ice || ice->getCastKind() != CK_LValueToRValue) return true; receiver = ice->getSubExpr()->IgnoreParens(); + // Look through OVEs. + if (auto opaque = dyn_cast(receiver)) { + if (opaque->getSourceExpr()) + receiver = opaque->getSourceExpr()->IgnoreParens(); + } + // Only __strong variables. if (receiver->getType().getObjCLifetime() != Qualifiers::OCL_Strong) return true; diff --git a/test/CodeGenObjC/arc-precise-lifetime.m b/test/CodeGenObjC/arc-precise-lifetime.m index 6dc3ebf544..42a0483079 100644 --- a/test/CodeGenObjC/arc-precise-lifetime.m +++ b/test/CodeGenObjC/arc-precise-lifetime.m @@ -26,7 +26,8 @@ void test0() { // CHECK-NEXT: ret void } -// rdar://problem/9821110 +// rdar://problem/9821110 - precise lifetime should suppress extension +// rdar://problem/22172983 - should work for calls via property syntax, too @interface Test1 - (char*) interior __attribute__((objc_returns_inner_pointer)); // Should we allow this on properties? Yes! see // rdar://14990439 @@ -34,8 +35,8 @@ void test0() { @end extern Test1 *test1_helper(void); -// CHECK-LABEL: define void @test1a() -void test1a(void) { +// CHECK-LABEL: define void @test1a_message() +void test1a_message(void) { // CHECK: [[PTR:%.*]] = alloca [[PTR_T:%.*]]*, align 8 // CHECK: [[C:%.*]] = alloca i8*, align 8 // CHECK: [[PTRPTR1:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* @@ -67,8 +68,43 @@ void test1a(void) { char *c = [(ptr) interior]; } -// CHECK-LABEL: define void @test1b() -void test1b(void) { + +// CHECK-LABEL: define void @test1a_property() +void test1a_property(void) { + // CHECK: [[PTR:%.*]] = alloca [[PTR_T:%.*]]*, align 8 + // CHECK: [[C:%.*]] = alloca i8*, align 8 + // CHECK: [[PTRPTR1:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* + // CHECK: call void @llvm.lifetime.start(i64 8, i8* [[PTRPTR1]]) + // CHECK: [[T0:%.*]] = call [[TEST1:%.*]]* @test1_helper() + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST1]]* + // CHECK-NEXT: store [[TEST1]]* [[T3]] + // CHECK-NEXT: [[CPTR1:%.*]] = bitcast i8** [[C]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.start(i64 8, i8* [[CPTR1]]) + // CHECK-NEXT: [[T0:%.*]] = load [[TEST1]]*, [[TEST1]]** + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutorelease(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST1]]* + // CHECK-NEXT: [[T4:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[T5:%.*]] = bitcast [[TEST1]]* [[T3]] to i8* + // CHECK-NEXT: [[T6:%.*]] = call i8* bitcast + // CHECK-NEXT: store i8* [[T6]], i8** + // CHECK-NEXT: [[CPTR2:%.*]] = bitcast i8** [[C]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.end(i64 8, i8* [[CPTR2]]) + // CHECK-NEXT: [[T0:%.*]] = load [[TEST1]]*, [[TEST1]]** + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) [[NUW]], !clang.imprecise_release + // CHECK-NEXT: [[PTRPTR2:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.end(i64 8, i8* [[PTRPTR2]]) + // CHECK-NEXT: ret void + Test1 *ptr = test1_helper(); + char *c = ptr.interior; +} + + +// CHECK-LABEL: define void @test1b_message() +void test1b_message(void) { // CHECK: [[PTR:%.*]] = alloca [[PTR_T:%.*]]*, align 8 // CHECK: [[C:%.*]] = alloca i8*, align 8 // CHECK: [[PTRPTR1:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* @@ -94,11 +130,76 @@ void test1b(void) { // CHECK-NEXT: [[PTRPTR2:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* // CHECK-NEXT: call void @llvm.lifetime.end(i64 8, i8* [[PTRPTR2]]) // CHECK-NEXT: ret void - __attribute__((objc_precise_lifetime)) Test1 *ptr = test1_helper(); + PRECISE_LIFETIME Test1 *ptr = test1_helper(); char *c = [ptr interior]; } -void test1c(void) { +// CHECK-LABEL: define void @test1b_property() +void test1b_property(void) { + // CHECK: [[PTR:%.*]] = alloca [[PTR_T:%.*]]*, align 8 + // CHECK: [[C:%.*]] = alloca i8*, align 8 + // CHECK: [[PTRPTR1:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* + // CHECK: call void @llvm.lifetime.start(i64 8, i8* [[PTRPTR1]]) + // CHECK: [[T0:%.*]] = call [[TEST1:%.*]]* @test1_helper() + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST1]]* + // CHECK-NEXT: store [[TEST1]]* [[T3]] + // CHECK-NEXT: [[CPTR1:%.*]] = bitcast i8** [[C]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.start(i64 8, i8* [[CPTR1]]) + // CHECK-NEXT: [[T0:%.*]] = load [[TEST1]]*, [[TEST1]]** + // CHECK-NEXT: [[T1:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* + // CHECK-NEXT: [[T3:%.*]] = call i8* bitcast + // CHECK-NEXT: store i8* [[T3]], i8** + // CHECK-NEXT: [[CPTR2:%.*]] = bitcast i8** [[C]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.end(i64 8, i8* [[CPTR2]]) + // CHECK-NEXT: [[T0:%.*]] = load [[TEST1]]*, [[TEST1]]** + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) [[NUW]] + // CHECK-NOT: clang.imprecise_release + // CHECK-NEXT: [[PTRPTR2:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.end(i64 8, i8* [[PTRPTR2]]) + // CHECK-NEXT: ret void + PRECISE_LIFETIME Test1 *ptr = test1_helper(); + char *c = ptr.interior; +} + +// CHECK-LABEL: define void @test1c_message() +void test1c_message(void) { + // CHECK: [[PTR:%.*]] = alloca [[PTR_T:%.*]]*, align 8 + // CHECK: [[PC:%.*]] = alloca i8*, align 8 + // CHECK: [[PTRPTR1:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* + // CHECK: call void @llvm.lifetime.start(i64 8, i8* [[PTRPTR1]]) + // CHECK: [[T0:%.*]] = call [[TEST1:%.*]]* @test1_helper() + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST1]]* + // CHECK-NEXT: store [[TEST1]]* [[T3]] + // CHECK-NEXT: [[PCPTR1:%.*]] = bitcast i8** [[PC]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.start(i64 8, i8* [[PCPTR1]]) + // CHECK-NEXT: [[T0:%.*]] = load [[TEST1]]*, [[TEST1]]** + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutorelease(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST1]]* + // CHECK-NEXT: [[T4:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[T5:%.*]] = bitcast [[TEST1]]* [[T3]] to i8* + // CHECK-NEXT: [[T6:%.*]] = call i8* bitcast + // CHECK-NEXT: store i8* [[T6]], i8** + // CHECK-NEXT: [[PCPTR2:%.*]] = bitcast i8** [[PC]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.end(i64 8, i8* [[PCPTR2]]) + // CHECK-NEXT: [[T0:%.*]] = load [[TEST1]]*, [[TEST1]]** + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) [[NUW]], !clang.imprecise_release + // CHECK-NEXT: [[PTRPTR2:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.end(i64 8, i8* [[PTRPTR2]]) + // CHECK-NEXT: ret void + Test1 *ptr = test1_helper(); + char *pc = [ptr PropertyReturnsInnerPointer]; +} + +// CHECK-LABEL: define void @test1c_property() +void test1c_property(void) { // CHECK: [[PTR:%.*]] = alloca [[PTR_T:%.*]]*, align 8 // CHECK: [[PC:%.*]] = alloca i8*, align 8 // CHECK: [[PTRPTR1:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* @@ -130,7 +231,38 @@ void test1c(void) { char *pc = ptr.PropertyReturnsInnerPointer; } -void test1d(void) { +// CHECK-LABEL: define void @test1d_message() +void test1d_message(void) { + // CHECK: [[PTR:%.*]] = alloca [[PTR_T:%.*]]*, align 8 + // CHECK: [[PC:%.*]] = alloca i8*, align 8 + // CHECK: [[PTRPTR1:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* + // CHECK: call void @llvm.lifetime.start(i64 8, i8* [[PTRPTR1]]) + // CHECK: [[T0:%.*]] = call [[TEST1:%.*]]* @test1_helper() + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST1]]* + // CHECK-NEXT: store [[TEST1]]* [[T3]] + // CHECK-NEXT: [[PCPTR1:%.*]] = bitcast i8** [[PC]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.start(i64 8, i8* [[PCPTR1]]) + // CHECK-NEXT: [[T0:%.*]] = load [[TEST1]]*, [[TEST1]]** + // CHECK-NEXT: [[SEVEN:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[EIGHT:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* + // CHECK-NEXT: [[CALL1:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(i8* [[EIGHT]], i8* [[SEVEN]]) + // CHECK-NEXT: store i8* [[CALL1]], i8** + // CHECK-NEXT: [[PCPTR2:%.*]] = bitcast i8** [[PC]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.end(i64 8, i8* [[PCPTR2]]) + // CHECK-NEXT: [[NINE:%.*]] = load [[TEST1]]*, [[TEST1]]** + // CHECK-NEXT: [[TEN:%.*]] = bitcast [[TEST1]]* [[NINE]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[TEN]]) + // CHECK-NEXT: [[PTRPTR2:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* + // CHECK-NEXT: call void @llvm.lifetime.end(i64 8, i8* [[PTRPTR2]]) + // CHECK-NEXT: ret void + PRECISE_LIFETIME Test1 *ptr = test1_helper(); + char *pc = [ptr PropertyReturnsInnerPointer]; +} + +// CHECK-LABEL: define void @test1d_property() +void test1d_property(void) { // CHECK: [[PTR:%.*]] = alloca [[PTR_T:%.*]]*, align 8 // CHECK: [[PC:%.*]] = alloca i8*, align 8 // CHECK: [[PTRPTR1:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* @@ -143,11 +275,8 @@ void test1d(void) { // CHECK-NEXT: [[PCPTR1:%.*]] = bitcast i8** [[PC]] to i8* // CHECK-NEXT: call void @llvm.lifetime.start(i64 8, i8* [[PCPTR1]]) // CHECK-NEXT: [[T0:%.*]] = load [[TEST1]]*, [[TEST1]]** - // CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* - // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retainAutorelease - // CHECK-NEXT: [[SIX:%.*]] = bitcast i8* [[T3]] to [[TEST1]]* // CHECK-NEXT: [[SEVEN:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ - // CHECK-NEXT: [[EIGHT:%.*]] = bitcast [[TEST1]]* [[SIX]] to i8* + // CHECK-NEXT: [[EIGHT:%.*]] = bitcast [[TEST1]]* [[T0]] to i8* // CHECK-NEXT: [[CALL1:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(i8* [[EIGHT]], i8* [[SEVEN]]) // CHECK-NEXT: store i8* [[CALL1]], i8** // CHECK-NEXT: [[PCPTR2:%.*]] = bitcast i8** [[PC]] to i8* @@ -158,7 +287,7 @@ void test1d(void) { // CHECK-NEXT: [[PTRPTR2:%.*]] = bitcast [[PTR_T]]** [[PTR]] to i8* // CHECK-NEXT: call void @llvm.lifetime.end(i64 8, i8* [[PTRPTR2]]) // CHECK-NEXT: ret void - __attribute__((objc_precise_lifetime)) Test1 *ptr = test1_helper(); + PRECISE_LIFETIME Test1 *ptr = test1_helper(); char *pc = ptr.PropertyReturnsInnerPointer; } -- 2.40.0