-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[DA] Fix zero coeff bug in Strong SIV test with runtime assumptions #155037
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-llvm-analysis Author: Sebastian Pop (sebpop) ChangesFix GitHub issue #149991 where Strong SIV test incorrectly concludes 'none!' for symbolic coefficients that could be zero, leading to 0/0 undef behavior. The issue occurs in subscripts like {base,+,coeff} where coeff is symbolic:
The Strong SIV test's Delta=0 case assumed 0/X=0 where X is the coefficient, but when X could be zero, we have 0/0 which is undefined. The analysis needs to be conservative when the coefficient might be zero. When coefficient is SCEVUnknown and cannot be proven non-zero at compile time, use SCEV range analysis to attempt proving coefficient > 0. If this fails, add a runtime assumption 'coeff > 0' to the dependence result. This allows precise analysis when possible (none! under assumption coeff > 0) while maintaining correctness by exposing the required assumption. Test cases:
Full diff: https://github.com/llvm/llvm-project/pull/155037.diff 3 Files Affected:
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index f33e04e804e3d..50e0fb7b3bcd3 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -1282,7 +1282,28 @@ bool DependenceInfo::strongSIVtest(const SCEV *Coeff, const SCEV *SrcConst,
Result.DV[Level].Direction &= Dependence::DVEntry::EQ;
++StrongSIVsuccesses;
} else if (Delta->isZero()) {
- // since 0/X == 0
+ // Check if coefficient could be zero. If so, 0/0 is undefined and we
+ // cannot conclude that only same-iteration dependencies exist.
+ // When coeff=0, all iterations access the same location.
+ if (isa<SCEVUnknown>(Coeff) && !SE->isKnownNonZero(Coeff)) {
+ // Use SCEV range analysis to prove coefficient > 0 in loop context.
+ const SCEV *Zero = SE->getZero(Coeff->getType());
+
+ // Ask SCEV's range analysis if it can prove Coeff > Zero
+ if (SE->isKnownPredicate(ICmpInst::ICMP_SGT, Coeff, Zero)) {
+ LLVM_DEBUG(
+ dbgs()
+ << "\t Coefficient proven positive by SCEV range analysis\n");
+ } else {
+ // Cannot prove at compile time, add runtime assumption
+ const SCEVPredicate *Pred =
+ SE->getComparePredicate(ICmpInst::ICMP_SGT, Coeff, Zero);
+ const_cast<DependenceInfo *>(this)->Assumptions.push_back(Pred);
+ LLVM_DEBUG(dbgs() << "\t Added runtime assumption: " << *Coeff
+ << " > 0\n");
+ }
+ }
+ // since 0/X == 0 (where X is known non-zero)
Result.DV[Level].Distance = Delta;
NewConstraint.setDistance(Delta, CurLoop);
Result.DV[Level].Direction &= Dependence::DVEntry::EQ;
@@ -3574,8 +3595,8 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
if (!isLoadOrStore(Src) || !isLoadOrStore(Dst)) {
// can only analyze simple loads and stores, i.e., no calls, invokes, etc.
LLVM_DEBUG(dbgs() << "can only handle simple loads and stores\n");
- return std::make_unique<Dependence>(Src, Dst,
- SCEVUnionPredicate(Assume, *SE));
+ return std::make_unique<Dependence>(
+ Src, Dst, SCEVUnionPredicate(this->Assumptions, *SE));
}
const MemoryLocation &DstLoc = MemoryLocation::get(Dst);
@@ -3586,8 +3607,8 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
case AliasResult::PartialAlias:
// cannot analyse objects if we don't understand their aliasing.
LLVM_DEBUG(dbgs() << "can't analyze may or partial alias\n");
- return std::make_unique<Dependence>(Src, Dst,
- SCEVUnionPredicate(Assume, *SE));
+ return std::make_unique<Dependence>(
+ Src, Dst, SCEVUnionPredicate(this->Assumptions, *SE));
case AliasResult::NoAlias:
// If the objects noalias, they are distinct, accesses are independent.
LLVM_DEBUG(dbgs() << "no alias\n");
@@ -3601,8 +3622,8 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
// The dependence test gets confused if the size of the memory accesses
// differ.
LLVM_DEBUG(dbgs() << "can't analyze must alias with different sizes\n");
- return std::make_unique<Dependence>(Src, Dst,
- SCEVUnionPredicate(Assume, *SE));
+ return std::make_unique<Dependence>(
+ Src, Dst, SCEVUnionPredicate(this->Assumptions, *SE));
}
Value *SrcPtr = getLoadStorePointerOperand(Src);
@@ -3621,8 +3642,8 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
// We check this upfront so we don't crash in cases where getMinusSCEV()
// returns a SCEVCouldNotCompute.
LLVM_DEBUG(dbgs() << "can't analyze SCEV with different pointer base\n");
- return std::make_unique<Dependence>(Src, Dst,
- SCEVUnionPredicate(Assume, *SE));
+ return std::make_unique<Dependence>(
+ Src, Dst, SCEVUnionPredicate(this->Assumptions, *SE));
}
// Even if the base pointers are the same, they may not be loop-invariant. It
@@ -3634,8 +3655,8 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
if (!isLoopInvariant(SrcBase, SrcLoop) ||
!isLoopInvariant(DstBase, DstLoop)) {
LLVM_DEBUG(dbgs() << "The base pointer is not loop invariant.\n");
- return std::make_unique<Dependence>(Src, Dst,
- SCEVUnionPredicate(Assume, *SE));
+ return std::make_unique<Dependence>(
+ Src, Dst, SCEVUnionPredicate(this->Assumptions, *SE));
}
uint64_t EltSize = SrcLoc.Size.toRaw();
@@ -3646,8 +3667,8 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
if (!SE->isKnownMultipleOf(SrcEv, EltSize, Assume) ||
!SE->isKnownMultipleOf(DstEv, EltSize, Assume)) {
LLVM_DEBUG(dbgs() << "can't analyze SCEV with different offsets\n");
- return std::make_unique<Dependence>(Src, Dst,
- SCEVUnionPredicate(Assume, *SE));
+ return std::make_unique<Dependence>(
+ Src, Dst, SCEVUnionPredicate(this->Assumptions, *SE));
}
if (!Assume.empty()) {
@@ -3670,7 +3691,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
LLVM_DEBUG(dbgs() << " common nesting levels = " << CommonLevels << "\n");
LLVM_DEBUG(dbgs() << " maximum nesting levels = " << MaxLevels << "\n");
- FullDependence Result(Src, Dst, SCEVUnionPredicate(Assume, *SE),
+ FullDependence Result(Src, Dst, SCEVUnionPredicate(this->Assumptions, *SE),
PossiblyLoopIndependent, CommonLevels);
++TotalArrayPairs;
diff --git a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
index 8f94a455d3724..c7b9865329214 100644
--- a/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/DADelin.ll
@@ -649,8 +649,13 @@ define void @coeff_may_negative(ptr %a, i32 %k) {
; 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: Runtime Assumptions:
+; CHECK-NEXT: Compare predicate: %k sgt) 0
; 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!
+; CHECK-NEXT: Runtime Assumptions:
+; CHECK-NEXT: Compare predicate: %k sgt) 0
+; CHECK-NEXT: Compare predicate: %k sgt) 0
;
entry:
br label %loop
@@ -688,8 +693,13 @@ define void @coeff_positive(ptr %a, i32 %k) {
; 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: Runtime Assumptions:
+; CHECK-NEXT: Compare predicate: %k sgt) 0
; 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!
+; CHECK-NEXT: Runtime Assumptions:
+; CHECK-NEXT: Compare predicate: %k sgt) 0
+; CHECK-NEXT: Compare predicate: %k sgt) 0
;
entry:
br label %loop
diff --git a/llvm/test/Analysis/DependenceAnalysis/zero-coefficient.ll b/llvm/test/Analysis/DependenceAnalysis/zero-coefficient.ll
new file mode 100644
index 0000000000000..d6ad924da722b
--- /dev/null
+++ b/llvm/test/Analysis/DependenceAnalysis/zero-coefficient.ll
@@ -0,0 +1,30 @@
+; 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 #149991: Strong SIV test with symbolic coefficient
+; that could be zero. Fixed using runtime assumptions: assume coefficient > 0.
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+
+define void @test_zero_coefficient(ptr %a, i64 %k) {
+; CHECK-LABEL: 'test_zero_coefficient'
+; CHECK-NEXT: Src: store i8 42, ptr %idx, align 1 --> Dst: store i8 42, ptr %idx, align 1
+; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: Runtime Assumptions:
+; CHECK-NEXT: Compare predicate: %k sgt) 0
+;
+entry:
+ br label %loop
+
+loop:
+ %i = phi i64 [ 0, %entry ], [ %i.next, %loop ]
+ %subscript = mul i64 %i, %k ; When %k=0, all iterations access %a[0]
+ %idx = getelementptr i8, ptr %a, i64 %subscript
+ store i8 42, ptr %idx
+ %i.next = add i64 %i, 1
+ %cond.exit = icmp eq i64 %i.next, 100
+ br i1 %cond.exit, label %exit, label %loop
+
+exit:
+ ret void
+}
|
…ce tests Previously, predicates were collected using a local `Assume` vector. This patch: 1. Removes local `Assume` vector, uses class member `Assumptions` instead. 2. Adds a `getNonRedundantAssumptions()` helper for deduplication. 3. Extends predicate collection to all dependence tests.
…tions (llvm#149977) Fix GitHub issue llvm#149991 where Strong SIV test incorrectly concludes 'none' for symbolic coefficients that could be zero, leading to 0/0 undefined behavior. The issue occurs in subscripts like {base,+,coeff} where coeff is symbolic: - When coeff != 0: different iterations access different locations - When coeff = 0: all iterations access the same location (many dependencies) The Strong SIV test's Delta=0 case assumed 0/X=0 where X is the coefficient, but when X could be zero, we have 0/0 which is undefined. The analysis needs to be conservative when the coefficient might be zero. Solution: When coefficient is SCEVUnknown and cannot be proven non-zero at compile time, use SCEV range analysis to attempt proving coefficient > 0. If this fails, add a runtime assumption 'coeff > 0' to the dependence result. This allows precise analysis when possible (none under assumption coeff > 0) while maintaining correctness by exposing the required assumption. Test cases: - zero-coefficient.ll: New test for the reported bug - DADelin.ll: Updated to expect runtime assumptions for symbolic coefficients
const SCEV *Zero = SE->getZero(Coeff->getType()); | ||
|
||
// Ask SCEV's range analysis if it can prove Coeff > Zero | ||
if (SE->isKnownPredicate(ICmpInst::ICMP_SGT, Coeff, Zero)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this (or should be) deduction already redudant with SE->isKnownNonZero
?
auto I = UniqueAssumptions.begin(); | ||
while (I != UniqueAssumptions.end()) { | ||
if (P->implies(*I, *SE)) { | ||
I = UniqueAssumptions.erase(I); | ||
} else { | ||
++I; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto I = UniqueAssumptions.begin(); | |
while (I != UniqueAssumptions.end()) { | |
if (P->implies(*I, *SE)) { | |
I = UniqueAssumptions.erase(I); | |
} else { | |
++I; | |
} | |
} | |
erase_if(UniqueAssumptions, [](SCEVPredicate *) {...}) |
Fix GitHub issue #149991 where Strong SIV test incorrectly concludes 'none!' for symbolic coefficients that could be zero, leading to 0/0 undef behavior.
The issue occurs in subscripts like {base,+,coeff} where coeff is symbolic:
The Strong SIV test's Delta=0 case assumed 0/X=0 where X is the coefficient, but when X could be zero, we have 0/0 which is undefined. The analysis needs to be conservative when the coefficient might be zero.
When coefficient is SCEVUnknown and cannot be proven non-zero at compile time, use SCEV range analysis to attempt proving coefficient > 0. If this fails, add a runtime assumption 'coeff > 0' to the dependence result.
This allows precise analysis when possible (none! under assumption coeff > 0) while maintaining correctness by exposing the required assumption.
Test cases: