summaryrefslogtreecommitdiffstats
path: root/llvm/test/Transforms/NewGVN/invariant.group.ll
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/test/Transforms/NewGVN/invariant.group.ll')
-rw-r--r--llvm/test/Transforms/NewGVN/invariant.group.ll460
1 files changed, 460 insertions, 0 deletions
diff --git a/llvm/test/Transforms/NewGVN/invariant.group.ll b/llvm/test/Transforms/NewGVN/invariant.group.ll
new file mode 100644
index 00000000000..78e61a2f2b6
--- /dev/null
+++ b/llvm/test/Transforms/NewGVN/invariant.group.ll
@@ -0,0 +1,460 @@
+; XFAIL: *
+; RUN: opt < %s -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) {
+; FIXME: The first call could be also removed by GVN. Right now
+; DCE removes it. The second call is CSE'd with the first one.
+; CHECK: %b1 = call i8* @llvm.strip.invariant.group.p0i8(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
+}
+
+; 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*)***
+
+; CHECK: 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*)***
+
+; CHECK: 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
+; CHECK: 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
+; CHECK: call void @_ZN1A3fooEv(
+ call void %13(%struct.A* %9)
+
+ ret void
+}
+
+; 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
+; CHECK-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, !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, !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, !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, !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
+}
+
+; CHECK-LABEL: define i8 @optimizable4() {
+define i8 @optimizable4() {
+entry:
+ %ptr = alloca i8
+ store i8 42, i8* %ptr, !invariant.group !0
+ %ptr2 = call i8* @llvm.launder.invariant.group.p0i8(i8* %ptr)
+; CHECK-NOT: load
+ %a = load i8, i8* %ptr2, !invariant.group !0
+
+; CHECK: 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
+; FIXME: we could change %c to 42, preserving volatile load
+; 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
+; FIXME: we could change %c to 42, preserving volatile load
+; CHECK: call void @bar(i8 %c)
+ call void @bar(i8 %c)
+; CHECK: ret i8 42
+ ret i8 %a
+}
+
+; CHECK-LABEL: define i8 @fun() {
+define i8 @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)
+
+ %newPtr = call i8* @getPointer(i8* %ptr)
+ %c = load i8, i8* %newPtr, !invariant.group !0 ; Can't assume anything, because we only have information about %ptr
+; CHECK: call void @bar(i8 %c)
+ call void @bar(i8 %c)
+
+ %unknownValue = load i8, i8* @unknownPtr
+; FIXME: Can assume that %unknownValue == 42
+; CHECK: store i8 %unknownValue, i8* %ptr, !invariant.group !0
+ store i8 %unknownValue, i8* %ptr, !invariant.group !0
+
+ %newPtr2 = call i8* @llvm.launder.invariant.group.p0i8(i8* %ptr)
+; CHECK-NOT: load
+ %d = load i8, i8* %newPtr2, !invariant.group !0
+; CHECK: ret i8 %unknownValue
+ ret i8 %d
+}
+
+; 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
+; CHECK: 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, !invariant.group !0
+ %a = load i8, i8* @unknownPtr, !invariant.group !0
+ call void @foo2(i8* @unknownPtr, i8 %a)
+; CHECK: %1 = load i8, i8* @unknownPtr, !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
+}
+; And in the case it is not global
+; CHECK-LABEL: define void @testNotGlobal() {
+define void @testNotGlobal() {
+ %a = alloca i8
+ call void @foo(i8* %a)
+; CHECK: %b = load i8, i8* %a, !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)
+; CHECK: %1 = trunc i8 %b to i1
+ %2 = load i1, i1* %b0, !invariant.group !0
+; CHECK-NEXT: call void @fooBit(i1* %b0, i1 %1)
+ call void @fooBit(i1* %b0, i1 %2)
+ %3 = load i1, i1* %b0, !invariant.group !0
+; CHECK-NEXT: call void @fooBit(i1* %b0, i1 %1)
+ call void @fooBit(i1* %b0, i1 %3)
+ ret void
+}
+
+; 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
+ ; CHECK: call void @_ZN1A3fooEv(%struct.A* nonnull %a)
+ call void %9(%struct.A* nonnull %a) #3
+ ; CHECK-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*)
+
+; Function Attrs: nounwind
+declare void @llvm.assume(i1 %cmp.vtables) #0
+
+
+attributes #0 = { nounwind }
+!0 = !{} \ No newline at end of file
OpenPOWER on IntegriCloud