[llvm] [InstCombine] Fold `select (a == V1 | a == V2), a, V2` to `select (a == V1), V1, V2` (PR #76203)

via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 21 21:01:34 PST 2023


https://github.com/XChy created https://github.com/llvm/llvm-project/pull/76203

Fixes #75784
Alive2 proof: https://alive2.llvm.org/ce/z/PNBSjJ

Note: when `V1 == undef` and `a == V2`, such transform is not valid: https://alive2.llvm.org/ce/z/BDT7Q9

>From 763ca8a665328a772e88f2fbc4d11a041f7c6a2d Mon Sep 17 00:00:00 2001
From: XChy <xxs_chy at outlook.com>
Date: Fri, 22 Dec 2023 01:18:57 +0800
Subject: [PATCH 1/2] [InstCombine][NFC] Precommit tests for PR75784

---
 .../Transforms/InstCombine/select-and-or.ll   | 208 ++++++++++++++++--
 1 file changed, 184 insertions(+), 24 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/select-and-or.ll b/llvm/test/Transforms/InstCombine/select-and-or.ll
index 7edcd767b86ecb..d30b543945216d 100644
--- a/llvm/test/Transforms/InstCombine/select-and-or.ll
+++ b/llvm/test/Transforms/InstCombine/select-and-or.ll
@@ -613,9 +613,9 @@ define i1 @and_or2_wrong_operand(i1 %a, i1 %b, i1 %c, i1 %d) {
 
 define i1 @and_or3(i1 %a, i1 %b, i32 %x, i32 %y) {
 ; CHECK-LABEL: @and_or3(
-; CHECK-NEXT:    [[C:%.*]] = icmp ne i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[C]], i1 true, i1 [[A:%.*]]
-; CHECK-NEXT:    [[R:%.*]] = select i1 [[B:%.*]], i1 [[TMP1]], i1 false
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 true, i1 [[A:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = select i1 [[B:%.*]], i1 [[TMP2]], i1 false
 ; CHECK-NEXT:    ret i1 [[R]]
 ;
   %c = icmp eq i32 %x, %y
@@ -626,9 +626,9 @@ define i1 @and_or3(i1 %a, i1 %b, i32 %x, i32 %y) {
 
 define i1 @and_or3_commuted(i1 %a, i1 %b, i32 %x, i32 %y) {
 ; CHECK-LABEL: @and_or3_commuted(
-; CHECK-NEXT:    [[C:%.*]] = icmp ne i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[C]], i1 true, i1 [[A:%.*]]
-; CHECK-NEXT:    [[R:%.*]] = select i1 [[B:%.*]], i1 [[TMP1]], i1 false
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 true, i1 [[A:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = select i1 [[B:%.*]], i1 [[TMP2]], i1 false
 ; CHECK-NEXT:    ret i1 [[R]]
 ;
   %c = icmp eq i32 %x, %y
@@ -665,9 +665,9 @@ define i1 @and_or3_multiuse(i1 %a, i1 %b, i32 %x, i32 %y) {
 
 define <2 x i1> @and_or3_vec(<2 x i1> %a, <2 x i1> %b, <2 x i32> %x, <2 x i32> %y) {
 ; CHECK-LABEL: @and_or3_vec(
-; CHECK-NEXT:    [[C:%.*]] = icmp ne <2 x i32> [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[TMP1:%.*]] = select <2 x i1> [[C]], <2 x i1> <i1 true, i1 true>, <2 x i1> [[A:%.*]]
-; CHECK-NEXT:    [[R:%.*]] = select <2 x i1> [[B:%.*]], <2 x i1> [[TMP1]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne <2 x i32> [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[TMP2:%.*]] = select <2 x i1> [[TMP1]], <2 x i1> <i1 true, i1 true>, <2 x i1> [[A:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = select <2 x i1> [[B:%.*]], <2 x i1> [[TMP2]], <2 x i1> zeroinitializer
 ; CHECK-NEXT:    ret <2 x i1> [[R]]
 ;
   %c = icmp eq <2 x i32> %x, %y
@@ -678,9 +678,9 @@ define <2 x i1> @and_or3_vec(<2 x i1> %a, <2 x i1> %b, <2 x i32> %x, <2 x i32> %
 
 define <2 x i1> @and_or3_vec_commuted(<2 x i1> %a, <2 x i1> %b, <2 x i32> %x, <2 x i32> %y) {
 ; CHECK-LABEL: @and_or3_vec_commuted(
-; CHECK-NEXT:    [[C:%.*]] = icmp ne <2 x i32> [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[TMP1:%.*]] = select <2 x i1> [[C]], <2 x i1> <i1 true, i1 true>, <2 x i1> [[A:%.*]]
-; CHECK-NEXT:    [[R:%.*]] = select <2 x i1> [[B:%.*]], <2 x i1> [[TMP1]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne <2 x i32> [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[TMP2:%.*]] = select <2 x i1> [[TMP1]], <2 x i1> <i1 true, i1 true>, <2 x i1> [[A:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = select <2 x i1> [[B:%.*]], <2 x i1> [[TMP2]], <2 x i1> zeroinitializer
 ; CHECK-NEXT:    ret <2 x i1> [[R]]
 ;
   %c = icmp eq <2 x i32> %x, %y
@@ -877,9 +877,9 @@ entry:
 
 define i1 @or_and3(i1 %a, i1 %b, i32 %x, i32 %y) {
 ; CHECK-LABEL: @or_and3(
-; CHECK-NEXT:    [[C:%.*]] = icmp ne i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[C]], i1 [[B:%.*]], i1 false
-; CHECK-NEXT:    [[R:%.*]] = select i1 [[A:%.*]], i1 true, i1 [[TMP1]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 [[B:%.*]], i1 false
+; CHECK-NEXT:    [[R:%.*]] = select i1 [[A:%.*]], i1 true, i1 [[TMP2]]
 ; CHECK-NEXT:    ret i1 [[R]]
 ;
   %c = icmp eq i32 %x, %y
@@ -890,9 +890,9 @@ define i1 @or_and3(i1 %a, i1 %b, i32 %x, i32 %y) {
 
 define i1 @or_and3_commuted(i1 %a, i1 %b, i32 %x, i32 %y) {
 ; CHECK-LABEL: @or_and3_commuted(
-; CHECK-NEXT:    [[C:%.*]] = icmp ne i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[C]], i1 [[B:%.*]], i1 false
-; CHECK-NEXT:    [[R:%.*]] = select i1 [[A:%.*]], i1 true, i1 [[TMP1]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[TMP2:%.*]] = select i1 [[TMP1]], i1 [[B:%.*]], i1 false
+; CHECK-NEXT:    [[R:%.*]] = select i1 [[A:%.*]], i1 true, i1 [[TMP2]]
 ; CHECK-NEXT:    ret i1 [[R]]
 ;
   %c = icmp eq i32 %x, %y
@@ -929,9 +929,9 @@ define i1 @or_and3_multiuse(i1 %a, i1 %b, i32 %x, i32 %y) {
 
 define <2 x i1> @or_and3_vec(<2 x i1> %a, <2 x i1> %b, <2 x i32> %x, <2 x i32> %y) {
 ; CHECK-LABEL: @or_and3_vec(
-; CHECK-NEXT:    [[C:%.*]] = icmp ne <2 x i32> [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[TMP1:%.*]] = select <2 x i1> [[C]], <2 x i1> [[B:%.*]], <2 x i1> zeroinitializer
-; CHECK-NEXT:    [[R:%.*]] = select <2 x i1> [[A:%.*]], <2 x i1> <i1 true, i1 true>, <2 x i1> [[TMP1]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne <2 x i32> [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[TMP2:%.*]] = select <2 x i1> [[TMP1]], <2 x i1> [[B:%.*]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    [[R:%.*]] = select <2 x i1> [[A:%.*]], <2 x i1> <i1 true, i1 true>, <2 x i1> [[TMP2]]
 ; CHECK-NEXT:    ret <2 x i1> [[R]]
 ;
   %c = icmp eq <2 x i32> %x, %y
@@ -942,9 +942,9 @@ define <2 x i1> @or_and3_vec(<2 x i1> %a, <2 x i1> %b, <2 x i32> %x, <2 x i32> %
 
 define <2 x i1> @or_and3_vec_commuted(<2 x i1> %a, <2 x i1> %b, <2 x i32> %x, <2 x i32> %y) {
 ; CHECK-LABEL: @or_and3_vec_commuted(
-; CHECK-NEXT:    [[C:%.*]] = icmp ne <2 x i32> [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[TMP1:%.*]] = select <2 x i1> [[C]], <2 x i1> [[B:%.*]], <2 x i1> zeroinitializer
-; CHECK-NEXT:    [[R:%.*]] = select <2 x i1> [[A:%.*]], <2 x i1> <i1 true, i1 true>, <2 x i1> [[TMP1]]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne <2 x i32> [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[TMP2:%.*]] = select <2 x i1> [[TMP1]], <2 x i1> [[B:%.*]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    [[R:%.*]] = select <2 x i1> [[A:%.*]], <2 x i1> <i1 true, i1 true>, <2 x i1> [[TMP2]]
 ; CHECK-NEXT:    ret <2 x i1> [[R]]
 ;
   %c = icmp eq <2 x i32> %x, %y
@@ -965,3 +965,163 @@ define i1 @or_and3_wrong_operand(i1 %a, i1 %b, i32 %x, i32 %y, i1 %d) {
   %r = select i1 %cond, i1 %d, i1 %b
   ret i1 %r
 }
+
+define i32 @and_eq_v1(i32 %a, i32 noundef %v1, i32 %v2)  {
+; CHECK-LABEL: @and_eq_v1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[V1:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[A]], [[V2:%.*]]
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[A]], i32 [[V2]]
+; CHECK-NEXT:    ret i32 [[SELECT]]
+;
+entry:
+  %cmp = icmp eq i32 %a, %v1
+  %cmp1 = icmp eq i32 %a, %v2
+  %cond = or i1 %cmp, %cmp1
+  %select = select i1 %cond, i32 %a, i32 %v2
+  ret i32 %select
+}
+
+define i32 @and_eq_v2(i32 %a, i32 %v1, i32 noundef %v2)  {
+; CHECK-LABEL: @and_eq_v2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[V1:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[A]], [[V2:%.*]]
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[A]], i32 [[V1]]
+; CHECK-NEXT:    ret i32 [[SELECT]]
+;
+entry:
+  %cmp = icmp eq i32 %a, %v1
+  %cmp1 = icmp eq i32 %a, %v2
+  %cond = or i1 %cmp, %cmp1
+  %select = select i1 %cond, i32 %a, i32 %v1
+  ret i32 %select
+}
+
+define i32 @and_ne_v1(i32 %a, i32 noundef %v1, i32 %v2)  {
+; CHECK-LABEL: @and_ne_v1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[A:%.*]], [[V1:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[A]], [[V2:%.*]]
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[V2]], i32 [[A]]
+; CHECK-NEXT:    ret i32 [[SELECT]]
+;
+entry:
+  %cmp = icmp ne i32 %a, %v1
+  %cmp1 = icmp ne i32 %a, %v2
+  %cond = and i1 %cmp, %cmp1
+  %select = select i1 %cond, i32 %v2, i32 %a
+  ret i32 %select
+}
+
+define i32 @and_ne_v2(i32 %a, i32 %v1, i32 noundef %v2)  {
+; CHECK-LABEL: @and_ne_v2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[A:%.*]], [[V1:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[A]], [[V2:%.*]]
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[V1]], i32 [[A]]
+; CHECK-NEXT:    ret i32 [[SELECT]]
+;
+entry:
+  %cmp = icmp ne i32 %a, %v1
+  %cmp1 = icmp ne i32 %a, %v2
+  %cond = and i1 %cmp, %cmp1
+  %select = select i1 %cond, i32 %v1, i32 %a
+  ret i32 %select
+}
+
+define i32 @and_eq_v1_multi_use(i32 %a, i32 noundef %v1, i32 %v2)  {
+; CHECK-LABEL: @and_eq_v1_multi_use(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[V1:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[A]], [[V2:%.*]]
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    call void @use(i1 [[CMP]])
+; CHECK-NEXT:    call void @use(i1 [[CMP1]])
+; CHECK-NEXT:    call void @use(i1 [[COND]])
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[A]], i32 [[V2]]
+; CHECK-NEXT:    ret i32 [[SELECT]]
+;
+entry:
+  %cmp = icmp eq i32 %a, %v1
+  %cmp1 = icmp eq i32 %a, %v2
+  %cond = or i1 %cmp, %cmp1
+  call void @use(i1 %cmp)
+  call void @use(i1 %cmp1)
+  call void @use(i1 %cond)
+  %select = select i1 %cond, i32 %a, i32 %v2
+  ret i32 %select
+}
+
+define <2 x i32> @and_eq_v1_vec(<2 x i32> %a, <2 x i32> noundef %v1, <2 x i32> %v2)  {
+; CHECK-LABEL: @and_eq_v1_vec(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], [[V1:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq <2 x i32> [[A]], [[V2:%.*]]
+; CHECK-NEXT:    [[COND:%.*]] = or <2 x i1> [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select <2 x i1> [[COND]], <2 x i32> [[A]], <2 x i32> [[V2]]
+; CHECK-NEXT:    ret <2 x i32> [[SELECT]]
+;
+entry:
+  %cmp = icmp eq <2 x i32> %a, %v1
+  %cmp1 = icmp eq <2 x i32> %a, %v2
+  %cond = or <2 x i1> %cmp, %cmp1
+  %select = select <2 x i1> %cond, <2 x i32> %a, <2 x i32> %v2
+  ret <2 x i32> %select
+}
+
+
+define i32 @and_slt_v1_fail(i32 %a, i32 noundef %v1, i32 noundef %v2)  {
+; CHECK-LABEL: @and_slt_v1_fail(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[A:%.*]], [[V1:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp slt i32 [[A]], [[V2:%.*]]
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[A]], i32 [[V2]]
+; CHECK-NEXT:    ret i32 [[SELECT]]
+;
+entry:
+  %cmp = icmp slt i32 %a, %v1
+  %cmp1 = icmp slt i32 %a, %v2
+  %cond = or i1 %cmp, %cmp1
+  %select = select i1 %cond, i32 %a, i32 %v2
+  ret i32 %select
+}
+
+define i32 @and_ne_different_operands_fail(i32 %a, i32 %b, i32 noundef %v1, i32 noundef %v2)  {
+; CHECK-LABEL: @and_ne_different_operands_fail(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[A:%.*]], [[V1:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[B:%.*]], [[V2:%.*]]
+; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[V1]], i32 [[A]]
+; CHECK-NEXT:    ret i32 [[SELECT]]
+;
+entry:
+  %cmp = icmp ne i32 %a, %v1
+  %cmp1 = icmp ne i32 %b, %v2
+  %cond = and i1 %cmp, %cmp1
+  %select = select i1 %cond, i32 %v1, i32 %a
+  ret i32 %select
+}
+
+define i32 @and_eq_v1_undef_fail(i32 %a, i32 %v1, i32 %v2)  {
+; CHECK-LABEL: @and_eq_v1_undef_fail(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[V1:%.*]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[A]], [[V2:%.*]]
+; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP]], [[CMP1]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[A]], i32 [[V2]]
+; CHECK-NEXT:    ret i32 [[SELECT]]
+;
+entry:
+  %cmp = icmp eq i32 %a, %v1
+  %cmp1 = icmp eq i32 %a, %v2
+  %cond = or i1 %cmp, %cmp1
+  %select = select i1 %cond, i32 %a, i32 %v2
+  ret i32 %select
+}

>From 07938c04890d5b84b8c40852408c5fac4c3637a7 Mon Sep 17 00:00:00 2001
From: XChy <xxs_chy at outlook.com>
Date: Fri, 22 Dec 2023 03:49:05 +0800
Subject: [PATCH 2/2] [InstCombine] Fold select of and/or of icmp to select of
 single icmp

---
 .../InstCombine/InstCombineInternal.h         |  2 +-
 .../InstCombine/InstCombineSelect.cpp         | 66 +++++++++++++++++++
 .../Transforms/InstCombine/select-and-or.ll   | 28 +++-----
 3 files changed, 76 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 9e76a0cf17b183..0667b08af99f51 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -442,7 +442,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   // into simplier select instruction using isImpliedCondition.
   Instruction *foldAndOrOfSelectUsingImpliedCond(Value *Op, SelectInst &SI,
                                                  bool IsAnd);
-
+  Instruction *foldSelectOfAndOr(SelectInst &SI);
   Instruction *hoistFNegAboveFMulFDiv(Value *FNegOp, Instruction &FMFSource);
 
 public:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 20bf00344b144b..90d905ac1c69a8 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -2683,6 +2683,69 @@ Instruction *InstCombinerImpl::foldAndOrOfSelectUsingImpliedCond(Value *Op,
   }
 }
 
+Instruction *InstCombinerImpl::foldSelectOfAndOr(SelectInst &SI) {
+  BinaryOperator *CondVal = dyn_cast<BinaryOperator>(SI.getCondition());
+  if (!CondVal)
+    return nullptr;
+
+  BinaryOperator::BinaryOps BinOpCode = CondVal->getOpcode();
+  bool IsAnd;
+
+  if (BinOpCode == BinaryOperator::Or)
+    IsAnd = false;
+  else if (BinOpCode == BinaryOperator::And)
+    IsAnd = true;
+  else
+    return nullptr;
+
+  Value *TrueVal = SI.getTrueValue(), *FalseVal = SI.getFalseValue();
+
+  // %cmp = icmp eq i32 %a, %v1 (v1 must not be undef)
+  // %cmp1 = icmp eq i32 %a, %v2
+  // %cond = or i1 %cmp, %cmp1
+  // %select = select i1 %cond, i32 %a, i32 %v2
+  // =>
+  // %cond = icmp eq i32 %a, %v1
+  // %select = select i1 %cond, i32 %v1, i32 %v2
+
+  // Or for an inverted version, we fold it like:
+  // %cmp = icmp ne i32 %a, %v1 (v1 must not be undef)
+  // %cmp1 = icmp ne i32 %a, %v2
+  // %cond = and i1 %cmp, %cmp1
+  // %select = select i1 %cond, i32 %v2, i32 %a
+  // =>
+  // %cond = icmp eq i32 %a, %v1
+  // %select = select i1 %cond, i32 %v1, i32 %v2
+
+  Value *A, *V1, *V2;
+  CmpInst::Predicate ExpectedPred =
+      IsAnd ? ICmpInst::ICMP_NE : ICmpInst::ICMP_EQ;
+  CmpInst::Predicate Pred1, Pred2;
+  Value *Cmp1 = CondVal->getOperand(0);
+  Value *Cmp2 = CondVal->getOperand(1);
+  if (match(Cmp1, m_c_ICmp(Pred1, m_Value(A), m_Value(V1))) &&
+      Pred1 == ExpectedPred &&
+      match(Cmp2, m_c_ICmp(Pred2, m_Deferred(A), m_Value(V2))) &&
+      Pred2 == ExpectedPred) {
+    Value *NewFalseVal = IsAnd ? TrueVal : FalseVal;
+    Value *ExpectedA = IsAnd ? FalseVal : TrueVal;
+    if (ExpectedA == A && (NewFalseVal == V1 || NewFalseVal == V2)) {
+      Value *NewTrueVal = NewFalseVal == V1 ? V2 : V1;
+      if (!isGuaranteedNotToBeUndef(NewTrueVal, SQ.AC, &SI, &DT))
+        return nullptr;
+
+      Value *NewCond = NewTrueVal == V1 ? Cmp1 : Cmp2;
+      if (!IsAnd)
+        return SelectInst::Create(NewCond, NewTrueVal, NewFalseVal);
+      // Invert it when original select is inverted
+      return SelectInst::Create(NewCond, NewFalseVal, NewTrueVal);
+    }
+    return nullptr;
+  }
+
+  return nullptr;
+}
+
 // Canonicalize select with fcmp to fabs(). -0.0 makes this tricky. We need
 // fast-math-flags (nsz) or fsub with +0.0 (not fneg) for this to work.
 static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
@@ -3409,6 +3472,9 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
     }
   }
 
+  if (Instruction *I = foldSelectOfAndOr(SI))
+    return I;
+
   auto *SIFPOp = dyn_cast<FPMathOperator>(&SI);
 
   if (auto *FCmp = dyn_cast<FCmpInst>(CondVal)) {
diff --git a/llvm/test/Transforms/InstCombine/select-and-or.ll b/llvm/test/Transforms/InstCombine/select-and-or.ll
index d30b543945216d..cf1b6377ff156d 100644
--- a/llvm/test/Transforms/InstCombine/select-and-or.ll
+++ b/llvm/test/Transforms/InstCombine/select-and-or.ll
@@ -970,9 +970,7 @@ define i32 @and_eq_v1(i32 %a, i32 noundef %v1, i32 %v2)  {
 ; CHECK-LABEL: @and_eq_v1(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[V1:%.*]]
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[A]], [[V2:%.*]]
-; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[A]], i32 [[V2]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[CMP]], i32 [[V1]], i32 [[V2:%.*]]
 ; CHECK-NEXT:    ret i32 [[SELECT]]
 ;
 entry:
@@ -986,10 +984,8 @@ entry:
 define i32 @and_eq_v2(i32 %a, i32 %v1, i32 noundef %v2)  {
 ; CHECK-LABEL: @and_eq_v2(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[V1:%.*]]
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[A]], [[V2:%.*]]
-; CHECK-NEXT:    [[COND:%.*]] = or i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[A]], i32 [[V1]]
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq i32 [[A:%.*]], [[V2:%.*]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[CMP1]], i32 [[V2]], i32 [[V1:%.*]]
 ; CHECK-NEXT:    ret i32 [[SELECT]]
 ;
 entry:
@@ -1003,10 +999,8 @@ entry:
 define i32 @and_ne_v1(i32 %a, i32 noundef %v1, i32 %v2)  {
 ; CHECK-LABEL: @and_ne_v1(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[A:%.*]], [[V1:%.*]]
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[A]], [[V2:%.*]]
-; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[V2]], i32 [[A]]
+; CHECK-NEXT:    [[CMP_NOT:%.*]] = icmp eq i32 [[A:%.*]], [[V1:%.*]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[CMP_NOT]], i32 [[V1]], i32 [[V2:%.*]]
 ; CHECK-NEXT:    ret i32 [[SELECT]]
 ;
 entry:
@@ -1020,10 +1014,8 @@ entry:
 define i32 @and_ne_v2(i32 %a, i32 %v1, i32 noundef %v2)  {
 ; CHECK-LABEL: @and_ne_v2(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[A:%.*]], [[V1:%.*]]
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ne i32 [[A]], [[V2:%.*]]
-; CHECK-NEXT:    [[COND:%.*]] = and i1 [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[V1]], i32 [[A]]
+; CHECK-NEXT:    [[CMP1_NOT:%.*]] = icmp eq i32 [[A:%.*]], [[V2:%.*]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[CMP1_NOT]], i32 [[V2]], i32 [[V1:%.*]]
 ; CHECK-NEXT:    ret i32 [[SELECT]]
 ;
 entry:
@@ -1043,7 +1035,7 @@ define i32 @and_eq_v1_multi_use(i32 %a, i32 noundef %v1, i32 %v2)  {
 ; CHECK-NEXT:    call void @use(i1 [[CMP]])
 ; CHECK-NEXT:    call void @use(i1 [[CMP1]])
 ; CHECK-NEXT:    call void @use(i1 [[COND]])
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], i32 [[A]], i32 [[V2]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[CMP]], i32 [[V1]], i32 [[V2]]
 ; CHECK-NEXT:    ret i32 [[SELECT]]
 ;
 entry:
@@ -1061,9 +1053,7 @@ define <2 x i32> @and_eq_v1_vec(<2 x i32> %a, <2 x i32> noundef %v1, <2 x i32> %
 ; CHECK-LABEL: @and_eq_v1_vec(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], [[V1:%.*]]
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp eq <2 x i32> [[A]], [[V2:%.*]]
-; CHECK-NEXT:    [[COND:%.*]] = or <2 x i1> [[CMP]], [[CMP1]]
-; CHECK-NEXT:    [[SELECT:%.*]] = select <2 x i1> [[COND]], <2 x i32> [[A]], <2 x i32> [[V2]]
+; CHECK-NEXT:    [[SELECT:%.*]] = select <2 x i1> [[CMP]], <2 x i32> [[V1]], <2 x i32> [[V2:%.*]]
 ; CHECK-NEXT:    ret <2 x i32> [[SELECT]]
 ;
 entry:



More information about the llvm-commits mailing list