Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions llvm/test/Analysis/DependenceAnalysis/gcd-miv-overflow.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
; RUN: opt < %s -disable-output "-passes=print<da>" 2>&1 \
; RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-ALL
; RUN: opt < %s -disable-output "-passes=print<da>" -da-enable-dependence-test=gcd-miv 2>&1 \
; RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-GCD-MIV

; offset0 = 4;
; offset1 = 0;
; for (i = 0; i < 100; i++) {
; A[offset0] = 1;
; A[offset1] = 2;
; offset0 += 3*m;
; offset1 += 3;
; }
;
; FIXME: DependenceAnalysis currently detects no dependency between the two
; stores, but it does exist. E.g., consider `m` is 12297829382473034411, which
; is a modular multiplicative inverse of 3 under modulo 2^64. Then `offset0` is
; effectively `i + 4`, so accesses will be as follows:
;
; - A[offset0] : A[4], A[5], A[6], ...
; - A[offset1] : A[0], A[3], A[6], ...
;
; The root cause is that DA interprets `3*m` in non-modular arithmetic, which
; isn't necessarily true due to overflow.
;
define void @gcdmiv_coef_ovfl(ptr %A, i64 %m) {
; CHECK-ALL-LABEL: 'gcdmiv_coef_ovfl'
; CHECK-ALL-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 1, ptr %gep.0, align 1
; CHECK-ALL-NEXT: da analyze - none!
; CHECK-ALL-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-ALL-NEXT: da analyze - none!
; CHECK-ALL-NEXT: Src: store i8 2, ptr %gep.1, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-ALL-NEXT: da analyze - none!
;
; CHECK-GCD-MIV-LABEL: 'gcdmiv_coef_ovfl'
; CHECK-GCD-MIV-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 1, ptr %gep.0, align 1
; CHECK-GCD-MIV-NEXT: da analyze - consistent output [*]!
; CHECK-GCD-MIV-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-GCD-MIV-NEXT: da analyze - none!
; CHECK-GCD-MIV-NEXT: Src: store i8 2, ptr %gep.1, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-GCD-MIV-NEXT: da analyze - consistent output [*]!
;
entry:
%step = mul i64 3, %m
br label %loop

loop:
%i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
%offset.0 = phi i64 [ 4, %entry ] , [ %offset.0.next, %loop ]
%offset.1 = phi i64 [ 0, %entry ] , [ %offset.1.next, %loop ]
%gep.0 = getelementptr inbounds i8, ptr %A, i64 %offset.0
%gep.1 = getelementptr inbounds i8, ptr %A, i64 %offset.1
store i8 1, ptr %gep.0
store i8 2, ptr %gep.1
%i.inc = add nuw nsw i64 %i, 1
%offset.0.next = add nsw i64 %offset.0, %step
%offset.1.next = add nsw i64 %offset.1, 3
%ec = icmp eq i64 %i.inc, 100
br i1 %ec, label %exit, label %loop

exit:
ret void
}
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CHECK: {{.*}}
68 changes: 68 additions & 0 deletions llvm/test/Analysis/DependenceAnalysis/strong-siv-overflow.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
; RUN: opt < %s -disable-output "-passes=print<da>" 2>&1 \
; RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-ALL
; RUN: opt < %s -disable-output "-passes=print<da>" -da-enable-dependence-test=strong-siv 2>&1 \
; RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-STRONG-SIV

