1 ; RUN: opt < %s -passes=newgvn -S | FileCheck %s
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
7 @unknownPtr = external global i8
9 ; CHECK-LABEL: define i8 @simple() {
13 store i8 42, ptr %ptr, !invariant.group !0
14 call void @foo(ptr %ptr)
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
23 ; CHECK-LABEL: define i8 @optimizable1() {
24 define i8 @optimizable1() {
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
31 call void @foo(ptr %ptr2); call to use %ptr2
36 ; CHECK-LABEL: define i8 @optimizable2() {
37 define i8 @optimizable2() {
40 store i8 42, ptr %ptr, !invariant.group !0
41 call void @foo(ptr %ptr)
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
47 call void @foo(ptr %ptr)
48 %b = load i8, ptr %ptr, !invariant.group !0
54 ; CHECK-LABEL: define i1 @proveEqualityForStrip(
55 define i1 @proveEqualityForStrip(ptr %a) {
56 %b1 = call ptr @llvm.strip.invariant.group.p0(ptr %a)
57 ; CHECK-NOT: llvm.strip.invariant.group
58 %b2 = call ptr @llvm.strip.invariant.group.p0(ptr %a)
59 %r = icmp eq ptr %b1, %b2
64 ; CHECK-LABEL: define i8 @unoptimizable1() {
65 define i8 @unoptimizable1() {
69 call void @foo(ptr %ptr)
70 %a = load i8, ptr %ptr, !invariant.group !0
75 ; NewGVN doesn't support assumes.
76 ; CHECK-LABEL: define void @indirectLoads() {
77 define void @indirectLoads() {
79 %a = alloca ptr, align 8
81 %call = call ptr @getPointer(ptr null)
82 call void @_ZN1AC1Ev(ptr %call)
84 ; CHECK: %vtable = load {{.*}} !invariant.group
85 %vtable = load ptr, ptr %call, align 8, !invariant.group !0
86 %cmp.vtables = icmp eq ptr %vtable, getelementptr inbounds ([3 x ptr], ptr @_ZTV1A, i64 0, i64 2)
87 call void @llvm.assume(i1 %cmp.vtables)
89 store ptr %call, ptr %a, align 8
90 %0 = load ptr, ptr %a, align 8
92 ; FIXME: call void @_ZN1A3fooEv(
93 %vtable1 = load ptr, ptr %0, align 8, !invariant.group !0
94 %1 = load ptr, ptr %vtable1, align 8
96 %2 = load ptr, ptr %a, align 8
98 ; FIXME: call void @_ZN1A3fooEv(
99 %vtable2 = load ptr, ptr %2, align 8, !invariant.group !0
100 %3 = load ptr, ptr %vtable2, align 8
103 %4 = load ptr, ptr %a, align 8
105 %vtable4 = load ptr, ptr %4, align 8, !invariant.group !0
106 %5 = load ptr, ptr %vtable4, align 8
107 ; FIXME: call void @_ZN1A3fooEv(
110 %vtable5 = load ptr, ptr %call, align 8, !invariant.group !0
111 %6 = load ptr, ptr %vtable5, align 8
112 ; FIXME: call void @_ZN1A3fooEv(
118 ; NewGVN won't CSE loads with different pointee types.
119 ; CHECK-LABEL: define void @combiningBitCastWithLoad() {
120 define void @combiningBitCastWithLoad() {
122 %a = alloca ptr, align 8
124 %call = call ptr @getPointer(ptr null)
125 call void @_ZN1AC1Ev(ptr %call)
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)
131 store ptr %call, ptr %a, align 8
132 ; FIXME-NOT: !invariant.group
133 %0 = load ptr, ptr %a, align 8
135 %vtable1 = load ptr, ptr %0, align 8, !invariant.group !0
136 %1 = load ptr, ptr %vtable1, align 8
142 ; CHECK-LABEL:define void @loadCombine() {
143 define void @loadCombine() {
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
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)
159 ; CHECK-LABEL: define void @loadCombine1() {
160 define void @loadCombine1() {
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
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)
176 ; CHECK-LABEL: define void @loadCombine2() {
177 define void @loadCombine2() {
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
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)
193 ; CHECK-LABEL: define void @loadCombine3() {
194 define void @loadCombine3() {
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
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)
210 ; CHECK-LABEL: define i8 @unoptimizable2() {
211 define i8 @unoptimizable2() {
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
224 ; CHECK-LABEL: define i8 @unoptimizable3() {
225 define i8 @unoptimizable3() {
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
236 ; NewGVN cares about the launder for some reason.
237 ; CHECK-LABEL: define i8 @optimizable4() {
238 define i8 @optimizable4() {
241 store i8 42, ptr %ptr
242 %ptr2 = call ptr @llvm.launder.invariant.group.p0(ptr %ptr)
244 %a = load i8, ptr %ptr2
250 ; CHECK-LABEL: define i8 @volatile1() {
251 define i8 @volatile1() {
254 store i8 42, ptr %ptr, !invariant.group !0
255 call void @foo(ptr %ptr)
256 %a = load i8, ptr %ptr, !invariant.group !0
257 %b = load volatile i8, ptr %ptr
258 ; CHECK: call void @bar(i8 %b)
259 call void @bar(i8 %b)
261 %c = load volatile i8, ptr %ptr, !invariant.group !0
262 ; We might be able to optimize this, but nobody cares
263 ; CHECK: call void @bar(i8 %c)
264 call void @bar(i8 %c)
269 ; CHECK-LABEL: define i8 @volatile2() {
270 define i8 @volatile2() {
273 store i8 42, ptr %ptr, !invariant.group !0
274 call void @foo(ptr %ptr)
275 %a = load i8, ptr %ptr, !invariant.group !0
276 %b = load volatile i8, ptr %ptr
277 ; CHECK: call void @bar(i8 %b)
278 call void @bar(i8 %b)
280 %c = load volatile i8, ptr %ptr, !invariant.group !0
281 ; We might be able to optimize this, but nobody cares
282 ; CHECK: call void @bar(i8 %c)
283 call void @bar(i8 %c)
288 ; CHECK-LABEL: define void @fun() {
292 store i8 42, ptr %ptr, !invariant.group !0
293 call void @foo(ptr %ptr)
295 %a = load i8, ptr %ptr, !invariant.group !0 ; Can assume that value under %ptr didn't change
296 ; CHECK: call void @bar(i8 42)
297 call void @bar(i8 %a)
302 ; FIXME: NewGVN doesn't run instsimplify on a load from a vtable definition?
303 ; This test checks if invariant.group understands gep with zeros
304 ; CHECK-LABEL: define void @testGEP0() {
305 define void @testGEP0() {
306 %a = alloca %struct.A, align 8
307 store ptr getelementptr inbounds ([3 x ptr], ptr @_ZTV1A, i64 0, i64 2), ptr %a, align 8, !invariant.group !0
308 ; CHECK: call void @_ZN1A3fooEv(ptr nonnull dereferenceable(8) %a)
309 call void @_ZN1A3fooEv(ptr nonnull dereferenceable(8) %a) ; This call may change vptr
310 %1 = load i8, ptr @unknownPtr, align 4
311 %2 = icmp eq i8 %1, 0
312 br i1 %2, label %_Z1gR1A.exit, label %3
314 ; This should be devirtualized by invariant.group
315 %4 = load ptr, ptr %a, align 8, !invariant.group !0
316 %5 = load ptr, ptr %4, align 8
317 ; FIXME: call void @_ZN1A3fooEv(ptr nonnull %a)
318 call void %5(ptr nonnull %a)
319 br label %_Z1gR1A.exit
321 _Z1gR1A.exit: ; preds = %0, %3
325 ; Check if no optimizations are performed with global pointers.
326 ; FIXME: we could do the optimizations if we would check if dependency comes
327 ; from the same function.
328 ; CHECK-LABEL: define void @testGlobal() {
329 define void @testGlobal() {
330 ; CHECK: %a = load i8, ptr @unknownPtr, align 1, !invariant.group !0
331 %a = load i8, ptr @unknownPtr, !invariant.group !0
332 call void @foo2(ptr @unknownPtr, i8 %a)
333 ; CHECK: %1 = load i8, ptr @unknownPtr, align 1, !invariant.group !0
334 %1 = load i8, ptr @unknownPtr, !invariant.group !0
335 call void @bar(i8 %1)
337 call void @fooBit(ptr @unknownPtr, i1 1)
338 ; Adding regex because of canonicalization of bitcasts
339 ; CHECK: %2 = load i1, ptr {{.*}}, !invariant.group !0
340 %2 = load i1, ptr @unknownPtr, !invariant.group !0
341 call void @fooBit(ptr @unknownPtr, i1 %2)
342 ; CHECK: %3 = load i1, ptr {{.*}}, !invariant.group !0
343 %3 = load i1, ptr @unknownPtr, !invariant.group !0
344 call void @fooBit(ptr @unknownPtr, i1 %3)
348 ; Might be similar to above where NewGVN doesn't handle loads of different types from the same location.
349 ; Not super important anyway.
350 ; CHECK-LABEL: define void @testTrunc() {
351 define void @testTrunc() {
353 call void @foo(ptr %a)
354 ; CHECK: %b = load i8, ptr %a, align 1, !invariant.group !0
355 %b = load i8, ptr %a, !invariant.group !0
356 call void @foo2(ptr %a, i8 %b)
358 %1 = load i8, ptr %a, !invariant.group !0
359 ; CHECK: call void @bar(i8 %b)
360 call void @bar(i8 %1)
362 call void @fooBit(ptr %a, i1 1)
363 ; FIXME: %1 = trunc i8 %b to i1
364 %2 = load i1, ptr %a, !invariant.group !0
365 ; FIXME-NEXT: call void @fooBit(ptr %a, i1 %1)
366 call void @fooBit(ptr %a, i1 %2)
367 %3 = load i1, ptr %a, !invariant.group !0
368 ; FIXME-NEXT: call void @fooBit(ptr %a, i1 %1)
369 call void @fooBit(ptr %a, i1 %3)
373 ; See comment in @testGEP0 on what NewGVN is lacking.
374 ; CHECK-LABEL: define void @handling_loops()
375 define void @handling_loops() {
376 %a = alloca %struct.A, align 8
377 store ptr getelementptr inbounds ([3 x ptr], ptr @_ZTV1A, i64 0, i64 2), ptr %a, align 8, !invariant.group !0
378 %1 = load i8, ptr @unknownPtr, align 4
379 %2 = icmp sgt i8 %1, 0
380 br i1 %2, label %.lr.ph.i, label %_Z2g2R1A.exit
382 .lr.ph.i: ; preds = %0
383 %3 = load i8, ptr @unknownPtr, align 4
384 %4 = icmp sgt i8 %3, 1
385 br i1 %4, label %._crit_edge.preheader, label %_Z2g2R1A.exit
387 ._crit_edge.preheader: ; preds = %.lr.ph.i
388 br label %._crit_edge
390 ._crit_edge: ; preds = %._crit_edge.preheader, %._crit_edge
391 %5 = phi i8 [ %7, %._crit_edge ], [ 1, %._crit_edge.preheader ]
392 %.pre = load ptr, ptr %a, align 8, !invariant.group !0
393 %6 = load ptr, ptr %.pre, align 8
394 ; FIXME: call void @_ZN1A3fooEv(ptr nonnull %a)
395 call void %6(ptr nonnull %a) #3
396 ; FIXME-NOT: call void %
397 %7 = add nuw nsw i8 %5, 1
398 %8 = load i8, ptr @unknownPtr, align 4
399 %9 = icmp slt i8 %7, %8
400 br i1 %9, label %._crit_edge, label %_Z2g2R1A.exit.loopexit
402 _Z2g2R1A.exit.loopexit: ; preds = %._crit_edge
403 br label %_Z2g2R1A.exit
405 _Z2g2R1A.exit: ; preds = %_Z2g2R1A.exit.loopexit, %.lr.ph.i, %0
410 declare void @foo(ptr)
411 declare void @foo2(ptr, i8)
412 declare void @bar(i8)
413 declare ptr @getPointer(ptr)
414 declare void @_ZN1A3fooEv(ptr)
415 declare void @_ZN1AC1Ev(ptr)
416 declare void @fooBit(ptr, i1)
418 declare ptr @llvm.launder.invariant.group.p0(ptr)
419 declare ptr @llvm.strip.invariant.group.p0(ptr)
421 ; Function Attrs: nounwind
422 declare void @llvm.assume(i1 %cmp.vtables) #0
425 attributes #0 = { nounwind }