[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