; for (i = 0; i < (1LL << 62); i++) {
; if (0 <= 2*i - 2)
; A[2*i - 2] = 1;
;
; if (0 <= 2*i - 4)
; A[2*i - 4] = 2;
; }
;
; FIXME: DependenceAnalysis currently detects no dependency between the two
; stores, but it does exist. For example, each store will access A[0] when i
; is 1 and 2 respectively.
; The root cause is that the product of the BTC and the coefficient
; ((1LL << 62) - 1 and 2) overflows in a signed sense.
define void @strongsiv_const_ovfl(ptr %A) {
; CHECK-LABEL: 'strongsiv_const_ovfl'
; CHECK-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 1, ptr %gep.0, align 1
; CHECK-NEXT: da analyze - none!
; CHECK-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-NEXT: da analyze - none!
; CHECK-NEXT: Src: store i8 2, ptr %gep.1, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-NEXT: da analyze - none!
;
entry:
br label %loop.header

loop.header:
%i = phi i64 [ 0, %entry ], [ %i.inc, %loop.latch ]
%offset.0 = phi i64 [ -2, %entry ], [ %offset.0.next, %loop.latch ]
%offset.1 = phi i64 [ -4, %entry ], [ %offset.1.next, %loop.latch ]
%ec = icmp eq i64 %i, 4611686018427387904
br i1 %ec, label %exit, label %loop.body

loop.body:
%cond.0 = icmp sge i64 %offset.0, 0
%cond.1 = icmp sge i64 %offset.1, 0
br i1 %cond.0, label %if.then.0, label %loop.middle

if.then.0:
%gep.0 = getelementptr inbounds i8, ptr %A, i64 %offset.0
store i8 1, ptr %gep.0
br label %loop.middle

loop.middle:
br i1 %cond.1, label %if.then.1, label %loop.latch

if.then.1:
%gep.1 = getelementptr inbounds i8, ptr %A, i64 %offset.1
store i8 2, ptr %gep.1
br label %loop.latch

loop.latch:
%i.inc = add nuw nsw i64 %i, 1
%offset.0.next = add nsw i64 %offset.0, 2
%offset.1.next = add nsw i64 %offset.1, 2
br label %loop.header

exit:
ret void
}
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CHECK-ALL: {{.*}}
; CHECK-STRONG-SIV: {{.*}}
137 changes: 137 additions & 0 deletions llvm/test/Analysis/DependenceAnalysis/symbolic-rdiv-overflow.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
; RUN: opt < %s -disable-output "-passes=print<da>" 2>&1 \
; RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-ALL
; RUN: opt < %s -disable-output "-passes=print<da>" -da-enable-dependence-test=symbolic-rdiv 2>&1 \
; RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-SYMBOLIC-RDIV

; for (i = 0; i < (1LL << 62); i++) {
; if (0 <= 2*i - 2)
; A[2*i - 2] = 1;
; A[i] = 2;
; }
;
; FIXME: DependenceAnalysis currently detects no dependency between the two
; stores, but it does exist. For example, each store will access A[0] when i
; is 1 and 0 respectively.
; The root cause is that the product of the BTC and the coefficient
; ((1LL << 62) - 1 and 2) overflows in a signed sense.
define void @symbolicrdiv_prod_ovfl(ptr %A) {
; CHECK-ALL-LABEL: 'symbolicrdiv_prod_ovfl'
; CHECK-ALL-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 1, ptr %gep.0, align 1
; CHECK-ALL-NEXT: da analyze - none!
; CHECK-ALL-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-ALL-NEXT: da analyze - none!
; CHECK-ALL-NEXT: Src: store i8 2, ptr %gep.1, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-ALL-NEXT: da analyze - none!
;
; CHECK-SYMBOLIC-RDIV-LABEL: 'symbolicrdiv_prod_ovfl'
; CHECK-SYMBOLIC-RDIV-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 1, ptr %gep.0, align 1
; CHECK-SYMBOLIC-RDIV-NEXT: da analyze - none!
; CHECK-SYMBOLIC-RDIV-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-SYMBOLIC-RDIV-NEXT: da analyze - none!
; CHECK-SYMBOLIC-RDIV-NEXT: Src: store i8 2, ptr %gep.1, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-SYMBOLIC-RDIV-NEXT: da analyze - consistent output [*]!
;
entry:
br label %loop.header

loop.header:
%i = phi i64 [ 0, %entry ], [ %i.inc, %loop.latch ]
%offset = phi i64 [ -2, %entry ], [ %offset.next, %loop.latch ]
%ec = icmp eq i64 %i, 4611686018427387904
br i1 %ec, label %exit, label %loop.body

loop.body:
%cond = icmp sge i64 %offset, 0
br i1 %cond, label %if.then, label %loop.latch

if.then:
%gep.0 = getelementptr inbounds i8, ptr %A, i64 %offset
store i8 1, ptr %gep.0
br label %loop.latch

loop.latch:
%gep.1 = getelementptr inbounds i8, ptr %A, i64 %i
store i8 2, ptr %gep.1
%i.inc = add nuw nsw i64 %i, 1
%offset.next = add nsw i64 %offset, 2
br label %loop.header

exit:
ret void
}

