diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -3255,6 +3255,23 @@ (C2->slt(*C1) && C1->isNonPositive()); } +static Value *simplifyICmpWithShl(CmpInst::Predicate Predicate, Value *LHS, + Value *RHS, const SimplifyQuery &Q, + unsigned MaxRecurse) { + Value *V; + const APInt *ShiftAmountL, *ShiftAmountR; + if (match(LHS, m_Shl(m_Value(V), m_APInt(ShiftAmountL))) && + match(RHS, m_Shl(m_Deferred(V), m_APInt(ShiftAmountR))) && + isKnownNonZero(V, Q.DL)) { + if (ICmpInst::compare(*ShiftAmountL, *ShiftAmountR, Predicate)) + return getTrue(getCompareTy(LHS)); + return simplifyICmpInst(Predicate, cast(LHS)->getOperand(0), + cast(RHS)->getOperand(0), Q, + MaxRecurse - 1); + } + return nullptr; +} + /// TODO: A large part of this logic is duplicated in InstCombine's /// foldICmpBinOp(). We should be able to share that and avoid the code /// duplication. @@ -3394,47 +3411,61 @@ return ConstantInt::getTrue(getCompareTy(RHS)); } - if (MaxRecurse && LBO && RBO && LBO->getOpcode() == RBO->getOpcode() && - LBO->getOperand(1) == RBO->getOperand(1)) { - switch (LBO->getOpcode()) { - default: - break; - case Instruction::UDiv: - case Instruction::LShr: - if (ICmpInst::isSigned(Pred) || !Q.IIQ.isExact(LBO) || - !Q.IIQ.isExact(RBO)) + if (MaxRecurse && LBO && RBO && LBO->getOpcode() == RBO->getOpcode()) { + if (LBO->getOperand(0) == RBO->getOperand(0)) { + switch (LBO->getOpcode()) { + default: break; - if (Value *V = simplifyICmpInst(Pred, LBO->getOperand(0), - RBO->getOperand(0), Q, MaxRecurse - 1)) - return V; - break; - case Instruction::SDiv: - if (!ICmpInst::isEquality(Pred) || !Q.IIQ.isExact(LBO) || - !Q.IIQ.isExact(RBO)) + case Instruction::Shl: + bool NUW = Q.IIQ.hasNoUnsignedWrap(LBO) && Q.IIQ.hasNoUnsignedWrap(RBO); + bool NSW = Q.IIQ.hasNoSignedWrap(LBO) && Q.IIQ.hasNoSignedWrap(RBO); + if ((!NUW && !NSW) || (!NSW && ICmpInst::isSigned(Pred))) + break; + if (Value *V = simplifyICmpWithShl(Pred, LBO, RBO, Q, MaxRecurse - 1)) + return V; + } + } + if (LBO->getOperand(1) == RBO->getOperand(1)) { + switch (LBO->getOpcode()) { + default: break; - if (Value *V = simplifyICmpInst(Pred, LBO->getOperand(0), - RBO->getOperand(0), Q, MaxRecurse - 1)) - return V; - break; - case Instruction::AShr: - if (!Q.IIQ.isExact(LBO) || !Q.IIQ.isExact(RBO)) + case Instruction::UDiv: + case Instruction::LShr: + if (ICmpInst::isSigned(Pred) || !Q.IIQ.isExact(LBO) || + !Q.IIQ.isExact(RBO)) + break; + if (Value *V = simplifyICmpInst(Pred, LBO->getOperand(0), + RBO->getOperand(0), Q, MaxRecurse - 1)) + return V; break; - if (Value *V = simplifyICmpInst(Pred, LBO->getOperand(0), - RBO->getOperand(0), Q, MaxRecurse - 1)) - return V; - break; - case Instruction::Shl: { - bool NUW = Q.IIQ.hasNoUnsignedWrap(LBO) && Q.IIQ.hasNoUnsignedWrap(RBO); - bool NSW = Q.IIQ.hasNoSignedWrap(LBO) && Q.IIQ.hasNoSignedWrap(RBO); - if (!NUW && !NSW) + case Instruction::SDiv: + if (!ICmpInst::isEquality(Pred) || !Q.IIQ.isExact(LBO) || + !Q.IIQ.isExact(RBO)) + break; + if (Value *V = simplifyICmpInst(Pred, LBO->getOperand(0), + RBO->getOperand(0), Q, MaxRecurse - 1)) + return V; break; - if (!NSW && ICmpInst::isSigned(Pred)) + case Instruction::AShr: + if (!Q.IIQ.isExact(LBO) || !Q.IIQ.isExact(RBO)) + break; + if (Value *V = simplifyICmpInst(Pred, LBO->getOperand(0), + RBO->getOperand(0), Q, MaxRecurse - 1)) + return V; break; - if (Value *V = simplifyICmpInst(Pred, LBO->getOperand(0), - RBO->getOperand(0), Q, MaxRecurse - 1)) - return V; - break; - } + case Instruction::Shl: { + bool NUW = Q.IIQ.hasNoUnsignedWrap(LBO) && Q.IIQ.hasNoUnsignedWrap(RBO); + bool NSW = Q.IIQ.hasNoSignedWrap(LBO) && Q.IIQ.hasNoSignedWrap(RBO); + if (!NUW && !NSW) + break; + if (!NSW && ICmpInst::isSigned(Pred)) + break; + if (Value *V = simplifyICmpInst(Pred, LBO->getOperand(0), + RBO->getOperand(0), Q, MaxRecurse - 1)) + return V; + break; + } + } } } return nullptr; diff --git a/llvm/test/Transforms/InstSimplify/compare.ll b/llvm/test/Transforms/InstSimplify/compare.ll --- a/llvm/test/Transforms/InstSimplify/compare.ll +++ b/llvm/test/Transforms/InstSimplify/compare.ll @@ -2812,6 +2812,143 @@ ret i1 %res } + +define i1 @icmp_lshr_known_non_zero_ult_true(i8 %x) { +; CHECK-LABEL: @icmp_lshr_known_non_zero_ult_true( +; CHECK-NEXT: ret i1 true +; + %or = or i8 %x, 1 + %x1 = shl nuw i8 %or, 1 + %x2 = shl nuw i8 %or, 2 + %cmp = icmp ult i8 %x1, %x2 + ret i1 %cmp +} + +define i1 @icmp_lshr_known_non_zero_ult_false(i8 %x) { +; CHECK-LABEL: @icmp_lshr_known_non_zero_ult_false( +; CHECK-NEXT: ret i1 false +; + %or = or i8 %x, 1 + %x1 = shl nuw i8 %or, 1 + %x2 = shl nuw i8 %or, 2 + %cmp = icmp ugt i8 %x1, %x2 + ret i1 %cmp +} + +define i1 @icmp_lshr_known_non_zero_slt_true(i8 %x) { +; CHECK-LABEL: @icmp_lshr_known_non_zero_slt_true( +; CHECK-NEXT: ret i1 true +; + %or = or i8 %x, 1 + %x1 = shl nuw nsw i8 %or, 1 + %x2 = shl nuw nsw i8 %or, 2 + %cmp = icmp slt i8 %x1, %x2 + ret i1 %cmp +} + +define i1 @icmp_lshr_known_non_zero_slt_false(i8 %x) { +; CHECK-LABEL: @icmp_lshr_known_non_zero_slt_false( +; CHECK-NEXT: ret i1 false +; + %or = or i8 %x, 1 + %x1 = shl nuw nsw i8 %or, 2 + %x2 = shl nuw nsw i8 %or, 1 + %cmp = icmp slt i8 %x1, %x2 + ret i1 %cmp +} + +define i1 @neg_icmp_lshr_unknown_value(i8 %x) { +; CHECK-LABEL: @neg_icmp_lshr_unknown_value( +; CHECK-NEXT: [[X1:%.*]] = shl nuw i8 [[X:%.*]], 2 +; CHECK-NEXT: [[X2:%.*]] = shl nuw i8 [[X]], 1 +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[X1]], [[X2]] +; CHECK-NEXT: ret i1 [[CMP]] +; + %x1 = shl nuw i8 %x, 2 + %x2 = shl nuw i8 %x, 1 + %cmp = icmp ugt i8 %x1, %x2 + ret i1 %cmp +} + +define i1 @neg_icmp_lshr_unknown_shift(i8 %x, i8 %C1) { +; CHECK-LABEL: @neg_icmp_lshr_unknown_shift( +; CHECK-NEXT: [[OR:%.*]] = or i8 [[X:%.*]], 1 +; CHECK-NEXT: [[X1:%.*]] = shl nuw i8 [[OR]], 2 +; CHECK-NEXT: [[X2:%.*]] = shl nuw i8 [[OR]], [[C1:%.*]] +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[X1]], [[X2]] +; CHECK-NEXT: ret i1 [[CMP]] +; + %or = or i8 %x, 1 + %x1 = shl nuw i8 %or, 2 + %x2 = shl nuw i8 %or, %C1 + %cmp = icmp ugt i8 %x1, %x2 + ret i1 %cmp +} + +define i1 @neg_icmp_lshr_known_non_zero_sgt_missing_nuw(i8 %x) { +; CHECK-LABEL: @neg_icmp_lshr_known_non_zero_sgt_missing_nuw( +; CHECK-NEXT: [[OR:%.*]] = or i8 [[X:%.*]], 1 +; CHECK-NEXT: [[X1:%.*]] = shl nsw i8 [[OR]], 2 +; CHECK-NEXT: [[X2:%.*]] = shl i8 [[OR]], 1 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[X1]], [[X2]] +; CHECK-NEXT: ret i1 [[CMP]] +; + %or = or i8 %x, 1 + %x1 = shl nsw i8 %or, 2 + %x2 = shl i8 %or, 1 + %cmp = icmp sgt i8 %x1, %x2 + ret i1 %cmp +} + +define i1 @neg_icmp_lshr_known_non_zero_sgt_missing_nsw(i8 %x) { +; CHECK-LABEL: @neg_icmp_lshr_known_non_zero_sgt_missing_nsw( +; CHECK-NEXT: [[OR:%.*]] = or i8 [[X:%.*]], 1 +; CHECK-NEXT: [[X1:%.*]] = shl nuw i8 [[OR]], 2 +; CHECK-NEXT: [[X2:%.*]] = shl i8 [[OR]], 1 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[X1]], [[X2]] +; CHECK-NEXT: ret i1 [[CMP]] +; + %or = or i8 %x, 1 + %x1 = shl nuw i8 %or, 2 + %x2 = shl i8 %or, 1 + %cmp = icmp sgt i8 %x1, %x2 + ret i1 %cmp +} + +define i1 @neg_icmp_lshr_known_non_zero_sgt_nuw_nsw_smallest_shift(i8 %x) { +; CHECK-LABEL: @neg_icmp_lshr_known_non_zero_sgt_nuw_nsw_smallest_shift( +; CHECK-NEXT: [[OR:%.*]] = or i8 [[X:%.*]], 1 +; CHECK-NEXT: [[X1:%.*]] = shl nuw nsw i8 [[OR]], 1 +; CHECK-NEXT: [[X2:%.*]] = shl i8 [[OR]], 2 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[X1]], [[X2]] +; CHECK-NEXT: ret i1 [[CMP]] +; + %or = or i8 %x, 1 + %x1 = shl nuw nsw i8 %or, 1 + %x2 = shl i8 %or, 2 + %cmp = icmp sgt i8 %x1, %x2 + ret i1 %cmp +} + +define i1 @neg_icmp_lshr_different_shift_values() { +; CHECK-LABEL: @neg_icmp_lshr_different_shift_values( +; CHECK-NEXT: [[VSCALE:%.*]] = call i64 @llvm.vscale.i64() +; CHECK-NEXT: [[VSCALE2:%.*]] = call i64 @llvm.vscale.i64() +; CHECK-NEXT: [[VSCALEX2:%.*]] = shl nuw nsw i64 [[VSCALE]], 1 +; CHECK-NEXT: [[VSCALEX4:%.*]] = shl nuw nsw i64 [[VSCALE2]], 2 +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[VSCALEX2]], [[VSCALEX4]] +; CHECK-NEXT: ret i1 [[CMP]] +; + %vscale = call i64 @llvm.vscale.i64() + %vscale2 = call i64 @llvm.vscale.i64() + %vscalex2 = shl nuw nsw i64 %vscale, 1 + %vscalex4 = shl nuw nsw i64 %vscale2, 2 + %cmp = icmp ult i64 %vscalex2, %vscalex4 + ret i1 %cmp +} + +declare i64 @llvm.vscale.i64() + ; TODO: Add coverage for global aliases, link once, etc..