From b635047aab2a1a1fb1da1c573926fb3eaf78c104 Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Mon, 8 Sep 2025 20:29:39 +0100 Subject: [PATCH 1/2] [SCEV] Fold ((-1 * C1) * D / C1) -> -1 * D. Treat negative constants C as -1 * abs(C1) when folding multiplies and udivs. Alive2 Proof: https://alive2.llvm.org/ce/z/bdj9W2 --- llvm/lib/Analysis/ScalarEvolution.cpp | 16 ++++++++++------ .../Analysis/ScalarEvolution/mul-udiv-folds.ll | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index 34e497d9ea3cb..30aa0856ae61d 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -3217,15 +3217,19 @@ const SCEV *ScalarEvolution::getMulExpr(SmallVectorImpl &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 C1. + // D is a multiple of C2, and C1 is a multiple of C2. const SCEV *D; + APInt C1V = LHSC->getAPInt(); + // If C1 is negative, try (-1 * abs(C1)) instead. + if (C1V.isNegative() && !C1V.isMinSignedValue()) + C1V = C1V.abs(); const SCEVConstant *C2; - const APInt &LHSV = LHSC->getAPInt(); - if (LHSV.isPowerOf2() && + if (C1V.isPowerOf2() && match(Ops[1], m_scev_UDiv(m_SCEV(D), m_SCEVConstant(C2))) && - C2->getAPInt().isPowerOf2() && LHSV.uge(C2->getAPInt()) && - LHSV.logBase2() <= getMinTrailingZeros(D)) { - return getMulExpr(getUDivExpr(LHSC, C2), D); + C2->getAPInt().isPowerOf2() && C1V.uge(C2->getAPInt()) && + C1V.logBase2() <= getMinTrailingZeros(D)) { + const SCEV *NewMul = getMulExpr(getUDivExpr(getConstant(C1V), C2), D); + 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 dfaf0c95bc2f8..8dd8ec47e7090 100644 --- a/llvm/test/Analysis/ScalarEvolution/mul-udiv-folds.ll +++ b/llvm/test/Analysis/ScalarEvolution/mul-udiv-folds.ll @@ -23,7 +23,7 @@ define void @udiv4_and_udiv2(i1 %c, ptr %A) { ; CHECK-NEXT: %gep.16 = getelementptr i16, ptr %A, i64 %iv ; CHECK-NEXT: --> {((2 * ((zext i32 %start to i64) /u 4)) + %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: ((3 * (zext i32 %start to i64)) + (-4 * ((zext i32 %start to i64) /u 4)) + %A) LoopDispositions: { %loop: Computable } +; CHECK-NEXT: --> {((zext i32 %start to i64) + %A),+,4}<%loop> U: full-set S: full-set Exits: ((2 * (zext i32 %start to i64)) + %A) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %gep.40 = getelementptr <{ i32, i8 }>, ptr %A, i64 %iv ; CHECK-NEXT: --> {((5 * ((zext i32 %start to i64) /u 4)) + %A),+,5}<%loop> U: full-set S: full-set Exits: ((5 * ((zext i32 %start to i64) /u 2)) + %A) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %gep.48 = getelementptr <{ i32, i16 }>, ptr %A, i64 %iv From 0c1705cfa170f6b0178263e670e01790813849ba Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Tue, 9 Sep 2025 22:12:34 +0100 Subject: [PATCH 2/2] !fixup adjust comment, thanks --- llvm/lib/Analysis/ScalarEvolution.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index 30aa0856ae61d..51caffc41002b 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -3220,7 +3220,7 @@ const SCEV *ScalarEvolution::getMulExpr(SmallVectorImpl &Ops, // D is a multiple of C2, and C1 is a multiple of C2. const SCEV *D; APInt C1V = LHSC->getAPInt(); - // If C1 is negative, try (-1 * abs(C1)) instead. + // (C1 * D /u C2) == -1 * -C1 * D /u C2 when C1 != INT_MIN. if (C1V.isNegative() && !C1V.isMinSignedValue()) C1V = C1V.abs(); const SCEVConstant *C2;