xref: /llvm-project/llvm/test/Transforms/LICM/call-hoisting.ll (revision e390c229a438ed1eb3396df8fbeeda89c49474e6)
1; RUN: opt -S -passes=licm %s | FileCheck %s
2; RUN: opt -aa-pipeline=basic-aa -passes='require<aa>,require<target-ir>,require<scalar-evolution>,require<opt-remark-emit>,loop-mssa(licm)' < %s -S | FileCheck %s
3
4declare i32 @load(ptr %p) argmemonly readonly nounwind
5
6define void @test_load(ptr noalias %loc, ptr noalias %sink) {
7; CHECK-LABEL: @test_load
8; CHECK-LABEL: entry:
9; CHECK: call i32 @load
10; CHECK-LABEL: loop:
11entry:
12  br label %loop
13
14loop:
15  %iv = phi i32 [0, %entry], [%iv.next, %loop]
16  %ret = call i32 @load(ptr %loc)
17  store volatile i32 %ret, ptr %sink
18  %iv.next = add i32 %iv, 1
19  %cmp = icmp slt i32 %iv, 200
20  br i1 %cmp, label %loop, label %exit
21
22exit:
23  ret void
24}
25
26declare i32 @spec(ptr %p, ptr %q) readonly argmemonly nounwind speculatable
27
28; We should strip the dereferenceable callsite attribute on spec call's argument since it is
29; can cause UB in the speculatable call when hoisted to preheader.
30; However, we need not strip the nonnull attribute since it just propagates
31; poison if the parameter was indeed null.
32define void @test_strip_attribute(ptr noalias %loc, ptr noalias %sink, ptr %q) {
33; CHECK-LABEL: @test_strip_attribute(
34; CHECK-NEXT:  entry:
35; CHECK-NEXT:    [[RET:%.*]] = call i32 @load(ptr [[LOC:%.*]])
36; CHECK-NEXT:    [[NULLCHK:%.*]] = icmp eq ptr [[Q:%.*]], null
37; CHECK-NEXT:    [[RET2:%.*]] = call i32 @spec(ptr nonnull [[Q]], ptr [[LOC]])
38; CHECK-NEXT:    br label [[LOOP:%.*]]
39; CHECK:       loop:
40; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[ISNULL:%.*]] ]
41; CHECK-NEXT:    br i1 [[NULLCHK]], label [[ISNULL]], label [[NONNULLBB:%.*]]
42entry:
43  br label %loop
44
45loop:
46  %iv = phi i32 [0, %entry], [%iv.next, %isnull ]
47  %ret = call i32 @load(ptr %loc)
48  %nullchk = icmp eq ptr %q, null
49  br i1 %nullchk, label %isnull, label %nonnullbb
50
51nonnullbb:
52  %ret2 = call i32 @spec(ptr nonnull %q, ptr dereferenceable(12) %loc)
53  br label %isnull
54
55isnull:
56  store volatile i32 %ret, ptr %sink
57  %iv.next = add i32 %iv, 1
58  %cmp = icmp slt i32 %iv, 200
59  br i1 %cmp, label %loop, label %exit
60
61exit:
62  ret void
63}
64
65declare void @store(i32 %val, ptr %p) argmemonly writeonly nounwind
66
67define void @test(ptr %loc) {
68; CHECK-LABEL: @test
69; CHECK-LABEL: loop:
70; CHECK: call void @store
71; CHECK-LABEL: exit:
72entry:
73  br label %loop
74
75loop:
76  %iv = phi i32 [0, %entry], [%iv.next, %loop]
77  call void @store(i32 0, ptr %loc)
78  %iv.next = add i32 %iv, 1
79  %cmp = icmp slt i32 %iv, 200
80  br i1 %cmp, label %loop, label %exit
81
82exit:
83  ret void
84}
85
86define void @test_multiexit(ptr %loc, i1 %earlycnd) {
87; CHECK-LABEL: @test_multiexit
88; CHECK-LABEL: loop:
89; CHECK: call void @store
90; CHECK-LABEL: backedge:
91entry:
92  br label %loop
93
94loop:
95  %iv = phi i32 [0, %entry], [%iv.next, %backedge]
96  call void @store(i32 0, ptr %loc)
97  %iv.next = add i32 %iv, 1
98  br i1 %earlycnd, label %exit1, label %backedge
99
100backedge:
101  %cmp = icmp slt i32 %iv, 200
102  br i1 %cmp, label %loop, label %exit2
103
104exit1:
105  ret void
106exit2:
107  ret void
108}
109
110define void @neg_lv_value(ptr %loc) {
111; CHECK-LABEL: @neg_lv_value
112; CHECK-LABEL: loop:
113; CHECK: call void @store
114; CHECK-LABEL: exit:
115entry:
116  br label %loop
117
118loop:
119  %iv = phi i32 [0, %entry], [%iv.next, %loop]
120  call void @store(i32 %iv, ptr %loc)
121  %iv.next = add i32 %iv, 1
122  %cmp = icmp slt i32 %iv, 200
123  br i1 %cmp, label %loop, label %exit
124
125exit:
126  ret void
127}
128
129define void @neg_lv_addr(ptr %loc) {
130; CHECK-LABEL: @neg_lv_addr
131; CHECK-LABEL: loop:
132; CHECK: call void @store
133; CHECK-LABEL: exit:
134entry:
135  br label %loop
136
137loop:
138  %iv = phi i32 [0, %entry], [%iv.next, %loop]
139  %p = getelementptr i32, ptr %loc, i32 %iv
140  call void @store(i32 0, ptr %p)
141  %iv.next = add i32 %iv, 1
142  %cmp = icmp slt i32 %iv, 200
143  br i1 %cmp, label %loop, label %exit
144
145exit:
146  ret void
147}
148
149define void @neg_mod(ptr %loc) {
150; CHECK-LABEL: @neg_mod
151; CHECK-LABEL: loop:
152; CHECK: call void @store
153; CHECK-LABEL: exit:
154entry:
155  br label %loop
156
157loop:
158  %iv = phi i32 [0, %entry], [%iv.next, %loop]
159  call void @store(i32 0, ptr %loc)
160  store i32 %iv, ptr %loc
161  %iv.next = add i32 %iv, 1
162  %cmp = icmp slt i32 %iv, 200
163  br i1 %cmp, label %loop, label %exit
164
165exit:
166  ret void
167}
168
169define void @neg_ref(ptr %loc) {
170; CHECK-LABEL: @neg_ref
171; CHECK-LABEL: loop:
172; CHECK: call void @store
173; CHECK-LABEL: exit1:
174entry:
175  br label %loop
176
177loop:
178  %iv = phi i32 [0, %entry], [%iv.next, %backedge]
179  call void @store(i32 0, ptr %loc)
180  %v = load i32, ptr %loc
181  %earlycnd = icmp eq i32 %v, 198
182  br i1 %earlycnd, label %exit1, label %backedge
183
184backedge:
185  %iv.next = add i32 %iv, 1
186  %cmp = icmp slt i32 %iv, 200
187  br i1 %cmp, label %loop, label %exit2
188
189exit1:
190  ret void
191exit2:
192  ret void
193}
194
195declare void @modref()
196
197define void @neg_modref(ptr %loc) {
198; CHECK-LABEL: @neg_modref
199; CHECK-LABEL: loop:
200; CHECK: call void @store
201; CHECK-LABEL: exit:
202entry:
203  br label %loop
204
205loop:
206  %iv = phi i32 [0, %entry], [%iv.next, %loop]
207  call void @store(i32 0, ptr %loc)
208  call void @modref()
209  %iv.next = add i32 %iv, 1
210  %cmp = icmp slt i32 %iv, 200
211  br i1 %cmp, label %loop, label %exit
212
213exit:
214  ret void
215}
216
217define void @neg_fence(ptr %loc) {
218; CHECK-LABEL: @neg_fence
219; CHECK-LABEL: loop:
220; CHECK: call void @store
221; CHECK-LABEL: exit:
222entry:
223  br label %loop
224
225loop:
226  %iv = phi i32 [0, %entry], [%iv.next, %loop]
227  call void @store(i32 0, ptr %loc)
228  fence seq_cst
229  %iv.next = add i32 %iv, 1
230  %cmp = icmp slt i32 %iv, 200
231  br i1 %cmp, label %loop, label %exit
232
233exit:
234  ret void
235}
236
237declare void @not_nounwind(i32 %v, ptr %p) writeonly argmemonly
238declare void @not_argmemonly(i32 %v, ptr %p) writeonly nounwind
239declare void @not_writeonly(i32 %v, ptr %p) argmemonly nounwind
240
241define void @neg_not_nounwind(ptr %loc) {
242; CHECK-LABEL: @neg_not_nounwind
243; CHECK-LABEL: loop:
244; CHECK: call void @not_nounwind
245; CHECK-LABEL: exit:
246entry:
247  br label %loop
248
249loop:
250  %iv = phi i32 [0, %entry], [%iv.next, %loop]
251  call void @not_nounwind(i32 0, ptr %loc)
252  %iv.next = add i32 %iv, 1
253  %cmp = icmp slt i32 %iv, 200
254  br i1 %cmp, label %loop, label %exit
255
256exit:
257  ret void
258}
259
260define void @neg_not_argmemonly(ptr %loc) {
261; CHECK-LABEL: @neg_not_argmemonly
262; CHECK-LABEL: loop:
263; CHECK: call void @not_argmemonly
264; CHECK-LABEL: exit:
265entry:
266  br label %loop
267
268loop:
269  %iv = phi i32 [0, %entry], [%iv.next, %loop]
270  call void @not_argmemonly(i32 0, ptr %loc)
271  %iv.next = add i32 %iv, 1
272  %cmp = icmp slt i32 %iv, 200
273  br i1 %cmp, label %loop, label %exit
274
275exit:
276  ret void
277}
278
279define void @neg_not_writeonly(ptr %loc) {
280; CHECK-LABEL: @neg_not_writeonly
281; CHECK-LABEL: loop:
282; CHECK: call void @not_writeonly
283; CHECK-LABEL: exit:
284entry:
285  br label %loop
286
287loop:
288  %iv = phi i32 [0, %entry], [%iv.next, %loop]
289  call void @not_writeonly(i32 0, ptr %loc)
290  %iv.next = add i32 %iv, 1
291  %cmp = icmp slt i32 %iv, 200
292  br i1 %cmp, label %loop, label %exit
293
294exit:
295  ret void
296}
297
298