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 icmp pred x, ~x #73990

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

ParkHanbum
Copy link
Contributor

@ParkHanbum ParkHanbum commented Nov 30, 2023

simplify compare between specific variable X and NOT(X)

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

Fixed #57532.

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 30, 2023

@llvm/pr-subscribers-llvm-transforms

Author: hanbum (ParkHanbum)

Changes

simplify compare between specific variable X and NOT(X)

Proof: https://alive2.llvm.org/ce/z/89XAvd

Fixed #57532.


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp (+57)
  • (modified) llvm/test/Transforms/InstCombine/icmp-of-xor-x.ll (+197)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 9bc84c7dd6e1539..f49cf31ffa82627 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7035,6 +7035,63 @@ 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>  ~X
+    //           X s<= ~X  -->   X s<  ~X
+    //           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_Value(Y))) &&
+        (match(X, m_c_Xor(m_Specific(Y), m_AllOnes())) ||
+         match(Y, m_c_Xor(m_Specific(X), m_AllOnes())))) {
+      //  ~X s< X   -->     X s> ~X
+      //  ~X s> X   -->     X s< ~X
+      //  ~X u< X   -->     X u> ~X
+      //  ~X u> X   -->     X u< ~X
+      if (match(X, m_c_Xor(m_Specific(Y), m_AllOnes()))) {
+        Pred = I.getSwappedPredicate();
+        std::swap(X, Y);
+      }
+
+      Constant *Const;
+      APInt C(X->getType()->getScalarSizeInBits(), 0);
+      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:
+        Pred =
+            Pred < ICmpInst::ICMP_ULT ? ICmpInst::ICMP_UGT : ICmpInst::ICMP_ULT;
+        C.setSignBit();
+        Const = ConstantInt::get(X->getType(), C);
+        break;
+      case ICmpInst::ICMP_SGT:
+      case ICmpInst::ICMP_SGE:
+        Pred = ICmpInst::ICMP_SGT;
+        Const = ConstantInt::get(X->getType(), -1);
+        break;
+      case ICmpInst::ICMP_SLT:
+      case ICmpInst::ICMP_SLE:
+        Pred = ICmpInst::ICMP_SLT;
+        Const = ConstantInt::get(X->getType(), 0);
+        break;
+      default:
+        llvm_unreachable("not a valid predicate");
+      }
+
+      return new ICmpInst(Pred, X, Const);
+    }
+
     // ~X < ~Y --> Y < X
     // ~X < C -->  X > ~C
     if (match(Op0, m_Not(m_Value(X)))) {
diff --git a/llvm/test/Transforms/InstCombine/icmp-of-xor-x.ll b/llvm/test/Transforms/InstCombine/icmp-of-xor-x.ll
index ef4f2bfecfd8ed9..2975001425bd6e1 100644
--- a/llvm/test/Transforms/InstCombine/icmp-of-xor-x.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-of-xor-x.ll
@@ -5,6 +5,203 @@ declare void @llvm.assume(i1)
 declare void @barrier()
 declare void @use.i8(i8)
 
+; X s< ~X --> X s< 0
+define i1 @src_xnx_slt_slt(i8 %x) {
+; CHECK-LABEL: @src_xnx_slt_slt(
+; 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
+}
+; X s> ~X  -->  X s> -1
+define i1 @src_xnx_sgt_sgt(i8 %x) {
+; CHECK-LABEL: @src_xnx_sgt_sgt(
+; 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
+}
+; X (compare) ~X can never be equal.
+; X s<= ~X  -->  X s< ~X
+define i1 @src_xnx_sle_to_slt(i8 %x) {
+; CHECK-LABEL: @src_xnx_sle_to_slt(
+; 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
+define i1 @src_xnx_sge_to_sgt(i8 %x) {
+; CHECK-LABEL: @src_xnx_sge_to_sgt(
+; 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
+define i1 @src_nxx_slt_sgt(i8 %x) {
+; CHECK-LABEL: @src_nxx_slt_sgt(
+; 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
+define i1 @src_nxx_sgt_slt(i8 %x) {
+; CHECK-LABEL: @src_nxx_sgt_slt(
+; 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)
+define i1 @src_xnx_ult_ult(i8 %x) {
+; CHECK-LABEL: @src_xnx_ult_ult(
+; 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 @tgt_xnx_ult_ult(i8 %x) {
+; CHECK-LABEL: @tgt_xnx_ult_ult(
+; CHECK-NEXT:    [[R:%.*]] = icmp sgt i8 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %r = icmp ult i8 %x, 128
+  ret i1 %r
+}
+; X u> ~X   -->     X u< SIGNBIT_OF(X)
+define i1 @src_xnx_ugt_ugt(i8 %x) {
+; CHECK-LABEL: @src_xnx_ugt_ugt(
+; 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 @tgt_xnx_ugt_ugt(i8 %x) {
+; CHECK-LABEL: @tgt_xnx_ugt_ugt(
+; CHECK-NEXT:    [[R:%.*]] = icmp sgt i8 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %r = icmp ult i8 %x, 128
+  ret i1 %r
+}
+; X (compare) ~X can never be equal.
+; X u<= ~X   -->      X u< ~X
+define i1 @src_xnx_ule_to_ult(i8 %x) {
+; CHECK-LABEL: @src_xnx_ule_to_ult(
+; 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
+}
+define i1 @tgt_xnx_ule_to_ult(i8 %x) {
+; CHECK-LABEL: @tgt_xnx_ule_to_ult(
+; 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
+}
+; X u>= ~X   -->      X u> ~X
+define i1 @src_xnx_uge_to_ugt(i8 %x) {
+; CHECK-LABEL: @src_xnx_uge_to_ugt(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[X:%.*]], -128
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp uge i8 %x, %not
+  ret i1 %cmp
+}
+define i1 @tgt_xnx_uge_to_ugt(i8 %x) {
+; CHECK-LABEL: @tgt_xnx_uge_to_ugt(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[X:%.*]], -128
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp ugt 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
+define i1 @src_nxx_ult_ugt(i8 %x) {
+; CHECK-LABEL: @src_nxx_ult_ugt(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[X:%.*]], -128
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp ult i8 %not, %x
+  ret i1 %cmp
+}
+define i1 @tgt_nxx_ult_ugt(i8 %x) {
+; CHECK-LABEL: @tgt_nxx_ult_ugt(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[X:%.*]], -128
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %not = xor i8 %x, -1
+  %cmp = icmp ugt i8 %x, %not
+  ret i1 %cmp
+}
+; ~X u> X   -->     X u< ~X
+define i1 @src_nxx_ugt_ult(i8 %x) {
+; CHECK-LABEL: @src_nxx_ugt_ult(
+; 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
+}
+define i1 @tgt_nxx_ugt_ult(i8 %x) {
+; CHECK-LABEL: @tgt_nxx_ugt_ult(
+; 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
+}
+
+; X == ~X  -> false
+define i1 @src_xnx_eq_to_0(i8 %x) {
+; CHECK-LABEL: @src_xnx_eq_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_xnx_ne_to_1(i8 %x) {
+; CHECK-LABEL: @src_xnx_ne_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(

Copy link

github-actions bot commented Nov 30, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

@ParkHanbum
Copy link
Contributor Author

😭 What's wrong here? The code that appears to violate clang-code-format was not the code I submitted. anyone help

@ParkHanbum ParkHanbum force-pushed the issue_57532 branch 2 times, most recently from 3861d6b to d4d7c77 Compare December 2, 2023 12:49
@dtcxzyw dtcxzyw changed the title [InstCombine] simplify x (comp) ~x [InstCombine] simplify icmp pred x, ~x Dec 2, 2023
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.

Just some style nits.

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp Outdated Show resolved Hide resolved
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp Outdated Show resolved Hide resolved
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp Outdated Show resolved Hide resolved
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp Outdated Show resolved Hide resolved
@ParkHanbum
Copy link
Contributor Author

I do something wrong while resolve conflict. for fix that, temporary closed PR a bit while.

Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

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

LGTM. Please wait for additional approval from other reviewers.

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp Outdated Show resolved Hide resolved
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.

This looks good to me as well, but I have one note on the test. I don't think your "commutative" tests are testing what you think they are...

ret i1 %cmp
}
; X s<= ~X --> X s< 0
define i1 @src_sle(i8 %x) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please take a look at this: https://llvm.godbolt.org/z/zvMcnvsE4 Currently, the src_sle and src_sle_comm (and other comm pairs) actually all test the case where the not is on the left hand side. To test the case where the not is on the right hand side, you need to introduce an additional, higher-complexity operation, like a mul in this example.

The other thing I noticed is that predicates already get canonicalized to from non-strict to strict, apparently thanks to this transform:

// icmp (X ^ Y_NonZero) u>= X --> icmp (X ^ Y_NonZero) u> X
I don't think this really changes anything for your patch, but it indicates that maybe there is some generalization that doesn't require the xor constant to be -1. But let's leave that for another patch...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nikic I could not even imagine that!! thank you for let me know!

I don't sure that I exact understood what you say.
are you want to that I need to change tests so that tests are can tested as purpose as for now.

should I change the code shown in the link next time?
or I could move my codes position into that function foldICmpXorXX?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nikic maybe this problem solved after implemented code moved to foidIcmpXorXX. how do you think?

dtcxzyw added a commit to dtcxzyw/llvm-opt-benchmark that referenced this pull request Jan 4, 2024
@ParkHanbum
Copy link
Contributor Author

ParkHanbum commented Jan 9, 2024

@dtcxzyw How about moving my code inside the foldICmpXorXX function?
Is it okay if I do that? if moved, then I think that no need tests for commuative.

static Instruction *foldICmpXorXX(ICmpInst &I, const SimplifyQuery &Q,
                                  InstCombinerImpl &IC) {
  Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1), *A;
  // Normalize xor operand as operand 0.
  CmpInst::Predicate Pred = I.getPredicate();
  CmpInst::Predicate StrictPred = ICmpInst::getStrictPredicate(Pred);
  if (match(Op1, m_c_Xor(m_Specific(Op0), m_Value()))) {
    std::swap(Op0, Op1);
    Pred = ICmpInst::getSwappedPredicate(Pred);
    StrictPred = ICmpInst::getStrictPredicate(Pred);
  }
  if (!match(Op0, m_c_Xor(m_Specific(Op1), m_Value(A))))
    return nullptr;

  // Transform
  // X s< ~X, X s<= ~X, X u> ~X, X u>= ~X
  // --> X s< 0
  // X s> ~X, X s>= ~X, X u< ~X, X u<= ~X
  // --> X s> -1
  // X ==  ~X  -->   false
  // X !=  ~X  -->   true
  if (match(Op0, m_Not(m_Specific(Op1)))) {
    CmpInst::Predicate NewPred;
    switch (StrictPred) {
    case ICmpInst::ICMP_EQ:
      return IC.replaceInstUsesWith(I, ConstantInt::getFalse(I.getType()));
    case ICmpInst::ICMP_NE:
      return IC.replaceInstUsesWith(I, ConstantInt::getTrue(I.getType()));
    case ICmpInst::ICMP_SLT:
    case ICmpInst::ICMP_UGT:
      NewPred = ICmpInst::ICMP_SGT;
      break;
    case ICmpInst::ICMP_SGT:
    case ICmpInst::ICMP_ULT:
      NewPred = ICmpInst::ICMP_SLT;
      break;
    default:
      llvm_unreachable("not a valid predicate");
    }
    Constant *Const = NewPred == ICmpInst::ICMP_SLT
                          ? Constant::getNullValue(Op1->getType())
                          : Constant::getAllOnesValue(Op1->getType());
    return new ICmpInst(NewPred, Op1, Const);
  }

@dtcxzyw
Copy link
Member

dtcxzyw commented Jan 9, 2024

@dtcxzyw How about moving my code inside the foldICmpXorXX function? Is it okay if I do that? if moved, then I think that no need tests for commuative.

static Instruction *foldICmpXorXX(ICmpInst &I, const SimplifyQuery &Q,
                                  InstCombinerImpl &IC) {
  Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1), *A;
  // Normalize xor operand as operand 0.
  CmpInst::Predicate Pred = I.getPredicate();
  CmpInst::Predicate StrictPred = ICmpInst::getStrictPredicate(Pred);
  if (match(Op1, m_c_Xor(m_Specific(Op0), m_Value()))) {
    std::swap(Op0, Op1);
    Pred = ICmpInst::getSwappedPredicate(Pred);
    StrictPred = ICmpInst::getStrictPredicate(Pred);
  }
  if (!match(Op0, m_c_Xor(m_Specific(Op1), m_Value(A))))
    return nullptr;

  // Transform
  // X s< ~X, X s<= ~X, X u> ~X, X u>= ~X
  // --> X s< 0
  // X s> ~X, X s>= ~X, X u< ~X, X u<= ~X
  // --> X s> -1
  // X ==  ~X  -->   false
  // X !=  ~X  -->   true
  if (match(Op0, m_Not(m_Specific(Op1)))) {
    CmpInst::Predicate NewPred;
    switch (StrictPred) {
    case ICmpInst::ICMP_EQ:
      return IC.replaceInstUsesWith(I, ConstantInt::getFalse(I.getType()));
    case ICmpInst::ICMP_NE:
      return IC.replaceInstUsesWith(I, ConstantInt::getTrue(I.getType()));
    case ICmpInst::ICMP_SLT:
    case ICmpInst::ICMP_UGT:
      NewPred = ICmpInst::ICMP_SGT;
      break;
    case ICmpInst::ICMP_SGT:
    case ICmpInst::ICMP_ULT:
      NewPred = ICmpInst::ICMP_SLT;
      break;
    default:
      llvm_unreachable("not a valid predicate");
    }
    Constant *Const = NewPred == ICmpInst::ICMP_SLT
                          ? Constant::getNullValue(Op1->getType())
                          : Constant::getAllOnesValue(Op1->getType());
    return new ICmpInst(NewPred, Op1, Const);
  }

Yeah, it makes sense. Please keep the commutative tests.

@ParkHanbum ParkHanbum force-pushed the issue_57532 branch 2 times, most recently from 0ed8159 to c2c55c6 Compare January 10, 2024 00:06
@ParkHanbum ParkHanbum requested a review from nikic January 10, 2024 02:35
@sftlbcn
Copy link

sftlbcn commented Jan 10, 2024

more generalized patch:
https://reviews.llvm.org/D145280

@dtcxzyw
Copy link
Member

dtcxzyw commented Jan 10, 2024

more generalized patch: https://reviews.llvm.org/D145280

Great! Wondering if we should generalize it as these patterns may not exist in real-world applications...

@dtcxzyw
Copy link
Member

dtcxzyw commented Jan 10, 2024

These patterns may not exist in real-world applications...

Confirmed :(

@ParkHanbum
Copy link
Contributor Author

@sftlbcn I took a rough look at it, I don't understand everything, but it's great! I also wanted to take a more general approach using bit, but It was difficult because I was a newbie.

should I close this PR?

@elhewaty
Copy link
Contributor

@sftlbcn I took a rough look at it, I don't understand everything, but it's great! I also wanted to take a more general approach using bit, but It was difficult because I was a newbie.

should I close this PR?

No, you shouldn't, and you should ping the reviewer with in one week if there's not reply.
@dtcxzyw, ping.

@dtcxzyw
Copy link
Member

dtcxzyw commented Mar 10, 2024

@sftlbcn I took a rough look at it, I don't understand everything, but it's great! I also wanted to take a more general approach using bit, but It was difficult because I was a newbie.

should I close this PR?

No, I think the current implementation is good enough :)

This patch add testcase for comparison between X and X^Neg_X.

comparison between X and X^Neg_C is determined solely by
presence of the sign bit, so we can simplify it to checking
whether X is negative or not.
This patch simplifies the comparison between X and X^Neg_X.

comparison between X and X^Neg_C is determined solely by
presence of the sign bit, so we can simplify it to checking
whether X is negative or not.

Proof: https://alive2.llvm.org/ce/z/P6zXUx
Copy link

✅ With the latest revision this PR passed the Python code formatter.

@ParkHanbum
Copy link
Contributor Author

ParkHanbum commented Mar 23, 2024

-. eq/ne case removed.
-. vector test added.
The comparison between X and X^Neg_C was already being handled by instcombine. I wrote the code without realizing it, so the related code and tests were deleted.

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.

[InstCombine] Combine x < ~x into x < 0
7 participants