We currently preserve the nsw flag when negating scales, which is incorrect for INT_MIN. However, just dropping the NSW flag in this case makes BasicAA behavior unreliable and asymmetric, because we may or may not drop the NSW flag depending on which side gets subtracted. Instead, leave the Scale alone and add an additional IsNegated flag, which indicates that the whole VarIndex should be interpreted as a subtraction. This allows us to retain the NSW flag. When accumulating the offset range, we need to use subtraction instead of adding for IsNegated indices. Everything else works on the absolute value of the scale, so the negation does not matter there. Fixes https://github.com/llvm/llvm-project/issues/63266. Differential Revision: https://reviews.llvm.org/D153270
271 lines
8.5 KiB
LLVM
271 lines
8.5 KiB
LLVM
; RUN: opt < %s -aa-pipeline=basic-aa -passes=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(ptr %s) {
|
|
%gep1 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 1
|
|
%gep2 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 0
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t2_fwd
|
|
; CHECK: MayAlias: i32* %gep1, i32* %gep2
|
|
define void @t2_fwd(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !0
|
|
%gep1 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 0
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t2_rev
|
|
; CHECK: MayAlias: i32* %gep1, i32* %gep2
|
|
define void @t2_rev(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !0
|
|
%gep1 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 0
|
|
%gep2 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 %in_array
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t3_fwd
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @t3_fwd(ptr %s, ptr %q) {
|
|
%knownzero = load i32, ptr %q, !range !1
|
|
%gep1 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 %knownzero
|
|
%gep2 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 1
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: t3_rev
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @t3_rev(ptr %s, ptr %q) {
|
|
%knownzero = load i32, ptr %q, !range !1
|
|
%gep1 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 1
|
|
%gep2 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 %knownzero
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: member_after
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @member_after(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !0
|
|
%gep1 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 2
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: member_after_rev
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @member_after_rev(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !0
|
|
%gep2 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 2
|
|
%gep1 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 %in_array
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: member_before
|
|
; CHECK: NoAlias: i32* %gep1, i32* %s
|
|
define void @member_before(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !0
|
|
%gep1 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 %in_array
|
|
load i32, ptr %gep1
|
|
load i32, ptr %s
|
|
ret void
|
|
}
|
|
|
|
; CHECK: Function: member_before_rev
|
|
; CHECK: NoAlias: i32* %gep1, i32* %s
|
|
define void @member_before_rev(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !0
|
|
%gep1 = getelementptr inbounds %struct.S, ptr %s, i64 0, i32 1, i32 %in_array
|
|
load i32, ptr %gep1
|
|
load i32, ptr %s
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: Function: t5
|
|
; CHECK: MayAlias: i32* %gep1, %struct.S2* %s
|
|
; CHECK: PartialAlias (off -4): i32* %gep2, %struct.S2* %s
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @t5(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !3
|
|
%gep1 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 2, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 1, i32 0
|
|
load %struct.S2, ptr %s
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: Function: t6
|
|
; CHECK: MayAlias: i32* %gep1, %struct.S2* %s
|
|
; CHECK: PartialAlias (off -16): i32* %gep2, %struct.S2* %s
|
|
; CHECK: MayAlias: i32* %gep1, i32* %gep2
|
|
define void @t6(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !3
|
|
%gep1 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 2, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 1, i32 3
|
|
load %struct.S2, ptr %s
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: Function: t7
|
|
; CHECK: MayAlias: i32* %gep1, %struct.S2* %s
|
|
; CHECK: PartialAlias (off -20): i32* %gep2, %struct.S2* %s
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @t7(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !4
|
|
%gep1 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 2, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 2, i32 0
|
|
load %struct.S2, ptr %s
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: Function: t8
|
|
; CHECK: MayAlias: i32* %gep1, %struct.S2* %s
|
|
; CHECK: PartialAlias (off -24): i32* %gep2, %struct.S2* %s
|
|
; CHECK: MayAlias: i32* %gep1, i32* %gep2
|
|
define void @t8(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !4
|
|
%gep1 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 2, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 2, i32 1
|
|
load %struct.S2, ptr %s
|
|
load i32, ptr %q
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: Function: t9
|
|
; CHECK: MayAlias: i32* %gep1, %struct.S2* %s
|
|
; CHECK: PartialAlias (off -20): i32* %gep2, %struct.S2* %s
|
|
; CHECK: NoAlias: i32* %gep1, i32* %gep2
|
|
define void @t9(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !5
|
|
%gep1 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 1, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 2, i32 0
|
|
load %struct.S2, ptr %s
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: Function: t10
|
|
; CHECK: MayAlias: i32* %gep1, %struct.S2* %s
|
|
; CHECK: PartialAlias (off -4): i32* %gep2, %struct.S2* %s
|
|
; CHECK: MayAlias: i32* %gep1, i32* %gep2
|
|
define void @t10(ptr %s, ptr %q) {
|
|
%in_array = load i32, ptr %q, !range !5
|
|
%gep1 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 2, i32 %in_array
|
|
%gep2 = getelementptr inbounds %struct.S2, ptr %s, i64 0, i32 1, i32 0
|
|
load %struct.S2, ptr %s
|
|
load i32, ptr %gep1
|
|
load i32, ptr %gep2
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: Function: zeroext_index
|
|
; CHECK: MayAlias: i32* %gep, [256 x i32]* %s
|
|
define void @zeroext_index(ptr %s, ptr %q) {
|
|
%a = load i8, ptr %q, !range !6
|
|
%in_array = zext i8 %a to i32
|
|
%gep = getelementptr inbounds [256 x i32], ptr %s, i64 0, i32 %in_array
|
|
load [256 x i32], ptr %s
|
|
load i32, ptr %gep
|
|
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(ptr %p, ptr %o1_ptr, ptr %o2_ptr) {
|
|
%o1 = load i32, ptr %o1_ptr, !range !0
|
|
%o2 = load i32, ptr %o2_ptr, !range !0
|
|
%p.01 = getelementptr i32, ptr %p, i32 %o1 ; p + [0, 1]
|
|
%p.02 = getelementptr i32, ptr %p.01, i32 %o2 ; p + [0, 2]
|
|
%p.2 = getelementptr i32, ptr %p, i32 2
|
|
%p.3 = getelementptr i32, ptr %p, i32 3
|
|
load i32, ptr %p
|
|
load i32, ptr %p.01
|
|
load i32, ptr %p.02
|
|
load i32, ptr %p.2
|
|
load i32, ptr %p.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(ptr %p, i64 %o) {
|
|
%c = icmp sge i64 %o, -1
|
|
call void @llvm.assume(i1 %c)
|
|
%p.neg1 = getelementptr i8, ptr %p, i64 -1
|
|
%p.o = getelementptr i8, ptr %p, i64 %o
|
|
%p.o.1 = getelementptr i8, ptr %p.o, i64 1
|
|
load i8, ptr %p
|
|
load i8, ptr %p.neg1
|
|
load i8, ptr %p.o
|
|
load i8, ptr %p.o.1
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: pr63266
|
|
; CHECK: MayAlias: i8* %gep2, i8* %offset16
|
|
define void @pr63266(i1 %c, ptr %base) {
|
|
entry:
|
|
%offset16 = getelementptr inbounds i8, ptr %base, i64 16
|
|
%gep1 = getelementptr i8, ptr %base, i64 -9223372036854775792
|
|
br i1 %c, label %if, label %join
|
|
|
|
if:
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi i64 [ -9223372036854775808, %if ], [ 0, %entry ]
|
|
%gep2 = getelementptr i8, ptr %gep1, i64 %phi
|
|
store i8 0, ptr %gep2
|
|
load i8, ptr %offset16
|
|
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 }
|