[llvm] [InstCombine] Fold (X << Y) / (X << Z) -> 1 << Y >> Z (PR #68863)

via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 12 23:56:50 PDT 2023


https://github.com/XChy updated https://github.com/llvm/llvm-project/pull/68863

>From c3688a434f6a137837a7b0b72f5b80160a07137f Mon Sep 17 00:00:00 2001
From: XChy <xxs_chy at outlook.com>
Date: Thu, 12 Oct 2023 15:42:21 +0800
Subject: [PATCH 1/2] [InstCombine][NFC] Precommit tests for PR68857

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

diff --git a/llvm/test/Transforms/InstCombine/div-shift.ll b/llvm/test/Transforms/InstCombine/div-shift.ll
index 76c5328dc8499e0..df2dcd8aa654df5 100644
--- a/llvm/test/Transforms/InstCombine/div-shift.ll
+++ b/llvm/test/Transforms/InstCombine/div-shift.ll
@@ -2,6 +2,7 @@
 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
 
 declare void @use(i8)
+declare void @use32(i32)
 
 declare i8 @llvm.umin.i8(i8, i8)
 declare i8 @llvm.umax.i8(i8, i8)
@@ -1025,3 +1026,200 @@ define i8 @udiv_shl_no_overflow(i8 %x, i8 %y) {
   %mul = udiv i8 %x, %min
   ret i8 %mul
 }
+
+; (X<<Y) / (X<<Z) -> 1 << Y >> Z
+
+define i32 @sdiv_shl_pair_const(i32 %a) {
+; CHECK-LABEL: @sdiv_shl_pair_const(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nsw i32 [[A:%.*]], 2
+; CHECK-NEXT:    [[RHS:%.*]] = shl nsw i32 [[A]], 1
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nsw i32 %a, 2
+  %rhs = shl nsw i32 %a, 1
+  %div = sdiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @udiv_shl_pair_const(i32 %a) {
+; CHECK-LABEL: @udiv_shl_pair_const(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nuw i32 [[A:%.*]], 2
+; CHECK-NEXT:    [[RHS:%.*]] = shl nuw i32 [[A]], 1
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nuw i32 %a, 2
+  %rhs = shl nuw i32 %a, 1
+  %div = udiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @sdiv_shl_pair1(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @sdiv_shl_pair1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nsw i32 [[A:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[RHS:%.*]] = shl nuw nsw i32 [[A]], [[Y:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nsw i32 %a, %x
+  %rhs = shl nuw nsw i32 %a, %y
+  %div = sdiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @sdiv_shl_pair2(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @sdiv_shl_pair2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nuw nsw i32 [[A:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[RHS:%.*]] = shl nsw i32 [[A]], [[Y:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nuw nsw i32 %a, %x
+  %rhs = shl nsw i32 %a, %y
+  %div = sdiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @sdiv_shl_pair3(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @sdiv_shl_pair3(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nuw nsw i32 [[A:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[RHS:%.*]] = shl nsw i32 [[A]], [[Y:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nuw nsw i32 %a, %x
+  %rhs = shl nsw i32 %a, %y
+  %div = sdiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @udiv_shl_pair1(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @udiv_shl_pair1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nuw i32 [[A:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[RHS:%.*]] = shl nuw i32 [[A]], [[Y:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nuw i32 %a, %x
+  %rhs = shl nuw i32 %a, %y
+  %div = udiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @udiv_shl_pair2(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @udiv_shl_pair2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nuw nsw i32 [[A:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[RHS:%.*]] = shl nuw i32 [[A]], [[Y:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nuw nsw i32 %a, %x
+  %rhs = shl nuw i32 %a, %y
+  %div = udiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @udiv_shl_pair3(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @udiv_shl_pair3(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nuw i32 [[A:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[RHS:%.*]] = shl nuw nsw i32 [[A]], [[Y:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nuw i32 %a, %x
+  %rhs = shl nuw nsw i32 %a, %y
+  %div = udiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @sdiv_shl_pair_overflow_fail1(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @sdiv_shl_pair_overflow_fail1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl i32 [[A:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[RHS:%.*]] = shl nsw i32 [[A]], [[Y:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl i32 %a, %x
+  %rhs = shl nsw i32 %a, %y
+  %div = sdiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @sdiv_shl_pair_overflow_fail2(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @sdiv_shl_pair_overflow_fail2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nsw i32 [[A:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[RHS:%.*]] = shl nuw i32 [[A]], [[Y:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nsw i32 %a, %x
+  %rhs = shl nuw i32 %a, %y
+  %div = sdiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @udiv_shl_pair_overflow_fail1(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @udiv_shl_pair_overflow_fail1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nsw i32 [[A:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[RHS:%.*]] = shl nuw i32 [[A]], [[Y:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nsw i32 %a, %x
+  %rhs = shl nuw i32 %a, %y
+  %div = udiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @udiv_shl_pair_overflow_fail2(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @udiv_shl_pair_overflow_fail2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nsw i32 [[A:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[RHS:%.*]] = shl i32 [[A]], [[Y:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nsw i32 %a, %x
+  %rhs = shl i32 %a, %y
+  %div = udiv i32 %lhs, %rhs
+  ret i32 %div
+}
+
+define i32 @udiv_shl_pair_overflow_fail3(i32 %a, i32 %x, i32 %y) {
+; CHECK-LABEL: @udiv_shl_pair_overflow_fail3(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[LHS:%.*]] = shl nuw nsw i32 [[A:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[RHS:%.*]] = shl i32 [[A]], [[Y:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+entry:
+  %lhs = shl nuw nsw i32 %a, %x
+  %rhs = shl i32 %a, %y
+  %div = udiv i32 %lhs, %rhs
+  ret i32 %div
+}

>From ec1412e307f245f7140a94840f3ee4a20fbcb92c Mon Sep 17 00:00:00 2001
From: XChy <xxs_chy at outlook.com>
Date: Thu, 12 Oct 2023 15:12:11 +0800
Subject: [PATCH 2/2] [InstCombine] Fold (A<<X)/(A<<Y) -> (1<<X)/(1<<Y)

---
 .../InstCombine/InstCombineMulDivRem.cpp      | 22 ++++++++++
 llvm/test/Transforms/InstCombine/div-shift.ll | 40 +++++++------------
 2 files changed, 36 insertions(+), 26 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index 560c87b6efa7038..d727b517bb90e97 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -980,6 +980,28 @@ static Instruction *foldIDivShl(BinaryOperator &I,
       Ret = BinaryOperator::CreateSDiv(X, Y);
   }
 
+  // If X << Y and X << Z does not overflow, then:
+  // (X << Y) / (X << Z) -> (1 << Y) / (1 << Z) -> 1 << Y >> Z
+  if (match(Op0, m_Shl(m_Value(X), m_Value(Y))) &&
+      match(Op1, m_Shl(m_Specific(X), m_Value(Z)))) {
+    auto *Shl0 = cast<OverflowingBinaryOperator>(Op0);
+    auto *Shl1 = cast<OverflowingBinaryOperator>(Op1);
+
+    if ((!IsSigned && Shl0->hasNoUnsignedWrap() && Shl1->hasNoUnsignedWrap()) ||
+        (IsSigned && Shl0->hasNoSignedWrap() && Shl1->hasNoSignedWrap())) {
+      Constant *One = ConstantInt::get(X->getType(), 1);
+      // Only preserve the nsw flag if dividend has nsw
+      // or divisor has nsw and operator is sdiv.
+      Value *Dividend = Builder.CreateShl(
+          One, Y, "shl.dividend",
+          /*HasNUW*/ true,
+          /*HasNSW*/
+          IsSigned ? (Shl0->hasNoUnsignedWrap() || Shl1->hasNoUnsignedWrap())
+                   : Shl0->hasNoSignedWrap());
+      Ret = BinaryOperator::CreateLShr(Dividend, Z);
+    }
+  }
+
   if (!Ret)
     return nullptr;
 
diff --git a/llvm/test/Transforms/InstCombine/div-shift.ll b/llvm/test/Transforms/InstCombine/div-shift.ll
index df2dcd8aa654df5..15dfa1474e4eb12 100644
--- a/llvm/test/Transforms/InstCombine/div-shift.ll
+++ b/llvm/test/Transforms/InstCombine/div-shift.ll
@@ -1032,10 +1032,7 @@ define i8 @udiv_shl_no_overflow(i8 %x, i8 %y) {
 define i32 @sdiv_shl_pair_const(i32 %a) {
 ; CHECK-LABEL: @sdiv_shl_pair_const(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[LHS:%.*]] = shl nsw i32 [[A:%.*]], 2
-; CHECK-NEXT:    [[RHS:%.*]] = shl nsw i32 [[A]], 1
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[LHS]], [[RHS]]
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 2
 ;
 entry:
   %lhs = shl nsw i32 %a, 2
@@ -1047,10 +1044,7 @@ entry:
 define i32 @udiv_shl_pair_const(i32 %a) {
 ; CHECK-LABEL: @udiv_shl_pair_const(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[LHS:%.*]] = shl nuw i32 [[A:%.*]], 2
-; CHECK-NEXT:    [[RHS:%.*]] = shl nuw i32 [[A]], 1
-; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[LHS]], [[RHS]]
-; CHECK-NEXT:    ret i32 [[DIV]]
+; CHECK-NEXT:    ret i32 2
 ;
 entry:
   %lhs = shl nuw i32 %a, 2
@@ -1062,9 +1056,8 @@ entry:
 define i32 @sdiv_shl_pair1(i32 %a, i32 %x, i32 %y) {
 ; CHECK-LABEL: @sdiv_shl_pair1(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[LHS:%.*]] = shl nsw i32 [[A:%.*]], [[X:%.*]]
-; CHECK-NEXT:    [[RHS:%.*]] = shl nuw nsw i32 [[A]], [[Y:%.*]]
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    [[SHL_DIVIDEND:%.*]] = shl nuw nsw i32 1, [[X:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = lshr i32 [[SHL_DIVIDEND]], [[Y:%.*]]
 ; CHECK-NEXT:    ret i32 [[DIV]]
 ;
 entry:
@@ -1077,9 +1070,8 @@ entry:
 define i32 @sdiv_shl_pair2(i32 %a, i32 %x, i32 %y) {
 ; CHECK-LABEL: @sdiv_shl_pair2(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[LHS:%.*]] = shl nuw nsw i32 [[A:%.*]], [[X:%.*]]
-; CHECK-NEXT:    [[RHS:%.*]] = shl nsw i32 [[A]], [[Y:%.*]]
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    [[SHL_DIVIDEND:%.*]] = shl nuw nsw i32 1, [[X:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = lshr i32 [[SHL_DIVIDEND]], [[Y:%.*]]
 ; CHECK-NEXT:    ret i32 [[DIV]]
 ;
 entry:
@@ -1092,9 +1084,8 @@ entry:
 define i32 @sdiv_shl_pair3(i32 %a, i32 %x, i32 %y) {
 ; CHECK-LABEL: @sdiv_shl_pair3(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[LHS:%.*]] = shl nuw nsw i32 [[A:%.*]], [[X:%.*]]
-; CHECK-NEXT:    [[RHS:%.*]] = shl nsw i32 [[A]], [[Y:%.*]]
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    [[SHL_DIVIDEND:%.*]] = shl nuw nsw i32 1, [[X:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = lshr i32 [[SHL_DIVIDEND]], [[Y:%.*]]
 ; CHECK-NEXT:    ret i32 [[DIV]]
 ;
 entry:
@@ -1107,9 +1098,8 @@ entry:
 define i32 @udiv_shl_pair1(i32 %a, i32 %x, i32 %y) {
 ; CHECK-LABEL: @udiv_shl_pair1(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[LHS:%.*]] = shl nuw i32 [[A:%.*]], [[X:%.*]]
-; CHECK-NEXT:    [[RHS:%.*]] = shl nuw i32 [[A]], [[Y:%.*]]
-; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    [[SHL_DIVIDEND:%.*]] = shl nuw i32 1, [[X:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = lshr i32 [[SHL_DIVIDEND]], [[Y:%.*]]
 ; CHECK-NEXT:    ret i32 [[DIV]]
 ;
 entry:
@@ -1122,9 +1112,8 @@ entry:
 define i32 @udiv_shl_pair2(i32 %a, i32 %x, i32 %y) {
 ; CHECK-LABEL: @udiv_shl_pair2(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[LHS:%.*]] = shl nuw nsw i32 [[A:%.*]], [[X:%.*]]
-; CHECK-NEXT:    [[RHS:%.*]] = shl nuw i32 [[A]], [[Y:%.*]]
-; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    [[SHL_DIVIDEND:%.*]] = shl nuw nsw i32 1, [[X:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = lshr i32 [[SHL_DIVIDEND]], [[Y:%.*]]
 ; CHECK-NEXT:    ret i32 [[DIV]]
 ;
 entry:
@@ -1137,9 +1126,8 @@ entry:
 define i32 @udiv_shl_pair3(i32 %a, i32 %x, i32 %y) {
 ; CHECK-LABEL: @udiv_shl_pair3(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[LHS:%.*]] = shl nuw i32 [[A:%.*]], [[X:%.*]]
-; CHECK-NEXT:    [[RHS:%.*]] = shl nuw nsw i32 [[A]], [[Y:%.*]]
-; CHECK-NEXT:    [[DIV:%.*]] = udiv i32 [[LHS]], [[RHS]]
+; CHECK-NEXT:    [[SHL_DIVIDEND:%.*]] = shl nuw i32 1, [[X:%.*]]
+; CHECK-NEXT:    [[DIV:%.*]] = lshr i32 [[SHL_DIVIDEND]], [[Y:%.*]]
 ; CHECK-NEXT:    ret i32 [[DIV]]
 ;
 entry:



More information about the llvm-commits mailing list