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

via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 15 17:16:13 PDT 2024


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

>From 190aaa400097a79ea1798faa873d75c4da77550a 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/4] [InstCombine] Pre-commit test (NFC)

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

diff --git a/llvm/test/Transforms/InstCombine/ashr-lshr.ll b/llvm/test/Transforms/InstCombine/ashr-lshr.ll
index c2a4f35412670..682435ec5cb1b 100644
--- a/llvm/test/Transforms/InstCombine/ashr-lshr.ll
+++ b/llvm/test/Transforms/InstCombine/ashr-lshr.ll
@@ -862,4 +862,126 @@ define i32 @ashr_mul_times_5_div_4_exact_2(i32 %x) {
   ret i32 %ashr
 }
 
+define i32 @ashr_shift_mul(i32 %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 %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 %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 %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 %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 %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
+}
+
+; Negative test
+
+define i32 @lshr_multiuse(i32 %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
+}
+
+; Negative test
+
+define i32 @ashr_multiuse(i32 %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 5b14992d4643cdcf907ec41115e85fc38f0a29c9 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Mon, 10 Jun 2024 12:47:14 -0400
Subject: [PATCH 2/4] [InstCombine] Fold mul (lshr exact (X, 2^N + 1)), N ->
 add (X , lshr (X, N))

Alive2 Proofs:
https://alive2.llvm.org/ce/z/LVqGEo
https://alive2.llvm.org/ce/z/dyeGEv
---
 .../InstCombine/InstCombineMulDivRem.cpp      | 33 ++++++++++++++
 llvm/test/Transforms/InstCombine/ashr-lshr.ll | 45 +++++++++++++++----
 2 files changed, 69 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index ca1b1921404d8..cb30d8b4f4723 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -255,6 +255,39 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
     }
   }
 
+  {
+    // mul (lshr exact X, N), (2^N + 1) -> add (X , lshr (X, N))
+    Value *NewOp;
+    const APInt *ShiftC;
+    const APInt *MulAP;
+    if (match(&I, m_Mul(m_CombineOr(m_LShr(m_Value(NewOp), m_APInt(ShiftC)),
+                                    m_AShr(m_Value(NewOp), m_APInt(ShiftC))),
+                        m_APInt(MulAP)))) {
+      if (BitWidth > 2 && (*MulAP - 1).isPowerOf2() &&
+          MulAP->logBase2() == ShiftC->getZExtValue()) {
+        BinaryOperator *OpBO = cast<BinaryOperator>(Op0);
+        if (OpBO->isExact() && (HasNSW || HasNUW)) {
+          Value *BinOp;
+          if (OpBO->getOpcode() == Instruction::AShr && HasNUW &&
+              OpBO->hasOneUse())
+            BinOp = Builder.CreateLShr(
+                NewOp, ConstantInt::get(Ty, ShiftC->getZExtValue()), "",
+                /*isExact=*/true);
+          else
+            BinOp = Op0;
+
+          auto *NewAdd = BinaryOperator::CreateAdd(NewOp, BinOp);
+          if (HasNSW && (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 682435ec5cb1b..dfb1879da076b 100644
--- a/llvm/test/Transforms/InstCombine/ashr-lshr.ll
+++ b/llvm/test/Transforms/InstCombine/ashr-lshr.ll
@@ -875,8 +875,8 @@ define i32 @ashr_shift_mul(i32 %x) {
 
 define i32 @ashr_shift_mul_nuw(i32 %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
@@ -887,7 +887,7 @@ define i32 @ashr_shift_mul_nuw(i32 %x) {
 define i32 @ashr_shift_mul_nsw(i32 %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
@@ -898,7 +898,7 @@ define i32 @ashr_shift_mul_nsw(i32 %x) {
 define i32 @lshr_shift_mul_nuw(i32 %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
@@ -920,7 +920,7 @@ define i32 @lshr_shift_mul(i32 %x) {
 define i32 @lshr_shift_mul_nsw(i32 %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
@@ -954,13 +954,11 @@ define i32 @ashr_no_exact(i32 %x) {
   ret i32 %res
 }
 
-; Negative test
-
 define i32 @lshr_multiuse(i32 %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
@@ -969,13 +967,42 @@ define i32 @lshr_multiuse(i32 %x) {
   ret i32 %res
 }
 
+
+; Negative test
+
+define i32 @lshr_multiuse_no_flags(i32 %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
+}
+
 ; Negative test
 
+define i32 @ashr_multiuse_no_flags(i32 %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 %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

>From 59c14ad12513504ff1dd298b50893832afaf73ab Mon Sep 17 00:00:00 2001
From: AtariDreams <gfunni234 at gmail.com>
Date: Sat, 15 Jun 2024 20:10:05 -0400
Subject: [PATCH 3/4] Initialize on declaration

---
 llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index cb30d8b4f4723..7c41e55ccf506 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -267,14 +267,12 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
           MulAP->logBase2() == ShiftC->getZExtValue()) {
         BinaryOperator *OpBO = cast<BinaryOperator>(Op0);
         if (OpBO->isExact() && (HasNSW || HasNUW)) {
-          Value *BinOp;
+          Value *BinOp = Op0;
           if (OpBO->getOpcode() == Instruction::AShr && HasNUW &&
               OpBO->hasOneUse())
             BinOp = Builder.CreateLShr(
                 NewOp, ConstantInt::get(Ty, ShiftC->getZExtValue()), "",
                 /*isExact=*/true);
-          else
-            BinOp = Op0;
 
           auto *NewAdd = BinaryOperator::CreateAdd(NewOp, BinOp);
           if (HasNSW && (OpBO->getOpcode() == Instruction::LShr ||

>From 40830383152880e6dfcaef108832c39ce7d89bb3 Mon Sep 17 00:00:00 2001
From: AtariDreams <gfunni234 at gmail.com>
Date: Sat, 15 Jun 2024 20:16:04 -0400
Subject: [PATCH 4/4] Update InstCombineMulDivRem.cpp

---
 llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index 7c41e55ccf506..0922fbdaad4f4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -268,7 +268,7 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
         BinaryOperator *OpBO = cast<BinaryOperator>(Op0);
         if (OpBO->isExact() && (HasNSW || HasNUW)) {
           Value *BinOp = Op0;
-          if (OpBO->getOpcode() == Instruction::AShr && HasNUW &&
+          if (HasNUW && OpBO->getOpcode() == Instruction::AShr &&
               OpBO->hasOneUse())
             BinOp = Builder.CreateLShr(
                 NewOp, ConstantInt::get(Ty, ShiftC->getZExtValue()), "",



More information about the llvm-commits mailing list