[llvm] [InstCombine] Use samesign constraints in unsigned known-bits folds (PR #209675)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 14 20:46:01 PDT 2026


https://github.com/imkiva created https://github.com/llvm/llvm-project/pull/209675

Extracted from #209097.

For a non-poison unsigned `icmp samesign`, both operands have the same sign bit. Propagate a known sign bit between the temporary `KnownBits` values in `foldICmpUsingKnownBits()` so the existing range folds can use this constraint.

This enables endpoint folds in both signed halves of the integer range, for example:

```
  icmp samesign ugt i8 %x, 126 -> icmp eq i8 %x, 127
  icmp samesign ult i8 %x, 127 -> icmp ne i8 %x, 127
  icmp samesign ugt i8 %x, -2  -> icmp eq i8 %x, -1
```

-------------------------------

Although propagating the sign constraint is sound for any non-poison icmp samesign, applying it to signed and equality predicates is unnecessarily aggressive here. `foldICmpUsingKnownBits()` may refine such a comparison in isolation before higher-level select or logical combines can recognize a better canonical form. This caused an instruction-count regression:

```
%masked = and i32 %a, -1073741825
%cmp1 = icmp eq i32 %masked, 0
%cmp2 = icmp samesign sgt i32 %a, -1
%result = select i1 %cmp1, i1 true, i1 %cmp2
ret i1 %result
```

Without the restriction, the known-bits fold observes that a non-poison %cmp2 constrains %a to be negative and folds %cmp2 to false. The surrounding select then becomes:

```
%masked = and i32 %a, -1073741825
%cmp1 = icmp eq i32 %masked, 0
ret i1 %cmp1
```

The existing canonical result is the cheaper one-instruction sign test:

```
%cmp = icmp sgt i32 %a, -1
ret i1 %cmp
```

>From 3404f3c774c1692f16f27a382a666621cfc758f7 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Wed, 15 Jul 2026 11:16:13 +0800
Subject: [PATCH] [InstCombine] Use samesign constraints in unsigned known-bits
 folds

---
 .../InstCombine/InstCombineCompares.cpp       |  16 +++
 .../InstCombine/icmp-samesign-known-bits.ll   | 100 ++++++++++++++++++
 2 files changed, 116 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/icmp-samesign-known-bits.ll

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 42c2983034e22..fc0704140dbce 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -6991,6 +6991,22 @@ Instruction *InstCombinerImpl::foldICmpUsingKnownBits(ICmpInst &I) {
       return &I;
   }
 
+  // If an unsigned samesign comparison is not poison, both operands have the
+  // same sign bit. Propagate a known sign bit between the temporary KnownBits
+  // values so the existing range folds can use that constraint.
+  if (I.hasSameSign() && I.isUnsigned()) {
+    auto PropagateSignBit = [](const KnownBits &From, KnownBits &To) {
+      if (To.isNegative() || To.isNonNegative())
+        return;
+      if (From.isNegative())
+        To.makeNegative();
+      else if (From.isNonNegative())
+        To.makeNonNegative();
+    };
+    PropagateSignBit(Op0Known, Op1Known);
+    PropagateSignBit(Op1Known, Op0Known);
+  }
+
   if (!isa<Constant>(Op0) && Op0Known.isConstant())
     return new ICmpInst(
         Pred, ConstantExpr::getIntegerValue(Ty, Op0Known.getConstant()), Op1);
diff --git a/llvm/test/Transforms/InstCombine/icmp-samesign-known-bits.ll b/llvm/test/Transforms/InstCombine/icmp-samesign-known-bits.ll
new file mode 100644
index 0000000000000..5af4ba5d7be52
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-samesign-known-bits.ll
@@ -0,0 +1,100 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @ugt_smax_minus_one(i8 %x) {
+; CHECK-LABEL: define i1 @ugt_smax_minus_one(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], 127
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign ugt i8 %x, 126
+  ret i1 %cmp
+}
+
+define i1 @ult_smax(i8 %x) {
+; CHECK-LABEL: define i1 @ult_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[X]], 127
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign ult i8 %x, 127
+  ret i1 %cmp
+}
+
+define i1 @ugt_smax(i8 %x) {
+; CHECK-LABEL: define i1 @ugt_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %cmp = icmp samesign ugt i8 %x, 127
+  ret i1 %cmp
+}
+
+define i1 @ugt_zero(i8 %x) {
+; CHECK-LABEL: define i1 @ugt_zero(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[X]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign ugt i8 %x, 0
+  ret i1 %cmp
+}
+
+define i1 @ult_zero(i8 %x) {
+; CHECK-LABEL: define i1 @ult_zero(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %cmp = icmp samesign ult i8 %x, 0
+  ret i1 %cmp
+}
+
+define i1 @ugt_negative_endpoint(i8 %x) {
+; CHECK-LABEL: define i1 @ugt_negative_endpoint(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign ugt i8 %x, -2
+  ret i1 %cmp
+}
+
+define i1 @ult_negative_endpoint(i8 %x) {
+; CHECK-LABEL: define i1 @ult_negative_endpoint(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], -128
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp samesign ult i8 %x, -127
+  ret i1 %cmp
+}
+
+define <2 x i1> @ugt_smax_minus_one_vec(<2 x i8> %x) {
+; CHECK-LABEL: define <2 x i1> @ugt_smax_minus_one_vec(
+; CHECK-SAME: <2 x i8> [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[X]], splat (i8 127)
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %cmp = icmp samesign ugt <2 x i8> %x, splat (i8 126)
+  ret <2 x i1> %cmp
+}
+
+define i1 @ugt_smax_minus_one_no_samesign(i8 %x) {
+; CHECK-LABEL: define i1 @ugt_smax_minus_one_no_samesign(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[X]], 126
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp ugt i8 %x, 126
+  ret i1 %cmp
+}
+
+define i1 @ule_smax_no_samesign(i8 %x) {
+; CHECK-LABEL: define i1 @ule_smax_no_samesign(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[X]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %cmp = icmp ule i8 %x, 127
+  ret i1 %cmp
+}



More information about the llvm-commits mailing list