Skip to content

Commit 6c358c6

Browse files
committed
[DependenceAnalysis] Fix SIV test crash when no AddRec after propagation
Fixes GitHub issue #148435 where testSIV() would hit an assertion failure when neither Src nor Dst expressions contain AddRec after constraint propagation. This can occur when propagation simplifies expressions to non-AddRec forms. The subscript is effectively a zero induction variable. The fix falls back to ZIV analysis in this case, treating the simplified expressions as loop-invariant, which is the correct behavior when all induction variable references have been eliminated through propagation. The patch also fixes a MIV case that may decay into a ZIV test. Add missing NonLinear case to switch statements in propagation code to prevent 'bad subscript classification' crash when subscripts are reclassified as NonLinear after constraint propagation. Add regression test to prevent future occurrences of this issue.
1 parent 1b9e9e2 commit 6c358c6

File tree

3 files changed

+170
-15
lines changed

3 files changed

+170
-15
lines changed

llvm/lib/Analysis/DependenceAnalysis.cpp

Lines changed: 46 additions & 15 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

@@ -2281,8 +2290,14 @@ bool DependenceInfo::testSIV(const SCEV *Src, const SCEV *Dst, unsigned &Level,
22812290
Result, NewConstraint) ||
22822291
gcdMIVtest(Src, Dst, Result);
22832292
}
2284-
llvm_unreachable("SIV test expected at least one AddRec");
2285-
return false;
2293+
// If neither expression is an AddRec, this means propagation has simplified
2294+
// them to non-AddRec forms. In this case, fall back to ZIV analysis since
2295+
// the expressions are effectively loop-invariant.
2296+
LLVM_DEBUG(dbgs() << " falling back to ZIV test due to no AddRec\n");
2297+
// Set to first valid level to avoid Level=0 causing DV[-1] access.
2298+
// See comment in establishNestingLevels.
2299+
Level = 1;
2300+
return testZIV(Src, Dst, Result);
22862301
}
22872302

