Skip to content

[DA] do not handle GEPs with different types #144088

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

sebpop
Copy link
Contributor

@sebpop sebpop commented Jun 13, 2025

This patch fixes an error in which the delinearized function for two GEPs end up to be the same except that the GEP type is different:

[256 x [256 x [256 x i64]]]
vs.
[256 x [256 x [256 x i32]]]

the result was that the dependence test was inferring a simple dependence with all elements overlapping, however because the type of elements indexed by the GEP are different, the overlap is non linear. This patch filters out such cases.

@llvmbot
Copy link
Member

llvmbot commented Jun 13, 2025

@llvm/pr-subscribers-llvm-analysis

Author: Sebastian Pop (sebpop)

Changes

This patch fixes an error in which the delinearized function for two GEPs end up to be the same except that the GEP type is different:

[256 x [256 x [256 x i64]]]
vs.
[256 x [256 x [256 x i32]]]

the result was that the dependence test was inferring a simple dependence with all elements overlapping, however because the type of elements indexed by the GEP are different, the overlap is non linear. This patch filters out such cases.


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

2 Files Affected:

  • (modified) llvm/lib/Analysis/DependenceAnalysis.cpp (+10)
  • (modified) llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll (+3-8)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index c1b1d002c9979..4c9ad4731b802 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3642,6 +3642,16 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst,
 
   Value *SrcPtr = getLoadStorePointerOperand(Src);
   Value *DstPtr = getLoadStorePointerOperand(Dst);
+
+  if (GetElementPtrInst *SrcGEP = dyn_cast<GetElementPtrInst>(SrcPtr)) {
+    if (GetElementPtrInst *DstGEP = dyn_cast<GetElementPtrInst>(DstPtr)) {
+      if (SrcGEP->getSourceElementType() != DstGEP->getSourceElementType()) {
+        LLVM_DEBUG(dbgs() << "can't analyze GEPs with different types\n");
+        return std::make_unique<Dependence>(Src, Dst,
+                                            SCEVUnionPredicate(Assume, *SE));
+      }
+    }
+  }
   const SCEV *SrcSCEV = SE->getSCEV(SrcPtr);
   const SCEV *DstSCEV = SE->getSCEV(DstPtr);
   LLVM_DEBUG(dbgs() << "    SrcSCEV = " << *SrcSCEV << "\n");
