; RUN: opt < %s -passes=newgvn -S | FileCheck %s %struct.A = type { i32 (...)** } @_ZTV1A = available_externally unnamed_addr constant [3 x i8*] [i8* null, i8* bitcast (i8** @_ZTI1A to i8*), i8* bitcast (void (%struct.A*)* @_ZN1A3fooEv to i8*)], align 8 @_ZTI1A = external constant i8* @unknownPtr = external global i8 ; CHECK-LABEL: define i8 @simple() { define i8 @simple() { entry: %ptr = alloca i8 store i8 42, i8* %ptr, !invariant.group !0 call void @foo(i8* %ptr) %a = load i8, i8* %ptr, !invariant.group !0 %b = load i8, i8* %ptr, !invariant.group !0 %c = load i8, i8* %ptr, !invariant.group !0 ; CHECK: ret i8 42 ret i8 %a } ; CHECK-LABEL: define i8 @optimizable1() { define i8 @optimizable1() { entry: %ptr = alloca i8 store i8 42, i8* %ptr, !invariant.group !0 %ptr2 = call i8* @llvm.launder.invariant.group.p0i8(i8* %ptr) %a = load i8, i8* %ptr, !invariant.group !0 call void @foo(i8* %ptr2); call to use %ptr2 ; CHECK: ret i8 42 ret i8 %a } ; CHECK-LABEL: define i8 @optimizable2() { define i8 @optimizable2() { entry: %ptr = alloca i8 store i8 42, i8* %ptr, !invariant.group !0 call void @foo(i8* %ptr) store i8 13, i8* %ptr ; can't use this store with invariant.group %a = load i8, i8* %ptr call void @bar(i8 %a) ; call to use %a call void @foo(i8* %ptr) %b = load i8, i8* %ptr, !invariant.group !0 ; CHECK: ret i8 42 ret i8 %b } ; CHECK-LABEL: define i1 @proveEqualityForStrip( define i1 @proveEqualityForStrip(i8* %a) { %b1 = call i8* @llvm.strip.invariant.group.p0i8(i8* %a) ; CHECK-NOT: llvm.strip.invariant.group %b2 = call i8* @llvm.strip.invariant.group.p0i8(i8* %a) %r = icmp eq i8* %b1, %b2 ; CHECK: ret i1 true ret i1 %r } ; CHECK-LABEL: define i8 @unoptimizable1() { define i8 @unoptimizable1() { entry: %ptr = alloca i8 store i8 42, i8* %ptr call void @foo(i8* %ptr) %a = load i8, i8* %ptr, !invariant.group !0 ; CHECK: ret i8 %a ret i8 %a } ; NewGVN doesn't support assumes. ; CHECK-LABEL: define void @indirectLoads() { define void @indirectLoads() { entry: %a = alloca %struct.A*, align 8 %0 = bitcast %struct.A** %a to i8* %call = call i8* @getPointer(i8* null) %1 = bitcast i8* %call to %struct.A* call void @_ZN1AC1Ev(%struct.A* %1) %2 = bitcast %struct.A* %1 to i8*** ; CHECK: %vtable = load {{.*}} !invariant.group %vtable = load i8**, i8*** %2, align 8, !invariant.group !0 %cmp.vtables = icmp eq i8** %vtable, getelementptr inbounds ([3 x i8*], [3 x i8*]* @_ZTV1A, i64 0, i64 2) call void @llvm.assume(i1 %cmp.vtables) store %struct.A* %1, %struct.A** %a, align 8 %3 = load %struct.A*, %struct.A** %a, align 8 %4 = bitcast %struct.A* %3 to void (%struct.A*)*** ; FIXME: call void @_ZN1A3fooEv( %vtable1 = load void (%struct.A*)**, void (%struct.A*)*** %4, align 8, !invariant.group !0 %vfn = getelementptr inbounds void (%struct.A*)*, void (%struct.A*)** %vtable1, i64 0 %5 = load void (%struct.A*)*, void (%struct.A*)** %vfn, align 8 call void %5(%struct.A* %3) %6 = load %struct.A*, %struct.A** %a, align 8 %7 = bitcast %struct.A* %6 to void (%struct.A*)*** ; FIXME: call void @_ZN1A3fooEv( %vtable2 = load void (%struct.A*)**, void (%struct.A*)*** %7, align 8, !invariant.group !0 %vfn3 = getelementptr inbounds void (%struct.A*)*, void (%struct.A*)** %vtable2, i64 0 %8 = load void (%struct.A*)*, void (%struct.A*)** %vfn3, align 8 call void %8(%struct.A* %6) %9 = load %struct.A*, %struct.A** %a, align 8 %10 = bitcast %struct.A* %9 to void (%struct.A*)*** %vtable4 = load void (%struct.A*)**, void (%struct.A*)*** %10, align 8, !invariant.group !0 %vfn5 = getelementptr inbounds void (%struct.A*)*, void (%struct.A*)** %vtable4, i64 0 %11 = load void (%struct.A*)*, void (%struct.A*)** %vfn5, align 8 ; FIXME: call void @_ZN1A3fooEv( call void %11(%struct.A* %9) %vtable5 = load i8**, i8*** %2, align 8, !invariant.group !0 %vfn6 = getelementptr inbounds i8*, i8** %vtable5, i64 0 %12 = bitcast i8** %vfn6 to void (%struct.A*)** %13 = load void (%struct.A*)*, void (%struct.A*)** %12, align 8 ; FIXME: call void @_ZN1A3fooEv( call void %13(%struct.A* %9) ret void } ; NewGVN won't CSE loads with different pointee types. ; CHECK-LABEL: define void @combiningBitCastWithLoad() { define void @combiningBitCastWithLoad() { entry: %a = alloca %struct.A*, align 8 %0 = bitcast %struct.A** %a to i8* %call = call i8* @getPointer(i8* null) %1 = bitcast i8* %call to %struct.A* call void @_ZN1AC1Ev(%struct.A* %1) %2 = bitcast %struct.A* %1 to i8*** ; CHECK: %vtable = load {{.*}} !invariant.group %vtable = load i8**, i8*** %2, align 8, !invariant.group !0 %cmp.vtables = icmp eq i8** %vtable, getelementptr inbounds ([3 x i8*], [3 x i8*]* @_ZTV1A, i64 0, i64 2) store %struct.A* %1, %struct.A** %a, align 8 ; FIXME-NOT: !invariant.group %3 = load %struct.A*, %struct.A** %a, align 8 %4 = bitcast %struct.A* %3 to void (%struct.A*)*** %vtable1 = load void (%struct.A*)**, void (%struct.A*)*** %4, align 8, !invariant.group !0 %vfn = getelementptr inbounds void (%struct.A*)*, void (%struct.A*)** %vtable1, i64 0 %5 = load void (%struct.A*)*, void (%struct.A*)** %vfn, align 8 call void %5(%struct.A* %3) ret void } ; CHECK-LABEL:define void @loadCombine() { define void @loadCombine() { enter: %ptr = alloca i8 store i8 42, i8* %ptr call void @foo(i8* %ptr) ; CHECK: %[[A:.*]] = load i8, i8* %ptr, align 1, !invariant.group %a = load i8, i8* %ptr, !invariant.group !0 ; CHECK-NOT: load %b = load i8, i8* %ptr, !invariant.group !0 ; CHECK: call void @bar(i8 %[[A]]) call void @bar(i8 %a) ; CHECK: call void @bar(i8 %[[A]]) call void @bar(i8 %b) ret void } ; CHECK-LABEL: define void @loadCombine1() { define void @loadCombine1() { enter: %ptr = alloca i8 store i8 42, i8* %ptr call void @foo(i8* %ptr) ; CHECK: %[[D:.*]] = load i8, i8* %ptr, align 1, !invariant.group %c = load i8, i8* %ptr ; CHECK-NOT: load %d = load i8, i8* %ptr, !invariant.group !0 ; CHECK: call void @bar(i8 %[[D]]) call void @bar(i8 %c) ; CHECK: call void @bar(i8 %[[D]]) call void @bar(i8 %d) ret void } ; CHECK-LABEL: define void @loadCombine2() { define void @loadCombine2() { enter: %ptr = alloca i8 store i8 42, i8* %ptr call void @foo(i8* %ptr) ; CHECK: %[[E:.*]] = load i8, i8* %ptr, align 1, !invariant.group %e = load i8, i8* %ptr, !invariant.group !0 ; CHECK-NOT: load %f = load i8, i8* %ptr ; CHECK: call void @bar(i8 %[[E]]) call void @bar(i8 %e) ; CHECK: call void @bar(i8 %[[E]]) call void @bar(i8 %f) ret void } ; CHECK-LABEL: define void @loadCombine3() { define void @loadCombine3() { enter: %ptr = alloca i8 store i8 42, i8* %ptr call void @foo(i8* %ptr) ; CHECK: %[[E:.*]] = load i8, i8* %ptr, align 1, !invariant.group %e = load i8, i8* %ptr, !invariant.group !0 ; CHECK-NOT: load %f = load i8, i8* %ptr, !invariant.group !0 ; CHECK: call void @bar(i8 %[[E]]) call void @bar(i8 %e) ; CHECK: call void @bar(i8 %[[E]]) call void @bar(i8 %f) ret void } ; CHECK-LABEL: define i8 @unoptimizable2() { define i8 @unoptimizable2() { entry: %ptr = alloca i8 store i8 42, i8* %ptr call void @foo(i8* %ptr) %a = load i8, i8* %ptr call void @foo(i8* %ptr) %b = load i8, i8* %ptr, !invariant.group !0 ; CHECK: ret i8 %a ret i8 %a } ; CHECK-LABEL: define i8 @unoptimizable3() { define i8 @unoptimizable3() { entry: %ptr = alloca i8 store i8 42, i8* %ptr, !invariant.group !0 %ptr2 = call i8* @getPointer(i8* %ptr) %a = load i8, i8* %ptr2, !invariant.group !0 ; CHECK: ret i8 %a ret i8 %a } ; NewGVN cares about the launder for some reason. ; CHECK-LABEL: define i8 @optimizable4() { define i8 @optimizable4() { entry: %ptr = alloca i8 store i8 42, i8* %ptr %ptr2 = call i8* @llvm.launder.invariant.group.p0i8(i8* %ptr) ; FIXME-NOT: load %a = load i8, i8* %ptr2 ; FIXME: ret i8 42 ret i8 %a } ; CHECK-LABEL: define i8 @volatile1() { define i8 @volatile1() { entry: %ptr = alloca i8 store i8 42, i8* %ptr, !invariant.group !0 call void @foo(i8* %ptr) %a = load i8, i8* %ptr, !invariant.group !0 %b = load volatile i8, i8* %ptr ; CHECK: call void @bar(i8 %b) call void @bar(i8 %b) %c = load volatile i8, i8* %ptr, !invariant.group !0 ; We might be able to optimize this, but nobody cares ; CHECK: call void @bar(i8 %c) call void @bar(i8 %c) ; CHECK: ret i8 42 ret i8 %a } ; CHECK-LABEL: define i8 @volatile2() { define i8 @volatile2() { entry: %ptr = alloca i8 store i8 42, i8* %ptr, !invariant.group !0 call void @foo(i8* %ptr) %a = load i8, i8* %ptr, !invariant.group !0 %b = load volatile i8, i8* %ptr ; CHECK: call void @bar(i8 %b) call void @bar(i8 %b) %c = load volatile i8, i8* %ptr, !invariant.group !0 ; We might be able to optimize this, but nobody cares ; CHECK: call void @bar(i8 %c) call void @bar(i8 %c) ; CHECK: ret i8 42 ret i8 %a } ; CHECK-LABEL: define void @fun() { define void @fun() { entry: %ptr = alloca i8 store i8 42, i8* %ptr, !invariant.group !0 call void @foo(i8* %ptr) %a = load i8, i8* %ptr, !invariant.group !0 ; Can assume that value under %ptr didn't change ; CHECK: call void @bar(i8 42) call void @bar(i8 %a) ret void } ; FIXME: NewGVN doesn't run instsimplify on a load from a vtable definition? ; This test checks if invariant.group understands gep with zeros ; CHECK-LABEL: define void @testGEP0() { define void @testGEP0() { %a = alloca %struct.A, align 8 %1 = bitcast %struct.A* %a to i8* %2 = getelementptr inbounds %struct.A, %struct.A* %a, i64 0, i32 0 store i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @_ZTV1A, i64 0, i64 2) to i32 (...)**), i32 (...)*** %2, align 8, !invariant.group !0 ; CHECK: call void @_ZN1A3fooEv(%struct.A* nonnull dereferenceable(8) %a) call void @_ZN1A3fooEv(%struct.A* nonnull dereferenceable(8) %a) ; This call may change vptr %3 = load i8, i8* @unknownPtr, align 4 %4 = icmp eq i8 %3, 0 br i1 %4, label %_Z1gR1A.exit, label %5 ; This should be devirtualized by invariant.group %6 = bitcast %struct.A* %a to void (%struct.A*)*** %7 = load void (%struct.A*)**, void (%struct.A*)*** %6, align 8, !invariant.group !0 %8 = load void (%struct.A*)*, void (%struct.A*)** %7, align 8 ; FIXME: call void @_ZN1A3fooEv(%struct.A* nonnull %a) call void %8(%struct.A* nonnull %a) br label %_Z1gR1A.exit _Z1gR1A.exit: ; preds = %0, %5 ret void } ; Check if no optimizations are performed with global pointers. ; FIXME: we could do the optimizations if we would check if dependency comes ; from the same function. ; CHECK-LABEL: define void @testGlobal() { define void @testGlobal() { ; CHECK: %a = load i8, i8* @unknownPtr, align 1, !invariant.group !0 %a = load i8, i8* @unknownPtr, !invariant.group !0 call void @foo2(i8* @unknownPtr, i8 %a) ; CHECK: %1 = load i8, i8* @unknownPtr, align 1, !invariant.group !0 %1 = load i8, i8* @unknownPtr, !invariant.group !0 call void @bar(i8 %1) %b0 = bitcast i8* @unknownPtr to i1* call void @fooBit(i1* %b0, i1 1) ; Adding regex because of canonicalization of bitcasts ; CHECK: %2 = load i1, i1* {{.*}}, !invariant.group !0 %2 = load i1, i1* %b0, !invariant.group !0 call void @fooBit(i1* %b0, i1 %2) ; CHECK: %3 = load i1, i1* {{.*}}, !invariant.group !0 %3 = load i1, i1* %b0, !invariant.group !0 call void @fooBit(i1* %b0, i1 %3) ret void } ; Might be similar to above where NewGVN doesn't handle loads of different types from the same location. ; Not super important anyway. ; CHECK-LABEL: define void @testTrunc() { define void @testTrunc() { %a = alloca i8 call void @foo(i8* %a) ; CHECK: %b = load i8, i8* %a, align 1, !invariant.group !0 %b = load i8, i8* %a, !invariant.group !0 call void @foo2(i8* %a, i8 %b) %1 = load i8, i8* %a, !invariant.group !0 ; CHECK: call void @bar(i8 %b) call void @bar(i8 %1) %b0 = bitcast i8* %a to i1* call void @fooBit(i1* %b0, i1 1) ; FIXME: %1 = trunc i8 %b to i1 %2 = load i1, i1* %b0, !invariant.group !0 ; FIXME-NEXT: call void @fooBit(i1* %b0, i1 %1) call void @fooBit(i1* %b0, i1 %2) %3 = load i1, i1* %b0, !invariant.group !0 ; FIXME-NEXT: call void @fooBit(i1* %b0, i1 %1) call void @fooBit(i1* %b0, i1 %3) ret void } ; See comment in @testGEP0 on what NewGVN is lacking. ; CHECK-LABEL: define void @handling_loops() define void @handling_loops() { %a = alloca %struct.A, align 8 %1 = bitcast %struct.A* %a to i8* %2 = getelementptr inbounds %struct.A, %struct.A* %a, i64 0, i32 0 store i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @_ZTV1A, i64 0, i64 2) to i32 (...)**), i32 (...)*** %2, align 8, !invariant.group !0 %3 = load i8, i8* @unknownPtr, align 4 %4 = icmp sgt i8 %3, 0 br i1 %4, label %.lr.ph.i, label %_Z2g2R1A.exit .lr.ph.i: ; preds = %0 %5 = bitcast %struct.A* %a to void (%struct.A*)*** %6 = load i8, i8* @unknownPtr, align 4 %7 = icmp sgt i8 %6, 1 br i1 %7, label %._crit_edge.preheader, label %_Z2g2R1A.exit ._crit_edge.preheader: ; preds = %.lr.ph.i br label %._crit_edge ._crit_edge: ; preds = %._crit_edge.preheader, %._crit_edge %8 = phi i8 [ %10, %._crit_edge ], [ 1, %._crit_edge.preheader ] %.pre = load void (%struct.A*)**, void (%struct.A*)*** %5, align 8, !invariant.group !0 %9 = load void (%struct.A*)*, void (%struct.A*)** %.pre, align 8 ; FIXME: call void @_ZN1A3fooEv(%struct.A* nonnull %a) call void %9(%struct.A* nonnull %a) #3 ; FIXME-NOT: call void % %10 = add nuw nsw i8 %8, 1 %11 = load i8, i8* @unknownPtr, align 4 %12 = icmp slt i8 %10, %11 br i1 %12, label %._crit_edge, label %_Z2g2R1A.exit.loopexit _Z2g2R1A.exit.loopexit: ; preds = %._crit_edge br label %_Z2g2R1A.exit _Z2g2R1A.exit: ; preds = %_Z2g2R1A.exit.loopexit, %.lr.ph.i, %0 ret void } declare void @foo(i8*) declare void @foo2(i8*, i8) declare void @bar(i8) declare i8* @getPointer(i8*) declare void @_ZN1A3fooEv(%struct.A*) declare void @_ZN1AC1Ev(%struct.A*) declare void @fooBit(i1*, i1) declare i8* @llvm.launder.invariant.group.p0i8(i8*) declare i8* @llvm.strip.invariant.group.p0i8(i8*) ; Function Attrs: nounwind declare void @llvm.assume(i1 %cmp.vtables) #0 attributes #0 = { nounwind } !0 = !{}