22882303
// testRDIV -
@@ -2343,8 +2358,14 @@ bool DependenceInfo::testRDIV(const SCEV *Src, const SCEV *Dst,
23432358
SrcLoop = DstAddRec->getLoop();
23442359
} else
23452360
llvm_unreachable("RDIV reached by surprising SCEVs");
2346-
} else
2347-
llvm_unreachable("RDIV expected at least one AddRec");
2361+
} else {
2362+
// If neither expression is an AddRec, this means propagation has simplified
2363+
// them to non-AddRec forms. Fall back to ZIV analysis since the expressions
2364+
// are effectively loop-invariant.
2365+
LLVM_DEBUG(
2366+
dbgs() << " RDIV falling back to ZIV test due to no AddRec\n");
2367+
return testZIV(Src, Dst, Result);
2368+
}
23482369
return exactRDIVtest(SrcCoeff, DstCoeff, SrcConst, DstConst, SrcLoop, DstLoop,
23492370
Result) ||
23502371
gcdMIVtest(Src, Dst, Result) ||
@@ -3821,7 +3842,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
38213842
break;
38223843
case Subscript::SIV: {
38233844
LLVM_DEBUG(dbgs() << ", SIV\n");
3824-
unsigned Level;
3845+
unsigned Level = 0;
38253846
const SCEV *SplitIter = nullptr;
38263847
if (testSIV(Pair[SI].Src, Pair[SI].Dst, Level, Result, NewConstraint,
38273848
SplitIter))
@@ -3872,12 +3893,17 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
38723893
for (unsigned SJ : Sivs.set_bits()) {
38733894
LLVM_DEBUG(dbgs() << "testing subscript " << SJ << ", SIV\n");
38743895
// SJ is an SIV subscript that's part of the current coupled group
3875-
unsigned Level;
3896+
unsigned Level = 0;
38763897
const SCEV *SplitIter = nullptr;
38773898
LLVM_DEBUG(dbgs() << "SIV\n");
38783899
if (testSIV(Pair[SJ].Src, Pair[SJ].Dst, Level, Result, NewConstraint,
38793900
SplitIter))
38803901
return nullptr;
3902+
3903+
LLVM_DEBUG(dbgs() << "MaxLevels = " << MaxLevels << "\n");
3904+
LLVM_DEBUG(dbgs() << "Level = " << Level << "\n");
3905+
assert(Level <= MaxLevels && "Level larger than MaxLevels.");
3906+
38813907
ConstrainedLevels.set(Level);
38823908
if (intersectConstraints(&Constraints[Level], &NewConstraint)) {
38833909
if (Constraints[Level].isEmpty()) {
@@ -4155,7 +4181,7 @@ const SCEV *DependenceInfo::getSplitIteration(const Dependence &Dep,
41554181
for (unsigned SI : Separable.set_bits()) {
41564182
switch (Pair[SI].Classification) {
41574183
case Subscript::SIV: {
4158-
unsigned Level;
4184+
unsigned Level = 0;
41594185
const SCEV *SplitIter = nullptr;
41604186
(void)testSIV(Pair[SI].Src, Pair[SI].Dst, Level, Result, NewConstraint,
41614187
SplitIter);
@@ -4195,12 +4221,17 @@ const SCEV *DependenceInfo::getSplitIteration(const Dependence &Dep,
41954221
bool Changed = false;
41964222
for (unsigned SJ : Sivs.set_bits()) {
41974223
// SJ is an SIV subscript that's part of the current coupled group
4198-
unsigned Level;
4224+
unsigned Level = 0;
41994225
const SCEV *SplitIter = nullptr;
42004226
(void)testSIV(Pair[SJ].Src, Pair[SJ].Dst, Level, Result, NewConstraint,
42014227
SplitIter);
42024228
if (Level == SplitLevel && SplitIter)
42034229
return SplitIter;
4230+
4231+
LLVM_DEBUG(dbgs() << "MaxLevels = " << MaxLevels << "\n");
4232+
LLVM_DEBUG(dbgs() << "Level = " << Level << "\n");
4233+
assert(Level <= MaxLevels && "Level larger than MaxLevels.");
4234+
42044235
ConstrainedLevels.set(Level);
42054236
if (intersectConstraints(&Constraints[Level], &NewConstraint))
42064237
Changed = true;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
10+
target triple = "aarch64-unknown-linux-gnu"
11+
12+
define void @_Z1cb(ptr %a) {
13+
; CHECK-LABEL: '_Z1cb'
14+
; CHECK-NEXT: Src: store i8 0, ptr %arrayidx9, align 1 --> Dst: store i8 0, ptr %arrayidx9, align 1
15+
; CHECK-NEXT: da analyze - output [*]!
16+
;
17+
entry:
18+
br label %for.body
19+
20+
for.cond.cleanup.loopexit: ; preds = %for.body
21+
ret void
22+
23+
for.body: ; preds = %for.body, %entry
24+
%indvars.iv23 = phi i64 [ 0, %entry ], [ %indvars.iv.next24, %for.body ]
25+
%idxprom = and i64 %indvars.iv23, 1
26+
%arrayidx9 = getelementptr inbounds [0 x [12 x [12 x i8]]], ptr %a, i64 0, i64 %idxprom, i64 0, i64 %indvars.iv23
27+
store i8 0, ptr %arrayidx9, align 1
28+
%indvars.iv.next24 = add i64 %indvars.iv23, 1
29+
%exitcond.not = icmp eq i64 %indvars.iv.next24, 0
30+
br i1 %exitcond.not, label %for.cond.cleanup.loopexit, label %for.body
31+
}
32+
33+
@a = external global [0 x [12 x [12 x i8]]], align 1
34+
35+
define void @test_siv_no_addrec(i1 %d, i32 %b) {
36+
; CHECK-LABEL: 'test_siv_no_addrec'
37+
; CHECK-NEXT: Src: store i8 0, ptr %arrayidx7, align 1 --> Dst: store i8 0, ptr %arrayidx7, align 1
38+
; CHECK-NEXT: da analyze - output [* *]!
39+
;
40+
entry:
41+
%conv.val = select i1 %d, i16 1, i16 0
42+
br label %for.cond
43+
44+
for.cond: ; preds = %for.inc8, %entry
45+
%e.0 = phi i32 [ %b, %entry ], [ %inc9, %for.inc8 ]
46+
%cmp = icmp ult i32 %e.0, 10
47+
br i1 %cmp, label %for.cond1, label %for.end10
48+
49+
for.cond1: ; preds = %for.inc, %for.cond
50+
%f.0 = phi i16 [ %conv.val, %for.cond ], [ %add, %for.inc ]
51+
%cmp2 = icmp slt i16 %f.0, 10
52+
br i1 %cmp2, label %for.body4, label %for.inc8
53+
54+
for.body4: ; preds = %for.cond1
55+
%sub = add i32 %e.0, -3
56+
%idxprom = zext i32 %sub to i64
57+
%idxprom5 = sext i16 %f.0 to i64
58+
%idxprom6 = zext i32 %e.0 to i64
59+
%arrayidx7 = getelementptr inbounds [0 x [12 x [12 x i8]]], ptr @a, i64 0, i64 %idxprom, i64 %idxprom5, i64 %idxprom6
60+
store i8 0, ptr %arrayidx7, align 1
61+
br label %for.inc
62+
63+
for.inc: ; preds = %for.body4
64+
%add = add i16 %f.0, 2
65+
br label %for.cond1
66+
67+
for.inc8: ; preds = %for.cond1
68+
%inc9 = add i32 %e.0, 1
69+
br label %for.cond
70+
71+
for.end10: ; preds = %for.cond
72+
ret void
73+
}
74+
75+
define void @f1(ptr %a) {
76+
; CHECK-LABEL: 'f1'
77+
; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
78+
; CHECK-NEXT: da analyze - none!
79+
; Note: the second patch for PR148435 modifies the above CHECK to correct "output [*]".
80+
;
81+
entry:
82+
br label %loop
83+
84+
loop:
85+
%i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
86+
%and = and i64 %i, 1
87+
%idx = getelementptr inbounds [4 x [4 x i8]], ptr %a, i64 0, i64 %and, i64 %and
88+
store i8 0, ptr %idx
89+
%i.next = add i64 %i, 1
90+
%exitcond.not = icmp slt i64 %i.next, 8
91+
br i1 %exitcond.not, label %loop, label %exit
92+
93+
exit:
94+
ret void
95+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
9+
10+
define void @bounds_check_test(ptr %a) {
11+
; CHECK-LABEL: 'bounds_check_test'
12+
; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1
13+
; CHECK-NEXT: da analyze - none!
14+
;
15+
entry:
16+
br label %loop
17+
18+
loop:
19+
%i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
20+
%and = and i64 %i, 1 ; Creates index 0 or 1
21+
%idx = getelementptr inbounds [4 x [4 x i8]], ptr %a, i64 0, i64 %and, i64 %i
22+
store i8 0, ptr %idx
23+
%i.next = add i64 %i, 1
24+
%exitcond.not = icmp slt i64 %i.next, 4
25+
br i1 %exitcond.not, label %loop, label %exit
26+
27+
exit:
28+
ret void
29+
}

0 commit comments

Comments
 (0)