diff --git a/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll b/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
index 1f8fac3087bff..5397fa5b97caf 100644
--- a/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
@@ -76,13 +76,9 @@ define i32 @gep_i8_vs_i32(ptr nocapture %A, i64 %n, i64 %m) {
 ; CHECK-NEXT:  Src: store i32 42, ptr %arrayidx0, align 1 --> Dst: store i32 42, ptr %arrayidx0, align 1
 ; CHECK-NEXT:    da analyze - none!
 ; CHECK-NEXT:  Src: store i32 42, ptr %arrayidx0, align 1 --> Dst: store i32 42, ptr %arrayidx1, align 4
-; CHECK-NEXT:    da analyze - output [|<]!
-; CHECK-NEXT:    Runtime Assumptions:
-; CHECK-NEXT:    Equal predicate: (zext i2 (trunc i64 %n to i2) to i64) == 0
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 42, ptr %arrayidx1, align 4 --> Dst: store i32 42, ptr %arrayidx1, align 4
 ; CHECK-NEXT:    da analyze - none!
-; CHECK-NEXT:  Runtime Assumptions:
-; CHECK-NEXT:  Equal predicate: (zext i2 (trunc i64 %n to i2) to i64) == 0
 ;
 entry:
   %arrayidx0 = getelementptr inbounds i8, ptr %A, i64 %n
@@ -98,7 +94,7 @@ define void @linearized_accesses(i64 %n, i64 %m, i64 %o, ptr %A) {
 ; CHECK-NEXT:  Src: store i32 1, ptr %idx0, align 4 --> Dst: store i32 1, ptr %idx0, align 4
 ; CHECK-NEXT:    da analyze - output [* * *]!
 ; CHECK-NEXT:  Src: store i32 1, ptr %idx0, align 4 --> Dst: store i32 1, ptr %idx1, align 4
-; CHECK-NEXT:    da analyze - output [* * *|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 1, ptr %idx1, align 4 --> Dst: store i32 1, ptr %idx1, align 4
 ; CHECK-NEXT:    da analyze - none!
 ;
@@ -149,8 +145,7 @@ define void @multidim_accesses(ptr %A) {
 ; CHECK-NEXT:  Src: store i32 1, ptr %idx0, align 4 --> Dst: store i32 1, ptr %idx0, align 4
 ; CHECK-NEXT:    da analyze - none!
 ; CHECK-NEXT:  Src: store i32 1, ptr %idx0, align 4 --> Dst: store i32 1, ptr %idx1, align 4
-; FIXME: the dependence distance is not constant. Distance vector should be [* * *|<]!
-; CHECK-NEXT:    da analyze - consistent output [0 0 0|<]!
+; CHECK-NEXT:    da analyze - confused!
 ; CHECK-NEXT:  Src: store i32 1, ptr %idx1, align 4 --> Dst: store i32 1, ptr %idx1, align 4
 ; CHECK-NEXT:    da analyze - none!
 ;

Copy link
Contributor

@kasuga-fj kasuga-fj left a comment

Choose a reason for hiding this comment

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

IIUIC, the problem here is the delinearization process that relies on the GEP source element type rather than the gap between two GEPs. That is, we should stop using llvm::tryDelinearizeFixedSizeImpl. As I tried, simply removing the call of it led to regressions in some tests. Some of these failures seem to occur due to the failure of delinearization that should theoretically be possible. I believe the correct approach is to improve the DependenceInfo::tryDelinearizeParametricSize or llvm::delinearization at first, then proceed to delete DependenceInfo::tryDelinearizeFixedSize.

Comment on lines -152 to +148
; FIXME: the dependence distance is not constant. Distance vector should be [* * *|<]!
; CHECK-NEXT: da analyze - consistent output [0 0 0|<]!
; CHECK-NEXT: da analyze - confused!
Copy link
Contributor

Choose a reason for hiding this comment

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

This is just an example of DA missing some dependencies that should exist, due to the delinearization reasoning based on the GEP source element type. There are variants of this example that is not resolved by your patch, e.g., like the following:

define void @multidim_accesses(ptr %A) {
entry:
  br label %for.i

for.i:
  %i = phi i64 [ 0, %entry ], [ %i.inc, %for.i.inc ]
  br label %for.j

for.j:
  %j = phi i64 [ 0, %for.i ], [ %j.inc, %for.j.inc ]
  br label %for.k

for.k:
  %k = phi i64 [ 0, %for.j ], [ %k.inc, %for.k.inc ]
  %k.inc = add nsw i64 %k, 1
  %idx0 = getelementptr inbounds [256 x [256 x [256 x i32]]], ptr %A, i64 %i, i64 %j, i64 %k
  store i64 1, ptr %idx0
  %idx1 = getelementptr inbounds [256 x [256 x [256 x i32]]], ptr %A, i64 %i, i64 %j, i64 %k.inc
  store i64 1, ptr %idx1
  br label %for.k.inc

for.k.inc:
  %k.exitcond = icmp eq i64 %k.inc, 255
  br i1 %k.exitcond, label %for.j.inc, label %for.k

for.j.inc:
  %j.inc = add nsw i64 %j, 1
  %j.exitcond = icmp eq i64 %j.inc, 256
  br i1 %j.exitcond, label %for.i.inc, label %for.j

for.i.inc:
  %i.inc = add nsw i64 %i, 1
  %i.exitcond = icmp eq i64 %i.inc, 256
  br i1 %i.exitcond, label %end, label %for.i

end:
  ret void
}

A portion of the output is as follows:

Src:  store i64 1, ptr %idx0, align 4 --> Dst:  store i64 1, ptr %idx1, align 4
  da analyze - output [= = >]!

However, the latter half of the first store overlaps with the first half of the second store (within the same iteration). Therefore, an = dependency exists for the k-loop, but DA currently does not recognize it.

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

Successfully merging this pull request may close these issues.

3 participants