Make the range check more precise by calculating the range of potentially accessed bytes for both accesses and checking whether their intersection is empty. In that case there can be no overlap between the accesses and the result is NoAlias. This is more powerful than the previous approach, because it can deal with sign-wrapped ranges. In the test case the original range is [-1, INT_MAX] but becomes [0, INT_MIN] after applying the offset. This is a wrapping range, so getSignedMin/getSignedMax will treat it as a full range. However, the range excludes the elements [INT_MIN+1, -1], which is enough to prove NoAlias with an access at offset -1. Differential Revision: https://reviews.llvm.org/D112486
226 lines
8.5 KiB
LLVM
226 lines
8.5 KiB
LLVM
; RUN: opt < %s -basic-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
|
|
|
%struct.S = type { i32, [2 x i32], i32 }
|
|
%struct.S2 = type { i32, [4 x i32], [4 x i32] }
|
|
|
|
; CHECK: Function: t1
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @t1(%struct.S* %s) {
|
|
%gep1 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 1
|
|
%gep2 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 0
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t2_fwd
|
|
; CHECK: MayAlias: i32* %gep1, i32* %gep2
|
|
define void @t2_fwd(%struct.S* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !0
|
|
%gep1 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 0
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t2_rev
|
|
; CHECK: MayAlias: i32* %gep1, i32* %gep2
|
|
define void @t2_rev(%struct.S* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !0
|
|
%gep1 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 0
|
|
%gep2 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 %in_array
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t3_fwd
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @t3_fwd(%struct.S* %s, i32* %q) {
|
|
%knownzero = load i32, i32* %q, !range !1
|
|
%gep1 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 %knownzero
|
|
%gep2 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 1
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t3_rev
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @t3_rev(%struct.S* %s, i32* %q) {
|
|
%knownzero = load i32, i32* %q, !range !1
|
|
%gep1 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 1
|
|
%gep2 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 %knownzero
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: member_after
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @member_after(%struct.S* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !0
|
|
%gep1 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 2
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: member_after_rev
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @member_after_rev(%struct.S* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !0
|
|
%gep2 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 2
|
|
%gep1 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 %in_array
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: member_before
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @member_before(%struct.S* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !0
|
|
%gep1 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 0
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: member_before_rev
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @member_before_rev(%struct.S* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !0
|
|
%gep2 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 0
|
|
%gep1 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 1, i32 %in_array
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t5
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %q
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %gep1
|
|
; CHECK-NEXT: MayAlias: i32* %gep1, i32* %q
|
|
; CHECK-NEXT: PartialAlias (off 4): %struct.S2* %s, i32* %gep2
|
|
; CHECK-NEXT: MayAlias: i32* %gep2, i32* %q
|
|
; CHECK-NEXT: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @t5(%struct.S2* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !3
|
|
%gep1 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 2, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 1, i32 0
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t6
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %q
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %gep1
|
|
; CHECK-NEXT: MayAlias: i32* %gep1, i32* %q
|
|
; CHECK-NEXT: PartialAlias (off 16): %struct.S2* %s, i32* %gep2
|
|
; CHECK-NEXT: MayAlias: i32* %gep2, i32* %q
|
|
; CHECK-NEXT: MayAlias: i32* %gep1, i32* %gep2
|
|
define void @t6(%struct.S2* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !3
|
|
%gep1 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 2, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 1, i32 3
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t7
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %q
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %gep1
|
|
; CHECK-NEXT: MayAlias: i32* %gep1, i32* %q
|
|
; CHECK-NEXT: PartialAlias (off 20): %struct.S2* %s, i32* %gep2
|
|
; CHECK-NEXT: MayAlias: i32* %gep2, i32* %q
|
|
; CHECK-NEXT: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @t7(%struct.S2* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !4
|
|
%gep1 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 2, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 2, i32 0
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t8
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %q
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %gep1
|
|
; CHECK-NEXT: MayAlias: i32* %gep1, i32* %q
|
|
; CHECK-NEXT: PartialAlias (off 24): %struct.S2* %s, i32* %gep2
|
|
; CHECK-NEXT: MayAlias: i32* %gep2, i32* %q
|
|
; CHECK-NEXT: MayAlias: i32* %gep1, i32* %gep2
|
|
define void @t8(%struct.S2* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !4
|
|
%gep1 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 2, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 2, i32 1
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t9
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %q
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %gep1
|
|
; CHECK-NEXT: MayAlias: i32* %gep1, i32* %q
|
|
; CHECK-NEXT: PartialAlias (off 20): %struct.S2* %s, i32* %gep2
|
|
; CHECK-NEXT: MayAlias: i32* %gep2, i32* %q
|
|
; CHECK-NEXT: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @t9(%struct.S2* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !5
|
|
%gep1 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 1, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 2, i32 0
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t10
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %q
|
|
; CHECK-NEXT: MayAlias: %struct.S2* %s, i32* %gep1
|
|
; CHECK-NEXT: MayAlias: i32* %gep1, i32* %q
|
|
; CHECK-NEXT: PartialAlias (off 4): %struct.S2* %s, i32* %gep2
|
|
; CHECK-NEXT: MayAlias: i32* %gep2, i32* %q
|
|
; CHECK-NEXT: MayAlias: i32* %gep1, i32* %gep2
|
|
define void @t10(%struct.S2* %s, i32* %q) {
|
|
%in_array = load i32, i32* %q, !range !5
|
|
%gep1 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 2, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, %struct.S2* %s, i64 0, i32 1, i32 0
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: zeroext_index
|
|
; CHECK-NEXT: MayAlias: [256 x i32]* %s, i8* %q
|
|
; CHECK-NEXT: MayAlias: [256 x i32]* %s, i32* %gep
|
|
; CHECK-NEXT: MayAlias: i32* %gep, i8* %q
|
|
define void @zeroext_index([256 x i32]* %s, i8* %q) {
|
|
%a = load i8, i8* %q, !range !6
|
|
%in_array = zext i8 %a to i32
|
|
%gep = getelementptr inbounds [256 x i32], [256 x i32]* %s, i64 0, i32 %in_array
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: Function: multiple
|
|
; CHECK: MayAlias: i32* %p, i32* %p.01
|
|
; CHECK: MayAlias: i32* %p, i32* %p.02
|
|
; CHECK: MayAlias: i32* %p.01, i32* %p.02
|
|
; CHECK: NoAlias: i32* %p.01, i32* %p.2
|
|
; CHECK: MayAlias: i32* %p.02, i32* %p.2
|
|
; CHECK: NoAlias: i32* %p.01, i32* %p.3
|
|
; CHECK: NoAlias: i32* %p.02, i32* %p.3
|
|
define void @multiple(i32* %p, i32* %o1_ptr, i32* %o2_ptr) {
|
|
%o1 = load i32, i32* %o1_ptr, !range !0
|
|
%o2 = load i32, i32* %o2_ptr, !range !0
|
|
%p.01 = getelementptr i32, i32* %p, i32 %o1 ; p + [0, 1]
|
|
%p.02 = getelementptr i32, i32* %p.01, i32 %o2 ; p + [0, 2]
|
|
%p.2 = getelementptr i32, i32* %p, i32 2
|
|
%p.3 = getelementptr i32, i32* %p, i32 3
|
|
ret void
|
|
}
|
|
|
|
; p.neg1 and p.o.1 don't alias, even though the addition o+1 may overflow.
|
|
; While it makes INT_MIN a possible offset, offset -1 is not possible.
|
|
; CHECK-LABEL: Function: benign_overflow
|
|
; CHECK: MayAlias: i8* %p, i8* %p.o
|
|
; CHECK: MayAlias: i8* %p.neg1, i8* %p.o
|
|
; CHECK: MayAlias: i8* %p, i8* %p.o.1
|
|
; CHECK: NoAlias: i8* %p.neg1, i8* %p.o.1
|
|
; CHECK: NoAlias: i8* %p.o, i8* %p.o.1
|
|
define void @benign_overflow(i8* %p, i64 %o) {
|
|
%c = icmp sge i64 %o, -1
|
|
call void @llvm.assume(i1 %c)
|
|
%p.neg1 = getelementptr i8, i8* %p, i64 -1
|
|
%p.o = getelementptr i8, i8* %p, i64 %o
|
|
%p.o.1 = getelementptr i8, i8* %p.o, i64 1
|
|
ret void
|
|
}
|
|
|
|
declare void @llvm.assume(i1)
|
|
|
|
|
|
!0 = !{ i32 0, i32 2 }
|
|
!1 = !{ i32 0, i32 1 }
|
|
!2 = !{ i32 1, i32 2 }
|
|
!3 = !{ i32 -2, i32 0 }
|
|
!4 = !{ i32 1, i32 536870911 }
|
|
!5 = !{ i32 -536870911, i32 4 }
|
|
!6 = !{ i8 -2, i8 0 }
|