summaryrefslogtreecommitdiffstats
path: root/test/Transforms/ObjCARC
diff options
context:
space:
mode:
Diffstat (limited to 'test/Transforms/ObjCARC')
-rw-r--r--test/Transforms/ObjCARC/apelim.ll4
-rw-r--r--test/Transforms/ObjCARC/arc-annotations.ll242
-rw-r--r--test/Transforms/ObjCARC/basic.ll942
-rw-r--r--test/Transforms/ObjCARC/cfg-hazards.ll36
-rw-r--r--test/Transforms/ObjCARC/contract-marker.ll2
-rw-r--r--test/Transforms/ObjCARC/contract-storestrong.ll3
-rw-r--r--test/Transforms/ObjCARC/contract-testcases.ll2
-rw-r--r--test/Transforms/ObjCARC/contract.ll56
-rw-r--r--test/Transforms/ObjCARC/expand.ll76
-rw-r--r--test/Transforms/ObjCARC/gvn.ll3
-rw-r--r--test/Transforms/ObjCARC/intrinsic-use-isolated.ll (renamed from test/Transforms/ObjCARC/clang-arc-used-intrinsic-removed-if-isolated.ll)0
-rw-r--r--test/Transforms/ObjCARC/intrinsic-use.ll53
-rw-r--r--test/Transforms/ObjCARC/invoke.ll4
-rw-r--r--test/Transforms/ObjCARC/move-and-merge-autorelease.ll2
-rw-r--r--test/Transforms/ObjCARC/retain-block-escape-analysis.ll96
-rw-r--r--test/Transforms/ObjCARC/rv.ll50
-rw-r--r--test/Transforms/ObjCARC/tail-call-invariant-enforcement.ll65
17 files changed, 1251 insertions, 385 deletions
diff --git a/test/Transforms/ObjCARC/apelim.ll b/test/Transforms/ObjCARC/apelim.ll
index 4541b3f..14412c6 100644
--- a/test/Transforms/ObjCARC/apelim.ll
+++ b/test/Transforms/ObjCARC/apelim.ll
@@ -26,7 +26,7 @@ entry:
ret void
}
-; CHECK: define internal void @_GLOBAL__I_x()
+; CHECK: define internal void @_GLOBAL__I_x() {
; CHECK-NOT: @objc
; CHECK: }
define internal void @_GLOBAL__I_x() {
@@ -37,7 +37,7 @@ entry:
ret void
}
-; CHECK: define internal void @_GLOBAL__I_y()
+; CHECK: define internal void @_GLOBAL__I_y() {
; CHECK: %0 = call i8* @objc_autoreleasePoolPush() [[NUW:#[0-9]+]]
; CHECK: call void @objc_autoreleasePoolPop(i8* %0) [[NUW]]
; CHECK: }
diff --git a/test/Transforms/ObjCARC/arc-annotations.ll b/test/Transforms/ObjCARC/arc-annotations.ll
index 4c56b4a..c0dea4b 100644
--- a/test/Transforms/ObjCARC/arc-annotations.ll
+++ b/test/Transforms/ObjCARC/arc-annotations.ll
@@ -30,25 +30,25 @@ declare i8* @returner()
; 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: %0 = tail call i8* @objc_retain(i8* %a) #0, !llvm.arc.annotation.bottomup ![[ANN0:[0-9]+]], !llvm.arc.annotation.topdown ![[ANN1:[0-9]+]]
; 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: store float 2.000000e+00, float* %b, !llvm.arc.annotation.bottomup ![[ANN2:[0-9]+]]
; 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: store i32 7, i32* %x, !llvm.arc.annotation.bottomup ![[ANN2]]
; 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 @objc_release(i8* %c) #0, !llvm.arc.annotation.bottomup ![[ANN3:[0-9]+]], !llvm.arc.annotation.topdown ![[ANN4:[0-9]+]]
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None)
; CHECK: }
define void @test0(i32* %x, i1 %p) nounwind {
@@ -73,235 +73,11 @@ return:
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"}
+; CHECK: ![[ANN0]] = metadata !{metadata !"(test0,%x)", metadata !"S_Use", metadata !"S_None"}
+; CHECK: ![[ANN1]] = metadata !{metadata !"(test0,%x)", metadata !"S_None", metadata !"S_Retain"}
+; CHECK: ![[ANN2]] = metadata !{metadata !"(test0,%x)", metadata !"S_Release", metadata !"S_Use"}
+; CHECK: ![[ANN3]] = metadata !{metadata !"(test0,%x)", metadata !"S_None", metadata !"S_Release"}
+; CHECK: ![[ANN4]] = metadata !{metadata !"(test0,%x)", metadata !"S_Retain", metadata !"S_None"}
diff --git a/test/Transforms/ObjCARC/basic.ll b/test/Transforms/ObjCARC/basic.ll
index 828a8a7..ca12792 100644
--- a/test/Transforms/ObjCARC/basic.ll
+++ b/test/Transforms/ObjCARC/basic.ll
@@ -20,6 +20,7 @@ declare void @callee()
declare void @callee_fnptr(void ()*)
declare void @invokee()
declare i8* @returner()
+declare void @bar(i32 ()*)
declare void @llvm.dbg.value(metadata, i64, metadata)
@@ -28,10 +29,11 @@ declare i8* @objc_msgSend(i8*, i8*, ...)
; Simple retain+release pair deletion, with some intervening control
; flow and harmless instructions.
-; CHECK: define void @test0(
-; CHECK-NOT: @objc_
+; CHECK: define void @test0_precise(i32* %x, i1 %p) [[NUW:#[0-9]+]] {
+; CHECK: @objc_retain
+; CHECK: @objc_release
; CHECK: }
-define void @test0(i32* %x, i1 %p) nounwind {
+define void @test0_precise(i32* %x, i1 %p) nounwind {
entry:
%a = bitcast i32* %x to i8*
%0 = call i8* @objc_retain(i8* %a) nounwind
@@ -53,16 +55,41 @@ return:
ret void
}
+; CHECK: define void @test0_imprecise(i32* %x, i1 %p) [[NUW]] {
+; CHECK-NOT: @objc_
+; CHECK: }
+define void @test0_imprecise(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, !clang.imprecise_release !0
+ 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: define void @test1_precise(i32* %x, i1 %p, i1 %q) [[NUW]] {
; CHECK: @objc_retain(i8* %a)
; CHECK: @objc_release
; CHECK: }
-define void @test1(i32* %x, i1 %p, i1 %q) nounwind {
+define void @test1_precise(i32* %x, i1 %p, i1 %q) nounwind {
entry:
%a = bitcast i32* %x to i8*
%0 = call i8* @objc_retain(i8* %a) nounwind
@@ -88,9 +115,69 @@ alt_return:
ret void
}
+; CHECK: define void @test1_imprecise(i32* %x, i1 %p, i1 %q) [[NUW]] {
+; CHECK: @objc_retain(i8* %a)
+; CHECK: @objc_release
+; CHECK: }
+define void @test1_imprecise(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, !clang.imprecise_release !0
+ ret void
+
+alt_return:
+ ret void
+}
+
+
; Don't do partial elimination into two different CFG diamonds.
-; CHECK: define void @test1b(
+; CHECK: define void @test1b_precise(i8* %x, i1 %p, i1 %q) {
+; CHECK: entry:
+; CHECK: tail call i8* @objc_retain(i8* %x) [[NUW]]
+; CHECK-NOT: @objc_
+; CHECK: if.end5:
+; CHECK: tail call void @objc_release(i8* %x) [[NUW]]
+; CHECK-NOT: @objc_
+; CHECK: }
+define void @test1b_precise(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
+ ret void
+}
+
+; CHECK: define void @test1b_imprecise(
; CHECK: entry:
; CHECK: tail call i8* @objc_retain(i8* %x) [[NUW:#[0-9]+]]
; CHECK-NOT: @objc_
@@ -98,7 +185,7 @@ alt_return:
; CHECK: tail call void @objc_release(i8* %x) [[NUW]], !clang.imprecise_release !0
; CHECK-NOT: @objc_
; CHECK: }
-define void @test1b(i8* %x, i1 %p, i1 %q) {
+define void @test1b_imprecise(i8* %x, i1 %p, i1 %q) {
entry:
tail call i8* @objc_retain(i8* %x) nounwind
br i1 %p, label %if.then, label %if.end
@@ -119,14 +206,15 @@ if.end5: ; preds = %if.then3, %if.end
ret void
}
+
; Like test0 but the pointer is passed to an intervening call,
; so the optimization is not safe.
-; CHECK: define void @test2(
+; CHECK: define void @test2_precise(
; CHECK: @objc_retain(i8* %a)
; CHECK: @objc_release
; CHECK: }
-define void @test2(i32* %x, i1 %p) nounwind {
+define void @test2_precise(i32* %x, i1 %p) nounwind {
entry:
%a = bitcast i32* %x to i8*
%0 = call i8* @objc_retain(i8* %a) nounwind
@@ -151,16 +239,45 @@ return:
ret void
}
+; CHECK: define void @test2_imprecise(
+; CHECK: @objc_retain(i8* %a)
+; CHECK: @objc_release
+; CHECK: }
+define void @test2_imprecise(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
+ call void @use_pointer(i8* %0)
+ %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, !clang.imprecise_release !0
+ 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: define void @test3_precise(
; TODO: @objc_retain(i8* %a)
; TODO: @objc_release
; CHECK: }
-define void @test3(i32* %x, i1* %q) nounwind {
+define void @test3_precise(i32* %x, i1* %q) nounwind {
entry:
%a = bitcast i32* %x to i8*
%0 = call i8* @objc_retain(i8* %a) nounwind
@@ -176,16 +293,37 @@ return:
ret void
}
+; CHECK: define void @test3_imprecise(
+; TODO: @objc_retain(i8* %a)
+; TODO: @objc_release
+; CHECK: }
+define void @test3_imprecise(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, !clang.imprecise_release !0
+ %j = load volatile i1* %q
+ br i1 %j, label %loop, label %return
+
+return:
+ ret void
+}
+
+
; TODO: For now, assume this can't happen.
; Like test0 but the retain is in a loop,
; so the optimization is not safe.
-; CHECK: define void @test4(
+; CHECK: define void @test4_precise(
; TODO: @objc_retain(i8* %a)
; TODO: @objc_release
; CHECK: }
-define void @test4(i32* %x, i1* %q) nounwind {
+define void @test4_precise(i32* %x, i1* %q) nounwind {
entry:
br label %loop
@@ -201,14 +339,35 @@ return:
ret void
}
+; CHECK: define void @test4_imprecise(
+; TODO: @objc_retain(i8* %a)
+; TODO: @objc_release
+; CHECK: }
+define void @test4_imprecise(i32* %x, i1* %q) nounwind {
+entry:
+ br label %loop
+
+loop:
+ %a = bitcast i32* %x to i8*
+ %0 = call i8* @objc_retain(i8* %a) nounwind
+ %j = load volatile i1* %q
+ br i1 %j, label %loop, label %return
+
+return:
+ %c = bitcast i32* %x to i8*
+ call void @objc_release(i8* %c) nounwind, !clang.imprecise_release !0
+ ret void
+}
+
+
; Like test0 but the pointer is conditionally passed to an intervening call,
; so the optimization is not safe.
-; CHECK: define void @test5(
+; CHECK: define void @test5a(
; CHECK: @objc_retain(i8*
; CHECK: @objc_release
; CHECK: }
-define void @test5(i32* %x, i1 %q, i8* %y) nounwind {
+define void @test5a(i32* %x, i1 %q, i8* %y) nounwind {
entry:
%a = bitcast i32* %x to i8*
%0 = call i8* @objc_retain(i8* %a) nounwind
@@ -220,13 +379,98 @@ entry:
ret void
}
+; CHECK: define void @test5b(
+; CHECK: @objc_retain(i8*
+; CHECK: @objc_release
+; CHECK: }
+define void @test5b(i32* %x, i1 %q, i8* %y) nounwind {
+entry:
+ %a = bitcast i32* %x to i8*
+ %0 = call i8* @objc_retain(i8* %a) nounwind
+ %s = select i1 %q, i8* %y, i8* %0
+ call void @use_pointer(i8* %s)
+ store i32 7, i32* %x
+ %c = bitcast i32* %x to i8*
+ call void @objc_release(i8* %c) nounwind, !clang.imprecise_release !0
+ ret void
+}
+
+
; retain+release pair deletion, where the release happens on two different
; flow paths.
-; CHECK: define void @test6(
+; CHECK: define void @test6a(
+; CHECK: entry:
+; CHECK: tail call i8* @objc_retain(
+; CHECK: t:
+; CHECK: call void @objc_release(
+; CHECK: f:
+; CHECK: call void @objc_release(
+; CHECK: return:
+; CHECK: }
+define void @test6a(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
+ %ct = bitcast i32* %x to i8*
+ call void @objc_release(i8* %ct) nounwind
+ br label %return
+
+f:
+ store i32 7, i32* %x
+ call void @callee()
+ %cf = bitcast i32* %x to i8*
+ call void @objc_release(i8* %cf) nounwind
+ br label %return
+
+return:
+ ret void
+}
+
+; CHECK: define void @test6b(
; CHECK-NOT: @objc_
; CHECK: }
-define void @test6(i32* %x, i1 %p) nounwind {
+define void @test6b(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
+ %ct = bitcast i32* %x to i8*
+ call void @objc_release(i8* %ct) nounwind, !clang.imprecise_release !0
+ br label %return
+
+f:
+ store i32 7, i32* %x
+ call void @callee()
+ %cf = bitcast i32* %x to i8*
+ call void @objc_release(i8* %cf) nounwind, !clang.imprecise_release !0
+ br label %return
+
+return:
+ ret void
+}
+
+; CHECK: define void @test6c(
+; CHECK: entry:
+; CHECK: tail call i8* @objc_retain(
+; CHECK: t:
+; CHECK: call void @objc_release(
+; CHECK: f:
+; CHECK: call void @objc_release(
+; CHECK: return:
+; CHECK: }
+define void @test6c(i32* %x, i1 %p) nounwind {
entry:
%a = bitcast i32* %x to i8*
%0 = call i8* @objc_retain(i8* %a) nounwind
@@ -244,6 +488,40 @@ f:
store i32 7, i32* %x
call void @callee()
%cf = bitcast i32* %x to i8*
+ call void @objc_release(i8* %cf) nounwind, !clang.imprecise_release !0
+ br label %return
+
+return:
+ ret void
+}
+
+; CHECK: define void @test6d(
+; CHECK: entry:
+; CHECK: tail call i8* @objc_retain(
+; CHECK: t:
+; CHECK: call void @objc_release(
+; CHECK: f:
+; CHECK: call void @objc_release(
+; CHECK: return:
+; CHECK: }
+define void @test6d(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
+ %ct = bitcast i32* %x to i8*
+ call void @objc_release(i8* %ct) nounwind, !clang.imprecise_release !0
+ br label %return
+
+f:
+ store i32 7, i32* %x
+ call void @callee()
+ %cf = bitcast i32* %x to i8*
call void @objc_release(i8* %cf) nounwind
br label %return
@@ -251,11 +529,19 @@ return:
ret void
}
+
; retain+release pair deletion, where the retain happens on two different
; flow paths.
-; CHECK: define void @test7(
-; CHECK-NOT: @objc_
+; CHECK: define void @test7(
+; CHECK: entry:
+; CHECK-NOT: objc_
+; CHECK: t:
+; CHECK: call i8* @objc_retain
+; CHECK: f:
+; CHECK: call i8* @objc_retain
+; CHECK: return:
+; CHECK: call void @objc_release
; CHECK: }
define void @test7(i32* %x, i1 %p) nounwind {
entry:
@@ -281,17 +567,44 @@ return:
ret void
}
+; CHECK: define void @test7b(
+; CHECK-NOT: @objc_
+; CHECK: }
+define void @test7b(i32* %x, i1 %p) nounwind {
+entry:
+ %a = bitcast i32* %x to i8*
+ br i1 %p, label %t, label %f
+
+t:
+ %0 = call i8* @objc_retain(i8* %a) nounwind
+ store i8 3, i8* %a
+ %b = bitcast i32* %x to float*
+ store float 2.0, float* %b
+ br label %return
+
+f:
+ %1 = call i8* @objc_retain(i8* %a) nounwind
+ store i32 7, i32* %x
+ call void @callee()
+ br label %return
+
+return:
+ %c = bitcast i32* %x to i8*
+ call void @objc_release(i8* %c) nounwind, !clang.imprecise_release !0
+ ret void
+}
+
; Like test7, but there's a retain/retainBlock mismatch. Don't delete!
-; CHECK: define void @test7b
+; CHECK: define void @test7c
; CHECK: t:
-; CHECK: call i8* @objc_retainBlock
+; CHECK: call i8* @objc_retainBlock
; CHECK: f:
-; CHECK: call i8* @objc_retain
+; CHECK: call i8* @objc_retain
; CHECK: return:
-; CHECK: call void @objc_release
+; CHECK: call void @objc_release
; CHECK: }
-define void @test7b(i32* %x, i1 %p) nounwind {
+define void @test7c(i32* %x, i1 %p) nounwind {
entry:
%a = bitcast i32* %x to i8*
br i1 %p, label %t, label %f
@@ -318,10 +631,106 @@ return:
; retain+release pair deletion, where the retain and release both happen on
; different flow paths. Wild!
-; CHECK: define void @test8(
+; CHECK: define void @test8a(
+; CHECK: entry:
+; CHECK: t:
+; CHECK: @objc_retain
+; CHECK: f:
+; CHECK: @objc_retain
+; CHECK: mid:
+; CHECK: u:
+; CHECK: @objc_release
+; CHECK: g:
+; CHECK: @objc_release
+; CHECK: return:
+; CHECK: }
+define void @test8a(i32* %x, i1 %p, i1 %q) nounwind {
+entry:
+ %a = bitcast i32* %x to i8*
+ br i1 %p, label %t, label %f
+
+t:
+ %0 = call i8* @objc_retain(i8* %a) nounwind
+ store i8 3, i8* %a
+ %b = bitcast i32* %x to float*
+ store float 2.0, float* %b
+ br label %mid
+
+f:
+ %1 = call i8* @objc_retain(i8* %a) nounwind
+ store i32 7, i32* %x
+ br label %mid
+
+mid:
+ br i1 %q, label %u, label %g
+
+u:
+ call void @callee()
+ %cu = bitcast i32* %x to i8*
+ call void @objc_release(i8* %cu) nounwind
+ br label %return
+
+g:
+ %cg = bitcast i32* %x to i8*
+ call void @objc_release(i8* %cg) nounwind
+ br label %return
+
+return:
+ ret void
+}
+
+; CHECK: define void @test8b(
; CHECK-NOT: @objc_
; CHECK: }
-define void @test8(i32* %x, i1 %p, i1 %q) nounwind {
+define void @test8b(i32* %x, i1 %p, i1 %q) nounwind {
+entry:
+ %a = bitcast i32* %x to i8*
+ br i1 %p, label %t, label %f
+
+t:
+ %0 = call i8* @objc_retain(i8* %a) nounwind
+ store i8 3, i8* %a
+ %b = bitcast i32* %x to float*
+ store float 2.0, float* %b
+ br label %mid
+
+f:
+ %1 = call i8* @objc_retain(i8* %a) nounwind
+ store i32 7, i32* %x
+ br label %mid
+
+mid:
+ br i1 %q, label %u, label %g
+
+u:
+ call void @callee()
+ %cu = bitcast i32* %x to i8*
+ call void @objc_release(i8* %cu) nounwind, !clang.imprecise_release !0
+ br label %return
+
+g:
+ %cg = bitcast i32* %x to i8*
+ call void @objc_release(i8* %cg) nounwind, !clang.imprecise_release !0
+ br label %return
+
+return:
+ ret void
+}
+
+; CHECK: define void @test8c(
+; CHECK: entry:
+; CHECK: t:
+; CHECK: @objc_retain
+; CHECK: f:
+; CHECK: @objc_retain
+; CHECK: mid:
+; CHECK: u:
+; CHECK: @objc_release
+; CHECK: g:
+; CHECK: @objc_release
+; CHECK: return:
+; CHECK: }
+define void @test8c(i32* %x, i1 %p, i1 %q) nounwind {
entry:
%a = bitcast i32* %x to i8*
br i1 %p, label %t, label %f
@@ -349,6 +758,54 @@ u:
g:
%cg = bitcast i32* %x to i8*
+ call void @objc_release(i8* %cg) nounwind, !clang.imprecise_release !0
+ br label %return
+
+return:
+ ret void
+}
+
+; CHECK: define void @test8d(
+; CHECK: entry:
+; CHECK: t:
+; CHECK: @objc_retain
+; CHECK: f:
+; CHECK: @objc_retain
+; CHECK: mid:
+; CHECK: u:
+; CHECK: @objc_release
+; CHECK: g:
+; CHECK: @objc_release
+; CHECK: return:
+; CHECK: }
+define void @test8d(i32* %x, i1 %p, i1 %q) nounwind {
+entry:
+ %a = bitcast i32* %x to i8*
+ br i1 %p, label %t, label %f
+
+t:
+ %0 = call i8* @objc_retain(i8* %a) nounwind
+ store i8 3, i8* %a
+ %b = bitcast i32* %x to float*
+ store float 2.0, float* %b
+ br label %mid
+
+f:
+ %1 = call i8* @objc_retain(i8* %a) nounwind
+ store i32 7, i32* %x
+ br label %mid
+
+mid:
+ br i1 %q, label %u, label %g
+
+u:
+ call void @callee()
+ %cu = bitcast i32* %x to i8*
+ call void @objc_release(i8* %cu) nounwind, !clang.imprecise_release !0
+ br label %return
+
+g:
+ %cg = bitcast i32* %x to i8*
call void @objc_release(i8* %cg) nounwind
br label %return
@@ -486,6 +943,7 @@ entry:
; CHECK-NEXT: @use_pointer
; CHECK-NEXT: @use_pointer
; CHECK-NEXT: ret void
+; CHECK-NEXT: }
define void @test13b(i8* %x, i64 %n) {
entry:
call i8* @objc_retain(i8* %x) nounwind
@@ -527,6 +985,7 @@ entry:
; CHECK-NEXT: @use_pointer
; CHECK-NEXT: @use_pointer
; CHECK-NEXT: ret void
+; CHECK-NEXT: }
define void @test13d(i8* %x, i64 %n) {
entry:
call i8* @objc_retain(i8* %x) nounwind
@@ -583,7 +1042,9 @@ entry:
; CHECK: define void @test15b
; CHECK-NEXT: entry:
+; CHECK-NEXT: @objc_retain
; CHECK-NEXT: @objc_autorelease
+; CHECK-NEXT: @objc_release
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define void @test15b(i8* %x, i64 %n) {
@@ -594,13 +1055,60 @@ entry:
ret void
}
+; CHECK: define void @test15c
+; CHECK-NEXT: entry:
+; CHECK-NEXT: @objc_autorelease
+; CHECK-NEXT: ret void
+; CHECK-NEXT: }
+define void @test15c(i8* %x, i64 %n) {
+entry:
+ call i8* @objc_retain(i8* %x) nounwind
+ call i8* @objc_autorelease(i8* %x) nounwind
+ call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
+ ret void
+}
+
; Retain+release pairs in diamonds, all dominated by a retain.
-; CHECK: define void @test16(
+; CHECK: define void @test16a(
+; CHECK: @objc_retain(i8* %x)
+; CHECK-NOT: @objc
+; CHECK: }
+define void @test16a(i1 %a, i1 %b, i8* %x) {
+entry:
+ call i8* @objc_retain(i8* %x) nounwind
+ br i1 %a, label %red, label %orange
+
+red:
+ call i8* @objc_retain(i8* %x) nounwind
+ br label %yellow
+
+orange:
+ call i8* @objc_retain(i8* %x) nounwind
+ br label %yellow
+
+yellow:
+ call void @use_pointer(i8* %x)
+ call void @use_pointer(i8* %x)
+ br i1 %b, label %green, label %blue
+
+green:
+ call void @objc_release(i8* %x) nounwind
+ br label %purple
+
+blue:
+ call void @objc_release(i8* %x) nounwind
+ br label %purple
+
+purple:
+ ret void
+}
+
+; CHECK: define void @test16b(
; CHECK: @objc_retain(i8* %x)
; CHECK-NOT: @objc
; CHECK: }
-define void @test16(i1 %a, i1 %b, i8* %x) {
+define void @test16b(i1 %a, i1 %b, i8* %x) {
entry:
call i8* @objc_retain(i8* %x) nounwind
br i1 %a, label %red, label %orange
@@ -619,17 +1127,86 @@ yellow:
br i1 %b, label %green, label %blue
green:
+ call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
+ br label %purple
+
+blue:
call void @objc_release(i8* %x) nounwind
br label %purple
+purple:
+ ret void
+}
+
+; CHECK: define void @test16c(
+; CHECK: @objc_retain(i8* %x)
+; CHECK-NOT: @objc
+; CHECK: }
+define void @test16c(i1 %a, i1 %b, i8* %x) {
+entry:
+ call i8* @objc_retain(i8* %x) nounwind
+ br i1 %a, label %red, label %orange
+
+red:
+ call i8* @objc_retain(i8* %x) nounwind
+ br label %yellow
+
+orange:
+ call i8* @objc_retain(i8* %x) nounwind
+ br label %yellow
+
+yellow:
+ call void @use_pointer(i8* %x)
+ call void @use_pointer(i8* %x)
+ br i1 %b, label %green, label %blue
+
+green:
+ call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
+ br label %purple
+
blue:
+ call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
+ br label %purple
+
+purple:
+ ret void
+}
+
+; CHECK: define void @test16d(
+; CHECK: @objc_retain(i8* %x)
+; CHECK-NOT: @objc
+; CHECK: }
+define void @test16d(i1 %a, i1 %b, i8* %x) {
+entry:
+ call i8* @objc_retain(i8* %x) nounwind
+ br i1 %a, label %red, label %orange
+
+red:
+ call i8* @objc_retain(i8* %x) nounwind
+ br label %yellow
+
+orange:
+ call i8* @objc_retain(i8* %x) nounwind
+ br label %yellow
+
+yellow:
+ call void @use_pointer(i8* %x)
+ call void @use_pointer(i8* %x)
+ br i1 %b, label %green, label %blue
+
+green:
call void @objc_release(i8* %x) nounwind
br label %purple
+blue:
+ call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
+ br label %purple
+
purple:
ret void
}
+
; Retain+release pairs in diamonds, all post-dominated by a release.
; CHECK: define void @test17(
@@ -720,6 +1297,7 @@ entry:
; CHECK: define void @test20(
; CHECK: %tmp1 = tail call i8* @objc_retain(i8* %tmp) [[NUW]]
; CHECK-NEXT: invoke
+; CHECK: }
define void @test20(double* %self) {
if.then12:
%tmp = bitcast double* %self to i8*
@@ -747,6 +1325,7 @@ if.end: ; preds = %invoke.cont23
; CHECK: define i8* @test21(
; CHECK: call i8* @returner()
; CHECK-NEXT: ret i8* %call
+; CHECK-NEXT: }
define i8* @test21() {
entry:
%call = call i8* @returner()
@@ -799,7 +1378,7 @@ entry:
; Don't optimize objc_retainBlock, but do strength reduce it.
-; CHECK: define void @test23b
+; CHECK: define void @test23b(i8* %p) {
; CHECK: @objc_retain
; CHECK: @objc_release
; CHECK: }
@@ -1163,12 +1742,16 @@ done:
ret void
}
-; Delete retain,release if there's just a possible dec.
+; Delete retain,release if there's just a possible dec and we have imprecise
+; releases.
-; CHECK: define void @test34(
-; CHECK-NOT: @objc_
+; CHECK: define void @test34a(
+; CHECK: call i8* @objc_retain
+; CHECK: true:
+; CHECK: done:
+; CHECK: call void @objc_release
; CHECK: }
-define void @test34(i8* %p, i1 %x, i8* %y) {
+define void @test34a(i8* %p, i1 %x, i8* %y) {
entry:
%f0 = call i8* @objc_retain(i8* %p)
br i1 %x, label %true, label %done
@@ -1184,12 +1767,38 @@ done:
ret void
}
-; Delete retain,release if there's just a use.
-
-; CHECK: define void @test35(
+; CHECK: define void @test34b(
; CHECK-NOT: @objc_
; CHECK: }
-define void @test35(i8* %p, i1 %x, i8* %y) {
+define void @test34b(i8* %p, i1 %x, i8* %y) {
+entry:
+ %f0 = call i8* @objc_retain(i8* %p)
+ br i1 %x, label %true, label %done
+
+true:
+ call void @callee()
+ br label %done
+
+done:
+ %g = bitcast i8* %p to i8*
+ %h = getelementptr i8* %g, i64 0
+ call void @objc_release(i8* %g), !clang.imprecise_release !0
+ ret void
+}
+
+
+; Delete retain,release if there's just a use and we do not have a precise
+; release.
+
+; Precise.
+; CHECK: define void @test35a(
+; CHECK: entry:
+; CHECK: call i8* @objc_retain
+; CHECK: true:
+; CHECK: done:
+; CHECK: call void @objc_release
+; CHECK: }
+define void @test35a(i8* %p, i1 %x, i8* %y) {
entry:
%f0 = call i8* @objc_retain(i8* %p)
br i1 %x, label %true, label %done
@@ -1205,16 +1814,36 @@ done:
ret void
}
-; Delete a retain,release if there's no actual use.
-
-; CHECK: define void @test36(
+; Imprecise.
+; CHECK: define void @test35b(
; CHECK-NOT: @objc_
+; CHECK: }
+define void @test35b(i8* %p, i1 %x, i8* %y) {
+entry:
+ %f0 = call i8* @objc_retain(i8* %p)
+ br i1 %x, label %true, label %done
+
+true:
+ %v = icmp eq i8* %p, %y
+ br label %done
+
+done:
+ %g = bitcast i8* %p to i8*
+ %h = getelementptr i8* %g, i64 0
+ call void @objc_release(i8* %g), !clang.imprecise_release !0
+ ret void
+}
+
+; Delete a retain,release if there's no actual use and we have precise release.
+
+; CHECK: define void @test36a(
+; CHECK: @objc_retain
; CHECK: call void @callee()
; CHECK-NOT: @objc_
; CHECK: call void @callee()
-; CHECK-NOT: @objc_
+; CHECK: @objc_release
; CHECK: }
-define void @test36(i8* %p) {
+define void @test36a(i8* %p) {
entry:
call i8* @objc_retain(i8* %p)
call void @callee()
@@ -1225,10 +1854,10 @@ entry:
; Like test36, but with metadata.
-; CHECK: define void @test37(
+; CHECK: define void @test36b(
; CHECK-NOT: @objc_
; CHECK: }
-define void @test37(i8* %p) {
+define void @test36b(i8* %p) {
entry:
call i8* @objc_retain(i8* %p)
call void @callee()
@@ -1439,6 +2068,7 @@ define void @test44(i8** %pp) {
; CHECK: call void @objc_release(i8* %q)
; CHECK: call void @use_pointer(i8* %p)
; CHECK: call void @objc_release(i8* %p)
+; CHECK: }
define void @test45(i8** %pp, i8** %qq) {
%p = load i8** %pp
%q = load i8** %qq
@@ -1455,6 +2085,7 @@ define void @test45(i8** %pp, i8** %qq) {
; CHECK: tail call i8* @objc_retain(i8* %p) [[NUW]]
; CHECK: true:
; CHECK: call i8* @objc_autorelease(i8* %p) [[NUW]]
+; CHECK: }
define void @test46(i8* %p, i1 %a) {
entry:
call i8* @objc_retain(i8* %p)
@@ -1474,6 +2105,7 @@ false:
; CHECK: define i8* @test47(
; CHECK-NOT: call
; CHECK: ret i8* %p
+; CHECK: }
define i8* @test47(i8* %p) nounwind {
%x = call i8* @objc_retainedObject(i8* %p)
ret i8* %x
@@ -1484,6 +2116,7 @@ define i8* @test47(i8* %p) nounwind {
; CHECK: define i8* @test48(
; CHECK-NOT: call
; CHECK: ret i8* %p
+; CHECK: }
define i8* @test48(i8* %p) nounwind {
%x = call i8* @objc_unretainedObject(i8* %p)
ret i8* %x
@@ -1494,32 +2127,51 @@ define i8* @test48(i8* %p) nounwind {
; CHECK: define i8* @test49(
; CHECK-NOT: call
; CHECK: ret i8* %p
+; CHECK: }
define i8* @test49(i8* %p) nounwind {
%x = call i8* @objc_unretainedPointer(i8* %p)
ret i8* %x
}
-; Do delete retain+release with intervening stores of the
-; address value.
+; Do delete retain+release with intervening stores of the address value if we
+; have imprecise release attached to objc_release.
-; CHECK: define void @test50(
+; CHECK: define void @test50a(
+; CHECK-NEXT: call i8* @objc_retain
+; CHECK-NEXT: call void @callee
+; CHECK-NEXT: store
+; CHECK-NEXT: call void @objc_release
+; CHECK-NEXT: ret void
+; CHECK-NEXT: }
+define void @test50a(i8* %p, i8** %pp) {
+ call i8* @objc_retain(i8* %p)
+ call void @callee()
+ store i8* %p, i8** %pp
+ call void @objc_release(i8* %p)
+ ret void
+}
+
+; CHECK: define void @test50b(
; CHECK-NOT: @objc_
; CHECK: }
-define void @test50(i8* %p, i8** %pp) {
+define void @test50b(i8* %p, i8** %pp) {
call i8* @objc_retain(i8* %p)
call void @callee()
store i8* %p, i8** %pp
- call void @objc_release(i8* %p)
+ call void @objc_release(i8* %p), !clang.imprecise_release !0
ret void
}
+
; Don't delete retain+release with intervening stores through the
; address value.
-; CHECK: define void @test51(
+; CHECK: define void @test51a(
; CHECK: call i8* @objc_retain(i8* %p)
; CHECK: call void @objc_release(i8* %p)
-define void @test51(i8* %p) {
+; CHECK: ret void
+; CHECK: }
+define void @test51a(i8* %p) {
call i8* @objc_retain(i8* %p)
call void @callee()
store i8 0, i8* %p
@@ -1527,15 +2179,30 @@ define void @test51(i8* %p) {
ret void
}
+; CHECK: define void @test51b(
+; CHECK: call i8* @objc_retain(i8* %p)
+; CHECK: call void @objc_release(i8* %p)
+; CHECK: ret void
+; CHECK: }
+define void @test51b(i8* %p) {
+ call i8* @objc_retain(i8* %p)
+ call void @callee()
+ store i8 0, i8* %p
+ call void @objc_release(i8* %p), !clang.imprecise_release !0
+ ret void
+}
+
; Don't delete retain+release with intervening use of a pointer of
; unknown provenance.
-; CHECK: define void @test52(
+; CHECK: define void @test52a(
; CHECK: call i8* @objc_retain
; CHECK: call void @callee()
; CHECK: call void @use_pointer(i8* %z)
; CHECK: call void @objc_release
-define void @test52(i8** %zz, i8** %pp) {
+; CHECK: ret void
+; CHECK: }
+define void @test52a(i8** %zz, i8** %pp) {
%p = load i8** %pp
%1 = call i8* @objc_retain(i8* %p)
call void @callee()
@@ -1545,6 +2212,23 @@ define void @test52(i8** %zz, i8** %pp) {
ret void
}
+; CHECK: define void @test52b(
+; CHECK: call i8* @objc_retain
+; CHECK: call void @callee()
+; CHECK: call void @use_pointer(i8* %z)
+; CHECK: call void @objc_release
+; CHECK: ret void
+; CHECK: }
+define void @test52b(i8** %zz, i8** %pp) {
+ %p = load i8** %pp
+ %1 = call i8* @objc_retain(i8* %p)
+ call void @callee()
+ %z = load i8** %zz
+ call void @use_pointer(i8* %z)
+ call void @objc_release(i8* %p), !clang.imprecise_release !0
+ ret void
+}
+
; Like test52, but the pointer has function type, so it's assumed to
; be not reference counted.
; Oops. That's wrong. Clang sometimes uses function types gratuitously.
@@ -1569,6 +2253,7 @@ define void @test53(void ()** %zz, i8** %pp) {
; CHECK: call i8* @returner()
; CHECK-NEXT: call void @objc_release(i8* %t) [[NUW]], !clang.imprecise_release !0
; CHECK-NEXT: ret void
+; CHECK: }
define void @test54() {
%t = call i8* @returner()
call i8* @objc_autorelease(i8* %t)
@@ -1697,19 +2382,78 @@ entry:
@constptr = external constant i8*
@something = external global i8*
-; CHECK: define void @test60(
-; CHECK-NOT: @objc_
+; We have a precise lifetime retain/release here. We can not remove them since
+; @something is not constant.
+
+; CHECK: define void @test60a(
+; CHECK: call i8* @objc_retain
+; CHECK: call void @objc_release
+; CHECK: }
+define void @test60a() {
+ %t = load i8** @constptr
+ %s = load i8** @something
+ call i8* @objc_retain(i8* %s)
+ call void @callee()
+ call void @use_pointer(i8* %t)
+ call void @objc_release(i8* %s)
+ ret void
+}
+
+; CHECK: define void @test60b(
+; CHECK: call i8* @objc_retain
+; CHECK-NOT: call i8* @objc_retain
+; CHECK-NOT: call i8* @objc_rrelease
; CHECK: }
-define void @test60() {
+define void @test60b() {
%t = load i8** @constptr
%s = load i8** @something
call i8* @objc_retain(i8* %s)
+ call i8* @objc_retain(i8* %s)
call void @callee()
call void @use_pointer(i8* %t)
call void @objc_release(i8* %s)
ret void
}
+; CHECK: define void @test60c(
+; CHECK-NOT: @objc_
+; CHECK: }
+define void @test60c() {
+ %t = load i8** @constptr
+ %s = load i8** @something
+ call i8* @objc_retain(i8* %s)
+ call void @callee()
+ call void @use_pointer(i8* %t)
+ call void @objc_release(i8* %s), !clang.imprecise_release !0
+ ret void
+}
+
+; CHECK: define void @test60d(
+; CHECK-NOT: @objc_
+; CHECK: }
+define void @test60d() {
+ %t = load i8** @constptr
+ %s = load i8** @something
+ call i8* @objc_retain(i8* %t)
+ call void @callee()
+ call void @use_pointer(i8* %s)
+ call void @objc_release(i8* %t)
+ ret void
+}
+
+; CHECK: define void @test60e(
+; CHECK-NOT: @objc_
+; CHECK: }
+define void @test60e() {
+ %t = load i8** @constptr
+ %s = load i8** @something
+ call i8* @objc_retain(i8* %t)
+ call void @callee()
+ call void @use_pointer(i8* %s)
+ call void @objc_release(i8* %t), !clang.imprecise_release !0
+ ret void
+}
+
; Constant pointers to objects don't need to be considered related to other
; pointers.
@@ -1876,11 +2620,13 @@ return: ; preds = %if.then, %entry
; An objc_retain can serve as a may-use for a different pointer.
; rdar://11931823
-; CHECK: define void @test66(
-; CHECK: %tmp7 = tail call i8* @objc_retain(i8* %cond) [[NUW]]
+; CHECK: define void @test66a(
+; CHECK: tail call i8* @objc_retain(i8* %cond) [[NUW]]
+; CHECK: tail call void @objc_release(i8* %call) [[NUW]]
+; CHECK: tail call i8* @objc_retain(i8* %tmp8) [[NUW]]
; CHECK: tail call void @objc_release(i8* %cond) [[NUW]]
; CHECK: }
-define void @test66(i8* %tmp5, i8* %bar, i1 %tobool, i1 %tobool1, i8* %call) {
+define void @test66a(i8* %tmp5, i8* %bar, i1 %tobool, i1 %tobool1, i8* %call) {
entry:
br i1 %tobool, label %cond.true, label %cond.end
@@ -1897,7 +2643,74 @@ cond.end: ; preds = %cond.true, %entry
ret void
}
-declare void @bar(i32 ()*)
+; CHECK: define void @test66b(
+; CHECK: tail call i8* @objc_retain(i8* %cond) [[NUW]]
+; CHECK: tail call void @objc_release(i8* %call) [[NUW]]
+; CHECK: tail call i8* @objc_retain(i8* %tmp8) [[NUW]]
+; CHECK: tail call void @objc_release(i8* %cond) [[NUW]]
+; CHECK: }
+define void @test66b(i8* %tmp5, i8* %bar, i1 %tobool, i1 %tobool1, i8* %call) {
+entry:
+ br i1 %tobool, label %cond.true, label %cond.end
+
+cond.true:
+ br label %cond.end
+
+cond.end: ; preds = %cond.true, %entry
+ %cond = phi i8* [ %tmp5, %cond.true ], [ %call, %entry ]
+ %tmp7 = tail call i8* @objc_retain(i8* %cond) nounwind
+ tail call void @objc_release(i8* %call) nounwind, !clang.imprecise_release !0
+ %tmp8 = select i1 %tobool1, i8* %cond, i8* %bar
+ %tmp9 = tail call i8* @objc_retain(i8* %tmp8) nounwind
+ tail call void @objc_release(i8* %cond) nounwind
+ ret void
+}
+
+; CHECK: define void @test66c(
+; CHECK: tail call i8* @objc_retain(i8* %cond) [[NUW]]
+; CHECK: tail call void @objc_release(i8* %call) [[NUW]]
+; CHECK: tail call i8* @objc_retain(i8* %tmp8) [[NUW]]
+; CHECK: tail call void @objc_release(i8* %cond) [[NUW]]
+; CHECK: }
+define void @test66c(i8* %tmp5, i8* %bar, i1 %tobool, i1 %tobool1, i8* %call) {
+entry:
+ br i1 %tobool, label %cond.true, label %cond.end
+
+cond.true:
+ br label %cond.end
+
+cond.end: ; preds = %cond.true, %entry
+ %cond = phi i8* [ %tmp5, %cond.true ], [ %call, %entry ]
+ %tmp7 = tail call i8* @objc_retain(i8* %cond) nounwind
+ tail call void @objc_release(i8* %call) nounwind
+ %tmp8 = select i1 %tobool1, i8* %cond, i8* %bar
+ %tmp9 = tail call i8* @objc_retain(i8* %tmp8) nounwind, !clang.imprecise_release !0
+ tail call void @objc_release(i8* %cond) nounwind
+ ret void
+}
+
+; CHECK: define void @test66d(
+; CHECK: tail call i8* @objc_retain(i8* %cond) [[NUW]]
+; CHECK: tail call void @objc_release(i8* %call) [[NUW]]
+; CHECK: tail call i8* @objc_retain(i8* %tmp8) [[NUW]]
+; CHECK: tail call void @objc_release(i8* %cond) [[NUW]]
+; CHECK: }
+define void @test66d(i8* %tmp5, i8* %bar, i1 %tobool, i1 %tobool1, i8* %call) {
+entry:
+ br i1 %tobool, label %cond.true, label %cond.end
+
+cond.true:
+ br label %cond.end
+
+cond.end: ; preds = %cond.true, %entry
+ %cond = phi i8* [ %tmp5, %cond.true ], [ %call, %entry ]
+ %tmp7 = tail call i8* @objc_retain(i8* %cond) nounwind
+ tail call void @objc_release(i8* %call) nounwind, !clang.imprecise_release !0
+ %tmp8 = select i1 %tobool1, i8* %cond, i8* %bar
+ %tmp9 = tail call i8* @objc_retain(i8* %tmp8) nounwind
+ tail call void @objc_release(i8* %cond) nounwind, !clang.imprecise_release !0
+ ret void
+}
; A few real-world testcases.
@@ -1907,7 +2720,7 @@ declare i32 @printf(i8* nocapture, ...) nounwind
declare i32 @puts(i8* nocapture) nounwind
@str = internal constant [16 x i8] c"-[ Top0 _getX ]\00"
-; CHECK: @"\01-[A z]"
+; CHECK: define { <2 x float>, <2 x float> } @"\01-[A z]"({}* %self, i8* nocapture %_cmd) [[NUW]] {
; CHECK-NOT: @objc_
; CHECK: }
@@ -1953,7 +2766,7 @@ invoke.cont:
ret {<2 x float>, <2 x float>} %tmp35
}
-; CHECK: @"\01-[Top0 _getX]"
+; CHECK: @"\01-[Top0 _getX]"({}* %self, i8* nocapture %_cmd) [[NUW]] {
; CHECK-NOT: @objc_
; CHECK: }
@@ -1972,12 +2785,13 @@ invoke.cont:
; A simple loop. Eliminate the retain and release inside of it!
-; CHECK: define void @loop
+; CHECK: define void @loop(i8* %x, i64 %n) {
; CHECK: for.body:
; CHECK-NOT: @objc_
; CHECK: @objc_msgSend
; CHECK-NOT: @objc_
; CHECK: for.end:
+; CHECK: }
define void @loop(i8* %x, i64 %n) {
entry:
%0 = tail call i8* @objc_retain(i8* %x) nounwind
@@ -2001,7 +2815,7 @@ for.end: ; preds = %for.body, %entry
; ObjCARCOpt can delete the retain,release on self.
-; CHECK: define void @TextEditTest
+; CHECK: define void @TextEditTest(%2* %self, %3* %pboard) {
; CHECK-NOT: call i8* @objc_retain(i8* %tmp7)
; CHECK: }
diff --git a/test/Transforms/ObjCARC/cfg-hazards.ll b/test/Transforms/ObjCARC/cfg-hazards.ll
index 899298b..0156d5b 100644
--- a/test/Transforms/ObjCARC/cfg-hazards.ll
+++ b/test/Transforms/ObjCARC/cfg-hazards.ll
@@ -8,6 +8,7 @@ declare void @use_pointer(i8*)
declare i8* @objc_retain(i8*)
declare void @objc_release(i8*)
declare void @callee()
+declare void @block_callee(void ()*)
; CHECK: define void @test0(
; CHECK: call i8* @objc_retain(
@@ -394,6 +395,41 @@ exit:
ret void
}
+; Do not improperly pair retains in a for loop with releases outside of a for
+; loop when the proper pairing is disguised by a separate provenance represented
+; by an alloca.
+; rdar://12969722
+
+; CHECK: define void @test13(i8* %a) [[NUW]] {
+; CHECK: entry:
+; CHECK: tail call i8* @objc_retain(i8* %a) [[NUW]]
+; CHECK: loop:
+; CHECK: tail call i8* @objc_retain(i8* %a) [[NUW]]
+; CHECK: call void @block_callee
+; CHECK: call void @objc_release(i8* %reloaded_a) [[NUW]]
+; CHECK: exit:
+; CHECK: call void @objc_release(i8* %a) [[NUW]]
+; CHECK: }
+define void @test13(i8* %a) nounwind {
+entry:
+ %block = alloca i8*
+ %a1 = tail call i8* @objc_retain(i8* %a) nounwind
+ br label %loop
+
+loop:
+ %a2 = tail call i8* @objc_retain(i8* %a) nounwind
+ store i8* %a, i8** %block, align 8
+ %casted_block = bitcast i8** %block to void ()*
+ call void @block_callee(void ()* %casted_block)
+ %reloaded_a = load i8** %block, align 8
+ call void @objc_release(i8* %reloaded_a) nounwind, !clang.imprecise_release !0
+ br i1 undef, label %loop, label %exit
+
+exit:
+ call void @objc_release(i8* %a) nounwind, !clang.imprecise_release !0
+ ret void
+}
+
; CHECK: attributes [[NUW]] = { nounwind }
!0 = metadata !{}
diff --git a/test/Transforms/ObjCARC/contract-marker.ll b/test/Transforms/ObjCARC/contract-marker.ll
index 01fd1e7..55a1b28 100644
--- a/test/Transforms/ObjCARC/contract-marker.ll
+++ b/test/Transforms/ObjCARC/contract-marker.ll
@@ -1,9 +1,11 @@
; RUN: opt -S -objc-arc-contract < %s | FileCheck %s
+; CHECK: define void @foo() {
; CHECK: %call = tail call i32* @qux()
; CHECK-NEXT: %tcall = bitcast i32* %call to i8*
; CHECK-NEXT: call void asm sideeffect "mov\09r7, r7\09\09@ marker for objc_retainAutoreleaseReturnValue", ""()
; CHECK-NEXT: %0 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %tcall) [[NUW:#[0-9]+]]
+; CHECK: }
define void @foo() {
entry:
diff --git a/test/Transforms/ObjCARC/contract-storestrong.ll b/test/Transforms/ObjCARC/contract-storestrong.ll
index 6999237..023604e 100644
--- a/test/Transforms/ObjCARC/contract-storestrong.ll
+++ b/test/Transforms/ObjCARC/contract-storestrong.ll
@@ -12,6 +12,7 @@ declare void @use_pointer(i8*)
; CHECK: entry:
; CHECK-NEXT: tail call void @objc_storeStrong(i8** @x, i8* %p) [[NUW:#[0-9]+]]
; CHECK-NEXT: ret void
+; CHECK-NEXT: }
define void @test0(i8* %p) {
entry:
%0 = tail call i8* @objc_retain(i8* %p) nounwind
@@ -107,6 +108,7 @@ entry:
; CHECK: define i1 @test5(i8* %newValue, i8* %foo) {
; CHECK: %t = icmp eq i8* %x1, %foo
; CHECK: tail call void @objc_storeStrong(i8** @x, i8* %newValue) [[NUW]]
+; CHECK: }
define i1 @test5(i8* %newValue, i8* %foo) {
entry:
%x0 = tail call i8* @objc_retain(i8* %newValue) nounwind
@@ -122,6 +124,7 @@ entry:
; CHECK: define i1 @test6(i8* %newValue, i8* %foo) {
; CHECK: %t = icmp eq i8* %x1, %foo
; CHECK: tail call void @objc_storeStrong(i8** @x, i8* %newValue) [[NUW]]
+; CHECK: }
define i1 @test6(i8* %newValue, i8* %foo) {
entry:
%x0 = tail call i8* @objc_retain(i8* %newValue) nounwind
diff --git a/test/Transforms/ObjCARC/contract-testcases.ll b/test/Transforms/ObjCARC/contract-testcases.ll
index 85b03be..fc023f8 100644
--- a/test/Transforms/ObjCARC/contract-testcases.ll
+++ b/test/Transforms/ObjCARC/contract-testcases.ll
@@ -50,6 +50,7 @@ bb6: ; preds = %bb5, %bb4, %bb4, %b
; CHECK: br i1 undef, label %bb7, label %bb7
; CHECK: bb7:
; CHECK: %tmp8 = phi %0* [ %0, %bb ], [ %0, %bb ]
+; CHECK: }
define void @test1() {
bb:
%tmp = tail call %0* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to %0* ()*)()
@@ -70,6 +71,7 @@ bb7: ; preds = %bb6, %bb6, %bb5
; CHECK: invoke.cont: ; preds = %entry
; CHECK-NEXT: call void asm sideeffect "mov\09r7, r7\09\09@ marker for objc_retainAutoreleaseReturnValue", ""()
; CHECK-NEXT: %tmp = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %call) [[NUW:#[0-9]+]]
+; CHECK: }
define void @_Z6doTestP8NSString() {
entry:
%call = invoke i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* ()*)()
diff --git a/test/Transforms/ObjCARC/contract.ll b/test/Transforms/ObjCARC/contract.ll
index 0b60683..3544f88 100644
--- a/test/Transforms/ObjCARC/contract.ll
+++ b/test/Transforms/ObjCARC/contract.ll
@@ -10,6 +10,7 @@ declare i8* @objc_retainAutoreleasedReturnValue(i8*)
declare void @use_pointer(i8*)
declare i8* @returner()
+declare void @callee()
; CHECK: define void @test0
; CHECK: call void @use_pointer(i8* %0)
@@ -137,6 +138,7 @@ define i8* @test6() {
; CHECK: call void @use_pointer(i8* %1)
; CHECK: tail call i8* @objc_autoreleaseReturnValue(i8* %1)
; CHECK: ret i8* %2
+; CHECK-NEXT: }
define i8* @test7(i8* %p) {
%1 = tail call i8* @objc_retain(i8* %p)
call void @use_pointer(i8* %p)
@@ -171,6 +173,60 @@ define void @test9(i8* %a, i8* %b) {
ret void
}
+
+; Turn objc_retain into objc_retainAutoreleasedReturnValue if its operand
+; is a return value.
+
+; CHECK: define void @test10()
+; CHECK: tail call i8* @objc_retainAutoreleasedReturnValue(i8* %p)
+define void @test10() {
+ %p = call i8* @returner()
+ tail call i8* @objc_retain(i8* %p) nounwind
+ ret void
+}
+
+; Convert objc_retain to objc_retainAutoreleasedReturnValue if its
+; argument is a return value.
+
+; CHECK: define void @test11(
+; CHECK-NEXT: %y = call i8* @returner()
+; CHECK-NEXT: tail call i8* @objc_retainAutoreleasedReturnValue(i8* %y) [[NUW]]
+; CHECK-NEXT: ret void
+define void @test11() {
+ %y = call i8* @returner()
+ tail call i8* @objc_retain(i8* %y) nounwind
+ ret void
+}
+
+; Don't convert objc_retain to objc_retainAutoreleasedReturnValue if its
+; argument is not a return value.
+
+; CHECK: define void @test12(
+; CHECK-NEXT: tail call i8* @objc_retain(i8* %y) [[NUW]]
+; CHECK-NEXT: ret void
+; CHECK-NEXT: }
+define void @test12(i8* %y) {
+ tail call i8* @objc_retain(i8* %y) nounwind
+ ret void
+}
+
+; Don't Convert objc_retain to objc_retainAutoreleasedReturnValue if it
+; isn't next to the call providing its return value.
+
+; CHECK: define void @test13(
+; CHECK-NEXT: %y = call i8* @returner()
+; CHECK-NEXT: call void @callee()
+; CHECK-NEXT: tail call i8* @objc_retain(i8* %y) [[NUW]]
+; CHECK-NEXT: ret void
+; CHECK-NEXT: }
+define void @test13() {
+ %y = call i8* @returner()
+ call void @callee()
+ tail call i8* @objc_retain(i8* %y) nounwind
+ ret void
+}
+
+
declare void @clang.arc.use(...) nounwind
; CHECK: attributes [[NUW]] = { nounwind }
diff --git a/test/Transforms/ObjCARC/expand.ll b/test/Transforms/ObjCARC/expand.ll
index 5388673..fe47ee5 100644
--- a/test/Transforms/ObjCARC/expand.ll
+++ b/test/Transforms/ObjCARC/expand.ll
@@ -4,25 +4,91 @@ target datalayout = "e-p:64:64:64"
declare i8* @objc_retain(i8*)
declare i8* @objc_autorelease(i8*)
+declare i8* @objc_retainAutoreleasedReturnValue(i8*)
+declare i8* @objc_autoreleaseReturnValue(i8*)
+declare i8* @objc_retainAutorelease(i8*)
+declare i8* @objc_retainAutoreleaseReturnValue(i8*)
+declare i8* @objc_retainBlock(i8*)
declare void @use_pointer(i8*)
-; CHECK: define void @test0
+; CHECK: define void @test_retain(i8* %x) [[NUW:#[0-9]+]] {
+; CHECK: call i8* @objc_retain(i8* %x)
; CHECK: call void @use_pointer(i8* %x)
; CHECK: }
-define void @test0(i8* %x) nounwind {
+define void @test_retain(i8* %x) nounwind {
entry:
%0 = call i8* @objc_retain(i8* %x) nounwind
call void @use_pointer(i8* %0)
ret void
}
-; CHECK: define void @test1
+; CHECK: define void @test_retainAutoreleasedReturnValue(i8* %x) [[NUW]] {
+; CHECK: call i8* @objc_retainAutoreleasedReturnValue(i8* %x)
; CHECK: call void @use_pointer(i8* %x)
; CHECK: }
-define void @test1(i8* %x) nounwind {
+define void @test_retainAutoreleasedReturnValue(i8* %x) nounwind {
+entry:
+ %0 = call i8* @objc_retainAutoreleasedReturnValue(i8* %x) nounwind
+ call void @use_pointer(i8* %0)
+ ret void
+}
+
+; CHECK: define void @test_retainAutorelease(i8* %x) [[NUW]] {
+; CHECK: call i8* @objc_retainAutorelease(i8* %x)
+; CHECK: call void @use_pointer(i8* %x)
+; CHECK: }
+define void @test_retainAutorelease(i8* %x) nounwind {
+entry:
+ %0 = call i8* @objc_retainAutorelease(i8* %x) nounwind
+ call void @use_pointer(i8* %0)
+ ret void
+}
+
+; CHECK: define void @test_retainAutoreleaseReturnValue(i8* %x) [[NUW]] {
+; CHECK: call i8* @objc_retainAutoreleaseReturnValue(i8* %x)
+; CHECK: call void @use_pointer(i8* %x)
+; CHECK: }
+define void @test_retainAutoreleaseReturnValue(i8* %x) nounwind {
+entry:
+ %0 = call i8* @objc_retainAutoreleaseReturnValue(i8* %x) nounwind
+ call void @use_pointer(i8* %0)
+ ret void
+}
+
+; CHECK: define void @test_autorelease(i8* %x) [[NUW]] {
+; CHECK: call i8* @objc_autorelease(i8* %x)
+; CHECK: call void @use_pointer(i8* %x)
+; CHECK: }
+define void @test_autorelease(i8* %x) nounwind {
entry:
%0 = call i8* @objc_autorelease(i8* %x) nounwind
- call void @use_pointer(i8* %x)
+ call void @use_pointer(i8* %0)
+ ret void
+}
+
+; CHECK: define void @test_autoreleaseReturnValue(i8* %x) [[NUW]] {
+; CHECK: call i8* @objc_autoreleaseReturnValue(i8* %x)
+; CHECK: call void @use_pointer(i8* %x)
+; CHECK: }
+define void @test_autoreleaseReturnValue(i8* %x) nounwind {
+entry:
+ %0 = call i8* @objc_autoreleaseReturnValue(i8* %x) nounwind
+ call void @use_pointer(i8* %0)
+ ret void
+}
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; RetainBlock is not strictly forwarding. Do not touch it. ;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; CHECK: define void @test_retainBlock(i8* %x) [[NUW]] {
+; CHECK: call i8* @objc_retainBlock(i8* %x)
+; CHECK: call void @use_pointer(i8* %0)
+; CHECK: }
+define void @test_retainBlock(i8* %x) nounwind {
+entry:
+ %0 = call i8* @objc_retainBlock(i8* %x) nounwind
+ call void @use_pointer(i8* %0)
ret void
}
diff --git a/test/Transforms/ObjCARC/gvn.ll b/test/Transforms/ObjCARC/gvn.ll
index 3648866..a828b54 100644
--- a/test/Transforms/ObjCARC/gvn.ll
+++ b/test/Transforms/ObjCARC/gvn.ll
@@ -7,11 +7,12 @@ declare i8* @objc_retain(i8*)
; GVN should be able to eliminate this redundant load, with ARC-specific
; alias analysis.
-; CHECK: @foo
+; CHECK: define i8* @foo(i32 %n)
; CHECK-NEXT: entry:
; CHECK-NEXT: %s = load i8** @x
; CHECK-NOT: load
; CHECK: ret i8* %s
+; CHECK-NEXT: }
define i8* @foo(i32 %n) nounwind {
entry:
%s = load i8** @x
diff --git a/test/Transforms/ObjCARC/clang-arc-used-intrinsic-removed-if-isolated.ll b/test/Transforms/ObjCARC/intrinsic-use-isolated.ll
index 4215b5c..4215b5c 100644
--- a/test/Transforms/ObjCARC/clang-arc-used-intrinsic-removed-if-isolated.ll
+++ b/test/Transforms/ObjCARC/intrinsic-use-isolated.ll
diff --git a/test/Transforms/ObjCARC/intrinsic-use.ll b/test/Transforms/ObjCARC/intrinsic-use.ll
index 9c7b81a..60370c1 100644
--- a/test/Transforms/ObjCARC/intrinsic-use.ll
+++ b/test/Transforms/ObjCARC/intrinsic-use.ll
@@ -34,8 +34,11 @@ declare void @test0_helper(i8*, i8**)
; CHECK-NEXT: @objc_release(i8* [[VAL1]])
; CHECK-NEXT: @objc_autorelease(i8* %x)
; CHECK-NEXT: store i8* %x, i8** %out
+; CHECK-NEXT: @objc_retain(i8* %x)
; CHECK-NEXT: @objc_release(i8* [[VAL2]])
+; CHECK-NEXT: @objc_release(i8* %x)
; CHECK-NEXT: ret void
+; CHECK-NEXT: }
define void @test0(i8** %out, i8* %x, i8* %y) {
entry:
%temp0 = alloca i8*, align 8
@@ -61,3 +64,53 @@ entry:
call void @objc_release(i8* %x) nounwind
ret void
}
+
+; CHECK: define void @test0a(
+; 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: @objc_release(i8* [[VAL2]])
+; CHECK-NEXT: store i8* %x, i8** %out
+; CHECK-NEXT: ret void
+; CHECK-NEXT: }
+define void @test0a(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, !clang.imprecise_release !0
+ 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, !clang.imprecise_release !0
+ %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, !clang.imprecise_release !0
+ call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
+ ret void
+}
+
+
+!0 = metadata !{}
+
diff --git a/test/Transforms/ObjCARC/invoke.ll b/test/Transforms/ObjCARC/invoke.ll
index f528b4a..9510f2e 100644
--- a/test/Transforms/ObjCARC/invoke.ll
+++ b/test/Transforms/ObjCARC/invoke.ll
@@ -17,6 +17,7 @@ declare i8* @returner()
; CHECK: lpad:
; CHECK: call void @objc_release(i8* %zipFile) [[NUW]], !clang.imprecise_release !0
; CHECK: ret void
+; CHECK-NEXT: }
define void @test0(i8* %zipFile) {
entry:
call i8* @objc_retain(i8* %zipFile) nounwind
@@ -48,6 +49,7 @@ lpad: ; preds = %entry
; CHECK: br label %done
; CHECK: done:
; CHECK-NEXT: ret void
+; CHECK-NEXT: }
define void @test1(i8* %zipFile) {
entry:
call i8* @objc_retain(i8* %zipFile) nounwind
@@ -110,6 +112,7 @@ finally.rethrow: ; preds = %invoke.cont, %entry
; CHECK: if.end:
; CHECK-NEXT: call void @objc_release(i8* %p) [[NUW]]
; CHECK-NEXT: ret void
+; CHECK-NEXT: }
define void @test3(i8* %p, i1 %b) {
entry:
%0 = call i8* @objc_retain(i8* %p)
@@ -145,6 +148,7 @@ if.end:
; CHECK: if.end:
; CHECK-NEXT: call void @objc_release(i8* %p) [[NUW]]
; CHECK-NEXT: ret void
+; CHECK-NEXT: }
define void @test4(i8* %p, i1 %b) {
entry:
%0 = call i8* @objc_retain(i8* %p)
diff --git a/test/Transforms/ObjCARC/move-and-merge-autorelease.ll b/test/Transforms/ObjCARC/move-and-merge-autorelease.ll
index 8462c70..e5d2f07 100644
--- a/test/Transforms/ObjCARC/move-and-merge-autorelease.ll
+++ b/test/Transforms/ObjCARC/move-and-merge-autorelease.ll
@@ -1,4 +1,4 @@
-; RUN: opt -S -objc-arc < %s | FileCheck %s
+; RUN: opt -S -objc-arc -objc-arc-contract < %s | FileCheck %s
; The optimizer should be able to move the autorelease past two phi nodes
; and fold it with the release in bb65.
diff --git a/test/Transforms/ObjCARC/retain-block-escape-analysis.ll b/test/Transforms/ObjCARC/retain-block-escape-analysis.ll
index 2c1ddce..8df05ad 100644
--- a/test/Transforms/ObjCARC/retain-block-escape-analysis.ll
+++ b/test/Transforms/ObjCARC/retain-block-escape-analysis.ll
@@ -23,6 +23,23 @@ define void @bitcasttest(i8* %storage, void (...)* %block) {
; CHECK: define void @bitcasttest
entry:
%t1 = bitcast void (...)* %block to i8*
+; CHECK: 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: call void @objc_release
+ call void @objc_release(i8* %t1)
+ ret void
+; CHECK: }
+}
+
+define void @bitcasttest_a(i8* %storage, void (...)* %block) {
+; CHECK: define void @bitcasttest_a
+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
@@ -31,14 +48,34 @@ entry:
%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)
+ call void @objc_release(i8* %t1), !clang.imprecise_release !0
ret void
+; CHECK: }
}
define void @geptest(void (...)** %storage_array, void (...)* %block) {
; CHECK: define void @geptest
entry:
%t1 = bitcast void (...)* %block to i8*
+; CHECK: 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: call void @objc_release
+ call void @objc_release(i8* %t1)
+ ret void
+; CHECK: }
+}
+
+define void @geptest_a(void (...)** %storage_array, void (...)* %block) {
+; CHECK: define void @geptest_a
+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
@@ -49,8 +86,9 @@ entry:
store void (...)* %t4, void (...)** %storage, align 8
; CHECK-NOT: call void @objc_release
- call void @objc_release(i8* %t1)
+ call void @objc_release(i8* %t1), !clang.imprecise_release !0
ret void
+; CHECK: }
}
define void @selecttest(void (...)** %store1, void (...)** %store2,
@@ -58,6 +96,24 @@ define void @selecttest(void (...)** %store1, void (...)** %store2,
; CHECK: define void @selecttest
entry:
%t1 = bitcast void (...)* %block to i8*
+; CHECK: 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: call void @objc_release
+ call void @objc_release(i8* %t1)
+ ret void
+; CHECK: }
+}
+
+define void @selecttest_a(void (...)** %store1, void (...)** %store2,
+ void (...)* %block) {
+; CHECK: define void @selecttest_a
+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
@@ -66,8 +122,9 @@ entry:
%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)
+ call void @objc_release(i8* %t1), !clang.imprecise_release !0
ret void
+; CHECK: }
}
define void @phinodetest(void (...)** %storage1,
@@ -76,6 +133,36 @@ define void @phinodetest(void (...)** %storage1,
; CHECK: define void @phinodetest
entry:
%t1 = bitcast void (...)* %block to i8*
+; CHECK: 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
+; CHECK: store1_set:
+
+store1_set:
+ br label %end
+
+store2_set:
+ br label %end
+
+end:
+; CHECK: end:
+ %storage = phi void (...)** [ %storage1, %store1_set ], [ %storage2, %store2_set]
+ store void (...)* %t4, void (...)** %storage, align 8
+; CHECK: call void @objc_release
+ call void @objc_release(i8* %t1)
+ ret void
+; CHECK: }
+}
+
+define void @phinodetest_a(void (...)** %storage1,
+ void (...)** %storage2,
+ void (...)* %block) {
+; CHECK: define void @phinodetest_a
+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
@@ -93,10 +180,11 @@ 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)
+ call void @objc_release(i8* %t1), !clang.imprecise_release !0
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 ;
diff --git a/test/Transforms/ObjCARC/rv.ll b/test/Transforms/ObjCARC/rv.ll
index 589c60f..e857c9f 100644
--- a/test/Transforms/ObjCARC/rv.ll
+++ b/test/Transforms/ObjCARC/rv.ll
@@ -136,17 +136,6 @@ define i8* @test7b() {
ret i8* %p
}
-; Turn objc_retain into objc_retainAutoreleasedReturnValue if its operand
-; is a return value.
-
-; CHECK: define void @test8()
-; CHECK: tail call i8* @objc_retainAutoreleasedReturnValue(i8* %p)
-define void @test8() {
- %p = call i8* @returner()
- call i8* @objc_retain(i8* %p)
- ret void
-}
-
; Don't apply the RV optimization to autorelease if there's no retain.
; CHECK: define i8* @test9(i8* %p)
@@ -235,45 +224,6 @@ define void @test15() {
ret void
}
-; Convert objc_retain to objc_retainAutoreleasedReturnValue if its
-; argument is a return value.
-
-; CHECK: define void @test16(
-; CHECK-NEXT: %y = call i8* @returner()
-; CHECK-NEXT: tail call i8* @objc_retainAutoreleasedReturnValue(i8* %y) [[NUW]]
-; CHECK-NEXT: ret void
-define void @test16() {
- %y = call i8* @returner()
- call i8* @objc_retain(i8* %y)
- ret void
-}
-
-; Don't convert objc_retain to objc_retainAutoreleasedReturnValue if its
-; argument is not a return value.
-
-; CHECK: define void @test17(
-; CHECK-NEXT: tail call i8* @objc_retain(i8* %y) [[NUW]]
-; CHECK-NEXT: ret void
-define void @test17(i8* %y) {
- call i8* @objc_retain(i8* %y)
- ret void
-}
-
-; Don't Convert objc_retain to objc_retainAutoreleasedReturnValue if it
-; isn't next to the call providing its return value.
-
-; CHECK: define void @test18(
-; CHECK-NEXT: %y = call i8* @returner()
-; CHECK-NEXT: call void @callee()
-; CHECK-NEXT: tail call i8* @objc_retain(i8* %y) [[NUW]]
-; CHECK-NEXT: ret void
-define void @test18() {
- %y = call i8* @returner()
- call void @callee()
- call i8* @objc_retain(i8* %y)
- ret void
-}
-
; Delete autoreleaseRV+retainRV pairs.
; CHECK: define i8* @test19(i8* %p) {
diff --git a/test/Transforms/ObjCARC/tail-call-invariant-enforcement.ll b/test/Transforms/ObjCARC/tail-call-invariant-enforcement.ll
index 26cd677..1ec61c8 100644
--- a/test/Transforms/ObjCARC/tail-call-invariant-enforcement.ll
+++ b/test/Transforms/ObjCARC/tail-call-invariant-enforcement.ll
@@ -1,74 +1,89 @@
; RUN: opt -objc-arc -S < %s | FileCheck %s
-declare i8* @objc_release(i8* %x)
+declare void @objc_release(i8* %x)
declare i8* @objc_retain(i8* %x)
declare i8* @objc_autorelease(i8* %x)
declare i8* @objc_autoreleaseReturnValue(i8* %x)
declare i8* @objc_retainAutoreleasedReturnValue(i8* %x)
+declare i8* @tmp(i8*)
; Never tail call objc_autorelease.
-define i8* @test0(i8* %x) {
+
+; CHECK: define i8* @test0(i8* %x) [[NUW:#[0-9]+]] {
+; CHECK: %tmp0 = call i8* @objc_autorelease(i8* %x) [[NUW]]
+; CHECK: %tmp1 = call i8* @objc_autorelease(i8* %x) [[NUW]]
+; CHECK: }
+define i8* @test0(i8* %x) nounwind {
entry:
- ; CHECK: %tmp0 = call i8* @objc_autorelease(i8* %x)
%tmp0 = call i8* @objc_autorelease(i8* %x)
- ; CHECK: %tmp1 = call i8* @objc_autorelease(i8* %x)
%tmp1 = tail call i8* @objc_autorelease(i8* %x)
ret i8* %x
}
; Always tail call autoreleaseReturnValue.
-define i8* @test1(i8* %x) {
+
+; CHECK: define i8* @test1(i8* %x) [[NUW]] {
+; CHECK: %tmp0 = tail call i8* @objc_autoreleaseReturnValue(i8* %x) [[NUW]]
+; CHECK: %tmp1 = tail call i8* @objc_autoreleaseReturnValue(i8* %x) [[NUW]]
+; CHECK: }
+define i8* @test1(i8* %x) nounwind {
entry:
- ; CHECK: %tmp0 = tail call i8* @objc_autoreleaseReturnValue(i8* %x)
%tmp0 = call i8* @objc_autoreleaseReturnValue(i8* %x)
- ; CHECK: %tmp1 = tail call i8* @objc_autoreleaseReturnValue(i8* %x)
%tmp1 = tail call i8* @objc_autoreleaseReturnValue(i8* %x)
ret i8* %x
}
; Always tail call objc_retain.
-define i8* @test2(i8* %x) {
+
+; CHECK: define i8* @test2(i8* %x) [[NUW]] {
+; CHECK: %tmp0 = tail call i8* @objc_retain(i8* %x) [[NUW]]
+; CHECK: %tmp1 = tail call i8* @objc_retain(i8* %x) [[NUW]]
+; CHECK: }
+define i8* @test2(i8* %x) nounwind {
entry:
- ; CHECK: %tmp0 = tail call i8* @objc_retain(i8* %x)
%tmp0 = call i8* @objc_retain(i8* %x)
- ; CHECK: %tmp1 = tail call i8* @objc_retain(i8* %x)
%tmp1 = tail call i8* @objc_retain(i8* %x)
ret i8* %x
}
-define i8* @tmp(i8* %x) {
- ret i8* %x
-}
-
; Always tail call objc_retainAutoreleasedReturnValue.
-define i8* @test3(i8* %x) {
+; CHECK: define i8* @test3(i8* %x) [[NUW]] {
+; CHECK: %tmp0 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %y) [[NUW]]
+; CHECK: %tmp1 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %z) [[NUW]]
+; CHECK: }
+define i8* @test3(i8* %x) nounwind {
entry:
%y = call i8* @tmp(i8* %x)
- ; CHECK: %tmp0 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %y)
%tmp0 = call i8* @objc_retainAutoreleasedReturnValue(i8* %y)
%z = call i8* @tmp(i8* %x)
- ; CHECK: %tmp1 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %z)
%tmp1 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %z)
ret i8* %x
}
; By itself, we should never change whether or not objc_release is tail called.
-define i8* @test4(i8* %x) {
+
+; CHECK: define void @test4(i8* %x) [[NUW]] {
+; CHECK: call void @objc_release(i8* %x) [[NUW]]
+; CHECK: tail call void @objc_release(i8* %x) [[NUW]]
+; CHECK: }
+define void @test4(i8* %x) nounwind {
entry:
- ; CHECK: %tmp0 = call i8* @objc_release(i8* %x)
- %tmp0 = call i8* @objc_release(i8* %x)
- ; CHECK: %tmp1 = tail call i8* @objc_release(i8* %x)
- %tmp1 = tail call i8* @objc_release(i8* %x)
- ret i8* %x
+ call void @objc_release(i8* %x)
+ tail call void @objc_release(i8* %x)
+ ret void
}
; If we convert a tail called @objc_autoreleaseReturnValue to an
; @objc_autorelease, ensure that the tail call is removed.
-define i8* @test5(i8* %x) {
+; CHECK: define i8* @test5(i8* %x) [[NUW]] {
+; CHECK: %tmp0 = call i8* @objc_autorelease(i8* %x) [[NUW]]
+; CHECK: }
+define i8* @test5(i8* %x) nounwind {
entry:
- ; CHECK: %tmp0 = call i8* @objc_autorelease(i8* %x)
%tmp0 = tail call i8* @objc_autoreleaseReturnValue(i8* %x)
ret i8* %tmp0
}
+; CHECK: attributes [[NUW]] = { nounwind }
+
OpenPOWER on IntegriCloud