[llvm] [InstCombine] Support trunc nuw condition in foldAndOrOfICmps (PR #221555)

Andreas Jonson via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 6 04:04:49 PDT 2026


https://github.com/andjo403 created https://github.com/llvm/llvm-project/pull/221555

Fixes regressions seen in https://github.com/llvm/llvm-project/pull/184182

first commit is a NFC rewrite, test still pass and no changes in https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1205

one proof: https://alive2.llvm.org/ce/z/8eRbWu

>From 7cc6f8aa3a38a491604906bcc939c47d054ae1cb Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sat, 5 Sep 2026 17:45:24 +0200
Subject: [PATCH 1/3] [InstCombine] Prepare foldAndOrOfICmps to handle trunc
 nuw (NFC)

---
 .../InstCombine/InstCombineAndOrXor.cpp       | 428 ++++++++++--------
 .../InstCombine/InstCombineInternal.h         |  14 +-
 2 files changed, 241 insertions(+), 201 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index cd7fed74a4cf3..d89110e65d9f4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -723,52 +723,56 @@ static Value *foldLogOpOfMaskedICmps(Value *LHS, Value *RHS, bool IsAnd,
 /// Example: (icmp sge x, 0) & (icmp slt x, n) --> icmp ult x, n
 /// If \p Inverted is true then the check is for the inverted range, e.g.
 /// (icmp slt x, 0) | (icmp sgt x, n) --> icmp ugt x, n
-Value *InstCombinerImpl::simplifyRangeCheck(ICmpInst *Cmp0, ICmpInst *Cmp1,
-                                            bool Inverted) {
+Value *InstCombinerImpl::simplifyRangeCheck(CmpPredicate PredL, Value *LHS0,
+                                            Value *LHS1, CmpPredicate PredR,
+                                            Value *RHS0, Value *RHS1,
+                                            Value *RHS, bool Inverted) {
   // Check the lower range comparison, e.g. x >= 0
   // InstCombine already ensured that if there is a constant it's on the RHS.
-  ConstantInt *RangeStart = dyn_cast<ConstantInt>(Cmp0->getOperand(1));
+  ConstantInt *RangeStart = dyn_cast<ConstantInt>(LHS1);
   if (!RangeStart)
     return nullptr;
 
-  ICmpInst::Predicate Pred0 = (Inverted ? Cmp0->getInversePredicate() :
-                               Cmp0->getPredicate());
+  if (Inverted) {
+    PredL = CmpPredicate::getInverse(PredL);
+    PredR = CmpPredicate::getInverse(PredR);
+  }
 
   // Accept x > -1 or x >= 0 (after potentially inverting the predicate).
-  if (!((Pred0 == ICmpInst::ICMP_SGT && RangeStart->isMinusOne()) ||
-        (Pred0 == ICmpInst::ICMP_SGE && RangeStart->isZero())))
+  if (!((PredL == ICmpInst::ICMP_SGT && RangeStart->isMinusOne()) ||
+        (PredL == ICmpInst::ICMP_SGE && RangeStart->isZero())))
     return nullptr;
 
-  ICmpInst::Predicate Pred1 = (Inverted ? Cmp1->getInversePredicate() :
-                               Cmp1->getPredicate());
-
-  Value *Input = Cmp0->getOperand(0);
-  Value *Cmp1Op0 = Cmp1->getOperand(0);
-  Value *Cmp1Op1 = Cmp1->getOperand(1);
+  Value *Input = LHS0;
   Value *RangeEnd;
-  if (match(Cmp1Op0, m_SExtOrSelf(m_Specific(Input)))) {
+  if (match(RHS0, m_SExtOrSelf(m_Specific(Input)))) {
     // For the upper range compare we have: icmp x, n
-    Input = Cmp1Op0;
-    RangeEnd = Cmp1Op1;
-  } else if (match(Cmp1Op1, m_SExtOrSelf(m_Specific(Input)))) {
+    Input = RHS0;
+    RangeEnd = RHS1;
+  } else if (match(RHS1, m_SExtOrSelf(m_Specific(Input)))) {
     // For the upper range compare we have: icmp n, x
-    Input = Cmp1Op1;
-    RangeEnd = Cmp1Op0;
-    Pred1 = ICmpInst::getSwappedPredicate(Pred1);
+    Input = RHS1;
+    RangeEnd = RHS0;
+    PredR = CmpPredicate::getSwapped(PredR);
   } else {
     return nullptr;
   }
 
   // Check the upper range comparison, e.g. x < n
   ICmpInst::Predicate NewPred;
-  switch (Pred1) {
-    case ICmpInst::ICMP_SLT: NewPred = ICmpInst::ICMP_ULT; break;
-    case ICmpInst::ICMP_SLE: NewPred = ICmpInst::ICMP_ULE; break;
-    default: return nullptr;
+  switch (PredR) {
+  case ICmpInst::ICMP_SLT:
+    NewPred = ICmpInst::ICMP_ULT;
+    break;
+  case ICmpInst::ICMP_SLE:
+    NewPred = ICmpInst::ICMP_ULE;
+    break;
+  default:
+    return nullptr;
   }
 
   // This simplification is only valid if the upper range is not negative.
-  KnownBits Known = computeKnownBits(RangeEnd, Cmp1);
+  KnownBits Known = computeKnownBits(RangeEnd, cast<Instruction>(RHS));
   if (!Known.isNonNegative())
     return nullptr;
 
@@ -782,21 +786,27 @@ Value *InstCombinerImpl::simplifyRangeCheck(ICmpInst *Cmp0, ICmpInst *Cmp1,
 //      -> (icmp eq (and X, Pow2OrZero), X)
 // (and (icmp ne X, 0), (icmp ne X, Pow2OrZero))
 //      -> (icmp ne (and X, Pow2OrZero), X)
-static Value *
-foldAndOrOfICmpsWithPow2AndWithZero(InstCombiner::BuilderTy &Builder,
-                                    ICmpInst *LHS, ICmpInst *RHS, bool IsAnd,
-                                    const SimplifyQuery &Q) {
-  CmpPredicate Pred = IsAnd ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ;
+static Value *foldAndOrOfICmpsWithPow2AndWithZero(
+    InstCombiner::BuilderTy &Builder, CmpPredicate PredL, Value *LHS0,
+    Value *LHS1, bool LHSOneUse, CmpPredicate PredR, Value *RHS0, Value *RHS1,
+    bool RHSOneUse, bool IsAnd, const SimplifyQuery &Q) {
+  CmpInst::Predicate Pred = IsAnd ? CmpInst::ICMP_NE : CmpInst::ICMP_EQ;
   // Make sure we have right compares for our op.
-  if (LHS->getPredicate() != Pred || RHS->getPredicate() != Pred)
+  if (PredL != Pred || PredR != Pred)
     return nullptr;
 
   // Make it so we can match LHS against the (icmp eq/ne X, 0) just for
   // simplicity.
-  if (match(RHS->getOperand(1), m_Zero()))
-    std::swap(LHS, RHS);
+  if (match(RHS1, m_Zero())) {
+    std::swap(PredL, PredR);
+    std::swap(LHS0, RHS0);
+    std::swap(LHS1, RHS1);
+  }
+
+  if (RHS1 == LHS0) {
+    std::swap(RHS0, RHS1);
+  }
 
-  Value *Pow2, *Op;
   // Match the desired pattern:
   // LHS: (icmp eq/ne X, 0)
   // RHS: (icmp eq/ne X, Pow2OrZero)
@@ -806,14 +816,13 @@ foldAndOrOfICmpsWithPow2AndWithZero(InstCombiner::BuilderTy &Builder,
   // be one-use. We don't create additional instructions if only one
   // of them is one-use. So cases where one is one-use and the other
   // is two-use might be profitable.
-  if (!match(LHS, m_OneUse(m_ICmp(Pred, m_Value(Op), m_Zero()))) ||
-      !match(RHS, m_OneUse(m_c_ICmp(Pred, m_Specific(Op), m_Value(Pow2)))) ||
-      match(Pow2, m_One()) ||
-      !isKnownToBeAPowerOfTwo(Pow2, Q.DL, /*OrZero=*/true, Q.AC, Q.CxtI, Q.DT))
+  if (!LHSOneUse || !RHSOneUse || !match(LHS1, m_Zero()) || RHS0 != LHS0 ||
+      match(RHS1, m_One()) ||
+      !isKnownToBeAPowerOfTwo(RHS1, Q.DL, /*OrZero=*/true, Q.AC, Q.CxtI, Q.DT))
     return nullptr;
 
-  Value *And = Builder.CreateAnd(Op, Pow2);
-  return Builder.CreateICmp(Pred, And, Op);
+  Value *And = Builder.CreateAnd(LHS0, RHS1);
+  return Builder.CreateICmp(Pred, And, LHS0);
 }
 
 /// General pattern:
@@ -846,19 +855,21 @@ foldAndOrOfICmpsWithPow2AndWithZero(InstCombiner::BuilderTy &Builder,
 /// masked bits are zero.
 /// So this should be transformed to:
 ///   %r = icmp ult i32 %arg, 128
-static Value *foldSignedTruncationCheck(ICmpInst *ICmp0, ICmpInst *ICmp1,
+static Value *foldSignedTruncationCheck(CmpPredicate PredL, Value *LHS0,
+                                        Value *LHS1, CmpPredicate PredR,
+                                        Value *RHS0, Value *RHS1,
                                         Instruction &CxtI,
                                         InstCombiner::BuilderTy &Builder) {
   assert(CxtI.getOpcode() == Instruction::And);
 
   // Match  icmp ult (add %arg, C01), C1   (C1 == C01 << 1; powers of two)
-  auto tryToMatchSignedTruncationCheck = [](ICmpInst *ICmp, Value *&X,
+  auto tryToMatchSignedTruncationCheck = [](CmpPredicate Pred, Value *LHS,
+                                            Value *RHS, Value *&X,
                                             APInt &SignBitMask) -> bool {
     const APInt *I01, *I1; // powers of two; I1 == I01 << 1
-    if (!(match(ICmp, m_SpecificICmp(ICmpInst::ICMP_ULT,
-                                     m_Add(m_Value(X), m_Power2(I01)),
-                                     m_Power2(I1))) &&
-          I1->ugt(*I01) && I01->shl(1) == *I1))
+    if (Pred != ICmpInst::ICMP_ULT ||
+        !match(LHS, m_Add(m_Value(X), m_Power2(I01))) ||
+        !match(RHS, m_Power2(I1)) || I1->ule(*I01) || I01->shl(1) != *I1)
       return false;
     // Which bit is the new sign bit as per the 'signed truncation' pattern?
     SignBitMask = *I01;
@@ -869,25 +880,24 @@ static Value *foldSignedTruncationCheck(ICmpInst *ICmp0, ICmpInst *ICmp1,
   // We need to match this first, else we will mismatch commutative cases.
   Value *X1;
   APInt HighestBit;
-  ICmpInst *OtherICmp;
-  if (tryToMatchSignedTruncationCheck(ICmp1, X1, HighestBit))
-    OtherICmp = ICmp0;
-  else if (tryToMatchSignedTruncationCheck(ICmp0, X1, HighestBit))
-    OtherICmp = ICmp1;
-  else
+  if (tryToMatchSignedTruncationCheck(PredR, RHS0, RHS1, X1, HighestBit)) {
+    std::swap(PredL, PredR);
+    std::swap(LHS0, RHS0);
+    std::swap(LHS1, RHS1);
+  } else if (!tryToMatchSignedTruncationCheck(PredL, LHS0, LHS1, X1,
+                                              HighestBit))
     return nullptr;
 
   assert(HighestBit.isPowerOf2() && "expected to be power of two (non-zero)");
 
   // Try to match/decompose into:  icmp eq (X & Mask), 0
-  auto tryToDecompose = [](ICmpInst *ICmp, Value *&X,
+  auto tryToDecompose = [](CmpPredicate Pred, Value *LHS, Value *RHS, Value *&X,
                            APInt &UnsetBitsMask) -> bool {
-    CmpPredicate Pred = ICmp->getPredicate();
     // Can it be decomposed into  icmp eq (X & Mask), 0  ?
-    auto Res = llvm::decomposeBitTestICmp(
-        ICmp->getOperand(0), ICmp->getOperand(1), Pred,
-        /*LookThroughTrunc=*/false, /*AllowNonZeroC=*/false,
-        /*DecomposeAnd=*/true);
+    auto Res = llvm::decomposeBitTestICmp(LHS, RHS, Pred,
+                                          /*LookThroughTrunc=*/false,
+                                          /*AllowNonZeroC=*/false,
+                                          /*DecomposeAnd=*/true);
     if (Res && Res->Pred == ICmpInst::ICMP_EQ) {
       X = Res->X;
       UnsetBitsMask = Res->Mask;
@@ -900,7 +910,7 @@ static Value *foldSignedTruncationCheck(ICmpInst *ICmp0, ICmpInst *ICmp1,
   // And the other icmp needs to be decomposable into a bit test.
   Value *X0;
   APInt UnsetBitsMask;
-  if (!tryToDecompose(OtherICmp, X0, UnsetBitsMask))
+  if (!tryToDecompose(PredR, RHS0, RHS1, X0, UnsetBitsMask))
     return nullptr;
 
   assert(!UnsetBitsMask.isZero() && "empty mask makes no sense.");
@@ -942,23 +952,24 @@ static Value *foldSignedTruncationCheck(ICmpInst *ICmp0, ICmpInst *ICmp1,
 /// fold (icmp ne ctpop(X) 1) & (icmp ne X 0) into (icmp ugt ctpop(X) 1).
 /// Also used for logical and/or, must be poison safe if range attributes are
 /// dropped.
-static Value *foldIsPowerOf2OrZero(ICmpInst *Cmp0, ICmpInst *Cmp1, bool IsAnd,
-                                   InstCombiner::BuilderTy &Builder,
+static Value *foldIsPowerOf2OrZero(CmpPredicate PredL, Value *LHS0, Value *LHS1,
+                                   CmpPredicate PredR, Value *RHS0, Value *RHS1,
+                                   bool IsAnd, InstCombiner::BuilderTy &Builder,
                                    InstCombinerImpl &IC) {
-  CmpPredicate Pred0, Pred1;
+
   Value *X;
-  if (!match(Cmp0, m_ICmp(Pred0, m_Ctpop(m_Value(X)), m_SpecificInt(1))) ||
-      !match(Cmp1, m_ICmp(Pred1, m_Specific(X), m_ZeroInt())))
+  if (!match(LHS0, m_Ctpop(m_Value(X))) || !match(LHS1, m_SpecificInt(1)) ||
+      RHS0 != X || !match(RHS1, m_ZeroInt()))
     return nullptr;
 
-  auto *CtPop = cast<Instruction>(Cmp0->getOperand(0));
-  if (IsAnd && Pred0 == ICmpInst::ICMP_NE && Pred1 == ICmpInst::ICMP_NE) {
+  auto *CtPop = cast<Instruction>(LHS0);
+  if (IsAnd && PredL == ICmpInst::ICMP_NE && PredR == ICmpInst::ICMP_NE) {
     // Drop range attributes and re-infer them in the next iteration.
     CtPop->dropPoisonGeneratingAnnotations();
     IC.addToWorklist(CtPop);
     return Builder.CreateICmpUGT(CtPop, ConstantInt::get(CtPop->getType(), 1));
   }
-  if (!IsAnd && Pred0 == ICmpInst::ICMP_EQ && Pred1 == ICmpInst::ICMP_EQ) {
+  if (!IsAnd && PredL == ICmpInst::ICMP_EQ && PredR == ICmpInst::ICMP_EQ) {
     // Drop range attributes and re-infer them in the next iteration.
     CtPop->dropPoisonGeneratingAnnotations();
     IC.addToWorklist(CtPop);
@@ -971,33 +982,32 @@ static Value *foldIsPowerOf2OrZero(ICmpInst *Cmp0, ICmpInst *Cmp1, bool IsAnd,
 /// Reduce a pair of compares that check if a value has exactly 1 bit set.
 /// Also used for logical and/or, must be poison safe if range attributes are
 /// dropped.
-static Value *foldIsPowerOf2(ICmpInst *Cmp0, ICmpInst *Cmp1, bool JoinedByAnd,
-                             InstCombiner::BuilderTy &Builder,
+static Value *foldIsPowerOf2(CmpPredicate PredL, Value *LHS0, Value *LHS1,
+                             CmpPredicate PredR, Value *RHS0, Value *RHS1,
+                             bool JoinedByAnd, InstCombiner::BuilderTy &Builder,
                              InstCombinerImpl &IC) {
   // Handle 'and' / 'or' commutation: make the equality check the first operand.
-  if (JoinedByAnd && Cmp1->getPredicate() == ICmpInst::ICMP_NE)
-    std::swap(Cmp0, Cmp1);
-  else if (!JoinedByAnd && Cmp1->getPredicate() == ICmpInst::ICMP_EQ)
-    std::swap(Cmp0, Cmp1);
+  if (PredR == (JoinedByAnd ? ICmpInst::ICMP_NE : ICmpInst::ICMP_EQ)) {
+    std::swap(PredL, PredR);
+    std::swap(LHS0, RHS0);
+    std::swap(LHS1, RHS1);
+  }
 
   // (X != 0) && (ctpop(X) u< 2) --> ctpop(X) == 1
-  Value *X;
-  if (JoinedByAnd &&
-      match(Cmp0, m_SpecificICmp(ICmpInst::ICMP_NE, m_Value(X), m_ZeroInt())) &&
-      match(Cmp1, m_SpecificICmp(ICmpInst::ICMP_ULT, m_Ctpop(m_Specific(X)),
-                                 m_SpecificInt(2)))) {
-    auto *CtPop = cast<Instruction>(Cmp1->getOperand(0));
+  if (JoinedByAnd && PredL == ICmpInst::ICMP_NE && match(LHS1, m_ZeroInt()) &&
+      PredR == ICmpInst::ICMP_ULT && match(RHS0, m_Ctpop(m_Specific(LHS0))) &&
+      match(RHS1, m_SpecificInt(2))) {
+    auto *CtPop = cast<Instruction>(RHS0);
     // Drop range attributes and re-infer them in the next iteration.
     CtPop->dropPoisonGeneratingAnnotations();
     IC.addToWorklist(CtPop);
     return Builder.CreateICmpEQ(CtPop, ConstantInt::get(CtPop->getType(), 1));
   }
   // (X == 0) || (ctpop(X) u> 1) --> ctpop(X) != 1
-  if (!JoinedByAnd &&
-      match(Cmp0, m_SpecificICmp(ICmpInst::ICMP_EQ, m_Value(X), m_ZeroInt())) &&
-      match(Cmp1, m_SpecificICmp(ICmpInst::ICMP_UGT, m_Ctpop(m_Specific(X)),
-                                 m_SpecificInt(1)))) {
-    auto *CtPop = cast<Instruction>(Cmp1->getOperand(0));
+  if (!JoinedByAnd && PredL == ICmpInst::ICMP_EQ && match(LHS1, m_ZeroInt()) &&
+      PredR == ICmpInst::ICMP_UGT && match(RHS0, m_Ctpop(m_Specific(LHS0))) &&
+      match(RHS1, m_SpecificInt(1))) {
+    auto *CtPop = cast<Instruction>(RHS0);
     // Drop range attributes and re-infer them in the next iteration.
     CtPop->dropPoisonGeneratingAnnotations();
     IC.addToWorklist(CtPop);
@@ -1077,7 +1087,7 @@ static Value *foldNegativePower2AndShiftedMask(
 /// 2, an earlier optimization converts the expression into (icmp X s> -1).
 /// Parameter P supports masking using undef/poison in either scalar or vector
 /// values.
-static Value *foldPowerOf2AndShiftedMask(ICmpInst *Cmp0, ICmpInst *Cmp1,
+static Value *foldPowerOf2AndShiftedMask(Value *Cmp0, Value *Cmp1,
                                          bool JoinedByAnd,
                                          InstCombiner::BuilderTy &Builder) {
   if (!JoinedByAnd)
@@ -1109,41 +1119,45 @@ static Value *foldPowerOf2AndShiftedMask(ICmpInst *Cmp0, ICmpInst *Cmp1,
 
 /// Commuted variants are assumed to be handled by calling this function again
 /// with the parameters swapped.
-static Value *foldUnsignedUnderflowCheck(ICmpInst *ZeroICmp,
-                                         ICmpInst *UnsignedICmp, bool IsAnd,
-                                         const SimplifyQuery &Q,
+static Value *foldUnsignedUnderflowCheck(CmpPredicate PredL, Value *LHS0,
+                                         Value *LHS1, bool LHSOneUse,
+                                         CmpPredicate PredR, Value *RHS0,
+                                         Value *RHS1, bool RHSOneUse,
+                                         bool IsAnd, const SimplifyQuery &Q,
                                          InstCombiner::BuilderTy &Builder) {
-  Value *ZeroCmpOp;
-  CmpPredicate EqPred;
-  if (!match(ZeroICmp, m_ICmp(EqPred, m_Value(ZeroCmpOp), m_Zero())) ||
-      !ICmpInst::isEquality(EqPred))
+  if (!match(LHS1, m_Zero()) || !ICmpInst::isEquality(PredL))
     return nullptr;
 
-  CmpPredicate UnsignedPred;
-
   Value *A, *B;
-  if (match(UnsignedICmp,
-            m_c_ICmp(UnsignedPred, m_Specific(ZeroCmpOp), m_Value(A))) &&
-      match(ZeroCmpOp, m_c_Add(m_Specific(A), m_Value(B))) &&
-      (ZeroICmp->hasOneUse() || UnsignedICmp->hasOneUse())) {
-    auto GetKnownNonZeroAndOther = [&](Value *&NonZero, Value *&Other) {
-      if (!isKnownNonZero(NonZero, Q))
-        std::swap(NonZero, Other);
-      return isKnownNonZero(NonZero, Q);
-    };
+  if (RHS0 == LHS0)
+    A = RHS1;
+  else if (RHS1 == LHS0) {
+    A = RHS0;
+    PredR = CmpPredicate::getSwapped(PredR);
+  } else
+    return nullptr;
 
-    // Given  ZeroCmpOp = (A + B)
-    //   ZeroCmpOp <  A && ZeroCmpOp != 0  -->  (0-X) <  Y  iff
-    //   ZeroCmpOp >= A || ZeroCmpOp == 0  -->  (0-X) >= Y  iff
-    //     with X being the value (A/B) that is known to be non-zero,
-    //     and Y being remaining value.
-    if (UnsignedPred == ICmpInst::ICMP_ULT && EqPred == ICmpInst::ICMP_NE &&
-        IsAnd && GetKnownNonZeroAndOther(B, A))
-      return Builder.CreateICmpULT(Builder.CreateNeg(B), A);
-    if (UnsignedPred == ICmpInst::ICMP_UGE && EqPred == ICmpInst::ICMP_EQ &&
-        !IsAnd && GetKnownNonZeroAndOther(B, A))
-      return Builder.CreateICmpUGE(Builder.CreateNeg(B), A);
-  }
+  if (!match(LHS0, m_c_Add(m_Specific(A), m_Value(B))) ||
+      !(LHSOneUse || RHSOneUse))
+    return nullptr;
+
+  auto GetKnownNonZeroAndOther = [&](Value *&NonZero, Value *&Other) {
+    if (!isKnownNonZero(NonZero, Q))
+      std::swap(NonZero, Other);
+    return isKnownNonZero(NonZero, Q);
+  };
+
+  // Given  ZeroCmpOp = (A + B)
+  //   ZeroCmpOp <  A && ZeroCmpOp != 0  -->  (0-X) <  Y  iff
+  //   ZeroCmpOp >= A || ZeroCmpOp == 0  -->  (0-X) >= Y  iff
+  //     with X being the value (A/B) that is known to be non-zero,
+  //     and Y being remaining value.
+  if (PredR == ICmpInst::ICMP_ULT && PredL == ICmpInst::ICMP_NE && IsAnd &&
+      GetKnownNonZeroAndOther(B, A))
+    return Builder.CreateICmpULT(Builder.CreateNeg(B), A);
+  if (PredR == ICmpInst::ICMP_UGE && PredL == ICmpInst::ICMP_EQ && !IsAnd &&
+      GetKnownNonZeroAndOther(B, A))
+    return Builder.CreateICmpUGE(Builder.CreateNeg(B), A);
 
   return nullptr;
 }
@@ -1272,29 +1286,32 @@ Value *InstCombinerImpl::foldEqOfParts(Value *Cmp0, Value *Cmp1, bool IsAnd) {
 /// Reduce logic-of-compares with equality to a constant by substituting a
 /// common operand with the constant. Callers are expected to call this with
 /// Cmp0/Cmp1 switched to handle logic op commutativity.
-static Value *foldAndOrOfICmpsWithConstEq(ICmpInst *Cmp0, ICmpInst *Cmp1,
-                                          bool IsAnd, bool IsLogical,
-                                          InstCombiner::BuilderTy &Builder,
-                                          const SimplifyQuery &Q,
-                                          Instruction &I) {
+static Value *
+foldAndOrOfICmpsWithConstEq(CmpPredicate PredL, Value *LHS0, Value *LHS1,
+                            Value *LHS, CmpPredicate PredR, Value *RHS0,
+                            Value *RHS1, bool RHSOneUse, bool IsAnd,
+                            bool IsLogical, InstCombiner::BuilderTy &Builder,
+                            const SimplifyQuery &Q, Instruction &I) {
   // Match an equality compare with a non-poison constant as Cmp0.
   // Also, give up if the compare can be constant-folded to avoid looping.
-  CmpPredicate Pred0;
-  Value *X;
-  Constant *C;
-  if (!match(Cmp0, m_ICmp(Pred0, m_Value(X), m_Constant(C))) ||
-      !isGuaranteedNotToBeUndefOrPoison(C) || isa<Constant>(X))
+  if (!isa<Constant>(LHS1) || !isGuaranteedNotToBeUndefOrPoison(LHS1) ||
+      isa<Constant>(LHS0))
     return nullptr;
-  if ((IsAnd && Pred0 != ICmpInst::ICMP_EQ) ||
-      (!IsAnd && Pred0 != ICmpInst::ICMP_NE))
+  if ((IsAnd && PredL != ICmpInst::ICMP_EQ) ||
+      (!IsAnd && PredL != ICmpInst::ICMP_NE))
     return nullptr;
 
   // The other compare must include a common operand (X). Canonicalize the
   // common operand as operand 1 (Pred1 is swapped if the common operand was
   // operand 0).
   Value *Y;
-  CmpPredicate Pred1;
-  if (!match(Cmp1, m_c_ICmp(Pred1, m_Value(Y), m_Specific(X))))
+
+  if (LHS0 == RHS0) {
+    Y = RHS1;
+    PredR = CmpPredicate::getSwapped(PredR);
+  } else if (LHS0 == RHS1)
+    Y = RHS0;
+  else
     return nullptr;
 
   // Replace variable with constant value equivalence to remove a variable use:
@@ -1302,21 +1319,21 @@ static Value *foldAndOrOfICmpsWithConstEq(ICmpInst *Cmp0, ICmpInst *Cmp1,
   // (X != C) || (Y Pred1 X) --> (X != C) || (Y Pred1 C)
   // Can think of the 'or' substitution with the 'and' bool equivalent:
   // A || B --> A || (!A && B)
-  Value *SubstituteCmp = simplifyICmpInst(Pred1, Y, C, Q);
+  Value *SubstituteCmp = simplifyICmpInst(PredR, Y, LHS1, Q);
   if (!SubstituteCmp) {
     // If we need to create a new instruction, require that the old compare can
     // be removed.
-    if (!Cmp1->hasOneUse())
+    if (!RHSOneUse)
       return nullptr;
-    SubstituteCmp = Builder.CreateICmp(Pred1, Y, C);
+    SubstituteCmp = Builder.CreateICmp(PredR, Y, LHS1);
   }
   if (IsLogical) {
     Instruction *MDFrom =
         ProfcheckDisableMetadataFixes && isa<SelectInst>(I) ? nullptr : &I;
-    return IsAnd ? Builder.CreateLogicalAnd(Cmp0, SubstituteCmp, "", MDFrom)
-                 : Builder.CreateLogicalOr(Cmp0, SubstituteCmp, "", MDFrom);
+    return IsAnd ? Builder.CreateLogicalAnd(LHS, SubstituteCmp, "", MDFrom)
+                 : Builder.CreateLogicalOr(LHS, SubstituteCmp, "", MDFrom);
   }
-  return Builder.CreateBinOp(IsAnd ? Instruction::And : Instruction::Or, Cmp0,
+  return Builder.CreateBinOp(IsAnd ? Instruction::And : Instruction::Or, LHS,
                              SubstituteCmp);
 }
 
@@ -1324,17 +1341,17 @@ static Value *foldAndOrOfICmpsWithConstEq(ICmpInst *Cmp0, ICmpInst *Cmp1,
 /// or   (icmp Pred1 V1, C1) | (icmp Pred2 V2, C2)
 /// into a single comparison using range-based reasoning.
 /// NOTE: This is also used for logical and/or, must be poison-safe!
-Value *InstCombinerImpl::foldAndOrOfICmpsUsingRanges(ICmpInst *ICmp1,
-                                                     ICmpInst *ICmp2,
-                                                     bool IsAnd) {
+Value *InstCombinerImpl::foldAndOrOfICmpsUsingRanges(
+    CmpPredicate PredL, Value *LHS0, Value *LHS1, bool LHSOneUse,
+    CmpPredicate PredR, Value *RHS0, Value *RHS1, bool RHSOneUse, bool IsAnd) {
   // Return (V, CR) for a range check idiom V in CR.
   auto MatchExactRangeCheck =
-      [](ICmpInst *ICmp) -> std::optional<std::pair<Value *, ConstantRange>> {
+      [](CmpPredicate Pred, Value *LHS,
+         Value *RHS) -> std::optional<std::pair<Value *, ConstantRange>> {
     const APInt *C;
-    if (!match(ICmp->getOperand(1), m_APInt(C)))
+    if (!match(RHS, m_APInt(C)))
       return std::nullopt;
-    Value *LHS = ICmp->getOperand(0);
-    CmpPredicate Pred = ICmp->getPredicate();
+
     Value *X;
     // Match (x & NegPow2) ==/!= C
     const APInt *Mask;
@@ -1355,11 +1372,11 @@ Value *InstCombinerImpl::foldAndOrOfICmpsUsingRanges(ICmpInst *ICmp1,
     return std::make_pair(LHS, CR);
   };
 
-  auto RC1 = MatchExactRangeCheck(ICmp1);
+  auto RC1 = MatchExactRangeCheck(PredL, LHS0, LHS1);
   if (!RC1)
     return nullptr;
 
-  auto RC2 = MatchExactRangeCheck(ICmp2);
+  auto RC2 = MatchExactRangeCheck(PredR, RHS0, RHS1);
   if (!RC2)
     return nullptr;
 
@@ -1378,8 +1395,7 @@ Value *InstCombinerImpl::foldAndOrOfICmpsUsingRanges(ICmpInst *ICmp1,
   Value *NewV = V1;
   std::optional<ConstantRange> CR = CR1.exactUnionWith(CR2);
   if (!CR) {
-    if (!(ICmp1->hasOneUse() && ICmp2->hasOneUse()) || CR1.isWrappedSet() ||
-        CR2.isWrappedSet())
+    if (!(LHSOneUse && RHSOneUse) || CR1.isWrappedSet() || CR2.isWrappedSet())
       return nullptr;
 
     // Check whether we have equal-size ranges that only differ by one bit.
@@ -3388,23 +3404,20 @@ Value *InstCombinerImpl::matchSelectFromAndOr(Value *A, Value *B, Value *C,
 
 // (icmp eq X, C) | (icmp ult Other, (X - C)) -> (icmp ule Other, (X - (C + 1)))
 // (icmp ne X, C) & (icmp uge Other, (X - C)) -> (icmp ugt Other, (X - (C + 1)))
-static Value *foldAndOrOfICmpEqConstantAndICmp(ICmpInst *LHS, ICmpInst *RHS,
+static Value *foldAndOrOfICmpEqConstantAndICmp(CmpPredicate PredL, Value *LHS0,
+                                               Value *LHS1, bool LHSOneUse,
+                                               CmpPredicate PredR, Value *RHS0,
+                                               Value *RHS1, bool RHSOneUse,
                                                bool IsAnd, bool IsLogical,
                                                IRBuilderBase &Builder) {
-  Value *LHS0 = LHS->getOperand(0);
-  Value *RHS0 = RHS->getOperand(0);
-  Value *RHS1 = RHS->getOperand(1);
-
-  ICmpInst::Predicate LPred =
-      IsAnd ? LHS->getInversePredicate() : LHS->getPredicate();
-  ICmpInst::Predicate RPred =
-      IsAnd ? RHS->getInversePredicate() : RHS->getPredicate();
+  if (IsAnd) {
+    PredL = CmpPredicate::getInverse(PredL);
+    PredR = CmpPredicate::getInverse(PredR);
+  }
 
   const APInt *CInt;
-  if (LPred != ICmpInst::ICMP_EQ ||
-      !match(LHS->getOperand(1), m_APIntAllowPoison(CInt)) ||
-      !LHS0->getType()->isIntOrIntVectorTy() ||
-      !(LHS->hasOneUse() || RHS->hasOneUse()))
+  if (PredL != ICmpInst::ICMP_EQ || !match(LHS1, m_APIntAllowPoison(CInt)) ||
+      !LHS0->getType()->isIntOrIntVectorTy() || !(LHSOneUse || RHSOneUse))
     return nullptr;
 
   auto MatchRHSOp = [LHS0, CInt](const Value *RHSOp) {
@@ -3414,9 +3427,9 @@ static Value *foldAndOrOfICmpEqConstantAndICmp(ICmpInst *LHS, ICmpInst *RHS,
   };
 
   Value *Other;
-  if (RPred == ICmpInst::ICMP_ULT && MatchRHSOp(RHS1))
+  if (PredR == ICmpInst::ICMP_ULT && MatchRHSOp(RHS1))
     Other = RHS0;
-  else if (RPred == ICmpInst::ICMP_UGT && MatchRHSOp(RHS0))
+  else if (PredR == ICmpInst::ICMP_UGT && MatchRHSOp(RHS0))
     Other = RHS1;
   else
     return nullptr;
@@ -3433,14 +3446,19 @@ static Value *foldAndOrOfICmpEqConstantAndICmp(ICmpInst *LHS, ICmpInst *RHS,
 /// Fold (icmp)&(icmp) or (icmp)|(icmp) if possible.
 /// If IsLogical is true, then the and/or is in select form and the transform
 /// must be poison-safe.
-Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
+Value *InstCombinerImpl::foldAndOrOfICmps(Value *LHS, Value *RHS,
                                           Instruction &I, bool IsAnd,
                                           bool IsLogical) {
-  const SimplifyQuery Q = SQ.getWithInstruction(&I);
+  CmpPredicate PredL, PredR;
+  Value *LHS0, *LHS1, *RHS0, *RHS1;
+  if (!match(LHS, m_ICmp(PredL, m_Value(LHS0), m_Value(LHS1))) ||
+      !match(RHS, m_ICmp(PredR, m_Value(RHS0), m_Value(RHS1))))
+    return nullptr;
 
-  ICmpInst::Predicate PredL = LHS->getPredicate(), PredR = RHS->getPredicate();
-  Value *LHS0 = LHS->getOperand(0), *RHS0 = RHS->getOperand(0);
-  Value *LHS1 = LHS->getOperand(1), *RHS1 = RHS->getOperand(1);
+  bool LHSOneUse = LHS->hasOneUse();
+  bool RHSOneUse = RHS->hasOneUse();
+
+  const SimplifyQuery Q = SQ.getWithInstruction(&I);
 
   const APInt *LHSC = nullptr, *RHSC = nullptr;
   match(LHS1, m_APInt(LHSC));
@@ -3456,38 +3474,45 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
     if (LHS0 == RHS0 && LHS1 == RHS1) {
       unsigned Code = IsAnd ? getICmpCode(PredL) & getICmpCode(PredR)
                             : getICmpCode(PredL) | getICmpCode(PredR);
-      bool IsSigned = LHS->isSigned() || RHS->isSigned();
+      bool IsSigned = ICmpInst::isSigned(PredL) || ICmpInst::isSigned(PredR);
       return getNewICmpValue(Code, IsSigned, LHS0, LHS1, Builder);
     }
   }
 
-  if (Value *V =
-          foldAndOrOfICmpEqConstantAndICmp(LHS, RHS, IsAnd, IsLogical, Builder))
+  if (Value *V = foldAndOrOfICmpEqConstantAndICmp(PredL, LHS0, LHS1, LHSOneUse,
+                                                  PredR, RHS0, RHS1, RHSOneUse,
+                                                  IsAnd, IsLogical, Builder))
     return V;
   // We can treat logical like bitwise here, because both operands are used on
   // the LHS, and as such poison from both will propagate.
-  if (Value *V = foldAndOrOfICmpEqConstantAndICmp(RHS, LHS, IsAnd,
-                                                  /*IsLogical*/ false, Builder))
+  if (Value *V = foldAndOrOfICmpEqConstantAndICmp(
+          PredR, RHS0, RHS1, RHSOneUse, PredL, LHS0, LHS1, LHSOneUse, IsAnd,
+          /*IsLogical*/ false, Builder))
     return V;
 
-  if (Value *V = foldAndOrOfICmpsWithConstEq(LHS, RHS, IsAnd, IsLogical,
-                                             Builder, Q, I))
+  if (Value *V = foldAndOrOfICmpsWithConstEq(PredL, LHS0, LHS1, LHS, PredR,
+                                             RHS0, RHS1, RHSOneUse, IsAnd,
+                                             IsLogical, Builder, Q, I))
     return V;
   // We can convert this case to bitwise and, because both operands are used
   // on the LHS, and as such poison from both will propagate.
   if (Value *V = foldAndOrOfICmpsWithConstEq(
-          RHS, LHS, IsAnd, /*IsLogical=*/false, Builder, Q, I)) {
+          PredR, RHS0, RHS1, RHS, PredL, LHS0, LHS1, LHSOneUse, IsAnd,
+          /*IsLogical=*/false, Builder, Q, I)) {
     // If RHS is still used, we should drop samesign flag.
-    if (IsLogical && RHS->hasSameSign() && !RHS->use_empty()) {
-      RHS->setSameSign(false);
-      addToWorklist(RHS);
+    if (IsLogical && PredR.hasSameSign() && !RHS->use_empty()) {
+      auto *CmpR = cast<ICmpInst>(RHS);
+      CmpR->setSameSign(false);
+      addToWorklist(CmpR);
     }
     return V;
   }
 
-  if (Value *V = foldIsPowerOf2OrZero(LHS, RHS, IsAnd, Builder, *this))
+  if (Value *V = foldIsPowerOf2OrZero(PredL, LHS0, LHS1, PredR, RHS0, RHS1,
+                                      IsAnd, Builder, *this))
     return V;
-  if (Value *V = foldIsPowerOf2OrZero(RHS, LHS, IsAnd, Builder, *this))
+  if (Value *V = foldIsPowerOf2OrZero(PredR, RHS0, RHS1, PredL, LHS0, LHS1,
+                                      IsAnd, Builder, *this))
     return V;
 
   // TODO: One of these directions is fine with logical and/or, the other could
@@ -3495,21 +3520,25 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
   if (!IsLogical) {
     // E.g. (icmp slt x, 0) | (icmp sgt x, n) --> icmp ugt x, n
     // E.g. (icmp sge x, 0) & (icmp slt x, n) --> icmp ult x, n
-    if (Value *V = simplifyRangeCheck(LHS, RHS, /*Inverted=*/!IsAnd))
+    if (Value *V = simplifyRangeCheck(PredL, LHS0, LHS1, PredR, RHS0, RHS1, RHS,
+                                      /*Inverted=*/!IsAnd))
       return V;
 
     // E.g. (icmp sgt x, n) | (icmp slt x, 0) --> icmp ugt x, n
     // E.g. (icmp slt x, n) & (icmp sge x, 0) --> icmp ult x, n
-    if (Value *V = simplifyRangeCheck(RHS, LHS, /*Inverted=*/!IsAnd))
+    if (Value *V = simplifyRangeCheck(PredR, RHS0, RHS1, PredL, LHS0, LHS1, LHS,
+                                      /*Inverted=*/!IsAnd))
       return V;
   }
 
   // TODO: Add conjugated or fold, check whether it is safe for logical and/or.
   if (IsAnd && !IsLogical)
-    if (Value *V = foldSignedTruncationCheck(LHS, RHS, I, Builder))
+    if (Value *V = foldSignedTruncationCheck(PredL, LHS0, LHS1, PredR, RHS0,
+                                             RHS1, I, Builder))
       return V;
 
-  if (Value *V = foldIsPowerOf2(LHS, RHS, IsAnd, Builder, *this))
+  if (Value *V = foldIsPowerOf2(PredL, LHS0, LHS1, PredR, RHS0, RHS1, IsAnd,
+                                Builder, *this))
     return V;
 
   if (Value *V = foldPowerOf2AndShiftedMask(LHS, RHS, IsAnd, Builder))
@@ -3517,9 +3546,13 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
 
   // TODO: Verify whether this is safe for logical and/or.
   if (!IsLogical) {
-    if (Value *X = foldUnsignedUnderflowCheck(LHS, RHS, IsAnd, Q, Builder))
+    if (Value *X = foldUnsignedUnderflowCheck(PredL, LHS0, LHS1, LHSOneUse,
+                                              PredR, RHS0, RHS1, RHSOneUse,
+                                              IsAnd, Q, Builder))
       return X;
-    if (Value *X = foldUnsignedUnderflowCheck(RHS, LHS, IsAnd, Q, Builder))
+    if (Value *X = foldUnsignedUnderflowCheck(PredR, RHS0, RHS1, RHSOneUse,
+                                              PredL, LHS0, LHS1, LHSOneUse,
+                                              IsAnd, Q, Builder))
       return X;
   }
 
@@ -3527,7 +3560,8 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
   // (icmp eq A, 0) & (icmp eq B, 0) --> (icmp eq (A|B), 0)
   // TODO: Remove this and below when foldLogOpOfMaskedICmps can handle undefs.
   if (PredL == (IsAnd ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE) &&
-      PredL == PredR && match(LHS1, m_ZeroInt()) && match(RHS1, m_ZeroInt()) &&
+      PredL.dropSameSign() == PredR.dropSameSign() &&
+      match(LHS1, m_ZeroInt()) && match(RHS1, m_ZeroInt()) &&
       LHS0->getType() == RHS0->getType() &&
       (!IsLogical || isGuaranteedNotToBePoison(RHS0))) {
     Value *NewOr = Builder.CreateOr(LHS0, RHS0);
@@ -3538,7 +3572,8 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
   // (icmp ne A, -1) | (icmp ne B, -1) --> (icmp ne (A&B), -1)
   // (icmp eq A, -1) & (icmp eq B, -1) --> (icmp eq (A&B), -1)
   if (PredL == (IsAnd ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE) &&
-      PredL == PredR && match(LHS1, m_AllOnes()) && match(RHS1, m_AllOnes()) &&
+      PredL.dropSameSign() == PredR.dropSameSign() &&
+      match(LHS1, m_AllOnes()) && match(RHS1, m_AllOnes()) &&
       LHS0->getType() == RHS0->getType() &&
       (!IsLogical || isGuaranteedNotToBePoison(RHS0))) {
     Value *NewAnd = Builder.CreateAnd(LHS0, RHS0);
@@ -3547,8 +3582,9 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
   }
 
   if (!IsLogical)
-    if (Value *V =
-            foldAndOrOfICmpsWithPow2AndWithZero(Builder, LHS, RHS, IsAnd, Q))
+    if (Value *V = foldAndOrOfICmpsWithPow2AndWithZero(
+            Builder, PredL, LHS0, LHS1, LHSOneUse, PredR, RHS0, RHS1, RHSOneUse,
+            IsAnd, Q))
       return V;
 
   // This only handles icmp of constants: (icmp1 A, C1) | (icmp2 B, C2).
@@ -3560,7 +3596,8 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
   // where CMAX is the all ones value for the truncated type,
   // iff the lower bits of C2 and CA are zero.
   if (PredL == (IsAnd ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE) &&
-      PredL == PredR && LHS->hasOneUse() && RHS->hasOneUse()) {
+      PredL.dropSameSign() == PredR.dropSameSign() && LHS->hasOneUse() &&
+      RHS->hasOneUse()) {
     Value *V;
     const APInt *AndC, *SmallC = nullptr, *BigC = nullptr;
 
@@ -3628,7 +3665,7 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
   // (X & ExpMask) == 0 || (X & ExpMask) == ExpMask -> !isnormal(X)
   Value *X;
   const APInt *MaskC;
-  if (LHS0 == RHS0 && PredL == PredR &&
+  if (LHS0 == RHS0 && PredL.dropSameSign() == PredR.dropSameSign() &&
       PredL == (IsAnd ? ICmpInst::ICMP_NE : ICmpInst::ICMP_EQ) &&
       !I.getFunction()->hasFnAttribute(Attribute::NoImplicitFloat) &&
       LHS->hasOneUse() && RHS->hasOneUse() &&
@@ -3641,7 +3678,8 @@ Value *InstCombinerImpl::foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS,
     return Builder.createIsFPClass(X, IsAnd ? FPClassTest::fcNormal
                                             : ~FPClassTest::fcNormal);
 
-  return foldAndOrOfICmpsUsingRanges(LHS, RHS, IsAnd);
+  return foldAndOrOfICmpsUsingRanges(PredL, LHS0, LHS1, LHSOneUse, PredR, RHS0,
+                                     RHS1, RHSOneUse, IsAnd);
 }
 
 /// If IsLogical is true, then the and/or is in select form and the transform
@@ -3659,10 +3697,8 @@ Value *InstCombinerImpl::foldBooleanAndOr(Value *LHS, Value *RHS,
                                         SQ.getWithInstruction(&I)))
     return V;
 
-  if (auto *LHSCmp = dyn_cast<ICmpInst>(LHS))
-    if (auto *RHSCmp = dyn_cast<ICmpInst>(RHS))
-      if (Value *Res = foldAndOrOfICmps(LHSCmp, RHSCmp, I, IsAnd, IsLogical))
-        return Res;
+  if (Value *Res = foldAndOrOfICmps(LHS, RHS, I, IsAnd, IsLogical))
+    return Res;
 
   if (auto *LHSCmp = dyn_cast<FCmpInst>(LHS))
     if (auto *RHSCmp = dyn_cast<FCmpInst>(RHS))
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 8b759e701da60..8d5b2ca7e9daa 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -119,7 +119,9 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   Instruction *visitUDiv(BinaryOperator &I);
   Instruction *visitSDiv(BinaryOperator &I);
   Instruction *visitFDiv(BinaryOperator &I);
-  Value *simplifyRangeCheck(ICmpInst *Cmp0, ICmpInst *Cmp1, bool Inverted);
+  Value *simplifyRangeCheck(CmpPredicate PredL, Value *LHS0, Value *LHS1,
+                            CmpPredicate PredR, Value *RHS0, Value *RHS1,
+                            Value *RHS, bool Inverted);
   Instruction *FoldOrOfLogicalAnds(Value *Op0, Value *Op1);
   Instruction *visitAnd(BinaryOperator &I);
   Instruction *visitOr(BinaryOperator &I);
@@ -406,14 +408,16 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
                                             const CastInst *CI2);
   Value *simplifyIntToPtrRoundTripCast(Value *Val);
 
-  Value *foldAndOrOfICmps(ICmpInst *LHS, ICmpInst *RHS, Instruction &I,
-                          bool IsAnd, bool IsLogical = false);
+  Value *foldAndOrOfICmps(Value *LHS, Value *RHS, Instruction &I, bool IsAnd,
+                          bool IsLogical = false);
   Value *foldXorOfICmps(ICmpInst *LHS, ICmpInst *RHS, BinaryOperator &Xor);
 
   Value *foldEqOfParts(Value *Cmp0, Value *Cmp1, bool IsAnd);
 
-  Value *foldAndOrOfICmpsUsingRanges(ICmpInst *ICmp1, ICmpInst *ICmp2,
-                                     bool IsAnd);
+  Value *foldAndOrOfICmpsUsingRanges(CmpPredicate PredL, Value *LHS0,
+                                     Value *LHS1, bool LHSOneUse,
+                                     CmpPredicate PredR, Value *RHS0,
+                                     Value *RHS1, bool RHSOneUse, bool IsAnd);
 
   /// Optimize (fcmp)&(fcmp) or (fcmp)|(fcmp).
   /// NOTE: Unlike most of instcombine, this returns a Value which should

>From 1eaf8cc98b30942d9c93b14da3dc5d3e35343b67 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 6 Sep 2026 11:15:40 +0200
Subject: [PATCH 2/3] [InstCombine] Pre commit test (NFC)

---
 .../test/Transforms/InstCombine/bit-checks.ll | 34 +++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/bit-checks.ll b/llvm/test/Transforms/InstCombine/bit-checks.ll
index 936c02c9f9052..2de5b9428f6d5 100644
--- a/llvm/test/Transforms/InstCombine/bit-checks.ll
+++ b/llvm/test/Transforms/InstCombine/bit-checks.ll
@@ -1367,6 +1367,40 @@ define i1 @no_masks_with_logical_or2(i32 %a, i32 %b, i32 noundef %c) {
   ret i1 %or2
 }
 
+define i1 @no_masks_with_logical_or_trunc_nuw(i32 %a, i32 %b, i32 noundef %c) {
+; CHECK-LABEL: @no_masks_with_logical_or_trunc_nuw(
+; CHECK-NEXT:    [[CMP1:%.*]] = trunc nuw i32 [[A:%.*]] to i1
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 63
+; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP2]]
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp ne i32 [[C:%.*]], 0
+; CHECK-NEXT:    [[OR2:%.*]] = or i1 [[OR1]], [[CMP3]]
+; CHECK-NEXT:    ret i1 [[OR2]]
+;
+  %cmp1 = trunc nuw i32 %a to i1
+  %cmp2 = icmp ne i32 %b, 63
+  %or1 = select i1 %cmp1, i1 true, i1 %cmp2
+  %cmp3 = icmp ne i32 %c, 0
+  %or2 = or i1 %or1, %cmp3
+  ret i1 %or2
+}
+
+define <2 x i1> @no_masks_with_logical_or_vec_trunc_nuw(<2 x i32> %a, <2 x i32> %b, <2 x i32> noundef %c) {
+; CHECK-LABEL: @no_masks_with_logical_or_vec_trunc_nuw(
+; CHECK-NEXT:    [[CMP1:%.*]] = trunc nuw <2 x i32> [[A:%.*]] to <2 x i1>
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne <2 x i32> [[B:%.*]], splat (i32 63)
+; CHECK-NEXT:    [[OR1:%.*]] = select <2 x i1> [[CMP1]], <2 x i1> splat (i1 true), <2 x i1> [[CMP2]]
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp ne <2 x i32> [[C:%.*]], zeroinitializer
+; CHECK-NEXT:    [[OR2:%.*]] = or <2 x i1> [[OR1]], [[CMP3]]
+; CHECK-NEXT:    ret <2 x i1> [[OR2]]
+;
+  %cmp1 = trunc nuw <2 x i32> %a to <2 x i1>
+  %cmp2 = icmp ne <2 x i32> %b, <i32 63, i32 63>
+  %or1 = select <2 x i1> %cmp1, <2 x i1> <i1 true, i1 true>, <2 x i1> %cmp2
+  %cmp3 = icmp ne <2 x i32> %c, <i32 0, i32 0>
+  %or2 = or <2 x i1> %or1, %cmp3
+  ret <2 x i1> %or2
+}
+
 define <2 x i1> @no_masks_with_logical_or_vec_poison1(<2 x i32> %a, <2 x i32> %b, <2 x i32> noundef %c) {
 ; CHECK-LABEL: @no_masks_with_logical_or_vec_poison1(
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne <2 x i32> [[B:%.*]], <i32 63, i32 poison>

>From 3bfdf95a7750147a7a2fc3620a2257b465acbad5 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 6 Sep 2026 12:56:07 +0200
Subject: [PATCH 3/3] [InstCombine] Support trunc nuw condition in
 foldAndOrOfICmps

---
 .../InstCombine/InstCombineAndOrXor.cpp       | 27 ++++++++++---------
 .../test/Transforms/InstCombine/bit-checks.ll | 14 +++++-----
 2 files changed, 21 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index d89110e65d9f4..fbeeb46d03956 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -3451,8 +3451,8 @@ Value *InstCombinerImpl::foldAndOrOfICmps(Value *LHS, Value *RHS,
                                           bool IsLogical) {
   CmpPredicate PredL, PredR;
   Value *LHS0, *LHS1, *RHS0, *RHS1;
-  if (!match(LHS, m_ICmp(PredL, m_Value(LHS0), m_Value(LHS1))) ||
-      !match(RHS, m_ICmp(PredR, m_Value(RHS0), m_Value(RHS1))))
+  if (!match(LHS, m_ICmpLike(PredL, m_Value(LHS0), m_Value(LHS1))) ||
+      !match(RHS, m_ICmpLike(PredR, m_Value(RHS0), m_Value(RHS1))))
     return nullptr;
 
   bool LHSOneUse = LHS->hasOneUse();
@@ -3496,17 +3496,20 @@ Value *InstCombinerImpl::foldAndOrOfICmps(Value *LHS, Value *RHS,
     return V;
   // We can convert this case to bitwise and, because both operands are used
   // on the LHS, and as such poison from both will propagate.
-  if (Value *V = foldAndOrOfICmpsWithConstEq(
-          PredR, RHS0, RHS1, RHS, PredL, LHS0, LHS1, LHSOneUse, IsAnd,
-          /*IsLogical=*/false, Builder, Q, I)) {
-    // If RHS is still used, we should drop samesign flag.
-    if (IsLogical && PredR.hasSameSign() && !RHS->use_empty()) {
-      auto *CmpR = cast<ICmpInst>(RHS);
-      CmpR->setSameSign(false);
-      addToWorklist(CmpR);
+  // Can not handle RHS = trunc nuw as it is not same as icmp ne 0 for all
+  // values
+  if (isa<ICmpInst>(RHS))
+    if (Value *V = foldAndOrOfICmpsWithConstEq(
+            PredR, RHS0, RHS1, RHS, PredL, LHS0, LHS1, LHSOneUse, IsAnd,
+            /*IsLogical=*/false, Builder, Q, I)) {
+      // If RHS is still used, we should drop samesign flag.
+      if (IsLogical && PredR.hasSameSign() && !RHS->use_empty()) {
+        auto *CmpR = cast<ICmpInst>(RHS);
+        CmpR->setSameSign(false);
+        addToWorklist(CmpR);
+      }
+      return V;
     }
-    return V;
-  }
 
   if (Value *V = foldIsPowerOf2OrZero(PredL, LHS0, LHS1, PredR, RHS0, RHS1,
                                       IsAnd, Builder, *this))
diff --git a/llvm/test/Transforms/InstCombine/bit-checks.ll b/llvm/test/Transforms/InstCombine/bit-checks.ll
index 2de5b9428f6d5..ec7446a000e9c 100644
--- a/llvm/test/Transforms/InstCombine/bit-checks.ll
+++ b/llvm/test/Transforms/InstCombine/bit-checks.ll
@@ -1369,11 +1369,10 @@ define i1 @no_masks_with_logical_or2(i32 %a, i32 %b, i32 noundef %c) {
 
 define i1 @no_masks_with_logical_or_trunc_nuw(i32 %a, i32 %b, i32 noundef %c) {
 ; CHECK-LABEL: @no_masks_with_logical_or_trunc_nuw(
-; CHECK-NEXT:    [[CMP1:%.*]] = trunc nuw i32 [[A:%.*]] to i1
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 63
-; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP2]]
-; CHECK-NEXT:    [[CMP3:%.*]] = icmp ne i32 [[C:%.*]], 0
-; CHECK-NEXT:    [[OR2:%.*]] = or i1 [[OR1]], [[CMP3]]
+; CHECK-NEXT:    [[C:%.*]] = or i32 [[C1:%.*]], [[A:%.*]]
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp ne i32 [[C]], 0
+; CHECK-NEXT:    [[OR2:%.*]] = select i1 [[CMP3]], i1 true, i1 [[CMP2]]
 ; CHECK-NEXT:    ret i1 [[OR2]]
 ;
   %cmp1 = trunc nuw i32 %a to i1
@@ -1386,11 +1385,10 @@ define i1 @no_masks_with_logical_or_trunc_nuw(i32 %a, i32 %b, i32 noundef %c) {
 
 define <2 x i1> @no_masks_with_logical_or_vec_trunc_nuw(<2 x i32> %a, <2 x i32> %b, <2 x i32> noundef %c) {
 ; CHECK-LABEL: @no_masks_with_logical_or_vec_trunc_nuw(
-; CHECK-NEXT:    [[CMP1:%.*]] = trunc nuw <2 x i32> [[A:%.*]] to <2 x i1>
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp ne <2 x i32> [[B:%.*]], splat (i32 63)
-; CHECK-NEXT:    [[OR1:%.*]] = select <2 x i1> [[CMP1]], <2 x i1> splat (i1 true), <2 x i1> [[CMP2]]
-; CHECK-NEXT:    [[CMP3:%.*]] = icmp ne <2 x i32> [[C:%.*]], zeroinitializer
-; CHECK-NEXT:    [[OR2:%.*]] = or <2 x i1> [[OR1]], [[CMP3]]
+; CHECK-NEXT:    [[C:%.*]] = or <2 x i32> [[C1:%.*]], [[A:%.*]]
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp ne <2 x i32> [[C]], zeroinitializer
+; CHECK-NEXT:    [[OR2:%.*]] = select <2 x i1> [[CMP3]], <2 x i1> splat (i1 true), <2 x i1> [[CMP2]]
 ; CHECK-NEXT:    ret <2 x i1> [[OR2]]
 ;
   %cmp1 = trunc nuw <2 x i32> %a to <2 x i1>



More information about the llvm-commits mailing list