[llvm] [SCEV] Updates for getUDivExactExpr() to improve handling of MulExpr numerators/denominators (PR #195704)
Ryan Buchner via llvm-commits
llvm-commits at lists.llvm.org
Tue May 5 10:49:17 PDT 2026
https://github.com/bababuck updated https://github.com/llvm/llvm-project/pull/195704
>From 856962bb6d969c84acb99a23bcddc885babe59a7 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Thu, 30 Apr 2026 15:54:22 -0700
Subject: [PATCH 1/6] [SCEV] Preserve NUW flags of MulExpr numerators in
getUDivExactExpr()
---
llvm/lib/Analysis/ScalarEvolution.cpp | 6 +-
.../Analysis/ScalarEvolutionTest.cpp | 72 +++++++++++++++++++
2 files changed, 76 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 676292ebe0346..9e280209813a0 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3755,7 +3755,9 @@ const SCEV *ScalarEvolution::getUDivExactExpr(SCEVUse LHS, SCEVUse RHS) {
if (const auto *LHSCst = dyn_cast<SCEVConstant>(Mul->getOperand(0))) {
if (LHSCst == RHSCst) {
SmallVector<SCEVUse, 2> Operands(drop_begin(Mul->operands()));
- return getMulExpr(Operands);
+ return getMulExpr(
+ Operands, LHSCst->isZero() ? SCEVNoWrapFlags::FlagAnyWrap
+ : Mul->getNoWrapFlags() & SCEV::FlagNUW);
}
// We can't just assume that LHSCst divides RHSCst cleanly, it could be
@@ -3770,7 +3772,7 @@ const SCEV *ScalarEvolution::getUDivExactExpr(SCEVUse LHS, SCEVUse RHS) {
SmallVector<SCEVUse, 2> Operands;
Operands.push_back(LHSCst);
append_range(Operands, Mul->operands().drop_front());
- LHS = getMulExpr(Operands);
+ LHS = getMulExpr(Operands, Mul->getNoWrapFlags() & SCEV::FlagNUW);
RHS = RHSCst;
Mul = dyn_cast<SCEVMulExpr>(LHS);
if (!Mul)
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index 8504534693281..d9d38e31b8570 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1579,6 +1579,78 @@ TEST_F(ScalarEvolutionsTest, SCEVUDivFloorCeiling) {
});
}
+TEST_F(ScalarEvolutionsTest, SCEVUDivExactPreserveNUW) {
+ LLVMContext C;
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M =
+ parseAssemblyString("define void @foo(i32 %a, i32 %b) {"
+ " ret void"
+ "}",
+ Err, C);
+
+ ASSERT_TRUE(M && "Could not parse module?");
+ ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
+
+ runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ const SCEV *A = SE.getSCEV(getArgByName(F, "a"));
+ const SCEV *B = SE.getSCEV(getArgByName(F, "b"));
+ const SCEV *Two = SE.getConstant(A->getType(), 2);
+ const SCEV *Three = SE.getConstant(A->getType(), 3);
+ const SCEV *Four = SE.getConstant(A->getType(), 4);
+ const SCEV *Six = SE.getConstant(A->getType(), 6);
+ const SCEV *Nine = SE.getConstant(A->getType(), 9);
+ const SCEV *Twelve = SE.getConstant(A->getType(), 12);
+ const SCEV *TwentyFour = SE.getConstant(A->getType(), 24);
+
+ SmallVector<SCEVUse, 3> LHSMulOps = {A, B, Twelve};
+ const SCEVMulExpr *LHSMulNUW =
+ cast<const SCEVMulExpr>(SE.getMulExpr(LHSMulOps, SCEV::FlagNUW));
+ // UDivExactExpr where the divisor is a constant that appears in dividend
+ // MulExpr
+ const SCEVMulExpr *EqualConsts =
+ cast<const SCEVMulExpr>(SE.getUDivExactExpr(LHSMulNUW, Twelve));
+ SmallVector<SCEVUse, 2> EqualConstsResOps = {A, B};
+ EXPECT_TRUE(EqualConsts->hasNoUnsignedWrap());
+ EXPECT_EQ(EqualConsts, SE.getMulExpr(EqualConstsResOps, SCEV::FlagNUW));
+
+ // UDivExactExpr where the divisor is a constant multiple of the dividend
+ // constant
+ const SCEV *DividendSmaller = SE.getUDivExactExpr(LHSMulNUW, TwentyFour);
+ SmallVector<SCEVUse, 2> DividendSmallerResOps = {A, B};
+ EXPECT_TRUE(cast<const SCEVMulExpr>(DividendSmaller->operands().front())
+ ->hasNoUnsignedWrap());
+ EXPECT_EQ(DividendSmaller,
+ SE.getUDivExpr(
+ SE.getMulExpr(DividendSmallerResOps, SCEV::FlagNUW), Two));
+
+ // UDivExactExpr where the dividend constant is a constant multiple of the
+ // divisor
+ const SCEVMulExpr *DivisorSmaller =
+ cast<const SCEVMulExpr>(SE.getUDivExactExpr(LHSMulNUW, Six));
+ SmallVector<SCEVUse, 2> DivisorSmallerResOps = {A, B, Two};
+ EXPECT_TRUE(DivisorSmaller->hasNoUnsignedWrap());
+ EXPECT_EQ(DivisorSmaller,
+ SE.getMulExpr(DivisorSmallerResOps, SCEV::FlagNUW));
+
+ // UDivExactExpr where the divisor and dividend constant,are not factors of
+ // each other
+ const SCEV *NoFactor = SE.getUDivExactExpr(LHSMulNUW, Nine);
+ SmallVector<SCEVUse, 2> NoFactorResOps = {A, B, Four};
+ EXPECT_TRUE(cast<const SCEVMulExpr>(NoFactor->operands().front())
+ ->hasNoUnsignedWrap());
+ EXPECT_EQ(
+ NoFactor,
+ SE.getUDivExpr(SE.getMulExpr(NoFactorResOps, SCEV::FlagNUW), Three));
+
+ // UDivExactExpr where the divisor is contained in the divident MulExpr
+ const SCEVMulExpr *NonConstFactor =
+ cast<const SCEVMulExpr>(SE.getUDivExactExpr(LHSMulNUW, B));
+ SmallVector<SCEVUse, 2> NonConstFactorResOps = {A, Twelve};
+ EXPECT_FALSE(NonConstFactor->hasNoUnsignedWrap());
+ EXPECT_EQ(NonConstFactor, SE.getMulExpr(NonConstFactorResOps));
+ });
+}
+
TEST_F(ScalarEvolutionsTest, CheckGetPowerOfTwo) {
Module M("CheckGetPowerOfTwo", Context);
FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), {}, false);
>From 23e2f52bb8ed1e3c0b8102774b052c55e7f465de Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Thu, 30 Apr 2026 15:54:27 -0700
Subject: [PATCH 2/6] [SCEV] Improve getUDivExactExpr() for cases where the
denominator is a MulExpr
---
llvm/lib/Analysis/ScalarEvolution.cpp | 33 ++++
.../Analysis/ScalarEvolutionTest.cpp | 154 ++++++++++++++++++
2 files changed, 187 insertions(+)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 9e280209813a0..56ac8b8451262 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3745,6 +3745,39 @@ const SCEV *ScalarEvolution::getUDivExactExpr(SCEVUse LHS, SCEVUse RHS) {
// just deal with u/exact (multiply, constant). See SCEVDivision towards the
// end of this file for inspiration.
+ if (LHS == RHS)
+ return getConstant(LHS->getType(), 1);
+
+ if (const SCEVMulExpr *RHSMul = dyn_cast<SCEVMulExpr>(RHS);
+ RHSMul && RHSMul->hasNoUnsignedWrap() &&
+ isa<SCEVMulExpr, SCEVConstant, SCEVUnknown>(LHS)) {
+ const SCEV *CurrSCEV = LHS;
+ SmallVector<SCEVUse, 2> Operands;
+ bool OnlyNonZero = true;
+ for (SCEVUse Op : RHSMul->operands()) {
+ if (!OnlyNonZero) {
+ Operands.push_back(Op);
+ continue;
+ }
+ const SCEV *NewSCEV = getUDivExactExpr(CurrSCEV, Op);
+ if (isa<SCEVUDivExpr>(NewSCEV)) {
+ Operands.push_back(Op);
+ continue;
+ }
+ if (!isa<SCEVConstant>(Op) || Op->isZero())
+ OnlyNonZero = false;
+ CurrSCEV = NewSCEV;
+ }
+ if (Operands.size())
+ return getUDivExpr(
+ CurrSCEV,
+ getMulExpr(Operands, OnlyNonZero
+ ? RHSMul->getNoWrapFlags() & SCEV::FlagNUW
+ : SCEVNoWrapFlags::FlagAnyWrap));
+ else
+ return CurrSCEV;
+ }
+
const SCEVMulExpr *Mul = dyn_cast<SCEVMulExpr>(LHS);
if (!Mul || !Mul->hasNoUnsignedWrap())
return getUDivExpr(LHS, RHS);
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index d9d38e31b8570..a89880e743b1f 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1651,6 +1651,160 @@ TEST_F(ScalarEvolutionsTest, SCEVUDivExactPreserveNUW) {
});
}
+TEST_F(ScalarEvolutionsTest, SCEVUDivExactNUWMulDivisor) {
+ LLVMContext C;
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M = parseAssemblyString(
+ "define void @foo(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) {"
+ " ret void"
+ "}",
+ Err, C);
+
+ ASSERT_TRUE(M && "Could not parse module?");
+ ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
+
+ runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ const SCEV *A = SE.getSCEV(getArgByName(F, "a"));
+ const SCEV *B = SE.getSCEV(getArgByName(F, "b"));
+ const SCEV *C = SE.getSCEV(getArgByName(F, "c"));
+ const SCEV *D = SE.getSCEV(getArgByName(F, "d"));
+ const SCEV *E = SE.getSCEV(getArgByName(F, "e"));
+ const SCEV *One = SE.getConstant(A->getType(), 1);
+ const SCEV *Two = SE.getConstant(A->getType(), 2);
+ const SCEV *Three = SE.getConstant(A->getType(), 3);
+ const SCEV *Six = SE.getConstant(A->getType(), 6);
+
+ SmallVector<SCEVUse, 2> BDOps = {B, D};
+ const SCEV *BD = SE.getMulExpr(BDOps, SCEV::FlagNUW);
+
+ SmallVector<SCEVUse, 2> CEOps = {C, E};
+ const SCEV *CE = SE.getMulExpr(CEOps, SCEV::FlagNUW);
+
+ SmallVector<SCEVUse, 2> CDEOps = {C, D, E};
+ const SCEV *CDE = SE.getMulExpr(CDEOps, SCEV::FlagNUW);
+
+ SmallVector<SCEVUse, 3> ABCOps = {A, B, C};
+ const SCEV *ABC = SE.getMulExpr(ABCOps, SCEV::FlagNUW);
+
+ SmallVector<SCEVUse, 3> ABEOps = {A, B, E};
+ const SCEV *ABE = SE.getMulExpr(ABEOps, SCEV::FlagNUW);
+
+ SmallVector<SCEVUse, 3> BCEOps = {B, C, E};
+ const SCEV *BCE = SE.getMulExpr(BCEOps, SCEV::FlagNUW);
+
+ SmallVector<SCEVUse, 2> A3Ops = {A, Three};
+ const SCEV *A3 = SE.getMulExpr(A3Ops, SCEV::FlagNUW);
+
+ SmallVector<SCEVUse, 2> B3Ops = {B, Three};
+ const SCEV *B3 = SE.getMulExpr(B3Ops, SCEV::FlagNUW);
+
+ SmallVector<SCEVUse, 2> D6Ops = {D, Six};
+ const SCEV *D6 = SE.getMulExpr(D6Ops, SCEV::FlagNUW);
+
+ SmallVector<SCEVUse, 4> ABCDOps = {A, B, C, D};
+ const SCEV *ABCDNoNUW = SE.getMulExpr(ABCDOps);
+
+ SmallVector<SCEVUse, 4> ABCE3Ops = {A, B, C, E, Three};
+ const SCEV *ABCE3 = SE.getMulExpr(ABCE3Ops, SCEV::FlagNUW);
+
+ // B / (B * D)
+ EXPECT_EQ(SE.getUDivExactExpr(B, BD), SE.getUDivExpr(One, D));
+
+ // A / (A * B * C)
+ {
+ auto *A_ABC = SE.getUDivExactExpr(A, ABC);
+ auto *Denom = dyn_cast<const SCEVMulExpr>(A_ABC->operands().back());
+ EXPECT_TRUE(Denom);
+ EXPECT_FALSE(!Denom || Denom->hasNoUnsignedWrap());
+ SmallVector<SCEVUse, 2> BCOps = {B, C};
+ const SCEV *BCNoNUW = SE.getMulExpr(BCOps);
+ EXPECT_EQ(A_ABC, SE.getUDivExpr(One, BCNoNUW));
+ }
+
+ // B / (A * B * C)
+ {
+ auto *B_ABC = SE.getUDivExactExpr(B, ABC);
+ auto *Denom = dyn_cast<const SCEVMulExpr>(B_ABC->operands().back());
+ EXPECT_TRUE(Denom);
+ EXPECT_FALSE(!Denom || Denom->hasNoUnsignedWrap());
+ SmallVector<SCEVUse, 2> ACOps = {A, C};
+ const SCEV *ACNoNUW = SE.getMulExpr(ACOps);
+ EXPECT_EQ(B_ABC, SE.getUDivExpr(One, ACNoNUW));
+ }
+
+ // C / (A * B * C)
+ {
+ auto *C_ABC = SE.getUDivExactExpr(C, ABC);
+ auto *Denom = dyn_cast<const SCEVMulExpr>(C_ABC->operands().back());
+ EXPECT_TRUE(Denom);
+ EXPECT_FALSE(!Denom || Denom->hasNoUnsignedWrap());
+ SmallVector<SCEVUse, 2> ABOps = {A, B};
+ const SCEV *ABNoNUW = SE.getMulExpr(ABOps);
+ EXPECT_EQ(C_ABC, SE.getUDivExpr(One, ABNoNUW));
+ }
+
+ // (C * E) / (C * D * E)
+ {
+ auto *CE_CDE = SE.getUDivExactExpr(CE, CDE);
+ auto *Denom = dyn_cast<const SCEVMulExpr>(CE_CDE->operands().back());
+ EXPECT_TRUE(Denom);
+ EXPECT_FALSE(!Denom || Denom->hasNoUnsignedWrap());
+ SmallVector<SCEVUse, 2> DEOps = {D, E};
+ const SCEV *DENoNUW = SE.getMulExpr(DEOps);
+ SmallVector<SCEVUse, 2> CDOps = {C, D};
+ const SCEV *CDNoNUW = SE.getMulExpr(CDOps);
+ EXPECT_TRUE(CE_CDE == SE.getUDivExpr(E, DENoNUW) ||
+ CE_CDE == SE.getUDivExpr(C, CDNoNUW));
+ }
+
+ // (B * D) / (A * B * E)
+ {
+ auto *BD_ABE = SE.getUDivExactExpr(BD, ABE);
+ auto *Denom = dyn_cast<const SCEVMulExpr>(BD_ABE->operands().back());
+ EXPECT_TRUE(Denom);
+ EXPECT_FALSE(!Denom || Denom->hasNoUnsignedWrap());
+ SmallVector<SCEVUse, 2> AEOps = {A, E};
+ const SCEV *AENoNUW = SE.getMulExpr(AEOps);
+ EXPECT_EQ(BD_ABE, SE.getUDivExpr(D, AENoNUW));
+ }
+
+ // (3 * A) / (3 * B)
+ EXPECT_EQ(SE.getUDivExactExpr(A3, B3), SE.getUDivExpr(A, B));
+
+ // (B * D) / (B * D)
+ EXPECT_EQ(SE.getUDivExactExpr(BD, BD), One);
+
+ // (3 * A) / (3 * A)
+ EXPECT_EQ(SE.getUDivExactExpr(A3, A3), One);
+
+ // (D) / (B * C * E)
+ EXPECT_EQ(SE.getUDivExactExpr(D, BCE), SE.getUDivExpr(D, BCE));
+
+ // (D * 6) / (3 * A * B * C * E)
+ {
+ auto *D6_ABCE3 = SE.getUDivExactExpr(D6, ABCE3);
+ EXPECT_TRUE(cast<const SCEVMulExpr>(D6_ABCE3->operands().front())
+ ->hasNoUnsignedWrap());
+ EXPECT_TRUE(cast<const SCEVMulExpr>(D6_ABCE3->operands().back())
+ ->hasNoUnsignedWrap());
+ auto *Num = dyn_cast<const SCEVMulExpr>(D6_ABCE3->operands().front());
+ auto *Denom = dyn_cast<const SCEVMulExpr>(D6_ABCE3->operands().back());
+ EXPECT_TRUE(Num);
+ EXPECT_TRUE(Num && Num->hasNoUnsignedWrap());
+ EXPECT_TRUE(Denom);
+ EXPECT_TRUE(Denom && Denom->hasNoUnsignedWrap());
+ SmallVector<SCEVUse, 4> ABCEOps = {A, B, C, E};
+ const SCEV *ABCE = SE.getMulExpr(ABCEOps, SCEV::FlagNUW);
+ SmallVector<SCEVUse, 2> D2Ops = {D, Two};
+ const SCEV *D2 = SE.getMulExpr(D2Ops, SCEV::FlagNUW);
+ EXPECT_EQ(D6_ABCE3, SE.getUDivExpr(D2, ABCE));
+ // No NUW
+ EXPECT_EQ(SE.getUDivExactExpr(A, ABCDNoNUW),
+ SE.getUDivExpr(A, ABCDNoNUW));
+ }
+ });
+}
+
TEST_F(ScalarEvolutionsTest, CheckGetPowerOfTwo) {
Module M("CheckGetPowerOfTwo", Context);
FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), {}, false);
>From 9547752db8940cb2dde47018e5e799da1851c72d Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 4 May 2026 16:17:45 -0700
Subject: [PATCH 3/6] Remove isZero() check from constants in MulExpr's
MulExpr cannot have 0 constants by construction.
---
llvm/lib/Analysis/ScalarEvolution.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 56ac8b8451262..395ccf0010bbe 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3764,7 +3764,7 @@ const SCEV *ScalarEvolution::getUDivExactExpr(SCEVUse LHS, SCEVUse RHS) {
Operands.push_back(Op);
continue;
}
- if (!isa<SCEVConstant>(Op) || Op->isZero())
+ if (!isa<SCEVConstant>(Op)) // Constant is guaranteed non-zero is MulExpr
OnlyNonZero = false;
CurrSCEV = NewSCEV;
}
@@ -3788,9 +3788,7 @@ const SCEV *ScalarEvolution::getUDivExactExpr(SCEVUse LHS, SCEVUse RHS) {
if (const auto *LHSCst = dyn_cast<SCEVConstant>(Mul->getOperand(0))) {
if (LHSCst == RHSCst) {
SmallVector<SCEVUse, 2> Operands(drop_begin(Mul->operands()));
- return getMulExpr(
- Operands, LHSCst->isZero() ? SCEVNoWrapFlags::FlagAnyWrap
- : Mul->getNoWrapFlags() & SCEV::FlagNUW);
+ return getMulExpr(Operands, Mul->getNoWrapFlags() & SCEV::FlagNUW);
}
// We can't just assume that LHSCst divides RHSCst cleanly, it could be
>From 93b6da33d4ac20fb7ad41744e35aba2dfffd5707 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 4 May 2026 17:26:26 -0700
Subject: [PATCH 4/6] [SCEV] Simplify X udiv X -> 1 in getUDivExpr()
---
llvm/lib/Analysis/ScalarEvolution.cpp | 8 ++++---
.../Analysis/ScalarEvolutionTest.cpp | 24 +++++++++++++++++++
2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 395ccf0010bbe..b053e3122cf10 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3561,6 +3561,11 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
if (match(LHS, m_scev_Zero()))
return LHS;
+ // X udiv X == 1
+ if (LHS == RHS)
+ return getConstant(LHS->getType(), 1);
+
+
if (const SCEVConstant *RHSC = dyn_cast<SCEVConstant>(RHS)) {
if (RHSC->getValue()->isOne())
return LHS; // X udiv 1 --> x
@@ -3745,9 +3750,6 @@ const SCEV *ScalarEvolution::getUDivExactExpr(SCEVUse LHS, SCEVUse RHS) {
// just deal with u/exact (multiply, constant). See SCEVDivision towards the
// end of this file for inspiration.
- if (LHS == RHS)
- return getConstant(LHS->getType(), 1);
-
if (const SCEVMulExpr *RHSMul = dyn_cast<SCEVMulExpr>(RHS);
RHSMul && RHSMul->hasNoUnsignedWrap() &&
isa<SCEVMulExpr, SCEVConstant, SCEVUnknown>(LHS)) {
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index a89880e743b1f..2c2e2a0c03a69 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1579,6 +1579,30 @@ TEST_F(ScalarEvolutionsTest, SCEVUDivFloorCeiling) {
});
}
+TEST_F(ScalarEvolutionsTest, SCEVUDivEqual) {
+ LLVMContext C;
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M =
+ parseAssemblyString("define void @foo(i32 %a) {"
+ " ret void"
+ "}",
+ Err, C);
+
+ ASSERT_TRUE(M && "Could not parse module?");
+ ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
+
+ runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ const SCEV *A = SE.getSCEV(getArgByName(F, "a"));
+ const SCEV *One = SE.getConstant(A->getType(), 1);
+ const SCEV *Two = SE.getConstant(A->getType(), 2);
+ EXPECT_EQ(One, SE.getUDivExpr(A, A));
+ EXPECT_EQ(One, SE.getUDivExpr(Two, Two));
+ EXPECT_EQ(One, SE.getUDivExactExpr(A, A));
+ EXPECT_EQ(One, SE.getUDivExactExpr(Two, Two));
+ });
+}
+
+
TEST_F(ScalarEvolutionsTest, SCEVUDivExactPreserveNUW) {
LLVMContext C;
SMDiagnostic Err;
>From d03ffa51a12239874c79209ba4e3171bf73d7a29 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 5 May 2026 10:38:57 -0700
Subject: [PATCH 5/6] [SCEV] Refactor with NUW propogation and other fixes
---
llvm/lib/Analysis/ScalarEvolution.cpp | 108 +++++++-----------
.../Analysis/ScalarEvolutionTest.cpp | 45 +++-----
2 files changed, 59 insertions(+), 94 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index b053e3122cf10..52e08a25f38b8 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3750,80 +3750,56 @@ const SCEV *ScalarEvolution::getUDivExactExpr(SCEVUse LHS, SCEVUse RHS) {
// just deal with u/exact (multiply, constant). See SCEVDivision towards the
// end of this file for inspiration.
+ SmallVector<SCEVUse> RHSOperands, LHSOperands;
if (const SCEVMulExpr *RHSMul = dyn_cast<SCEVMulExpr>(RHS);
- RHSMul && RHSMul->hasNoUnsignedWrap() &&
- isa<SCEVMulExpr, SCEVConstant, SCEVUnknown>(LHS)) {
- const SCEV *CurrSCEV = LHS;
- SmallVector<SCEVUse, 2> Operands;
- bool OnlyNonZero = true;
- for (SCEVUse Op : RHSMul->operands()) {
- if (!OnlyNonZero) {
- Operands.push_back(Op);
- continue;
- }
- const SCEV *NewSCEV = getUDivExactExpr(CurrSCEV, Op);
- if (isa<SCEVUDivExpr>(NewSCEV)) {
- Operands.push_back(Op);
- continue;
- }
- if (!isa<SCEVConstant>(Op)) // Constant is guaranteed non-zero is MulExpr
- OnlyNonZero = false;
- CurrSCEV = NewSCEV;
- }
- if (Operands.size())
- return getUDivExpr(
- CurrSCEV,
- getMulExpr(Operands, OnlyNonZero
- ? RHSMul->getNoWrapFlags() & SCEV::FlagNUW
- : SCEVNoWrapFlags::FlagAnyWrap));
- else
- return CurrSCEV;
- }
-
- const SCEVMulExpr *Mul = dyn_cast<SCEVMulExpr>(LHS);
- if (!Mul || !Mul->hasNoUnsignedWrap())
- return getUDivExpr(LHS, RHS);
+ RHSMul && RHSMul->hasNoUnsignedWrap())
+ RHSOperands = to_vector(RHSMul->operands());
+ else
+ RHSOperands.push_back(RHS);
+ if (const SCEVMulExpr *LHSMul = dyn_cast<SCEVMulExpr>(LHS);
+ LHSMul && LHSMul->hasNoUnsignedWrap())
+ LHSOperands = to_vector(LHSMul->operands());
+ else
+ LHSOperands.push_back(LHS);
- if (const SCEVConstant *RHSCst = dyn_cast<SCEVConstant>(RHS)) {
- // If the mulexpr multiplies by a constant, then that constant must be the
- // first element of the mulexpr.
- if (const auto *LHSCst = dyn_cast<SCEVConstant>(Mul->getOperand(0))) {
- if (LHSCst == RHSCst) {
- SmallVector<SCEVUse, 2> Operands(drop_begin(Mul->operands()));
- return getMulExpr(Operands, Mul->getNoWrapFlags() & SCEV::FlagNUW);
- }
+ for (unsigned RHSIdx = 0; RHSIdx < RHSOperands.size(); ++RHSIdx) {
+ SCEVUse RHSOp = RHSOperands[RHSIdx];
+ if (RHSOp->isOne())
+ continue;
+ if (const SCEVConstant *RHSCst = dyn_cast<SCEVConstant>(RHSOp)) {
+ // If the mulexpr multiplies by a constant, then that constant must be the
+ // first element of the mulexpr.
+ if (const auto *LHSCst = dyn_cast<SCEVConstant>(LHSOperands[0])) {
+ if (LHSCst == RHSCst) {
+ RHSOperands[RHSIdx] = getConstant(RHSOp->getType(), 1);
+ LHSOperands[0] = getConstant(RHSOp->getType(), 1);
+ continue;
+ }
- // We can't just assume that LHSCst divides RHSCst cleanly, it could be
- // that there's a factor provided by one of the other terms. We need to
- // check.
- APInt Factor = gcd(LHSCst, RHSCst);
- if (!Factor.isIntN(1)) {
- LHSCst =
- cast<SCEVConstant>(getConstant(LHSCst->getAPInt().udiv(Factor)));
- RHSCst =
- cast<SCEVConstant>(getConstant(RHSCst->getAPInt().udiv(Factor)));
- SmallVector<SCEVUse, 2> Operands;
- Operands.push_back(LHSCst);
- append_range(Operands, Mul->operands().drop_front());
- LHS = getMulExpr(Operands, Mul->getNoWrapFlags() & SCEV::FlagNUW);
- RHS = RHSCst;
- Mul = dyn_cast<SCEVMulExpr>(LHS);
- if (!Mul)
- return getUDivExactExpr(LHS, RHS);
+ // We can't just assume that LHSCst divides RHSCst cleanly, it could be
+ // that there's a factor provided by one of the other terms. We need to
+ // check.
+ APInt Factor = gcd(LHSCst, RHSCst);
+ if (!Factor.isIntN(1)) {
+ LHSOperands[0] =
+ cast<SCEVConstant>(getConstant(LHSCst->getAPInt().udiv(Factor)));
+ RHSOperands[RHSIdx] =
+ cast<SCEVConstant>(getConstant(RHSCst->getAPInt().udiv(Factor)));
+ }
+ continue;
}
}
- }
- for (int i = 0, e = Mul->getNumOperands(); i != e; ++i) {
- if (Mul->getOperand(i) == RHS) {
- SmallVector<SCEVUse, 2> Operands;
- append_range(Operands, Mul->operands().take_front(i));
- append_range(Operands, Mul->operands().drop_front(i + 1));
- return getMulExpr(Operands);
+ for (unsigned LHSIdx = 0; LHSIdx < LHSOperands.size(); ++LHSIdx) {
+ if (LHSOperands[LHSIdx] == RHSOp) {
+ RHSOperands[RHSIdx] = getConstant(RHSOp->getType(), 1);
+ LHSOperands[LHSIdx] = getConstant(LHSOperands[LHSIdx]->getType(), 1);
+ break;
+ }
}
}
-
- return getUDivExpr(LHS, RHS);
+ return getUDivExpr(getMulExpr(LHSOperands, SCEV::FlagNUW),
+ getMulExpr(RHSOperands, SCEV::FlagNUW));
}
/// Get an add recurrence expression for the specified loop. Simplify the
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index 2c2e2a0c03a69..c92620904f7e3 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1670,7 +1670,7 @@ TEST_F(ScalarEvolutionsTest, SCEVUDivExactPreserveNUW) {
const SCEVMulExpr *NonConstFactor =
cast<const SCEVMulExpr>(SE.getUDivExactExpr(LHSMulNUW, B));
SmallVector<SCEVUse, 2> NonConstFactorResOps = {A, Twelve};
- EXPECT_FALSE(NonConstFactor->hasNoUnsignedWrap());
+ EXPECT_TRUE(NonConstFactor->hasNoUnsignedWrap());
EXPECT_EQ(NonConstFactor, SE.getMulExpr(NonConstFactorResOps));
});
}
@@ -1739,10 +1739,10 @@ TEST_F(ScalarEvolutionsTest, SCEVUDivExactNUWMulDivisor) {
auto *A_ABC = SE.getUDivExactExpr(A, ABC);
auto *Denom = dyn_cast<const SCEVMulExpr>(A_ABC->operands().back());
EXPECT_TRUE(Denom);
- EXPECT_FALSE(!Denom || Denom->hasNoUnsignedWrap());
+ EXPECT_TRUE(Denom && Denom->hasNoUnsignedWrap());
SmallVector<SCEVUse, 2> BCOps = {B, C};
- const SCEV *BCNoNUW = SE.getMulExpr(BCOps);
- EXPECT_EQ(A_ABC, SE.getUDivExpr(One, BCNoNUW));
+ const SCEV *BC = SE.getMulExpr(BCOps, SCEV::FlagNUW);
+ EXPECT_EQ(A_ABC, SE.getUDivExpr(One, BC));
}
// B / (A * B * C)
@@ -1750,10 +1750,10 @@ TEST_F(ScalarEvolutionsTest, SCEVUDivExactNUWMulDivisor) {
auto *B_ABC = SE.getUDivExactExpr(B, ABC);
auto *Denom = dyn_cast<const SCEVMulExpr>(B_ABC->operands().back());
EXPECT_TRUE(Denom);
- EXPECT_FALSE(!Denom || Denom->hasNoUnsignedWrap());
+ EXPECT_TRUE(Denom && Denom->hasNoUnsignedWrap());
SmallVector<SCEVUse, 2> ACOps = {A, C};
- const SCEV *ACNoNUW = SE.getMulExpr(ACOps);
- EXPECT_EQ(B_ABC, SE.getUDivExpr(One, ACNoNUW));
+ const SCEV *AC = SE.getMulExpr(ACOps, SCEV::FlagNUW);
+ EXPECT_EQ(B_ABC, SE.getUDivExpr(One, AC));
}
// C / (A * B * C)
@@ -1761,35 +1761,24 @@ TEST_F(ScalarEvolutionsTest, SCEVUDivExactNUWMulDivisor) {
auto *C_ABC = SE.getUDivExactExpr(C, ABC);
auto *Denom = dyn_cast<const SCEVMulExpr>(C_ABC->operands().back());
EXPECT_TRUE(Denom);
- EXPECT_FALSE(!Denom || Denom->hasNoUnsignedWrap());
+ EXPECT_TRUE(Denom && Denom->hasNoUnsignedWrap());
SmallVector<SCEVUse, 2> ABOps = {A, B};
- const SCEV *ABNoNUW = SE.getMulExpr(ABOps);
- EXPECT_EQ(C_ABC, SE.getUDivExpr(One, ABNoNUW));
+ const SCEV *AB = SE.getMulExpr(ABOps, SCEV::FlagNUW);
+ EXPECT_EQ(C_ABC, SE.getUDivExpr(One, AB));
}
// (C * E) / (C * D * E)
- {
- auto *CE_CDE = SE.getUDivExactExpr(CE, CDE);
- auto *Denom = dyn_cast<const SCEVMulExpr>(CE_CDE->operands().back());
- EXPECT_TRUE(Denom);
- EXPECT_FALSE(!Denom || Denom->hasNoUnsignedWrap());
- SmallVector<SCEVUse, 2> DEOps = {D, E};
- const SCEV *DENoNUW = SE.getMulExpr(DEOps);
- SmallVector<SCEVUse, 2> CDOps = {C, D};
- const SCEV *CDNoNUW = SE.getMulExpr(CDOps);
- EXPECT_TRUE(CE_CDE == SE.getUDivExpr(E, DENoNUW) ||
- CE_CDE == SE.getUDivExpr(C, CDNoNUW));
- }
+ EXPECT_TRUE(SE.getUDivExactExpr(CE, CDE) == SE.getUDivExpr(One, D));
// (B * D) / (A * B * E)
{
auto *BD_ABE = SE.getUDivExactExpr(BD, ABE);
auto *Denom = dyn_cast<const SCEVMulExpr>(BD_ABE->operands().back());
EXPECT_TRUE(Denom);
- EXPECT_FALSE(!Denom || Denom->hasNoUnsignedWrap());
+ EXPECT_TRUE(Denom && Denom->hasNoUnsignedWrap());
SmallVector<SCEVUse, 2> AEOps = {A, E};
- const SCEV *AENoNUW = SE.getMulExpr(AEOps);
- EXPECT_EQ(BD_ABE, SE.getUDivExpr(D, AENoNUW));
+ const SCEV *AE = SE.getMulExpr(AEOps, SCEV::FlagNUW);
+ EXPECT_EQ(BD_ABE, SE.getUDivExpr(D, AE));
}
// (3 * A) / (3 * B)
@@ -1822,10 +1811,10 @@ TEST_F(ScalarEvolutionsTest, SCEVUDivExactNUWMulDivisor) {
SmallVector<SCEVUse, 2> D2Ops = {D, Two};
const SCEV *D2 = SE.getMulExpr(D2Ops, SCEV::FlagNUW);
EXPECT_EQ(D6_ABCE3, SE.getUDivExpr(D2, ABCE));
- // No NUW
- EXPECT_EQ(SE.getUDivExactExpr(A, ABCDNoNUW),
- SE.getUDivExpr(A, ABCDNoNUW));
}
+
+ // No NUW
+ EXPECT_EQ(SE.getUDivExactExpr(A, ABCDNoNUW), SE.getUDivExpr(A, ABCDNoNUW));
});
}
>From 31ba6621cba890921f44ce8e4233558f1b7ee7ae Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 5 May 2026 10:48:07 -0700
Subject: [PATCH 6/6] Lint fixes
---
llvm/lib/Analysis/ScalarEvolution.cpp | 1 -
llvm/unittests/Analysis/ScalarEvolutionTest.cpp | 1 -
2 files changed, 2 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 52e08a25f38b8..716346a7f73db 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3565,7 +3565,6 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
if (LHS == RHS)
return getConstant(LHS->getType(), 1);
-
if (const SCEVConstant *RHSC = dyn_cast<SCEVConstant>(RHS)) {
if (RHSC->getValue()->isOne())
return LHS; // X udiv 1 --> x
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index c92620904f7e3..b3cc2991104b8 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1602,7 +1602,6 @@ TEST_F(ScalarEvolutionsTest, SCEVUDivEqual) {
});
}
-
TEST_F(ScalarEvolutionsTest, SCEVUDivExactPreserveNUW) {
LLVMContext C;
SMDiagnostic Err;
More information about the llvm-commits
mailing list