diff options
Diffstat (limited to 'test/Transforms/ObjCARC')
-rw-r--r-- | test/Transforms/ObjCARC/arc-annotations.ll | 307 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/basic.ll | 4 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/clang-arc-used-intrinsic-removed-if-isolated.ll | 16 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/contract.ll | 11 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/dont-infinite-loop-during-block-escape-analysis.ll | 87 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/intrinsic-use.ll | 63 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/no-objc-arc-exceptions.ll | 7 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/retain-block-escape-analysis.ll | 127 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/retain-block.ll | 4 | ||||
-rw-r--r-- | test/Transforms/ObjCARC/retain-not-declared.ll | 4 |
10 files changed, 534 insertions, 96 deletions
diff --git a/test/Transforms/ObjCARC/arc-annotations.ll b/test/Transforms/ObjCARC/arc-annotations.ll new file mode 100644 index 0000000000..4c56b4a3de --- /dev/null +++ b/test/Transforms/ObjCARC/arc-annotations.ll @@ -0,0 +1,307 @@ +; This file consists of various tests which ensure that the objc-arc-annotations +; are working correctly. In the future, I will use this in other lit tests to +; check the data flow analysis of ARC. + +; REQUIRES: asserts +; RUN: opt -S -objc-arc -enable-objc-arc-annotations < %s | FileCheck %s + +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 i8* @objc_autoreleasePoolPush() +declare i8* @objc_retainBlock(i8*) + +declare i8* @objc_retainedObject(i8*) +declare i8* @objc_unretainedObject(i8*) +declare i8* @objc_unretainedPointer(i8*) + +declare void @use_pointer(i8*) +declare void @callee() +declare void @callee_fnptr(void ()*) +declare void @invokee() +declare i8* @returner() + +; Simple retain+release pair deletion, with some intervening control +; flow and harmless instructions. + +; CHECK: define void @test0( +; CHECK: entry: +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None) +; CHECK: %0 = tail call i8* @objc_retain(i8* %a) #0, !llvm.arc.annotation.bottomup !0, !llvm.arc.annotation.topdown !1 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Use) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain) +; CHECK: t: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use) +; CHECK: store float 2.000000e+00, float* %b, !llvm.arc.annotation.bottomup !2 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain) +; CHECK: f: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use) +; CHECK: store i32 7, i32* %x, !llvm.arc.annotation.bottomup !2 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain) +; CHECK: return: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Release) +; CHECK: call void @objc_release(i8* %c) #0, !llvm.arc.annotation.bottomup !3, !llvm.arc.annotation.topdown !4 +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None) +; CHECK: } +define void @test0(i32* %x, i1 %p) nounwind { +entry: + %a = bitcast i32* %x to i8* + %0 = call i8* @objc_retain(i8* %a) nounwind + br i1 %p, label %t, label %f + +t: + store i8 3, i8* %a + %b = bitcast i32* %x to float* + store float 2.0, float* %b + br label %return + +f: + store i32 7, i32* %x + br label %return + +return: + %c = bitcast i32* %x to i8* + call void @objc_release(i8* %c) nounwind + ret void +} + +; Like test0 but the release isn't always executed when the retain is, +; so the optimization is not safe. + +; TODO: Make the objc_release's argument be %0. + +; CHECK: define void @test1( +; CHECK: entry: +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None) +; CHECK: %0 = tail call i8* @objc_retain(i8* %a) #0, !llvm.arc.annotation.bottomup !5, !llvm.arc.annotation.topdown !6 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_None) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain) +; CHECK: t: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use) +; CHECK: store float 2.000000e+00, float* %b, !llvm.arc.annotation.bottomup !7 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain) +; CHECK: f: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None) +; CHECK: call void @callee(), !llvm.arc.annotation.topdown !8 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_None) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_CanRelease) +; CHECK: return: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_None) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Release) +; CHECK: call void @objc_release(i8* %c) #0, !llvm.arc.annotation.bottomup !9 +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None) +; CHECK: alt_return: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_None) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None) +; CHECK: } +define void @test1(i32* %x, i1 %p, i1 %q) nounwind { +entry: + %a = bitcast i32* %x to i8* + %0 = call i8* @objc_retain(i8* %a) nounwind + br i1 %p, label %t, label %f + +t: + store i8 3, i8* %a + %b = bitcast i32* %x to float* + store float 2.0, float* %b + br label %return + +f: + store i32 7, i32* %x + call void @callee() + br i1 %q, label %return, label %alt_return + +return: + %c = bitcast i32* %x to i8* + call void @objc_release(i8* %c) nounwind + ret void + +alt_return: + ret void +} + +; Don't do partial elimination into two different CFG diamonds. + +; CHECK: define void @test1b( +; CHECK: entry: +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None) +; CHECK: %0 = tail call i8* @objc_retain(i8* %x) #0, !llvm.arc.annotation.bottomup !10, !llvm.arc.annotation.topdown !11 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_None) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain) +; CHECK: if.then: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_CanRelease) +; CHECK: tail call void @callee(), !llvm.arc.annotation.bottomup !12, !llvm.arc.annotation.topdown !13 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Use) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_CanRelease) +; CHECK: if.end: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_CanRelease) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use) +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Use) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_CanRelease) +; CHECK: if.then3: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_CanRelease) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use) +; CHECK: tail call void @use_pointer(i8* %x), !llvm.arc.annotation.bottomup !14, !llvm.arc.annotation.topdown !15 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_MovableRelease) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Use) +; CHECK: if.end5: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_None) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_MovableRelease) +; CHECK: tail call void @objc_release(i8* %x) #0, !clang.imprecise_release !16, !llvm.arc.annotation.bottomup !17 +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None) +; CHECK: } +define void @test1b(i8* %x, i1 %p, i1 %q) { +entry: + tail call i8* @objc_retain(i8* %x) nounwind + br i1 %p, label %if.then, label %if.end + +if.then: ; preds = %entry + tail call void @callee() + br label %if.end + +if.end: ; preds = %if.then, %entry + br i1 %q, label %if.then3, label %if.end5 + +if.then3: ; preds = %if.end + tail call void @use_pointer(i8* %x) + br label %if.end5 + +if.end5: ; preds = %if.then3, %if.end + tail call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0 + ret void +} + +; Like test0 but the pointer is passed to an intervening call, +; so the optimization is not safe. + +; CHECK: define void @test2( +; CHECK: entry: +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None) +; CHECK: %e = tail call i8* @objc_retain(i8* %a) #0, !llvm.arc.annotation.bottomup !18, !llvm.arc.annotation.topdown !19 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_CanRelease) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain) +; CHECK: t: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use) +; CHECK: store float 2.000000e+00, float* %b, !llvm.arc.annotation.bottomup !20 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain) +; CHECK: f: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_CanRelease) +; CHECK: call void @use_pointer(i8* %e), !llvm.arc.annotation.bottomup !21, !llvm.arc.annotation.topdown !22 +; CHECK: store float 3.000000e+00, float* %d, !llvm.arc.annotation.bottomup !20, !llvm.arc.annotation.topdown !23 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Use) +; CHECK: return: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Use) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Release) +; CHECK: call void @objc_release(i8* %c) #0, !llvm.arc.annotation.bottomup !24, !llvm.arc.annotation.topdown !25 +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None) +; CHECK: } +define void @test2(i32* %x, i1 %p) nounwind { +entry: + %a = bitcast i32* %x to i8* + %e = call i8* @objc_retain(i8* %a) nounwind + br i1 %p, label %t, label %f + +t: + store i8 3, i8* %a + %b = bitcast i32* %x to float* + store float 2.0, float* %b + br label %return + +f: + store i32 7, i32* %x + call void @use_pointer(i8* %e) + %d = bitcast i32* %x to float* + store float 3.0, float* %d + br label %return + +return: + %c = bitcast i32* %x to i8* + call void @objc_release(i8* %c) nounwind + ret void +} + +; Like test0 but the release is in a loop, +; so the optimization is not safe. + +; TODO: For now, assume this can't happen. + +; CHECK: define void @test3( +; CHECK: entry: +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None) +; CHECK: tail call i8* @objc_retain(i8* %a) #0, !llvm.arc.annotation.bottomup !26, !llvm.arc.annotation.topdown !27 +; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain) +; CHECK: loop: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain) +; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Release) +; CHECK: call void @objc_release(i8* %c) #0, !llvm.arc.annotation.bottomup !28, !llvm.arc.annotation.topdown !29 +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None) +; CHECK: return: +; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_None) +; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None) +; CHECK: } +define void @test3(i32* %x, i1* %q) nounwind { +entry: + %a = bitcast i32* %x to i8* + %0 = call i8* @objc_retain(i8* %a) nounwind + br label %loop + +loop: + %c = bitcast i32* %x to i8* + call void @objc_release(i8* %c) nounwind + %j = load volatile i1* %q + br i1 %j, label %loop, label %return + +return: + ret void +} + +!0 = metadata !{} + +; CHECK: !0 = metadata !{metadata !"(test0,%x)", metadata !"S_Use", metadata !"S_None"} +; CHECK: !1 = metadata !{metadata !"(test0,%x)", metadata !"S_None", metadata !"S_Retain"} +; CHECK: !2 = metadata !{metadata !"(test0,%x)", metadata !"S_Release", metadata !"S_Use"} +; CHECK: !3 = metadata !{metadata !"(test0,%x)", metadata !"S_None", metadata !"S_Release"} +; CHECK: !4 = metadata !{metadata !"(test0,%x)", metadata !"S_Retain", metadata !"S_None"} +; CHECK: !5 = metadata !{metadata !"(test1,%x)", metadata !"S_None", metadata !"S_None"} +; CHECK: !6 = metadata !{metadata !"(test1,%x)", metadata !"S_None", metadata !"S_Retain"} +; CHECK: !7 = metadata !{metadata !"(test1,%x)", metadata !"S_Release", metadata !"S_Use"} +; CHECK: !8 = metadata !{metadata !"(test1,%x)", metadata !"S_Retain", metadata !"S_CanRelease"} +; CHECK: !9 = metadata !{metadata !"(test1,%x)", metadata !"S_None", metadata !"S_Release"} +; CHECK: !10 = metadata !{metadata !"(test1b,%x)", metadata !"S_None", metadata !"S_None"} +; CHECK: !11 = metadata !{metadata !"(test1b,%x)", metadata !"S_None", metadata !"S_Retain"} +; CHECK: !12 = metadata !{metadata !"(test1b,%x)", metadata !"S_Use", metadata !"S_CanRelease"} +; CHECK: !13 = metadata !{metadata !"(test1b,%x)", metadata !"S_Retain", metadata !"S_CanRelease"} +; CHECK: !14 = metadata !{metadata !"(test1b,%x)", metadata !"S_MovableRelease", metadata !"S_Use"} +; CHECK: !15 = metadata !{metadata !"(test1b,%x)", metadata !"S_CanRelease", metadata !"S_Use"} +; CHECK: !16 = metadata !{} +; CHECK: !17 = metadata !{metadata !"(test1b,%x)", metadata !"S_None", metadata !"S_MovableRelease"} +; CHECK: !18 = metadata !{metadata !"(test2,%x)", metadata !"S_CanRelease", metadata !"S_None"} +; CHECK: !19 = metadata !{metadata !"(test2,%x)", metadata !"S_None", metadata !"S_Retain"} +; CHECK: !20 = metadata !{metadata !"(test2,%x)", metadata !"S_Release", metadata !"S_Use"} +; CHECK: !21 = metadata !{metadata !"(test2,%x)", metadata !"S_Use", metadata !"S_CanRelease"} +; CHECK: !22 = metadata !{metadata !"(test2,%x)", metadata !"S_Retain", metadata !"S_CanRelease"} +; CHECK: !23 = metadata !{metadata !"(test2,%x)", metadata !"S_CanRelease", metadata !"S_Use"} +; CHECK: !24 = metadata !{metadata !"(test2,%x)", metadata !"S_None", metadata !"S_Release"} +; CHECK: !25 = metadata !{metadata !"(test2,%x)", metadata !"S_Use", metadata !"S_None"} +; CHECK: !26 = metadata !{metadata !"(test3,%x)", metadata !"S_Release", metadata !"S_None"} +; CHECK: !27 = metadata !{metadata !"(test3,%x)", metadata !"S_None", metadata !"S_Retain"} +; CHECK: !28 = metadata !{metadata !"(test3,%x)", metadata !"S_None", metadata !"S_Release"} +; CHECK: !29 = metadata !{metadata !"(test3,%x)", metadata !"S_Retain", metadata !"S_None"} + diff --git a/test/Transforms/ObjCARC/basic.ll b/test/Transforms/ObjCARC/basic.ll index 4c24ebf865..bf6ccc8a7f 100644 --- a/test/Transforms/ObjCARC/basic.ll +++ b/test/Transforms/ObjCARC/basic.ll @@ -795,10 +795,10 @@ entry: ret void } -; Don't optimize objc_retainBlock. +; Don't optimize objc_retainBlock, but do strength reduce it. ; CHECK: define void @test23b -; CHECK: @objc_retainBlock +; CHECK: @objc_retain ; CHECK: @objc_release ; CHECK: } define void @test23b(i8* %p) { diff --git a/test/Transforms/ObjCARC/clang-arc-used-intrinsic-removed-if-isolated.ll b/test/Transforms/ObjCARC/clang-arc-used-intrinsic-removed-if-isolated.ll new file mode 100644 index 0000000000..4215b5c364 --- /dev/null +++ b/test/Transforms/ObjCARC/clang-arc-used-intrinsic-removed-if-isolated.ll @@ -0,0 +1,16 @@ +; RUN: opt -objc-arc-contract -S < %s | FileCheck %s + +; This file makes sure that clang.arc.used is removed even if no other ARC +; interesting calls are in the module. + +declare void @clang.arc.use(...) nounwind + +; Kill calls to @clang.arc.use(...) +; CHECK: define void @test0( +; CHECK-NOT: clang.arc.use +; CHECK: } +define void @test0(i8* %a, i8* %b) { + call void (...)* @clang.arc.use(i8* %a, i8* %b) nounwind + ret void +} + diff --git a/test/Transforms/ObjCARC/contract.ll b/test/Transforms/ObjCARC/contract.ll index b6fba59603..0b60683d99 100644 --- a/test/Transforms/ObjCARC/contract.ll +++ b/test/Transforms/ObjCARC/contract.ll @@ -162,4 +162,15 @@ return: ; preds = %if.then, %entry ret i8* %retval } +; Kill calls to @clang.arc.use(...) +; CHECK: define void @test9( +; CHECK-NOT: clang.arc.use +; CHECK: } +define void @test9(i8* %a, i8* %b) { + call void (...)* @clang.arc.use(i8* %a, i8* %b) nounwind + ret void +} + +declare void @clang.arc.use(...) nounwind + ; CHECK: attributes [[NUW]] = { nounwind } diff --git a/test/Transforms/ObjCARC/dont-infinite-loop-during-block-escape-analysis.ll b/test/Transforms/ObjCARC/dont-infinite-loop-during-block-escape-analysis.ll deleted file mode 100644 index bdee2be94f..0000000000 --- a/test/Transforms/ObjCARC/dont-infinite-loop-during-block-escape-analysis.ll +++ /dev/null @@ -1,87 +0,0 @@ -; RUN: opt -S -objc-arc < %s -; bugzilla://14551 -; rdar://12851911 - -; Make sure that we do not hang clang during escape analysis. - -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" -target triple = "x86_64-darwin" - -%struct.__block_descriptor = type { i64, i64 } -%struct.__block_byref_foo = type { i8*, %struct.__block_byref_foo*, i32, i32, i32 } - -@_NSConcreteGlobalBlock = external global i8* -@.str = private unnamed_addr constant [6 x i8] c"v8@?0\00", align 1 -@__block_descriptor_tmp = internal constant { i64, i64, i8*, i8* } { i64 0, i64 32, i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i8* null } -@__block_literal_global = internal constant { i8**, i32, i32, i8*, %struct.__block_descriptor* } { i8** @_NSConcreteGlobalBlock, i32 1342177280, i32 0, i8* bitcast (void (i8*)* @__hang_clang_block_invoke to i8*), %struct.__block_descriptor* bitcast ({ i64, i64, i8*, i8* }* @__block_descriptor_tmp to %struct.__block_descriptor*) }, align 8 - -define void @hang_clang() uwtable optsize ssp { -entry: - %foo = alloca %struct.__block_byref_foo, align 8 - %byref.isa = getelementptr inbounds %struct.__block_byref_foo* %foo, i64 0, i32 0 - store i8* null, i8** %byref.isa, align 8 - %byref.forwarding = getelementptr inbounds %struct.__block_byref_foo* %foo, i64 0, i32 1 - store %struct.__block_byref_foo* %foo, %struct.__block_byref_foo** %byref.forwarding, align 8 - %byref.flags = getelementptr inbounds %struct.__block_byref_foo* %foo, i64 0, i32 2 - store i32 536870912, i32* %byref.flags, align 8 - %byref.size = getelementptr inbounds %struct.__block_byref_foo* %foo, i64 0, i32 3 - store i32 32, i32* %byref.size, align 4 - %foo1 = getelementptr inbounds %struct.__block_byref_foo* %foo, i64 0, i32 4 - store i32 0, i32* %foo1, align 8, !tbaa !4 - br label %for.body - -for.body: ; preds = %for.inc.for.body_crit_edge, %entry - %0 = phi i1 [ true, %entry ], [ %phitmp, %for.inc.for.body_crit_edge ] - %i.06 = phi i32 [ 1, %entry ], [ %phitmp8, %for.inc.for.body_crit_edge ] - %block.05 = phi void (...)* [ null, %entry ], [ %block.1, %for.inc.for.body_crit_edge ] - br i1 %0, label %for.inc, label %if.then - -if.then: ; preds = %for.body - %1 = call i8* @objc_retainBlock(i8* bitcast ({ i8**, i32, i32, i8*, %struct.__block_descriptor* }* @__block_literal_global to i8*)) nounwind, !clang.arc.copy_on_escape !7 - %2 = bitcast i8* %1 to void (...)* - %3 = bitcast void (...)* %block.05 to i8* - call void @objc_release(i8* %3) nounwind, !clang.imprecise_release !7 - br label %for.inc - -for.inc: ; preds = %for.body, %if.then - %block.1 = phi void (...)* [ %2, %if.then ], [ %block.05, %for.body ] - %exitcond = icmp eq i32 %i.06, 10 - br i1 %exitcond, label %for.end, label %for.inc.for.body_crit_edge - -for.inc.for.body_crit_edge: ; preds = %for.inc - %.pre = load %struct.__block_byref_foo** %byref.forwarding, align 8 - %foo2.phi.trans.insert = getelementptr inbounds %struct.__block_byref_foo* %.pre, i64 0, i32 4 - %.pre7 = load i32* %foo2.phi.trans.insert, align 4, !tbaa !4 - %phitmp = icmp eq i32 %.pre7, 0 - %phitmp8 = add i32 %i.06, 1 - br label %for.body - -for.end: ; preds = %for.inc - %4 = bitcast %struct.__block_byref_foo* %foo to i8* - call void @_Block_object_dispose(i8* %4, i32 8) - %5 = bitcast void (...)* %block.1 to i8* - call void @objc_release(i8* %5) nounwind, !clang.imprecise_release !7 - ret void -} - -define internal void @__hang_clang_block_invoke(i8* nocapture %.block_descriptor) nounwind uwtable readnone optsize ssp { -entry: - ret void -} - -declare i8* @objc_retainBlock(i8*) - -declare void @objc_release(i8*) nonlazybind - -declare void @_Block_object_dispose(i8*, i32) - -!llvm.module.flags = !{!0, !1, !2, !3} - -!0 = metadata !{i32 1, metadata !"Objective-C Version", i32 2} -!1 = metadata !{i32 1, metadata !"Objective-C Image Info Version", i32 0} -!2 = metadata !{i32 1, metadata !"Objective-C Image Info Section", metadata !"__DATA, __objc_imageinfo, regular, no_dead_strip"} -!3 = metadata !{i32 4, metadata !"Objective-C Garbage Collection", i32 0} -!4 = metadata !{metadata !"int", metadata !5} -!5 = metadata !{metadata !"omnipotent char", metadata !6} -!6 = metadata !{metadata !"Simple C/C++ TBAA"} -!7 = metadata !{} diff --git a/test/Transforms/ObjCARC/intrinsic-use.ll b/test/Transforms/ObjCARC/intrinsic-use.ll new file mode 100644 index 0000000000..9c7b81a95d --- /dev/null +++ b/test/Transforms/ObjCARC/intrinsic-use.ll @@ -0,0 +1,63 @@ +; RUN: opt -basicaa -objc-arc -S < %s | FileCheck %s + +target datalayout = "e-p:64:64:64" + +declare i8* @objc_retain(i8*) +declare i8* @objc_retainAutorelease(i8*) +declare void @objc_release(i8*) +declare i8* @objc_autorelease(i8*) + +declare void @clang.arc.use(...) + +declare void @test0_helper(i8*, i8**) + +; Ensure that we honor clang.arc.use as a use and don't miscompile +; the reduced test case from <rdar://13195034>. +; +; FIXME: the fact that we re-order retains w.r.t. @clang.arc.use could +; be problematic if we get run twice, e.g. under LTO. +; +; CHECK: define void @test0( +; CHECK: @objc_retain(i8* %x) +; CHECK-NEXT: store i8* %y, i8** %temp0 +; CHECK-NEXT: @objc_retain(i8* %y) +; CHECK-NEXT: call void @test0_helper +; CHECK-NEXT: [[VAL1:%.*]] = load i8** %temp0 +; CHECK-NEXT: call void (...)* @clang.arc.use(i8* %y) +; CHECK-NEXT: @objc_retain(i8* [[VAL1]]) +; CHECK-NEXT: @objc_release(i8* %y) +; CHECK-NEXT: store i8* [[VAL1]], i8** %temp1 +; CHECK-NEXT: call void @test0_helper +; CHECK-NEXT: [[VAL2:%.*]] = load i8** %temp1 +; CHECK-NEXT: call void (...)* @clang.arc.use(i8* [[VAL1]]) +; CHECK-NEXT: @objc_retain(i8* [[VAL2]]) +; CHECK-NEXT: @objc_release(i8* [[VAL1]]) +; CHECK-NEXT: @objc_autorelease(i8* %x) +; CHECK-NEXT: store i8* %x, i8** %out +; CHECK-NEXT: @objc_release(i8* [[VAL2]]) +; CHECK-NEXT: ret void +define void @test0(i8** %out, i8* %x, i8* %y) { +entry: + %temp0 = alloca i8*, align 8 + %temp1 = alloca i8*, align 8 + %0 = call i8* @objc_retain(i8* %x) nounwind + %1 = call i8* @objc_retain(i8* %y) nounwind + store i8* %y, i8** %temp0 + call void @test0_helper(i8* %x, i8** %temp0) + %val1 = load i8** %temp0 + %2 = call i8* @objc_retain(i8* %val1) nounwind + call void (...)* @clang.arc.use(i8* %y) nounwind + call void @objc_release(i8* %y) nounwind + store i8* %val1, i8** %temp1 + call void @test0_helper(i8* %x, i8** %temp1) + %val2 = load i8** %temp1 + %3 = call i8* @objc_retain(i8* %val2) nounwind + call void (...)* @clang.arc.use(i8* %val1) nounwind + call void @objc_release(i8* %val1) nounwind + %4 = call i8* @objc_retain(i8* %x) nounwind + %5 = call i8* @objc_autorelease(i8* %x) nounwind + store i8* %x, i8** %out + call void @objc_release(i8* %val2) nounwind + call void @objc_release(i8* %x) nounwind + ret void +} diff --git a/test/Transforms/ObjCARC/no-objc-arc-exceptions.ll b/test/Transforms/ObjCARC/no-objc-arc-exceptions.ll index 9728f6e0d9..58b5bbe9c7 100644 --- a/test/Transforms/ObjCARC/no-objc-arc-exceptions.ll +++ b/test/Transforms/ObjCARC/no-objc-arc-exceptions.ll @@ -59,11 +59,12 @@ lpad: ; preds = %entry resume { i8*, i32 } %t8 } -; There is no !clang.arc.no_objc_arc_exceptions -; metadata here, so the optimizer shouldn't eliminate anything. +; There is no !clang.arc.no_objc_arc_exceptions metadata here, so the optimizer +; shouldn't eliminate anything, but *CAN* strength reduce the objc_retainBlock +; to an objc_retain. ; CHECK: define void @test0_no_metadata( -; CHECK: call i8* @objc_retainBlock( +; CHECK: call i8* @objc_retain( ; CHECK: invoke ; CHECK: call void @objc_release( ; CHECK: } diff --git a/test/Transforms/ObjCARC/retain-block-escape-analysis.ll b/test/Transforms/ObjCARC/retain-block-escape-analysis.ll new file mode 100644 index 0000000000..2c1ddce328 --- /dev/null +++ b/test/Transforms/ObjCARC/retain-block-escape-analysis.ll @@ -0,0 +1,127 @@ +; RUN: opt -S -objc-arc < %s | FileCheck %s + +declare i8* @objc_retain(i8*) nonlazybind +declare void @objc_release(i8*) nonlazybind +declare i8* @objc_retainBlock(i8*) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Use by an instruction which copies the value is an escape if the ; +; result is an escape. The current instructions with this property are: ; +; ; +; 1. BitCast. ; +; 2. GEP. ; +; 3. PhiNode. ; +; 4. SelectInst. ; +; ; +; Make sure that such instructions do not confuse the optimizer into removing ; +; an objc_retainBlock that is needed. ; +; ; +; rdar://13273675. (With extra test cases to handle bitcast, phi, and select. ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +define void @bitcasttest(i8* %storage, void (...)* %block) { +; CHECK: define void @bitcasttest +entry: + %t1 = bitcast void (...)* %block to i8* +; CHECK-NOT: tail call i8* @objc_retain + %t2 = tail call i8* @objc_retain(i8* %t1) +; CHECK: tail call i8* @objc_retainBlock + %t3 = tail call i8* @objc_retainBlock(i8* %t1), !clang.arc.copy_on_escape !0 + %t4 = bitcast i8* %storage to void (...)** + %t5 = bitcast i8* %t3 to void (...)* + store void (...)* %t5, void (...)** %t4, align 8 +; CHECK-NOT: call void @objc_release + call void @objc_release(i8* %t1) + ret void +} + +define void @geptest(void (...)** %storage_array, void (...)* %block) { +; CHECK: define void @geptest +entry: + %t1 = bitcast void (...)* %block to i8* +; CHECK-NOT: tail call i8* @objc_retain + %t2 = tail call i8* @objc_retain(i8* %t1) +; CHECK: tail call i8* @objc_retainBlock + %t3 = tail call i8* @objc_retainBlock(i8* %t1), !clang.arc.copy_on_escape !0 + %t4 = bitcast i8* %t3 to void (...)* + + %storage = getelementptr inbounds void (...)** %storage_array, i64 0 + + store void (...)* %t4, void (...)** %storage, align 8 +; CHECK-NOT: call void @objc_release + call void @objc_release(i8* %t1) + ret void +} + +define void @selecttest(void (...)** %store1, void (...)** %store2, + void (...)* %block) { +; CHECK: define void @selecttest +entry: + %t1 = bitcast void (...)* %block to i8* +; CHECK-NOT: tail call i8* @objc_retain + %t2 = tail call i8* @objc_retain(i8* %t1) +; CHECK: tail call i8* @objc_retainBlock + %t3 = tail call i8* @objc_retainBlock(i8* %t1), !clang.arc.copy_on_escape !0 + %t4 = bitcast i8* %t3 to void (...)* + %store = select i1 undef, void (...)** %store1, void (...)** %store2 + store void (...)* %t4, void (...)** %store, align 8 +; CHECK-NOT: call void @objc_release + call void @objc_release(i8* %t1) + ret void +} + +define void @phinodetest(void (...)** %storage1, + void (...)** %storage2, + void (...)* %block) { +; CHECK: define void @phinodetest +entry: + %t1 = bitcast void (...)* %block to i8* +; CHECK-NOT: tail call i8* @objc_retain + %t2 = tail call i8* @objc_retain(i8* %t1) +; CHECK: tail call i8* @objc_retainBlock + %t3 = tail call i8* @objc_retainBlock(i8* %t1), !clang.arc.copy_on_escape !0 + %t4 = bitcast i8* %t3 to void (...)* + br i1 undef, label %store1_set, label %store2_set + +store1_set: + br label %end + +store2_set: + br label %end + +end: + %storage = phi void (...)** [ %storage1, %store1_set ], [ %storage2, %store2_set] + store void (...)* %t4, void (...)** %storage, align 8 +; CHECK-NOT: call void @objc_release + call void @objc_release(i8* %t1) + ret void +} + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; This test makes sure that we do not hang clang when visiting a use ; +; cycle caused by phi nodes during objc-arc analysis. *NOTE* This ; +; test case looks a little convoluted since it was produced by ; +; bugpoint. ; +; ; +; bugzilla://14551 ; +; rdar://12851911 ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +define void @phinode_use_cycle(i8* %block) uwtable optsize ssp { +; CHECK: define void @phinode_use_cycle(i8* %block) +entry: + br label %for.body + +for.body: ; preds = %if.then, %for.body, %entry + %block.05 = phi void (...)* [ null, %entry ], [ %1, %if.then ], [ %block.05, %for.body ] + br i1 undef, label %for.body, label %if.then + +if.then: ; preds = %for.body + %0 = call i8* @objc_retainBlock(i8* %block), !clang.arc.copy_on_escape !0 + %1 = bitcast i8* %0 to void (...)* + %2 = bitcast void (...)* %block.05 to i8* + call void @objc_release(i8* %2) nounwind, !clang.imprecise_release !0 + br label %for.body +} + +!0 = metadata !{} diff --git a/test/Transforms/ObjCARC/retain-block.ll b/test/Transforms/ObjCARC/retain-block.ll index ee57049d8a..1bb3f0276a 100644 --- a/test/Transforms/ObjCARC/retain-block.ll +++ b/test/Transforms/ObjCARC/retain-block.ll @@ -98,7 +98,7 @@ entry: ; CHECK-NEXT: tail call i8* @objc_retainBlock(i8* %tmp) [[NUW]] ; CHECK-NEXT: @use_pointer(i8* %tmp2) ; CHECK-NEXT: @use_pointer(i8* %tmp2) -; CHECK-NEXT: tail call void @objc_release(i8* %tmp) [[NUW]], !clang.imprecise_release !0 +; CHECK-NEXT: tail call void @objc_release(i8* %tmp2) [[NUW]], !clang.imprecise_release !0 ; CHECK-NOT: @objc ; CHECK: } define void @test1_no_metadata(i8* %tmp) { @@ -122,7 +122,7 @@ entry: ; CHECK-NEXT: store i8* %tmp2, i8** %z ; CHECK-NEXT: @use_pointer(i8* %tmp2) ; CHECK-NEXT: @use_pointer(i8* %tmp2) -; CHECK-NEXT: tail call void @objc_release(i8* %tmp) [[NUW]], !clang.imprecise_release !0 +; CHECK-NEXT: tail call void @objc_release(i8* %tmp2) [[NUW]], !clang.imprecise_release !0 ; CHECK-NOT: @objc ; CHECK: } define void @test1_escape(i8* %tmp, i8** %z) { diff --git a/test/Transforms/ObjCARC/retain-not-declared.ll b/test/Transforms/ObjCARC/retain-not-declared.ll index e834179bb7..165829f7c0 100644 --- a/test/Transforms/ObjCARC/retain-not-declared.ll +++ b/test/Transforms/ObjCARC/retain-not-declared.ll @@ -21,8 +21,8 @@ define i8* @test0(i8* %p) { entry: %call = tail call i8* @objc_unretainedObject(i8* %p) %0 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %call) nounwind - %1 = tail call i8* @objc_autoreleaseReturnValue(i8* %0) nounwind - ret i8* %1 + %1 = tail call i8* @objc_autoreleaseReturnValue(i8* %call) nounwind + ret i8* %call } ; Properly create the @objc_retain declaration when it doesn't already exist. |