[llvm] [InstCombine] Support division of numbers that can be converted to a shift (PR #88220)

via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 13 13:14:01 PDT 2024


https://github.com/AtariDreams updated https://github.com/llvm/llvm-project/pull/88220

>From e90ef0c22e0de3946f1cc84d9df6c58e6b8c6c82 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Tue, 9 Apr 2024 21:04:26 -0400
Subject: [PATCH 1/2] Pre-commit tests (NFC)

---
 llvm/test/Transforms/InstCombine/div.ll | 57 +++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/div.ll b/llvm/test/Transforms/InstCombine/div.ll
index e8a25ff44d0296..2f23761584a5c3 100644
--- a/llvm/test/Transforms/InstCombine/div.ll
+++ b/llvm/test/Transforms/InstCombine/div.ll
@@ -1638,6 +1638,63 @@ define i32 @sdiv_mul_sub_nsw(i32 %x, i32 %y) {
   ret i32 %d
 }
 
+  define i32 @mul_by_150_udiv_by_100 (i32 %0) {
+; CHECK-LABEL: @mul_by_150_udiv_by_100(
+; CHECK-NEXT:    [[TMP2:%.*]] = mul nuw i32 [[TMP0:%.*]], 150
+; CHECK-NEXT:    [[TMP3:%.*]] = udiv i32 [[TMP2]], 100
+; CHECK-NEXT:    ret i32 [[TMP3]]
+;
+  %2 = mul nuw i32 %0, 150
+  %3 = udiv i32 %2, 100
+  ret i32 %3
+}
+
+  define i32 @mul_by_150_sdiv_by_100(i32 %0) {
+; CHECK-LABEL: @mul_by_150_sdiv_by_100(
+; CHECK-NEXT:    [[TMP2:%.*]] = mul nuw i32 [[TMP0:%.*]], -150
+; CHECK-NEXT:    [[TMP3:%.*]] = sdiv i32 [[TMP2]], -100
+; CHECK-NEXT:    ret i32 [[TMP3]]
+;
+  %2 = mul nuw i32 %0, -150
+  %3 = sdiv i32 %2, -100
+  ret i32 %3
+}
+
+  define i32 @mul_by_3_sdiv_by_2(i32 %0) {
+; CHECK-LABEL: @mul_by_3_sdiv_by_2(
+; CHECK-NEXT:    [[TMP2:%.*]] = mul nuw i32 [[TMP0:%.*]], -3
+; CHECK-NEXT:    [[TMP3:%.*]] = sdiv i32 [[TMP2]], -2
+; CHECK-NEXT:    ret i32 [[TMP3]]
+;
+  %2 = mul nuw i32 %0, -3
+  %3 = sdiv i32 %2, -2
+  ret i32 %3
+}
+
+;negative case, no nsw
+
+  define i32 @mul_by_150_sdiv_by_100_neg (i32 %0) {
+; CHECK-LABEL: @mul_by_150_sdiv_by_100_neg(
+; CHECK-NEXT:    [[TMP2:%.*]] = mul i32 [[TMP0:%.*]], 150
+; CHECK-NEXT:    [[TMP3:%.*]] = sdiv i32 [[TMP2]], 100
+; CHECK-NEXT:    ret i32 [[TMP3]]
+;
+  %2 = mul i32 %0, 150
+  %3 = sdiv i32 %2, 100
+  ret i32 %3
+}
+
+  define i32 @mul_by_150_sdiv_by_100_neg2 (i32 %0) {
+; CHECK-LABEL: @mul_by_150_sdiv_by_100_neg2(
+; CHECK-NEXT:    [[TMP2:%.*]] = mul i32 [[TMP0:%.*]], 150
+; CHECK-NEXT:    [[TMP3:%.*]] = udiv i32 [[TMP2]], 100
+; CHECK-NEXT:    ret i32 [[TMP3]]
+;
+  %2 = mul i32 %0, 150
+  %3 = udiv i32 %2, 100
+  ret i32 %3
+}
+
 define i32 @sdiv_mul_nsw_sub_nsw(i32 %x, i32 %y) {
 ; CHECK-LABEL: @sdiv_mul_nsw_sub_nsw(
 ; CHECK-NEXT:    ret i32 -1

>From 3b8603a68334e6f40cc95d62c044dcee0de5726f Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Tue, 9 Apr 2024 20:32:12 -0400
Subject: [PATCH 2/2] [InstCombine] Support division of numbers that can be
 converted to a shift

As well as negative numbers
---
 .../InstCombine/InstCombineMulDivRem.cpp      | 75 +++++++++++++++++++
 llvm/test/Transforms/InstCombine/div.ll       |  4 +-
 2 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index 4dc1319f1c437f..99645d3fb24b74 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -1128,6 +1128,31 @@ Instruction *InstCombinerImpl::commonIDivTransforms(BinaryOperator &I) {
                                       ConstantInt::get(Ty, Product));
     }
 
+    // See if we can remove negative numbers. IE: sdiv (mul (X, -3), -2) -> sdiv
+    // (mul (X, 3), 2) This is useful in conjunction with other checks such as
+    // if a constant is a power of 2.
+    // TODO: Enhance with KnownBits? Then we can get rid of the overflow check
+    // in case X is INT_MIN
+    // TODO: We can make this a udiv in some cases.
+    if (match(Op0, m_Mul(m_Value(X), m_APInt(C1))) && IsSigned &&
+        Op0->hasOneUse() && C1->isNegative() && C2->isNegative() &&
+        !C1->isMinSignedValue() && !C2->isMinSignedValue()) {
+      auto *OBO = cast<OverflowingBinaryOperator>(Op0);
+      if (OBO->hasNoSignedWrap() || OBO->hasNoUnsignedWrap()) {
+        // if nuw is true, nsw is also true in this context.
+        APInt Zero = APInt::getZero(C1->getBitWidth());
+        APInt NegativeC1 = Zero - *C1;
+        APInt NegativeC2 = Zero - *C2;
+        Instruction *newDiv = BinaryOperator::CreateSDiv(
+            Builder.CreateMul(X, ConstantInt::get(Ty, NegativeC1), "",
+                              /* HasNUW */ OBO->hasNoUnsignedWrap(),
+                              /* HasNSW */ true),
+            ConstantInt::get(Ty, NegativeC2));
+
+        newDiv->setIsExact(I.isExact());
+      }
+    }
+
     APInt Quotient(C2->getBitWidth(), /*val=*/0ULL, IsSigned);
     if ((IsSigned && match(Op0, m_NSWMul(m_Value(X), m_APInt(C1)))) ||
         (!IsSigned && match(Op0, m_NUWMul(m_Value(X), m_APInt(C1))))) {
@@ -1149,6 +1174,56 @@ Instruction *InstCombinerImpl::commonIDivTransforms(BinaryOperator &I) {
         Mul->setHasNoSignedWrap(OBO->hasNoSignedWrap());
         return Mul;
       }
+
+      // If we can reduce these functions so they can factor out to a shift or
+      // something similar (i.e Reduce (X * 150/100) -> (X * 3) >> 1)
+      //
+      // (X * C1)/C2 -> X * (C1/C3) >> log2(C2/C3) where C3 divides exactly C1
+      // and C2 and C2/C3 is a power of 2.
+      // FIXME: Support negative numbers
+      if (C1->isStrictlyPositive() && C2->isStrictlyPositive()) {
+
+        APInt C3 = APIntOps::GreatestCommonDivisor(*C1, *C2);
+        APInt Q(C2->getBitWidth(), /*val=*/0ULL, IsSigned);
+
+        // Returns false if division by 0
+        if (isMultiple(*C2, C3, Q, IsSigned) && Q.isPowerOf2()) {
+          auto *OBO = cast<OverflowingBinaryOperator>(Op0);
+
+          APInt C4 = IsSigned ? C1->sdiv(C3) : C1->udiv(C3);
+
+          if (C4.isOne()) {
+            Instruction *Shift;
+            if (IsSigned)
+              Shift = BinaryOperator::CreateLShr(
+                  X, ConstantInt::get(Ty, Q.logBase2()));
+            else
+              Shift = BinaryOperator::CreateAShr(
+                  X, ConstantInt::get(Ty, Q.logBase2()));
+            Shift->setIsExact(I.isExact());
+            return Shift;
+          }
+
+          if (Op0->hasOneUse()) {
+            auto *Mul = Builder.CreateMul(
+                X, ConstantInt::get(Ty, C4), "",
+                // If it is unsigned, then we cannot have any nsw or nuw flags.
+                // It has been tried before, but no avail.
+                /* HasNUW */ OBO->hasNoUnsignedWrap(),
+                /* HasNSW */ OBO->hasNoSignedWrap());
+
+            Instruction *Shift;
+            if (IsSigned)
+              Shift = BinaryOperator::CreateLShr(
+                  Mul, ConstantInt::get(Ty, Q.logBase2()));
+            else
+              Shift = BinaryOperator::CreateAShr(
+                  Mul, ConstantInt::get(Ty, Q.logBase2()));
+            Shift->setIsExact(I.isExact());
+            return Shift;
+          }
+        }
+      }
     }
 
     if ((IsSigned && match(Op0, m_NSWShl(m_Value(X), m_APInt(C1))) &&
diff --git a/llvm/test/Transforms/InstCombine/div.ll b/llvm/test/Transforms/InstCombine/div.ll
index 2f23761584a5c3..276b1df3778a57 100644
--- a/llvm/test/Transforms/InstCombine/div.ll
+++ b/llvm/test/Transforms/InstCombine/div.ll
@@ -1640,8 +1640,8 @@ define i32 @sdiv_mul_sub_nsw(i32 %x, i32 %y) {
 
   define i32 @mul_by_150_udiv_by_100 (i32 %0) {
 ; CHECK-LABEL: @mul_by_150_udiv_by_100(
-; CHECK-NEXT:    [[TMP2:%.*]] = mul nuw i32 [[TMP0:%.*]], 150
-; CHECK-NEXT:    [[TMP3:%.*]] = udiv i32 [[TMP2]], 100
+; CHECK-NEXT:    [[TMP2:%.*]] = mul nuw nsw i32 [[TMP0:%.*]], 3
+; CHECK-NEXT:    [[TMP3:%.*]] = lshr i32 [[TMP2]], 1
 ; CHECK-NEXT:    ret i32 [[TMP3]]
 ;
   %2 = mul nuw i32 %0, 150



More information about the llvm-commits mailing list