[llvm] [InstCombine] Fold mul (lshr exact (X, N)), 2^N + 1 -> add (X , lshr exact (X, N)) (PR #95042)

Rose Silicon via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 6 07:43:24 PDT 2024


https://github.com/RSilicon updated https://github.com/llvm/llvm-project/pull/95042

>From 719dddaed92d55ef2b0121242618bb78b34ab71d Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Mon, 10 Jun 2024 12:44:49 -0400
Subject: [PATCH 1/2] [InstCombine] Pre-commit tests (NFC)

---
 llvm/test/Transforms/InstCombine/ashr-lshr.ll | 164 ++++++++++++++++++
 1 file changed, 164 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/ashr-lshr.ll b/llvm/test/Transforms/InstCombine/ashr-lshr.ll
index 49041906680b3a..cd90e0a123cf81 100644
--- a/llvm/test/Transforms/InstCombine/ashr-lshr.ll
+++ b/llvm/test/Transforms/InstCombine/ashr-lshr.ll
@@ -1075,6 +1075,170 @@ entry:
   call void @use(i32 %and)
   %shr = ashr i32 %and, 31
   ret i32 %shr
+define i32 @ashr_shift_mul(i32 noundef %x) {
+; CHECK-LABEL: @ashr_shift_mul(
+; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    [[RES:%.*]] = mul i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = ashr exact i32 %x, 3
+  %res = mul i32 %a, 9
+  ret i32 %res
+}
+
+define i32 @ashr_shift_mul_nuw(i32 noundef %x) {
+; CHECK-LABEL: @ashr_shift_mul_nuw(
+; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    [[RES:%.*]] = mul nuw i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = ashr exact i32 %x, 3
+  %res = mul nuw i32 %a, 9
+  ret i32 %res
+}
+
+define i32 @ashr_shift_mul_nsw(i32 noundef %x) {
+; CHECK-LABEL: @ashr_shift_mul_nsw(
+; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    [[RES:%.*]] = mul nsw i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = ashr exact i32 %x, 3
+  %res = mul nsw i32 %a, 9
+  ret i32 %res
+}
+
+define i32 @lshr_shift_mul_nuw(i32 noundef %x) {
+; CHECK-LABEL: @lshr_shift_mul_nuw(
+; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    [[RES:%.*]] = mul nuw i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = lshr exact i32 %x, 3
+  %res = mul nuw i32 %a, 9
+  ret i32 %res
+}
+
+define i32 @lshr_shift_mul(i32 noundef %x) {
+; CHECK-LABEL: @lshr_shift_mul(
+; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    [[RES:%.*]] = mul i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = lshr exact i32 %x, 3
+  %res = mul i32 %a, 9
+  ret i32 %res
+}
+
+define i32 @lshr_shift_mul_nsw(i32 noundef %x) {
+; CHECK-LABEL: @lshr_shift_mul_nsw(
+; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    [[RES:%.*]] = mul nuw nsw i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = lshr exact i32 %x, 3
+  %res = mul nsw i32 %a, 9
+  ret i32 %res
+}
+
+; Negative test
+
+define i32 @lshr_no_exact(i32 %x) {
+; CHECK-LABEL: @lshr_no_exact(
+; CHECK-NEXT:    [[A:%.*]] = lshr i32 [[X:%.*]], 3
+; CHECK-NEXT:    [[RES:%.*]] = mul nuw nsw i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = lshr i32 %x, 3
+  %res = mul nsw i32 %a, 9
+  ret i32 %res
+}
+
+; Negative test
+
+define i32 @ashr_no_exact(i32 %x) {
+; CHECK-LABEL: @ashr_no_exact(
+; CHECK-NEXT:    [[A:%.*]] = ashr i32 [[X:%.*]], 3
+; CHECK-NEXT:    [[RES:%.*]] = mul nsw i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = ashr i32 %x, 3
+  %res = mul nsw i32 %a, 9
+  ret i32 %res
+}
+
+define i32 @lshr_no_undef(i32 %x) {
+; CHECK-LABEL: @lshr_no_undef(
+; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    [[RES:%.*]] = mul nuw nsw i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = lshr exact i32 %x, 3
+  %res = mul nsw i32 %a, 9
+  ret i32 %res
+}
+
+define i32 @ashr_no_undef(i32 %x) {
+; CHECK-LABEL: @ashr_no_undef(
+; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    [[RES:%.*]] = mul nsw i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = ashr exact i32 %x, 3
+  %res = mul nsw i32 %a, 9
+  ret i32 %res
+}
+
+define i32 @lshr_multiuse(i32 noundef %x) {
+; CHECK-LABEL: @lshr_multiuse(
+; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    call void @use(i32 [[A]])
+; CHECK-NEXT:    [[RES:%.*]] = mul nuw nsw i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = lshr exact i32 %x, 3
+  call void @use(i32 %a)
+  %res = mul nsw i32 %a, 9
+  ret i32 %res
+}
+
+define i32 @lshr_multiuse_no_flags(i32 noundef %x) {
+; CHECK-LABEL: @lshr_multiuse_no_flags(
+; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    call void @use(i32 [[A]])
+; CHECK-NEXT:    [[RES:%.*]] = mul i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = lshr exact i32 %x, 3
+  call void @use(i32 %a)
+  %res = mul i32 %a, 9
+  ret i32 %res
+}
+
+define i32 @ashr_multiuse_no_flags(i32 noundef %x) {
+; CHECK-LABEL: @ashr_multiuse_no_flags(
+; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    call void @use(i32 [[A]])
+; CHECK-NEXT:    [[RES:%.*]] = mul i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = ashr exact i32 %x, 3
+  call void @use(i32 %a)
+  %res = mul i32 %a, 9
+  ret i32 %res
+}
+
+define i32 @ashr_multiuse(i32 noundef %x) {
+; CHECK-LABEL: @ashr_multiuse(
+; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    call void @use(i32 [[A]])
+; CHECK-NEXT:    [[RES:%.*]] = mul nsw i32 [[A]], 9
+; CHECK-NEXT:    ret i32 [[RES]]
+;
+  %a = ashr exact i32 %x, 3
+  call void @use(i32 %a)
+  %res = mul nsw i32 %a, 9
+  ret i32 %res
 }
 
 declare void @use(i32)

>From d51cfe27dbfe89f3be3089808f1cd8638d1eab60 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Wed, 17 Jul 2024 14:47:11 -0400
Subject: [PATCH 2/2] [InstCombine] Fold mul (lshr exact (X, N)), 2^N + 1 ->
 add (X , lshr exact (X, N))

Alive2 Proofs:
https://alive2.llvm.org/ce/z/aJnxyp
https://alive2.llvm.org/ce/z/dyeGEv
---
 .../InstCombine/InstCombineMulDivRem.cpp      | 29 +++++++++++++++++
 llvm/test/Transforms/InstCombine/ashr-lshr.ll | 32 ++++++++++---------
 2 files changed, 46 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index f4f3644acfe5ea..b16083134c92e5 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -261,6 +261,35 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
     }
   }
 
+  {
+    // mul (lshr exact X, N), (2^N + 1) -> add (X, lshr exact (X, N))
+    Value *NewOp;
+    const APInt *ShiftC;
+    const APInt *MulAP;
+    if (match(&I, m_Mul(m_Exact(m_Shr(m_Value(NewOp), m_APInt(ShiftC))),
+                        m_APInt(MulAP)))) {
+      if (BitWidth > 2 && (*MulAP - 1).isPowerOf2() &&
+          *ShiftC == MulAP->logBase2()) {
+        Value *BinOp = Op0;
+        BinaryOperator *OpBO = cast<BinaryOperator>(Op0);
+        if (!isGuaranteedNotToBeUndef(NewOp, &AC, &I, &DT))
+          NewOp = Builder.CreateFreeze(NewOp, NewOp->getName() + ".fr");
+        if (HasNUW && OpBO->getOpcode() == Instruction::AShr &&
+            OpBO->hasOneUse())
+          BinOp = Builder.CreateLShr(NewOp, ConstantInt::get(Ty, *ShiftC), "",
+                                     /*isExact=*/true);
+
+        auto *NewAdd = BinaryOperator::CreateAdd(NewOp, BinOp);
+        if (HasNSW && (HasNUW || OpBO->getOpcode() == Instruction::LShr ||
+                       ShiftC->getZExtValue() < BitWidth - 1))
+          NewAdd->setHasNoSignedWrap(true);
+
+        NewAdd->setHasNoUnsignedWrap(HasNUW);
+        return NewAdd;
+      }
+    }
+  }
+
   if (Op0->hasOneUse() && match(Op1, m_NegatedPower2())) {
     // Interpret  X * (-1<<C)  as  (-X) * (1<<C)  and try to sink the negation.
     // The "* (1<<C)" thus becomes a potential shifting opportunity.
diff --git a/llvm/test/Transforms/InstCombine/ashr-lshr.ll b/llvm/test/Transforms/InstCombine/ashr-lshr.ll
index cd90e0a123cf81..321f9688aed754 100644
--- a/llvm/test/Transforms/InstCombine/ashr-lshr.ll
+++ b/llvm/test/Transforms/InstCombine/ashr-lshr.ll
@@ -1078,7 +1078,7 @@ entry:
 define i32 @ashr_shift_mul(i32 noundef %x) {
 ; CHECK-LABEL: @ashr_shift_mul(
 ; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
-; CHECK-NEXT:    [[RES:%.*]] = mul i32 [[A]], 9
+; CHECK-NEXT:    [[RES:%.*]] = add i32 [[A]], [[X]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = ashr exact i32 %x, 3
@@ -1088,8 +1088,8 @@ define i32 @ashr_shift_mul(i32 noundef %x) {
 
 define i32 @ashr_shift_mul_nuw(i32 noundef %x) {
 ; CHECK-LABEL: @ashr_shift_mul_nuw(
-; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
-; CHECK-NEXT:    [[RES:%.*]] = mul nuw i32 [[A]], 9
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr exact i32 [[X:%.*]], 3
+; CHECK-NEXT:    [[RES:%.*]] = add nuw i32 [[TMP1]], [[X]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = ashr exact i32 %x, 3
@@ -1100,7 +1100,7 @@ define i32 @ashr_shift_mul_nuw(i32 noundef %x) {
 define i32 @ashr_shift_mul_nsw(i32 noundef %x) {
 ; CHECK-LABEL: @ashr_shift_mul_nsw(
 ; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
-; CHECK-NEXT:    [[RES:%.*]] = mul nsw i32 [[A]], 9
+; CHECK-NEXT:    [[RES:%.*]] = add nsw i32 [[A]], [[X]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = ashr exact i32 %x, 3
@@ -1111,7 +1111,7 @@ define i32 @ashr_shift_mul_nsw(i32 noundef %x) {
 define i32 @lshr_shift_mul_nuw(i32 noundef %x) {
 ; CHECK-LABEL: @lshr_shift_mul_nuw(
 ; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
-; CHECK-NEXT:    [[RES:%.*]] = mul nuw i32 [[A]], 9
+; CHECK-NEXT:    [[RES:%.*]] = add nuw i32 [[A]], [[X]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = lshr exact i32 %x, 3
@@ -1122,7 +1122,7 @@ define i32 @lshr_shift_mul_nuw(i32 noundef %x) {
 define i32 @lshr_shift_mul(i32 noundef %x) {
 ; CHECK-LABEL: @lshr_shift_mul(
 ; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
-; CHECK-NEXT:    [[RES:%.*]] = mul i32 [[A]], 9
+; CHECK-NEXT:    [[RES:%.*]] = add i32 [[A]], [[X]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = lshr exact i32 %x, 3
@@ -1133,7 +1133,7 @@ define i32 @lshr_shift_mul(i32 noundef %x) {
 define i32 @lshr_shift_mul_nsw(i32 noundef %x) {
 ; CHECK-LABEL: @lshr_shift_mul_nsw(
 ; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
-; CHECK-NEXT:    [[RES:%.*]] = mul nuw nsw i32 [[A]], 9
+; CHECK-NEXT:    [[RES:%.*]] = add nsw i32 [[A]], [[X]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = lshr exact i32 %x, 3
@@ -1169,8 +1169,9 @@ define i32 @ashr_no_exact(i32 %x) {
 
 define i32 @lshr_no_undef(i32 %x) {
 ; CHECK-LABEL: @lshr_no_undef(
-; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
-; CHECK-NEXT:    [[RES:%.*]] = mul nuw nsw i32 [[A]], 9
+; CHECK-NEXT:    [[X_FR:%.*]] = freeze i32 [[X:%.*]]
+; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X_FR]], 3
+; CHECK-NEXT:    [[RES:%.*]] = add nsw i32 [[X_FR]], [[A]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = lshr exact i32 %x, 3
@@ -1180,8 +1181,9 @@ define i32 @lshr_no_undef(i32 %x) {
 
 define i32 @ashr_no_undef(i32 %x) {
 ; CHECK-LABEL: @ashr_no_undef(
-; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
-; CHECK-NEXT:    [[RES:%.*]] = mul nsw i32 [[A]], 9
+; CHECK-NEXT:    [[X_FR:%.*]] = freeze i32 [[X:%.*]]
+; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X_FR]], 3
+; CHECK-NEXT:    [[RES:%.*]] = add nsw i32 [[X_FR]], [[A]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = ashr exact i32 %x, 3
@@ -1193,7 +1195,7 @@ define i32 @lshr_multiuse(i32 noundef %x) {
 ; CHECK-LABEL: @lshr_multiuse(
 ; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
 ; CHECK-NEXT:    call void @use(i32 [[A]])
-; CHECK-NEXT:    [[RES:%.*]] = mul nuw nsw i32 [[A]], 9
+; CHECK-NEXT:    [[RES:%.*]] = add nsw i32 [[A]], [[X]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = lshr exact i32 %x, 3
@@ -1206,7 +1208,7 @@ define i32 @lshr_multiuse_no_flags(i32 noundef %x) {
 ; CHECK-LABEL: @lshr_multiuse_no_flags(
 ; CHECK-NEXT:    [[A:%.*]] = lshr exact i32 [[X:%.*]], 3
 ; CHECK-NEXT:    call void @use(i32 [[A]])
-; CHECK-NEXT:    [[RES:%.*]] = mul i32 [[A]], 9
+; CHECK-NEXT:    [[RES:%.*]] = add i32 [[A]], [[X]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = lshr exact i32 %x, 3
@@ -1219,7 +1221,7 @@ define i32 @ashr_multiuse_no_flags(i32 noundef %x) {
 ; CHECK-LABEL: @ashr_multiuse_no_flags(
 ; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
 ; CHECK-NEXT:    call void @use(i32 [[A]])
-; CHECK-NEXT:    [[RES:%.*]] = mul i32 [[A]], 9
+; CHECK-NEXT:    [[RES:%.*]] = add i32 [[A]], [[X]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = ashr exact i32 %x, 3
@@ -1232,7 +1234,7 @@ define i32 @ashr_multiuse(i32 noundef %x) {
 ; CHECK-LABEL: @ashr_multiuse(
 ; CHECK-NEXT:    [[A:%.*]] = ashr exact i32 [[X:%.*]], 3
 ; CHECK-NEXT:    call void @use(i32 [[A]])
-; CHECK-NEXT:    [[RES:%.*]] = mul nsw i32 [[A]], 9
+; CHECK-NEXT:    [[RES:%.*]] = add nsw i32 [[A]], [[X]]
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %a = ashr exact i32 %x, 3



More information about the llvm-commits mailing list