[llvm] [InstCombine] fold icmp of select with constants and invertible op (PR #147182)

Acthinks Yang via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 6 07:47:30 PDT 2025


https://github.com/Acthinks updated https://github.com/llvm/llvm-project/pull/147182

>From 65c071f98235aba983083825af75815afdfe3c93 Mon Sep 17 00:00:00 2001
From: Acthinks <yangzhh at mail.ustc.edu.cn>
Date: Sat, 5 Jul 2025 22:48:16 +0800
Subject: [PATCH 1/3] [Precommit] fold icmp of select with constants and
 invertible op

---
 .../icmp-select-operator-constant.ll          | 342 ++++++++++++++++++
 1 file changed, 342 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/icmp-select-operator-constant.ll

diff --git a/llvm/test/Transforms/InstCombine/icmp-select-operator-constant.ll b/llvm/test/Transforms/InstCombine/icmp-select-operator-constant.ll
new file mode 100644
index 0000000000000..f6c9a19cde79d
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-select-operator-constant.ll
@@ -0,0 +1,342 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+; shl nsw
+; scmp
+define i1 @shl_nsw_scmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @shl_nsw_scmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[A_SHL:%.*]] = shl nsw i8 [[A]], 3
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 16
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %a_shl = shl nsw i8 %a, 3
+  %sel = select i1 %cond, i8 8, i8 16
+  %cmp = icmp sgt i8 %a_shl, %sel
+  ret i1 %cmp
+}
+; scmp commutative
+define i1 @c_shl_nsw_scmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @c_shl_nsw_scmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 16
+; CHECK-NEXT:    [[A_SHL:%.*]] = shl nsw i8 [[A]], 3
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[SEL]], [[A_SHL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sel = select i1 %cond, i8 8, i8 16
+  %a_shl = shl nsw i8 %a, 3
+  %cmp = icmp sgt i8 %sel, %a_shl
+  ret i1 %cmp
+}
+; scmp mismatch
+define i1 @shl_nsw_scmp_mismatch(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @shl_nsw_scmp_mismatch(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[A_SHL:%.*]] = shl nsw i8 [[A]], 3
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %a_shl = shl nsw i8 %a, 3
+  %sel = select i1 %cond, i8 8, i8 1
+  %cmp = icmp sgt i8 %a_shl, %sel
+  ret i1 %cmp
+}
+; ucmp
+define i1 @shl_nsw_ucmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @shl_nsw_ucmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[A_SHL:%.*]] = shl nsw i8 [[A]], 3
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 24
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %a_shl = shl nsw i8 %a, 3
+  %sel = select i1 %cond, i8 8, i8 24
+  %cmp = icmp ugt i8 %a_shl, %sel
+  ret i1 %cmp
+}
+
+; shl nuw only ucmp/eq/ne
+; ucmp
+define i1 @shl_nuw_ucmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @shl_nuw_ucmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[A_SHL:%.*]] = shl nuw i8 [[A]], 3
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 32
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %a_shl = shl nuw i8 %a, 3
+  %sel = select i1 %cond, i8 8, i8 32
+  %cmp = icmp ult i8 %a_shl, %sel
+  ret i1 %cmp
+}
+
+; eq
+define i1 @shl_nuw_eqcmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @shl_nuw_eqcmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[A_SHL:%.*]] = shl nuw i8 [[A]], 3
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 64
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[SEL]], [[A_SHL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %a_shl = shl nuw i8 %a, 3
+  %sel = select i1 %cond, i8 8, i8 64
+  %cmp = icmp eq i8 %sel, %a_shl
+  ret i1 %cmp
+}
+
+; scmp mismatch
+define i1 @shl_nuw_scmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @shl_nuw_scmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[A_SHL:%.*]] = shl nuw i8 [[A]], 3
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 32
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %a_shl = shl nuw i8 %a, 3
+  %sel = select i1 %cond, i8 8, i8 32
+  %cmp = icmp slt i8 %a_shl, %sel
+  ret i1 %cmp
+}
+
+; ashr exact
+; ucmp
+define i1 @ashr_exact_ucmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @ashr_exact_ucmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[A_SHL:%.*]] = ashr exact i8 [[A]], 2
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 12, i8 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %a_shl = ashr exact i8 %a, 2
+  %sel = select i1 %cond, i8 12, i8 4
+  %cmp = icmp uge i8 %a_shl, %sel
+  ret i1 %cmp
+}
+; scmp
+define i1 @ashr_exact_scmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @ashr_exact_scmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[A_SHL:%.*]] = ashr exact i8 [[A]], 2
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %a_shl = ashr exact i8 %a, 2
+  %sel = select i1 %cond, i8 8, i8 4
+  %cmp = icmp sgt i8 %a_shl, %sel
+  ret i1 %cmp
+}
+
+; lshr exact only ucmp/eq/ne
+; ucmp
+define i1 @lshr_exact_ucmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @lshr_exact_ucmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[A_SHL:%.*]] = lshr exact i8 [[A]], 1
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 1, i8 3
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ugt i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %a_shl = lshr exact i8 %a, 1
+  %sel = select i1 %cond, i8 1, i8 3
+  %cmp = icmp ugt i8 %a_shl, %sel
+  ret i1 %cmp
+}
+; scmp mismatch
+define i1 @lshr_exact_scmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @lshr_exact_scmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[A_SHL:%.*]] = lshr exact i8 [[A]], 1
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 1, i8 3
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign uge i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %a_shl = lshr exact i8 %a, 1
+  %sel = select i1 %cond, i8 1, i8 3
+  %cmp = icmp sge i8 %a_shl, %sel
+  ret i1 %cmp
+}
+
+; zext only ucmp/eq/ne
+; ucmp
+define i1 @zext_ucmp(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
+; CHECK-LABEL: define i1 @zext_ucmp(
+; CHECK-SAME: i8 [[A:%.*]], i16 [[C0:%.*]], i16 [[C1:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = zext i8 [[A]] to i16
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i16 128, i16 64
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ugt i16 [[SEL]], [[IDX]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %idx = zext i8 %a to i16
+  %sel = select i1 %cond, i16 128, i16 64
+  %cmp = icmp ult i16 %idx, %sel
+  ret i1 %cmp
+}
+; scmp mismatch
+define i1 @zext_scmp_mismatch(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
+; CHECK-LABEL: define i1 @zext_scmp_mismatch(
+; CHECK-SAME: i8 [[A:%.*]], i16 [[C0:%.*]], i16 [[C1:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = zext i8 [[A]] to i16
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i16 128, i16 64
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ugt i16 [[SEL]], [[IDX]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %idx = zext i8 %a to i16
+  %sel = select i1 %cond, i16 128, i16 64
+  %cmp = icmp slt i16 %idx, %sel
+  ret i1 %cmp
+}
+
+; sext
+; ucmp
+define i1 @sext_ucmp(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
+; CHECK-LABEL: define i1 @sext_ucmp(
+; CHECK-SAME: i8 [[A:%.*]], i16 [[C0:%.*]], i16 [[C1:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = sext i8 [[A]] to i16
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i16 -127, i16 126
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i16 [[SEL]], [[IDX]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %idx = sext i8 %a to i16
+  %sel = select i1 %cond, i16 -127, i16 126
+  %cmp = icmp ult i16 %idx, %sel
+  ret i1 %cmp
+}
+; ucmp mismatch
+define i1 @sext_ucmp_mismatch(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
+; CHECK-LABEL: define i1 @sext_ucmp_mismatch(
+; CHECK-SAME: i8 [[A:%.*]], i16 [[C0:%.*]], i16 [[C1:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = sext i8 [[A]] to i16
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i16 -129, i16 128
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i16 [[SEL]], [[IDX]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %idx = sext i8 %a to i16
+  %sel = select i1 %cond, i16 -129, i16 128
+  %cmp = icmp ult i16 %idx, %sel
+  ret i1 %cmp
+}
+; scmp
+define i1 @sext_scmp(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
+; CHECK-LABEL: define i1 @sext_scmp(
+; CHECK-SAME: i8 [[A:%.*]], i16 [[C0:%.*]], i16 [[C1:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[IDX:%.*]] = sext i8 [[A]] to i16
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i16 -5, i16 9
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i16 [[SEL]], [[IDX]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %idx = sext i8 %a to i16
+  %sel = select i1 %cond, i16 -5, i16 9
+  %cmp = icmp slt i16 %idx, %sel
+  ret i1 %cmp
+}
+
+; or disjoint
+; ucmp
+define i1 @or_disjoint_ucmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @or_disjoint_ucmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[OR:%.*]] = or disjoint i8 [[A]], 3
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 11, i8 7
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[OR]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %or = or disjoint i8 %a, 3
+  %sel = select i1 %cond, i8 11, i8 7
+  %cmp = icmp ult i8 %or, %sel
+  ret i1 %cmp
+}
+; scmp mismatch
+define i1 @or_disjoint_scmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @or_disjoint_scmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[OR:%.*]] = or disjoint i8 [[A]], 3
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 11, i8 7
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[OR]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %or = or disjoint i8 %a, 3
+  %sel = select i1 %cond, i8 11, i8 7
+  %cmp = icmp slt i8 %or, %sel
+  ret i1 %cmp
+}
+; mismatch constant '4' not disjoint
+define i1 @or_ucmp_mismatch(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @or_ucmp_mismatch(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[OR:%.*]] = or disjoint i8 [[A]], 3
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 11, i8 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[OR]], [[SEL]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %or = or disjoint i8 %a, 3
+  %sel = select i1 %cond, i8 11, i8 4
+  %cmp = icmp ult i8 %or, %sel
+  ret i1 %cmp
+}
+
+; sub only eq/ne
+define i1 @sub_eq(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @sub_eq(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[COND]], i8 4, i8 12
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[A]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sub = sub i8 %a, 5
+  %sel = select i1 %cond, i8 -1, i8 7
+  %cmp = icmp eq i8 %sub, %sel
+  ret i1 %cmp
+}
+; ucmp mismatch
+define i1 @sub_ucmp(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @sub_ucmp(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = add i8 [[A]], -13
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i8 [[TMP1]], -8
+; CHECK-NEXT:    [[NOT_COND:%.*]] = xor i1 [[COND]], true
+; CHECK-NEXT:    [[CMP:%.*]] = select i1 [[NOT_COND]], i1 [[CMP1]], i1 false
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sub = sub i8 %a, 5
+  %sel = select i1 %cond, i8 -1, i8 7
+  %cmp = icmp ugt i8 %sub, %sel
+  ret i1 %cmp
+}
+
+; add only eq/ne
+define i1 @add_ne(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @add_ne(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[COND]], i8 -6, i8 2
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[A]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sub = add i8 %a, 5
+  %sel = select i1 %cond, i8 -1, i8 7
+  %cmp = icmp ne i8 %sub, %sel
+  ret i1 %cmp
+}
+
+; xor only eq/ne
+define i1 @xor_eq(i8 %a, i1 %cond) {
+; CHECK-LABEL: define i1 @xor_eq(
+; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 -1, i8 7
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i8 [[A]], [[SEL]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[TMP1]], 5
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sub = xor i8 %a, 5
+  %sel = select i1 %cond, i8 -1, i8 7
+  %cmp = icmp eq i8 %sub, %sel
+  ret i1 %cmp
+}

>From e633e7e29d82625ac323d9943bd8ee86d8c070af Mon Sep 17 00:00:00 2001
From: Acthinks <yangzhh at mail.ustc.edu.cn>
Date: Fri, 4 Jul 2025 19:25:09 +0800
Subject: [PATCH 2/3] [InstCombine] fold icmp of select with constants and
 invertible op

Proof: https://alive2.llvm.org/ce/z/5K6q5z
Closes #146642
---
 .../InstCombine/InstCombineCompares.cpp       | 110 ++++++++++++++++++
 .../icmp-select-operator-constant.ll          |  84 ++++++-------
 2 files changed, 144 insertions(+), 50 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index c6f317a668cfe..ee5ed3b5b75e6 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -4332,6 +4332,98 @@ Instruction *InstCombinerImpl::foldICmpInstWithConstantNotInt(ICmpInst &I) {
   return nullptr;
 }
 
+/// If the APInt C has the same invertible function with Operator RefOp in Pred,
+/// return the operands of the function corresponding to each input. Otherwise,
+/// return std::nullopt. This is equivalent to saying that Op1 pred Op2 is true
+/// exactly when the specified pair of RefOp pred C is true.
+/// alive2: https://alive2.llvm.org/ce/z/4jniEb
+static std::optional<std::pair<Value *, Value *>>
+getInvertibleOperandsWithPredicte(const Operator *RefOp, const APInt C,
+                                  CmpInst::Predicate Pred) {
+  APInt Op1C;
+  // for BinaryOperator just handle RefOp with constant Operand(1)
+  if (isa<BinaryOperator>(RefOp)) {
+    if (isa<ConstantInt>(RefOp->getOperand(1)))
+      Op1C = cast<ConstantInt>(RefOp->getOperand(1))->getValue();
+    else
+      return std::nullopt;
+  }
+
+  auto getOperands = [&](APInt A) -> auto {
+    return std::make_pair(RefOp->getOperand(0),
+                          ConstantInt::get(RefOp->getOperand(0)->getType(), A));
+  };
+  switch (RefOp->getOpcode()) {
+  default:
+    break;
+  case Instruction::Or:
+    if (cast<PossiblyDisjointInst>(RefOp)->isDisjoint() && ((C & Op1C) == Op1C))
+      return getOperands(C ^ Op1C);
+    break;
+  case Instruction::Add: {
+    // TODO: add/sub could support nsw/nuw for scmp/ucmp
+    if (CmpInst::isEquality(Pred))
+      return getOperands(C - Op1C);
+    break;
+  }
+  case Instruction::Xor: {
+    if (CmpInst::isEquality(Pred))
+      return getOperands(C ^ Op1C);
+    break;
+  }
+  case Instruction::Sub: {
+    if (CmpInst::isEquality(Pred))
+      return getOperands(C + Op1C);
+    break;
+  }
+  // alive2: https://alive2.llvm.org/ce/z/WPQznV
+  case Instruction::Shl: {
+    // Z = shl nsw X, Y <=> X = ashr exact Z, Y
+    // Z = shl nuw X, Y <=> X = lshr exact Z, Y
+    if (C.ashr(Op1C).shl(Op1C) == C) {
+      auto *OBO1 = cast<OverflowingBinaryOperator>(RefOp);
+      if (OBO1->hasNoSignedWrap())
+        return getOperands(C.ashr(Op1C));
+      else if (OBO1->hasNoUnsignedWrap() && !ICmpInst::isSigned(Pred))
+        return getOperands(C.lshr(Op1C));
+    }
+    break;
+  }
+  case Instruction::AShr: {
+    // Z = ashr exact X, Y <=> X = shl nsw Z, Y
+    auto *PEO1 = cast<PossiblyExactOperator>(RefOp);
+    if (PEO1->isExact() && C.shl(Op1C).ashr(Op1C) == C)
+      return getOperands(C.shl(Op1C));
+    break;
+  }
+  case Instruction::LShr: {
+    // Z = lshr exact X, Y <=> X = shl nuw Z, Y
+    auto *PEO1 = cast<PossiblyExactOperator>(RefOp);
+    if (PEO1->isExact() && C.shl(Op1C).lshr(Op1C) == C &&
+        !ICmpInst::isSigned(Pred))
+      return getOperands(C.shl(Op1C));
+    break;
+  }
+  case Instruction::SExt: {
+    unsigned NumBits = RefOp->getType()->getScalarSizeInBits();
+    unsigned NumBitsOp0 =
+        RefOp->getOperand(0)->getType()->getScalarSizeInBits();
+    if (C.trunc(NumBitsOp0).sext(NumBits) == C)
+      return getOperands(C.trunc(NumBitsOp0));
+    break;
+  }
+  case Instruction::ZExt: {
+    unsigned NumBits = RefOp->getType()->getScalarSizeInBits();
+    unsigned NumBitsOp0 =
+        RefOp->getOperand(0)->getType()->getScalarSizeInBits();
+    if (C.trunc(NumBitsOp0).zext(NumBits) == C && !ICmpInst::isSigned(Pred))
+      return getOperands(C.trunc(NumBitsOp0));
+    break;
+  }
+  }
+  return std::nullopt;
+}
+
 Instruction *InstCombinerImpl::foldSelectICmp(CmpPredicate Pred, SelectInst *SI,
                                               Value *RHS, const ICmpInst &I) {
   // Try to fold the comparison into the select arms, which will cause the
@@ -4391,6 +4483,24 @@ Instruction *InstCombinerImpl::foldSelectICmp(CmpPredicate Pred, SelectInst *SI,
     return SelectInst::Create(SI->getOperand(0), Op1, Op2);
   }
 
+  // fold select with constants and invertible op
+  Value *Cond;
+  const APInt *C1, *C2;
+  auto *RHSOp = dyn_cast<Operator>(RHS);
+  if (RHSOp &&
+      match(SI, m_OneUse(m_Select(m_Value(Cond), m_APInt(C1), m_APInt(C2))))) {
+    if (auto Values0 = getInvertibleOperandsWithPredicte(RHSOp, *C1, Pred)) {
+      if (auto Values1 = getInvertibleOperandsWithPredicte(RHSOp, *C2, Pred)) {
+        assert(Values0->first == Values1->first &&
+               "Invertible Operand0 mismatch");
+        auto *NewSI = Builder.CreateSelect(Cond, Values0->second,
+                                           Values1->second, SI->getName());
+        return ICmpInst::Create(Instruction::ICmp, I.getPredicate(), NewSI,
+                                Values0->first, I.getName());
+      }
+    }
+  }
+
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/icmp-select-operator-constant.ll b/llvm/test/Transforms/InstCombine/icmp-select-operator-constant.ll
index f6c9a19cde79d..944d0a00bfe75 100644
--- a/llvm/test/Transforms/InstCombine/icmp-select-operator-constant.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-select-operator-constant.ll
@@ -6,9 +6,8 @@
 define i1 @shl_nsw_scmp(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @shl_nsw_scmp(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[A_SHL:%.*]] = shl nsw i8 [[A]], 3
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 16
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 1, i8 2
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %a_shl = shl nsw i8 %a, 3
@@ -20,9 +19,8 @@ define i1 @shl_nsw_scmp(i8 %a, i1 %cond) {
 define i1 @c_shl_nsw_scmp(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @c_shl_nsw_scmp(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 16
-; CHECK-NEXT:    [[A_SHL:%.*]] = shl nsw i8 [[A]], 3
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[SEL]], [[A_SHL]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 1, i8 2
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %sel = select i1 %cond, i8 8, i8 16
@@ -48,9 +46,8 @@ define i1 @shl_nsw_scmp_mismatch(i8 %a, i1 %cond) {
 define i1 @shl_nsw_ucmp(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @shl_nsw_ucmp(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[A_SHL:%.*]] = shl nsw i8 [[A]], 3
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 24
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 1, i8 3
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %a_shl = shl nsw i8 %a, 3
@@ -64,9 +61,8 @@ define i1 @shl_nsw_ucmp(i8 %a, i1 %cond) {
 define i1 @shl_nuw_ucmp(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @shl_nuw_ucmp(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[A_SHL:%.*]] = shl nuw i8 [[A]], 3
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 32
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 1, i8 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %a_shl = shl nuw i8 %a, 3
@@ -79,9 +75,8 @@ define i1 @shl_nuw_ucmp(i8 %a, i1 %cond) {
 define i1 @shl_nuw_eqcmp(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @shl_nuw_eqcmp(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[A_SHL:%.*]] = shl nuw i8 [[A]], 3
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 64
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[SEL]], [[A_SHL]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 1, i8 8
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %a_shl = shl nuw i8 %a, 3
@@ -110,9 +105,8 @@ define i1 @shl_nuw_scmp(i8 %a, i1 %cond) {
 define i1 @ashr_exact_ucmp(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @ashr_exact_ucmp(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[A_SHL:%.*]] = ashr exact i8 [[A]], 2
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 12, i8 4
-; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 48, i8 16
+; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %a_shl = ashr exact i8 %a, 2
@@ -124,9 +118,8 @@ define i1 @ashr_exact_ucmp(i8 %a, i1 %cond) {
 define i1 @ashr_exact_scmp(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @ashr_exact_scmp(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[A_SHL:%.*]] = ashr exact i8 [[A]], 2
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 8, i8 4
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 32, i8 16
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %a_shl = ashr exact i8 %a, 2
@@ -140,9 +133,8 @@ define i1 @ashr_exact_scmp(i8 %a, i1 %cond) {
 define i1 @lshr_exact_ucmp(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @lshr_exact_ucmp(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[A_SHL:%.*]] = lshr exact i8 [[A]], 1
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 1, i8 3
-; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ugt i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 2, i8 6
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %a_shl = lshr exact i8 %a, 1
@@ -154,9 +146,8 @@ define i1 @lshr_exact_ucmp(i8 %a, i1 %cond) {
 define i1 @lshr_exact_scmp(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @lshr_exact_scmp(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[A_SHL:%.*]] = lshr exact i8 [[A]], 1
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 1, i8 3
-; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign uge i8 [[A_SHL]], [[SEL]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 2, i8 6
+; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %a_shl = lshr exact i8 %a, 1
@@ -170,9 +161,8 @@ define i1 @lshr_exact_scmp(i8 %a, i1 %cond) {
 define i1 @zext_ucmp(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
 ; CHECK-LABEL: define i1 @zext_ucmp(
 ; CHECK-SAME: i8 [[A:%.*]], i16 [[C0:%.*]], i16 [[C1:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[IDX:%.*]] = zext i8 [[A]] to i16
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i16 128, i16 64
-; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ugt i16 [[SEL]], [[IDX]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 -128, i8 64
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %idx = zext i8 %a to i16
@@ -184,9 +174,8 @@ define i1 @zext_ucmp(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
 define i1 @zext_scmp_mismatch(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
 ; CHECK-LABEL: define i1 @zext_scmp_mismatch(
 ; CHECK-SAME: i8 [[A:%.*]], i16 [[C0:%.*]], i16 [[C1:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[IDX:%.*]] = zext i8 [[A]] to i16
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i16 128, i16 64
-; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ugt i16 [[SEL]], [[IDX]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 -128, i8 64
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %idx = zext i8 %a to i16
@@ -200,9 +189,8 @@ define i1 @zext_scmp_mismatch(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
 define i1 @sext_ucmp(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
 ; CHECK-LABEL: define i1 @sext_ucmp(
 ; CHECK-SAME: i8 [[A:%.*]], i16 [[C0:%.*]], i16 [[C1:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[IDX:%.*]] = sext i8 [[A]] to i16
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i16 -127, i16 126
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i16 [[SEL]], [[IDX]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 -127, i8 126
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %idx = sext i8 %a to i16
@@ -228,9 +216,8 @@ define i1 @sext_ucmp_mismatch(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
 define i1 @sext_scmp(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
 ; CHECK-LABEL: define i1 @sext_scmp(
 ; CHECK-SAME: i8 [[A:%.*]], i16 [[C0:%.*]], i16 [[C1:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[IDX:%.*]] = sext i8 [[A]] to i16
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i16 -5, i16 9
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i16 [[SEL]], [[IDX]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 -5, i8 9
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %idx = sext i8 %a to i16
@@ -244,9 +231,8 @@ define i1 @sext_scmp(i8 %a, i16 %c0, i16 %c1, i1 %cond) {
 define i1 @or_disjoint_ucmp(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @or_disjoint_ucmp(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[OR:%.*]] = or disjoint i8 [[A]], 3
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 11, i8 7
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[OR]], [[SEL]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 8, i8 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %or = or disjoint i8 %a, 3
@@ -258,9 +244,8 @@ define i1 @or_disjoint_ucmp(i8 %a, i1 %cond) {
 define i1 @or_disjoint_scmp(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @or_disjoint_scmp(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[OR:%.*]] = or disjoint i8 [[A]], 3
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 11, i8 7
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[OR]], [[SEL]]
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 8, i8 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %or = or disjoint i8 %a, 3
@@ -288,7 +273,7 @@ define i1 @sub_eq(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @sub_eq(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
 ; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[COND]], i8 4, i8 12
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[A]], [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[TMP1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %sub = sub i8 %a, 5
@@ -317,7 +302,7 @@ define i1 @add_ne(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @add_ne(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
 ; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[COND]], i8 -6, i8 2
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[A]], [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[TMP1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %sub = add i8 %a, 5
@@ -330,9 +315,8 @@ define i1 @add_ne(i8 %a, i1 %cond) {
 define i1 @xor_eq(i8 %a, i1 %cond) {
 ; CHECK-LABEL: define i1 @xor_eq(
 ; CHECK-SAME: i8 [[A:%.*]], i1 [[COND:%.*]]) {
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i8 -1, i8 7
-; CHECK-NEXT:    [[TMP1:%.*]] = xor i8 [[A]], [[SEL]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[TMP1]], 5
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND]], i8 -6, i8 2
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[SEL1]], [[A]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %sub = xor i8 %a, 5

>From 0431871a56560722f581a8306d5299feddd9d642 Mon Sep 17 00:00:00 2001
From: Acthinks Yang <yangzhh at mail.ustc.edu.cn>
Date: Sun, 6 Jul 2025 22:47:22 +0800
Subject: [PATCH 3/3] get the right Pred

Co-authored-by: Yingwei Zheng <dtcxzyw at qq.com>
---
 llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index ee5ed3b5b75e6..72767943cf78f 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -4495,7 +4495,7 @@ Instruction *InstCombinerImpl::foldSelectICmp(CmpPredicate Pred, SelectInst *SI,
                "Invertible Operand0 mismatch");
         auto *NewSI = Builder.CreateSelect(Cond, Values0->second,
                                            Values1->second, SI->getName());
-        return ICmpInst::Create(Instruction::ICmp, I.getPredicate(), NewSI,
+        return ICmpInst::Create(Instruction::ICmp, Pred, NewSI,
                                 Values0->first, I.getName());
       }
     }



More information about the llvm-commits mailing list