Skip to content

Conversation

1997alireza
Copy link
Contributor

@1997alireza 1997alireza commented Oct 22, 2025

By doubling the size of the expressions in the strong SIV test, no overflow occurs in the multiplication SE->getMulExpr(WideUpperBound, WideAbsCoeff) anymore.

This patch resolves the issue #159846, and fixes the strong SIV test created in the issue #164246 by proving the dependency.

While this patch resolves the overflow bug in the strong SIV test, it also prevents the test from performing and disproving the dependency in some cases. The reason behind this is due to adding sext operation when we are extending the expressions, which prevents SCEV to compare the expressions and disprove the dependency in those special cases. We are working on that. Check out the progress report.

@llvmbot llvmbot added the llvm:analysis Includes value tracking, cost tables and constant folding label Oct 22, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 22, 2025

@llvm/pr-subscribers-llvm-analysis

Author: Alireza Torabian (1997alireza)

Changes

By doubling the size of the expressions in the strong SIV test, no overflow occurs in the multiplication SE->getMulExpr(WideUpperBound, WideAbsCoeff) anymore.

This patch resolves the issue #159846, and fixes the strong SIV test created in the issue #164246 by proving the dependency.

While this patch resolves the overflow bug in the strong SIV test, it also prevents the test from performing and disproving the dependency in some cases. The reason behind this is due to adding sext operation when we are extending the expressions, which prevents SCEV to compare the expressions and disprove the dependency. We are working on that. Check out the progress report.


Full diff: https://github.com/llvm/llvm-project/pull/164704.diff

