Skip to content
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

[InstCombine] Simplify the pattern a ne/eq (zext/sext (a ne/eq c)) #65852

Merged
merged 15 commits into from
Oct 6, 2023

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Sep 9, 2023

This patch folds the pattern a ne/eq (zext/sext (a ne/eq c)) into a boolean constant or a compare.
Clang vs GCC: https://godbolt.org/z/4ro817WE8
Proof for zext: https://alive2.llvm.org/ce/z/6z9NRF
Proof for sext: https://alive2.llvm.org/ce/z/tv5wuE
Fixes #65073.

@llvmbot
Copy link
Collaborator

llvmbot commented Sep 18, 2023

@llvm/pr-subscribers-llvm-transforms

Changes

This patch folds the pattern a ne/eq (zext (a ne/eq c)) into a boolean constant or a compare.
Clang vs GCC: https://godbolt.org/z/4ro817WE8
Alive2: https://alive2.llvm.org/ce/z/6z9NRF
Fixes #65073.


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp (+62)
  • (modified) llvm/test/Transforms/InstCombine/icmp-range.ll (+18-58)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index a219dac7acfbe16..d0b62c17ec94358 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -6380,7 +6380,69 @@ Instruction *InstCombinerImpl::foldICmpUsingBoolRange(ICmpInst &I) {
       Y->getType()->isIntOrIntVectorTy(1) && Pred == ICmpInst::ICMP_ULE)
     return BinaryOperator::CreateOr(Builder.CreateIsNull(X), Y);
 
+  ICmpInst::Predicate Pred1, Pred2;
   const APInt *C;
