Skip to content

Conversation

@double-fault
Copy link

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

@double-fault double-fault requested a review from nikic as a code owner December 10, 2025 17:14
@double-fault
Copy link
Author

Ping @dtcxzyw for code review (I cant request on github)

@github-actions
Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels Dec 10, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 10, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Ashish Ahuja (double-fault)

Changes

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 --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]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants