[llvm] [InstCombine] Add fold `(x * z) % (y * z) == 0` -> `x % y == 0` (PR #171655)

via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 10 09:38:53 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Ashish Ahuja (double-fault)

<details>
<summary>Changes</summary>

Alive2: https://alive2.llvm.org/ce/z/ylAxft

Partially implements #<!-- -->76585. Does not implement the case mentioned in the first comment on the issue, i.e when we have constants with common factors - this patch already got a bit long with the tests so I think it'll be better to do that as a separate patch.

Few things to note:

 - This folds both `icmp eq` and `icmp ne`, but I have only added trivial tests for `icmp ne` as otherwise each test would get duplicated which I think is unnecessary.
 - Does not fold when the comparison constant is a zero vector with some poison values; further up the call chain the constant is matched with `m_APInt` which I think does not match such a vector, and supporting such folds would require a lot more code changes.

Thanks


---

Patch is 21.34 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/171655.diff


3 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp (+59) 
- (modified) llvm/lib/Transforms/InstCombine/InstCombineInternal.h (+2) 
- (added) llvm/test/Transforms/InstCombine/icmp-rem.ll (+525) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index abf4381ebd794..729fa86c5a801 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -2735,6 +2735,61 @@ Instruction *InstCombinerImpl::foldICmpSRemConstant(ICmpInst &Cmp,
   return new ICmpInst(ICmpInst::ICMP_UGT, And, ConstantInt::get(Ty, SignMask));
 }
 
+/// Fold icmp {eq, ne} ({us}rem (mul n{us}w XZ, YZ)), 0 ->
+/// icmp {eq, ne} ({us}rem (mul n{us}w X, Y)), 0
+Instruction *InstCombinerImpl::foldICmpRemConstant(ICmpInst &Cmp,
+                                                   BinaryOperator *Rem,
+                                                   const APInt &C) {
+  assert((Rem->getOpcode() == Instruction::SRem ||
+          Rem->getOpcode() == Instruction::URem) &&
+         "foldICmpRemConstant is only for srem/urem.");
+
+  if (!C.isZero() || !Rem->hasOneUse())
+    return nullptr;
+
+  const ICmpInst::Predicate Pred = Cmp.getPredicate();
+  if (Pred != ICmpInst::ICMP_EQ && Pred != ICmpInst::ICMP_NE)
+    return nullptr;
+
+  Value *Dividend = Rem->getOperand(0);
+  Value *Divisor = Rem->getOperand(1);
+
+  Value *X, *Y, *Z;
+  Value *NewRem;
+  if (Rem->getOpcode() == Instruction::SRem) {
+    if (!match(Dividend, m_NSWMul(m_Value(X), m_Value(Z))))
+      return nullptr;
+
+    // m_c_NSWMul does not exist
+    if (!match(Divisor, m_NSWMul(m_Value(Y), m_Specific(Z))) &&
+        !match(Divisor, m_NSWMul(m_Specific(Z), m_Value(Y)))) {
+      std::swap(X, Z);
+      if (!match(Divisor, m_NSWMul(m_Value(Y), m_Specific(Z))) &&
+          !match(Divisor, m_NSWMul(m_Specific(Z), m_Value(Y))))
+        return nullptr;
+    }
+
+    NewRem = Builder.CreateSRem(X, Y);
+  } else if (Rem->getOpcode() == Instruction::URem) {
+    if (!match(Dividend, m_NUWMul(m_Value(X), m_Value(Z))))
+      return nullptr;
+
+    // m_c_NUWMul does not exist
+    if (!match(Divisor, m_NUWMul(m_Value(Y), m_Specific(Z))) &&
+        !match(Divisor, m_NUWMul(m_Specific(Z), m_Value(Y)))) {
+      std::swap(X, Z);
+      if (!match(Divisor, m_NUWMul(m_Value(Y), m_Specific(Z))) &&
+          !match(Divisor, m_NUWMul(m_Specific(Z), m_Value(Y))))
+        return nullptr;
+    }
+
+    NewRem = Builder.CreateURem(X, Y);
+  }
+
+  Type *Ty = Rem->getType();
+  return new ICmpInst(Pred, NewRem, ConstantInt::getNullValue(Ty));
+}
+
 /// Fold icmp (udiv X, Y), C.
 Instruction *InstCombinerImpl::foldICmpUDivConstant(ICmpInst &Cmp,
                                                     BinaryOperator *UDiv,
@@ -4009,6 +4064,10 @@ Instruction *InstCombinerImpl::foldICmpBinOpWithConstant(ICmpInst &Cmp,
   case Instruction::SRem:
     if (Instruction *I = foldICmpSRemConstant(Cmp, BO, C))
       return I;
+    [[fallthrough]];
+  case Instruction::URem:
+    if (Instruction *I = foldICmpRemConstant(Cmp, BO, C))
+      return I;
     break;
   case Instruction::UDiv:
     if (Instruction *I = foldICmpUDivConstant(Cmp, BO, C))
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 9bdd8cb71f7f3..de6d2ec3ac356 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -758,6 +758,8 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
                                    const APInt &C);
   Instruction *foldICmpSRemConstant(ICmpInst &Cmp, BinaryOperator *UDiv,
                                     const APInt &C);
+  Instruction *foldICmpRemConstant(ICmpInst &Cmp, BinaryOperator *Rem,
+                                   const APInt &C);
   Instruction *foldICmpUDivConstant(ICmpInst &Cmp, BinaryOperator *UDiv,
                                     const APInt &C);
   Instruction *foldICmpDivConstant(ICmpInst &Cmp, BinaryOperator *Div,
diff --git a/llvm/test/Transforms/InstCombine/icmp-rem.ll b/llvm/test/Transforms/InstCombine/icmp-rem.ll
new file mode 100644
index 0000000000000..6e1fbb89ad0ca
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-rem.ll
@@ -0,0 +1,525 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @use(i8)
+
+define i1 @icmp_eq_srem_mul(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[TMP1]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %x, %z
+  %b = mul nsw i8 %y, %z
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_srem_mul_commuted1(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_commuted1(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[TMP1]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %z, %x
+  %b = mul nsw i8 %y, %z
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_srem_mul_commuted2(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_commuted2(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[TMP1]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %x, %z
+  %b = mul nsw i8 %z, %y
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_srem_mul_commuted3(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_commuted3(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[TMP1]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %z, %x
+  %b = mul nsw i8 %z, %y
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_srem_mul_multi_use(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_multi_use(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nsw i8 [[X]], [[Z]]
+; CHECK-NEXT:    call void @use(i8 [[A]])
+; CHECK-NEXT:    [[B:%.*]] = mul nsw i8 [[Y]], [[Z]]
+; CHECK-NEXT:    call void @use(i8 [[B]])
+; CHECK-NEXT:    [[R:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %x, %z
+  call void @use(i8 %a)
+  %b = mul nsw i8 %y, %z
+  call void @use(i8 %b)
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_ne_srem_mul(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_ne_srem_mul(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ne i8 [[TMP1]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %x, %z
+  %b = mul nsw i8 %y, %z
+  %r = srem i8 %a, %b
+  %c = icmp ne i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_srem_mul_const1(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_const1(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %x, -2
+  %b = mul nsw i8 %y, -2
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_srem_mul_const2(i8 %x, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_const2(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i8 [[X]], 3
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %x, %z
+  %b = mul nsw i8 %z, -3
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_srem_mul_const3(i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_const3(
+; CHECK-SAME: i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem i8 -3, [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %z, -3
+  %b = mul nsw i8 %y, %z
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define <2 x i1> @icmp_eq_srem_mul_vec_splat(<2 x i8> %x, <2 x i8> %y, <2 x i8> %z) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_srem_mul_vec_splat(
+; CHECK-SAME: <2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> [[Z:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = srem <2 x i8> [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq <2 x i8> [[R]], zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[C]]
+;
+  %a = mul nsw <2 x i8> %x, %z
+  %b = mul nsw <2 x i8> %y, %z
+  %r = srem <2 x i8> %a, %b
+  %c = icmp eq <2 x i8> %r, zeroinitializer
+  ret <2 x i1> %c
+}
+
+define <2 x i1> @icmp_eq_srem_mul_vec_splat_poison(<2 x i8> %x, <2 x i8> %y, <2 x i8> %z) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_srem_mul_vec_splat_poison(
+; CHECK-SAME: <2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nsw <2 x i8> [[X]], [[Z]]
+; CHECK-NEXT:    [[B:%.*]] = mul nsw <2 x i8> [[Y]], [[Z]]
+; CHECK-NEXT:    [[R:%.*]] = srem <2 x i8> [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq <2 x i8> [[R]], <i8 0, i8 poison>
+; CHECK-NEXT:    ret <2 x i1> [[C]]
+;
+  %a = mul nsw <2 x i8> %x, %z
+  %b = mul nsw <2 x i8> %y, %z
+  %r = srem <2 x i8> %a, %b
+  %c = icmp eq <2 x i8> %r, <i8 0, i8 poison>
+  ret <2 x i1> %c
+}
+
+define <2 x i1> @icmp_eq_srem_mul_vec_non_splat(<2 x i8> %x, <2 x i8> %y, <2 x i8> %z) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_srem_mul_vec_non_splat(
+; CHECK-SAME: <2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nsw <2 x i8> [[X]], [[Z]]
+; CHECK-NEXT:    [[B:%.*]] = mul nsw <2 x i8> [[Y]], [[Z]]
+; CHECK-NEXT:    [[R:%.*]] = srem <2 x i8> [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq <2 x i8> [[R]], <i8 0, i8 -1>
+; CHECK-NEXT:    ret <2 x i1> [[C]]
+;
+  %a = mul nsw <2 x i8> %x, %z
+  %b = mul nsw <2 x i8> %y, %z
+  %r = srem <2 x i8> %a, %b
+  %c = icmp eq <2 x i8> %r, <i8 0, i8 -1>
+  ret <2 x i1> %c
+}
+
+define i1 @icmp_eq_srem_mul_negative_flags1(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_negative_flags1(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul i8 [[X]], [[Z]]
+; CHECK-NEXT:    [[B:%.*]] = mul nsw i8 [[Y]], [[Z]]
+; CHECK-NEXT:    [[R:%.*]] = srem i8 [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul i8 %x, %z
+  %b = mul nsw i8 %y, %z
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_srem_mul_negative_flags2(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_negative_flags2(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nsw i8 [[X]], [[Z]]
+; CHECK-NEXT:    [[B:%.*]] = mul i8 [[Y]], [[Z]]
+; CHECK-NEXT:    [[R:%.*]] = srem i8 [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %x, %z
+  %b = mul i8 %y, %z
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_srem_mul_negative_common_op(i8 %x, i8 %y, i8 %u, i8 %v) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_negative_common_op(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[U:%.*]], i8 [[V:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nsw i8 [[X]], [[U]]
+; CHECK-NEXT:    [[B:%.*]] = mul nsw i8 [[Y]], [[V]]
+; CHECK-NEXT:    [[R:%.*]] = srem i8 [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %x, %u
+  %b = mul nsw i8 %y, %v
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_srem_mul_negative_multi_use(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_negative_multi_use(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nsw i8 [[X]], [[Z]]
+; CHECK-NEXT:    [[B:%.*]] = mul nsw i8 [[Y]], [[Z]]
+; CHECK-NEXT:    [[R:%.*]] = srem i8 [[A]], [[B]]
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %x, %z
+  %b = mul nsw i8 %y, %z
+  %r = srem i8 %a, %b
+  call void @use(i8 %r)
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_srem_mul_negative_wrong_constant(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_mul_negative_wrong_constant(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nsw i8 [[X]], [[Z]]
+; CHECK-NEXT:    [[B:%.*]] = mul nsw i8 [[Y]], [[Z]]
+; CHECK-NEXT:    [[R:%.*]] = srem i8 [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], -1
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nsw i8 %x, %z
+  %b = mul nsw i8 %y, %z
+  %r = srem i8 %a, %b
+  %c = icmp eq i8 %r, -1
+  ret i1 %c
+}
+
+define i1 @icmp_eq_urem_mul(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_urem_mul(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nuw i8 %x, %z
+  %b = mul nuw i8 %y, %z
+  %r = urem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_urem_mul_commuted1(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_urem_mul_commuted1(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nuw i8 %z, %x
+  %b = mul nuw i8 %y, %z
+  %r = urem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_urem_mul_commuted2(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_urem_mul_commuted2(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nuw i8 %x, %z
+  %b = mul nuw i8 %z, %y
+  %r = urem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_urem_mul_commuted3(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_urem_mul_commuted3(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nuw i8 %z, %x
+  %b = mul nuw i8 %z, %y
+  %r = urem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_urem_mul_multi_use(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_urem_mul_multi_use(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nuw i8 [[X]], [[Z]]
+; CHECK-NEXT:    call void @use(i8 [[A]])
+; CHECK-NEXT:    [[B:%.*]] = mul nuw i8 [[Y]], [[Z]]
+; CHECK-NEXT:    call void @use(i8 [[B]])
+; CHECK-NEXT:    [[R:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nuw i8 %x, %z
+  call void @use(i8 %a)
+  %b = mul nuw i8 %y, %z
+  call void @use(i8 %b)
+  %r = urem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_ne_urem_mul(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_ne_urem_mul(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp ne i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nuw i8 %x, %z
+  %b = mul nuw i8 %y, %z
+  %r = urem i8 %a, %b
+  %c = icmp ne i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_urem_mul_const1(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_urem_mul_const1(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nuw i8 %x, 3
+  %b = mul nuw i8 %y, 3
+  %r = urem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_urem_mul_const2(i8 %x, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_urem_mul_const2(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = urem i8 [[X]], 3
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nuw i8 %x, %z
+  %b = mul nuw i8 %z, 3
+  %r = urem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_urem_mul_const3(i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_urem_mul_const3(
+; CHECK-SAME: i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = urem i8 3, [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nuw i8 %z, 3
+  %b = mul nuw i8 %y, %z
+  %r = urem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define <2 x i1> @icmp_eq_urem_mul_vec_splat(<2 x i8> %x, <2 x i8> %y, <2 x i8> %z) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_urem_mul_vec_splat(
+; CHECK-SAME: <2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> [[Z:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = urem <2 x i8> [[X]], [[Y]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq <2 x i8> [[R]], zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[C]]
+;
+  %a = mul nuw <2 x i8> %x, %z
+  %b = mul nuw <2 x i8> %y, %z
+  %r = urem <2 x i8> %a, %b
+  %c = icmp eq <2 x i8> %r, zeroinitializer
+  ret <2 x i1> %c
+}
+
+define <2 x i1> @icmp_eq_urem_mul_vec_splat_poison(<2 x i8> %x, <2 x i8> %y, <2 x i8> %z) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_urem_mul_vec_splat_poison(
+; CHECK-SAME: <2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nuw <2 x i8> [[X]], [[Z]]
+; CHECK-NEXT:    [[B:%.*]] = mul nuw <2 x i8> [[Y]], [[Z]]
+; CHECK-NEXT:    [[R:%.*]] = urem <2 x i8> [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq <2 x i8> [[R]], <i8 0, i8 poison>
+; CHECK-NEXT:    ret <2 x i1> [[C]]
+;
+  %a = mul nuw <2 x i8> %x, %z
+  %b = mul nuw <2 x i8> %y, %z
+  %r = urem <2 x i8> %a, %b
+  %c = icmp eq <2 x i8> %r, <i8 0, i8 poison>
+  ret <2 x i1> %c
+}
+
+define <2 x i1> @icmp_eq_urem_mul_vec_non_splat(<2 x i8> %x, <2 x i8> %y, <2 x i8> %z) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_urem_mul_vec_non_splat(
+; CHECK-SAME: <2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nuw <2 x i8> [[X]], [[Z]]
+; CHECK-NEXT:    [[B:%.*]] = mul nuw <2 x i8> [[Y]], [[Z]]
+; CHECK-NEXT:    [[R:%.*]] = urem <2 x i8> [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq <2 x i8> [[R]], <i8 0, i8 1>
+; CHECK-NEXT:    ret <2 x i1> [[C]]
+;
+  %a = mul nuw <2 x i8> %x, %z
+  %b = mul nuw <2 x i8> %y, %z
+  %r = urem <2 x i8> %a, %b
+  %c = icmp eq <2 x i8> %r, <i8 0, i8 1>
+  ret <2 x i1> %c
+}
+
+define i1 @icmp_eq_urem_mul_negative_flags1(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_urem_mul_negative_flags1(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul i8 [[X]], [[Z]]
+; CHECK-NEXT:    [[B:%.*]] = mul nuw i8 [[Y]], [[Z]]
+; CHECK-NEXT:    [[R:%.*]] = urem i8 [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul i8 %x, %z
+  %b = mul nuw i8 %y, %z
+  %r = urem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_urem_mul_negative_flags2(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: define i1 @icmp_eq_urem_mul_negative_flags2(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nuw i8 [[X]], [[Z]]
+; CHECK-NEXT:    [[B:%.*]] = mul i8 [[Y]], [[Z]]
+; CHECK-NEXT:    [[R:%.*]] = urem i8 [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %a = mul nuw i8 %x, %z
+  %b = mul i8 %y, %z
+  %r = urem i8 %a, %b
+  %c = icmp eq i8 %r, 0
+  ret i1 %c
+}
+
+define i1 @icmp_eq_urem_mul_negative_common_op(i8 %x, i8 %y, i8 %u, i8 %v) {
+; CHECK-LABEL: define i1 @icmp_eq_urem_mul_negative_common_op(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[U:%.*]], i8 [[V:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = mul nuw i8 [[X]], [[U]]
+; CHECK-NEXT:    [[B:%.*]] = mul nuw i8 [[Y]], [[V]]
+; CHECK-NEXT:    [[R:%.*]] = urem i8 [[A]], [[B]]
+; CHECK-NEXT:    ...
[truncated]

``````````

</details>


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


More information about the llvm-commits mailing list