1; RUN: opt -objc-arc -S < %s | FileCheck %s
2
3declare void @objc_release(i8* %x)
4declare i8* @objc_retain(i8* %x)
5declare i8* @objc_autorelease(i8* %x)
6declare i8* @objc_autoreleaseReturnValue(i8* %x)
7declare i8* @objc_retainAutoreleasedReturnValue(i8* %x)
8declare i8* @tmp(i8*)
9
10; Never tail call objc_autorelease.
11
12; CHECK: define i8* @test0(i8* %x) [[NUW:#[0-9]+]] {
13; CHECK: %tmp0 = call i8* @objc_autorelease(i8* %x) [[NUW]]
14; CHECK: %tmp1 = call i8* @objc_autorelease(i8* %x) [[NUW]]
15; CHECK: }
16define i8* @test0(i8* %x) nounwind {
17entry:
18  %tmp0 = call i8* @objc_autorelease(i8* %x)
19  %tmp1 = tail call i8* @objc_autorelease(i8* %x)
20
21  ret i8* %x
22}
23
24; Always tail call autoreleaseReturnValue.
25
26; CHECK: define i8* @test1(i8* %x) [[NUW]] {
27; CHECK: %tmp0 = tail call i8* @objc_autoreleaseReturnValue(i8* %x) [[NUW]]
28; CHECK: %tmp1 = tail call i8* @objc_autoreleaseReturnValue(i8* %x) [[NUW]]
29; CHECK: }
30define i8* @test1(i8* %x) nounwind {
31entry:
32  %tmp0 = call i8* @objc_autoreleaseReturnValue(i8* %x)
33  %tmp1 = tail call i8* @objc_autoreleaseReturnValue(i8* %x)
34  ret i8* %x
35}
36
37; Always tail call objc_retain.
38
39; CHECK: define i8* @test2(i8* %x) [[NUW]] {
40; CHECK: %tmp0 = tail call i8* @objc_retain(i8* %x) [[NUW]]
41; CHECK: %tmp1 = tail call i8* @objc_retain(i8* %x) [[NUW]]
42; CHECK: }
43define i8* @test2(i8* %x) nounwind {
44entry:
45  %tmp0 = call i8* @objc_retain(i8* %x)
46  %tmp1 = tail call i8* @objc_retain(i8* %x)
47  ret i8* %x
48}
49
50; Always tail call objc_retainAutoreleasedReturnValue.
51; CHECK: define i8* @test3(i8* %x) [[NUW]] {
52; CHECK: %tmp0 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %y) [[NUW]]
53; CHECK: %tmp1 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %z) [[NUW]]
54; CHECK: }
55define i8* @test3(i8* %x) nounwind {
56entry:
57  %y = call i8* @tmp(i8* %x)
58  %tmp0 = call i8* @objc_retainAutoreleasedReturnValue(i8* %y)
59  %z = call i8* @tmp(i8* %x)
60  %tmp1 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %z)
61  ret i8* %x
62}
63
64; By itself, we should never change whether or not objc_release is tail called.
65
66; CHECK: define void @test4(i8* %x) [[NUW]] {
67; CHECK: call void @objc_release(i8* %x) [[NUW]]
68; CHECK: tail call void @objc_release(i8* %x) [[NUW]]
69; CHECK: }
70define void @test4(i8* %x) nounwind {
71entry:
72  call void @objc_release(i8* %x)
73  tail call void @objc_release(i8* %x)
74  ret void
75}
76
77; If we convert a tail called @objc_autoreleaseReturnValue to an
78; @objc_autorelease, ensure that the tail call is removed.
79; CHECK: define i8* @test5(i8* %x) [[NUW]] {
80; CHECK: %tmp0 = call i8* @objc_autorelease(i8* %x) [[NUW]]
81; CHECK: }
82define i8* @test5(i8* %x) nounwind {
83entry:
84  %tmp0 = tail call i8* @objc_autoreleaseReturnValue(i8* %x)
85  ret i8* %tmp0
86}
87
88; CHECK: attributes [[NUW]] = { nounwind }
89
90