; offset0 = -4611686018427387904; // -2^62
; offset1 = 4611686018427387904; // 2^62
; for (i = 0; i < (1LL << 62) - 100; i++) {
; if (0 <= offset0)
; A[offset0] = 1;
; if (0 <= offset1)
; A[offset1] = 2;
; offset0 += 2;
; offset1 -= 1;
; }
;
; FIXME: DependenceAnalysis currently detects no dependency between the two
; stores, but it does exist. For example,
;
; memory access | i == 2^61 | i == 2^61 + 2^59 | i == 2^61 + 2^60
; -------------------------|-----------|------------------|-------------------
; A[2*i - 2^62] (offset0) | | A[2^60] | A[2^61]
; A[-i + 2^62] (offset1) | A[2^61] | | A[2^60]
Comment on lines +78 to +81
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

;
; The root cause is that the calculation of the differenct between the two
; constants (-2^62 and 2^62) overflows in a signed sense.
define void @symbolicrdiv_delta_ovfl(ptr %A) {
; CHECK-ALL-LABEL: 'symbolicrdiv_delta_ovfl'
; CHECK-ALL-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 1, ptr %gep.0, align 1
; CHECK-ALL-NEXT: da analyze - none!
; CHECK-ALL-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-ALL-NEXT: da analyze - none!
; CHECK-ALL-NEXT: Src: store i8 2, ptr %gep.1, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-ALL-NEXT: da analyze - none!
;
; CHECK-SYMBOLIC-RDIV-LABEL: 'symbolicrdiv_delta_ovfl'
; CHECK-SYMBOLIC-RDIV-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 1, ptr %gep.0, align 1
; CHECK-SYMBOLIC-RDIV-NEXT: da analyze - consistent output [*]!
; CHECK-SYMBOLIC-RDIV-NEXT: Src: store i8 1, ptr %gep.0, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-SYMBOLIC-RDIV-NEXT: da analyze - none!
; CHECK-SYMBOLIC-RDIV-NEXT: Src: store i8 2, ptr %gep.1, align 1 --> Dst: store i8 2, ptr %gep.1, align 1
; CHECK-SYMBOLIC-RDIV-NEXT: da analyze - consistent output [*]!
;
entry:
br label %loop.header

loop.header:
%i = phi i64 [ 0, %entry ], [ %i.inc, %loop.latch ]
%offset.0 = phi i64 [ -4611686018427387904, %entry ], [ %offset.0.next, %loop.latch ]
%offset.1 = phi i64 [ 4611686018427387904, %entry ], [ %offset.1.next, %loop.latch ]
%cond.0 = icmp sge i64 %offset.0, 0
%cond.1 = icmp sge i64 %offset.1, 0
br i1 %cond.0, label %if.then.0, label %loop.middle

if.then.0:
%gep.0 = getelementptr inbounds i8, ptr %A, i64 %offset.0
store i8 1, ptr %gep.0
br label %loop.middle

loop.middle:
br i1 %cond.1, label %if.then.1, label %loop.latch

if.then.1:
%gep.1 = getelementptr inbounds i8, ptr %A, i64 %offset.1
store i8 2, ptr %gep.1
br label %loop.latch

loop.latch:
%i.inc = add nuw nsw i64 %i, 1
%offset.0.next = add nsw i64 %offset.0, 2
%offset.1.next = sub nsw i64 %offset.1, 1
%ec = icmp eq i64 %i.inc, 4611686018427387804 ; 2^62 - 100
br i1 %ec, label %exit, label %loop.header

exit:
ret void
}
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CHECK: {{.*}}
125 changes: 125 additions & 0 deletions llvm/test/Analysis/DependenceAnalysis/weak-crossing-siv-overflow.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
; RUN: opt < %s -disable-output "-passes=print<da>" 2>&1 \
; RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-ALL
; RUN: opt < %s -disable-output "-passes=print<da>" -da-enable-dependence-test=weak-crossing-siv 2>&1 \
; RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-WEAK-CROSSING-SIV

