[llvm] [LoopAccessAnalysis] Analyze forked pointer dependences per alternative (PR #214216)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 5 05:36:52 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Sahil Kumar (Samyra312007)
<details>
<summary>Changes</summary>
The memory dependence checker reported an IndirectUnsafe dependence whenever
an access pointer is a fork of multiple strided pointers, e.g. produced by a
select of two pointers, because SCEV cannot form a single AddRec for it and
getPtrStride fails. The runtime pointer checking already handles such pointers
by analyzing each fork alternative separately (findForkedSCEVs); the dependence
analysis now does the same.
Each pair of fork alternatives is checked and the results are aggregated:
any unsafe pair makes the whole dependence unsafe, any pair that needs a
runtime check classifies the dependence as unknown (so the loop is retried
with runtime checks), and otherwise the first safe classification is kept.
This allows vectorizing loops like
for (i = 0; i < n; ++i)
result[i] = condition[i] ? input[i] : result[i];
which previously failed with "Unsafe indirect dependence" and is now vectorized
as a predicated select of the two pointer forks with runtime pointer checks.
Tests:
- Analysis/LoopAccessAnalysis/select-pointer-dependence.ll
- Transforms/LoopVectorize/select-pointer-dependence.ll
- Updated existing expectations in
Analysis/LoopAccessAnalysis/select-dependence.ll
(IndirectUnsafe -> safe with runtime checks)
Fixes #<!-- -->212787
---
Patch is 27.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/214216.diff
5 Files Affected:
- (modified) llvm/include/llvm/Analysis/LoopAccessAnalysis.h (+42)
- (modified) llvm/lib/Analysis/LoopAccessAnalysis.cpp (+149-6)
- (modified) llvm/test/Analysis/LoopAccessAnalysis/select-dependence.ll (+76-12)
- (added) llvm/test/Analysis/LoopAccessAnalysis/select-pointer-dependence.ll (+59)
- (added) llvm/test/Transforms/LoopVectorize/select-pointer-dependence.ll (+106)
``````````diff
diff --git a/llvm/include/llvm/Analysis/LoopAccessAnalysis.h b/llvm/include/llvm/Analysis/LoopAccessAnalysis.h
index 392321448c895..c149feab6f405 100644
--- a/llvm/include/llvm/Analysis/LoopAccessAnalysis.h
+++ b/llvm/include/llvm/Analysis/LoopAccessAnalysis.h
@@ -447,6 +447,48 @@ class MemoryDepChecker {
const MemAccessInfo &B,
Instruction *BInst);
+ /// Get the stride of a SCEV expression for the dependence analysis, without
+ /// considering symbolic strides or wrap predicates that the whole-pointer
+ /// analysis in getPtrStride may add. Returns 0 for loop-invariant SCEVs,
+ /// the element count stride for affine non-wrapping AddRecs and
+ /// std::nullopt otherwise. \p Predicates collects any no-wrap assumptions
+ /// made on the AddRec.
+ std::optional<int64_t> getStrideFromSCEV(
+ const SCEV *S, Type *AccessTy,
+ SmallVectorImpl<const SCEVPredicate *> *Predicates);
+
+ /// Compute the dependence distance/stride info (or directly a DepType, if
+ /// the analysis can already be resolved) for a pair of access SCEVs with the
+ /// given element-count strides. This is the shared analysis used both for
+ /// whole pointers (see getDependenceDistanceStrideAndSize) and for each
+ /// alternative of a forked pointer (see getForkedDepType).
+ std::variant<Dependence::DepType, DepDistanceStrideAndSizeInfo>
+ classifyStridedDistance(const SCEV *Src, const SCEV *Sink,
+ std::optional<int64_t> StrideAPtr,
+ std::optional<int64_t> StrideBPtr, Type *ATy,
+ Type *BTy, bool AIsWrite, bool BIsWrite,
+ Instruction *AInst, Instruction *BInst);
+
+ /// Classify the dependence for a pair of access SCEVs given the distance and
+ /// stride info computed by classifyStridedDistance, updating the safe vector
+ /// width limits as appropriate.
+ Dependence::DepType classifyDependence(const SCEV *Src, const SCEV *Sink,
+ Type *SrcTy, Type *SinkTy,
+ const DepDistanceStrideAndSizeInfo &Info);
+
+ /// Check the dependence between two accesses when at least one of the access
+ /// pointers is a fork of multiple strided pointers, e.g. produced by a
+ /// select of two pointers. SCEV cannot form a single AddRec for such a
+ /// pointer, so the analysis in getDependenceDistanceStrideAndSize would
+ /// report an IndirectUnsafe dependence. Instead, check each pair of fork
+ /// alternatives (mirroring the runtime pointer checking in
+ /// AccessAnalysis::createCheckForAccess) and aggregate the results. Returns
+ /// std::nullopt if neither access pointer is a fork, in which case the
+ /// regular single-SCEV analysis should be used.
+ std::optional<Dependence::DepType>
+ getForkedDepType(const MemAccessInfo &A, Instruction *AInst,
+ const MemAccessInfo &B, Instruction *BInst);
+
// Return true if we can prove that \p Sink only accesses memory after \p
// Src's end or vice versa.
bool areAccessesCompletelyBeforeOrAfter(const SCEV *Src, Type *SrcTy,
diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
index e248b22de7d43..0e348416aa1e7 100644
--- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp
+++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp
@@ -2120,8 +2120,6 @@ std::variant<MemoryDepChecker::Dependence::DepType,
MemoryDepChecker::getDependenceDistanceStrideAndSize(
const AccessAnalysis::MemAccessInfo &A, Instruction *AInst,
const AccessAnalysis::MemAccessInfo &B, Instruction *BInst) {
- const auto &DL = InnermostLoop->getHeader()->getDataLayout();
- auto &SE = *PSE.getSE();
const auto &[APtr, AIsWrite] = A;
const auto &[BPtr, BIsWrite] = B;
@@ -2146,8 +2144,19 @@ MemoryDepChecker::getDependenceDistanceStrideAndSize(
/*ShouldCheckWrap=*/true, &Predicates);
PSE.addPredicates(Predicates);
- const SCEV *Src = PSE.getSCEV(APtr);
- const SCEV *Sink = PSE.getSCEV(BPtr);
+ return classifyStridedDistance(PSE.getSCEV(APtr), PSE.getSCEV(BPtr),
+ StrideAPtr, StrideBPtr, ATy, BTy, AIsWrite,
+ BIsWrite, AInst, BInst);
+}
+
+std::variant<MemoryDepChecker::Dependence::DepType,
+ MemoryDepChecker::DepDistanceStrideAndSizeInfo>
+MemoryDepChecker::classifyStridedDistance(
+ const SCEV *Src, const SCEV *Sink, std::optional<int64_t> StrideAPtr,
+ std::optional<int64_t> StrideBPtr, Type *ATy, Type *BTy, bool AIsWrite,
+ bool BIsWrite, Instruction *AInst, Instruction *BInst) {
+ const auto &DL = InnermostLoop->getHeader()->getDataLayout();
+ auto &SE = *PSE.getSE();
// If the induction step is negative we have to invert source and sink of the
// dependence when measuring the distance between them. We should not swap
@@ -2234,6 +2243,112 @@ MemoryDepChecker::getDependenceDistanceStrideAndSize(
TypeByteSize, AIsWrite, BIsWrite);
}
+std::optional<int64_t>
+MemoryDepChecker::getStrideFromSCEV(
+ const SCEV *S, Type *AccessTy,
+ SmallVectorImpl<const SCEVPredicate *> *Predicates) {
+ ScalarEvolution &SE = *PSE.getSE();
+ if (SE.isLoopInvariant(S, InnermostLoop))
+ return 0;
+ const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(S);
+ if (!AR)
+ return std::nullopt;
+ std::optional<int64_t> Stride =
+ getStrideFromAddRec(AR, InnermostLoop, AccessTy, /*Ptr=*/nullptr, PSE);
+ if (!Stride)
+ return std::nullopt;
+ if (isNoWrap(PSE, AR, /*Ptr=*/nullptr, AccessTy, InnermostLoop, *DT, Stride,
+ Predicates))
+ return Stride;
+ return std::nullopt;
+}
+
+std::optional<MemoryDepChecker::Dependence::DepType>
+MemoryDepChecker::getForkedDepType(const MemAccessInfo &A, Instruction *AInst,
+ const MemAccessInfo &B, Instruction *BInst) {
+ // Two reads are independent.
+ const auto &[APtr, AIsWrite] = A;
+ const auto &[BPtr, BIsWrite] = B;
+ if (!AIsWrite && !BIsWrite)
+ return Dependence::NoDep;
+
+ ScalarEvolution &SE = *PSE.getSE();
+
+ // A pointer may be a fork of multiple strided pointers, e.g. produced by a
+ // select of two pointers. SCEV cannot form a single AddRec for such a
+ // pointer, so the regular analysis in getDependenceDistanceStrideAndSize
+ // would report an IndirectUnsafe dependence. The runtime pointer checking
+ // handles such pointers by analyzing each fork alternative separately
+ // (findForkedSCEVs); do the same here by checking each pair of alternatives
+ // and aggregating the results.
+ SmallVector<PointerIntPair<const SCEV *, 1, bool>> Srcs;
+ SmallVector<PointerIntPair<const SCEV *, 1, bool>> Sinks;
+ findForkedSCEVs(&SE, InnermostLoop, APtr, Srcs, MaxForkedSCEVDepth);
+ findForkedSCEVs(&SE, InnermostLoop, BPtr, Sinks, MaxForkedSCEVDepth);
+
+ // If neither access pointer is a fork, fall back to the regular single-SCEV
+ // analysis in getDependenceDistanceStrideAndSize.
+ if (Srcs.size() == 1 && Sinks.size() == 1)
+ return std::nullopt;
+
+ // We can only analyze a fork if each alternative is loop-invariant or an
+ // affine AddRec; this is the same requirement as for generating runtime
+ // checks for forked pointers (see AccessAnalysis::createCheckForAccess).
+ auto IsLoopInvariantOrAR =
+ [&](const PointerIntPair<const SCEV *, 1, bool> &P) {
+ return SE.isLoopInvariant(P.getPointer(), InnermostLoop) ||
+ isa<SCEVAddRecExpr>(P.getPointer());
+ };
+ if (!all_of(Srcs, IsLoopInvariantOrAR) ||
+ !all_of(Sinks, IsLoopInvariantOrAR))
+ return Dependence::IndirectUnsafe;
+
+ // We cannot check pointers in different address spaces.
+ if (APtr->getType()->getPointerAddressSpace() !=
+ BPtr->getType()->getPointerAddressSpace())
+ return Dependence::Unknown;
+
+ Type *ATy = getLoadStoreType(AInst);
+ Type *BTy = getLoadStoreType(BInst);
+
+ SmallVector<const SCEVPredicate *> Predicates;
+ Dependence::DepType Result = Dependence::NoDep;
+ bool ResultNeedsRtCheck = false;
+ for (const auto &[SrcArm, _] : Srcs) {
+ for (const auto &[SinkArm, _] : Sinks) {
+ std::optional<int64_t> StrideSrc =
+ getStrideFromSCEV(SrcArm, ATy, &Predicates);
+ std::optional<int64_t> StrideSink =
+ getStrideFromSCEV(SinkArm, BTy, &Predicates);
+
+ auto ArmRes = classifyStridedDistance(
+ SrcArm, SinkArm, StrideSrc, StrideSink, ATy, BTy, AIsWrite, BIsWrite,
+ AInst, BInst);
+ Dependence::DepType ArmType =
+ std::holds_alternative<Dependence::DepType>(ArmRes)
+ ? std::get<Dependence::DepType>(ArmRes)
+ : classifyDependence(SrcArm, SinkArm, ATy, BTy,
+ std::get<DepDistanceStrideAndSizeInfo>(ArmRes));
+
+ // Aggregate conservatively over all fork alternatives: if any pair of
+ // alternatives has an unsafe dependence then the fork as a whole is
+ // unsafe; otherwise, if any pair needs a runtime check to prove
+ // independence, classify the dependence as unknown so that we retry with
+ // runtime checks.
+ if (Dependence::isSafeForVectorization(ArmType) ==
+ VectorizationSafetyStatus::Unsafe)
+ return ArmType;
+ if (Dependence::isSafeForVectorization(ArmType) ==
+ VectorizationSafetyStatus::PossiblySafeWithRtChecks)
+ ResultNeedsRtCheck = true;
+ else if (Result == Dependence::NoDep)
+ Result = ArmType;
+ }
+ }
+ PSE.addPredicates(Predicates);
+ return ResultNeedsRtCheck ? Dependence::Unknown : Result;
+}
+
MemoryDepChecker::Dependence::DepType
MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx,
const MemAccessInfo &B, unsigned BIdx) {
@@ -2252,6 +2367,15 @@ MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx,
return areAccessesCompletelyBeforeOrAfter(Src, ATy, Sink, BTy);
};
+ // If either access pointer is a fork of multiple strided pointers, e.g.
+ // produced by a select of two pointers, SCEV cannot form a single AddRec for
+ // the pointer and the analysis in getDependenceDistanceStrideAndSize would
+ // report an IndirectUnsafe dependence. Instead, analyze each pair of fork
+ // alternatives and aggregate the results (see getForkedDepType).
+ if (std::optional<Dependence::DepType> ForkedType =
+ getForkedDepType(A, InstMap[AIdx], B, InstMap[BIdx]))
+ return *ForkedType;
+
// Get the dependence distance, stride, type size and what access writes for
// the dependence between A and B.
auto Res =
@@ -2263,13 +2387,32 @@ MemoryDepChecker::isDependent(const MemAccessInfo &A, unsigned AIdx,
return std::get<Dependence::DepType>(Res);
}
- auto &[Dist, MaxStride, CommonStride, TypeByteSize, AIsWrite, BIsWrite] =
- std::get<DepDistanceStrideAndSizeInfo>(Res);
+ return classifyDependence(PSE.getSCEV(A.getPointer()),
+ PSE.getSCEV(B.getPointer()),
+ getLoadStoreType(InstMap[AIdx]),
+ getLoadStoreType(InstMap[BIdx]),
+ std::get<DepDistanceStrideAndSizeInfo>(Res));
+}
+
+MemoryDepChecker::Dependence::DepType
+MemoryDepChecker::classifyDependence(
+ const SCEV *Src, const SCEV *Sink, Type *SrcTy, Type *SinkTy,
+ const DepDistanceStrideAndSizeInfo &Info) {
+ const SCEV *Dist = Info.Dist;
+ uint64_t MaxStride = Info.MaxStride;
+ std::optional<uint64_t> CommonStride = Info.CommonStride;
+ uint64_t TypeByteSize = Info.TypeByteSize;
+ bool AIsWrite = Info.AIsWrite;
+ bool BIsWrite = Info.BIsWrite;
bool HasSameSize = TypeByteSize > 0;
ScalarEvolution &SE = *PSE.getSE();
auto &DL = InnermostLoop->getHeader()->getDataLayout();
+ auto CheckCompletelyBeforeOrAfter = [&]() {
+ return areAccessesCompletelyBeforeOrAfter(Src, SrcTy, Sink, SinkTy);
+ };
+
// If the distance between the acecsses is larger than their maximum absolute
// stride multiplied by the symbolic maximum backedge taken count (which is an
// upper bound of the number of iterations), the accesses are independet, i.e.
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/select-dependence.ll b/llvm/test/Analysis/LoopAccessAnalysis/select-dependence.ll
index 5b01efd821c47..9a1b20df11f30 100644
--- a/llvm/test/Analysis/LoopAccessAnalysis/select-dependence.ll
+++ b/llvm/test/Analysis/LoopAccessAnalysis/select-dependence.ll
@@ -4,15 +4,47 @@
define void @test(ptr noalias %x, ptr noalias %y, ptr noalias %z) {
; CHECK-LABEL: 'test'
; CHECK-NEXT: loop:
-; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT: Unsafe indirect dependence.
+; CHECK-NEXT: Memory dependences are safe with a maximum safe vector width of 2048 bits, with a maximum safe store-load forward width of 2048 bits with run-time checks
; CHECK-NEXT: Dependences:
-; CHECK-NEXT: IndirectUnsafe:
-; CHECK-NEXT: %load = load double, ptr %gep.sel, align 8 ->
-; CHECK-NEXT: store double %load, ptr %gep.sel2, align 8
-; CHECK-EMPTY:
; CHECK-NEXT: Run-time memory checks:
+; CHECK-NEXT: Check 0:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Against group GRP1:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Check 1:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Against group GRP2:
+; CHECK-NEXT: %gep.sel = getelementptr inbounds double, ptr %sel, i64 %iv
+; CHECK-NEXT: Check 2:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Against group GRP3:
+; CHECK-NEXT: %gep.sel = getelementptr inbounds double, ptr %sel, i64 %iv
+; CHECK-NEXT: Check 3:
+; CHECK-NEXT: Comparing group GRP1:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Against group GRP2:
+; CHECK-NEXT: %gep.sel = getelementptr inbounds double, ptr %sel, i64 %iv
+; CHECK-NEXT: Check 4:
+; CHECK-NEXT: Comparing group GRP1:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Against group GRP3:
+; CHECK-NEXT: %gep.sel = getelementptr inbounds double, ptr %sel, i64 %iv
; CHECK-NEXT: Grouped accesses:
+; CHECK-NEXT: Group GRP0:
+; CHECK-NEXT: (Low: %y High: (760 + %y))
+; CHECK-NEXT: Member: {%y,+,8}<nw><%loop>
+; CHECK-NEXT: Group GRP1:
+; CHECK-NEXT: (Low: %z High: (760 + %z))
+; CHECK-NEXT: Member: {%z,+,8}<nw><%loop>
+; CHECK-NEXT: Group GRP2:
+; CHECK-NEXT: (Low: %x High: (760 + %x))
+; CHECK-NEXT: Member: {%x,+,8}<nw><%loop>
+; CHECK-NEXT: Group GRP3:
+; CHECK-NEXT: (Low: (-256 + %y) High: (504 + %y))
+; CHECK-NEXT: Member: {(-256 + %y),+,8}<nw><%loop>
; CHECK-EMPTY:
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
; CHECK-NEXT: SCEV assumptions:
@@ -44,15 +76,47 @@ exit:
define void @test_phi(ptr noalias %x, ptr noalias %y, ptr noalias %z) {
; CHECK-LABEL: 'test_phi'
; CHECK-NEXT: loop:
-; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop
-; CHECK-NEXT: Unsafe indirect dependence.
+; CHECK-NEXT: Memory dependences are safe with a maximum safe vector width of 2048 bits, with a maximum safe store-load forward width of 2048 bits with run-time checks
; CHECK-NEXT: Dependences:
-; CHECK-NEXT: IndirectUnsafe:
-; CHECK-NEXT: %load = load double, ptr %gep.sel, align 8 ->
-; CHECK-NEXT: store double %load, ptr %gep.sel2, align 8
-; CHECK-EMPTY:
; CHECK-NEXT: Run-time memory checks:
+; CHECK-NEXT: Check 0:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Against group GRP1:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Check 1:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Against group GRP2:
+; CHECK-NEXT: %gep.sel = getelementptr inbounds double, ptr %sel, i64 %iv
+; CHECK-NEXT: Check 2:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Against group GRP3:
+; CHECK-NEXT: %gep.sel = getelementptr inbounds double, ptr %sel, i64 %iv
+; CHECK-NEXT: Check 3:
+; CHECK-NEXT: Comparing group GRP1:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Against group GRP2:
+; CHECK-NEXT: %gep.sel = getelementptr inbounds double, ptr %sel, i64 %iv
+; CHECK-NEXT: Check 4:
+; CHECK-NEXT: Comparing group GRP1:
+; CHECK-NEXT: %gep.sel2 = getelementptr inbounds double, ptr %sel2, i64 %iv
+; CHECK-NEXT: Against group GRP3:
+; CHECK-NEXT: %gep.sel = getelementptr inbounds double, ptr %sel, i64 %iv
; CHECK-NEXT: Grouped accesses:
+; CHECK-NEXT: Group GRP0:
+; CHECK-NEXT: (Low: %y High: (760 + %y))
+; CHECK-NEXT: Member: {%y,+,8}<nw><%loop>
+; CHECK-NEXT: Group GRP1:
+; CHECK-NEXT: (Low: %z High: (760 + %z))
+; CHECK-NEXT: Member: {%z,+,8}<nw><%loop>
+; CHECK-NEXT: Group GRP2:
+; CHECK-NEXT: (Low: %x High: (760 + %x))
+; CHECK-NEXT: Member: {%x,+,8}<nw><%loop>
+; CHECK-NEXT: Group GRP3:
+; CHECK-NEXT: (Low: (-256 + %y) High: (504 + %y))
+; CHECK-NEXT: Member: {(-256 + %y),+,8}<nw><%loop>
; CHECK-EMPTY:
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.
; CHECK-NEXT: SCEV assumptions:
diff --git a/llvm/test/Analysis/LoopAccessAnalysis/select-pointer-dependence.ll b/llvm/test/Analysis/LoopAccessAnalysis/select-pointer-dependence.ll
new file mode 100644
index 0000000000000..7541454bef1a8
--- /dev/null
+++ b/llvm/test/Analysis/LoopAccessAnalysis/select-pointer-dependence.ll
@@ -0,0 +1,59 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes='print<access-info>' -disable-output 2>&1 < %s | FileCheck %s
+
+; Test case for vectorizing loops where the load pointer is a select (fork) of
+; multiple strided pointers and one of the alternatives aliases the store
+; target. The dependence analysis must check each pair of fork alternatives
+; (mirroring the runtime pointer checks) instead of giving up with an
+; IndirectUnsafe dependence.
+
+define void @cond_update(ptr noalias %out, ptr noalias %src_b, i1 %s, i64 %n) {
+; CHECK-LABEL: 'cond_update'
+; CHECK-NEXT: loop:
+; CHECK-NEXT: Memory dependences are safe with run-time checks
+; CHECK-NEXT: Dependences:
+; CHECK-NEXT: Run-time memory checks:
+; CHECK-NEXT: Check 0:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %go = getelementptr inbounds i64, ptr %out, i64 %i
+; CHECK-NEXT: Against group GRP1:
+; CHECK-NEXT: %sel = select i1 %s, ptr %go, ptr %gb
+; CHECK-NEXT: Check 1:
+; CHECK-NEXT: Comparing group GRP0:
+; CHECK-NEXT: %go = getelementptr inbounds i64, ptr %out, i64 %i
+; CHECK-NEXT: Against group GRP2:
+; CHECK-NEXT: %sel = select i1 %s, ptr %go, ptr %gb
+; CHECK-NEXT: Grouped accesses:
+; CHECK-NEXT: Group GRP0:
+; CHECK-NEXT: (Low: %out High: ((8 * %n) + %out))
+; CHECK-NEXT: Member: {%out,+,8}<nuw><%loop>
+; CHECK-NEXT: Grou...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/214216
More information about the llvm-commits
mailing list