diff options
author | Sebastian Pop <spop@nvidia.com> | 2025-08-22 18:50:18 -0500 |
---|---|---|
committer | Sebastian Pop <spop@nvidia.com> | 2025-09-09 14:40:22 -0500 |
commit | 7ad8fffa551a9d56a69dc18045afe8ede1cbcfb7 (patch) | |
tree | a27684022584412df6db7ca68495b86db4492813 | |
parent | ca28604e723af7940ab764a139fa512f5fa59ebc (diff) | |
download | llvm-users/sebpop/pr149977.zip llvm-users/sebpop/pr149977.tar.gz llvm-users/sebpop/pr149977.tar.bz2 |
[DA] Fix Strong SIV test for symbolic coefficients and deltas (#149977)users/sebpop/pr149977
Fixes GitHub issue #149977 where Strong SIV test incorrectly rejected
dependencies with symbolic coefficients and deltas due to overly conservative
bound checking.
Root cause: The bound constraint check |Delta| > UpperBound * |Coeff| would
prematurely reject dependencies when SCEV couldn't prove the relationship
definitively for symbolic expressions, preventing the analysis from reaching
the division logic.
Solution:
1. Make bound check less conservative for symbolic expressions by adding
runtime assumptions when SCEV cannot determine the relationship.
2. Enable symbolic division using SE->getUDivExactExpr for Delta/Coeff.
3. Add runtime assumptions where symbolic division cannot be computed.
This enables precise dependence analysis for cases like:
- Coefficient: -k (symbolic)
- Delta: -(2*k + 1) (symbolic)
- Distance: (2*k + 1)/k (computed symbolically)
Test case validates:
- When k = -1: distance = 1, clear flow dependence detected.
- Runtime assumptions ensure bounds are satisfied.
-rw-r--r-- | llvm/lib/Analysis/DependenceAnalysis.cpp | 68 | ||||
-rw-r--r-- | llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll | 2 | ||||
-rw-r--r-- | llvm/test/Analysis/DependenceAnalysis/DADelin.ll | 4 | ||||
-rw-r--r-- | llvm/test/Analysis/DependenceAnalysis/PR149977.ll | 45 | ||||
-rw-r--r-- | llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll | 2 | ||||
-rw-r--r-- | llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll | 2 |
6 files changed, 111 insertions, 12 deletions
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp index ad5415d..d1ae9cd 100644 --- a/llvm/lib/Analysis/DependenceAnalysis.cpp +++ b/llvm/lib/Analysis/DependenceAnalysis.cpp @@ -1249,10 +1249,33 @@ bool DependenceInfo::strongSIVtest(const SCEV *Coeff, const SCEV *SrcConst, SE->isKnownNonNegative(Coeff) ? Coeff : SE->getNegativeSCEV(Coeff); const SCEV *Product = SE->getMulExpr(UpperBound, AbsCoeff); if (isKnownPredicate(CmpInst::ICMP_SGT, AbsDelta, Product)) { - // Distance greater than trip count - no dependence - ++StrongSIVindependence; - ++StrongSIVsuccesses; - return true; + // Check if this involves symbolic expressions where we might be too + // conservative. + if (isa<SCEVUnknown>(Delta) || isa<SCEVUnknown>(Coeff) || + !isa<SCEVConstant>(AbsDelta) || !isa<SCEVConstant>(Product)) { + // For symbolic expressions, add runtime assumption rather than + // rejecting. + const SCEVPredicate *BoundPred = + SE->getComparePredicate(ICmpInst::ICMP_SLE, AbsDelta, Product); + if (UnderRuntimeAssumptions) { + SmallVector<const SCEVPredicate *, 4> NewPreds( + Assumptions.getPredicates()); + NewPreds.push_back(BoundPred); + const_cast<DependenceInfo *>(this)->Assumptions = + SCEVUnionPredicate(NewPreds, *SE); + LLVM_DEBUG(dbgs() << "\t Added runtime bound assumption\n"); + } else { + // Cannot add runtime assumptions, let more complex tests try. + LLVM_DEBUG(dbgs() << "\t Would need runtime bound assumption but " + "not allowed. Failing this test.\n"); + return false; + } + } else { + // Distance definitely greater than trip count - no dependence + ++StrongSIVindependence; + ++StrongSIVsuccesses; + return true; + } } } @@ -1293,9 +1316,40 @@ bool DependenceInfo::strongSIVtest(const SCEV *Coeff, const SCEV *SrcConst, Result.DV[Level].Distance = Delta; // since X/1 == X NewConstraint.setDistance(Delta, CurLoop); } else { - Result.Consistent = false; - NewConstraint.setLine(Coeff, SE->getNegativeSCEV(Coeff), - SE->getNegativeSCEV(Delta), CurLoop); + // Try symbolic division: Distance = Delta / Coeff. + if (const SCEV *Distance = SE->getUDivExactExpr(Delta, Coeff)) { + LLVM_DEBUG(dbgs() << "\t Symbolic distance = " << *Distance << "\n"); + Result.DV[Level].Distance = Distance; + NewConstraint.setDistance(Distance, CurLoop); + } else { + // Cannot compute exact division - check if we can add runtime + // assumptions. + if (isa<SCEVUnknown>(Coeff) && !SE->isKnownNonZero(Coeff)) { + // Add runtime assumption that coefficient is non-zero for division. + const SCEV *Zero = SE->getZero(Coeff->getType()); + const SCEVPredicate *NonZeroPred = + SE->getComparePredicate(ICmpInst::ICMP_NE, Coeff, Zero); + if (UnderRuntimeAssumptions) { + SmallVector<const SCEVPredicate *, 4> NewPreds( + Assumptions.getPredicates()); + NewPreds.push_back(NonZeroPred); + const_cast<DependenceInfo *>(this)->Assumptions = + SCEVUnionPredicate(NewPreds, *SE); + LLVM_DEBUG(dbgs() << "\t Added runtime assumption: " << *Coeff + << " != 0 for symbolic division\n"); + } else { + // Cannot add runtime assumptions, this test fails. + LLVM_DEBUG(dbgs() + << "\t Would need runtime assumption " << *Coeff + << " != 0 but not allowed. Failing this test.\n"); + return false; + } + } + + Result.Consistent = false; + NewConstraint.setLine(Coeff, SE->getNegativeSCEV(Coeff), + SE->getNegativeSCEV(Delta), CurLoop); + } } // maybe we can get a useful direction diff --git a/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll b/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll index 81e461a..8933c5f 100644 --- a/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll +++ b/llvm/test/Analysis/DependenceAnalysis/BasePtrBug.ll @@ -18,7 +18,7 @@ define void @test1(ptr nocapture %A, ptr nocapture %B, i32 %N) #0 { ; CHECK-NEXT: Src: %0 = load i32, ptr %gep.0, align 4 --> Dst: %0 = load i32, ptr %gep.0, align 4 ; CHECK-NEXT: da analyze - none! ; CHECK-NEXT: Src: %0 = load i32, ptr %gep.0, align 4 --> Dst: %1 = load i32, ptr %gep.1, align 4 -; CHECK-NEXT: da analyze - input [*|<]! +; CHECK-NEXT: da analyze - consistent input [((-4 * (sext i32 %div to i64))<nsw> /u 4)|<]! ; CHECK-NEXT: Src: %0 = load i32, ptr %gep.0, align 4 --> Dst: store i32 %add, ptr %gep.B, align 4 ; CHECK-NEXT: da analyze - confused! ; CHECK-NEXT: Src: %1 = load i32, ptr %gep.1, align 4 --> Dst: %1 = load i32, ptr %gep.1, align 4 diff --git a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll index 8f94a45..6e50712 100644 --- a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll +++ b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll @@ -648,7 +648,7 @@ define void @coeff_may_negative(ptr %a, i32 %k) { ; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.0, align 1 ; CHECK-NEXT: da analyze - none! ; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1 -; CHECK-NEXT: da analyze - output [*|<]! +; CHECK-NEXT: da analyze - consistent output [((-1 * %k) /u %k)|<]! ; CHECK-NEXT: Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1 ; CHECK-NEXT: da analyze - none! ; @@ -687,7 +687,7 @@ define void @coeff_positive(ptr %a, i32 %k) { ; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.0, align 1 ; CHECK-NEXT: da analyze - none! ; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1 -; CHECK-NEXT: da analyze - output [*|<]! +; CHECK-NEXT: da analyze - consistent output [((-1 * %k) /u %k)|<]! ; CHECK-NEXT: Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1 ; CHECK-NEXT: da analyze - none! ; diff --git a/llvm/test/Analysis/DependenceAnalysis/PR149977.ll b/llvm/test/Analysis/DependenceAnalysis/PR149977.ll new file mode 100644 index 0000000..1576fe9 --- /dev/null +++ b/llvm/test/Analysis/DependenceAnalysis/PR149977.ll @@ -0,0 +1,45 @@ +; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -disable-output "-passes=print<da>" 2>&1 | FileCheck %s + +; Test case for GitHub issue #149977: Strong SIV test with symbolic coefficients and deltas +; The issue was that the bound constraint check was overly conservative with symbolic expressions, +; causing valid dependencies to be rejected before reaching the division logic. +; +; Mathematical analysis: +; - Access patterns: a[-k*i] vs a[-k*i + (2*k + 1)] +; - Strong SIV equation: -k*(i2-i1) = (2*k + 1) +; - Distance: (2*k + 1)/k +; - For k=-1: distance = -1/-1 = 1 (clear dependence) + +define void @f(ptr %a, i64 %k) { +; CHECK-LABEL: 'f' +; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.0, align 1 +; CHECK-NEXT: da analyze - none! +; CHECK-NEXT: Src: store i8 42, ptr %idx.0, align 1 --> Dst: store i8 42, ptr %idx.1, align 1 +; CHECK-NEXT: da analyze - consistent output [((-1 + (-2 * %k)) /u (-1 * %k))]! +; CHECK-NEXT: Runtime Assumptions: +; CHECK-NEXT: Compare predicate: (1 + (2 * %k))<nuw><nsw> sle) (2 * %k) +; CHECK-NEXT: Src: store i8 42, ptr %idx.1, align 1 --> Dst: store i8 42, ptr %idx.1, align 1 +; CHECK-NEXT: da analyze - none! +; +entry: + %mk = sub i64 0, %k ; mk = -k + %kk = mul i64 %k, 2 ; kk = 2*k + %kk.inc = add i64 1, %kk ; kk.inc = 2*k + 1 + br label %loop + +loop: + %i = phi i64 [ 0, %entry ], [ %i.next, %loop ] + %subscript.0 = mul i64 %mk, %i ; -k * i + %subscript.1 = add i64 %subscript.0, %kk.inc ; -k * i + (2*k + 1) + %idx.0 = getelementptr i8, ptr %a, i64 %subscript.0 + %idx.1 = getelementptr i8, ptr %a, i64 %subscript.1 + store i8 42, ptr %idx.0 + store i8 42, ptr %idx.1 + %i.next = add i64 %i, 1 + %cond.exit = icmp eq i64 %i.next, 3 + br i1 %cond.exit, label %exit, label %loop + +exit: + ret void +} diff --git a/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll b/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll index 44bd9b7..b7d2587 100644 --- a/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll +++ b/llvm/test/Analysis/DependenceAnalysis/StrongSIV.ll @@ -385,7 +385,7 @@ define void @strong8(ptr %A, ptr %B, i64 %n) nounwind uwtable ssp { ; CHECK-NEXT: Src: store i32 %conv, ptr %arrayidx, align 4 --> Dst: store i32 %conv, ptr %arrayidx, align 4 ; CHECK-NEXT: da analyze - none! ; CHECK-NEXT: Src: store i32 %conv, ptr %arrayidx, align 4 --> Dst: %0 = load i32, ptr %arrayidx1, align 4 -; CHECK-NEXT: da analyze - flow [*|<]! +; CHECK-NEXT: da analyze - consistent flow [((4 * %n) /u 4)|<]! ; CHECK-NEXT: Src: store i32 %conv, ptr %arrayidx, align 4 --> Dst: store i32 %0, ptr %B.addr.01, align 4 ; CHECK-NEXT: da analyze - confused! ; CHECK-NEXT: Src: %0 = load i32, ptr %arrayidx1, align 4 --> Dst: %0 = load i32, ptr %arrayidx1, align 4 diff --git a/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll b/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll index cdfaec7..4b8a06a 100644 --- a/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll +++ b/llvm/test/Analysis/DependenceAnalysis/SymbolicSIV.ll @@ -440,7 +440,7 @@ define void @symbolicsiv7(ptr %A, ptr %B, i64 %n, i64 %N, i64 %M) nounwind uwtab ; CHECK-NEXT: Src: store i32 %conv, ptr %arrayidx, align 4 --> Dst: store i32 %conv, ptr %arrayidx, align 4 ; CHECK-NEXT: da analyze - none! ; CHECK-NEXT: Src: store i32 %conv, ptr %arrayidx, align 4 --> Dst: %1 = load i32, ptr %arrayidx6, align 4 -; CHECK-NEXT: da analyze - flow [<>]! +; CHECK-NEXT: da analyze - consistent flow [((-8 + (16 * %M)) /u (8 * %N))]! ; CHECK-NEXT: Src: store i32 %conv, ptr %arrayidx, align 4 --> Dst: store i32 %1, ptr %B.addr.02, align 4 ; CHECK-NEXT: da analyze - confused! ; CHECK-NEXT: Src: %1 = load i32, ptr %arrayidx6, align 4 --> Dst: %1 = load i32, ptr %arrayidx6, align 4 |