diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp index f33e04e804e3d..c6c2bc9b4ba25 100644 --- a/llvm/lib/Analysis/DependenceAnalysis.cpp +++ b/llvm/lib/Analysis/DependenceAnalysis.cpp @@ -873,6 +873,9 @@ void DependenceInfo::collectCommonLoops(const SCEV *Expression, SmallBitVector &Loops) const { while (LoopNest) { unsigned Level = LoopNest->getLoopDepth(); + LLVM_DEBUG(dbgs() << "MaxLevels = " << MaxLevels << "\n"); + LLVM_DEBUG(dbgs() << "Level = " << Level << "\n"); + assert(Level <= MaxLevels && "Level larger than MaxLevels."); if (Level <= CommonLevels && !SE->isLoopInvariant(Expression, LoopNest)) Loops.set(Level); LoopNest = LoopNest->getParentLoop(); @@ -959,6 +962,10 @@ bool DependenceInfo::checkSubscript(const SCEV *Expr, const Loop *LoopNest, if (!AddRec) return isLoopInvariant(Expr, LoopNest); + const SCEV *Step = AddRec->getStepRecurrence(*SE); + if (!isLoopInvariant(Step, LoopNest)) + return false; + // The AddRec must depend on one of the containing loops. Otherwise, // mapSrcLoop and mapDstLoop return indices outside the intended range. This // 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, if (!L) return false; + unsigned Level = IsSrc ? mapSrcLoop(L) : mapDstLoop(L); + // Check that the mapped loop index is within bounds for the SmallBitVector. + // This can happen when loop depths exceed MaxLevels due to the mapping + // algorithm. + + LLVM_DEBUG(dbgs() << "MaxLevels = " << MaxLevels << "\n"); + LLVM_DEBUG(dbgs() << "Level = " << Level << "\n"); + assert(Level <= MaxLevels && "Level larger than MaxLevels."); + Loops.set(Level); const SCEV *Start = AddRec->getStart(); - const SCEV *Step = AddRec->getStepRecurrence(*SE); - if (!isLoopInvariant(Step, LoopNest)) - return false; - if (IsSrc) - Loops.set(mapSrcLoop(AddRec->getLoop())); - else - Loops.set(mapDstLoop(AddRec->getLoop())); return checkSubscript(Start, LoopNest, Loops, IsSrc); } @@ -2281,8 +2290,14 @@ bool DependenceInfo::testSIV(const SCEV *Src, const SCEV *Dst, unsigned &Level, Result, NewConstraint) || gcdMIVtest(Src, Dst, Result); } - llvm_unreachable("SIV test expected at least one AddRec"); - return false; + // If neither expression is an AddRec, this means propagation has simplified + // them to non-AddRec forms. In this case, fall back to ZIV analysis since + // the expressions are effectively loop-invariant. + LLVM_DEBUG(dbgs() << " falling back to ZIV test due to no AddRec\n"); + // Set to first valid level to avoid Level=0 causing DV[-1] access. + // See comment in establishNestingLevels. + Level = 1; + return testZIV(Src, Dst, Result); } // testRDIV - @@ -2343,8 +2358,14 @@ bool DependenceInfo::testRDIV(const SCEV *Src, const SCEV *Dst, SrcLoop = DstAddRec->getLoop(); } else llvm_unreachable("RDIV reached by surprising SCEVs"); - } else - llvm_unreachable("RDIV expected at least one AddRec"); + } else { + // If neither expression is an AddRec, this means propagation has simplified + // them to non-AddRec forms. Fall back to ZIV analysis since the expressions + // are effectively loop-invariant. + LLVM_DEBUG( + dbgs() << " RDIV falling back to ZIV test due to no AddRec\n"); + return testZIV(Src, Dst, Result); + } return exactRDIVtest(SrcCoeff, DstCoeff, SrcConst, DstConst, SrcLoop, DstLoop, Result) || gcdMIVtest(Src, Dst, Result) || @@ -3821,7 +3842,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst, break; case Subscript::SIV: { LLVM_DEBUG(dbgs() << ", SIV\n"); - unsigned Level; + unsigned Level = 0; const SCEV *SplitIter = nullptr; if (testSIV(Pair[SI].Src, Pair[SI].Dst, Level, Result, NewConstraint, SplitIter)) @@ -3872,12 +3893,17 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst, for (unsigned SJ : Sivs.set_bits()) { LLVM_DEBUG(dbgs() << "testing subscript " << SJ << ", SIV\n"); // SJ is an SIV subscript that's part of the current coupled group - unsigned Level; + unsigned Level = 0; const SCEV *SplitIter = nullptr; LLVM_DEBUG(dbgs() << "SIV\n"); if (testSIV(Pair[SJ].Src, Pair[SJ].Dst, Level, Result, NewConstraint, SplitIter)) return nullptr; + + LLVM_DEBUG(dbgs() << "MaxLevels = " << MaxLevels << "\n"); + LLVM_DEBUG(dbgs() << "Level = " << Level << "\n"); + assert(Level <= MaxLevels && "Level larger than MaxLevels."); + ConstrainedLevels.set(Level); if (intersectConstraints(&Constraints[Level], &NewConstraint)) { if (Constraints[Level].isEmpty()) { @@ -4155,7 +4181,7 @@ const SCEV *DependenceInfo::getSplitIteration(const Dependence &Dep, for (unsigned SI : Separable.set_bits()) { switch (Pair[SI].Classification) { case Subscript::SIV: { - unsigned Level; + unsigned Level = 0; const SCEV *SplitIter = nullptr; (void)testSIV(Pair[SI].Src, Pair[SI].Dst, Level, Result, NewConstraint, SplitIter); @@ -4195,12 +4221,17 @@ const SCEV *DependenceInfo::getSplitIteration(const Dependence &Dep, bool Changed = false; for (unsigned SJ : Sivs.set_bits()) { // SJ is an SIV subscript that's part of the current coupled group - unsigned Level; + unsigned Level = 0; const SCEV *SplitIter = nullptr; (void)testSIV(Pair[SJ].Src, Pair[SJ].Dst, Level, Result, NewConstraint, SplitIter); if (Level == SplitLevel && SplitIter) return SplitIter; + + LLVM_DEBUG(dbgs() << "MaxLevels = " << MaxLevels << "\n"); + LLVM_DEBUG(dbgs() << "Level = " << Level << "\n"); + assert(Level <= MaxLevels && "Level larger than MaxLevels."); + ConstrainedLevels.set(Level); if (intersectConstraints(&Constraints[Level], &NewConstraint)) Changed = true; diff --git a/llvm/test/Analysis/DependenceAnalysis/PR148435.ll b/llvm/test/Analysis/DependenceAnalysis/PR148435.ll new file mode 100644 index 0000000000000..1633a91336f68 --- /dev/null +++ b/llvm/test/Analysis/DependenceAnalysis/PR148435.ll @@ -0,0 +1,95 @@ +; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -disable-output "-passes=print" 2>&1 | FileCheck %s + +; Test case for bug #148435 - SIV test assertion failure. +; This test ensures that testSIV handles the case where neither Src nor Dst +; expressions contain AddRec after propagation, which can happen when +; constraints simplify the expressions to non-AddRec forms. + +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" +target triple = "aarch64-unknown-linux-gnu" + +define void @_Z1cb(ptr %a) { +; CHECK-LABEL: '_Z1cb' +; CHECK-NEXT: Src: store i8 0, ptr %arrayidx9, align 1 --> Dst: store i8 0, ptr %arrayidx9, align 1 +; CHECK-NEXT: da analyze - output [*]! +; +entry: + br label %for.body + +for.cond.cleanup.loopexit: ; preds = %for.body + ret void + +for.body: ; preds = %for.body, %entry + %indvars.iv23 = phi i64 [ 0, %entry ], [ %indvars.iv.next24, %for.body ] + %idxprom = and i64 %indvars.iv23, 1 + %arrayidx9 = getelementptr inbounds [0 x [12 x [12 x i8]]], ptr %a, i64 0, i64 %idxprom, i64 0, i64 %indvars.iv23 + store i8 0, ptr %arrayidx9, align 1 + %indvars.iv.next24 = add i64 %indvars.iv23, 1 + %exitcond.not = icmp eq i64 %indvars.iv.next24, 0 + br i1 %exitcond.not, label %for.cond.cleanup.loopexit, label %for.body +} + +@a = external global [0 x [12 x [12 x i8]]], align 1 + +define void @test_siv_no_addrec(i1 %d, i32 %b) { +; CHECK-LABEL: 'test_siv_no_addrec' +; CHECK-NEXT: Src: store i8 0, ptr %arrayidx7, align 1 --> Dst: store i8 0, ptr %arrayidx7, align 1 +; CHECK-NEXT: da analyze - output [* *]! +; +entry: + %conv.val = select i1 %d, i16 1, i16 0 + br label %for.cond + +for.cond: ; preds = %for.inc8, %entry + %e.0 = phi i32 [ %b, %entry ], [ %inc9, %for.inc8 ] + %cmp = icmp ult i32 %e.0, 10 + br i1 %cmp, label %for.cond1, label %for.end10 + +for.cond1: ; preds = %for.inc, %for.cond + %f.0 = phi i16 [ %conv.val, %for.cond ], [ %add, %for.inc ] + %cmp2 = icmp slt i16 %f.0, 10 + br i1 %cmp2, label %for.body4, label %for.inc8 + +for.body4: ; preds = %for.cond1 + %sub = add i32 %e.0, -3 + %idxprom = zext i32 %sub to i64 + %idxprom5 = sext i16 %f.0 to i64 + %idxprom6 = zext i32 %e.0 to i64 + %arrayidx7 = getelementptr inbounds [0 x [12 x [12 x i8]]], ptr @a, i64 0, i64 %idxprom, i64 %idxprom5, i64 %idxprom6 + store i8 0, ptr %arrayidx7, align 1 + br label %for.inc + +for.inc: ; preds = %for.body4 + %add = add i16 %f.0, 2 + br label %for.cond1 + +for.inc8: ; preds = %for.cond1 + %inc9 = add i32 %e.0, 1 + br label %for.cond + +for.end10: ; preds = %for.cond + ret void +} + +define void @f1(ptr %a) { +; CHECK-LABEL: 'f1' +; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1 +; CHECK-NEXT: da analyze - none! +; Note: the second patch for PR148435 modifies the above CHECK to correct "output [*]". +; +entry: + br label %loop + +loop: + %i = phi i64 [ 0, %entry ], [ %i.next, %loop ] + %and = and i64 %i, 1 + %idx = getelementptr inbounds [4 x [4 x i8]], ptr %a, i64 0, i64 %and, i64 %and + store i8 0, ptr %idx + %i.next = add i64 %i, 1 + %exitcond.not = icmp slt i64 %i.next, 8 + br i1 %exitcond.not, label %loop, label %exit + +exit: + ret void +} diff --git a/llvm/test/Analysis/DependenceAnalysis/bounds-check.ll b/llvm/test/Analysis/DependenceAnalysis/bounds-check.ll new file mode 100644 index 0000000000000..dca86e5e55643 --- /dev/null +++ b/llvm/test/Analysis/DependenceAnalysis/bounds-check.ll @@ -0,0 +1,29 @@ +; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -disable-output "-passes=print" 2>&1 | FileCheck %s + +; Test case for SmallBitVector bounds checking bug in DependenceAnalysis. +; This test ensures that loop index mapping functions don't cause out-of-bounds +; access to SmallBitVector when loop depths exceed MaxLevels. + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" + +define void @bounds_check_test(ptr %a) { +; CHECK-LABEL: 'bounds_check_test' +; CHECK-NEXT: Src: store i8 0, ptr %idx, align 1 --> Dst: store i8 0, ptr %idx, align 1 +; CHECK-NEXT: da analyze - none! +; +entry: + br label %loop + +loop: + %i = phi i64 [ 0, %entry ], [ %i.next, %loop ] + %and = and i64 %i, 1 ; Creates index 0 or 1 + %idx = getelementptr inbounds [4 x [4 x i8]], ptr %a, i64 0, i64 %and, i64 %i + store i8 0, ptr %idx + %i.next = add i64 %i, 1 + %exitcond.not = icmp slt i64 %i.next, 4 + br i1 %exitcond.not, label %loop, label %exit + +exit: + ret void +}