; max_i = INT64_MAX/3 // 3074457345618258602
; for (long long i = 0; i <= max_i; i++) {
; A[-3*i + INT64_MAX] = 0;
; if (i)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the if (i) matter for DA?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of this condition (and similar ones in other cases) is to avoid UB caused by the combination of the size limitation of allocated objects and inbounds. However, I'm not sure if the presence of these branches affects DA, or simply dropping inbounds would be sufficient.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen it in the other cases to avoid the subscripts becoming negative. In this case, A[-3*0 + INT64_MAX] == A[INT64_MAX] still seems to be valid, at least no less than other values of i. E.g. with &A == nullptr (valid in Linux kernel code), the resulting address is still within the half-address space bound.

However, I see the point, we can/should leave it to be on the safe side.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is if (i) A[3*i - 2], which prevents access A[3*0 - 2] == A[-2]. Like the other guards, this one also excludes negative subscripts.

; A[3*i - 2] = 1;
; }
;
; FIXME: DependenceAnalysis currently detects no dependency between
; `A[-3*i + INT64_MAX]` and `A[3*i - 2]`, but it does exist. For example,
;
; memory access | i == 1 | i == max_i
; ---------------------|------------------|------------------
; A[-3*i + INT64_MAX] | A[INT64_MAX - 3] | A[1]
; A[3*i - 2] | A[1] | A[INT64_MAX - 3]
;
; The root cause is that the calculation of the differenct between the two
; constants (INT64_MAX and -2) triggers an overflow.

define void @weakcorssing_delta_ovfl(ptr %A) {
; CHECK-ALL-LABEL: 'weakcorssing_delta_ovfl'
; CHECK-ALL-NEXT: Src: store i8 0, ptr %idx.0, align 1 --> Dst: store i8 0, ptr %idx.0, align 1
; CHECK-ALL-NEXT: da analyze - none!
; CHECK-ALL-NEXT: Src: store i8 0, ptr %idx.0, align 1 --> Dst: store i8 1, ptr %idx.1, align 1
; CHECK-ALL-NEXT: da analyze - none!
; CHECK-ALL-NEXT: Src: store i8 1, ptr %idx.1, align 1 --> Dst: store i8 1, ptr %idx.1, align 1
; CHECK-ALL-NEXT: da analyze - none!
;
; CHECK-WEAK-CROSSING-SIV-LABEL: 'weakcorssing_delta_ovfl'
; CHECK-WEAK-CROSSING-SIV-NEXT: Src: store i8 0, ptr %idx.0, align 1 --> Dst: store i8 0, ptr %idx.0, align 1
; CHECK-WEAK-CROSSING-SIV-NEXT: da analyze - consistent output [*]!
; CHECK-WEAK-CROSSING-SIV-NEXT: Src: store i8 0, ptr %idx.0, align 1 --> Dst: store i8 1, ptr %idx.1, align 1
; CHECK-WEAK-CROSSING-SIV-NEXT: da analyze - none!
; CHECK-WEAK-CROSSING-SIV-NEXT: Src: store i8 1, ptr %idx.1, align 1 --> Dst: store i8 1, ptr %idx.1, align 1
; CHECK-WEAK-CROSSING-SIV-NEXT: da analyze - consistent output [*]!
;
entry:
br label %loop.header

loop.header:
%i = phi i64 [ 0, %entry ], [ %i.inc, %loop.latch ]
%subscript.0 = phi i64 [ 9223372036854775807, %entry ], [ %subscript.0.next, %loop.latch ]
%subscript.1 = phi i64 [ -2, %entry ], [ %subscript.1.next, %loop.latch ]
%idx.0 = getelementptr inbounds i8, ptr %A, i64 %subscript.0
store i8 0, ptr %idx.0
%cond.store = icmp ne i64 %i, 0
br i1 %cond.store, label %if.store, label %loop.latch

if.store:
%idx.1 = getelementptr inbounds i8, ptr %A, i64 %subscript.1
store i8 1, ptr %idx.1
br label %loop.latch

loop.latch:
%i.inc = add nuw nsw i64 %i, 1
%subscript.0.next = add nsw i64 %subscript.0, -3
%subscript.1.next = add nsw i64 %subscript.1, 3
%ec = icmp sgt i64 %i.inc, 3074457345618258602
br i1 %ec, label %exit, label %loop.header

exit:
ret void
}