+  // icmp eq/ne X, (zext (icmp eq/ne X, C))
+  if (match(&I, m_c_ICmp(Pred1, m_Value(X),
+                         m_ZExt(m_ICmp(Pred2, m_Deferred(X), m_APInt(C))))) &&
+      ICmpInst::isEquality(Pred1) && ICmpInst::isEquality(Pred2)) {
+    if (C->isZero()) {
+      if (Pred2 == ICmpInst::ICMP_EQ) {
+        // icmp eq X, (zext (icmp eq X, 0)) --> false
+        // icmp ne X, (zext (icmp eq X, 0)) --> true
+        return replaceInstUsesWith(
+            I,
+            Constant::getIntegerValue(
+                I.getType(),
+                APInt(1U, static_cast<uint64_t>(Pred1 == ICmpInst::ICMP_NE))));
+      } else {
+        // icmp eq X, (zext (icmp ne X, 0)) --> icmp ult X, 2
+        // icmp ne X, (zext (icmp ne X, 0)) --> icmp ugt X, 1
+        return ICmpInst::Create(
+            Instruction::ICmp,
+            Pred1 == ICmpInst::ICMP_NE ? ICmpInst::ICMP_UGT
+                                       : ICmpInst::ICMP_ULT,
+            X,
+            Constant::getIntegerValue(
+                X->getType(), APInt(X->getType()->getScalarSizeInBits(),
+                                    Pred1 == ICmpInst::ICMP_NE ? 1 : 2)));
+      }
+    } else if (C->isOne()) {
+      if (Pred2 == ICmpInst::ICMP_NE) {
+        // icmp eq X, (zext (icmp ne X, 1)) --> false
+        // icmp ne X, (zext (icmp ne X, 1)) --> true
+        return replaceInstUsesWith(
+            I,
+            Constant::getIntegerValue(
+                I.getType(),
+                APInt(1U, static_cast<uint64_t>(Pred1 == ICmpInst::ICMP_NE))));
+      } else {
+        // icmp eq X, (zext (icmp eq X, 1)) --> icmp ult X, 2
+        // icmp ne X, (zext (icmp eq X, 1)) --> icmp ugt X, 1
+        return ICmpInst::Create(
+            Instruction::ICmp,
+            Pred1 == ICmpInst::ICMP_NE ? ICmpInst::ICMP_UGT
+                                       : ICmpInst::ICMP_ULT,
+            X,
+            Constant::getIntegerValue(
+                X->getType(), APInt(X->getType()->getScalarSizeInBits(),
+                                    Pred1 == ICmpInst::ICMP_NE ? 1 : 2)));
+      }
+    } else {
+      // C != 0 && C != 1
+      // icmp eq X, (zext (icmp eq X, C)) --> icmp eq X, 0
+      // icmp eq X, (zext (icmp ne X, C)) --> icmp eq X, 1
+      // icmp ne X, (zext (icmp eq X, C)) --> icmp ne X, 0
+      // icmp ne X, (zext (icmp ne X, C)) --> icmp ne X, 1
+      return ICmpInst::Create(
+          Instruction::ICmp, Pred1, X,
+          Constant::getIntegerValue(
+              X->getType(),
+              APInt(X->getType()->getScalarSizeInBits(),
+                    static_cast<uint64_t>(Pred2 == ICmpInst::ICMP_NE))));
+    }
+  }
+
   if (match(I.getOperand(0), m_c_Add(m_ZExt(m_Value(X)), m_SExt(m_Value(Y)))) &&
       match(I.getOperand(1), m_APInt(C)) &&
       X->getType()->isIntOrIntVectorTy(1) &&
diff --git a/llvm/test/Transforms/InstCombine/icmp-range.ll b/llvm/test/Transforms/InstCombine/icmp-range.ll
index a26e760059b43fe..88d5a723747ad5f 100644
--- a/llvm/test/Transforms/InstCombine/icmp-range.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-range.ll
@@ -1037,10 +1037,7 @@ define i1 @icmp_ne_bool_1(ptr %ptr) {
 ; Tests from PR65073
 define i1 @icmp_ne_zext_eq_zero(i32 %a) {
 ; CHECK-LABEL: @icmp_ne_zext_eq_zero(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 0
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[CONV]], [[A]]
-; CHECK-NEXT:    ret i1 [[CMP1]]
+; CHECK-NEXT:    ret i1 true
 ;
   %cmp = icmp eq i32 %a, 0
   %conv = zext i1 %cmp to i32
@@ -1050,9 +1047,7 @@ define i1 @icmp_ne_zext_eq_zero(i32 %a) {
 
 define i1 @icmp_ne_zext_ne_zero(i32 %a) {
 ; CHECK-LABEL: @icmp_ne_zext_ne_zero(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[A:%.*]], 0
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[CONV]], [[A]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ugt i32 [[A:%.*]], 1
 ; CHECK-NEXT:    ret i1 [[CMP1]]
 ;
   %cmp = icmp ne i32 %a, 0
@@ -1063,10 +1058,7 @@ define i1 @icmp_ne_zext_ne_zero(i32 %a) {
 
 define i1 @icmp_eq_zext_eq_zero(i32 %a) {
 ; CHECK-LABEL: @icmp_eq_zext_eq_zero(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 0
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[CONV]], [[A]]
-; CHECK-NEXT:    ret i1 [[CMP1]]
+; CHECK-NEXT:    ret i1 false
 ;
   %cmp = icmp eq i32 %a, 0
   %conv = zext i1 %cmp to i32
@@ -1076,9 +1068,7 @@ define i1 @icmp_eq_zext_eq_zero(i32 %a) {
 
 define i1 @icmp_eq_zext_ne_zero(i32 %a) {
 ; CHECK-LABEL: @icmp_eq_zext_ne_zero(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[A:%.*]], 0
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[CONV]], [[A]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i32 [[A:%.*]], 2
 ; CHECK-NEXT:    ret i1 [[CMP1]]
 ;
   %cmp = icmp ne i32 %a, 0
@@ -1089,9 +1079,7 @@ define i1 @icmp_eq_zext_ne_zero(i32 %a) {
 
 define i1 @icmp_ne_zext_eq_one(i32 %a) {
 ; CHECK-LABEL: @icmp_ne_zext_eq_one(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[CONV]], [[A]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ugt i32 [[A:%.*]], 1
 ; CHECK-NEXT:    ret i1 [[CMP1]]
 ;
   %cmp = icmp eq i32 %a, 1
@@ -1102,10 +1090,7 @@ define i1 @icmp_ne_zext_eq_one(i32 %a) {
 
 define i1 @icmp_ne_zext_ne_one(i32 %a) {
 ; CHECK-LABEL: @icmp_ne_zext_ne_one(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[CONV]], [[A]]
-; CHECK-NEXT:    ret i1 [[CMP1]]
+; CHECK-NEXT:    ret i1 true
 ;
   %cmp = icmp ne i32 %a, 1
   %conv = zext i1 %cmp to i32
@@ -1115,9 +1100,7 @@ define i1 @icmp_ne_zext_ne_one(i32 %a) {
 
 define i1 @icmp_eq_zext_eq_one(i32 %a) {
 ; CHECK-LABEL: @icmp_eq_zext_eq_one(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[CONV]], [[A]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i32 [[A:%.*]], 2
 ; CHECK-NEXT:    ret i1 [[CMP1]]
 ;
   %cmp = icmp eq i32 %a, 1
@@ -1128,10 +1111,7 @@ define i1 @icmp_eq_zext_eq_one(i32 %a) {
 
 define i1 @icmp_eq_zext_ne_one(i32 %a) {
 ; CHECK-LABEL: @icmp_eq_zext_ne_one(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[CONV]], [[A]]
-; CHECK-NEXT:    ret i1 [[CMP1]]
+; CHECK-NEXT:    ret i1 false
 ;
   %cmp = icmp ne i32 %a, 1
   %conv = zext i1 %cmp to i32
@@ -1141,9 +1121,7 @@ define i1 @icmp_eq_zext_ne_one(i32 %a) {
 
 define i1 @icmp_ne_zext_eq_non_boolean(i32 %a) {
 ; CHECK-LABEL: @icmp_ne_zext_eq_non_boolean(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 2
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[CONV]], [[A]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[A:%.*]], 0
 ; CHECK-NEXT:    ret i1 [[CMP1]]
 ;
   %cmp = icmp eq i32 %a, 2
@@ -1154,9 +1132,7 @@ define i1 @icmp_ne_zext_eq_non_boolean(i32 %a) {
 
 define i1 @icmp_ne_zext_ne_non_boolean(i32 %a) {
 ; CHECK-LABEL: @icmp_ne_zext_ne_non_boolean(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[A:%.*]], 2
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[CONV]], [[A]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[A:%.*]], 1
 ; CHECK-NEXT:    ret i1 [[CMP1]]
 ;
   %cmp = icmp ne i32 %a, 2
@@ -1167,9 +1143,7 @@ define i1 @icmp_ne_zext_ne_non_boolean(i32 %a) {
 
 define i1 @icmp_eq_zext_eq_non_boolean(i32 %a) {
 ; CHECK-LABEL: @icmp_eq_zext_eq_non_boolean(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 2
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[CONV]], [[A]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[A:%.*]], 0
 ; CHECK-NEXT:    ret i1 [[CMP1]]
 ;
   %cmp = icmp eq i32 %a, 2
@@ -1180,9 +1154,7 @@ define i1 @icmp_eq_zext_eq_non_boolean(i32 %a) {
 
 define i1 @icmp_eq_zext_ne_non_boolean(i32 %a) {
 ; CHECK-LABEL: @icmp_eq_zext_ne_non_boolean(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[A:%.*]], 2
-; CHECK-NEXT:    [[CONV:%.*]] = zext i1 [[CMP]] to i32
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[CONV]], [[A]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[A:%.*]], 1
 ; CHECK-NEXT:    ret i1 [[CMP1]]
 ;
   %cmp = icmp ne i32 %a, 2
@@ -1192,11 +1164,8 @@ define i1 @icmp_eq_zext_ne_non_boolean(i32 %a) {
 }
 
 define <2 x i1> @icmp_ne_zext_eq_zero_vec(<2 x i32> %a) {
-; CHECK-LABEL: @icmp_ne_zext_eq_zero_vec(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], zeroinitializer
-; CHECK-NEXT:    [[CONV:%.*]] = zext <2 x i1> [[CMP]] to <2 x i32>
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne <2 x i32> [[CONV]], [[A]]
-; CHECK-NEXT:    ret <2 x i1> [[CMP1]]
+; CHECK-LABEL: @icmp_ne_zext_eq_zero_vec
+; CHECK-NEXT:    ret <2 x i1> <i1 true, i1 true>
 ;
   %cmp = icmp eq <2 x i32> %a, <i32 0, i32 0>
   %conv = zext <2 x i1> %cmp to <2 x i32>
@@ -1206,9 +1175,7 @@ define <2 x i1> @icmp_ne_zext_eq_zero_vec(<2 x i32> %a) {
 
 define <2 x i1> @icmp_ne_zext_ne_zero_vec(<2 x i32> %a) {
 ; CHECK-LABEL: @icmp_ne_zext_ne_zero_vec(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne <2 x i32> [[A:%.*]], zeroinitializer
-; CHECK-NEXT:    [[CONV:%.*]] = zext <2 x i1> [[CMP]] to <2 x i32>
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne <2 x i32> [[CONV]], [[A]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ugt <2 x i32> [[A:%.*]], <i32 1, i32 1>
 ; CHECK-NEXT:    ret <2 x i1> [[CMP1]]
 ;
   %cmp = icmp ne <2 x i32> %a, <i32 0, i32 0>
@@ -1219,9 +1186,7 @@ define <2 x i1> @icmp_ne_zext_ne_zero_vec(<2 x i32> %a) {
 
 define <2 x i1> @icmp_ne_zext_eq_one_vec(<2 x i32> %a) {
 ; CHECK-LABEL: @icmp_ne_zext_eq_one_vec(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], <i32 1, i32 1>
-; CHECK-NEXT:    [[CONV:%.*]] = zext <2 x i1> [[CMP]] to <2 x i32>
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne <2 x i32> [[CONV]], [[A]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ugt <2 x i32> [[A:%.*]], <i32 1, i32 1>
 ; CHECK-NEXT:    ret <2 x i1> [[CMP1]]
 ;
   %cmp = icmp eq <2 x i32> %a, <i32 1, i32 1>
@@ -1232,10 +1197,7 @@ define <2 x i1> @icmp_ne_zext_eq_one_vec(<2 x i32> %a) {
 
 define <2 x i1> @icmp_ne_zext_ne_one_vec(<2 x i32> %a) {
 ; CHECK-LABEL: @icmp_ne_zext_ne_one_vec(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne <2 x i32> [[A:%.*]], <i32 1, i32 1>
-; CHECK-NEXT:    [[CONV:%.*]] = zext <2 x i1> [[CMP]] to <2 x i32>
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne <2 x i32> [[CONV]], [[A]]
-; CHECK-NEXT:    ret <2 x i1> [[CMP1]]
+; CHECK-NEXT:    ret <2 x i1> <i1 true, i1 true>
 ;
   %cmp = icmp ne <2 x i32> %a, <i32 1, i32 1>
   %conv = zext <2 x i1> %cmp to <2 x i32>
@@ -1245,9 +1207,7 @@ define <2 x i1> @icmp_ne_zext_ne_one_vec(<2 x i32> %a) {
 
 define <2 x i1> @icmp_ne_zext_eq_non_boolean_vec(<2 x i32> %a) {
 ; CHECK-LABEL: @icmp_ne_zext_eq_non_boolean_vec(
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], <i32 2, i32 2>
-; CHECK-NEXT:    [[CONV:%.*]] = zext <2 x i1> [[CMP]] to <2 x i32>
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne <2 x i32> [[CONV]], [[A]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne <2 x i32> [[A:%.*]], zeroinitializer
 ; CHECK-NEXT:    ret <2 x i1> [[CMP1]]
 ;
   %cmp = icmp eq <2 x i32> %a, <i32 2, i32 2>

@dtcxzyw dtcxzyw removed the request for review from spatel-gh September 18, 2023 17:57
@dtcxzyw dtcxzyw changed the title [InstCombine] Simplify the pattern a ne/eq (zext (a ne/eq c)) [InstCombine] Simplify the pattern a ne/eq (zext/sext (a ne/eq c)) Sep 18, 2023
@goldsteinn
Copy link
Contributor

LGTM.

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Sep 30, 2023

Ping.

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Oct 5, 2023

Ping.

@goldsteinn
Copy link
Contributor

Continue to LGTM...

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically LGTM, but I think this is still missing negative tests for non-equality pred1/pred2?

@nikic
Copy link
Contributor

nikic commented Oct 6, 2023

Looks like incorrect conflict resolution in the last merge.

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Oct 6, 2023

The conflict resolution should be correct now.

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it looks correct now.

@dtcxzyw dtcxzyw merged commit b3b3336 into llvm:main Oct 6, 2023
2 of 3 checks passed
@dtcxzyw dtcxzyw deleted the fold-icmp-eq-zext-eq-self branch October 6, 2023 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

a ne/eq (a ne/eq 0) pattern is not simplified
5 participants