[InstCombine] reduce icmp_ne0-of-and-of-select-of-constants
authorSanjay Patel <spatel@rotateright.com>
Tue, 31 Jan 2023 19:29:57 +0000 (14:29 -0500)
committerSanjay Patel <spatel@rotateright.com>
Tue, 31 Jan 2023 19:59:55 +0000 (14:59 -0500)
Follow-up to:
D142847 / 98855059674c

This handles the 'ne' variant by inverting the result.

https://alive2.llvm.org/ce/z/D229jS

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
llvm/test/Transforms/InstCombine/icmp-select.ll

index 8ff75d5..1c7344b 100644 (file)
@@ -1892,19 +1892,24 @@ Instruction *InstCombinerImpl::foldICmpAndConstant(ICmpInst &Cmp,
   // common bits set, it's the same as checking if exactly one select condition
   // is set:
   // ((A ? TC : FC) & (B ? TC : FC)) == 0 --> xor A, B
+  // ((A ? TC : FC) & (B ? TC : FC)) != 0 --> not(xor A, B)
   // TODO: Generalize for non-constant values.
-  // TODO: Invert with a "ne" predicate.
   // TODO: Handle signed/unsigned predicates.
   // TODO: Handle other bitwise logic connectors.
   // TODO: Extend to handle a non-zero compare constant.
-  if (Pred == CmpInst::ICMP_EQ && C.isZero()) {
+  if (C.isZero() && (Pred == CmpInst::ICMP_EQ || And->hasOneUse())) {
+    assert(Cmp.isEquality() && "Not expecting non-equality predicates");
     Value *A, *B;
     const APInt *TC, *FC;
     if (match(X, m_Select(m_Value(A), m_APInt(TC), m_APInt(FC))) &&
         match(Y,
               m_Select(m_Value(B), m_SpecificInt(*TC), m_SpecificInt(*FC))) &&
-        !TC->isZero() && !FC->isZero() && !TC->intersects(*FC))
-      return BinaryOperator::CreateXor(A, B);
+        !TC->isZero() && !FC->isZero() && !TC->intersects(*FC)) {
+      Value *R = Builder.CreateXor(A, B);
+      if (Pred == CmpInst::ICMP_NE)
+        R = Builder.CreateNot(R);
+      return replaceInstUsesWith(Cmp, R);
+    }
   }
 
   // ((zext i1 X) & Y) == 0 --> !((trunc Y) & X)
index b7e43da..0d723c9 100644 (file)
@@ -260,6 +260,8 @@ define i1 @umin_seq_comparison(i8 %x, i8 %y) {
   ret i1 %cmp2
 }
 
+; ((A ? TC : FC) & (B ? TC : FC)) == 0 --> xor A, B
+
 define i1 @select_constants_and_icmp_eq0(i1 %x, i1 %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_eq0(
 ; CHECK-NEXT:    [[CMP:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
@@ -419,12 +421,12 @@ define i1 @select_constants_and_icmp_eq_fval(i1 %x, i1 %y) {
   ret i1 %cmp
 }
 
+; ((A ? TC : FC) & (B ? TC : FC)) != 0 --> not(xor A, B)
+
 define i1 @select_constants_and_icmp_ne0(i1 %x, i1 %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_ne0(
-; CHECK-NEXT:    [[S1:%.*]] = select i1 [[X:%.*]], i8 2, i8 1
-; CHECK-NEXT:    [[S2:%.*]] = select i1 [[Y:%.*]], i8 2, i8 1
-; CHECK-NEXT:    [[AND:%.*]] = and i8 [[S1]], [[S2]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[AND]], 0
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[CMP:%.*]] = xor i1 [[TMP1]], true
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %s1 = select i1 %x, i8 2, i8 1
@@ -434,14 +436,16 @@ define i1 @select_constants_and_icmp_ne0(i1 %x, i1 %y) {
   ret i1 %cmp
 }
 
+; extra uses on select intermediates are ok
+
 define i1 @select_constants_and_icmp_ne0_uses(i1 %x, i1 %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_ne0_uses(
 ; CHECK-NEXT:    [[S1:%.*]] = select i1 [[X:%.*]], i8 2, i8 1
 ; CHECK-NEXT:    call void @use(i8 [[S1]])
 ; CHECK-NEXT:    [[S2:%.*]] = select i1 [[Y:%.*]], i8 2, i8 1
 ; CHECK-NEXT:    call void @use(i8 [[S2]])
-; CHECK-NEXT:    [[AND:%.*]] = and i8 [[S1]], [[S2]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[AND]], 0
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = xor i1 [[TMP1]], true
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %s1 = select i1 %x, i8 2, i8 1
@@ -453,6 +457,8 @@ define i1 @select_constants_and_icmp_ne0_uses(i1 %x, i1 %y) {
   ret i1 %cmp
 }
 
+; negative test - don't create extra instructions
+
 define i1 @select_constants_and_icmp_ne0_all_uses(i1 %x, i1 %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_ne0_all_uses(
 ; CHECK-NEXT:    [[S1:%.*]] = select i1 [[X:%.*]], i8 2, i8 1
@@ -474,12 +480,12 @@ define i1 @select_constants_and_icmp_ne0_all_uses(i1 %x, i1 %y) {
   ret i1 %cmp
 }
 
+; vector splat constants are ok
+
 define <2 x i1> @select_constants_and_icmp_ne0_vec_splat(<2 x i1> %x, <2 x i1> %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_ne0_vec_splat(
-; CHECK-NEXT:    [[S1:%.*]] = select <2 x i1> [[X:%.*]], <2 x i9> <i9 3, i9 3>, <2 x i9> <i9 48, i9 48>
-; CHECK-NEXT:    [[S2:%.*]] = select <2 x i1> [[Y:%.*]], <2 x i9> <i9 3, i9 3>, <2 x i9> <i9 48, i9 48>
-; CHECK-NEXT:    [[AND:%.*]] = and <2 x i9> [[S1]], [[S2]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne <2 x i9> [[AND]], zeroinitializer
+; CHECK-NEXT:    [[TMP1:%.*]] = xor <2 x i1> [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[CMP:%.*]] = xor <2 x i1> [[TMP1]], <i1 true, i1 true>
 ; CHECK-NEXT:    ret <2 x i1> [[CMP]]
 ;
   %s1 = select <2 x i1> %x, <2 x i9> <i9 3, i9 3>, <2 x i9> <i9 48, i9 48>
@@ -489,6 +495,8 @@ define <2 x i1> @select_constants_and_icmp_ne0_vec_splat(<2 x i1> %x, <2 x i1> %
   ret <2 x i1> %cmp
 }
 
+; common bit set - simplified via known bits
+
 define i1 @select_constants_and_icmp_ne0_common_bit(i1 %x, i1 %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_ne0_common_bit(
 ; CHECK-NEXT:    ret i1 true
@@ -500,6 +508,8 @@ define i1 @select_constants_and_icmp_ne0_common_bit(i1 %x, i1 %y) {
   ret i1 %cmp
 }
 
+; negative test - need matching constants
+
 define i1 @select_constants_and_icmp_ne0_no_common_op1(i1 %x, i1 %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_ne0_no_common_op1(
 ; CHECK-NEXT:    [[S1:%.*]] = select i1 [[X:%.*]], i8 16, i8 3
@@ -515,6 +525,8 @@ define i1 @select_constants_and_icmp_ne0_no_common_op1(i1 %x, i1 %y) {
   ret i1 %cmp
 }
 
+; negative test - need matching constants
+
 define i1 @select_constants_and_icmp_ne0_no_common_op2(i1 %x, i1 %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_ne0_no_common_op2(
 ; CHECK-NEXT:    [[S1:%.*]] = select i1 [[X:%.*]], i8 16, i8 3
@@ -530,6 +542,8 @@ define i1 @select_constants_and_icmp_ne0_no_common_op2(i1 %x, i1 %y) {
   ret i1 %cmp
 }
 
+; reduces via FoldOpInstSelect, but this could be a simple 'nor'
+
 define i1 @select_constants_and_icmp_ne0_zero_tval(i1 %x, i1 %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_ne0_zero_tval(
 ; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[X:%.*]], i1 true, i1 [[Y:%.*]]
@@ -543,6 +557,8 @@ define i1 @select_constants_and_icmp_ne0_zero_tval(i1 %x, i1 %y) {
   ret i1 %cmp
 }
 
+; reduces via FoldOpInstSelect, but this could be a simple 'and'
+
 define i1 @select_constants_and_icmp_ne0_zero_fval(i1 %x, i1 %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_ne0_zero_fval(
 ; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[X:%.*]], i1 [[Y:%.*]], i1 false
@@ -555,6 +571,8 @@ define i1 @select_constants_and_icmp_ne0_zero_fval(i1 %x, i1 %y) {
   ret i1 %cmp
 }
 
+; TODO: ~(x & y)
+
 define i1 @select_constants_and_icmp_ne_tval(i1 %x, i1 %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_ne_tval(
 ; CHECK-NEXT:    [[S1:%.*]] = select i1 [[X:%.*]], i8 6, i8 1
@@ -570,6 +588,8 @@ define i1 @select_constants_and_icmp_ne_tval(i1 %x, i1 %y) {
   ret i1 %cmp
 }
 
+; TODO: (x | y)
+
 define i1 @select_constants_and_icmp_ne_fval(i1 %x, i1 %y) {
 ; CHECK-LABEL: @select_constants_and_icmp_ne_fval(
 ; CHECK-NEXT:    [[S1:%.*]] = select i1 [[X:%.*]], i8 12, i8 3