1 Files Affected:

  • (modified) llvm/lib/Analysis/DependenceAnalysis.cpp (+28-10)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 853bd66c8a7f8..af900e2a92998 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -1668,17 +1668,35 @@ bool DependenceInfo::strongSIVtest(const SCEV *Coeff, const SCEV *SrcConst,
   LLVM_DEBUG(dbgs() << "\t    Delta = " << *Delta);
   LLVM_DEBUG(dbgs() << ", " << *Delta->getType() << "\n");
 
+  TypeSize CoeffSize =
+      Coeff->getType()->getScalarType()->getPrimitiveSizeInBits();
+  TypeSize SrcSize =
+      SrcConst->getType()->getScalarType()->getPrimitiveSizeInBits();
+  TypeSize DstSize =
+      DstConst->getType()->getScalarType()->getPrimitiveSizeInBits();
+  TypeSize WideSize = std::max(CoeffSize, std::max(SrcSize, DstSize)) * 2;
+  LLVMContext &Context = CurDstLoop->getHeader()->getParent()->getContext();
+  Type *WideTy = IntegerType::get(Context, WideSize);
+  const SCEV *WideSrcC = SE->getSignExtendExpr(SrcConst, WideTy);
+  const SCEV *WideDstC = SE->getSignExtendExpr(DstConst, WideTy);
+  const SCEV *WideDelta = SE->getMinusSCEV(WideSrcC, WideDstC);
+  const SCEV *WideCoeff = SE->getSignExtendExpr(Coeff, WideTy);
+
   // check that |Delta| < iteration count
-  if (const SCEV *UpperBound =
-          collectUpperBound(CurSrcLoop, Delta->getType())) {
-    LLVM_DEBUG(dbgs() << "\t    UpperBound = " << *UpperBound);
-    LLVM_DEBUG(dbgs() << ", " << *UpperBound->getType() << "\n");
-    const SCEV *AbsDelta =
-        SE->isKnownNonNegative(Delta) ? Delta : SE->getNegativeSCEV(Delta);
-    const SCEV *AbsCoeff =
-        SE->isKnownNonNegative(Coeff) ? Coeff : SE->getNegativeSCEV(Coeff);
-    const SCEV *Product = SE->getMulExpr(UpperBound, AbsCoeff);
-    if (isKnownPredicate(CmpInst::ICMP_SGT, AbsDelta, Product)) {
+  if (const SCEV *WideUpperBound =
+          collectUpperBound(CurSrcLoop, WideDelta->getType())) {
+    LLVM_DEBUG(dbgs() << "\t    WideUpperBound = " << *WideUpperBound);
+    LLVM_DEBUG(dbgs() << ", " << *WideUpperBound->getType() << "\n");
+
+    // FIXME: Use SCEV getAbsExpr function to compute the abstract values
+    const SCEV *WideAbsDelta = SE->isKnownNonNegative(WideDelta)
+                                   ? WideDelta
+                                   : SE->getNegativeSCEV(WideDelta);
+    const SCEV *WideAbsCoeff = SE->isKnownNonNegative(WideCoeff)
+                                   ? WideCoeff
+                                   : SE->getNegativeSCEV(WideCoeff);
+    const SCEV *WideProduct = SE->getMulExpr(WideUpperBound, WideAbsCoeff);
+    if (isKnownPredicate(CmpInst::ICMP_SGT, WideAbsDelta, WideProduct)) {
       // Distance greater than trip count - no dependence
       ++StrongSIVindependence;
       ++StrongSIVsuccesses;

@1997alireza
Copy link
Contributor Author

1997alireza commented Oct 22, 2025

@kasuga-fj This patch addresses the issue with the strong SIV test you referenced in #164246. While strong SIV produces the correct result consistent output [1], the DA result is none!. This reason is due to an overflow in the RDIV test, which causes the test to incorrectly disprove the dependency.

While we should resolve the bug in RDIV, I believe it may not be necessary for DA to perform the additional tests when strong SIV has already proved the dependency. Specifically, I’m referring to line 2717. Even if disproven is false, we can determine whether strong SIV successfully proved the dependency or if the test failed to produce any results. Based on this outcome, we can then decide whether to proceed with the other tests.

By doubling the size of the expressions in the strong SIV test, no
overflow occurs in the multiplication required to check the test.
@kasuga-fj
Copy link
Contributor

The change itself doesn't seem to be problematic, but as I mentioned in #159846 (comment), I'm now skeptical of the approach that uses wider integer types. For the cases I provided, where overflows are triggered by large constant values, I don't think they are practical enough to justify the added complexity of using wider integer types. While it's certainly beneficial to provide more accurate results in such cases, I believe it's sufficient to simply return an unknown dependency.

@amehsan amehsan self-requested a review October 23, 2025 14:27
@amehsan
Copy link
Contributor

amehsan commented Oct 23, 2025

For the cases I provided, where overflows are triggered by large constant values, I don't think they are practical enough to justify the added complexity of using wider integer types.

What is the concern here, is it compile time or something else? large constant values are very rare in practice, so I don't think that should prevent us from merging this PR.

While in principle I agree that the primary concern is correctness, and it is ok to give up on accuracy for correctness, if there is an approach that can give us more accurate results while preserving correctness, it should be preferrable.

(EDIT: I haven't checked the code, but I expect for the cases where arithmetic operations on the constants do not result in overflow, widening the type should not have any impact on the complexity of the operations. Zero bits on the left side shouldn't matter).

Copy link
Contributor

@amehsan amehsan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please also add any required changes in the test files?

@kasuga-fj
Copy link
Contributor

I meant the complexity of the code, not computational complexity. Since DA already has a lot of issues, I'd prefer to keep the code as simple as possible. In this specific case I think the simplest solution is to detect (perhaps potentially) overflows during the calculations and bail out if one is detected.

If simply widening the integer type improves accuracy in many cases compared to bailing out when an overflow occurs, that would be great. However, looking at the tasks listed in #159846 (comment), I don't think that's the case. As you said we may be able to prove in some way that 2*n doesn't overflow, it looks to me that such reasoning will require non-trivial effort. In addition, I believe that the cases where such inference can be applied are quite limited. For example, it the store is not A[i + 2*n] but A[i + 7*n], then I don't think we prove 7*n doesn't overflow. Actually, when n is 7905747460161236407, which is a modular multiplicative inverse of 7 with respect to the modulus 2^64, the program will be well-defined while 7*n overflows.

In conclusion, at the moment, I don't think widening the integer type justifies the complexity increase in the code.

LLVM_DEBUG(dbgs() << "\t WideUpperBound = " << *WideUpperBound);
LLVM_DEBUG(dbgs() << ", " << *WideUpperBound->getType() << "\n");

// FIXME: Use SCEV getAbsExpr function to compute the abstract values
Copy link
Contributor

@kasuga-fj kasuga-fj Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JFYI: Abs means absolute value.

@amehsan
Copy link
Contributor

amehsan commented Oct 23, 2025

As you said we may be able to prove in some way that 2*n doesn't overflow, it looks to me that such reasoning will require non-trivial effort. In addition, I believe that the cases where such inference can be applied are quite limited. For example, it the store is not A[i + 2*n] but A[i + 7*n], then I don't think we prove 7*n doesn't overflow. Actually, when n is 7905747460161236407, which is a modular multiplicative inverse of 7 with respect to the modulus 2^64, the program will be well-defined while 7*n overflows.

That example is a separate issue. That is not related to this patch. and that case is not something that we want care about its accuracy.

If simply widening the integer type improves accuracy in many cases compared to bailing out when an overflow occurs, that would be great. However, looking at the tasks listed in #159846 (comment), I don't think that's the case.

The issue is not how many hanadwritten testcases can be improved. The issue is how often this can help in the real and unseen code. In an arithmetic operation where one of the involved expressions is symbolic, it is very difficult to prove the result won't overflow. Simply because often times we don't have any bound on the value of the symbolic expression. This is why I proposed widening in the first place. For symbolic intermediate computations in DA, without widening we have to give up on many real-world cases very quickly. @1997alireza can post an example to clarify this.

The complexity increase is very small here. Once we miss a performance opportunity it is not easy to detect it. Once detected, it takes a lot of effort to find the root cause, come up with the idea and implement it. So if you consider longer term, I expect significant benefit from this patch.

@kasuga-fj
Copy link
Contributor

I'm okay if you have some real world examples (but I'm not entirely sure why wider type is useful even though we don't know the bounds of symbolic values).

@amehsan
Copy link
Contributor

amehsan commented Oct 23, 2025

I'm not entirely sure why wider type is useful even though we don't know the bounds of symbolic values

Consider this code:

const SCEV *Product = SE->getMulExpr(UpperBound, AbsCoeff)

if UpperBound is a symbolic 64 bit value and you multiply it with something, most likely you cannot prove the result doesn't overflow and you have to give up and return unknown dependence, because you cannot do a correct comparison in the next step.

but if we extend both values to 128 bit and multiply them, the result will definitely not overflow 128 bits. So in the next step

isKnownPredicate(CmpInst::ICMP_SGT, AbsDelta, Product)

Assuming AbsDelata is also extended to 128 bit you can perform a correct comparison.

@nikic
Copy link
Contributor

nikic commented Oct 23, 2025

But isn't that isKnownPredicate always going to fail in the case where the multiply may overflow? That means that the value is potentially larger than the original type, and AbsDelta fits within that type. Maybe I'm misunderstanding something here.

By the way, it should be possible to write a test for this using -da-enable-dependence-test, to test the fix for SDIV even if RDIV is still buggy.

@amehsan
Copy link
Contributor

amehsan commented Oct 23, 2025

But isn't that isKnownPredicate always going to fail in the case where the multiply may overflow? That means that the value is potentially larger than the original type, and AbsDelta fits within that type. Maybe I'm misunderstanding something here.

if we exnted the type then the multiply will provably not overflow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:analysis Includes value tracking, cost tables and constant folding

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants