xref: /llvm-project/llvm/test/Transforms/Inline/cgscc-incremental-invalidate.ll (revision 151602c7a9935558ca671b35359989b261045db0)
1; Test for a subtle bug when computing analyses during inlining and mutating
2; the SCC structure. Without care, this can fail to invalidate analyses.
3;
4; RUN: opt < %s -aa-pipeline= -passes='cgscc(inline,function(verify<domtree>))' -debug-pass-manager -inline-deferral -S 2>&1 | FileCheck %s
5
6; First we check that the passes run in the way we expect. Otherwise this test
7; may stop testing anything.
8;
9; CHECK: Running pass: InlinerPass on (test1_f, test1_g, test1_h)
10; CHECK: Running analysis: DominatorTreeAnalysis on test1_f
11; CHECK: Invalidating analysis: DominatorTreeAnalysis on test1_f
12; CHECK: Invalidating analysis: LoopAnalysis on test1_f
13; CHECK: Invalidating analysis: BranchProbabilityAnalysis on test1_f
14; CHECK: Invalidating analysis: BlockFrequencyAnalysis on test1_f
15; CHECK: Running analysis: DominatorTreeAnalysis on test1_g
16; CHECK: Invalidating analysis: DominatorTreeAnalysis on test1_g
17; CHECK: Invalidating analysis: LoopAnalysis on test1_g
18; CHECK: Invalidating analysis: BranchProbabilityAnalysis on test1_g
19; CHECK: Invalidating analysis: BlockFrequencyAnalysis on test1_g
20; CHECK: Invalidating analysis: DominatorTreeAnalysis on test1_h
21; CHECK: Invalidating analysis: LoopAnalysis on test1_h
22; CHECK: Invalidating analysis: BranchProbabilityAnalysis on test1_h
23; CHECK: Invalidating analysis: BlockFrequencyAnalysis on test1_h
24; CHECK-NOT: Invalidating analysis:
25; CHECK: Running pass: DominatorTreeVerifierPass on test1_g
26; CHECK-NEXT: Running analysis: DominatorTreeAnalysis on test1_g
27; CHECK-NOT: Invalidating analysis:
28; CHECK: Running pass: DominatorTreeVerifierPass on test1_h
29; CHECK-NEXT: Running analysis: DominatorTreeAnalysis on test1_h
30; CHECK-NOT: Invalidating analysis:
31; CHECK: Running pass: DominatorTreeVerifierPass on test1_f
32
33; An external function used to control branches.
34declare i1 @flag()
35; CHECK-LABEL: declare i1 @flag()
36
37; The utility function with interesting control flow that gets inlined below to
38; perturb the dominator tree.
39define internal void @callee() {
40entry:
41  %ptr = alloca i8
42  %flag = call i1 @flag()
43  br i1 %flag, label %then, label %else
44
45then:
46  store volatile i8 42, ptr %ptr
47  br label %return
48
49else:
50  store volatile i8 -42, ptr %ptr
51  br label %return
52
53return:
54  ret void
55}
56
57; The 'test1_' prefixed functions work to carefully test that incrementally
58; reducing an SCC in the inliner cannot accidentially leave stale function
59; analysis results due to failing to invalidate them for all the functions.
60
61; We visit this function first in the inliner, and while we inline callee
62; perturbing the CFG, we don't inline anything else and the SCC structure
63; remains in tact.
64define void @test1_f() {
65; CHECK-LABEL: define void @test1_f()
66entry:
67  ; We force this edge to survive inlining.
68  call void @test1_g() noinline
69; CHECK: call void @test1_g()
70
71  ; Pull interesting CFG into this function.
72  call void @callee()
73; CHECK-NOT: call void @callee()
74
75  ret void
76; CHECK: ret void
77}
78
79; We visit this function second and here we inline the edge to 'test1_f'
80; separating it into its own SCC. The current SCC is now just 'test1_g' and
81; 'test1_h'.
82define void @test1_g() {
83; CHECK-LABEL: define void @test1_g()
84entry:
85  ; This edge gets inlined away.
86  call void @test1_f()
87; CHECK-NOT: call void @test1_f()
88; CHECK: call void @test1_g()
89
90  ; We force this edge to survive inlining.
91  call void @test1_h() noinline
92; CHECK: call void @test1_h()
93
94  ; Pull interesting CFG into this function.
95  call void @callee()
96; CHECK-NOT: call void @callee()
97
98  ret void
99; CHECK: ret void
100}
101
102; The inliner visits this last function. It can't actually break any cycles
103; here, but because we visit this function we compute fresh analyses for it.
104; These analyses are then invalidated when we inline callee disrupting the
105; CFG, and it is important that they be freed.
106define void @test1_h() {
107; CHECK-LABEL: define void @test1_h()
108entry:
109  call void @test1_g()
110; CHECK: call void @test1_g()
111
112  ; Pull interesting CFG into this function.
113  call void @callee()
114; CHECK-NOT: call void @callee()
115
116  ret void
117; CHECK: ret void
118}
119
120; The 'test2_' prefixed code works to carefully trigger forming an SCC with
121; a dominator tree for one of the functions but not the other and without even
122; a function analysis manager proxy for the SCC that things get merged into.
123; Without proper handling when updating the call graph this will find a stale
124; dominator tree.
125
126@test2_global = external global i32, align 4
127
128define void @test2_hoge(ptr %arg) {
129; CHECK-LABEL: define void @test2_hoge(
130bb:
131  %tmp2 = call zeroext i1 %arg(ptr @test2_global)
132; CHECK: call zeroext i1 %arg(
133  br label %bb3
134
135bb3:
136  %tmp5 = call zeroext i1 %arg(ptr @test2_global)
137; CHECK: call zeroext i1 %arg(
138  br i1 %tmp5, label %bb3, label %bb6
139
140bb6:
141  ret void
142}
143
144define zeroext i1 @test2_widget(ptr %arg) {
145; CHECK-LABEL: define zeroext i1 @test2_widget(
146bb:
147  %tmp1 = alloca i8, align 1
148  %tmp2 = alloca i32, align 4
149  call void @test2_quux()
150; CHECK-NOT:     call
151;
152; CHECK:         call zeroext i1 @test2_widget(ptr @test2_global)
153; CHECK-NEXT:    br label %[[NEW_BB:.*]]
154;
155; CHECK:       [[NEW_BB]]:
156; CHECK-NEXT:    call zeroext i1 @test2_widget(ptr @test2_global)
157;
158; CHECK:       {{.*}}:
159
160  call void @test2_hoge.1(ptr %arg)
161; CHECK-NEXT:    call void @test2_hoge.1(
162
163  %tmp4 = call zeroext i1 @test2_barney(ptr %tmp2)
164  %tmp5 = zext i1 %tmp4 to i32
165  store i32 %tmp5, ptr %tmp2, align 4
166  %tmp6 = call zeroext i1 @test2_barney(ptr null)
167  call void @test2_ham(ptr %tmp1)
168; CHECK:         call void @test2_ham(
169
170  call void @test2_quux()
171; CHECK-NOT:     call
172;
173; CHECK:         call zeroext i1 @test2_widget(ptr @test2_global)
174; CHECK-NEXT:    br label %[[NEW_BB:.*]]
175;
176; CHECK:       [[NEW_BB]]:
177; CHECK-NEXT:    call zeroext i1 @test2_widget(ptr @test2_global)
178;
179; CHECK:       {{.*}}:
180  ret i1 true
181; CHECK-NEXT:    ret i1 true
182}
183
184define internal void @test2_quux() {
185; CHECK-NOT: @test2_quux
186bb:
187  call void @test2_hoge(ptr @test2_widget)
188  ret void
189}
190
191declare void @test2_hoge.1(ptr)
192
193declare zeroext i1 @test2_barney(ptr)
194
195declare void @test2_ham(ptr)
196