Skip to content

Commit fd4d670

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 7118430 commit fd4d670

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
@@ -1373,7 +1373,8 @@ bool DependenceInfo::testZIV(const SCEV *Src, const SCEV *Dst,
13731373
bool DependenceInfo::strongSIVtest(const SCEV *Coeff, const SCEV *SrcConst,
13741374
const SCEV *DstConst, const Loop *CurSrcLoop,
13751375
const Loop *CurDstLoop, unsigned Level,
1376-
FullDependence &Result) const {
1376+
FullDependence &Result,
1377+
bool UnderRuntimeAssumptions) {
13771378
if (!isDependenceTestEnabled(DependenceTestType::StrongSIV))
13781379
return false;
13791380

@@ -1445,7 +1446,38 @@ bool DependenceInfo::strongSIVtest(const SCEV *Coeff, const SCEV *SrcConst,
14451446
Result.DV[Level].Direction &= Dependence::DVEntry::EQ;
14461447
++StrongSIVsuccesses;
14471448
} else if (Delta->isZero()) {
1448-
// since 0/X == 0
1449+
// Check if coefficient could be zero. If so, 0/0 is undefined and we
1450+
// cannot conclude that only same-iteration dependencies exist.
1451+
// When coeff=0, all iterations access the same location.
1452+
if (isa<SCEVUnknown>(Coeff)) {
1453+
// Use both isKnownNonZero and range analysis to prove coefficient != 0.
1454+
bool CoeffKnownNonZero = SE->isKnownNonZero(Coeff) ||
1455+
isKnownPredicate(CmpInst::ICMP_NE, Coeff,
1456+
SE->getZero(Coeff->getType()));
1457+
if (!CoeffKnownNonZero) {
1458+
// Cannot prove at compile time, would need runtime assumption.
1459+
if (UnderRuntimeAssumptions) {
1460+
const SCEVPredicate *Pred = SE->getComparePredicate(
1461+
ICmpInst::ICMP_NE, Coeff, SE->getZero(Coeff->getType()));
1462+
SmallVector<const SCEVPredicate *, 4> NewPreds(
1463+
Result.Assumptions.getPredicates());
1464+
NewPreds.push_back(Pred);
1465+
Result.Assumptions = SCEVUnionPredicate(NewPreds, *SE);
1466+
LLVM_DEBUG(dbgs() << "\t Added runtime assumption: " << *Coeff
1467+
<< " != 0\n");
1468+
} else {
1469+
// Cannot add runtime assumptions, this test cannot handle this case.
1470+
// Let more complex tests try.
1471+
LLVM_DEBUG(dbgs() << "\t Would need runtime assumption " << *Coeff
1472+
<< " != 0, but not allowed. Failing this test.\n");
1473+
return false;
1474+
}
1475+
} else {
1476+
LLVM_DEBUG(
1477+
dbgs() << "\t Coefficient proven non-zero by SCEV analysis\n");
1478+
}
1479+
}
1480+
// Since 0/X == 0 (where X is known non-zero or assumed non-zero).
14491481
Result.DV[Level].Distance = Delta;
14501482
Result.DV[Level].Direction &= Dependence::DVEntry::EQ;
14511483
++StrongSIVsuccesses;
@@ -2408,7 +2440,8 @@ bool DependenceInfo::symbolicRDIVtest(const SCEV *A1, const SCEV *A2,
24082440
//
24092441
// Return true if dependence disproved.
24102442
bool DependenceInfo::testSIV(const SCEV *Src, const SCEV *Dst, unsigned &Level,
2411-
FullDependence &Result) const {
2443+
FullDependence &Result,
2444+
bool UnderRuntimeAssumptions) {
24122445
LLVM_DEBUG(dbgs() << " src = " << *Src << "\n");
24132446
LLVM_DEBUG(dbgs() << " dst = " << *Dst << "\n");
24142447
const SCEVAddRecExpr *SrcAddRec = dyn_cast<SCEVAddRecExpr>(Src);
@@ -2426,8 +2459,9 @@ bool DependenceInfo::testSIV(const SCEV *Src, const SCEV *Dst, unsigned &Level,
24262459
Level = mapSrcLoop(CurSrcLoop);
24272460
bool disproven;
24282461
if (SrcCoeff == DstCoeff)
2429-
disproven = strongSIVtest(SrcCoeff, SrcConst, DstConst, CurSrcLoop,
2430-
CurDstLoop, Level, Result);
2462+
disproven =
2463+
strongSIVtest(SrcCoeff, SrcConst, DstConst, CurSrcLoop, CurDstLoop,
2464+
Level, Result, UnderRuntimeAssumptions);
24312465
else if (SrcCoeff == SE->getNegativeSCEV(DstCoeff))
24322466
disproven = weakCrossingSIVtest(SrcCoeff, SrcConst, DstConst, CurSrcLoop,
24332467
CurDstLoop, Level, Result);
@@ -3666,6 +3700,10 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
36663700

36673701
FullDependence Result(Src, Dst, SCEVUnionPredicate(Assume, *SE),
36683702
PossiblyLoopIndependent, CommonLevels);
3703+
// Track assumptions before running dependence tests. If new assumptions are
3704+
// added during tests, we must return the result even if AllEqual to preserve
3705+
// those assumptions for the caller.
3706+
size_t AssumptionsBeforeTests = Result.Assumptions.getPredicates().size();
36693707
++TotalArrayPairs;
36703708

36713709
for (unsigned P = 0; P < Pairs; ++P) {
@@ -3709,7 +3747,8 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
37093747
case Subscript::SIV: {
37103748
LLVM_DEBUG(dbgs() << ", SIV\n");
37113749
unsigned Level;
3712-
if (testSIV(Pair[SI].Src, Pair[SI].Dst, Level, Result))
3750+
if (testSIV(Pair[SI].Src, Pair[SI].Dst, Level, Result,
3751+
UnderRuntimeAssumptions))
37133752
return nullptr;
37143753
break;
37153754
}
@@ -3790,14 +3829,18 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
37903829
} else {
37913830
// On the other hand, if all directions are equal and there's no
37923831
// loop-independent dependence possible, then no dependence exists.
3832+
// However, if we added runtime assumptions during the dependence tests,
3833+
// we must return the result to preserve those assumptions for the caller.
37933834
bool AllEqual = true;
37943835
for (unsigned II = 1; II <= CommonLevels; ++II) {
37953836
if (Result.getDirection(II) != Dependence::DVEntry::EQ) {
37963837
AllEqual = false;
37973838
break;
37983839
}
37993840
}
3800-
if (AllEqual)
3841+
bool AddedAssumptionsDuringTests =
3842+
Result.Assumptions.getPredicates().size() > AssumptionsBeforeTests;
3843+
if (AllEqual && !AddedAssumptionsDuringTests)
38013844
return nullptr;
38023845
}
38033846

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)