[llvm] [SCEV] Fold (C1 * A /u C2) -> A /u (C2 /u C1), if C2 > C1. (PR #157656)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 10 10:59:49 PDT 2025
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/157656
>From 9780db85379e83a483ac260148a09f19b97a5c78 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 8 Sep 2025 14:22:33 +0100
Subject: [PATCH 1/2] [SCEV] Fold (C1 * A /u C2) -> A /u (C2 /u C1), if C2 >
C1.
If C2 >u C1 and C1 >u 1, fold to A /u (C2 /u C1).
Depends on https://github.com/llvm/llvm-project/pull/157555.
Alive2 Proof: https://alive2.llvm.org/ce/z/BWvQYN
---
llvm/lib/Analysis/ScalarEvolution.cpp | 14 ++++++++++----
.../Analysis/ScalarEvolution/mul-udiv-folds.ll | 2 +-
.../LoopStrengthReduce/duplicated-phis.ll | 3 +--
3 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 51caffc41002b..7b2d39d03ab43 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3217,7 +3217,8 @@ const SCEV *ScalarEvolution::getMulExpr(SmallVectorImpl<const SCEV *> &Ops,
}
// Try to fold (C1 * D /u C2) -> C1/C2 * D, if C1 and C2 are powers-of-2,
- // D is a multiple of C2, and C1 is a multiple of C2.
+ // D is a multiple of C2, and C1 is a multiple of C2. If C2 is a multiple
+ // of C1, fold to (D /u (C2 /u C1)).
const SCEV *D;
APInt C1V = LHSC->getAPInt();
// (C1 * D /u C2) == -1 * -C1 * D /u C2 when C1 != INT_MIN.
@@ -3226,10 +3227,15 @@ const SCEV *ScalarEvolution::getMulExpr(SmallVectorImpl<const SCEV *> &Ops,
const SCEVConstant *C2;
if (C1V.isPowerOf2() &&
match(Ops[1], m_scev_UDiv(m_SCEV(D), m_SCEVConstant(C2))) &&
- C2->getAPInt().isPowerOf2() && C1V.uge(C2->getAPInt()) &&
+ C2->getAPInt().isPowerOf2() &&
C1V.logBase2() <= getMinTrailingZeros(D)) {
- const SCEV *NewMul = getMulExpr(getUDivExpr(getConstant(C1V), C2), D);
- return C1V == LHSC->getAPInt() ? NewMul : getNegativeSCEV(NewMul);
+ const SCEV *NewMul = nullptr;
+ if (C1V.uge(C2->getAPInt()))
+ NewMul = getMulExpr(getUDivExpr(getConstant(C1V), C2), D);
+ else if (C1V.ugt(1))
+ NewMul = getUDivExpr(D, getUDivExpr(C2, getConstant(C1V)));
+ if (NewMul)
+ return C1V == LHSC->getAPInt() ? NewMul : getNegativeSCEV(NewMul);
}
}
}
diff --git a/llvm/test/Analysis/ScalarEvolution/mul-udiv-folds.ll b/llvm/test/Analysis/ScalarEvolution/mul-udiv-folds.ll
index 8dd8ec47e7090..1d34706baadeb 100644
--- a/llvm/test/Analysis/ScalarEvolution/mul-udiv-folds.ll
+++ b/llvm/test/Analysis/ScalarEvolution/mul-udiv-folds.ll
@@ -21,7 +21,7 @@ define void @udiv4_and_udiv2(i1 %c, ptr %A) {
; CHECK-NEXT: %gep.8 = getelementptr i8, ptr %A, i64 %iv
; CHECK-NEXT: --> {(((zext i32 %start to i64) /u 4) + %A),+,1}<%loop> U: full-set S: full-set Exits: (((zext i32 %start to i64) /u 2) + %A) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.16 = getelementptr i16, ptr %A, i64 %iv
-; CHECK-NEXT: --> {((2 * ((zext i32 %start to i64) /u 4))<nuw><nsw> + %A),+,2}<%loop> U: full-set S: full-set Exits: ((zext i32 %start to i64) + %A) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: --> {(((zext i32 %start to i64) /u 2) + %A),+,2}<%loop> U: full-set S: full-set Exits: ((zext i32 %start to i64) + %A) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.32 = getelementptr i32, ptr %A, i64 %iv
; CHECK-NEXT: --> {((zext i32 %start to i64) + %A),+,4}<%loop> U: full-set S: full-set Exits: ((2 * (zext i32 %start to i64))<nuw><nsw> + %A) LoopDispositions: { %loop: Computable }
; CHECK-NEXT: %gep.40 = getelementptr <{ i32, i8 }>, ptr %A, i64 %iv
diff --git a/llvm/test/Transforms/LoopStrengthReduce/duplicated-phis.ll b/llvm/test/Transforms/LoopStrengthReduce/duplicated-phis.ll
index cee8c8abdb450..c59f7d9c2a41a 100644
--- a/llvm/test/Transforms/LoopStrengthReduce/duplicated-phis.ll
+++ b/llvm/test/Transforms/LoopStrengthReduce/duplicated-phis.ll
@@ -18,8 +18,7 @@ define i64 @test_duplicated_phis(i64 noundef %N) {
; CHECK: [[FOR_BODY_PREHEADER_NEW]]:
; CHECK-NEXT: [[UNROLL_ITER:%.*]] = and i64 [[MUL]], -4
; CHECK-NEXT: [[TMP4:%.*]] = add i64 [[UNROLL_ITER]], -4
-; CHECK-NEXT: [[TMP5:%.*]] = lshr i64 [[TMP4]], 2
-; CHECK-NEXT: [[TMP3:%.*]] = shl nuw nsw i64 [[TMP5]], 1
+; CHECK-NEXT: [[TMP3:%.*]] = lshr i64 [[TMP4]], 1
; CHECK-NEXT: [[LSR_IV_NEXT:%.*]] = sub i64 -3, [[TMP3]]
; CHECK-NEXT: br label %[[FOR_BODY:.*]]
; CHECK: [[FOR_BODY]]:
>From 5764262e9f75bfe931ad17e2f84085b25793c170 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Wed, 10 Sep 2025 18:58:16 +0100
Subject: [PATCH 2/2] !fixup turn check into assert
---
llvm/lib/Analysis/ScalarEvolution.cpp | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 7b2d39d03ab43..ebb863076d2c5 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3221,21 +3221,23 @@ const SCEV *ScalarEvolution::getMulExpr(SmallVectorImpl<const SCEV *> &Ops,
// of C1, fold to (D /u (C2 /u C1)).
const SCEV *D;
APInt C1V = LHSC->getAPInt();
- // (C1 * D /u C2) == -1 * -C1 * D /u C2 when C1 != INT_MIN.
- if (C1V.isNegative() && !C1V.isMinSignedValue())
+ // (C1 * D /u C2) == -1 * -C1 * D /u C2 when C1 != INT_MIN. Don't treat -1
+ // as -1 * 1, as it won't enable additional folds.
+ if (C1V.isNegative() && !C1V.isMinSignedValue() && !C1V.isAllOnes())
C1V = C1V.abs();
const SCEVConstant *C2;
if (C1V.isPowerOf2() &&
match(Ops[1], m_scev_UDiv(m_SCEV(D), m_SCEVConstant(C2))) &&
C2->getAPInt().isPowerOf2() &&
C1V.logBase2() <= getMinTrailingZeros(D)) {
- const SCEV *NewMul = nullptr;
- if (C1V.uge(C2->getAPInt()))
+ const SCEV *NewMul;
+ if (C1V.uge(C2->getAPInt())) {
NewMul = getMulExpr(getUDivExpr(getConstant(C1V), C2), D);
- else if (C1V.ugt(1))
+ } else {
+ assert(C1V.ugt(1) && "C1 <= 1 should have been folded earlier");
NewMul = getUDivExpr(D, getUDivExpr(C2, getConstant(C1V)));
- if (NewMul)
- return C1V == LHSC->getAPInt() ? NewMul : getNegativeSCEV(NewMul);
+ }
+ return C1V == LHSC->getAPInt() ? NewMul : getNegativeSCEV(NewMul);
}
}
}
More information about the llvm-commits
mailing list