[llvm] goldsteinn/mul of exact div (PR #96915)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 27 07:51:34 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: None (goldsteinn)

<details>
<summary>Changes</summary>

- **[InstCombine] Add tests for folding `(mul (div exact X, C0), C1)`; NFC**
- **[InstCombine] Fold `(mul (div exact X, C0), C1)` -> `(div exact X, C0/C1)`**


---
Full diff: https://github.com/llvm/llvm-project/pull/96915.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp (+22) 
- (modified) llvm/test/Transforms/InstCombine/exact.ll (+65-11) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index c3f1c12d2f564..aadf836669794 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -365,6 +365,28 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
       return BinaryOperator::CreateMul(NegOp0, X);
   }
 
+  if (Op0->hasOneUse()) {
+    // (mul (div exact X, C0), C1)
+    //    -> (div exact C0 / C1)
+    // iff C0 % C1 == 0 and C0 / C1 doesn't create UB.
+    const APInt *C1;
+    auto UDivCheck = [&C1](const APInt &C) { return C.urem(*C1).isZero(); };
+    auto SDivCheck = [&C1](const APInt &C) {
+      APInt Quot, Rem;
+      APInt::sdivrem(C, *C1, Quot, Rem);
+      return Rem.isZero() && !Quot.isMinSignedValue();
+    };
+    if (match(Op1, m_APInt(C1)) &&
+        (match(Op0, m_Exact(m_UDiv(m_Value(X), m_CheckedInt(UDivCheck)))) ||
+         match(Op0, m_Exact(m_SDiv(m_Value(X), m_CheckedInt(SDivCheck)))))) {
+      auto BOpc = cast<BinaryOperator>(Op0)->getOpcode();
+      return BinaryOperator::CreateExact(
+          BOpc, X,
+          Builder.CreateBinOp(BOpc, cast<BinaryOperator>(Op0)->getOperand(1),
+                              Op1));
+    }
+  }
+
   // (X / Y) *  Y = X - (X % Y)
   // (X / Y) * -Y = (X % Y) - X
   {
diff --git a/llvm/test/Transforms/InstCombine/exact.ll b/llvm/test/Transforms/InstCombine/exact.ll
index 10d46e7b2dfb3..4d2ea7dcd0fa8 100644
--- a/llvm/test/Transforms/InstCombine/exact.ll
+++ b/llvm/test/Transforms/InstCombine/exact.ll
@@ -63,7 +63,7 @@ define i32 @sdiv5(i32 %x) {
 
 define i32 @sdiv6(i32 %x) {
 ; CHECK-LABEL: @sdiv6(
-; CHECK-NEXT:    [[Z:%.*]] = sub i32 0, [[X:%.*]]
+; CHECK-NEXT:    [[Z:%.*]] = sub nsw i32 0, [[X:%.*]]
 ; CHECK-NEXT:    ret i32 [[Z]]
 ;
   %y = sdiv exact i32 %x, 3
@@ -120,7 +120,7 @@ define i1 @ashr_icmp1(i64 %X) {
 ; CHECK-NEXT:    [[B:%.*]] = icmp eq i64 [[X:%.*]], 0
 ; CHECK-NEXT:    ret i1 [[B]]
 ;
-  %A = ashr exact i64 %X, 2   ; X/4
+  %A = ashr exact i64 %X, 2  ; X/4
   %B = icmp eq i64 %A, 0
   ret i1 %B
 }
@@ -131,7 +131,7 @@ define i1 @ashr_icmp2(i64 %X) {
 ; CHECK-NEXT:    ret i1 [[Z]]
 ;
   %Y = ashr exact i64 %X, 2  ; x / 4
-  %Z = icmp slt i64 %Y, 4    ; x < 16
+  %Z = icmp slt i64 %Y, 4  ; x < 16
   ret i1 %Z
 }
 
@@ -181,7 +181,7 @@ define i1 @udiv_icmp1(i64 %X) {
 ; CHECK-NEXT:    [[B:%.*]] = icmp ne i64 [[X:%.*]], 0
 ; CHECK-NEXT:    ret i1 [[B]]
 ;
-  %A = udiv exact i64 %X, 5   ; X/5
+  %A = udiv exact i64 %X, 5  ; X/5
   %B = icmp ne i64 %A, 0
   ret i1 %B
 }
@@ -201,7 +201,7 @@ define i1 @udiv_icmp2(i64 %X) {
 ; CHECK-NEXT:    [[B:%.*]] = icmp eq i64 [[X:%.*]], 0
 ; CHECK-NEXT:    ret i1 [[B]]
 ;
-  %A = udiv exact i64 %X, 5   ; X/5 == 0 --> x == 0
+  %A = udiv exact i64 %X, 5  ; X/5 == 0 --> x == 0
   %B = icmp eq i64 %A, 0
   ret i1 %B
 }
@@ -221,7 +221,7 @@ define i1 @sdiv_icmp1(i64 %X) {
 ; CHECK-NEXT:    [[B:%.*]] = icmp eq i64 [[X:%.*]], 0
 ; CHECK-NEXT:    ret i1 [[B]]
 ;
-  %A = sdiv exact i64 %X, 5   ; X/5 == 0 --> x == 0
+  %A = sdiv exact i64 %X, 5  ; X/5 == 0 --> x == 0
   %B = icmp eq i64 %A, 0
   ret i1 %B
 }
@@ -241,7 +241,7 @@ define i1 @sdiv_icmp2(i64 %X) {
 ; CHECK-NEXT:    [[B:%.*]] = icmp eq i64 [[X:%.*]], 5
 ; CHECK-NEXT:    ret i1 [[B]]
 ;
-  %A = sdiv exact i64 %X, 5   ; X/5 == 1 --> x == 5
+  %A = sdiv exact i64 %X, 5  ; X/5 == 1 --> x == 5
   %B = icmp eq i64 %A, 1
   ret i1 %B
 }
@@ -261,7 +261,7 @@ define i1 @sdiv_icmp3(i64 %X) {
 ; CHECK-NEXT:    [[B:%.*]] = icmp eq i64 [[X:%.*]], -5
 ; CHECK-NEXT:    ret i1 [[B]]
 ;
-  %A = sdiv exact i64 %X, 5   ; X/5 == -1 --> x == -5
+  %A = sdiv exact i64 %X, 5  ; X/5 == -1 --> x == -5
   %B = icmp eq i64 %A, -1
   ret i1 %B
 }
@@ -281,7 +281,7 @@ define i1 @sdiv_icmp4(i64 %X) {
 ; CHECK-NEXT:    [[B:%.*]] = icmp eq i64 [[X:%.*]], 0
 ; CHECK-NEXT:    ret i1 [[B]]
 ;
-  %A = sdiv exact i64 %X, -5   ; X/-5 == 0 --> x == 0
+  %A = sdiv exact i64 %X, -5  ; X/-5 == 0 --> x == 0
   %B = icmp eq i64 %A, 0
   ret i1 %B
 }
@@ -301,7 +301,7 @@ define i1 @sdiv_icmp5(i64 %X) {
 ; CHECK-NEXT:    [[B:%.*]] = icmp eq i64 [[X:%.*]], -5
 ; CHECK-NEXT:    ret i1 [[B]]
 ;
-  %A = sdiv exact i64 %X, -5   ; X/-5 == 1 --> x == -5
+  %A = sdiv exact i64 %X, -5  ; X/-5 == 1 --> x == -5
   %B = icmp eq i64 %A, 1
   ret i1 %B
 }
@@ -321,7 +321,7 @@ define i1 @sdiv_icmp6(i64 %X) {
 ; CHECK-NEXT:    [[B:%.*]] = icmp eq i64 [[X:%.*]], 5
 ; CHECK-NEXT:    ret i1 [[B]]
 ;
-  %A = sdiv exact i64 %X, -5   ; X/-5 == -1 --> x == 5
+  %A = sdiv exact i64 %X, -5  ; X/-5 == -1 --> x == 5
   %B = icmp eq i64 %A, -1
   ret i1 %B
 }
@@ -336,3 +336,57 @@ define <2 x i1> @sdiv_icmp6_vec(<2 x i64> %X) {
   ret <2 x i1> %B
 }
 
+define i8 @mul_of_udiv(i8 %x) {
+; CHECK-LABEL: @mul_of_udiv(
+; CHECK-NEXT:    [[MUL1:%.*]] = lshr exact i8 [[X:%.*]], 1
+; CHECK-NEXT:    ret i8 [[MUL1]]
+;
+  %div = udiv exact i8 %x, 12
+  %mul = mul i8 %div, 6
+  ret i8 %mul
+}
+
+define i8 @mul_of_sdiv(i8 %x) {
+; CHECK-LABEL: @mul_of_sdiv(
+; CHECK-NEXT:    [[MUL_NEG:%.*]] = ashr exact i8 [[X:%.*]], 1
+; CHECK-NEXT:    [[MUL:%.*]] = sub nsw i8 0, [[MUL_NEG]]
+; CHECK-NEXT:    ret i8 [[MUL]]
+;
+  %div = sdiv exact i8 %x, 12
+  %mul = mul i8 %div, -6
+  ret i8 %mul
+}
+
+define i8 @mul_of_sdiv_fail_missing_exact(i8 %x) {
+; CHECK-LABEL: @mul_of_sdiv_fail_missing_exact(
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i8 [[X:%.*]], 12
+; CHECK-NEXT:    [[MUL:%.*]] = mul i8 [[DIV]], -6
+; CHECK-NEXT:    ret i8 [[MUL]]
+;
+  %div = sdiv i8 %x, 12
+  %mul = mul i8 %div, -6
+  ret i8 %mul
+}
+
+define i8 @mul_of_udiv_fail_bad_remainder(i8 %x) {
+; CHECK-LABEL: @mul_of_udiv_fail_bad_remainder(
+; CHECK-NEXT:    [[DIV:%.*]] = udiv exact i8 [[X:%.*]], 11
+; CHECK-NEXT:    [[MUL:%.*]] = mul nuw i8 [[DIV]], 6
+; CHECK-NEXT:    ret i8 [[MUL]]
+;
+  %div = udiv exact i8 %x, 11
+  %mul = mul i8 %div, 6
+  ret i8 %mul
+}
+
+define i8 @mul_of_sdiv_fail_ub(i8 %x) {
+; CHECK-LABEL: @mul_of_sdiv_fail_ub(
+; CHECK-NEXT:    [[X_FR:%.*]] = freeze i8 [[X:%.*]]
+; CHECK-NEXT:    [[TMP1:%.*]] = srem i8 [[X_FR]], 6
+; CHECK-NEXT:    [[MUL:%.*]] = sub i8 [[TMP1]], [[X_FR]]
+; CHECK-NEXT:    ret i8 [[MUL]]
+;
+  %div = sdiv i8 %x, 6
+  %mul = mul i8 %div, -6
+  ret i8 %mul
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/96915


More information about the llvm-commits mailing list