[llvm] [SCEV] Share logic of udiv-AR-overflow with willNotOverflow (PR #217133)
Ramkumar Ramachandra via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 14:21:14 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 1/3] [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
>From 421424fe797c951d3ef128c701a257dd3111efa4 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Sun, 23 Aug 2026 17:27:18 +0100
Subject: [PATCH 2/3] [SCEV] Fix a couple of minor issues
---
llvm/lib/Analysis/ScalarEvolution.cpp | 5 +-
.../uniform_across_vf_induction1_div_urem.ll | 52 +++++++++++++++----
2 files changed, 44 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index ae1e26c803eca..239bc0512a50d 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -2384,7 +2384,7 @@ bool ScalarEvolution::willNotOverflow(unsigned Opcode, bool Signed,
if (!CtxI)
return false;
// TODO: Support mul.
- if (Opcode == Instruction::Mul)
+ if (Opcode == Instruction::Mul || Opcode == Instruction::PHI)
return false;
auto *RHSC = dyn_cast<SCEVConstant>(RHS);
// TODO: Lift this limitation.
@@ -3534,7 +3534,8 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
// {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();
- bool NoWrap = willNotOverflow(Instruction::PHI, /*Signed=*/false,
+ bool NoWrap = !StepInt.urem(DivInt) &&
+ willNotOverflow(Instruction::PHI, /*Signed=*/false,
AR->getStart(), Step, /*CtxI=*/nullptr,
AR->getLoop());
if (!StepInt.urem(DivInt) && NoWrap) {
diff --git a/llvm/test/Transforms/LoopVectorize/uniform_across_vf_induction1_div_urem.ll b/llvm/test/Transforms/LoopVectorize/uniform_across_vf_induction1_div_urem.ll
index 3f40db5179407..7c14a9562394e 100644
--- a/llvm/test/Transforms/LoopVectorize/uniform_across_vf_induction1_div_urem.ll
+++ b/llvm/test/Transforms/LoopVectorize/uniform_across_vf_induction1_div_urem.ll
@@ -239,18 +239,48 @@ define void @ld_div8_urem3(ptr noalias %A, ptr noalias %B) {
; CHECK-NEXT: br label [[VECTOR_BODY:%.*]]
; CHECK: vector.body:
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ]
-; CHECK-NEXT: [[TMP0:%.*]] = lshr i64 [[INDEX]], 3
-; CHECK-NEXT: [[TMP1:%.*]] = urem i64 [[TMP0]], 3
-; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i64, ptr [[A]], i64 [[TMP1]]
-; CHECK-NEXT: [[TMP3:%.*]] = load i64, ptr [[TMP2]], align 8
-; CHECK-NEXT: [[TMP4:%.*]] = add nsw i64 [[TMP3]], 42
-; CHECK-NEXT: [[BROADCAST_SPLATINSERT:%.*]] = insertelement <8 x i64> poison, i64 [[TMP4]], i64 0
-; CHECK-NEXT: [[BROADCAST_SPLAT:%.*]] = shufflevector <8 x i64> [[BROADCAST_SPLATINSERT]], <8 x i64> poison, <8 x i32> zeroinitializer
-; CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i64, ptr [[B]], i64 [[INDEX]]
-; CHECK-NEXT: store <8 x i64> [[BROADCAST_SPLAT]], ptr [[TMP5]], align 8
+; CHECK-NEXT: [[VEC_IND:%.*]] = phi <8 x i64> [ <i64 0, i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 7>, [[VECTOR_PH]] ], [ [[VEC_IND_NEXT:%.*]], [[VECTOR_BODY]] ]
+; CHECK-NEXT: [[TMP0:%.*]] = lshr <8 x i64> [[VEC_IND]], splat (i64 3)
+; CHECK-NEXT: [[TMP1:%.*]] = urem <8 x i64> [[TMP0]], splat (i64 3)
+; CHECK-NEXT: [[TMP2:%.*]] = extractelement <8 x i64> [[TMP1]], i64 0
+; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i64, ptr [[A]], i64 [[TMP2]]
+; CHECK-NEXT: [[TMP4:%.*]] = extractelement <8 x i64> [[TMP1]], i64 1
+; CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i64, ptr [[A]], i64 [[TMP4]]
+; CHECK-NEXT: [[TMP6:%.*]] = extractelement <8 x i64> [[TMP1]], i64 2
+; CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds i64, ptr [[A]], i64 [[TMP6]]
+; CHECK-NEXT: [[TMP8:%.*]] = extractelement <8 x i64> [[TMP1]], i64 3
+; CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds i64, ptr [[A]], i64 [[TMP8]]
+; CHECK-NEXT: [[TMP10:%.*]] = extractelement <8 x i64> [[TMP1]], i64 4
+; CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds i64, ptr [[A]], i64 [[TMP10]]
+; CHECK-NEXT: [[TMP12:%.*]] = extractelement <8 x i64> [[TMP1]], i64 5
+; CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds i64, ptr [[A]], i64 [[TMP12]]
+; CHECK-NEXT: [[TMP14:%.*]] = extractelement <8 x i64> [[TMP1]], i64 6
+; CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds i64, ptr [[A]], i64 [[TMP14]]
+; CHECK-NEXT: [[TMP16:%.*]] = extractelement <8 x i64> [[TMP1]], i64 7
+; CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds i64, ptr [[A]], i64 [[TMP16]]
+; CHECK-NEXT: [[TMP18:%.*]] = load i64, ptr [[TMP3]], align 8
+; CHECK-NEXT: [[TMP19:%.*]] = load i64, ptr [[TMP5]], align 8
+; CHECK-NEXT: [[TMP20:%.*]] = load i64, ptr [[TMP7]], align 8
+; CHECK-NEXT: [[TMP21:%.*]] = load i64, ptr [[TMP9]], align 8
+; CHECK-NEXT: [[TMP22:%.*]] = load i64, ptr [[TMP11]], align 8
+; CHECK-NEXT: [[TMP23:%.*]] = load i64, ptr [[TMP13]], align 8
+; CHECK-NEXT: [[TMP24:%.*]] = load i64, ptr [[TMP15]], align 8
+; CHECK-NEXT: [[TMP25:%.*]] = load i64, ptr [[TMP17]], align 8
+; CHECK-NEXT: [[TMP26:%.*]] = insertelement <8 x i64> poison, i64 [[TMP18]], i64 0
+; CHECK-NEXT: [[TMP27:%.*]] = insertelement <8 x i64> [[TMP26]], i64 [[TMP19]], i64 1
+; CHECK-NEXT: [[TMP28:%.*]] = insertelement <8 x i64> [[TMP27]], i64 [[TMP20]], i64 2
+; CHECK-NEXT: [[TMP29:%.*]] = insertelement <8 x i64> [[TMP28]], i64 [[TMP21]], i64 3
+; CHECK-NEXT: [[TMP30:%.*]] = insertelement <8 x i64> [[TMP29]], i64 [[TMP22]], i64 4
+; CHECK-NEXT: [[TMP31:%.*]] = insertelement <8 x i64> [[TMP30]], i64 [[TMP23]], i64 5
+; CHECK-NEXT: [[TMP32:%.*]] = insertelement <8 x i64> [[TMP31]], i64 [[TMP24]], i64 6
+; CHECK-NEXT: [[TMP33:%.*]] = insertelement <8 x i64> [[TMP32]], i64 [[TMP25]], i64 7
+; CHECK-NEXT: [[TMP34:%.*]] = add nsw <8 x i64> [[TMP33]], splat (i64 42)
+; CHECK-NEXT: [[TMP35:%.*]] = getelementptr inbounds i64, ptr [[B]], i64 [[INDEX]]
+; CHECK-NEXT: store <8 x i64> [[TMP34]], ptr [[TMP35]], align 8
; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 8
-; CHECK-NEXT: [[TMP6:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1000
-; CHECK-NEXT: br i1 [[TMP6]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP5:![0-9]+]]
+; CHECK-NEXT: [[VEC_IND_NEXT]] = add nsw <8 x i64> [[VEC_IND]], splat (i64 8)
+; CHECK-NEXT: [[TMP36:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1000
+; CHECK-NEXT: br i1 [[TMP36]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP5:![0-9]+]]
; CHECK: middle.block:
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
>From 3e6dff1016e04af0291ad5b2b852cdf740f7dd26 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Sun, 23 Aug 2026 22:09:15 +0100
Subject: [PATCH 3/3] [SCEV] Introduce willNotWrapByExtend, strip hack
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 14 ++--
llvm/lib/Analysis/ScalarEvolution.cpp | 83 ++++++++++++--------
2 files changed, 55 insertions(+), 42 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 8c8b983caf870..d17c21ea3401e 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -704,14 +704,12 @@ class ScalarEvolution {
/// scAddRecExpr. The result will be cached in HasRecMap.
LLVM_ABI bool containsAddRecurrence(const SCEV *S);
- /// 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);
+ /// 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);
/// 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 239bc0512a50d..175e0f7803963 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -2337,54 +2337,66 @@ static bool CollectAddOperandsWithScales(SmallDenseMap<SCEVUse, APInt, 16> &M,
return Interesting;
}
-bool ScalarEvolution::willNotOverflow(unsigned Opcode, bool Signed,
+/// Checks that the application of binary function \p OperationFn to \p LHS and
+/// \p RHS does not wrap in the unsigned or signed (if \p Signed) manner.
+static bool
+willNotWrapByExtend(function_ref<const SCEV *(SCEVUse, SCEVUse)> OperationFn,
+ const SCEV *LHS, const SCEV *RHS, bool Signed,
+ ScalarEvolution *SE) {
+ auto *NarrowTy = LHS->getType();
+ auto *WideTy = IntegerType::get(NarrowTy->getContext(),
+ SE->getTypeSizeInBits(NarrowTy) * 2);
+
+ using ExtFnTy = const SCEV *(ScalarEvolution::*)(SCEVUse, Type *, unsigned);
+ std::function<const SCEV *(SCEVUse, Type *)> ExtensionFn =
+ Signed
+ ? bind_front(
+ bind_back<ExtFnTy>(&ScalarEvolution::getSignExtendExpr, 0), SE)
+ : bind_front(
+ bind_back<ExtFnTy>(&ScalarEvolution::getZeroExtendExpr, 0), SE);
+
+ // Check ExtensionFn(OperationFn(LHS, RHS)) == OperationFn(ExtensionFn(LHS),
+ // ExtensionFn(RHS))
+ const SCEV *A = ExtensionFn(OperationFn(LHS, RHS), WideTy);
+ const SCEV *LHSB = ExtensionFn(LHS, WideTy);
+ const SCEV *RHSB = ExtensionFn(RHS, WideTy);
+ const SCEV *B = OperationFn(LHSB, RHSB);
+ return A == B;
+}
+
+bool ScalarEvolution::willNotOverflow(Instruction::BinaryOps BinOp, bool Signed,
const SCEV *LHS, const SCEV *RHS,
- const Instruction *CtxI, const Loop *L) {
+ const Instruction *CtxI) {
using OpFnTy = const SCEV *(ScalarEvolution::*)(SCEVUse, SCEVUse,
SCEV::NoWrapFlags, unsigned);
- std::function<const SCEV *(SCEVUse, SCEVUse, SCEV::NoWrapFlags, unsigned)>
- OperationFn;
- switch (Opcode) {
+ std::function<const SCEV *(SCEVUse, SCEVUse)> OperationFn;
+ switch (BinOp) {
default:
llvm_unreachable("Unsupported binary op");
case Instruction::Add:
- OperationFn = bind_front<OpFnTy>(&ScalarEvolution::getAddExpr, this);
+ OperationFn = bind_front(
+ bind_back<OpFnTy>(&ScalarEvolution::getAddExpr, SCEV::FlagAnyWrap, 0),
+ this);
break;
case Instruction::Sub:
- OperationFn = bind_front<OpFnTy>(&ScalarEvolution::getMinusSCEV, this);
+ OperationFn = bind_front(
+ bind_back<OpFnTy>(&ScalarEvolution::getMinusSCEV, SCEV::FlagAnyWrap, 0),
+ this);
break;
case Instruction::Mul:
- 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); };
+ OperationFn = bind_front(
+ bind_back<OpFnTy>(&ScalarEvolution::getMulExpr, SCEV::FlagAnyWrap, 0),
+ this);
break;
}
- 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 =
- 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)
+ if (willNotWrapByExtend(OperationFn, LHS, RHS, Signed, this))
return true;
// Can we use context to prove the fact we need?
if (!CtxI)
return false;
// TODO: Support mul.
- if (Opcode == Instruction::Mul || Opcode == Instruction::PHI)
+ if (BinOp == Instruction::Mul)
return false;
auto *RHSC = dyn_cast<SCEVConstant>(RHS);
// TODO: Lift this limitation.
@@ -2392,7 +2404,7 @@ bool ScalarEvolution::willNotOverflow(unsigned Opcode, bool Signed,
return false;
APInt C = RHSC->getAPInt();
unsigned NumBits = C.getBitWidth();
- bool IsSub = (Opcode == Instruction::Sub);
+ bool IsSub = (BinOp == Instruction::Sub);
bool IsNegativeConst = (Signed && C.isNegative());
// Compute the direction and magnitude by which we need to check overflow.
bool OverflowDown = IsSub ^ IsNegativeConst;
@@ -3535,9 +3547,12 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
const APInt &StepInt = Step->getAPInt();
const APInt &DivInt = RHSC->getAPInt();
bool NoWrap = !StepInt.urem(DivInt) &&
- willNotOverflow(Instruction::PHI, /*Signed=*/false,
- AR->getStart(), Step, /*CtxI=*/nullptr,
- AR->getLoop());
+ willNotWrapByExtend(
+ [&](SCEVUse Start, SCEVUse Step) {
+ return getAddRecExpr(Start, Step, AR->getLoop(),
+ SCEV::FlagAnyWrap);
+ },
+ AR->getStart(), Step, /*Signed=*/false, this);
if (!StepInt.urem(DivInt) && NoWrap) {
SmallVector<SCEVUse, 4> Operands;
for (const SCEV *Op : AR->operands())
More information about the llvm-commits
mailing list