427 lines
12 KiB
LLVM
427 lines
12 KiB
LLVM
|
; RUN: opt < %s -passes=newgvn -S | FileCheck %s
|
||
|
|
||
|
%struct.A = type { ptr }
|
||
|
@_ZTV1A = available_externally unnamed_addr constant [3 x ptr] [ptr null, ptr @_ZTI1A, ptr @_ZN1A3fooEv], align 8
|
||
|
@_ZTI1A = external constant ptr
|
||
|
|
||
|
@unknownPtr = external global i8
|
||
|
|
||
|
; CHECK-LABEL: define i8 @simple() {
|
||
|
define i8 @simple() {
|
||
|
entry:
|
||
|
%ptr = alloca i8
|
||
|
store i8 42, ptr %ptr, !invariant.group !0
|
||
|
call void @foo(ptr %ptr)
|
||
|
|
||
|
%a = load i8, ptr %ptr, !invariant.group !0
|
||
|
%b = load i8, ptr %ptr, !invariant.group !0
|
||
|
%c = load i8, ptr %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, ptr %ptr, !invariant.group !0
|
||
|
%ptr2 = call ptr @llvm.launder.invariant.group.p0(ptr %ptr)
|
||
|
%a = load i8, ptr %ptr, !invariant.group !0
|
||
|
|
||
|
call void @foo(ptr %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, ptr %ptr, !invariant.group !0
|
||
|
call void @foo(ptr %ptr)
|
||
|
|
||
|
store i8 13, ptr %ptr ; can't use this store with invariant.group
|
||
|
%a = load i8, ptr %ptr
|
||
|
call void @bar(i8 %a) ; call to use %a
|
||
|
|
||
|
call void @foo(ptr %ptr)
|
||
|
%b = load i8, ptr %ptr, !invariant.group !0
|
||
|
|
||
|
; CHECK: ret i8 42
|
||
|
ret i8 %b
|
||
|
}
|
||
|
|
||
|
; CHECK-LABEL: define i1 @proveEqualityForStrip(
|
||
|
define i1 @proveEqualityForStrip(ptr %a) {
|
||
|
%b1 = call ptr @llvm.strip.invariant.group.p0(ptr %a)
|
||
|
; CHECK-NOT: llvm.strip.invariant.group
|
||
|
%b2 = call ptr @llvm.strip.invariant.group.p0(ptr %a)
|
||
|
%r = icmp eq ptr %b1, %b2
|
||
|
; CHECK: ret i1 true
|
||
|
ret i1 %r
|
||
|
}
|
||
|
|
||
|
; CHECK-LABEL: define i8 @unoptimizable1() {
|
||
|
define i8 @unoptimizable1() {
|
||
|
entry:
|
||
|
%ptr = alloca i8
|
||
|
store i8 42, ptr %ptr
|
||
|
call void @foo(ptr %ptr)
|
||
|
%a = load i8, ptr %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 ptr, align 8
|
||
|
|
||
|
%call = call ptr @getPointer(ptr null)
|
||
|
call void @_ZN1AC1Ev(ptr %call)
|
||
|
|
||
|
; CHECK: %vtable = load {{.*}} !invariant.group
|
||
|
%vtable = load ptr, ptr %call, align 8, !invariant.group !0
|
||
|
%cmp.vtables = icmp eq ptr %vtable, getelementptr inbounds ([3 x ptr], ptr @_ZTV1A, i64 0, i64 2)
|
||
|
call void @llvm.assume(i1 %cmp.vtables)
|
||
|
|
||
|
store ptr %call, ptr %a, align 8
|
||
|
%0 = load ptr, ptr %a, align 8
|
||
|
|
||
|
; FIXME: call void @_ZN1A3fooEv(
|
||
|
%vtable1 = load ptr, ptr %0, align 8, !invariant.group !0
|
||
|
%1 = load ptr, ptr %vtable1, align 8
|
||
|
call void %1(ptr %0)
|
||
|
%2 = load ptr, ptr %a, align 8
|
||
|
|
||
|
; FIXME: call void @_ZN1A3fooEv(
|
||
|
%vtable2 = load ptr, ptr %2, align 8, !invariant.group !0
|
||
|
%3 = load ptr, ptr %vtable2, align 8
|
||
|
|
||
|
call void %3(ptr %2)
|
||
|
%4 = load ptr, ptr %a, align 8
|
||
|
|
||
|
%vtable4 = load ptr, ptr %4, align 8, !invariant.group !0
|
||
|
%5 = load ptr, ptr %vtable4, align 8
|
||
|
; FIXME: call void @_ZN1A3fooEv(
|
||
|
call void %5(ptr %4)
|
||
|
|
||
|
%vtable5 = load ptr, ptr %call, align 8, !invariant.group !0
|
||
|
%6 = load ptr, ptr %vtable5, align 8
|
||
|
; FIXME: call void @_ZN1A3fooEv(
|
||
|
call void %6(ptr %4)
|
||
|
|
||
|
ret void
|
||
|
}
|
||
|
|
||
|
; NewGVN won't CSE loads with different pointee types.
|
||
|
; CHECK-LABEL: define void @combiningBitCastWithLoad() {
|
||
|
define void @combiningBitCastWithLoad() {
|
||
|
entry:
|
||
|
%a = alloca ptr, align 8
|
||
|
|
||
|
%call = call ptr @getPointer(ptr null)
|
||
|
call void @_ZN1AC1Ev(ptr %call)
|
||
|
|
||
|
; CHECK: %vtable = load {{.*}} !invariant.group
|
||
|
%vtable = load ptr, ptr %call, align 8, !invariant.group !0
|
||
|
%cmp.vtables = icmp eq ptr %vtable, getelementptr inbounds ([3 x ptr], ptr @_ZTV1A, i64 0, i64 2)
|
||
|
|
||
|
store ptr %call, ptr %a, align 8
|
||
|
; FIXME-NOT: !invariant.group
|
||
|
%0 = load ptr, ptr %a, align 8
|
||
|
|
||
|
%vtable1 = load ptr, ptr %0, align 8, !invariant.group !0
|
||
|
%1 = load ptr, ptr %vtable1, align 8
|
||
|
call void %1(ptr %0)
|
||
|
|
||
|
ret void
|
||
|
}
|
||
|
|
||
|
; CHECK-LABEL:define void @loadCombine() {
|
||
|
define void @loadCombine() {
|
||
|
enter:
|
||
|
%ptr = alloca i8
|
||
|
store i8 42, ptr %ptr
|
||
|
call void @foo(ptr %ptr)
|
||
|
; CHECK: %[[A:.*]] = load i8, ptr %ptr, align 1, !invariant.group
|
||
|
%a = load i8, ptr %ptr, !invariant.group !0
|
||
|
; CHECK-NOT: load
|
||
|
%b = load i8, ptr %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, ptr %ptr
|
||
|
call void @foo(ptr %ptr)
|
||
|
; CHECK: %[[D:.*]] = load i8, ptr %ptr, align 1, !invariant.group
|
||
|
%c = load i8, ptr %ptr
|
||
|
; CHECK-NOT: load
|
||
|
%d = load i8, ptr %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, ptr %ptr
|
||
|
call void @foo(ptr %ptr)
|
||
|
; CHECK: %[[E:.*]] = load i8, ptr %ptr, align 1, !invariant.group
|
||
|
%e = load i8, ptr %ptr, !invariant.group !0
|
||
|
; CHECK-NOT: load
|
||
|
%f = load i8, ptr %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, ptr %ptr
|
||
|
call void @foo(ptr %ptr)
|
||
|
; CHECK: %[[E:.*]] = load i8, ptr %ptr, align 1, !invariant.group
|
||
|
%e = load i8, ptr %ptr, !invariant.group !0
|
||
|
; CHECK-NOT: load
|
||
|
%f = load i8, ptr %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, ptr %ptr
|
||
|
call void @foo(ptr %ptr)
|
||
|
%a = load i8, ptr %ptr
|
||
|
call void @foo(ptr %ptr)
|
||
|
%b = load i8, ptr %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, ptr %ptr, !invariant.group !0
|
||
|
%ptr2 = call ptr @getPointer(ptr %ptr)
|
||
|
%a = load i8, ptr %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, ptr %ptr
|
||
|
%ptr2 = call ptr @llvm.launder.invariant.group.p0(ptr %ptr)
|
||
|
; FIXME-NOT: load
|
||
|
%a = load i8, ptr %ptr2
|
||
|
|
||
|
; FIXME: ret i8 42
|
||
|
ret i8 %a
|
||
|
}
|
||
|
|
||
|
; CHECK-LABEL: define i8 @volatile1() {
|
||
|
define i8 @volatile1() {
|
||
|
entry:
|
||
|
%ptr = alloca i8
|
||
|
store i8 42, ptr %ptr, !invariant.group !0
|
||
|
call void @foo(ptr %ptr)
|
||
|
%a = load i8, ptr %ptr, !invariant.group !0
|
||
|
%b = load volatile i8, ptr %ptr
|
||
|
; CHECK: call void @bar(i8 %b)
|
||
|
call void @bar(i8 %b)
|
||
|
|
||
|
%c = load volatile i8, ptr %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, ptr %ptr, !invariant.group !0
|
||
|
call void @foo(ptr %ptr)
|
||
|
%a = load i8, ptr %ptr, !invariant.group !0
|
||
|
%b = load volatile i8, ptr %ptr
|
||
|
; CHECK: call void @bar(i8 %b)
|
||
|
call void @bar(i8 %b)
|
||
|
|
||
|
%c = load volatile i8, ptr %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, ptr %ptr, !invariant.group !0
|
||
|
call void @foo(ptr %ptr)
|
||
|
|
||
|
%a = load i8, ptr %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
|
||
|
store ptr getelementptr inbounds ([3 x ptr], ptr @_ZTV1A, i64 0, i64 2), ptr %a, align 8, !invariant.group !0
|
||
|
; CHECK: call void @_ZN1A3fooEv(ptr nonnull dereferenceable(8) %a)
|
||
|
call void @_ZN1A3fooEv(ptr nonnull dereferenceable(8) %a) ; This call may change vptr
|
||
|
%1 = load i8, ptr @unknownPtr, align 4
|
||
|
%2 = icmp eq i8 %1, 0
|
||
|
br i1 %2, label %_Z1gR1A.exit, label %3
|
||
|
|
||
|
; This should be devirtualized by invariant.group
|
||
|
%4 = load ptr, ptr %a, align 8, !invariant.group !0
|
||
|
%5 = load ptr, ptr %4, align 8
|
||
|
; FIXME: call void @_ZN1A3fooEv(ptr nonnull %a)
|
||
|
call void %5(ptr nonnull %a)
|
||
|
br label %_Z1gR1A.exit
|
||
|
|
||
|
_Z1gR1A.exit: ; preds = %0, %3
|
||
|
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, ptr @unknownPtr, align 1, !invariant.group !0
|
||
|
%a = load i8, ptr @unknownPtr, !invariant.group !0
|
||
|
call void @foo2(ptr @unknownPtr, i8 %a)
|
||
|
; CHECK: %1 = load i8, ptr @unknownPtr, align 1, !invariant.group !0
|
||
|
%1 = load i8, ptr @unknownPtr, !invariant.group !0
|
||
|
call void @bar(i8 %1)
|
||
|
|
||
|
call void @fooBit(ptr @unknownPtr, i1 1)
|
||
|
; Adding regex because of canonicalization of bitcasts
|
||
|
; CHECK: %2 = load i1, ptr {{.*}}, !invariant.group !0
|
||
|
%2 = load i1, ptr @unknownPtr, !invariant.group !0
|
||
|
call void @fooBit(ptr @unknownPtr, i1 %2)
|
||
|
; CHECK: %3 = load i1, ptr {{.*}}, !invariant.group !0
|
||
|
%3 = load i1, ptr @unknownPtr, !invariant.group !0
|
||
|
call void @fooBit(ptr @unknownPtr, 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(ptr %a)
|
||
|
; CHECK: %b = load i8, ptr %a, align 1, !invariant.group !0
|
||
|
%b = load i8, ptr %a, !invariant.group !0
|
||
|
call void @foo2(ptr %a, i8 %b)
|
||
|
|
||
|
%1 = load i8, ptr %a, !invariant.group !0
|
||
|
; CHECK: call void @bar(i8 %b)
|
||
|
call void @bar(i8 %1)
|
||
|
|
||
|
call void @fooBit(ptr %a, i1 1)
|
||
|
; FIXME: %1 = trunc i8 %b to i1
|
||
|
%2 = load i1, ptr %a, !invariant.group !0
|
||
|
; FIXME-NEXT: call void @fooBit(ptr %a, i1 %1)
|
||
|
call void @fooBit(ptr %a, i1 %2)
|
||
|
%3 = load i1, ptr %a, !invariant.group !0
|
||
|
; FIXME-NEXT: call void @fooBit(ptr %a, i1 %1)
|
||
|
call void @fooBit(ptr %a, 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
|
||
|
store ptr getelementptr inbounds ([3 x ptr], ptr @_ZTV1A, i64 0, i64 2), ptr %a, align 8, !invariant.group !0
|
||
|
%1 = load i8, ptr @unknownPtr, align 4
|
||
|
%2 = icmp sgt i8 %1, 0
|
||
|
br i1 %2, label %.lr.ph.i, label %_Z2g2R1A.exit
|
||
|
|
||
|
.lr.ph.i: ; preds = %0
|
||
|
%3 = load i8, ptr @unknownPtr, align 4
|
||
|
%4 = icmp sgt i8 %3, 1
|
||
|
br i1 %4, 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
|
||
|
%5 = phi i8 [ %7, %._crit_edge ], [ 1, %._crit_edge.preheader ]
|
||
|
%.pre = load ptr, ptr %a, align 8, !invariant.group !0
|
||
|
%6 = load ptr, ptr %.pre, align 8
|
||
|
; FIXME: call void @_ZN1A3fooEv(ptr nonnull %a)
|
||
|
call void %6(ptr nonnull %a) #3
|
||
|
; FIXME-NOT: call void %
|
||
|
%7 = add nuw nsw i8 %5, 1
|
||
|
%8 = load i8, ptr @unknownPtr, align 4
|
||
|
%9 = icmp slt i8 %7, %8
|
||
|
br i1 %9, 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(ptr)
|
||
|
declare void @foo2(ptr, i8)
|
||
|
declare void @bar(i8)
|
||
|
declare ptr @getPointer(ptr)
|
||
|
declare void @_ZN1A3fooEv(ptr)
|
||
|
declare void @_ZN1AC1Ev(ptr)
|
||
|
declare void @fooBit(ptr, i1)
|
||
|
|
||
|
declare ptr @llvm.launder.invariant.group.p0(ptr)
|
||
|
declare ptr @llvm.strip.invariant.group.p0(ptr)
|
||
|
|
||
|
; Function Attrs: nounwind
|
||
|
declare void @llvm.assume(i1 %cmp.vtables) #0
|
||
|
|
||
|
|
||
|
attributes #0 = { nounwind }
|
||
|
!0 = !{}
|