[InstSimplify] Simplify bool icmp with not in LHS
authorHasyimi Bahrudin <hasyimi@gatech.edu>
Thu, 9 Dec 2021 17:03:59 +0000 (12:03 -0500)
committerSanjay Patel <spatel@rotateright.com>
Thu, 9 Dec 2021 21:26:46 +0000 (16:26 -0500)
Refer to https://llvm.org/PR52546.

Simplifies the following cases:
    not(X) == 0 -> X != 0 -> X
    not(X) <=u 0 -> X >u 0 -> X
    not(X) >=s 0 -> X <s 0 -> X
    not(X) != 1 -> X == 1 -> X
    not(X) <=u 1 -> X >=u 1 -> X
    not(X) >s 1 -> X <=s -1 -> X

Differential Revision: https://reviews.llvm.org/D114666

llvm/lib/Analysis/InstructionSimplify.cpp
llvm/test/Transforms/InstSimplify/icmp-not-bool-constant.ll

index e571c6b..029859f 100644 (file)
@@ -2703,9 +2703,17 @@ static Value *simplifyICmpOfBools(CmpInst::Predicate Pred, Value *LHS,
   if (!OpTy->isIntOrIntVectorTy(1))
     return nullptr;
 
-  // A boolean compared to true/false can be simplified in 14 out of the 20
-  // (10 predicates * 2 constants) possible combinations. Cases not handled here
-  // require a 'not' of the LHS, so those must be transformed in InstCombine.
+  // A boolean compared to true/false can be reduced in 14 out of the 20
+  // (10 predicates * 2 constants) possible combinations. The other
+  // 6 cases require a 'not' of the LHS.
+
+  auto ExtractNotLHS = [](Value *V) -> Value * {
+    Value *X;
+    if (match(V, m_Not(m_Value(X))))
+      return X;
+    return nullptr;
+  };
+
   if (match(RHS, m_Zero())) {
     switch (Pred) {
     case CmpInst::ICMP_NE:  // X !=  0 -> X
@@ -2713,6 +2721,13 @@ static Value *simplifyICmpOfBools(CmpInst::Predicate Pred, Value *LHS,
     case CmpInst::ICMP_SLT: // X <s  0 -> X
       return LHS;
 
+    case CmpInst::ICMP_EQ:  // not(X) ==  0 -> X != 0 -> X
+    case CmpInst::ICMP_ULE: // not(X) <=u 0 -> X >u 0 -> X
+    case CmpInst::ICMP_SGE: // not(X) >=s 0 -> X <s 0 -> X
+      if (Value *X = ExtractNotLHS(LHS))
+        return X;
+      break;
+
     case CmpInst::ICMP_ULT: // X <u  0 -> false
     case CmpInst::ICMP_SGT: // X >s  0 -> false
       return getFalse(ITy);
@@ -2730,6 +2745,13 @@ static Value *simplifyICmpOfBools(CmpInst::Predicate Pred, Value *LHS,
     case CmpInst::ICMP_SLE: // X <=s -1 -> X
       return LHS;
 
+    case CmpInst::ICMP_NE:  // not(X) !=  1 -> X ==   1 -> X
+    case CmpInst::ICMP_ULT: // not(X) <=u 1 -> X >=u  1 -> X
+    case CmpInst::ICMP_SGT: // not(X) >s  1 -> X <=s -1 -> X
+      if (Value *X = ExtractNotLHS(LHS))
+        return X;
+      break;
+
     case CmpInst::ICMP_UGT: // X >u   1 -> false
     case CmpInst::ICMP_SLT: // X <s  -1 -> false
       return getFalse(ITy);
index b631c6b..855647d 100644 (file)
@@ -2,7 +2,7 @@
 ; RUN: opt < %s -instsimplify -S | FileCheck %s
 
 ; Test all integer predicates with bool types and true/false constants,
-; with not on LHS (icmp pred (xor X, true), true|false).
+; with not on LHS (icmp pred not(X), true|false).
 ; Use vectors to provide test coverage that is not duplicated in other folds.
 
 define <2 x i1> @eq_t_not(<2 x i1> %a) {
@@ -17,26 +17,58 @@ define <2 x i1> @eq_t_not(<2 x i1> %a) {
 
 define <2 x i1> @eq_f_not(<2 x i1> %a) {
 ; CHECK-LABEL: @eq_f_not(
-; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i1> [[A:%.*]], <i1 true, i1 true>
-; CHECK-NEXT:    [[R:%.*]] = icmp eq <2 x i1> [[NOT]], zeroinitializer
-; CHECK-NEXT:    ret <2 x i1> [[R]]
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
 ;
   %not = xor <2 x i1> %a, <i1 true, i1 true>
   %r = icmp eq <2 x i1> %not, <i1 false, i1 false>
   ret <2 x i1> %r
 }
 
+define <2 x i1> @eq_f_not_swap(<2 x i1> %a) {
+; CHECK-LABEL: @eq_f_not_swap(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> <i1 true, i1 true>, %a
+  %r = icmp eq <2 x i1> %not, <i1 false, i1 false>
+  ret <2 x i1> %r
+}
+
+define <2 x i1> @eq_f_not_undef(<2 x i1> %a) {
+; CHECK-LABEL: @eq_f_not_undef(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> %a, <i1 undef, i1 true>
+  %r = icmp eq <2 x i1> %not, <i1 false, i1 false>
+  ret <2 x i1> %r
+}
+
 define <2 x i1> @ne_t_not(<2 x i1> %a) {
 ; CHECK-LABEL: @ne_t_not(
-; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i1> [[A:%.*]], <i1 true, i1 true>
-; CHECK-NEXT:    [[R:%.*]] = icmp ne <2 x i1> [[NOT]], <i1 true, i1 true>
-; CHECK-NEXT:    ret <2 x i1> [[R]]
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
 ;
   %not = xor <2 x i1> %a, <i1 true, i1 true>
   %r = icmp ne <2 x i1> %not, <i1 true, i1 true>
   ret <2 x i1> %r
 }
 
+define <2 x i1> @ne_t_not_swap(<2 x i1> %a) {
+; CHECK-LABEL: @ne_t_not_swap(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> <i1 true, i1 true>, %a
+  %r = icmp ne <2 x i1> %not, <i1 true, i1 true>
+  ret <2 x i1> %r
+}
+
+define <2 x i1> @ne_t_not_undef(<2 x i1> %a) {
+; CHECK-LABEL: @ne_t_not_undef(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> %a, <i1 undef, i1 true>
+  %r = icmp ne <2 x i1> %not, <i1 true, i1 true>
+  ret <2 x i1> %r
+}
+
 define <2 x i1> @ne_f_not(<2 x i1> %a) {
 ; CHECK-LABEL: @ne_f_not(
 ; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i1> [[A:%.*]], <i1 true, i1 true>
@@ -68,15 +100,31 @@ define <2 x i1> @ugt_f_not(<2 x i1> %a) {
 
 define <2 x i1> @ult_t_not(<2 x i1> %a) {
 ; CHECK-LABEL: @ult_t_not(
-; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i1> [[A:%.*]], <i1 true, i1 true>
-; CHECK-NEXT:    [[R:%.*]] = icmp ult <2 x i1> [[NOT]], <i1 true, i1 true>
-; CHECK-NEXT:    ret <2 x i1> [[R]]
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
 ;
   %not = xor <2 x i1> %a, <i1 true, i1 true>
   %r = icmp ult <2 x i1> %not, <i1 true, i1 true>
   ret <2 x i1> %r
 }
 
+define <2 x i1> @ult_t_not_swap(<2 x i1> %a) {
+; CHECK-LABEL: @ult_t_not_swap(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> <i1 true, i1 true>, %a
+  %r = icmp ult <2 x i1> %not, <i1 true, i1 true>
+  ret <2 x i1> %r
+}
+
+define <2 x i1> @ult_t_not_undef(<2 x i1> %a) {
+; CHECK-LABEL: @ult_t_not_undef(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> %a, <i1 undef, i1 true>
+  %r = icmp ult <2 x i1> %not, <i1 true, i1 true>
+  ret <2 x i1> %r
+}
+
 define <2 x i1> @ult_f_not(<2 x i1> %a) {
 ; CHECK-LABEL: @ult_f_not(
 ; CHECK-NEXT:    ret <2 x i1> zeroinitializer
@@ -88,15 +136,31 @@ define <2 x i1> @ult_f_not(<2 x i1> %a) {
 
 define <2 x i1> @sgt_t_not(<2 x i1> %a) {
 ; CHECK-LABEL: @sgt_t_not(
-; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i1> [[A:%.*]], <i1 true, i1 true>
-; CHECK-NEXT:    [[R:%.*]] = icmp sgt <2 x i1> [[NOT]], <i1 true, i1 true>
-; CHECK-NEXT:    ret <2 x i1> [[R]]
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
 ;
   %not = xor <2 x i1> %a, <i1 true, i1 true>
   %r = icmp sgt <2 x i1> %not, <i1 true, i1 true>
   ret <2 x i1> %r
 }
 
+define <2 x i1> @sgt_t_not_swap(<2 x i1> %a) {
+; CHECK-LABEL: @sgt_t_not_swap(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> <i1 true, i1 true>, %a
+  %r = icmp sgt <2 x i1> %not, <i1 true, i1 true>
+  ret <2 x i1> %r
+}
+
+define <2 x i1> @sgt_t_not_undef(<2 x i1> %a) {
+; CHECK-LABEL: @sgt_t_not_undef(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> %a, <i1 undef, i1 true>
+  %r = icmp sgt <2 x i1> %not, <i1 true, i1 true>
+  ret <2 x i1> %r
+}
+
 define <2 x i1> @sgt_f_not(<2 x i1> %a) {
 ; CHECK-LABEL: @sgt_f_not(
 ; CHECK-NEXT:    ret <2 x i1> zeroinitializer
@@ -155,15 +219,31 @@ define <2 x i1> @ule_t_not(<2 x i1> %a) {
 
 define <2 x i1> @ule_f_not(<2 x i1> %a) {
 ; CHECK-LABEL: @ule_f_not(
-; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i1> [[A:%.*]], <i1 true, i1 true>
-; CHECK-NEXT:    [[R:%.*]] = icmp ule <2 x i1> [[NOT]], zeroinitializer
-; CHECK-NEXT:    ret <2 x i1> [[R]]
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
 ;
   %not = xor <2 x i1> %a, <i1 true, i1 true>
   %r = icmp ule <2 x i1> %not, <i1 false, i1 false>
   ret <2 x i1> %r
 }
 
+define <2 x i1> @ule_f_not_swap(<2 x i1> %a) {
+; CHECK-LABEL: @ule_f_not_swap(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> <i1 true, i1 true>, %a
+  %r = icmp ule <2 x i1> %not, <i1 false, i1 false>
+  ret <2 x i1> %r
+}
+
+define <2 x i1> @ule_f_not_undef(<2 x i1> %a) {
+; CHECK-LABEL: @ule_f_not_undef(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> %a, <i1 undef, i1 true>
+  %r = icmp ule <2 x i1> %not, <i1 false, i1 false>
+  ret <2 x i1> %r
+}
+
 define <2 x i1> @sge_t_not(<2 x i1> %a) {
 ; CHECK-LABEL: @sge_t_not(
 ; CHECK-NEXT:    ret <2 x i1> <i1 true, i1 true>
@@ -175,15 +255,31 @@ define <2 x i1> @sge_t_not(<2 x i1> %a) {
 
 define <2 x i1> @sge_f_not(<2 x i1> %a) {
 ; CHECK-LABEL: @sge_f_not(
-; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i1> [[A:%.*]], <i1 true, i1 true>
-; CHECK-NEXT:    [[R:%.*]] = icmp sge <2 x i1> [[NOT]], zeroinitializer
-; CHECK-NEXT:    ret <2 x i1> [[R]]
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
 ;
   %not = xor <2 x i1> %a, <i1 true, i1 true>
   %r = icmp sge <2 x i1> %not, <i1 false, i1 false>
   ret <2 x i1> %r
 }
 
+define <2 x i1> @sge_f_not_swap(<2 x i1> %a) {
+; CHECK-LABEL: @sge_f_not_swap(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> <i1 true, i1 true>, %a
+  %r = icmp sge <2 x i1> %not, <i1 false, i1 false>
+  ret <2 x i1> %r
+}
+
+define <2 x i1> @sge_f_not_undef(<2 x i1> %a) {
+; CHECK-LABEL: @sge_f_not_undef(
+; CHECK-NEXT:    ret <2 x i1> [[A:%.*]]
+;
+  %not = xor <2 x i1> %a, <i1 undef, i1 true>
+  %r = icmp sge <2 x i1> %not, <i1 false, i1 false>
+  ret <2 x i1> %r
+}
+
 define <2 x i1> @sle_t_not(<2 x i1> %a) {
 ; CHECK-LABEL: @sle_t_not(
 ; CHECK-NEXT:    [[NOT:%.*]] = xor <2 x i1> [[A:%.*]], <i1 true, i1 true>