diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -4097,6 +4097,14 @@ ICmpInst::ICMP_SLT, Op, Constant::getNullValue(Op->getType()), CxtI, DL); } +static std::optional getKnownSign(Value *Op, Instruction *CxtI, + const DataLayout &DL, + AssumptionCache *AC, + DominatorTree *DT) { + KnownBits Known; + return getKnownSign(Op, CxtI, DL, AC, DT, Known); +} + /// Try to fold icmp (binop), X or icmp X, (binop). /// TODO: A large part of this logic is duplicated in InstSimplify's /// simplifyICmpWithBinOp(). We should be able to share that and avoid the code @@ -4394,6 +4402,73 @@ } { + bool Op0IsOr = false, Op1IsOr = false; + if (match(Op0, m_c_Or(m_Specific(Op1), m_Value(A)))) + Op0IsOr = true; + else if (match(Op1, m_c_Or(m_Specific(Op0), m_Value(A)))) + Op1IsOr = true; + + if (Op0IsOr || Op1IsOr) { + Value *OrOp = Op0IsOr ? Op0 : Op1; + Value *X = Op0IsOr ? Op1 : Op0; + + // icmp (X | Y) u<= X --> (X | Y) == X + if (Op0IsOr ? Pred == ICmpInst::ICMP_ULE : Pred == ICmpInst::ICMP_UGE) + return new ICmpInst(ICmpInst::ICMP_EQ, Op0, Op1); + + // icmp (X | Y) u> X --> (X | Y) != X + else if (Op0IsOr ? Pred == ICmpInst::ICMP_UGT + : Pred == ICmpInst::ICMP_ULT) + return new ICmpInst(ICmpInst::ICMP_NE, Op0, Op1); + + // icmp (X | noundef Y) eq/ne X + // if (X | noundef Y).hasOneUse() + // --> (X & noundef Y) eq/ne noundef Y + if (ICmpInst::isEquality(Pred) && OrOp->hasOneUse() && + isGuaranteedNotToBeUndefOrPoison(A, Q.AC, Q.CxtI, Q.DT, + /*Depth*/ 0)) + return new ICmpInst(Pred, Builder.CreateAnd(X, A), A); + + if (!ICmpInst::isEquality(Pred) && + ICmpInst::getSignedPredicate(Pred) == Pred) { + auto KnownSign = getKnownSign(A, &I, DL, &AC, &DT); + if (KnownSign != std::nullopt) { + // icmp (X | MinInt) s> X --> false + // icmp (X | MinInt) s<= X --> true + // icmp (X | MinInt) s>= X --> X s< 0 + // icmp (X | MinInt) s< X --> X s>= 0 + if (*KnownSign /* true is Signed. */ && + isKnownToBeAPowerOfTwo(A, /*OrZero*/ true, 0, &I)) { + if (Op0IsOr && + (Pred == ICmpInst::ICMP_SGT || Pred == ICmpInst::ICMP_SLE)) + return replaceInstUsesWith( + I, ConstantInt::get(I.getType(), Pred == ICmpInst::ICMP_SLE)); + else if (Op1IsOr && + (Pred == ICmpInst::ICMP_SLT || Pred == ICmpInst::ICMP_SGE)) + return replaceInstUsesWith( + I, ConstantInt::get(I.getType(), Pred == ICmpInst::ICMP_SGE)); + else if (Op0IsOr ? Pred == ICmpInst::ICMP_SGE + : Pred == ICmpInst::ICMP_SLE) + return new ICmpInst(ICmpInst::ICMP_SLT, X, + Constant::getNullValue(Op0->getType())); + else + return new ICmpInst(ICmpInst::ICMP_SGE, X, + Constant::getNullValue(Op0->getType())); + } + // icmp (X | Pos_Y) s> X --> (X | Pos_Y) != X + // icmp (X | Pos_Y) s<= X --> (X | Pos_Y) == X + else if (!*KnownSign) { + if (Op0IsOr ? Pred == ICmpInst::ICMP_SGT + : Pred == ICmpInst::ICMP_SLT) + return new ICmpInst(ICmpInst::ICMP_NE, Op0, Op1); + else if (Op0IsOr ? Pred == ICmpInst::ICMP_SLE + : Pred == ICmpInst::ICMP_SGE) + return new ICmpInst(ICmpInst::ICMP_EQ, Op0, Op1); + } + } + } + } + bool Op0IsXor = false, Op1IsXor = false; if (match(Op0, m_c_Xor(m_Specific(Op1), m_Value(A)))) Op0IsXor = true; diff --git a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll --- a/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll +++ b/llvm/test/Transforms/InstCombine/icmp-of-or-x.ll @@ -8,7 +8,7 @@ define i1 @or_ugt(i8 %x, i8 %y) { ; CHECK-LABEL: @or_ugt( ; CHECK-NEXT: [[XN1:%.*]] = or i8 [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[XN1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[XN1]], [[X]] ; CHECK-NEXT: ret i1 [[R]] ; %xn1 = or i8 %x, %y @@ -19,7 +19,7 @@ define <2 x i1> @or_ule(<2 x i8> %x, <2 x i8> %y) { ; CHECK-LABEL: @or_ule( ; CHECK-NEXT: [[XN1:%.*]] = or <2 x i8> [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[R:%.*]] = icmp ule <2 x i8> [[XN1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i8> [[XN1]], [[X]] ; CHECK-NEXT: ret <2 x i1> [[R]] ; %xn1 = or <2 x i8> %x, %y @@ -32,7 +32,7 @@ ; CHECK-NEXT: [[X:%.*]] = add <2 x i8> [[XX:%.*]], [[Z:%.*]] ; CHECK-NEXT: [[Y:%.*]] = and <2 x i8> [[YY:%.*]], ; CHECK-NEXT: [[XN1:%.*]] = or <2 x i8> [[X]], [[Y]] -; CHECK-NEXT: [[R:%.*]] = icmp slt <2 x i8> [[X]], [[XN1]] +; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i8> [[X]], [[XN1]] ; CHECK-NEXT: ret <2 x i1> [[R]] ; %x = add <2 x i8> %xx, %z @@ -47,7 +47,7 @@ ; CHECK-NEXT: [[NS:%.*]] = icmp sgt i8 [[Y:%.*]], -1 ; CHECK-NEXT: call void @llvm.assume(i1 [[NS]]) ; CHECK-NEXT: [[XN1:%.*]] = or i8 [[X:%.*]], [[Y]] -; CHECK-NEXT: [[R:%.*]] = icmp sle i8 [[XN1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[XN1]], [[X]] ; CHECK-NEXT: ret i1 [[R]] ; %ns = icmp sge i8 %y, 0 @@ -70,8 +70,8 @@ define i1 @or_eq_noundef(i8 %x, i8 noundef %y) { ; CHECK-LABEL: @or_eq_noundef( -; CHECK-NEXT: [[XN1:%.*]] = or i8 [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[XN1]], [[X]] +; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[TMP1]], [[Y]] ; CHECK-NEXT: ret i1 [[R]] ; %xn1 = or i8 %x, %y @@ -92,8 +92,8 @@ define <2 x i1> @or_ne_noundef(<2 x i8> %x, <2 x i8> noundef %y) { ; CHECK-LABEL: @or_ne_noundef( -; CHECK-NEXT: [[XN1:%.*]] = or <2 x i8> [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i8> [[XN1]], [[X]] +; CHECK-NEXT: [[TMP1:%.*]] = and <2 x i8> [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = icmp ne <2 x i8> [[TMP1]], [[Y]] ; CHECK-NEXT: ret <2 x i1> [[R]] ; %xn1 = or <2 x i8> %x, %y @@ -116,8 +116,7 @@ define i1 @or_slt_intmin(i8 %x) { ; CHECK-LABEL: @or_slt_intmin( -; CHECK-NEXT: [[XN1:%.*]] = or i8 [[X:%.*]], -128 -; CHECK-NEXT: [[R:%.*]] = icmp slt i8 [[XN1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[X:%.*]], -1 ; CHECK-NEXT: ret i1 [[R]] ; %xn1 = or i8 %x, 128 @@ -127,10 +126,7 @@ define <2 x i1> @or_slt_intmin_2(<2 x i8> %xx, <2 x i8> %z) { ; CHECK-LABEL: @or_slt_intmin_2( -; CHECK-NEXT: [[X:%.*]] = add <2 x i8> [[XX:%.*]], [[Z:%.*]] -; CHECK-NEXT: [[XN1:%.*]] = or <2 x i8> [[X]], -; CHECK-NEXT: [[R:%.*]] = icmp slt <2 x i8> [[X]], [[XN1]] -; CHECK-NEXT: ret <2 x i1> [[R]] +; CHECK-NEXT: ret <2 x i1> zeroinitializer ; %x = add <2 x i8> %xx, %z %xn1 = or <2 x i8> %x, @@ -146,8 +142,7 @@ ; CHECK-NEXT: br i1 [[C]], label [[NEG:%.*]], label [[POS:%.*]] ; CHECK: neg: ; CHECK-NEXT: [[X:%.*]] = add i8 [[XX:%.*]], [[Z:%.*]] -; CHECK-NEXT: [[XN1:%.*]] = or i8 [[X]], [[CP2]] -; CHECK-NEXT: [[R:%.*]] = icmp sle i8 [[X]], [[XN1]] +; CHECK-NEXT: [[R:%.*]] = icmp slt i8 [[X]], 0 ; CHECK-NEXT: ret i1 [[R]] ; CHECK: pos: ; CHECK-NEXT: call void @barrier() @@ -169,8 +164,7 @@ define i1 @or_sge_intmin(i8 %x) { ; CHECK-LABEL: @or_sge_intmin( -; CHECK-NEXT: [[XN1:%.*]] = or i8 [[X:%.*]], -128 -; CHECK-NEXT: [[R:%.*]] = icmp sge i8 [[XN1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = icmp slt i8 [[X:%.*]], 0 ; CHECK-NEXT: ret i1 [[R]] ; %xn1 = or i8 %x, 128 @@ -185,9 +179,7 @@ ; CHECK-NEXT: [[C:%.*]] = icmp sgt i8 [[CP2]], -1 ; CHECK-NEXT: br i1 [[C]], label [[POS:%.*]], label [[NEG:%.*]] ; CHECK: neg: -; CHECK-NEXT: [[XN1:%.*]] = or i8 [[CP2]], [[X:%.*]] -; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[XN1]], [[X]] -; CHECK-NEXT: ret i1 [[R]] +; CHECK-NEXT: ret i1 false ; CHECK: pos: ; CHECK-NEXT: call void @barrier() ; CHECK-NEXT: ret i1 false @@ -208,8 +200,7 @@ define <2 x i1> @or_sgt_intmin_2(<2 x i8> %xx, <2 x i8> %z) { ; CHECK-LABEL: @or_sgt_intmin_2( ; CHECK-NEXT: [[X:%.*]] = add <2 x i8> [[XX:%.*]], [[Z:%.*]] -; CHECK-NEXT: [[XN1:%.*]] = or <2 x i8> [[X]], -; CHECK-NEXT: [[R:%.*]] = icmp sgt <2 x i8> [[X]], [[XN1]] +; CHECK-NEXT: [[R:%.*]] = icmp sgt <2 x i8> [[X]], ; CHECK-NEXT: ret <2 x i1> [[R]] ; %x = add <2 x i8> %xx, %z