-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[InstCombine] Add fold (x * z) % (y * z) == 0 -> x % y == 0
#171655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Ping @dtcxzyw for code review (I cant request on github) |
|
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 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. |
|
@llvm/pr-subscribers-llvm-transforms Author: Ashish Ahuja (double-fault) ChangesAlive2: 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:
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:
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]
|
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:
icmp eqandicmp ne, but I have only added trivial tests foricmp neas otherwise each test would get duplicated which I think is unnecessary.m_APIntwhich I think does not match such a vector, and supporting such folds would require a lot more code changes.Thanks