Skip to content

Commit 802c010

Browse files
committed
[DA] Fix zero coefficient bug in Strong SIV test using runtime assumptions
The Strong SIV test was incorrectly concluding "no dependence" when the coefficient is symbolic and the delta (difference between source and destination) is zero. When delta=0, the Strong SIV test divides delta/coeff to get the distance. The bug occurs when coeff is an unknown symbolic value: if coeff=0 at runtime, then 0/0 is undefined and all iterations access the same memory location, creating a true dependence that was being missed.
1 parent 53ece54 commit 802c010

File tree

4 files changed

+98
-13
lines changed

4 files changed

+98
-13
lines changed

llvm/include/llvm/Analysis/DependenceAnalysis.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ class DependenceInfo {
554554
/// If the dependence isn't proven to exist,
555555
/// marks the Result as inconsistent.
556556
bool testSIV(const SCEV *Src, const SCEV *Dst, unsigned &Level,
557-
FullDependence &Result) const;
557+
FullDependence &Result, bool UnderRuntimeAssumptions);
558558

559559
/// testRDIV - Tests the RDIV subscript pair (Src and Dst) for dependence.
560560
/// Things of the form [c1 + a1*i] and [c2 + a2*j]
@@ -584,7 +584,7 @@ class DependenceInfo {
584584
bool strongSIVtest(const SCEV *Coeff, const SCEV *SrcConst,
585585
const SCEV *DstConst, const Loop *CurrentSrcLoop,
586586
const Loop *CurrentDstLoop, unsigned Level,
587-
FullDependence &Result) const;
587+
FullDependence &Result, bool UnderRuntimeAssumptions);
588588

589589
/// weakCrossingSIVtest - Tests the weak-crossing SIV subscript pair
590590
/// (Src and Dst) for dependence.

