1; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs,inline)' -S | FileCheck %s
2; This test runs the inliner and the function attribute deduction. It ensures
3; that when the inliner mutates the call graph it correctly updates the CGSCC
4; iteration so that we can compute refined function attributes. In this way it
5; is leveraging function attribute computation to observe correct call graph
6; updates.
7
8; Boring unknown external function call.
9; CHECK: declare void @unknown()
10declare void @unknown()
11
12; Sanity check: this should get annotated as readnone.
13; CHECK: Function Attrs: nounwind readnone
14; CHECK-NEXT: declare void @readnone()
15declare void @readnone() readnone nounwind
16
17; The 'test1_' prefixed functions are designed to trigger forming a new direct
18; call in the inlined body of the function. After that, we form a new SCC and
19; using that can deduce precise function attrs.
20
21; This function should no longer exist.
22; CHECK-NOT: @test1_f()
23define internal void @test1_f(void()* %p) {
24entry:
25  call void %p()
26  ret void
27}
28
29; This function should have had 'readnone' deduced for its SCC.
30; CHECK: Function Attrs: noinline nounwind readnone
31; CHECK-NEXT: define void @test1_g()
32define void @test1_g() noinline {
33entry:
34  call void @test1_f(void()* @test1_h)
35  ret void
36}
37
38; This function should have had 'readnone' deduced for its SCC.
39; CHECK: Function Attrs: noinline nounwind readnone
40; CHECK-NEXT: define void @test1_h()
41define void @test1_h() noinline {
42entry:
43  call void @test1_g()
44  call void @readnone()
45  ret void
46}
47
48
49; The 'test2_' prefixed functions are designed to trigger forming a new direct
50; call due to RAUW-ing the returned value of a called function into the caller.
51; This too should form a new SCC which can then be reasoned about to compute
52; precise function attrs.
53
54; This function should no longer exist.
55; CHECK-NOT: @test2_f()
56define internal void()* @test2_f() {
57entry:
58  ret void()* @test2_h
59}
60
61; This function should have had 'readnone' deduced for its SCC.
62; CHECK: Function Attrs: noinline nounwind readnone
63; CHECK-NEXT: define void @test2_g()
64define void @test2_g() noinline {
65entry:
66  %p = call void()* @test2_f()
67  call void %p()
68  ret void
69}
70
71; This function should have had 'readnone' deduced for its SCC.
72; CHECK: Function Attrs: noinline nounwind readnone
73; CHECK-NEXT: define void @test2_h()
74define void @test2_h() noinline {
75entry:
76  call void @test2_g()
77  call void @readnone()
78  ret void
79}
80
81
82; The 'test3_' prefixed functions are designed to inline in a way that causes
83; call sites to become trivially dead during the middle of inlining callsites of
84; a single function to make sure that the inliner does not get confused by this
85; pattern.
86
87; CHECK-NOT: @test3_maybe_unknown(
88define internal void @test3_maybe_unknown(i1 %b) {
89entry:
90  br i1 %b, label %then, label %exit
91
92then:
93  call void @unknown()
94  br label %exit
95
96exit:
97  ret void
98}
99
100; CHECK-NOT: @test3_f(
101define internal i1 @test3_f() {
102entry:
103  ret i1 false
104}
105
106; CHECK-NOT: @test3_g(
107define internal i1 @test3_g(i1 %b) {
108entry:
109  br i1 %b, label %then1, label %if2
110
111then1:
112  call void @test3_maybe_unknown(i1 true)
113  br label %if2
114
115if2:
116  %f = call i1 @test3_f()
117  br i1 %f, label %then2, label %exit
118
119then2:
120  call void @test3_maybe_unknown(i1 true)
121  br label %exit
122
123exit:
124  ret i1 false
125}
126
127; FIXME: Currently the inliner doesn't successfully mark this as readnone
128; because while it simplifies trivially dead CFGs when inlining callees it
129; doesn't simplify the caller's trivially dead CFG and so we end with a dead
130; block calling @unknown.
131; CHECK-NOT: Function Attrs: readnone
132; CHECK: define void @test3_h()
133define void @test3_h() {
134entry:
135  %g = call i1 @test3_g(i1 false)
136  br i1 %g, label %then, label %exit
137
138then:
139  call void @test3_maybe_unknown(i1 true)
140  br label %exit
141
142exit:
143  call void @test3_maybe_unknown(i1 false)
144  ret void
145}
146
147
148; The 'test4_' prefixed functions are designed to trigger forming a new direct
149; call in the inlined body of the function similar to 'test1_'. However, after
150; that we continue to inline another edge of the graph forcing us to do a more
151; interesting call graph update for the new call edge. Eventually, we still
152; form a new SCC and should use that can deduce precise function attrs.
153
154; This function should have had 'readnone' deduced for its SCC.
155; CHECK: Function Attrs: noinline nounwind readnone
156; CHECK-NEXT: define void @test4_f1()
157define void @test4_f1() noinline {
158entry:
159  call void @test4_h()
160  ret void
161}
162
163; CHECK-NOT: @test4_f2
164define internal void @test4_f2() {
165entry:
166  call void @test4_f1()
167  ret void
168}
169
170; CHECK-NOT: @test4_g
171define internal void @test4_g(void()* %p) {
172entry:
173  call void %p()
174  ret void
175}
176
177; This function should have had 'readnone' deduced for its SCC.
178; CHECK: Function Attrs: noinline nounwind readnone
179; CHECK-NEXT: define void @test4_h()
180define void @test4_h() noinline {
181entry:
182  call void @test4_g(void()* @test4_f2)
183  ret void
184}
185