[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