[llvm] [InstCombine] Fold mul (shr exact (X, N)), 2^N + 1 -> add (X , shr exact (X, N)) (PR #112407)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Feb 7 12:28:26 PST 2025
https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/112407
>From 5bde6a457f7c19262db3c1d6c53e06a538236714 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 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 60debf02b586212..55d5ec9b1875a5f 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 37b0139466d4868c06f5ffb8b70fae708f7527a4 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/4] [InstCombine] Fold mul (shr exact (X, N)), 2^N + 1 -> add
(X , shr exact (X, N))
Alive2 Proofs:
https://alive2.llvm.org/ce/z/aJnxyp
https://alive2.llvm.org/ce/z/dyeGEv
---
.../InstCombine/InstCombineMulDivRem.cpp | 31 +++++++++++++++++
llvm/test/Transforms/InstCombine/ashr-lshr.ll | 34 +++++++++++--------
2 files changed, 50 insertions(+), 15 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index f85a3c936513530..cb33b505f372a3a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -261,6 +261,37 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
}
}
+ // mul (shr exact X, N), (2^N + 1) -> add (X, shr 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");
+
+ // mul (ashr nuw exact X, N) -> add (X, lshr nuw exact (X, N))
+ 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 55d5ec9b1875a5f..12891a38a105580 100644
--- a/llvm/test/Transforms/InstCombine/ashr-lshr.ll
+++ b/llvm/test/Transforms/InstCombine/ashr-lshr.ll
@@ -1075,10 +1075,12 @@ 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: [[RES:%.*]] = add i32 [[X]], [[A]]
; CHECK-NEXT: ret i32 [[RES]]
;
%a = ashr exact i32 %x, 3
@@ -1088,8 +1090,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 [[X]], [[TMP1]]
; CHECK-NEXT: ret i32 [[RES]]
;
%a = ashr exact i32 %x, 3
@@ -1100,7 +1102,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 [[X]], [[A]]
; CHECK-NEXT: ret i32 [[RES]]
;
%a = ashr exact i32 %x, 3
@@ -1111,7 +1113,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 [[X]], [[A]]
; CHECK-NEXT: ret i32 [[RES]]
;
%a = lshr exact i32 %x, 3
@@ -1122,7 +1124,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 [[X]], [[A]]
; CHECK-NEXT: ret i32 [[RES]]
;
%a = lshr exact i32 %x, 3
@@ -1133,7 +1135,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 [[X]], [[A]]
; CHECK-NEXT: ret i32 [[RES]]
;
%a = lshr exact i32 %x, 3
@@ -1169,8 +1171,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 +1183,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 +1197,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 [[X]], [[A]]
; CHECK-NEXT: ret i32 [[RES]]
;
%a = lshr exact i32 %x, 3
@@ -1206,7 +1210,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 [[X]], [[A]]
; CHECK-NEXT: ret i32 [[RES]]
;
%a = lshr exact i32 %x, 3
@@ -1219,7 +1223,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 [[X]], [[A]]
; CHECK-NEXT: ret i32 [[RES]]
;
%a = ashr exact i32 %x, 3
@@ -1232,7 +1236,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 [[X]], [[A]]
; CHECK-NEXT: ret i32 [[RES]]
;
%a = ashr exact i32 %x, 3
>From 4595c197161ca2068b1b51e0f19bd2e4bdb18b76 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Fri, 7 Feb 2025 15:28:10 -0500
Subject: [PATCH 3/4] Update
llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
Co-authored-by: Yingwei Zheng <dtcxzyw at qq.com>
---
llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index cb33b505f372a3a..199ffa7f23ee057 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -266,9 +266,8 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
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() &&
+ if (BitWidth > 2 && match(&I, m_Mul(m_Exact(m_Shr(m_Value(NewOp), m_APInt(ShiftC))),
+ m_APInt(MulAP))) && (*MulAP - 1).isPowerOf2() &&
*ShiftC == MulAP->logBase2()) {
Value *BinOp = Op0;
BinaryOperator *OpBO = cast<BinaryOperator>(Op0);
>From e55a65a26b4b4ace5e951c22b804c6035d4992ec Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Fri, 7 Feb 2025 15:28:17 -0500
Subject: [PATCH 4/4] Update
llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
Co-authored-by: Yingwei Zheng <dtcxzyw at qq.com>
---
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 199ffa7f23ee057..4b284e98f597972 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -274,7 +274,7 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
if (!isGuaranteedNotToBeUndef(NewOp, &AC, &I, &DT))
NewOp = Builder.CreateFreeze(NewOp, NewOp->getName() + ".fr");
- // mul (ashr nuw exact X, N) -> add (X, lshr nuw exact (X, N))
+ // mul nuw (ashr exact X, N) -> add nuw (X, lshr exact (X, N))
if (HasNUW && OpBO->getOpcode() == Instruction::AShr &&
OpBO->hasOneUse())
BinOp = Builder.CreateLShr(NewOp, ConstantInt::get(Ty, *ShiftC), "",
More information about the llvm-commits
mailing list