[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