1; Make sure that even without some external devirtualization iteration tool,
2; the CGSCC pass manager correctly observes and re-visits SCCs that change
3; structure due to devirtualization. We trigger devirtualization here with GVN
4; which forwards a store through a load and to an indirect call.
5;
6; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs)' -S < %s | FileCheck %s --check-prefix=BEFORE
7; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs,function(gvn))' -S < %s | FileCheck %s --check-prefix=AFTER
8;
9; Also check that adding an extra CGSCC pass after the function update but
10; without requiring the outer manager to iterate doesn't break any invariant.
11; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs,function(gvn),function-attrs)' -S < %s | FileCheck %s --check-prefix=AFTER
12
13declare void @readnone() nofree nosync readnone
14declare void @unknown()
15
16; The @test1_* checks that if we refine an indirect call to a direct call and
17; in the process change the very structure of the call graph we also revisit
18; that component of the graph and do so in an up-to-date fashion.
19
20; BEFORE: define void @test1_a1() {
21; AFTER: define void @test1_a1() {
22define void @test1_a1() {
23  %fptr = alloca void()*
24  store void()* @test1_b2, void()** %fptr
25  store void()* @test1_b1, void()** %fptr
26  %f = load void()*, void()** %fptr
27  call void %f()
28  ret void
29}
30
31; BEFORE: define void @test1_b1() {
32; AFTER: define void @test1_b1() {
33define void @test1_b1() {
34  call void @unknown()
35  call void @test1_a1()
36  ret void
37}
38
39; BEFORE: define void @test1_a2() {
40; AFTER: define void @test1_a2() #0 {
41define void @test1_a2() {
42  %fptr = alloca void()*
43  store void()* @test1_b1, void()** %fptr
44  store void()* @test1_b2, void()** %fptr
45  %f = load void()*, void()** %fptr
46  call void %f()
47  ret void
48}
49
50; BEFORE: define void @test1_b2() {
51; AFTER: define void @test1_b2() #0 {
52define void @test1_b2() {
53  call void @readnone()
54  call void @test1_a2()
55  ret void
56}
57
58
59; The @test2_* set of functions exercise a case where running function passes
60; introduces a new post-order relationship that was not present originally and
61; makes sure we walk across the SCCs in that order.
62
63; CHECK: define void @test2_a() {
64define void @test2_a() {
65  call void @test2_b1()
66  call void @test2_b2()
67  call void @test2_b3()
68  call void @unknown()
69  ret void
70}
71
72; CHECK: define void @test2_b1() #0 {
73define void @test2_b1() {
74  %fptr = alloca void()*
75  store void()* @test2_a, void()** %fptr
76  store void()* @readnone, void()** %fptr
77  %f = load void()*, void()** %fptr
78  call void %f()
79  ret void
80}
81
82; CHECK: define void @test2_b2() #0 {
83define void @test2_b2() {
84  %fptr = alloca void()*
85  store void()* @test2_a, void()** %fptr
86  store void()* @test2_b2, void()** %fptr
87  store void()* @test2_b3, void()** %fptr
88  store void()* @test2_b1, void()** %fptr
89  %f = load void()*, void()** %fptr
90  call void %f()
91  ret void
92}
93
94; CHECK: define void @test2_b3() #0 {
95define void @test2_b3() {
96  %fptr = alloca void()*
97  store void()* @test2_a, void()** %fptr
98  store void()* @test2_b2, void()** %fptr
99  store void()* @test2_b3, void()** %fptr
100  store void()* @test2_b1, void()** %fptr
101  %f = load void()*, void()** %fptr
102  call void %f()
103  ret void
104}
105
106; CHECK: attributes #0 = { nofree nosync readnone }
107