[llvm] [InstCombine] simplify `x (comp) ~x` (PR #73990)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Nov 30 13:43:46 PST 2023
https://github.com/ParkHanbum created https://github.com/llvm/llvm-project/pull/73990
simplify compare between specific variable `X` and `NOT(X)`
Proof: https://alive2.llvm.org/ce/z/89XAvd
Fixed https://github.com/llvm/llvm-project/issues/57532.
>From cae153121bb6c4af88a979e95cfdbc8877225510 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/89XAvd
---
.../Transforms/InstCombine/icmp-of-xor-x.ll | 197 ++++++++++++++++++
1 file changed, 197 insertions(+)
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(
>From bac64b37174e16c41a2d84bab5eef434e6cf86a8 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/89XAvd
Fixed #57532.
---
.../InstCombine/InstCombineCompares.cpp | 57 +++++++++++++++++++
1 file changed, 57 insertions(+)
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)))) {
More information about the llvm-commits
mailing list