[llvm] [InstCombine] simplify `x (comp) ~x` (PR #73990)

via llvm-commits llvm-commits at lists.llvm.org
Sat Dec 2 04:32:56 PST 2023


https://github.com/ParkHanbum updated https://github.com/llvm/llvm-project/pull/73990

>From 2bf0346c8ac479caff5add8e8ed5eba5e77fc966 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Fri, 1 Dec 2023 06:28:23 +0900
Subject: [PATCH 1/2] [InstCombine] Add test for simplify `x (comp) ~x`

simplify compare between specific variable `X` and  `NOT(X)`

Proof: https://alive2.llvm.org/ce/z/uGuaAs
---
 .../Transforms/InstCombine/icmp-of-xor-x.ll   | 321 ++++++++++++++++++
 1 file changed, 321 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/icmp-of-xor-x.ll b/llvm/test/Transforms/InstCombine/icmp-of-xor-x.ll
index ef4f2bfecfd8..3f6cda49aa7e 100644
--- a/llvm/test/Transforms/InstCombine/icmp-of-xor-x.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-of-xor-x.ll
@@ -5,6 +5,327 @@ declare void @llvm.assume(i1)
 declare void @barrier()
 declare void @use.i8(i8)
 
+; X s< ~X --> X s< 0
+define i1 @src_slt_xnx_to_slt_0_i1(i1 %x) {
+; CHECK-LABEL: @src_slt_xnx_to_slt_0_i1(
+; CHECK-NEXT:    ret i1 [[X:%.*]]
+;
+  %not = xor i1 %x, -1
+  %cmp = icmp slt i1 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_slt_xnx_to_slt_0_i8(i8 %x) {
+; CHECK-LABEL: @src_slt_xnx_to_slt_0_i8(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp slt i8 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_slt_xnx_to_slt_0_i16(i16 %x) {
+; CHECK-LABEL: @src_slt_xnx_to_slt_0_i16(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i16 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i16 %x, -1
+  %cmp = icmp slt i16 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_slt_xnx_to_slt_0_i32(i32 %x) {
+; CHECK-LABEL: @src_slt_xnx_to_slt_0_i32(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i32 %x, -1
+  %cmp = icmp slt i32 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_slt_xnx_to_slt_0_i64(i64 %x) {
+; CHECK-LABEL: @src_slt_xnx_to_slt_0_i64(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i64 %x, -1
+  %cmp = icmp slt i64 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_slt_xnx_to_slt_0_i128(i128 %x) {
+; CHECK-LABEL: @src_slt_xnx_to_slt_0_i128(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i128 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i128 %x, -1
+  %cmp = icmp slt i128 %x, %not
+  ret i1 %cmp
+}
+; X s> ~X  -->  X s> -1
+define i1 @src_sgt_xnx_to_sgt_n1_i1(i1 %x) {
+; CHECK-LABEL: @src_sgt_xnx_to_sgt_n1_i1(
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[X:%.*]], true
+; CHECK-NEXT:    ret i1 [[NOT]]
+;
+  %not = xor i1 %x, -1
+  %cmp = icmp sgt i1 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_sgt_xnx_to_sgt_n1_i8(i8 %x) {
+; CHECK-LABEL: @src_sgt_xnx_to_sgt_n1_i8(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp sgt i8 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_sgt_xnx_to_sgt_n1_i16(i16 %x) {
+; CHECK-LABEL: @src_sgt_xnx_to_sgt_n1_i16(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i16 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i16 %x, -1
+  %cmp = icmp sgt i16 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_sgt_xnx_to_sgt_n1_i32(i32 %x) {
+; CHECK-LABEL: @src_sgt_xnx_to_sgt_n1_i32(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i32 %x, -1
+  %cmp = icmp sgt i32 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_sgt_xnx_to_sgt_n1_i64(i64 %x) {
+; CHECK-LABEL: @src_sgt_xnx_to_sgt_n1_i64(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i64 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i64 %x, -1
+  %cmp = icmp sgt i64 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_sgt_xnx_to_sgt_n1_i128(i128 %x) {
+; CHECK-LABEL: @src_sgt_xnx_to_sgt_n1_i128(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i128 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i128 %x, -1
+  %cmp = icmp sgt i128 %x, %not
+  ret i1 %cmp
+}
+; X (compare) ~X can never be equal.
+; X s<= ~X  -->  X s< ~X  -->  X s< 0
+define i1 @src_sle_xnx_to_slt_0_i8(i8 %x) {
+; CHECK-LABEL: @src_sle_xnx_to_slt_0_i8(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp sle i8 %x, %not
+  ret i1 %cmp
+}
+; X s>= ~X  -->  X s> ~X  -->  X s> -1
+define i1 @src_sge_xnx_to_sgt_n1_i8(i8 %x) {
+; CHECK-LABEL: @src_sge_xnx_to_sgt_n1_i8(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp sge i8 %x, %not
+  ret i1 %cmp
+}
+; slt or sgt can be converted to the other by swapping the true and false clauses
+; ~X s< X  -->  X s> ~X  -->  X s> -1
+define i1 @src_slt_nxx_to_sgt_nega1(i8 %x) {
+; CHECK-LABEL: @src_slt_nxx_to_sgt_nega1(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp slt i8 %not, %x
+  ret i1 %cmp
+}
+; ~X s> X  -->  X s< ~X  -->  x s< 0
+define i1 @src_sgt_nxx_to_slt_0(i8 %x) {
+; CHECK-LABEL: @src_sgt_nxx_to_slt_0(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp sgt i8 %not, %x
+  ret i1 %cmp
+}
+; X u< ~X --> X u> SIGNBIT_OF(X) + 0 --> X s> -1
+define i1 @src_ult_xnx_to_sgt_n1_i1(i1 %x) {
+; CHECK-LABEL: @src_ult_xnx_to_sgt_n1_i1(
+; CHECK-NEXT:    [[NOT:%.*]] = xor i1 [[X:%.*]], true
+; CHECK-NEXT:    ret i1 [[NOT]]
+;
+  %not = xor i1 %x, -1
+  %cmp = icmp ult i1 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_ult_xnx_to_sgt_n1_i8(i8 %x) {
+; CHECK-LABEL: @src_ult_xnx_to_sgt_n1_i8(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp ult i8 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_ult_xnx_to_sgt_n1_i16(i16 %x) {
+; CHECK-LABEL: @src_ult_xnx_to_sgt_n1_i16(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i16 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i16 %x, -1
+  %cmp = icmp ult i16 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_ult_xnx_to_sgt_n1_i32(i32 %x) {
+; CHECK-LABEL: @src_ult_xnx_to_sgt_n1_i32(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i32 %x, -1
+  %cmp = icmp ult i32 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_ult_xnx_to_sgt_n1_i64(i64 %x) {
+; CHECK-LABEL: @src_ult_xnx_to_sgt_n1_i64(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i64 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i64 %x, -1
+  %cmp = icmp ult i64 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_ult_xnx_to_sgt_n1_i128(i128 %x) {
+; CHECK-LABEL: @src_ult_xnx_to_sgt_n1_i128(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i128 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i128 %x, -1
+  %cmp = icmp ult i128 %x, %not
+  ret i1 %cmp
+}
+; X u> ~X --> X u> SIGNBIT_OF(X) - 1 --> X s< 0
+define i1 @src_ugt_xnx_to_slt_0_i1(i1 %x) {
+; CHECK-LABEL: @src_ugt_xnx_to_slt_0_i1(
+; CHECK-NEXT:    ret i1 [[X:%.*]]
+;
+  %not = xor i1 %x, -1
+  %cmp = icmp ugt i1 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_ugt_xnx_to_slt_0_i8(i8 %x) {
+; CHECK-LABEL: @src_ugt_xnx_to_slt_0_i8(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp ugt i8 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_ugt_xnx_to_slt_0_i16(i16 %x) {
+; CHECK-LABEL: @src_ugt_xnx_to_slt_0_i16(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i16 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i16 %x, -1
+  %cmp = icmp ugt i16 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_ugt_xnx_to_slt_0_i32(i32 %x) {
+; CHECK-LABEL: @src_ugt_xnx_to_slt_0_i32(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i32 %x, -1
+  %cmp = icmp ugt i32 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_ugt_xnx_to_slt_0_i64(i64 %x) {
+; CHECK-LABEL: @src_ugt_xnx_to_slt_0_i64(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i64 %x, -1
+  %cmp = icmp ugt i64 %x, %not
+  ret i1 %cmp
+}
+define i1 @src_ugt_xnx_to_slt_0_i128(i128 %x) {
+; CHECK-LABEL: @src_ugt_xnx_to_slt_0_i128(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i128 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i128 %x, -1
+  %cmp = icmp ugt i128 %x, %not
+  ret i1 %cmp
+}
+; X (compare) ~X can never be equal.
+; X u<= ~X --> X u< ~X --> X u> SIGNBIT_OF(X)+0 --> X s> -1
+define i1 @src_ule_xnx_to_ugt(i8 %x) {
+; CHECK-LABEL: @src_ule_xnx_to_ugt(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp ule i8 %x, %not
+  ret i1 %cmp
+}
+; X u>= ~X --> X u> ~X --> X u< SIGNBIT_OF(X)-1 --> X s< 0
+define i1 @src_uge_xnx_to_ult(i8 %x) {
+; CHECK-LABEL: @src_uge_xnx_to_ult(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp uge i8 %x, %not
+  ret i1 %cmp
+}
+; ult or ugt can be converted to the other by swapping the true and false clauses
+; ~X u< X --> X u> ~X --> X u> SIGNBIT_OF(X)+0 --> X s< 0
+define i1 @src_ult_nxx_to_ult(i8 %x) {
+; CHECK-LABEL: @src_ult_nxx_to_ult(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp ult i8 %not, %x
+  ret i1 %cmp
+}
+; ~X u> X --> X u< ~X --> X u< SIGNBIT_OF(X)+1  --> X s> -1
+define i1 @src_ugt_nxx_to_ugt(i8 %x) {
+; CHECK-LABEL: @src_ugt_nxx_to_ugt(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp ugt i8 %not, %x
+  ret i1 %cmp
+}
+; X == ~X  ->  false
+define i1 @src_eq_xnx_to_0(i8 %x) {
+; CHECK-LABEL: @src_eq_xnx_to_0(
+; CHECK-NEXT:    ret i1 false
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp eq i8 %x, %not
+  ret i1 %cmp
+}
+; X != ~X  ->  true
+define i1 @src_ne_xnx_to_1(i8 %x) {
+; CHECK-LABEL: @src_ne_xnx_to_1(
+; CHECK-NEXT:    ret i1 true
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp ne i8 %x, %not
+  ret i1 %cmp
+}
+
 ; test for (~x ^ y) < ~z
 define i1 @test_xor1(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: @test_xor1(

>From 3861d6bbbc10982393b7838a5735494c50d7b6c8 Mon Sep 17 00:00:00 2001
From: Hanbum Park <kese111 at gmail.com>
Date: Fri, 1 Dec 2023 06:32:35 +0900
Subject: [PATCH 2/2] [InstCombine] simplify `x (comp) ~x`

simplify compare between specific variable `X` and  `NOT(X)`

Proof: https://alive2.llvm.org/ce/z/ng7_WC

Fixed #57532.
---
 .../InstCombine/InstCombineCompares.cpp       | 45 +++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 9bc84c7dd6e1..546c165fc36d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7035,6 +7035,51 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
       return new ICmpInst(I.getSwappedPredicate(Pred), Builder.CreateXor(X, Y),
                           Z);
 
+    // Transform X s<  ~X  -->   X s<  0
+    //           X s>  ~X  -->   X s>  -1
+    //           X s>= ~X  -->   X s>  -1
+    //           X s<= ~X  -->   X s<  0
+    //           X u<  ~X  -->   X u<  (SIGNBIT(X))
+    //           X u>  ~X  -->   X u>  (SIGNBIT(X))
+    //           X u<= ~X  -->   X u<  (SIGNBIT(X))
+    //           X u>= ~X  -->   X u>  (SIGNBIT(X))
+    //           X ==  ~X  -->   false
+    //           X !=  ~X  -->   true
+    if (match(&I, m_c_ICmp(Pred, m_Value(X), m_Not(m_Deferred(X))))) {
+      CmpInst::Predicate PredEqRemoved;
+      Constant *Const;
+      APInt C;
+      switch (Pred) {
+      case ICmpInst::ICMP_EQ:
+        return replaceInstUsesWith(I, ConstantInt::getFalse(I.getType()));
+        break;
+      case ICmpInst::ICMP_NE:
+        return replaceInstUsesWith(I, ConstantInt::getTrue(I.getType()));
+        break;
+      case ICmpInst::ICMP_UGT:
+      case ICmpInst::ICMP_UGE:
+      case ICmpInst::ICMP_ULT:
+      case ICmpInst::ICMP_ULE:
+        PredEqRemoved = CmpInst::getStrictPredicate(Pred);
+        C = APInt::getSignMask(X->getType()->getScalarSizeInBits()); 
+        C = PredEqRemoved == ICmpInst::ICMP_UGT ? C-1 : C+0;
+        Const = ConstantInt::get(X->getType(), C);
+        break;
+      case ICmpInst::ICMP_SGT:
+      case ICmpInst::ICMP_SGE:
+      case ICmpInst::ICMP_SLT:
+      case ICmpInst::ICMP_SLE:
+        PredEqRemoved = CmpInst::getStrictPredicate(Pred);
+        Const = ConstantInt::get(X->getType(),
+                                 PredEqRemoved == ICmpInst::ICMP_SGT ? -1 : 0,
+                                 /*IsSigned*/ true);
+        break;
+      default:
+        llvm_unreachable("not a valid predicate");
+      }
+      return new ICmpInst(PredEqRemoved, X, Const);
+    }
+
     // ~X < ~Y --> Y < X
     // ~X < C -->  X > ~C
     if (match(Op0, m_Not(m_Value(X)))) {



More information about the llvm-commits mailing list