llvm/lib/Analysis/DependenceAnalysis.cpp

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,7 +1296,8 @@ bool DependenceInfo::testZIV(const SCEV *Src, const SCEV *Dst,
12961296
bool DependenceInfo::strongSIVtest(const SCEV *Coeff, const SCEV *SrcConst,
12971297
const SCEV *DstConst, const Loop *CurSrcLoop,
12981298
const Loop *CurDstLoop, unsigned Level,
1299-
FullDependence &Result) const {
1299+
FullDependence &Result,
1300+
bool UnderRuntimeAssumptions) {
13001301
if (!isDependenceTestEnabled(DependenceTestType::StrongSIV))
13011302
return false;
13021303

@@ -1368,7 +1369,38 @@ bool DependenceInfo::strongSIVtest(const SCEV *Coeff, const SCEV *SrcConst,
13681369
Result.DV[Level].Direction &= Dependence::DVEntry::EQ;
13691370
++StrongSIVsuccesses;
13701371
} else if (Delta->isZero()) {
1371-
// since 0/X == 0
1372+
// Check if coefficient could be zero. If so, 0/0 is undefined and we
1373+
// cannot conclude that only same-iteration dependencies exist.
1374+
// When coeff=0, all iterations access the same location.
1375+
if (isa<SCEVUnknown>(Coeff)) {
1376+
// Use both isKnownNonZero and range analysis to prove coefficient != 0.
1377+
bool CoeffKnownNonZero = SE->isKnownNonZero(Coeff) ||
1378+
isKnownPredicate(CmpInst::ICMP_NE, Coeff,
1379+
SE->getZero(Coeff->getType()));
1380+
if (!CoeffKnownNonZero) {
1381+
// Cannot prove at compile time, would need runtime assumption.
1382+
if (UnderRuntimeAssumptions) {
1383+
const SCEVPredicate *Pred = SE->getComparePredicate(
1384+
ICmpInst::ICMP_NE, Coeff, SE->getZero(Coeff->getType()));
1385+
SmallVector<const SCEVPredicate *, 4> NewPreds(
1386+
Result.Assumptions.getPredicates());
1387+
NewPreds.push_back(Pred);
1388+
Result.Assumptions = SCEVUnionPredicate(NewPreds, *SE);
1389+
LLVM_DEBUG(dbgs() << "\t Added runtime assumption: " << *Coeff
1390+
<< " != 0\n");
1391+
} else {
1392+
// Cannot add runtime assumptions, this test cannot handle this case.
1393+
// Let more complex tests try.
1394+
LLVM_DEBUG(dbgs() << "\t Would need runtime assumption " << *Coeff
1395+
<< " != 0, but not allowed. Failing this test.\n");
1396+
return false;
1397+
}
1398+
} else {
1399+
LLVM_DEBUG(
1400+
dbgs() << "\t Coefficient proven non-zero by SCEV analysis\n");
1401+
}
1402+
}
1403+
// Since 0/X == 0 (where X is known non-zero or assumed non-zero).
13721404
Result.DV[Level].Distance = Delta;
13731405
Result.DV[Level].Direction &= Dependence::DVEntry::EQ;
13741406
++StrongSIVsuccesses;
@@ -2331,7 +2363,8 @@ bool DependenceInfo::symbolicRDIVtest(const SCEV *A1, const SCEV *A2,
23312363
//
23322364
// Return true if dependence disproved.
23332365
bool DependenceInfo::testSIV(const SCEV *Src, const SCEV *Dst, unsigned &Level,
2334-
FullDependence &Result) const {
2366+
FullDependence &Result,
2367+
bool UnderRuntimeAssumptions) {
23352368
LLVM_DEBUG(dbgs() << " src = " << *Src << "\n");
23362369
LLVM_DEBUG(dbgs() << " dst = " << *Dst << "\n");
23372370
const SCEVAddRecExpr *SrcAddRec = dyn_cast<SCEVAddRecExpr>(Src);
@@ -2349,8 +2382,9 @@ bool DependenceInfo::testSIV(const SCEV *Src, const SCEV *Dst, unsigned &Level,
23492382
Level = mapSrcLoop(CurSrcLoop);
23502383
bool disproven;
23512384
if (SrcCoeff == DstCoeff)
2352-
disproven = strongSIVtest(SrcCoeff, SrcConst, DstConst, CurSrcLoop,
2353-
CurDstLoop, Level, Result);
2385+
disproven =
2386+
strongSIVtest(SrcCoeff, SrcConst, DstConst, CurSrcLoop, CurDstLoop,
2387+
Level, Result, UnderRuntimeAssumptions);
23542388
else if (SrcCoeff == SE->getNegativeSCEV(DstCoeff))
23552389
disproven = weakCrossingSIVtest(SrcCoeff, SrcConst, DstConst, CurSrcLoop,
23562390
CurDstLoop, Level, Result);
@@ -3540,6 +3574,10 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
35403574

35413575
FullDependence Result(Src, Dst, SCEVUnionPredicate(Assume, *SE),
35423576
PossiblyLoopIndependent, CommonLevels);
3577+
// Track assumptions before running dependence tests. If new assumptions are
3578+
// added during tests, we must return the result even if AllEqual to preserve
3579+
// those assumptions for the caller.
3580+
size_t AssumptionsBeforeTests = Result.Assumptions.getPredicates().size();
35433581
++TotalArrayPairs;
35443582

35453583
for (unsigned P = 0; P < Pairs; ++P) {
@@ -3583,7 +3621,8 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
35833621
case Subscript::SIV: {
35843622
LLVM_DEBUG(dbgs() << ", SIV\n");
35853623
unsigned Level;
3586-
if (testSIV(Pair[SI].Src, Pair[SI].Dst, Level, Result))
3624+
if (testSIV(Pair[SI].Src, Pair[SI].Dst, Level, Result,
3625+
UnderRuntimeAssumptions))
35873626
return nullptr;
35883627
break;
35893628
}
@@ -3664,14 +3703,18 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
36643703
} else {
36653704
// On the other hand, if all directions are equal and there's no
36663705
// loop-independent dependence possible, then no dependence exists.
3706+
// However, if we added runtime assumptions during the dependence tests,
3707+
// we must return the result to preserve those assumptions for the caller.
36673708
bool AllEqual = true;
36683709
for (unsigned II = 1; II <= CommonLevels; ++II) {
36693710
if (Result.getDirection(II) != Dependence::DVEntry::EQ) {
36703711
AllEqual = false;
36713712
break;
36723713
}
36733714
}
3674-
if (AllEqual)
3715+
bool AddedAssumptionsDuringTests =
3716+
Result.Assumptions.getPredicates().size() > AssumptionsBeforeTests;
3717+
if (AllEqual && !AddedAssumptionsDuringTests)
36753718
return nullptr;
36763719
}
36773720

llvm/test/Analysis/DependenceAnalysis/DADelin.ll

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -646,11 +646,15 @@ exit:
646646
define void @coeff_may_negative(ptr %a, i32 %k) {
647647
; CHECK-LABEL: 'coeff_may_negative'
648648
; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.0, align 1
649-
; CHECK-NEXT: da analyze - none!
649+
; CHECK-NEXT: da analyze - consistent output [0]!
650+
; CHECK-NEXT: Runtime Assumptions:
651+
; CHECK-NEXT: Compare predicate: %k ne) 0
650652
; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
651653
; CHECK-NEXT: da analyze - output [*|<]!
652654
; CHECK-NEXT: Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
653-
; CHECK-NEXT: da analyze - none!
655+
; CHECK-NEXT: da analyze - consistent output [0]!
656+
; CHECK-NEXT: Runtime Assumptions:
657+
; CHECK-NEXT: Compare predicate: %k ne) 0
654658
;
655659
entry:
656660
br label %loop
@@ -685,11 +689,15 @@ exit:
685689
define void @coeff_positive(ptr %a, i32 %k) {
686690
; CHECK-LABEL: 'coeff_positive'
687691
; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.0, align 1
688-
; CHECK-NEXT: da analyze - none!
692+
; CHECK-NEXT: da analyze - consistent output [0]!
693+
; CHECK-NEXT: Runtime Assumptions:
694+
; CHECK-NEXT: Compare predicate: %k ne) 0
689695
; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
690696
; CHECK-NEXT: da analyze - output [*|<]!
691697
; CHECK-NEXT: Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1
692-
; CHECK-NEXT: da analyze - none!
698+
; CHECK-NEXT: da analyze - consistent output [0]!
699+
; CHECK-NEXT: Runtime Assumptions:
700+
; CHECK-NEXT: Compare predicate: %k ne) 0
693701
;
694702
entry:
695703
br label %loop
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt < %s -disable-output "-passes=print<da>" -aa-pipeline=basic-aa 2>&1 \
3+
; RUN: | FileCheck %s
4+
5+
; Test case for bug fix where Strong SIV test incorrectly concludes "no dependence"
6+
; when the coefficient is symbolic (unknown at compile time) and delta is zero.
7+
;
8+
; In this case, the array access is A[k*i] with both src and dst at the same
9+
; location in the same iteration. If k=0, then all iterations access the same
10+
; element, meaning there IS a dependence between different iterations.
11+
; The Strong SIV test should add a runtime assumption that k != 0.
12+
13+
define void @test_zero_coefficient(ptr noalias %A, i64 %k) {
14+
; CHECK-LABEL: 'test_zero_coefficient'
15+
; CHECK-NEXT: Src: store i8 42, ptr %idx, align 1 --> Dst: store i8 42, ptr %idx, align 1
16+
; CHECK-NEXT: da analyze - consistent output [0]!
17+
; CHECK-NEXT: Runtime Assumptions:
18+
; CHECK-NEXT: Compare predicate: %k ne) 0
19+
;
20+
entry:
21+
br label %loop
22+
23+
loop:
24+
%i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
25+
%off = mul nsw i64 %i, %k
26+
%idx = getelementptr inbounds i8, ptr %A, i64 %off
27+
store i8 42, ptr %idx, align 1
28+
%i.next = add nsw i64 %i, 1
29+
%cmp = icmp slt i64 %i.next, 100
30+
br i1 %cmp, label %loop, label %exit
31+
32+
exit:
33+
ret void
34+
}

0 commit comments

Comments
 (0)