[llvm] [VPlan] Introduce vputils::getStrideExpr (NFC) (PR #216760)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 2 05:05:25 PDT 2026


https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/216760

>From d28c125ad163fe140a8b4858886b34cadb9f44c2 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Mon, 17 Aug 2026 12:54:18 +0100
Subject: [PATCH] [VPlan] Introduce vputils::getStrideExpr (NFC)

This removes the LoopAccessAnalysis dependency.
---
 .../Transforms/Vectorize/VPlanTransforms.cpp  | 27 +++++-----
 llvm/lib/Transforms/Vectorize/VPlanUtils.cpp  | 54 +++++++++++++++----
 llvm/lib/Transforms/Vectorize/VPlanUtils.h    | 13 +++--
 3 files changed, 67 insertions(+), 27 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index cc1c9a683e4048..25fdeda58fa216 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -5794,7 +5794,7 @@ void VPlanTransforms::makeMemOpWideningDecisions(VPlan &Plan, VFRange &Range,
         VPValue *Ptr = VPI->getOperand(!IsLoad);
         Type *ScalarTy =
             IsLoad ? VPI->getScalarType() : VPI->getOperand(0)->getScalarType();
-        std::optional<int64_t> Stride =
+        std::optional<APInt> Stride =
             vputils::getConstantStride(Ptr, ScalarTy, CostCtx.PSE, CostCtx.L);
         if (Stride != 1 && Stride != -1)
           return false;
@@ -6144,14 +6144,16 @@ void VPlanTransforms::convertToStridedAccesses(VPlan &Plan,
       VPValue *Ptr = MemR->getAddr();
       // Check if this is a strided access by analyzing the address SCEV for an
       // affine addRec.
-      const SCEV *PtrSCEV = vputils::getSCEVExprForVPValue(Ptr, PSE, &L);
-      const SCEV *Start;
-      const SCEVConstant *Step;
+      auto StrideTup = vputils::getStrideExpr(
+          Ptr, PSE, L, Type::getInt8Ty(Plan.getContext()));
+      if (!StrideTup)
+        continue;
+      auto [Start, Stride, NW] = *StrideTup;
       // TODO: Support non-constant loop invariant stride.
-      if (!match(PtrSCEV,
-                 m_scev_AffineAddRec(m_SCEV(Start), m_SCEVConstant(Step),
-                                     m_SpecificLoop(&L))))
+      const APInt *StrideC;
+      if (!match(Stride, m_scev_APInt(StrideC)))
         continue;
+      bool HasNUW = any(NW & SCEV::FlagNUW);
 
       VPValue *StoredValue = nullptr;
       Type *DataTy;
@@ -6202,19 +6204,16 @@ void VPlanTransforms::convertToStridedAccesses(VPlan &Plan,
       // supports a general VPValue as the start value.
       VPValue *StartVPV =
           VPSCEVExpander(Builder, *PSE.getSE(), R.getDebugLoc()).expand(Start);
-      VPValue *StrideInBytes = Plan.getOrAddLiveIn(Step->getValue());
+      VPValue *StrideInBytes = Plan.getConstantInt(*StrideC);
       Type *IndexTy = Plan.getDataLayout().getIndexType(Ptr->getScalarType());
       assert(IndexTy == StrideInBytes->getScalarType() &&
              "Stride type from SCEV must match the index type");
       VPValue *CanIV = Builder.createScalarZExtOrTrunc(
           VectorLoop->getCanonicalIV(), IndexTy, DebugLoc::getUnknown());
-      auto *AddRecPtr = cast<SCEVAddRecExpr>(PtrSCEV);
       auto *Offset = Builder.createOverflowingOp(
-          Instruction::Mul, {CanIV, StrideInBytes},
-          {AddRecPtr->hasNoUnsignedWrap(), /*HasNSW=*/false});
-      GEPNoWrapFlags NWFlags = AddRecPtr->hasNoUnsignedWrap()
-                                   ? GEPNoWrapFlags::noUnsignedWrap()
-                                   : GEPNoWrapFlags::none();
+          Instruction::Mul, {CanIV, StrideInBytes}, {HasNUW, /*HasNSW=*/false});
+      GEPNoWrapFlags NWFlags =
+          HasNUW ? GEPNoWrapFlags::noUnsignedWrap() : GEPNoWrapFlags::none();
       VPValue *BasePtr = Builder.createNoWrapPtrAdd(StartVPV, Offset, NWFlags);
 
       // Create a new vector pointer for strided access.
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index b68040911c1429..09e628c901e6e3 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -19,7 +19,6 @@
 #include "llvm/Analysis/BlockFrequencyInfoImpl.h"
 #include "llvm/Analysis/BranchProbabilityInfo.h"
 #include "llvm/Analysis/InstSimplifyFolder.h"
-#include "llvm/Analysis/LoopAccessAnalysis.h"
 #include "llvm/Analysis/LoopInfo.h"
 #include "llvm/Analysis/MemoryLocation.h"
 #include "llvm/Analysis/ScalarEvolutionExpressions.h"
@@ -365,17 +364,52 @@ const SCEV *vputils::getSCEVExprForVPValue(const VPValue *V,
   return PSE.getPredicatedSCEV(Expr);
 }
 
-std::optional<int64_t>
-vputils::getConstantStride(VPValue *Addr, Type *AccessTy,
-                           PredicatedScalarEvolution &PSE, const Loop *L) {
+std::optional<std::tuple<const SCEV *, const SCEV *, SCEVFlags>>
+vputils::getStrideExpr(const VPValue *Ptr, PredicatedScalarEvolution &PSE,
+                       const Loop &L, Type *AccessTy) {
+  assert(Ptr->getScalarType()->isPointerTy() && "Ptr must be pointer type");
+  ScalarEvolution &SE = *PSE.getSE();
+  const SCEV *PtrSCEV = vputils::getSCEVExprForVPValue(Ptr, PSE, &L);
+  if (!isa<SCEVAddRecExpr>(PtrSCEV))
+    return std::nullopt;
+  SCEVFlags NWFlags = cast<SCEVAddRecExpr>(PtrSCEV)->getNoWrapFlags();
+  const SCEV *PointerBase = SE.getPointerBase(PtrSCEV);
+  const SCEV *StrideExpr = SE.removePointerBase(PtrSCEV);
+  Type *StrideTy = StrideExpr->getType();
+  const SCEV *Start;
+  const SCEV *Step;
+  if (!match(StrideExpr, m_scev_AffineAddRec(m_SCEV(Start), m_SCEV(Step),
+                                             m_SpecificLoop(&L))))
+    return std::nullopt;
+  const SCEV *Base =
+      SE.getAddExpr(PointerBase, SE.getNoopOrSignExtend(Start, StrideTy));
+  const DataLayout &DL = SE.getDataLayout();
+  TypeSize AllocSz = DL.getTypeAllocSize(AccessTy);
+  if (AllocSz.isScalable())
+    return std::nullopt;
+  // TODO: ScalarEvolution doesn't have SRem/SDiv expressions yet, so we
+  // resort to matching APInt.
+  const APInt *StepC;
+  if (!match(Step, m_scev_APInt(StepC)) ||
+      StepC->sext(StrideTy->getIntegerBitWidth()).srem(AllocSz) != 0)
+    return std::nullopt;
+  return std::make_tuple(
+      Base,
+      SE.getConstant(StepC->sext(StrideTy->getIntegerBitWidth()).sdiv(AllocSz)),
+      NWFlags);
+}
+
+std::optional<APInt> vputils::getConstantStride(VPValue *Addr, Type *AccessTy,
+                                                PredicatedScalarEvolution &PSE,
+                                                const Loop *L) {
   assert(!hasIrregularType(AccessTy, L->getHeader()->getDataLayout()) &&
          "should not try to widen irregular types");
-  const SCEV *AddrSCEV = getSCEVExprForVPValue(Addr, PSE, L);
-  auto *AddRec = dyn_cast<SCEVAddRecExpr>(AddrSCEV);
-  if (!AddRec)
-    return {};
-
-  return getStrideFromAddRec(AddRec, L, AccessTy, /*Ptr=*/nullptr, PSE);
+  if (auto StrideExpr = getStrideExpr(Addr, PSE, *L, AccessTy)) {
+    const APInt *C;
+    if (match(std::get<1>(*StrideExpr), m_scev_APInt(C)))
+      return *C;
+  }
+  return {};
 }
 
 bool vputils::isAddressSCEVForCost(const SCEV *Addr, ScalarEvolution &SE,
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index c046239e2f9830..c4827957a66774 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -47,12 +47,19 @@ LLVM_ABI_FOR_TEST const SCEV *
 getSCEVExprForVPValue(const VPValue *V, PredicatedScalarEvolution &PSE,
                       const Loop *L = nullptr);
 
+/// Get a stride expression, the AddRec's step found from \p Ptr divided by the
+/// alloc-size of \p AccessTy. Returns a tuple of the start SCEV expression, the
+/// stride SCEV expression, and the AddRec's wrap flags.
+std::optional<std::tuple<const SCEV *, const SCEV *, SCEVFlags>>
+getStrideExpr(const VPValue *Ptr, PredicatedScalarEvolution &PSE, const Loop &L,
+              Type *AccessTy);
+
 /// If the pointer operand \p Addr of a memory access is an affine AddRec
 /// w.r.t. \p L with a constant stride, return the stride in units of
 /// \p AccessTy. Otherwise return std::nullopt.
-std::optional<int64_t> getConstantStride(VPValue *Addr, Type *AccessTy,
-                                         PredicatedScalarEvolution &PSE,
-                                         const Loop *L);
+std::optional<APInt> getConstantStride(VPValue *Addr, Type *AccessTy,
+                                       PredicatedScalarEvolution &PSE,
+                                       const Loop *L);
 
 /// Returns true if \p Addr is an address SCEV that can be passed to
 /// TTI::getAddressComputationCost, i.e. the address SCEV is loop invariant, an



More information about the llvm-commits mailing list