[llvm] [InstCombine] Fold signed icmp X, (X | C) when C is negative (PR #217805)

via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 20:11:35 PDT 2026


https://github.com/milkHongYe created https://github.com/llvm/llvm-project/pull/217805

OR-ing a negative constant `C` forces the sign bit of the result to 1. Therefore, in a signed comparison,` (X | C)` is always less than or equal to `X`: if X is already negative, the sign bit remains unchanged, and setting additional low bits makes the value move closer to zero; if X is non-negative, the sign bit is set to 1, making the result strictly less than `X`.
This gives us the following four patterns:
```
icmp sgt X, (X | C)  -->  icmp sge X, 0
icmp sle X, (X | C)  -->   icmp slt X, 0
icmp slt X, (X | C)  -->   false
icmp sge X, (X | C)  --> true
```
The swapped forms in the patch are handled uniformly by inverting the predicate before matching. This allows the same piece of code to cover all eight combinations of signed predicates and operand orders. The existing test cases only cover six of these patterns, so I added test cases for the remaining two.

>From 1c1f0e62c1bdcff2de462b78389a24b782d87b70 Mon Sep 17 00:00:00 2001
From: milkHongYe <1483685464 at qq.com>
Date: Fri, 21 Aug 2026 10:52:32 +0800
Subject: [PATCH] [InstCombine] Fold signed icmp X, (X | C) when C is negative

---
 .../InstCombine/InstCombineCompares.cpp       | 28 +++++++++++++
 .../Transforms/InstCombine/icmp-of-or-x.ll    | 39 ++++++++++++-------
 2 files changed, 52 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 5f0b3fb888ecf..915df7e233af6 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -5310,6 +5310,34 @@ Instruction *InstCombinerImpl::foldICmpBinOp(ICmpInst &I,
     return NewICmp;
 
   const CmpInst::Predicate Pred = I.getPredicate();
+
+  // icmp sgt X, (X | C) --> icmp sge X, 0, when C is negative
+  // icmp sle X, (X | C) --> icmp slt X, 0
+  // icmp slt X, (X | C) --> false, icmp sge X, (X | C) --> true
+  if (ICmpInst::isSigned(Pred)) {
+    const APInt *C;
+    Value *X = Op0;
+    CmpInst::Predicate P = Pred;
+    if (!match(Op1, m_Or(m_Specific(Op0), m_APInt(C)))) {
+      X = Op1;
+      P = ICmpInst::getSwappedPredicate(Pred);
+      if (!match(Op0, m_Or(m_Specific(Op1), m_APInt(C))))
+        X = nullptr;
+    }
+    if (X && C->isNegative()) {
+      if (P == ICmpInst::ICMP_SGT)
+        return new ICmpInst(ICmpInst::ICMP_SGE, X,
+                            Constant::getNullValue(X->getType()));
+      if (P == ICmpInst::ICMP_SLE)
+        return new ICmpInst(ICmpInst::ICMP_SLT, X,
+                            Constant::getNullValue(X->getType()));
+      if (P == ICmpInst::ICMP_SLT)
+        return replaceInstUsesWith(I, ConstantInt::getFalse(I.getType()));
+      if (P == ICmpInst::ICMP_SGE)
+        return replaceInstUsesWith(I, ConstantInt::getTrue(I.getType()));
+    }
+  }
+
   Value *X;
 
   // Convert add-with-unsigned-overflow comparisons into a 'not' with compare.
diff --git a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
index 993325f6ff0b0..e5f31c46ce630 100644
--- a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll
@@ -180,8 +180,7 @@ define <2 x i1> @or_ne_noundef_fail_reuse(<2 x i8> %x, <2 x i8> noundef %y) {
 
 define i1 @or_slt_intmin(i8 %x) {
 ; CHECK-LABEL: @or_slt_intmin(
-; CHECK-NEXT:    [[XN1:%.*]] = or i8 [[X:%.*]], -128
-; CHECK-NEXT:    [[R:%.*]] = icmp slt i8 [[XN1]], [[X]]
+; CHECK-NEXT:    [[R:%.*]] = icmp sgt i8 [[X:%.*]], -1
 ; CHECK-NEXT:    ret i1 [[R]]
 ;
   %xn1 = or i8 %x, 128
@@ -191,10 +190,7 @@ define i1 @or_slt_intmin(i8 %x) {
 
 define <2 x i1> @or_slt_intmin_2(<2 x i8> %xx, <2 x i8> %z) {
 ; CHECK-LABEL: @or_slt_intmin_2(
-; CHECK-NEXT:    [[X:%.*]] = add <2 x i8> [[XX:%.*]], [[Z:%.*]]
-; CHECK-NEXT:    [[XN1:%.*]] = or <2 x i8> [[X]], splat (i8 -128)
-; CHECK-NEXT:    [[R:%.*]] = icmp slt <2 x i8> [[X]], [[XN1]]
-; CHECK-NEXT:    ret <2 x i1> [[R]]
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
 ;
   %x = add <2 x i8> %xx, %z
   %xn1 = or <2 x i8> %x, <i8 128, i8 128>
@@ -208,8 +204,7 @@ define i1 @or_sle_intmin_indirect_2(i8 %xx, i8 %C, i8 %z) {
 ; CHECK-NEXT:    br i1 [[CMP]], label [[NEG:%.*]], label [[POS:%.*]]
 ; CHECK:       neg:
 ; CHECK-NEXT:    [[X:%.*]] = add i8 [[XX:%.*]], [[Z:%.*]]
-; CHECK-NEXT:    [[XN1:%.*]] = or i8 [[X]], -128
-; CHECK-NEXT:    [[R:%.*]] = icmp sle i8 [[X]], [[XN1]]
+; CHECK-NEXT:    [[R:%.*]] = icmp slt i8 [[X]], 0
 ; CHECK-NEXT:    ret i1 [[R]]
 ; CHECK:       pos:
 ; CHECK-NEXT:    call void @barrier()
@@ -231,8 +226,7 @@ pos:
 
 define i1 @or_sge_intmin(i8 %x) {
 ; CHECK-LABEL: @or_sge_intmin(
-; CHECK-NEXT:    [[XN1:%.*]] = or i8 [[X:%.*]], -128
-; CHECK-NEXT:    [[R:%.*]] = icmp sge i8 [[XN1]], [[X]]
+; CHECK-NEXT:    [[R:%.*]] = icmp slt i8 [[X:%.*]], 0
 ; CHECK-NEXT:    ret i1 [[R]]
 ;
   %xn1 = or i8 %x, 128
@@ -245,9 +239,7 @@ define i1 @or_sgt_intmin_indirect(i8 %x, i8 %C) {
 ; CHECK-NEXT:    [[C_NOT:%.*]] = icmp eq i8 [[C:%.*]], -128
 ; CHECK-NEXT:    br i1 [[C_NOT]], label [[NEG:%.*]], label [[POS:%.*]]
 ; CHECK:       neg:
-; CHECK-NEXT:    [[XN1:%.*]] = or i8 [[X:%.*]], -128
-; CHECK-NEXT:    [[R:%.*]] = icmp sgt i8 [[XN1]], [[X]]
-; CHECK-NEXT:    ret i1 [[R]]
+; CHECK-NEXT:    ret i1 false
 ; CHECK:       pos:
 ; CHECK-NEXT:    call void @barrier()
 ; CHECK-NEXT:    ret i1 false
@@ -268,8 +260,7 @@ pos:
 define <2 x i1> @or_sgt_intmin_2(<2 x i8> %xx, <2 x i8> %z) {
 ; CHECK-LABEL: @or_sgt_intmin_2(
 ; CHECK-NEXT:    [[X:%.*]] = add <2 x i8> [[XX:%.*]], [[Z:%.*]]
-; CHECK-NEXT:    [[XN1:%.*]] = or <2 x i8> [[X]], splat (i8 -128)
-; CHECK-NEXT:    [[R:%.*]] = icmp sgt <2 x i8> [[X]], [[XN1]]
+; CHECK-NEXT:    [[R:%.*]] = icmp sgt <2 x i8> [[X]], splat (i8 -1)
 ; CHECK-NEXT:    ret <2 x i1> [[R]]
 ;
   %x = add <2 x i8> %xx, %z
@@ -278,6 +269,24 @@ define <2 x i1> @or_sgt_intmin_2(<2 x i8> %xx, <2 x i8> %z) {
   ret <2 x i1> %r
 }
 
+define i1 @or_sge_intmin_rhs(i8 %x) {
+; CHECK-LABEL: @or_sge_intmin_rhs(
+; CHECK-NEXT:    ret i1 true
+;
+  %xn1 = or i8 %x, -128
+  %r = icmp sge i8 %x, %xn1
+  ret i1 %r
+}
+
+define i1 @or_sle_intmin_lhs(i8 %x) {
+; CHECK-LABEL: @or_sle_intmin_lhs(
+; CHECK-NEXT:    ret i1 true
+;
+  %xn1 = or i8 %x, -128
+  %r = icmp sle i8 %xn1, %x
+  ret i1 %r
+}
+
 define i1 @or_simplify_ule(i8 %y_in, i8 %rhs_in, i1 %c) {
 ; CHECK-LABEL: @or_simplify_ule(
 ; CHECK-NEXT:    [[RHS:%.*]] = and i8 [[RHS_IN:%.*]], -2



More information about the llvm-commits mailing list