[llvm] [SCEV] Use willNotOverflow for AR-check in udiv (NFC) (PR #217133)
Ramkumar Ramachandra via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 08:40:36 PDT 2026
https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/217133
>From 78368e88812e21ecf48ab61c4a490f416b32c16c Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Wed, 19 Aug 2026 08:53:15 +0100
Subject: [PATCH] [SCEV] Use willNotOverflow for AR-check in udiv (NFC)
Extend willNotOverflow to cover Instruction::PHI for AddRecs, and use it
to check the LHS AddRec's NUW in getUDivExpr.
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 14 ++--
llvm/lib/Analysis/ScalarEvolution.cpp | 69 +++++++++-----------
2 files changed, 38 insertions(+), 45 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index d17c21ea3401e..8c8b983caf870 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -704,12 +704,14 @@ class ScalarEvolution {
/// scAddRecExpr. The result will be cached in HasRecMap.
LLVM_ABI bool containsAddRecurrence(const SCEV *S);
- /// Is operation \p BinOp between \p LHS and \p RHS provably does not have
- /// a signed/unsigned overflow (\p Signed)? If \p CtxI is specified, the
- /// no-overflow fact should be true in the context of this instruction.
- LLVM_ABI bool willNotOverflow(Instruction::BinaryOps BinOp, bool Signed,
- const SCEV *LHS, const SCEV *RHS,
- const Instruction *CtxI = nullptr);
+ /// Is operation with \p Opcode between \p LHS and \p RHS provably does not
+ /// have a signed/unsigned overflow (\p Signed)? If \p CtxI is specified, the
+ /// no-overflow fact should be true in the context of this instruction. If \p
+ /// Opcode is Instruction::PHI, the parameter \p L must be specified.
+ LLVM_ABI bool willNotOverflow(unsigned Opcode, bool Signed, const SCEV *LHS,
+ const SCEV *RHS,
+ const Instruction *CtxI = nullptr,
+ const Loop *L = nullptr);
/// Parse NSW/NUW flags from add/sub/mul IR binary operation \p Op into
/// SCEV no-wrap flags, and deduce flag[s] that aren't known yet.
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index a05b7e9714f01..ae1e26c803eca 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -2337,46 +2337,54 @@ static bool CollectAddOperandsWithScales(SmallDenseMap<SCEVUse, APInt, 16> &M,
return Interesting;
}
-bool ScalarEvolution::willNotOverflow(Instruction::BinaryOps BinOp, bool Signed,
+bool ScalarEvolution::willNotOverflow(unsigned Opcode, bool Signed,
const SCEV *LHS, const SCEV *RHS,
- const Instruction *CtxI) {
- const SCEV *(ScalarEvolution::*Operation)(SCEVUse, SCEVUse, SCEV::NoWrapFlags,
- unsigned);
- switch (BinOp) {
+ const Instruction *CtxI, const Loop *L) {
+ using OpFnTy = const SCEV *(ScalarEvolution::*)(SCEVUse, SCEVUse,
+ SCEV::NoWrapFlags, unsigned);
+ std::function<const SCEV *(SCEVUse, SCEVUse, SCEV::NoWrapFlags, unsigned)>
+ OperationFn;
+ switch (Opcode) {
default:
llvm_unreachable("Unsupported binary op");
case Instruction::Add:
- Operation = &ScalarEvolution::getAddExpr;
+ OperationFn = bind_front<OpFnTy>(&ScalarEvolution::getAddExpr, this);
break;
case Instruction::Sub:
- Operation = &ScalarEvolution::getMinusSCEV;
+ OperationFn = bind_front<OpFnTy>(&ScalarEvolution::getMinusSCEV, this);
break;
case Instruction::Mul:
- Operation = &ScalarEvolution::getMulExpr;
+ OperationFn = bind_front<OpFnTy>(&ScalarEvolution::getMulExpr, this);
+ break;
+ case Instruction::PHI:
+ assert(L && "Loop argument must be given for PHI");
+ OperationFn = [&](SCEVUse LHS, SCEVUse RHS, SCEV::NoWrapFlags NW,
+ unsigned) { return getAddRecExpr(LHS, RHS, L, NW); };
break;
}
- const SCEV *(ScalarEvolution::*Extension)(SCEVUse, Type *, unsigned) =
- Signed ? &ScalarEvolution::getSignExtendExpr
- : &ScalarEvolution::getZeroExtendExpr;
+ using ExtFnTy = const SCEV *(ScalarEvolution::*)(SCEVUse, Type *, unsigned);
+ std::function<const SCEV *(SCEVUse, Type *, unsigned)> ExtensionFn =
+ Signed ? bind_front<ExtFnTy>(&ScalarEvolution::getSignExtendExpr, this)
+ : bind_front<ExtFnTy>(&ScalarEvolution::getZeroExtendExpr, this);
// Check ext(LHS op RHS) == ext(LHS) op ext(RHS)
auto *NarrowTy = cast<IntegerType>(LHS->getType());
auto *WideTy =
IntegerType::get(NarrowTy->getContext(), NarrowTy->getBitWidth() * 2);
- const SCEV *A = (this->*Extension)(
- (this->*Operation)(LHS, RHS, SCEV::FlagAnyWrap, 0), WideTy, 0);
- const SCEV *LHSB = (this->*Extension)(LHS, WideTy, 0);
- const SCEV *RHSB = (this->*Extension)(RHS, WideTy, 0);
- const SCEV *B = (this->*Operation)(LHSB, RHSB, SCEV::FlagAnyWrap, 0);
+ const SCEV *A =
+ ExtensionFn(OperationFn(LHS, RHS, SCEV::FlagAnyWrap, 0), WideTy, 0);
+ const SCEV *LHSB = ExtensionFn(LHS, WideTy, 0);
+ const SCEV *RHSB = ExtensionFn(RHS, WideTy, 0);
+ const SCEV *B = OperationFn(LHSB, RHSB, SCEV::FlagAnyWrap, 0);
if (A == B)
return true;
// Can we use context to prove the fact we need?
if (!CtxI)
return false;
// TODO: Support mul.
- if (BinOp == Instruction::Mul)
+ if (Opcode == Instruction::Mul)
return false;
auto *RHSC = dyn_cast<SCEVConstant>(RHS);
// TODO: Lift this limitation.
@@ -2384,7 +2392,7 @@ bool ScalarEvolution::willNotOverflow(Instruction::BinaryOps BinOp, bool Signed,
return false;
APInt C = RHSC->getAPInt();
unsigned NumBits = C.getBitWidth();
- bool IsSub = (BinOp == Instruction::Sub);
+ bool IsSub = (Opcode == Instruction::Sub);
bool IsNegativeConst = (Signed && C.isNegative());
// Compute the direction and magnitude by which we need to check overflow.
bool OverflowDown = IsSub ^ IsNegativeConst;
@@ -3520,27 +3528,16 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
if (!RHSC->getValue()->isZero()) {
// Determine if the division can be folded into the operands of
// its operands.
- // TODO: Generalize this to non-constants by using known-bits information.
- Type *Ty = LHS->getType();
- unsigned LZ = RHSC->getAPInt().countl_zero();
- unsigned MaxShiftAmt = getTypeSizeInBits(Ty) - LZ - 1;
- // For non-power-of-two values, effectively round the value up to the
- // nearest power of two.
- if (!RHSC->getAPInt().isPowerOf2())
- ++MaxShiftAmt;
- IntegerType *ExtTy =
- IntegerType::get(getContext(), getTypeSizeInBits(Ty) + MaxShiftAmt);
if (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(LHS))
if (const SCEVConstant *Step =
dyn_cast<SCEVConstant>(AR->getStepRecurrence(*this))) {
// {X,+,N}/C --> {X/C,+,N/C} if safe and N/C can be folded.
const APInt &StepInt = Step->getAPInt();
const APInt &DivInt = RHSC->getAPInt();
- if (!StepInt.urem(DivInt) &&
- getZeroExtendExpr(AR, ExtTy) ==
- getAddRecExpr(getZeroExtendExpr(AR->getStart(), ExtTy),
- getZeroExtendExpr(Step, ExtTy),
- AR->getLoop(), SCEV::FlagAnyWrap)) {
+ bool NoWrap = willNotOverflow(Instruction::PHI, /*Signed=*/false,
+ AR->getStart(), Step, /*CtxI=*/nullptr,
+ AR->getLoop());
+ if (!StepInt.urem(DivInt) && NoWrap) {
SmallVector<SCEVUse, 4> Operands;
for (const SCEV *Op : AR->operands())
Operands.push_back(getUDivExpr(Op, RHS));
@@ -3551,12 +3548,6 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
const APInt *StartRem;
if (!DivInt.urem(StepInt) && match(getURemExpr(AR->getStart(), Step),
m_scev_APInt(StartRem))) {
- bool NoWrap =
- getZeroExtendExpr(AR, ExtTy) ==
- getAddRecExpr(getZeroExtendExpr(AR->getStart(), ExtTy),
- getZeroExtendExpr(Step, ExtTy), AR->getLoop(),
- SCEV::FlagAnyWrap);
-
// With N <= C and both N, C as powers-of-2, the transformation
// {X,+,N}/C => {(X - X%N),+,N}/C preserves division results even
// if wrapping occurs, as the division results remain equivalent for
More information about the llvm-commits
mailing list