; max_i = INT64_MAX/3 // 3074457345618258602
; for (long long i = 0; i <= max_i; i++) {
; A[-3*i + INT64_MAX] = 0;
; A[3*i + 1] = 1;
; }
;
; FIXME: DependenceAnalysis currently detects no dependency between
; `A[-3*i + INT64_MAX]` and `A[3*i - 2]`, but it does exist. For example,
;
; memory access | i == 0 | i == 1 | i == max_i - 1 | i == max_i
; ---------------------|--------|------------------|----------------|------------------
; A[-3*i + INT64_MAX] | | A[INT64_MAX - 3] | A[1] |
; A[3*i + 1] | A[1] | | | A[INT64_MAX - 3]
;
; The root cause is that the product of the BTC, the coefficient, and 2
; triggers an overflow.
;
define void @weakcorssing_prod_ovfl(ptr %A) {
; CHECK-ALL-LABEL: 'weakcorssing_prod_ovfl'
; CHECK-ALL-NEXT: Src: store i8 0, ptr %idx.0, align 1 --> Dst: store i8 0, ptr %idx.0, align 1
; CHECK-ALL-NEXT: da analyze - none!
; CHECK-ALL-NEXT: Src: store i8 0, ptr %idx.0, align 1 --> Dst: store i8 1, ptr %idx.1, align 1
; CHECK-ALL-NEXT: da analyze - none!
; CHECK-ALL-NEXT: Src: store i8 1, ptr %idx.1, align 1 --> Dst: store i8 1, ptr %idx.1, align 1
; CHECK-ALL-NEXT: da analyze - none!
;
; CHECK-WEAK-CROSSING-SIV-LABEL: 'weakcorssing_prod_ovfl'
; CHECK-WEAK-CROSSING-SIV-NEXT: Src: store i8 0, ptr %idx.0, align 1 --> Dst: store i8 0, ptr %idx.0, align 1
; CHECK-WEAK-CROSSING-SIV-NEXT: da analyze - consistent output [*]!
; CHECK-WEAK-CROSSING-SIV-NEXT: Src: store i8 0, ptr %idx.0, align 1 --> Dst: store i8 1, ptr %idx.1, align 1
; CHECK-WEAK-CROSSING-SIV-NEXT: da analyze - none!
; CHECK-WEAK-CROSSING-SIV-NEXT: Src: store i8 1, ptr %idx.1, align 1 --> Dst: store i8 1, ptr %idx.1, align 1
; CHECK-WEAK-CROSSING-SIV-NEXT: da analyze - consistent output [*]!
;
entry:
br label %loop

loop:
%i = phi i64 [ 0, %entry ], [ %i.inc, %loop ]
%subscript.0 = phi i64 [ 9223372036854775807, %entry ], [ %subscript.0.next, %loop ]
%subscript.1 = phi i64 [ 1, %entry ], [ %subscript.1.next, %loop ]
%idx.0 = getelementptr inbounds i8, ptr %A, i64 %subscript.0
%idx.1 = getelementptr inbounds i8, ptr %A, i64 %subscript.1
store i8 0, ptr %idx.0
store i8 1, ptr %idx.1
%i.inc = add nuw nsw i64 %i, 1
%subscript.0.next = add nsw i64 %subscript.0, -3
%subscript.1.next = add nsw i64 %subscript.1, 3
%ec = icmp sgt i64 %i.inc, 3074457345618258602
br i1 %ec, label %exit, label %loop

exit:
ret void
}
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CHECK: {{.*}}
Loading
Loading