Skip to content

Commit a0b0582

Browse files
committed
[DependenceAnalysis] Prevent non-AddRec expressions from reaching testSIV
Fixes GitHub issue #148435 where testSIV() would hit an assertion failure with Src and Dst not containing AddRec expressions. The fix adds reclassification logic at all points where SIV subscripts are tested to ensure that if expressions have become non-AddRec, they are reclassified as ZIV instead of causing assertion failures. Add regression test to prevent future occurrences of this issue.
1 parent 879f40a commit a0b0582

File tree

3 files changed

+240
-31
lines changed

3 files changed

+240
-31
lines changed

llvm/lib/Analysis/DependenceAnalysis.cpp

Lines changed: 121 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,9 @@ void DependenceInfo::collectCommonLoops(const SCEV *Expression,
873873
SmallBitVector &Loops) const {
874874
while (LoopNest) {
875875
unsigned Level = LoopNest->getLoopDepth();
876+
LLVM_DEBUG(dbgs() << "MaxLevels = " << MaxLevels << "\n");
877+
LLVM_DEBUG(dbgs() << "Level = " << Level << "\n");
878+
assert(Level <= MaxLevels && "Level larger than MaxLevels.");
876879
if (Level <= CommonLevels && !SE->isLoopInvariant(Expression, LoopNest))
877880
Loops.set(Level);
878881
LoopNest = LoopNest->getParentLoop();
@@ -959,6 +962,10 @@ bool DependenceInfo::checkSubscript(const SCEV *Expr, const Loop *LoopNest,
959962
if (!AddRec)
960963
return isLoopInvariant(Expr, LoopNest);
961964

965+
const SCEV *Step = AddRec->getStepRecurrence(*SE);
966+
if (!isLoopInvariant(Step, LoopNest))
967+
return false;
968+
962969
// The AddRec must depend on one of the containing loops. Otherwise,
963970
// mapSrcLoop and mapDstLoop return indices outside the intended range. This
964971
// can happen when a subscript in one loop references an IV from a sibling
@@ -970,14 +977,16 @@ bool DependenceInfo::checkSubscript(const SCEV *Expr, const Loop *LoopNest,
970977
if (!L)
971978
return false;
972979

980+
unsigned Level = IsSrc ? mapSrcLoop(L) : mapDstLoop(L);
981+
// Check that the mapped loop index is within bounds for the SmallBitVector.
982+
// This can happen when loop depths exceed MaxLevels due to the mapping
983+
// algorithm.
984+
985+
LLVM_DEBUG(dbgs() << "MaxLevels = " << MaxLevels << "\n");
986+
LLVM_DEBUG(dbgs() << "Level = " << Level << "\n");
987+
assert(Level <= MaxLevels && "Level larger than MaxLevels.");
988+
Loops.set(Level);
973989
const SCEV *Start = AddRec->getStart();
974-
const SCEV *Step = AddRec->getStepRecurrence(*SE);
975-
if (!isLoopInvariant(Step, LoopNest))
976-
return false;
977-
if (IsSrc)
978-
Loops.set(mapSrcLoop(AddRec->getLoop()));
979-
else
980-
Loops.set(mapDstLoop(AddRec->getLoop()));
981990
return checkSubscript(Start, LoopNest, Loops, IsSrc);
982991
}
983992

@@ -3835,6 +3844,15 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
38353844
// test separable subscripts
38363845
for (unsigned SI : Separable.set_bits()) {
38373846
LLVM_DEBUG(dbgs() << "testing subscript " << SI);
3847+
3848+
// For SIV subscripts, reclassify to handle cases where expressions
3849+
// may have become non-AddRec.
3850+
if (Pair[SI].Classification == Subscript::SIV) {
3851+
Pair[SI].Classification = classifyPair(
3852+
Pair[SI].Src, LI->getLoopFor(Src->getParent()), Pair[SI].Dst,
3853+
LI->getLoopFor(Dst->getParent()), Pair[SI].Loops);
3854+
}
3855+
38383856
switch (Pair[SI].Classification) {
38393857
case Subscript::ZIV:
38403858
LLVM_DEBUG(dbgs() << ", ZIV\n");
@@ -3843,7 +3861,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
38433861
break;
38443862
case Subscript::SIV: {
38453863
LLVM_DEBUG(dbgs() << ", SIV\n");
3846-
unsigned Level;
3864+
unsigned Level = 0;
38473865
const SCEV *SplitIter = nullptr;
38483866
if (testSIV(Pair[SI].Src, Pair[SI].Dst, Level, Result, NewConstraint,
38493867
SplitIter))
@@ -3893,13 +3911,52 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
38933911
bool Changed = false;
38943912
for (unsigned SJ : Sivs.set_bits()) {
38953913
LLVM_DEBUG(dbgs() << "testing subscript " << SJ << ", SIV\n");
3896-
// SJ is an SIV subscript that's part of the current coupled group
3897-
unsigned Level;
3914+
// SJ is an SIV subscript that's part of the current coupled group.
3915+
3916+
// Reclassify to handle cases where expressions may have become
3917+
// non-AddRec.
3918+
Pair[SJ].Classification = classifyPair(
3919+
Pair[SJ].Src, LI->getLoopFor(Src->getParent()), Pair[SJ].Dst,
3920+
LI->getLoopFor(Dst->getParent()), Pair[SJ].Loops);
3921+
3922+
unsigned Level = 0;
38983923
const SCEV *SplitIter = nullptr;
3899-
LLVM_DEBUG(dbgs() << "SIV\n");
3900-
if (testSIV(Pair[SJ].Src, Pair[SJ].Dst, Level, Result, NewConstraint,
3901-
SplitIter))
3902-
return nullptr;
3924+
3925+
switch (Pair[SJ].Classification) {
3926+
case Subscript::ZIV:
3927+
LLVM_DEBUG(dbgs() << " -> reclassified as ZIV\n");
3928+
if (testZIV(Pair[SJ].Src, Pair[SJ].Dst, Result))
3929+
return nullptr;
3930+
Sivs.reset(SJ);
3931+
continue;
3932+
case Subscript::SIV:
3933+
LLVM_DEBUG(dbgs() << "SIV\n");
3934+
if (testSIV(Pair[SJ].Src, Pair[SJ].Dst, Level, Result,
3935+
NewConstraint, SplitIter))
3936+
return nullptr;
3937+
break;
3938+
case Subscript::RDIV:
3939+
LLVM_DEBUG(dbgs() << " -> reclassified as RDIV\n");
3940+
if (testRDIV(Pair[SJ].Src, Pair[SJ].Dst, Result))
3941+
return nullptr;
3942+
Sivs.reset(SJ);
3943+
continue;
3944+
case Subscript::MIV:
3945+
LLVM_DEBUG(dbgs() << " -> reclassified as MIV\n");
3946+
Mivs.set(SJ);
3947+
Sivs.reset(SJ);
3948+
continue;
3949+
case Subscript::NonLinear:
3950+
LLVM_DEBUG(dbgs() << " -> reclassified as NonLinear\n");
3951+
Result.Consistent = false;
3952+
Sivs.reset(SJ);
3953+
continue;
3954+
}
3955+
3956+
LLVM_DEBUG(dbgs() << "MaxLevels = " << MaxLevels << "\n");
3957+
LLVM_DEBUG(dbgs() << "Level = " << Level << "\n");
3958+
assert(Level <= MaxLevels && "Level larger than MaxLevels.");
3959+
39033960
ConstrainedLevels.set(Level);
39043961
if (intersectConstraints(&Constraints[Level], &NewConstraint)) {
39053962
if (Constraints[Level].isEmpty()) {
@@ -3939,8 +3996,11 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
39393996
case Subscript::RDIV:
39403997
case Subscript::MIV:
39413998
break;
3942-
default:
3943-
llvm_unreachable("bad subscript classification");
3999+
case Subscript::NonLinear:
4000+
LLVM_DEBUG(dbgs() << "NonLinear\n");
4001+
Result.Consistent = false;
4002+
Mivs.reset(SJ);
4003+
break;
39444004
}
39454005
}
39464006
}
@@ -4177,14 +4237,22 @@ const SCEV *DependenceInfo::getSplitIteration(const Dependence &Dep,
41774237
for (unsigned SI : Separable.set_bits()) {
41784238
switch (Pair[SI].Classification) {
41794239
case Subscript::SIV: {
4180-
unsigned Level;
4181-
const SCEV *SplitIter = nullptr;
4182-
(void)testSIV(Pair[SI].Src, Pair[SI].Dst, Level, Result, NewConstraint,
4183-
SplitIter);
4184-
if (Level == SplitLevel) {
4185-
assert(SplitIter != nullptr);
4186-
return SplitIter;
4240+
// Reclassify to handle cases where expressions may have become non-AddRec.
4241+
Pair[SI].Classification = classifyPair(
4242+
Pair[SI].Src, LI->getLoopFor(Src->getParent()), Pair[SI].Dst,
4243+
LI->getLoopFor(Dst->getParent()), Pair[SI].Loops);
4244+
if (Pair[SI].Classification == Subscript::SIV) {
4245+
unsigned Level = 0;
4246+
const SCEV *SplitIter = nullptr;
4247+
(void)testSIV(Pair[SI].Src, Pair[SI].Dst, Level, Result, NewConstraint,
4248+
SplitIter);
4249+
if (Level == SplitLevel) {
4250+
assert(SplitIter != nullptr);
4251+
return SplitIter;
4252+
}
41874253
}
4254+
// If reclassified as non-SIV, we can't get a split iteration from this
4255+
// subscript.
41884256
break;
41894257
}
41904258
case Subscript::ZIV:
@@ -4216,13 +4284,33 @@ const SCEV *DependenceInfo::getSplitIteration(const Dependence &Dep,
42164284
while (Sivs.any()) {
42174285
bool Changed = false;
42184286
for (unsigned SJ : Sivs.set_bits()) {
4219-
// SJ is an SIV subscript that's part of the current coupled group
4220-
unsigned Level;
4287+
// SJ is an SIV subscript that's part of the current coupled group.
4288+
4289+
// Reclassify to handle cases where expressions may have become
4290+
// non-AddRec.
4291+
Pair[SJ].Classification = classifyPair(
4292+
Pair[SJ].Src, LI->getLoopFor(Src->getParent()), Pair[SJ].Dst,
4293+
LI->getLoopFor(Dst->getParent()), Pair[SJ].Loops);
4294+
4295+
unsigned Level = 0;
42214296
const SCEV *SplitIter = nullptr;
4222-
(void)testSIV(Pair[SJ].Src, Pair[SJ].Dst, Level, Result, NewConstraint,
4223-
SplitIter);
4224-
if (Level == SplitLevel && SplitIter)
4225-
return SplitIter;
4297+
4298+
if (Pair[SJ].Classification == Subscript::SIV) {
4299+
(void)testSIV(Pair[SJ].Src, Pair[SJ].Dst, Level, Result,
4300+
NewConstraint, SplitIter);
4301+
if (Level == SplitLevel && SplitIter)
4302+
return SplitIter;
4303+
} else {
4304+
// If reclassified as non-SIV, we can't get a split iteration from
4305+
// this subscript.
4306+
Sivs.reset(SJ);
4307+
continue;
4308+
}
4309+
4310+
LLVM_DEBUG(dbgs() << "MaxLevels = " << MaxLevels << "\n");
4311+
LLVM_DEBUG(dbgs() << "Level = " << Level << "\n");
4312+
assert(Level <= MaxLevels && "Level larger than MaxLevels.");
4313+
42264314
ConstrainedLevels.set(Level);
42274315
if (intersectConstraints(&Constraints[Level], &NewConstraint))
42284316
Changed = true;
@@ -4250,8 +4338,10 @@ const SCEV *DependenceInfo::getSplitIteration(const Dependence &Dep,
42504338
case Subscript::RDIV:
42514339
case Subscript::MIV:
42524340
break;
4253-
default:
4254-
llvm_unreachable("bad subscript classification");
4341+
case Subscript::NonLinear:
4342+
Result.Consistent = false;
4343+
Mivs.reset(SJ);
4344+
break;
42554345
}
42564346
}
42574347
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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>" 2>&1 | FileCheck %s
3+
4+
; Test case for bug #148435 - SIV test assertion failure.
5+
; This test ensures that testSIV handles the case where neither Src nor Dst
6+
; expressions contain AddRec after propagation, which can happen when
7+
; constraints simplify the expressions to non-AddRec forms.
8+
9+
define void @_Z1cb(ptr %a) {
10+
; CHECK-LABEL: '_Z1cb'
11+
; CHECK-NEXT: Src: store i8 0, ptr %arrayidx9, align 1 --> Dst: store i8 0, ptr %arrayidx9, align 1
12+
; CHECK-NEXT: da analyze - output [*]!
13+
;
14+
entry:
15+
br label %for.body
16+
17+
for.cond.cleanup.loopexit: ; preds = %for.body
18+
ret void
19+
20+
for.body: ; preds = %for.body, %entry
21+
%indvars.iv23 = phi i64 [ 0, %entry ], [ %indvars.iv.next24, %for.body ]
22+
%idxprom = and i64 %indvars.iv23, 1
23+
%arrayidx9 = getelementptr inbounds [0 x [12 x [12 x i8]]], ptr %a, i64 0, i64 %idxprom, i64 0, i64 %indvars.iv23
24+
store i8 0, ptr %arrayidx9, align 1
25+
%indvars.iv.next24 = add i64 %indvars.iv23, 1
26+
%exitcond.not = icmp eq i64 %indvars.iv.next24, 0
27+
br i1 %exitcond.not, label %for.cond.cleanup.loopexit, label %for.body
28+
}
29+
30+
@a = external global [0 x [12 x [12 x i8]]], align 1
31+
32+
define void @test_siv_no_addrec(i1 %d, i32 %b) {
33+
; CHECK-LABEL: 'test_siv_no_addrec'
34+
; CHECK-NEXT: Src: store i8 0, ptr %arrayidx7, align 1 --> Dst: store i8 0, ptr %arrayidx7, align 1
35+
; CHECK-NEXT: da analyze - output [* *]!
36+
;
37+
entry:
38+
%conv.val = select i1 %d, i16 1, i16 0
39+
br label %for.cond
40+
41+
for.cond: ; preds = %for.inc8, %entry
42+
%e.0 = phi i32 [ %b, %entry ], [ %inc9, %for.inc8 ]
43+
%cmp = icmp ult i32 %e.0, 10
44+
br i1 %cmp, label %for.cond1, label %for.end10
45+
46+
for.cond1: ; preds = %for.inc, %for.cond
47+
%f.0 = phi i16 [ %conv.val, %for.cond ], [ %add, %for.inc ]
48+
%cmp2 = icmp slt i16 %f.0, 10
49+
br i1 %cmp2, label %for.body4, label %for.inc8
50+
51+
for.body4: ; preds = %for.cond1
52+
%sub = add i32 %e.0, -3
53+
%idxprom = zext i32 %sub to i64
54+
%idxprom5 = sext i16 %f.0 to i64
55+
%idxprom6 = zext i32 %e.0 to i64
56+
%arrayidx7 = getelementptr inbounds [0 x [12 x [12 x i8]]], ptr @a, i64 0, i64 %idxprom, i64 %idxprom5, i64 %idxprom6
57+
store i8 0, ptr %arrayidx7, align 1
58+
br label %for.inc
59+
60+
for.inc: ; preds = %for.body4
61+
%add = add i16 %f.0, 2
62+
br label %for.cond1
63+
64+
for.inc8: ; preds = %for.cond1
65+
%inc9 = add i32 %e.0, 1
66+
br label %for.cond
67+
68+
for.end10: ; preds = %for.cond
69+
ret void
70+
}
71+
72+
define void @f1(ptr %a) {
73+
; CHECK-LABEL: 'f1'
74+
; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
75+
; CHECK-NEXT: da analyze - none!
76+
; Note: the second patch for PR148435 modifies the above CHECK to correct "output [*]".
77+
;
78+
entry:
79+
br label %loop
80+
81+
loop:
82+
%i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
83+
%and = and i64 %i, 1
84+
%idx = getelementptr inbounds [4 x [4 x i8]], ptr %a, i64 0, i64 %and, i64 %and
85+
store i8 0, ptr %idx
86+
%i.next = add i64 %i, 1
87+
%exitcond.not = icmp slt i64 %i.next, 8
88+
br i1 %exitcond.not, label %loop, label %exit
89+
90+
exit:
91+
ret void
92+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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>" 2>&1 | FileCheck %s
3+
4+
; Test case for SmallBitVector bounds checking bug in DependenceAnalysis.
5+
; This test ensures that loop index mapping functions don't cause out-of-bounds
6+
; access to SmallBitVector when loop depths exceed MaxLevels.
7+
8+
define void @bounds_check_test(ptr %a) {
9+
; CHECK-LABEL: 'bounds_check_test'
10+
; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
11+
; CHECK-NEXT: da analyze - none!
12+
;
13+
entry:
14+
br label %loop
15+
16+
loop:
17+
%i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
18+
%and = and i64 %i, 1 ; Creates index 0 or 1
19+
%idx = getelementptr inbounds [4 x [4 x i8]], ptr %a, i64 0, i64 %and, i64 %i
20+
store i8 0, ptr %idx
21+
%i.next = add i64 %i, 1
22+
%exitcond.not = icmp slt i64 %i.next, 4
23+
br i1 %exitcond.not, label %loop, label %exit
24+
25+
exit:
26+
ret void
27+
}

0 commit comments

Comments
 (0)