[llvm] [DA] do not handle array accesses of different offsets (PR #123436)
Sebastian Pop via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 27 09:52:47 PST 2025
https://github.com/sebpop updated https://github.com/llvm/llvm-project/pull/123436
>From 6bb7ff9b6a81b930a58a2776a08609358952cebe Mon Sep 17 00:00:00 2001
From: Sebastian Pop <spop at nvidia.com>
Date: Thu, 14 Nov 2024 18:17:04 +0000
Subject: [PATCH 1/8] [DA] Dependence analysis does not handle array accesses
of different sizes
This fixes bug https://github.com/llvm/llvm-project/issues/16183
where the elements of a same array are accesses as i32 and i64.
This is not handled by the array dependence analysis.
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 38 +++++++++++--------
.../DependenceAnalysis/DifferentAccessSize.ll | 22 +++++++++++
2 files changed, 44 insertions(+), 16 deletions(-)
create mode 100644 llvm/test/Analysis/DependenceAnalysis/DifferentAccessSize.ll
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index dc0ed22dbcc0b..5aa6ef6b1a5c2 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3596,14 +3596,10 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
return std::make_unique<Dependence>(Src, Dst);
}
- assert(isLoadOrStore(Src) && "instruction is not load or store");
- assert(isLoadOrStore(Dst) && "instruction is not load or store");
- Value *SrcPtr = getLoadStorePointerOperand(Src);
- Value *DstPtr = getLoadStorePointerOperand(Dst);
+ const MemoryLocation &DstLoc = MemoryLocation::get(Dst);
+ const MemoryLocation &SrcLoc = MemoryLocation::get(Src);
- switch (underlyingObjectsAlias(AA, F->getDataLayout(),
- MemoryLocation::get(Dst),
- MemoryLocation::get(Src))) {
+ switch (underlyingObjectsAlias(AA, F->getDataLayout(), DstLoc, SrcLoc)) {
case AliasResult::MayAlias:
case AliasResult::PartialAlias:
// cannot analyse objects if we don't understand their aliasing.
@@ -3617,16 +3613,15 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
break; // The underlying objects alias; test accesses for dependence.
}
- // establish loop nesting levels
- establishNestingLevels(Src, Dst);
- LLVM_DEBUG(dbgs() << " common nesting levels = " << CommonLevels << "\n");
- LLVM_DEBUG(dbgs() << " maximum nesting levels = " << MaxLevels << "\n");
-
- FullDependence Result(Src, Dst, PossiblyLoopIndependent, CommonLevels);
- ++TotalArrayPairs;
+ if (DstLoc.Size != SrcLoc.Size) {
+ // 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);
+ }
- unsigned Pairs = 1;
- SmallVector<Subscript, 2> Pair(Pairs);
+ Value *SrcPtr = getLoadStorePointerOperand(Src);
+ Value *DstPtr = getLoadStorePointerOperand(Dst);
const SCEV *SrcSCEV = SE->getSCEV(SrcPtr);
const SCEV *DstSCEV = SE->getSCEV(DstPtr);
LLVM_DEBUG(dbgs() << " SrcSCEV = " << *SrcSCEV << "\n");
@@ -3641,6 +3636,17 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
LLVM_DEBUG(dbgs() << "can't analyze SCEV with different pointer base\n");
return std::make_unique<Dependence>(Src, Dst);
}
+
+ // establish loop nesting levels
+ establishNestingLevels(Src, Dst);
+ LLVM_DEBUG(dbgs() << " common nesting levels = " << CommonLevels << "\n");
+ LLVM_DEBUG(dbgs() << " maximum nesting levels = " << MaxLevels << "\n");
+
+ FullDependence Result(Src, Dst, PossiblyLoopIndependent, CommonLevels);
+ ++TotalArrayPairs;
+
+ unsigned Pairs = 1;
+ SmallVector<Subscript, 2> Pair(Pairs);
Pair[0].Src = SrcSCEV;
Pair[0].Dst = DstSCEV;
diff --git a/llvm/test/Analysis/DependenceAnalysis/DifferentAccessSize.ll b/llvm/test/Analysis/DependenceAnalysis/DifferentAccessSize.ll
new file mode 100644
index 0000000000000..2dded8f3b13a1
--- /dev/null
+++ b/llvm/test/Analysis/DependenceAnalysis/DifferentAccessSize.ll
@@ -0,0 +1,22 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -disable-output "-passes=print<da>" -aa-pipeline=basic-aa 2>&1 \
+; RUN: | FileCheck %s
+
+; The dependence test does not handle array accesses of different sizes: i32 and i64.
+; Bug 16183 - https://github.com/llvm/llvm-project/issues/16183
+
+define i64 @bug16183_alias(ptr nocapture %A) {
+; CHECK-LABEL: 'bug16183_alias'
+; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 4 --> Dst: store i32 2, ptr %arrayidx, align 4
+; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 4 --> Dst: %0 = load i64, ptr %A, align 8
+; CHECK-NEXT: da analyze - confused!
+; CHECK-NEXT: Src: %0 = load i64, ptr %A, align 8 --> Dst: %0 = load i64, ptr %A, align 8
+; CHECK-NEXT: da analyze - none!
+;
+entry:
+ %arrayidx = getelementptr inbounds i32, ptr %A, i64 1
+ store i32 2, ptr %arrayidx, align 4
+ %0 = load i64, ptr %A, align 8
+ ret i64 %0
+}
>From f2c7da8bbc148ca05494342469a69e253c1763d7 Mon Sep 17 00:00:00 2001
From: Sebastian Pop <spop at nvidia.com>
Date: Fri, 17 Jan 2025 20:54:19 +0000
Subject: [PATCH 2/8] [DA] check memory offsets are multiples of elements size
Check that the offsets from memory base address are multiples of the array
element size for a pair of memory accesses in a dependence test.
The patch adds a testcase that cannot be disambiguated by the DA.
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 98 ++++++++++++++++++-
.../DependenceAnalysis/DifferentOffsets.ll | 25 +++++
.../DependenceAnalysis/MIVCheckConst.ll | 2 +-
3 files changed, 123 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 5aa6ef6b1a5c2..cab10c704f665 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3626,7 +3626,9 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
const SCEV *DstSCEV = SE->getSCEV(DstPtr);
LLVM_DEBUG(dbgs() << " SrcSCEV = " << *SrcSCEV << "\n");
LLVM_DEBUG(dbgs() << " DstSCEV = " << *DstSCEV << "\n");
- if (SE->getPointerBase(SrcSCEV) != SE->getPointerBase(DstSCEV)) {
+ const SCEV *SrcBase = SE->getPointerBase(SrcSCEV);
+ const SCEV *DstBase = SE->getPointerBase(DstSCEV);
+ if (SrcBase != DstBase) {
// If two pointers have different bases, trying to analyze indexes won't
// work; we can't compare them to each other. This can happen, for example,
// if one is produced by an LCSSA PHI node.
@@ -3637,6 +3639,100 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
return std::make_unique<Dependence>(Src, Dst);
}
+ auto EltSize = SrcLoc.Size.toRaw();
+ assert(EltSize == DstLoc.Size.toRaw() && "Array element size differ");
+
+ // Check that memory access offsets in V are multiples of array EltSize.
+ std::function<bool(const SCEV *, const SCEV *&)> checkOffsets =
+ [&](const SCEV *V, const SCEV *&Param) -> bool {
+ if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(V)) {
+ if (!checkOffsets(AddRec->getStart(), Param))
+ return false;
+ return checkOffsets(AddRec->getStepRecurrence(*SE), Param);
+ }
+ if (auto *Cst = dyn_cast<SCEVConstant>(V)) {
+ APInt C = Cst->getAPInt();
+ return C.srem(EltSize) == 0;
+ }
+
+ auto checkParamsMultipleOfSize = [&](const SCEV *V,
+ const SCEV *&Param) -> bool {
+ if (EltSize == 1)
+ return true;
+ if (!Param) {
+ Param = V;
+ return true;
+ }
+ if (Param == V)
+ return true;
+
+ // Check whether "(Param - V) % Size == 0".
+ const SCEV *Diff = SE->getMinusSCEV(Param, V);
+ if (const SCEVConstant *Cst = dyn_cast<SCEVConstant>(Diff)) {
+ APInt Val = Cst->getAPInt();
+ if (Val.isZero())
+ return true;
+ auto Rem =
+ Val.srem(APInt(Val.getBitWidth(), EltSize, /*isSigned=*/true));
+ if (Rem.isZero())
+ return true;
+ LLVM_DEBUG(dbgs() << "SCEV with different offsets: " << *Param << " - "
+ << *V << " = " << *Diff << " % " << EltSize << " = "
+ << Rem << " != 0\n");
+ return false;
+ }
+ // Check if the symbolic difference is a multiple of Size.
+ const SCEV *Val = SE->getConstant(
+ APInt(Diff->getType()->getScalarSizeInBits(), EltSize));
+
+ // Check by using the remainder computation.
+ const SCEV *Remainder = SE->getURemExpr(Diff, Val);
+ if (const SCEVConstant *C = dyn_cast<SCEVConstant>(Remainder))
+ if (C->getValue()->isZero())
+ return true;
+
+ // Check by using the division computation.
+ const SCEV *Q = SE->getUDivExpr(Diff, Val);
+ const SCEV *Product = SE->getMulExpr(Q, Val);
+ if (Diff == Product)
+ return true;
+ LLVM_DEBUG(dbgs() << "SCEV with different offsets:\n"
+ << *Param << " - " << *V << " = " << *Diff << "\n"
+ << "Remainder = " << *Remainder << "\n"
+ << "Q = " << *Q << " Product = " << *Product << "\n");
+ return false;
+ };
+ // Expressions like "n".
+ if (isa<SCEVUnknown>(V))
+ return checkParamsMultipleOfSize(V, Param);
+ // Expressions like "n + 1".
+ if (isa<SCEVAddExpr>(V))
+ return !SCEVExprContains(V, [](const SCEV *S) {
+ return isa<SCEVUnknown>(S);
+ }) || checkParamsMultipleOfSize(V, Param);
+
+ if (isa<SCEVMulExpr>(V))
+ return !SCEVExprContains(V, [](const SCEV *S) {
+ return isa<SCEVUnknown>(S);
+ }) || checkParamsMultipleOfSize(V, Param);
+
+ LLVM_DEBUG(dbgs() << "SCEV node not handled yet: " << *V << "\n");
+ return false;
+ };
+
+ // Check that memory access offsets are multiples of element sizes.
+ const SCEV *SrcEv = SE->getMinusSCEV(SrcSCEV, SrcBase);
+ const SCEV *DstEv = SE->getMinusSCEV(DstSCEV, DstBase);
+ const SCEV *Param = nullptr;
+
+ if (Src != Dst) {
+ // Check that memory access offsets are multiples of element sizes.
+ if (!checkOffsets(SrcEv, Param) || !checkOffsets(DstEv, Param)) {
+ LLVM_DEBUG(dbgs() << "can't analyze SCEV with different offsets\n");
+ return std::make_unique<Dependence>(Src, Dst);
+ }
+ }
+
// establish loop nesting levels
establishNestingLevels(Src, Dst);
LLVM_DEBUG(dbgs() << " common nesting levels = " << CommonLevels << "\n");
diff --git a/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll b/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
new file mode 100644
index 0000000000000..00375da1f26de
--- /dev/null
+++ b/llvm/test/Analysis/DependenceAnalysis/DifferentOffsets.ll
@@ -0,0 +1,25 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -disable-output "-passes=print<da>" -aa-pipeline=basic-aa 2>&1 \
+; RUN: | FileCheck %s
+
+; The dependence test does not handle array accesses with difference between array accesses
+; is not a multiple of the array element size.
+
+; In this test, the element size is i32 = 4 bytes and the difference between the
+; load and the store is 2 bytes.
+
+define i32 @alias_with_different_offsets(ptr nocapture %A) {
+; CHECK-LABEL: 'alias_with_different_offsets'
+; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 1 --> Dst: store i32 2, ptr %arrayidx, align 1
+; CHECK-NEXT: da analyze - none!
+; CHECK-NEXT: Src: store i32 2, ptr %arrayidx, align 1 --> Dst: %0 = load i32, ptr %A, align 1
+; CHECK-NEXT: da analyze - confused!
+; CHECK-NEXT: Src: %0 = load i32, ptr %A, align 1 --> Dst: %0 = load i32, ptr %A, align 1
+; CHECK-NEXT: da analyze - none!
+;
+entry:
+ %arrayidx = getelementptr inbounds i8, ptr %A, i64 2
+ store i32 2, ptr %arrayidx, align 1
+ %0 = load i32, ptr %A, align 1
+ ret i32 %0
+}
diff --git a/llvm/test/Analysis/DependenceAnalysis/MIVCheckConst.ll b/llvm/test/Analysis/DependenceAnalysis/MIVCheckConst.ll
index fa58a81d2355b..c8fbbe60b0bf2 100644
--- a/llvm/test/Analysis/DependenceAnalysis/MIVCheckConst.ll
+++ b/llvm/test/Analysis/DependenceAnalysis/MIVCheckConst.ll
@@ -41,7 +41,7 @@ define void @test(ptr %A, ptr %B, i1 %arg, i32 %n, i32 %m) #0 align 2 {
; CHECK-NEXT: Src: %v27 = load <32 x i32>, ptr %v25, align 256 --> Dst: %v27 = load <32 x i32>, ptr %v25, align 256
; CHECK-NEXT: da analyze - consistent input [0 S S]!
; CHECK-NEXT: Src: %v27 = load <32 x i32>, ptr %v25, align 256 --> Dst: %v32 = load <32 x i32>, ptr %v30, align 128
-; CHECK-NEXT: da analyze - input [* S S|<]!
+; CHECK-NEXT: da analyze - confused!
; CHECK-NEXT: Src: %v32 = load <32 x i32>, ptr %v30, align 128 --> Dst: %v32 = load <32 x i32>, ptr %v30, align 128
; CHECK-NEXT: da analyze - consistent input [0 S S]!
;
>From f14530dbd6754861c0bda672e90b8aaee0dd2267 Mon Sep 17 00:00:00 2001
From: Sebastian Pop <spop at nvidia.com>
Date: Wed, 26 Feb 2025 19:13:14 +0000
Subject: [PATCH 3/8] fix auto type
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index cab10c704f665..2ddc0c152c932 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3639,7 +3639,7 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
return std::make_unique<Dependence>(Src, Dst);
}
- auto EltSize = SrcLoc.Size.toRaw();
+ uint64_t EltSize = SrcLoc.Size.toRaw();
assert(EltSize == DstLoc.Size.toRaw() && "Array element size differ");
// Check that memory access offsets in V are multiples of array EltSize.
>From 34d18016537e17cc638ef83e9fd9433e87ccf7f9 Mon Sep 17 00:00:00 2001
From: Sebastian Pop <spop at nvidia.com>
Date: Wed, 26 Feb 2025 19:26:31 +0000
Subject: [PATCH 4/8] convert checkOffsets lambda to static function
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 158 +++++++++++------------
1 file changed, 78 insertions(+), 80 deletions(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 2ddc0c152c932..1ace975ddf9e3 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3569,6 +3569,82 @@ bool DependenceInfo::invalidate(Function &F, const PreservedAnalyses &PA,
Inv.invalidate<LoopAnalysis>(F, PA);
}
+static bool checkOffsets(ScalarEvolution *SE, const SCEV *V, const SCEV *&Param,
+ uint64_t EltSize) {
+ if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(V)) {
+ if (!checkOffsets(SE, AddRec->getStart(), Param, EltSize))
+ return false;
+ return checkOffsets(SE, AddRec->getStepRecurrence(*SE), Param, EltSize);
+ }
+ if (auto *Cst = dyn_cast<SCEVConstant>(V)) {
+ APInt C = Cst->getAPInt();
+ return C.srem(EltSize) == 0;
+ }
+
+ auto checkParamsMultipleOfSize = [&](const SCEV *V,
+ const SCEV *&Param) -> bool {
+ if (EltSize == 1)
+ return true;
+ if (!Param) {
+ Param = V;
+ return true;
+ }
+ if (Param == V)
+ return true;
+
+ // Check whether "(Param - V) % Size == 0".
+ const SCEV *Diff = SE->getMinusSCEV(Param, V);
+ if (const SCEVConstant *Cst = dyn_cast<SCEVConstant>(Diff)) {
+ APInt Val = Cst->getAPInt();
+ if (Val.isZero())
+ return true;
+ auto Rem = Val.srem(APInt(Val.getBitWidth(), EltSize, /*isSigned=*/true));
+ if (Rem.isZero())
+ return true;
+ LLVM_DEBUG(dbgs() << "SCEV with different offsets: " << *Param << " - "
+ << *V << " = " << *Diff << " % " << EltSize << " = "
+ << Rem << " != 0\n");
+ return false;
+ }
+ // Check if the symbolic difference is a multiple of Size.
+ const SCEV *Val =
+ SE->getConstant(APInt(Diff->getType()->getScalarSizeInBits(), EltSize));
+
+ // Check by using the remainder computation.
+ const SCEV *Remainder = SE->getURemExpr(Diff, Val);
+ if (const SCEVConstant *C = dyn_cast<SCEVConstant>(Remainder))
+ if (C->getValue()->isZero())
+ return true;
+
+ // Check by using the division computation.
+ const SCEV *Q = SE->getUDivExpr(Diff, Val);
+ const SCEV *Product = SE->getMulExpr(Q, Val);
+ if (Diff == Product)
+ return true;
+ LLVM_DEBUG(dbgs() << "SCEV with different offsets:\n"
+ << *Param << " - " << *V << " = " << *Diff << "\n"
+ << "Remainder = " << *Remainder << "\n"
+ << "Q = " << *Q << " Product = " << *Product << "\n");
+ return false;
+ };
+ // Expressions like "n".
+ if (isa<SCEVUnknown>(V))
+ return checkParamsMultipleOfSize(V, Param);
+ // Expressions like "n + 1".
+ if (isa<SCEVAddExpr>(V))
+ return !SCEVExprContains(V, [](const SCEV *S) {
+ return isa<SCEVUnknown>(S);
+ }) || checkParamsMultipleOfSize(V, Param);
+
+ if (isa<SCEVMulExpr>(V))
+ return !SCEVExprContains(V, [](const SCEV *S) {
+ return isa<SCEVUnknown>(S);
+ }) || checkParamsMultipleOfSize(V, Param);
+
+ LLVM_DEBUG(dbgs() << "SCEV node not handled yet: " << *V << "\n");
+ return false;
+}
+
// depends -
// Returns NULL if there is no dependence.
// Otherwise, return a Dependence with as many details as possible.
@@ -3642,92 +3718,14 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
uint64_t EltSize = SrcLoc.Size.toRaw();
assert(EltSize == DstLoc.Size.toRaw() && "Array element size differ");
- // Check that memory access offsets in V are multiples of array EltSize.
- std::function<bool(const SCEV *, const SCEV *&)> checkOffsets =
- [&](const SCEV *V, const SCEV *&Param) -> bool {
- if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(V)) {
- if (!checkOffsets(AddRec->getStart(), Param))
- return false;
- return checkOffsets(AddRec->getStepRecurrence(*SE), Param);
- }
- if (auto *Cst = dyn_cast<SCEVConstant>(V)) {
- APInt C = Cst->getAPInt();
- return C.srem(EltSize) == 0;
- }
-
- auto checkParamsMultipleOfSize = [&](const SCEV *V,
- const SCEV *&Param) -> bool {
- if (EltSize == 1)
- return true;
- if (!Param) {
- Param = V;
- return true;
- }
- if (Param == V)
- return true;
-
- // Check whether "(Param - V) % Size == 0".
- const SCEV *Diff = SE->getMinusSCEV(Param, V);
- if (const SCEVConstant *Cst = dyn_cast<SCEVConstant>(Diff)) {
- APInt Val = Cst->getAPInt();
- if (Val.isZero())
- return true;
- auto Rem =
- Val.srem(APInt(Val.getBitWidth(), EltSize, /*isSigned=*/true));
- if (Rem.isZero())
- return true;
- LLVM_DEBUG(dbgs() << "SCEV with different offsets: " << *Param << " - "
- << *V << " = " << *Diff << " % " << EltSize << " = "
- << Rem << " != 0\n");
- return false;
- }
- // Check if the symbolic difference is a multiple of Size.
- const SCEV *Val = SE->getConstant(
- APInt(Diff->getType()->getScalarSizeInBits(), EltSize));
-
- // Check by using the remainder computation.
- const SCEV *Remainder = SE->getURemExpr(Diff, Val);
- if (const SCEVConstant *C = dyn_cast<SCEVConstant>(Remainder))
- if (C->getValue()->isZero())
- return true;
-
- // Check by using the division computation.
- const SCEV *Q = SE->getUDivExpr(Diff, Val);
- const SCEV *Product = SE->getMulExpr(Q, Val);
- if (Diff == Product)
- return true;
- LLVM_DEBUG(dbgs() << "SCEV with different offsets:\n"
- << *Param << " - " << *V << " = " << *Diff << "\n"
- << "Remainder = " << *Remainder << "\n"
- << "Q = " << *Q << " Product = " << *Product << "\n");
- return false;
- };
- // Expressions like "n".
- if (isa<SCEVUnknown>(V))
- return checkParamsMultipleOfSize(V, Param);
- // Expressions like "n + 1".
- if (isa<SCEVAddExpr>(V))
- return !SCEVExprContains(V, [](const SCEV *S) {
- return isa<SCEVUnknown>(S);
- }) || checkParamsMultipleOfSize(V, Param);
-
- if (isa<SCEVMulExpr>(V))
- return !SCEVExprContains(V, [](const SCEV *S) {
- return isa<SCEVUnknown>(S);
- }) || checkParamsMultipleOfSize(V, Param);
-
- LLVM_DEBUG(dbgs() << "SCEV node not handled yet: " << *V << "\n");
- return false;
- };
-
- // Check that memory access offsets are multiples of element sizes.
const SCEV *SrcEv = SE->getMinusSCEV(SrcSCEV, SrcBase);
const SCEV *DstEv = SE->getMinusSCEV(DstSCEV, DstBase);
const SCEV *Param = nullptr;
if (Src != Dst) {
// Check that memory access offsets are multiples of element sizes.
- if (!checkOffsets(SrcEv, Param) || !checkOffsets(DstEv, Param)) {
+ if (!checkOffsets(SE, SrcEv, Param, EltSize) ||
+ !checkOffsets(SE, DstEv, Param, EltSize)) {
LLVM_DEBUG(dbgs() << "can't analyze SCEV with different offsets\n");
return std::make_unique<Dependence>(Src, Dst);
}
>From a5751316de78b1a314e1a37cd9dc44a470556b8c Mon Sep 17 00:00:00 2001
From: Sebastian Pop <spop at nvidia.com>
Date: Wed, 26 Feb 2025 19:28:58 +0000
Subject: [PATCH 5/8] remove useless comment
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 1ace975ddf9e3..4a730fdf45f86 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3731,7 +3731,6 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst) {
}
}
- // establish loop nesting levels
establishNestingLevels(Src, Dst);
LLVM_DEBUG(dbgs() << " common nesting levels = " << CommonLevels << "\n");
LLVM_DEBUG(dbgs() << " maximum nesting levels = " << MaxLevels << "\n");
>From c4e67a916d4521f37cf854806d1e58b7eff485b9 Mon Sep 17 00:00:00 2001
From: Sebastian Pop <spop at nvidia.com>
Date: Wed, 26 Feb 2025 20:49:10 +0000
Subject: [PATCH 6/8] add more comments and examples
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 41 ++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 4a730fdf45f86..96f3dc5eca96c 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3569,6 +3569,11 @@ bool DependenceInfo::invalidate(Function &F, const PreservedAnalyses &PA,
Inv.invalidate<LoopAnalysis>(F, PA);
}
+// Check that memory access offsets in V are multiples of array element size
+// EltSize. Param records the first parametric expression. If the scalar
+// evolution V contains two or more parameters, we check that the subsequent
+// parametric expressions are multiples of the first parametric expression
+// Param.
static bool checkOffsets(ScalarEvolution *SE, const SCEV *V, const SCEV *&Param,
uint64_t EltSize) {
if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(V)) {
@@ -3578,9 +3583,20 @@ static bool checkOffsets(ScalarEvolution *SE, const SCEV *V, const SCEV *&Param,
}
if (auto *Cst = dyn_cast<SCEVConstant>(V)) {
APInt C = Cst->getAPInt();
+
+ // For example, alias_with_different_offsets in
+ // test/Analysis/DependenceAnalysis/DifferentOffsets.ll accesses "%A + 2":
+ // %arrayidx = getelementptr inbounds i8, ptr %A, i64 2
+ // store i32 42, ptr %arrayidx, align 1
+ // which is writing an i32, i.e., EltSize = 4 bytes, with an offset C = 2.
+ // checkOffsets returns false, as the offset C=2 is not a multiple of 4.
return C.srem(EltSize) == 0;
}
+ // Use a lambda helper function to check V for parametric expressions.
+ // Param records the first parametric expression. If the scalar evolution V
+ // contains two or more parameters, we check that the subsequent parametric
+ // expressions are multiples of the first parametric expression Param.
auto checkParamsMultipleOfSize = [&](const SCEV *V,
const SCEV *&Param) -> bool {
if (EltSize == 1)
@@ -3600,6 +3616,12 @@ static bool checkOffsets(ScalarEvolution *SE, const SCEV *V, const SCEV *&Param,
return true;
auto Rem = Val.srem(APInt(Val.getBitWidth(), EltSize, /*isSigned=*/true));
if (Rem.isZero())
+ // For example in test/Analysis/DependenceAnalysis/Preliminary.ll
+ // SrcSCEV = ((4 * (sext i8 %n to i64))<nsw> + %A)
+ // DstSCEV = (4 + (4 * (sext i8 %n to i64))<nsw> + %A)
+ // Param = (4 * (sext i8 %n to i64))<nsw>
+ // V = 4 + (4 * (sext i8 %n to i64))<nsw>
+ // Diff = -4, Rem = 0, and so all offsets are multiple of 4.
return true;
LLVM_DEBUG(dbgs() << "SCEV with different offsets: " << *Param << " - "
<< *V << " = " << *Diff << " % " << EltSize << " = "
@@ -3614,6 +3636,10 @@ static bool checkOffsets(ScalarEvolution *SE, const SCEV *V, const SCEV *&Param,
const SCEV *Remainder = SE->getURemExpr(Diff, Val);
if (const SCEVConstant *C = dyn_cast<SCEVConstant>(Remainder))
if (C->getValue()->isZero())
+ // For example test/Analysis/DependenceAnalysis/DADelin.ll
+ // SrcSCEV = {{{%A,+,(4 * %m * %o)}<%for.cond1.preheader>,+,(4 * %o)}
+ // DstSCEV = {{{%A,+,(4 * %m * %o)}<%for.cond1.preheader>,+,(4 * %o)}
+ // The strides '(4 * %m * %o)' and '(4 * %o)' are multiple of 4.
return true;
// Check by using the division computation.
@@ -3625,17 +3651,32 @@ static bool checkOffsets(ScalarEvolution *SE, const SCEV *V, const SCEV *&Param,
<< *Param << " - " << *V << " = " << *Diff << "\n"
<< "Remainder = " << *Remainder << "\n"
<< "Q = " << *Q << " Product = " << *Product << "\n");
+ // For example in test/Analysis/DependenceAnalysis/MIVCheckConst.ll
+ // SrcSCEV = {(80640 + (4 * (1 + %n) * %v1) + %A),+,(8 * %v1)}<%bb13>
+ // DstSCEV = {(126720 + (128 * %m) + %A),+,256}<%bb13>
+ // We fail to prove that the offsets 80640 + (4 * (1 + %n) * %v1) and
+ // (8 * %v1) are multiples of 128.
+ // Param = 80640 + (4 * (1 + %n) * %v1)
+ // (80640 + (4 * (1 + %n) * %v1)) - (8 * %v1) =
+ // (80640 + ((-4 + (4 * %n)) * %v1))
+ // Remainder = (zext i7 ((trunc i32 %v1 to i7) *
+ // (-4 + (4 * (trunc i32 %n to i7)))) to i32)
+ // Q = ((80640 + ((-4 + (4 * %n)) * %v1)) /u 128)
+ // Product = (128 * ((80640 + ((-4 + (4 * %n)) * %v1)) /u 128))<nuw>
return false;
};
+
// Expressions like "n".
if (isa<SCEVUnknown>(V))
return checkParamsMultipleOfSize(V, Param);
+
// Expressions like "n + 1".
if (isa<SCEVAddExpr>(V))
return !SCEVExprContains(V, [](const SCEV *S) {
return isa<SCEVUnknown>(S);
}) || checkParamsMultipleOfSize(V, Param);
+ // Expressions like "n * 2".
if (isa<SCEVMulExpr>(V))
return !SCEVExprContains(V, [](const SCEV *S) {
return isa<SCEVUnknown>(S);
>From 535b8124d943137743297139a666a2a6391598d2 Mon Sep 17 00:00:00 2001
From: Sebastian Pop <sebpop at gmail.com>
Date: Thu, 27 Feb 2025 11:51:05 -0600
Subject: [PATCH 7/8] add extra slash for function comments
Co-authored-by: Michael Kruse <github at meinersbur.de>
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 96f3dc5eca96c..4c3357f7543d3 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3569,11 +3569,11 @@ bool DependenceInfo::invalidate(Function &F, const PreservedAnalyses &PA,
Inv.invalidate<LoopAnalysis>(F, PA);
}
-// Check that memory access offsets in V are multiples of array element size
-// EltSize. Param records the first parametric expression. If the scalar
-// evolution V contains two or more parameters, we check that the subsequent
-// parametric expressions are multiples of the first parametric expression
-// Param.
+/// Check that memory access offsets in V are multiples of array element size
+/// EltSize. Param records the first parametric expression. If the scalar
+/// evolution V contains two or more parameters, we check that the subsequent
+/// parametric expressions are multiples of the first parametric expression
+/// Param.
static bool checkOffsets(ScalarEvolution *SE, const SCEV *V, const SCEV *&Param,
uint64_t EltSize) {
if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(V)) {
>From 08379138fcd6a9d489d4af52fded4746a581f1eb Mon Sep 17 00:00:00 2001
From: Sebastian Pop <sebpop at gmail.com>
Date: Thu, 27 Feb 2025 11:52:37 -0600
Subject: [PATCH 8/8] simplify logic with a single return
Co-authored-by: Michael Kruse <github at meinersbur.de>
---
llvm/lib/Analysis/DependenceAnalysis.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Analysis/DependenceAnalysis.cpp b/llvm/lib/Analysis/DependenceAnalysis.cpp
index 4c3357f7543d3..82b271152f614 100644
--- a/llvm/lib/Analysis/DependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/DependenceAnalysis.cpp
@@ -3577,9 +3577,8 @@ bool DependenceInfo::invalidate(Function &F, const PreservedAnalyses &PA,
static bool checkOffsets(ScalarEvolution *SE, const SCEV *V, const SCEV *&Param,
uint64_t EltSize) {
if (auto *AddRec = dyn_cast<SCEVAddRecExpr>(V)) {
- if (!checkOffsets(SE, AddRec->getStart(), Param, EltSize))
- return false;
- return checkOffsets(SE, AddRec->getStepRecurrence(*SE), Param, EltSize);
+ return checkOffsets(SE, AddRec->getStart(), Param, EltSize) &&
+ checkOffsets(SE, AddRec->getStepRecurrence(*SE), Param, EltSize);
}
if (auto *Cst = dyn_cast<SCEVConstant>(V)) {
APInt C = Cst->getAPInt();
More information about the llvm-commits
mailing list