aboutsummaryrefslogtreecommitdiff
path: root/test/Transforms/ObjCARC
diff options
context:
space:
mode:
Diffstat (limited to 'test/Transforms/ObjCARC')
-rw-r--r--test/Transforms/ObjCARC/arc-annotations.ll307
-rw-r--r--test/Transforms/ObjCARC/basic.ll4
-rw-r--r--test/Transforms/ObjCARC/clang-arc-used-intrinsic-removed-if-isolated.ll16
-rw-r--r--test/Transforms/ObjCARC/contract.ll11
-rw-r--r--test/Transforms/ObjCARC/dont-infinite-loop-during-block-escape-analysis.ll87
-rw-r--r--test/Transforms/ObjCARC/intrinsic-use.ll63
-rw-r--r--test/Transforms/ObjCARC/no-objc-arc-exceptions.ll7
-rw-r--r--test/Transforms/ObjCARC/retain-block-escape-analysis.ll127
-rw-r--r--test/Transforms/ObjCARC/retain-block.ll4
-rw-r--r--test/Transforms/ObjCARC/retain-not-declared.ll4
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.