-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[DA] Fix zero coeff bug in Strong SIV test with runtime assumptions (#149991) #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
+}
|
22d4899 to
f7365db
Compare
sjoerdmeijer
left a comment
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.
LGTM, provided that @Meinersbur previous minor comments have been addressed, which I think is the case.
…tions The Strong SIV test was incorrectly concluding "no dependence" when the coefficient is symbolic and the delta (difference between source and destination) is zero. When delta=0, the Strong SIV test divides delta/coeff to get the distance. The bug occurs when coeff is an unknown symbolic value: if coeff=0 at runtime, then 0/0 is undefined and all iterations access the same memory location, creating a true dependence that was being missed.
| bool AddedAssumptionsDuringTests = | ||
| Result.Assumptions.getPredicates().size() > AssumptionsBeforeTests; | ||
| if (AllEqual && !AddedAssumptionsDuringTests) |
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.
Why is it allowed to ignore the assumptions added before tests?
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.
Currently we return nullptr when dependence vector is "AllEqual".
This code is here to guard against returning nullptr when the dependence is conditional to runtime assumptions: if we return nullptr the assumptions would be discarded.
We may need to revisit what we return for "no dependence" because we can't attach assumptions to a "nullptr".
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.
I think the condition should be Result.Assumptions.getPredicates().empty(), not !AddedAssumptionsDuringTests. If I understand correctly, current implementation silently discards the assumptions added before tests.
| // 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)) { |
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.
Why only SCEVUnknown? What happens if Coeff is like %m * %n? I think this may also be 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.
I rewrote the code to avoid enumerating all possible expressions that Coeff can take at this location:
if (isa<SCEVConstant>(Coeff) || SE->isKnownNonZero(Coeff)) {
LLVM_DEBUG(
dbgs() << "\t Coefficient proven non-zero by SCEV analysis\n");
} else {
// Cannot prove at compile time, would need runtime assumption.
[...]
}
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.
isa<SCEVConstant>(Coeff) would be redundant.
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 Strong SIV test was incorrectly concluding "no dependence" when the
coefficient is symbolic and the delta (difference between source and
destination) is zero.
When delta=0, the Strong SIV test divides delta/coeff to get the distance.
The bug occurs when coeff is an unknown symbolic value: if coeff=0 at runtime,
then 0/0 is undefined and all iterations access the same memory location,
creating a true dependence that was being missed.