diff options
Diffstat (limited to 'test/Transforms/ObjCARC')
-rw-r--r-- | test/Transforms/ObjCARC/basic.ll | 82 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/contract.ll | 18 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/escape.ll | 131 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/invoke.ll | 109 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/pr12270.ll | 6 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/retain-not-declared.ll | 2 |
6 files changed, 343 insertions, 5 deletions
diff --git a/test/Transforms/ObjCARC/basic.ll b/test/Transforms/ObjCARC/basic.ll index 08bd8c0..ba2f778 100644 --- a/test/Transforms/ObjCARC/basic.ll +++ b/test/Transforms/ObjCARC/basic.ll @@ -3,10 +3,12 @@ target datalayout = "e-p:64:64:64" declare i8* @objc_retain(i8*) +declare i8* @objc_retainAutoreleasedReturnValue(i8*) declare void @objc_release(i8*) declare i8* @objc_autorelease(i8*) +declare i8* @objc_autoreleaseReturnValue(i8*) declare void @objc_autoreleasePoolPop(i8*) -declare void @objc_autoreleasePoolPush() +declare i8* @objc_autoreleasePoolPush() declare i8* @objc_retainBlock(i8*) declare i8* @objc_retainedObject(i8*) @@ -526,7 +528,7 @@ entry: define void @test13d(i8* %x, i64 %n) { entry: call i8* @objc_retain(i8* %x) nounwind - call void @objc_autoreleasePoolPush() + call i8* @objc_autoreleasePoolPush() call i8* @objc_retain(i8* %x) nounwind call void @use_pointer(i8* %x) call void @use_pointer(i8* %x) @@ -1400,7 +1402,7 @@ entry: ; CHECK-NEXT: call i8* @objc_autorelease(i8* %p) ; CHECK-NEXT: call void @use_pointer(i8* %p) ; CHECK-NEXT: call void @use_pointer(i8* %p) -; CHECK-NEXT: call void @objc_autoreleasePoolPush() +; CHECK-NEXT: call i8* @objc_autoreleasePoolPush() ; CHECK-NEXT: ret void ; CHECK-NEXT: } define void @test43b(i8* %p) { @@ -1410,7 +1412,7 @@ entry: call i8* @objc_retain(i8* %p) call void @use_pointer(i8* %p) call void @use_pointer(i8* %p) - call void @objc_autoreleasePoolPush() + call i8* @objc_autoreleasePoolPush() call void @objc_release(i8* %p) ret void } @@ -1797,6 +1799,78 @@ exit: ret void } +; Move an autorelease past a phi with a null. + +; CHECK: define i8* @test65( +; CHECK: if.then: +; CHECK: call i8* @objc_autorelease( +; CHECK: return: +; CHECK-NOT: @objc_autorelease +; CHECK: } +define i8* @test65(i1 %x) { +entry: + br i1 %x, label %return, label %if.then + +if.then: ; preds = %entry + %c = call i8* @returner() + %s = call i8* @objc_retainAutoreleasedReturnValue(i8* %c) nounwind + br label %return + +return: ; preds = %if.then, %entry + %retval = phi i8* [ %s, %if.then ], [ null, %entry ] + %q = call i8* @objc_autorelease(i8* %retval) nounwind + ret i8* %retval +} + +; Don't move an autorelease past an autorelease pool boundary. + +; CHECK: define i8* @test65b( +; CHECK: if.then: +; CHECK-NOT: @objc_autorelease +; CHECK: return: +; CHECK: call i8* @objc_autorelease( +; CHECK: } +define i8* @test65b(i1 %x) { +entry: + %t = call i8* @objc_autoreleasePoolPush() + br i1 %x, label %return, label %if.then + +if.then: ; preds = %entry + %c = call i8* @returner() + %s = call i8* @objc_retainAutoreleasedReturnValue(i8* %c) nounwind + br label %return + +return: ; preds = %if.then, %entry + %retval = phi i8* [ %s, %if.then ], [ null, %entry ] + call void @objc_autoreleasePoolPop(i8* %t) + %q = call i8* @objc_autorelease(i8* %retval) nounwind + ret i8* %retval +} + +; Don't move an autoreleaseReuturnValue, which would break +; the RV optimization. + +; CHECK: define i8* @test65c( +; CHECK: if.then: +; CHECK-NOT: @objc_autorelease +; CHECK: return: +; CHECK: call i8* @objc_autoreleaseReturnValue( +; CHECK: } +define i8* @test65c(i1 %x) { +entry: + br i1 %x, label %return, label %if.then + +if.then: ; preds = %entry + %c = call i8* @returner() + %s = call i8* @objc_retainAutoreleasedReturnValue(i8* %c) nounwind + br label %return + +return: ; preds = %if.then, %entry + %retval = phi i8* [ %s, %if.then ], [ null, %entry ] + %q = call i8* @objc_autoreleaseReturnValue(i8* %retval) nounwind + ret i8* %retval +} + declare void @bar(i32 ()*) ; A few real-world testcases. diff --git a/test/Transforms/ObjCARC/contract.ll b/test/Transforms/ObjCARC/contract.ll index 04ae3ca..c48f8a5 100644 --- a/test/Transforms/ObjCARC/contract.ll +++ b/test/Transforms/ObjCARC/contract.ll @@ -143,3 +143,21 @@ define i8* @test7(i8* %p) { %2 = tail call i8* @objc_autoreleaseReturnValue(i8* %p) ret i8* %p } + +; Do the return value substitution for PHI nodes too. + +; CHECK: define i8* @test8( +; CHECK: %retval = phi i8* [ %p, %if.then ], [ null, %entry ] +; CHECK: } +define i8* @test8(i1 %x, i8* %c) { +entry: + br i1 %x, label %return, label %if.then + +if.then: ; preds = %entry + %p = call i8* @objc_retain(i8* %c) nounwind + br label %return + +return: ; preds = %if.then, %entry + %retval = phi i8* [ %c, %if.then ], [ null, %entry ] + ret i8* %retval +} diff --git a/test/Transforms/ObjCARC/escape.ll b/test/Transforms/ObjCARC/escape.ll new file mode 100644 index 0000000..3f694cf --- /dev/null +++ b/test/Transforms/ObjCARC/escape.ll @@ -0,0 +1,131 @@ +; RUN: opt -objc-arc -S < %s | FileCheck %s +; rdar://11229925 + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" + +%struct.__block_byref_weakLogNTimes = type { i8*, %struct.__block_byref_weakLogNTimes*, i32, i32, i8*, i8*, void (...)* } +%struct.__block_descriptor = type { i64, i64 } + +; Don't optimize away the retainBlock, because the object's address "escapes" +; with the objc_storeWeak call. + +; CHECK: define void @test0( +; CHECK: %tmp7 = call i8* @objc_retainBlock(i8* %tmp6) nounwind, !clang.arc.copy_on_escape !0 +; CHECK: call void @objc_release(i8* %tmp7) nounwind, !clang.imprecise_release !0 +; CHECK: } +define void @test0() nounwind { +entry: + %weakLogNTimes = alloca %struct.__block_byref_weakLogNTimes, align 8 + %block = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>, align 8 + %byref.isa = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 0 + store i8* null, i8** %byref.isa, align 8 + %byref.forwarding = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 1 + store %struct.__block_byref_weakLogNTimes* %weakLogNTimes, %struct.__block_byref_weakLogNTimes** %byref.forwarding, align 8 + %byref.flags = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 2 + store i32 33554432, i32* %byref.flags, align 8 + %byref.size = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 3 + store i32 48, i32* %byref.size, align 4 + %tmp1 = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 4 + store i8* bitcast (void (i8*, i8*)* @__Block_byref_object_copy_ to i8*), i8** %tmp1, align 8 + %tmp2 = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 5 + store i8* bitcast (void (i8*)* @__Block_byref_object_dispose_ to i8*), i8** %tmp2, align 8 + %weakLogNTimes1 = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 6 + %tmp3 = bitcast void (...)** %weakLogNTimes1 to i8** + %tmp4 = call i8* @objc_initWeak(i8** %tmp3, i8* null) nounwind + %block.isa = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 0 + store i8* null, i8** %block.isa, align 8 + %block.flags = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 1 + store i32 1107296256, i32* %block.flags, align 8 + %block.reserved = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 2 + store i32 0, i32* %block.reserved, align 4 + %block.invoke = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 3 + store i8* bitcast (void (i8*, i32)* @__main_block_invoke_0 to i8*), i8** %block.invoke, align 8 + %block.descriptor = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 4 + store %struct.__block_descriptor* null, %struct.__block_descriptor** %block.descriptor, align 8 + %block.captured = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 5 + %tmp5 = bitcast %struct.__block_byref_weakLogNTimes* %weakLogNTimes to i8* + store i8* %tmp5, i8** %block.captured, align 8 + %tmp6 = bitcast <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block to i8* + %tmp7 = call i8* @objc_retainBlock(i8* %tmp6) nounwind, !clang.arc.copy_on_escape !0 + %tmp8 = load %struct.__block_byref_weakLogNTimes** %byref.forwarding, align 8 + %weakLogNTimes3 = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %tmp8, i64 0, i32 6 + %tmp9 = bitcast void (...)** %weakLogNTimes3 to i8** + %tmp10 = call i8* @objc_storeWeak(i8** %tmp9, i8* %tmp7) nounwind + %tmp11 = getelementptr inbounds i8* %tmp7, i64 16 + %tmp12 = bitcast i8* %tmp11 to i8** + %tmp13 = load i8** %tmp12, align 8 + %tmp14 = bitcast i8* %tmp13 to void (i8*, i32)* + call void %tmp14(i8* %tmp7, i32 10) nounwind, !clang.arc.no_objc_arc_exceptions !0 + call void @objc_release(i8* %tmp7) nounwind, !clang.imprecise_release !0 + call void @_Block_object_dispose(i8* %tmp5, i32 8) nounwind + call void @objc_destroyWeak(i8** %tmp3) nounwind + ret void +} + +; Like test0, but it makes a regular call instead of a storeWeak call, +; so the optimization is valid. + +; CHECK: define void @test1( +; CHECK-NOT: @objc_retainBlock +; CHECK: } +define void @test1() nounwind { +entry: + %weakLogNTimes = alloca %struct.__block_byref_weakLogNTimes, align 8 + %block = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>, align 8 + %byref.isa = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 0 + store i8* null, i8** %byref.isa, align 8 + %byref.forwarding = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 1 + store %struct.__block_byref_weakLogNTimes* %weakLogNTimes, %struct.__block_byref_weakLogNTimes** %byref.forwarding, align 8 + %byref.flags = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 2 + store i32 33554432, i32* %byref.flags, align 8 + %byref.size = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 3 + store i32 48, i32* %byref.size, align 4 + %tmp1 = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 4 + store i8* bitcast (void (i8*, i8*)* @__Block_byref_object_copy_ to i8*), i8** %tmp1, align 8 + %tmp2 = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 5 + store i8* bitcast (void (i8*)* @__Block_byref_object_dispose_ to i8*), i8** %tmp2, align 8 + %weakLogNTimes1 = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %weakLogNTimes, i64 0, i32 6 + %tmp3 = bitcast void (...)** %weakLogNTimes1 to i8** + %tmp4 = call i8* @objc_initWeak(i8** %tmp3, i8* null) nounwind + %block.isa = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 0 + store i8* null, i8** %block.isa, align 8 + %block.flags = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 1 + store i32 1107296256, i32* %block.flags, align 8 + %block.reserved = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 2 + store i32 0, i32* %block.reserved, align 4 + %block.invoke = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 3 + store i8* bitcast (void (i8*, i32)* @__main_block_invoke_0 to i8*), i8** %block.invoke, align 8 + %block.descriptor = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 4 + store %struct.__block_descriptor* null, %struct.__block_descriptor** %block.descriptor, align 8 + %block.captured = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 5 + %tmp5 = bitcast %struct.__block_byref_weakLogNTimes* %weakLogNTimes to i8* + store i8* %tmp5, i8** %block.captured, align 8 + %tmp6 = bitcast <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block to i8* + %tmp7 = call i8* @objc_retainBlock(i8* %tmp6) nounwind, !clang.arc.copy_on_escape !0 + %tmp8 = load %struct.__block_byref_weakLogNTimes** %byref.forwarding, align 8 + %weakLogNTimes3 = getelementptr inbounds %struct.__block_byref_weakLogNTimes* %tmp8, i64 0, i32 6 + %tmp9 = bitcast void (...)** %weakLogNTimes3 to i8** + %tmp10 = call i8* @not_really_objc_storeWeak(i8** %tmp9, i8* %tmp7) nounwind + %tmp11 = getelementptr inbounds i8* %tmp7, i64 16 + %tmp12 = bitcast i8* %tmp11 to i8** + %tmp13 = load i8** %tmp12, align 8 + %tmp14 = bitcast i8* %tmp13 to void (i8*, i32)* + call void %tmp14(i8* %tmp7, i32 10) nounwind, !clang.arc.no_objc_arc_exceptions !0 + call void @objc_release(i8* %tmp7) nounwind, !clang.imprecise_release !0 + call void @_Block_object_dispose(i8* %tmp5, i32 8) nounwind + call void @objc_destroyWeak(i8** %tmp3) nounwind + ret void +} + +declare void @__Block_byref_object_copy_(i8*, i8*) nounwind +declare void @__Block_byref_object_dispose_(i8*) nounwind +declare void @objc_destroyWeak(i8**) +declare i8* @objc_initWeak(i8**, i8*) +declare void @__main_block_invoke_0(i8* nocapture, i32) nounwind ssp +declare void @_Block_object_dispose(i8*, i32) +declare i8* @objc_retainBlock(i8*) +declare i8* @objc_storeWeak(i8**, i8*) +declare i8* @not_really_objc_storeWeak(i8**, i8*) +declare void @objc_release(i8*) + +!0 = metadata !{} diff --git a/test/Transforms/ObjCARC/invoke.ll b/test/Transforms/ObjCARC/invoke.ll index 9e26209..76e82a5 100644 --- a/test/Transforms/ObjCARC/invoke.ll +++ b/test/Transforms/ObjCARC/invoke.ll @@ -6,6 +6,7 @@ declare i8* @objc_retainAutoreleasedReturnValue(i8*) declare i8* @objc_msgSend(i8*, i8*, ...) declare void @use_pointer(i8*) declare void @callee() +declare i8* @returner() ; ARCOpt shouldn't try to move the releases to the block containing the invoke. @@ -103,6 +104,114 @@ finally.rethrow: ; preds = %invoke.cont, %entry unreachable } +; Don't try to place code on invoke critical edges. + +; CHECK: define void @test3( +; CHECK: if.end: +; CHECK-NEXT: call void @objc_release(i8* %p) nounwind +; CHECK-NEXT: ret void +define void @test3(i8* %p, i1 %b) { +entry: + %0 = call i8* @objc_retain(i8* %p) + call void @callee() + br i1 %b, label %if.else, label %if.then + +if.then: + invoke void @use_pointer(i8* %p) + to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 + +if.else: + invoke void @use_pointer(i8* %p) + to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 + +lpad: + %r = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__objc_personality_v0 to i8*) + cleanup + ret void + +if.end: + call void @objc_release(i8* %p) + ret void +} + +; Like test3, but with ARC-relevant exception handling. + +; CHECK: define void @test4( +; CHECK: lpad: +; CHECK-NEXT: %r = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__objc_personality_v0 to i8*) +; CHECK-NEXT: cleanup +; CHECK-NEXT: call void @objc_release(i8* %p) nounwind +; CHECK-NEXT: ret void +; CHECK: if.end: +; CHECK-NEXT: call void @objc_release(i8* %p) nounwind +; CHECK-NEXT: ret void +define void @test4(i8* %p, i1 %b) { +entry: + %0 = call i8* @objc_retain(i8* %p) + call void @callee() + br i1 %b, label %if.else, label %if.then + +if.then: + invoke void @use_pointer(i8* %p) + to label %if.end unwind label %lpad + +if.else: + invoke void @use_pointer(i8* %p) + to label %if.end unwind label %lpad + +lpad: + %r = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__objc_personality_v0 to i8*) + cleanup + call void @objc_release(i8* %p) + ret void + +if.end: + call void @objc_release(i8* %p) + ret void +} + +; Don't turn the retainAutoreleaseReturnValue into retain, because it's +; for an invoke which we can assume codegen will put immediately prior. + +; CHECK: define void @test5( +; CHECK: call i8* @objc_retainAutoreleasedReturnValue(i8* %z) +; CHECK: } +define void @test5() { +entry: + %z = invoke i8* @returner() + to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 + +lpad: + %r13 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__objc_personality_v0 to i8*) + cleanup + ret void + +if.end: + call i8* @objc_retainAutoreleasedReturnValue(i8* %z) + ret void +} + +; Like test5, but there's intervening code. + +; CHECK: define void @test6( +; CHECK: call i8* @objc_retain(i8* %z) +; CHECK: } +define void @test6() { +entry: + %z = invoke i8* @returner() + to label %if.end unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 + +lpad: + %r13 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__objc_personality_v0 to i8*) + cleanup + ret void + +if.end: + call void @callee() + call i8* @objc_retainAutoreleasedReturnValue(i8* %z) + ret void +} + declare i32 @__gxx_personality_v0(...) declare i32 @__objc_personality_v0(...) diff --git a/test/Transforms/ObjCARC/pr12270.ll b/test/Transforms/ObjCARC/pr12270.ll index 30610f8..1faae5f 100644 --- a/test/Transforms/ObjCARC/pr12270.ll +++ b/test/Transforms/ObjCARC/pr12270.ll @@ -9,7 +9,13 @@ entry: return: ; No predecessors! %bar = bitcast %2* %x to i8* %foo = call i8* @objc_autoreleaseReturnValue(i8* %bar) nounwind + call void @callee() + call void @use_pointer(i8* %foo) + call void @objc_release(i8* %foo) nounwind ret void } declare i8* @objc_autoreleaseReturnValue(i8*) +declare void @objc_release(i8*) +declare void @callee() +declare void @use_pointer(i8*) diff --git a/test/Transforms/ObjCARC/retain-not-declared.ll b/test/Transforms/ObjCARC/retain-not-declared.ll index 41bde01..f876e51 100644 --- a/test/Transforms/ObjCARC/retain-not-declared.ll +++ b/test/Transforms/ObjCARC/retain-not-declared.ll @@ -30,7 +30,7 @@ entry: ; CHECK: @test1( ; CHECK: @objc_retain( -; CHECK: @objc_retain( +; CHECK: @objc_retainAutoreleasedReturnValue( ; CHECK: @objc_release( ; CHECK: @objc_release( ; CHECK: } |