xref: /llvm-project/llvm/test/Transforms/GVN/invariant.group.ll (revision 23abf931386002fb9d2c11d026846475c224c641)
1; RUN: opt < %s -passes=gvn -S | FileCheck %s
2
3%struct.A = type { ptr }
4@_ZTV1A = available_externally unnamed_addr constant [3 x ptr] [ptr null, ptr @_ZTI1A, ptr @_ZN1A3fooEv], align 8
5@_ZTI1A = external constant ptr
6
7@unknownPtr = external global i8
8
9; CHECK-LABEL: define i8 @simple() {
10define i8 @simple() {
11entry:
12    %ptr = alloca i8
13    store i8 42, ptr %ptr, !invariant.group !0
14    call void @foo(ptr %ptr)
15
16    %a = load i8, ptr %ptr, !invariant.group !0
17    %b = load i8, ptr %ptr, !invariant.group !0
18    %c = load i8, ptr %ptr, !invariant.group !0
19; CHECK: ret i8 42
20    ret i8 %a
21}
22
23; CHECK-LABEL: define i8 @optimizable1() {
24define i8 @optimizable1() {
25entry:
26    %ptr = alloca i8
27    store i8 42, ptr %ptr, !invariant.group !0
28    %ptr2 = call ptr @llvm.launder.invariant.group.p0(ptr %ptr)
29    %a = load i8, ptr %ptr, !invariant.group !0
30
31    call void @foo(ptr %ptr2); call to use %ptr2
32; CHECK: ret i8 42
33    ret i8 %a
34}
35
36; CHECK-LABEL: define i8 @optimizable2() {
37define i8 @optimizable2() {
38entry:
39    %ptr = alloca i8
40    store i8 42, ptr %ptr, !invariant.group !0
41    call void @foo(ptr %ptr)
42
43    store i8 13, ptr %ptr ; can't use this store with invariant.group
44    %a = load i8, ptr %ptr
45    call void @bar(i8 %a) ; call to use %a
46
47    call void @foo(ptr %ptr)
48    %b = load i8, ptr %ptr, !invariant.group !0
49
50; CHECK: ret i8 42
51    ret i8 %b
52}
53
54; CHECK-LABEL: define i1 @proveEqualityForStrip(
55define i1 @proveEqualityForStrip(ptr %a) {
56; FIXME: The first call could be also removed by GVN. Right now
57; DCE removes it. The second call is CSE'd with the first one.
58; CHECK: %b1 = call ptr @llvm.strip.invariant.group.p0(ptr %a)
59  %b1 = call ptr @llvm.strip.invariant.group.p0(ptr %a)
60; CHECK-NOT: llvm.strip.invariant.group
61  %b2 = call ptr @llvm.strip.invariant.group.p0(ptr %a)
62  %r = icmp eq ptr %b1, %b2
63; CHECK: ret i1 true
64  ret i1 %r
65}
66; CHECK-LABEL: define i8 @unoptimizable1() {
67define i8 @unoptimizable1() {
68entry:
69    %ptr = alloca i8
70    store i8 42, ptr %ptr
71    call void @foo(ptr %ptr)
72    %a = load i8, ptr %ptr, !invariant.group !0
73; CHECK: ret i8 %a
74    ret i8 %a
75}
76
77; CHECK-LABEL: define void @indirectLoads() {
78define void @indirectLoads() {
79entry:
80  %a = alloca ptr, align 8
81
82  %call = call ptr @getPointer(ptr null)
83  call void @_ZN1AC1Ev(ptr %call)
84
85; CHECK: %vtable = load {{.*}} !invariant.group
86  %vtable = load ptr, ptr %call, align 8, !invariant.group !0
87  %cmp.vtables = icmp eq ptr %vtable, getelementptr inbounds ([3 x ptr], ptr @_ZTV1A, i64 0, i64 2)
88  call void @llvm.assume(i1 %cmp.vtables)
89
90  store ptr %call, ptr %a, align 8
91  %0 = load ptr, ptr %a, align 8
92
93; CHECK: call void @_ZN1A3fooEv(
94  %vtable1 = load ptr, ptr %0, align 8, !invariant.group !0
95  %1 = load ptr, ptr %vtable1, align 8
96  call void %1(ptr %0)
97  %2 = load ptr, ptr %a, align 8
98
99; CHECK: call void @_ZN1A3fooEv(
100  %vtable2 = load ptr, ptr %2, align 8, !invariant.group !0
101  %3 = load ptr, ptr %vtable2, align 8
102
103  call void %3(ptr %2)
104  %4 = load ptr, ptr %a, align 8
105
106  %vtable4 = load ptr, ptr %4, align 8, !invariant.group !0
107  %5 = load ptr, ptr %vtable4, align 8
108; CHECK: call void @_ZN1A3fooEv(
109  call void %5(ptr %4)
110
111  %vtable5 = load ptr, ptr %call, align 8, !invariant.group !0
112  %6 = load ptr, ptr %vtable5, align 8
113; CHECK: call void @_ZN1A3fooEv(
114  call void %6(ptr %4)
115
116  ret void
117}
118
119; CHECK-LABEL: define void @combiningBitCastWithLoad() {
120define void @combiningBitCastWithLoad() {
121entry:
122  %a = alloca ptr, align 8
123
124  %call = call ptr @getPointer(ptr null)
125  call void @_ZN1AC1Ev(ptr %call)
126
127; CHECK: %vtable = load {{.*}} !invariant.group
128  %vtable = load ptr, ptr %call, align 8, !invariant.group !0
129  %cmp.vtables = icmp eq ptr %vtable, getelementptr inbounds ([3 x ptr], ptr @_ZTV1A, i64 0, i64 2)
130
131  store ptr %call, ptr %a, align 8
132; CHECK-NOT: !invariant.group
133  %0 = load ptr, ptr %a, align 8
134
135  %vtable1 = load ptr, ptr %0, align 8, !invariant.group !0
136  %1 = load ptr, ptr %vtable1, align 8
137  call void %1(ptr %0)
138
139  ret void
140}
141
142; CHECK-LABEL:define void @loadCombine() {
143define void @loadCombine() {
144enter:
145  %ptr = alloca i8
146  store i8 42, ptr %ptr
147  call void @foo(ptr %ptr)
148; CHECK: %[[A:.*]] = load i8, ptr %ptr, align 1, !invariant.group
149  %a = load i8, ptr %ptr, !invariant.group !0
150; CHECK-NOT: load
151  %b = load i8, ptr %ptr, !invariant.group !0
152; CHECK: call void @bar(i8 %[[A]])
153  call void @bar(i8 %a)
154; CHECK: call void @bar(i8 %[[A]])
155  call void @bar(i8 %b)
156  ret void
157}
158
159; CHECK-LABEL: define void @loadCombine1() {
160define void @loadCombine1() {
161enter:
162  %ptr = alloca i8
163  store i8 42, ptr %ptr
164  call void @foo(ptr %ptr)
165; CHECK: %[[D:.*]] = load i8, ptr %ptr, align 1, !invariant.group
166  %c = load i8, ptr %ptr
167; CHECK-NOT: load
168  %d = load i8, ptr %ptr, !invariant.group !0
169; CHECK: call void @bar(i8 %[[D]])
170  call void @bar(i8 %c)
171; CHECK: call void @bar(i8 %[[D]])
172  call void @bar(i8 %d)
173  ret void
174}
175
176; CHECK-LABEL: define void @loadCombine2() {
177define void @loadCombine2() {
178enter:
179  %ptr = alloca i8
180  store i8 42, ptr %ptr
181  call void @foo(ptr %ptr)
182; CHECK: %[[E:.*]] = load i8, ptr %ptr, align 1, !invariant.group
183  %e = load i8, ptr %ptr, !invariant.group !0
184; CHECK-NOT: load
185  %f = load i8, ptr %ptr
186; CHECK: call void @bar(i8 %[[E]])
187  call void @bar(i8 %e)
188; CHECK: call void @bar(i8 %[[E]])
189  call void @bar(i8 %f)
190  ret void
191}
192
193; CHECK-LABEL: define void @loadCombine3() {
194define void @loadCombine3() {
195enter:
196  %ptr = alloca i8
197  store i8 42, ptr %ptr
198  call void @foo(ptr %ptr)
199; CHECK: %[[E:.*]] = load i8, ptr %ptr, align 1, !invariant.group
200  %e = load i8, ptr %ptr, !invariant.group !0
201; CHECK-NOT: load
202  %f = load i8, ptr %ptr, !invariant.group !0
203; CHECK: call void @bar(i8 %[[E]])
204  call void @bar(i8 %e)
205; CHECK: call void @bar(i8 %[[E]])
206  call void @bar(i8 %f)
207  ret void
208}
209
210; CHECK-LABEL: define i8 @unoptimizable2() {
211define i8 @unoptimizable2() {
212entry:
213    %ptr = alloca i8
214    store i8 42, ptr %ptr
215    call void @foo(ptr %ptr)
216    %a = load i8, ptr %ptr
217    call void @foo(ptr %ptr)
218    %b = load i8, ptr %ptr, !invariant.group !0
219
220; CHECK: ret i8 %a
221    ret i8 %a
222}
223
224; CHECK-LABEL: define i8 @unoptimizable3() {
225define i8 @unoptimizable3() {
226entry:
227    %ptr = alloca i8
228    store i8 42, ptr %ptr, !invariant.group !0
229    %ptr2 = call ptr @getPointer(ptr %ptr)
230    %a = load i8, ptr %ptr2, !invariant.group !0
231
232; CHECK: ret i8 %a
233    ret i8 %a
234}
235
236; CHECK-LABEL: define i8 @optimizable4() {
237define i8 @optimizable4() {
238entry:
239    %ptr = alloca i8
240    store i8 42, ptr %ptr, !invariant.group !0
241    %ptr2 = call ptr @llvm.launder.invariant.group.p0(ptr %ptr)
242; CHECK-NOT: load
243    %a = load i8, ptr %ptr2, !invariant.group !0
244
245; CHECK: ret i8 42
246    ret i8 %a
247}
248
249; CHECK-LABEL: define i8 @volatile1() {
250define i8 @volatile1() {
251entry:
252    %ptr = alloca i8
253    store i8 42, ptr %ptr, !invariant.group !0
254    call void @foo(ptr %ptr)
255    %a = load i8, ptr %ptr, !invariant.group !0
256    %b = load volatile i8, ptr %ptr
257; CHECK: call void @bar(i8 %b)
258    call void @bar(i8 %b)
259
260    %c = load volatile i8, ptr %ptr, !invariant.group !0
261; FIXME: we could change %c to 42, preserving volatile load
262; CHECK: call void @bar(i8 %c)
263    call void @bar(i8 %c)
264; CHECK: ret i8 42
265    ret i8 %a
266}
267
268; CHECK-LABEL: define i8 @volatile2() {
269define i8 @volatile2() {
270entry:
271    %ptr = alloca i8
272    store i8 42, ptr %ptr, !invariant.group !0
273    call void @foo(ptr %ptr)
274    %a = load i8, ptr %ptr, !invariant.group !0
275    %b = load volatile i8, ptr %ptr
276; CHECK: call void @bar(i8 %b)
277    call void @bar(i8 %b)
278
279    %c = load volatile i8, ptr %ptr, !invariant.group !0
280; FIXME: we could change %c to 42, preserving volatile load
281; CHECK: call void @bar(i8 %c)
282    call void @bar(i8 %c)
283; CHECK: ret i8 42
284    ret i8 %a
285}
286
287; CHECK-LABEL: define i8 @fun() {
288define i8 @fun() {
289entry:
290    %ptr = alloca i8
291    store i8 42, ptr %ptr, !invariant.group !0
292    call void @foo(ptr %ptr)
293
294    %a = load i8, ptr %ptr, !invariant.group !0 ; Can assume that value under %ptr didn't change
295; CHECK: call void @bar(i8 42)
296    call void @bar(i8 %a)
297
298    %newPtr = call ptr @getPointer(ptr %ptr)
299    %c = load i8, ptr %newPtr, !invariant.group !0 ; Can't assume anything, because we only have information about %ptr
300; CHECK: call void @bar(i8 %c)
301    call void @bar(i8 %c)
302
303    %unknownValue = load i8, ptr @unknownPtr
304; FIXME: Can assume that %unknownValue == 42
305; CHECK: store i8 %unknownValue, ptr %ptr, align 1, !invariant.group !0
306    store i8 %unknownValue, ptr %ptr, !invariant.group !0
307
308    %newPtr2 = call ptr @llvm.launder.invariant.group.p0(ptr %ptr)
309; CHECK-NOT: load
310    %d = load i8, ptr %newPtr2, !invariant.group !0
311; CHECK: ret i8 %unknownValue
312    ret i8 %d
313}
314
315; This test checks if invariant.group understands gep with zeros
316; CHECK-LABEL: define void @testGEP0() {
317define void @testGEP0() {
318  %a = alloca %struct.A, align 8
319  store ptr getelementptr inbounds ([3 x ptr], ptr @_ZTV1A, i64 0, i64 2), ptr %a, align 8, !invariant.group !0
320; CHECK: call void @_ZN1A3fooEv(ptr nonnull dereferenceable(8) %a)
321  call void @_ZN1A3fooEv(ptr nonnull dereferenceable(8) %a) ; This call may change vptr
322  %1 = load i8, ptr @unknownPtr, align 4
323  %2 = icmp eq i8 %1, 0
324  br i1 %2, label %_Z1gR1A.exit, label %3
325
326; This should be devirtualized by invariant.group
327  %4 = load ptr, ptr %a, align 8, !invariant.group !0
328  %5 = load ptr, ptr %4, align 8
329; CHECK: call void @_ZN1A3fooEv(ptr nonnull %a)
330  call void %5(ptr nonnull %a)
331  br label %_Z1gR1A.exit
332
333_Z1gR1A.exit:                                     ; preds = %0, %3
334  ret void
335}
336
337; Check if no optimizations are performed with global pointers.
338; FIXME: we could do the optimizations if we would check if dependency comes
339; from the same function.
340; CHECK-LABEL: define void @testGlobal() {
341define void @testGlobal() {
342; CHECK:  %a = load i8, ptr @unknownPtr, align 1, !invariant.group !0
343   %a = load i8, ptr @unknownPtr, !invariant.group !0
344   call void @foo2(ptr @unknownPtr, i8 %a)
345; CHECK:  %1 = load i8, ptr @unknownPtr, align 1, !invariant.group !0
346   %1 = load i8, ptr @unknownPtr, !invariant.group !0
347   call void @bar(i8 %1)
348
349   call void @fooBit(ptr @unknownPtr, i1 1)
350; Adding regex because of canonicalization of bitcasts
351; CHECK: %2 = load i1, ptr {{.*}}, !invariant.group !0
352   %2 = load i1, ptr @unknownPtr, !invariant.group !0
353   call void @fooBit(ptr @unknownPtr, i1 %2)
354; CHECK:  %3 = load i1, ptr {{.*}}, !invariant.group !0
355   %3 = load i1, ptr @unknownPtr, !invariant.group !0
356   call void @fooBit(ptr @unknownPtr, i1 %3)
357   ret void
358}
359; And in the case it is not global
360; CHECK-LABEL: define void @testNotGlobal() {
361define void @testNotGlobal() {
362   %a = alloca i8
363   call void @foo(ptr %a)
364; CHECK:  %b = load i8, ptr %a, align 1, !invariant.group !0
365   %b = load i8, ptr %a, !invariant.group !0
366   call void @foo2(ptr %a, i8 %b)
367
368   %1 = load i8, ptr %a, !invariant.group !0
369; CHECK: call void @bar(i8 %b)
370   call void @bar(i8 %1)
371
372   call void @fooBit(ptr %a, i1 1)
373; CHECK: %1 = trunc i8 %b to i1
374   %2 = load i1, ptr %a, !invariant.group !0
375; CHECK-NEXT: call void @fooBit(ptr %a, i1 %1)
376   call void @fooBit(ptr %a, i1 %2)
377   %3 = load i1, ptr %a, !invariant.group !0
378; CHECK-NEXT: call void @fooBit(ptr %a, i1 %1)
379   call void @fooBit(ptr %a, i1 %3)
380   ret void
381}
382
383; CHECK-LABEL: define void @handling_loops()
384define void @handling_loops() {
385  %a = alloca %struct.A, align 8
386  store ptr getelementptr inbounds ([3 x ptr], ptr @_ZTV1A, i64 0, i64 2), ptr %a, align 8, !invariant.group !0
387  %1 = load i8, ptr @unknownPtr, align 4
388  %2 = icmp sgt i8 %1, 0
389  br i1 %2, label %.lr.ph.i, label %_Z2g2R1A.exit
390
391.lr.ph.i:                                         ; preds = %0
392  %3 = load i8, ptr @unknownPtr, align 4
393  %4 = icmp sgt i8 %3, 1
394  br i1 %4, label %._crit_edge.preheader, label %_Z2g2R1A.exit
395
396._crit_edge.preheader:                            ; preds = %.lr.ph.i
397  br label %._crit_edge
398
399._crit_edge:                                      ; preds = %._crit_edge.preheader, %._crit_edge
400  %5 = phi i8 [ %7, %._crit_edge ], [ 1, %._crit_edge.preheader ]
401  %.pre = load ptr, ptr %a, align 8, !invariant.group !0
402  %6 = load ptr, ptr %.pre, align 8
403  ; CHECK: call void @_ZN1A3fooEv(ptr nonnull %a)
404  call void %6(ptr nonnull %a) #3
405  ; CHECK-NOT: call void %
406  %7 = add nuw nsw i8 %5, 1
407  %8 = load i8, ptr @unknownPtr, align 4
408  %9 = icmp slt i8 %7, %8
409  br i1 %9, label %._crit_edge, label %_Z2g2R1A.exit.loopexit
410
411_Z2g2R1A.exit.loopexit:                           ; preds = %._crit_edge
412  br label %_Z2g2R1A.exit
413
414_Z2g2R1A.exit:                                    ; preds = %_Z2g2R1A.exit.loopexit, %.lr.ph.i, %0
415  ret void
416}
417
418
419declare void @foo(ptr)
420declare void @foo2(ptr, i8)
421declare void @bar(i8)
422declare ptr @getPointer(ptr)
423declare void @_ZN1A3fooEv(ptr)
424declare void @_ZN1AC1Ev(ptr)
425declare void @fooBit(ptr, i1)
426
427declare ptr @llvm.launder.invariant.group.p0(ptr)
428declare ptr @llvm.strip.invariant.group.p0(ptr)
429
430
431declare void @llvm.assume(i1 %cmp.vtables)
432
433
434!0 = !{}
435