[InstCombine] Generalize icmp handling in isKnownNonZero()

The dominating condition handling in isKnownNonZero() currently
only takes into account conditions of the form "x != 0" or "x == 0".
However, there are plenty of other conditions that imply non-zero,
a common one being "x s> 0".

Peculiarly, the handling for assumes was already dealing with more
general non-zero-ness conditions, so this just reuses the same
logic for the dominating condition case.
This commit is contained in:
Nikita Popov 2020-12-25 12:28:01 +01:00
parent b0e6007c82
commit 35676a4f9a
3 changed files with 73 additions and 60 deletions

View File

@ -590,41 +590,30 @@ bool llvm::isValidAssumeForContext(const Instruction *Inv,
return false;
}
static bool cmpExcludesZero(CmpInst::Predicate Pred, const Value *RHS) {
// v u> y implies v != 0.
if (Pred == ICmpInst::ICMP_UGT)
return true;
// Special-case v != 0 to also handle v != null.
if (Pred == ICmpInst::ICMP_NE)
return match(RHS, m_Zero());
// All other predicates - rely on generic ConstantRange handling.
const APInt *C;
if (!match(RHS, m_APInt(C)))
return false;
ConstantRange TrueValues = ConstantRange::makeExactICmpRegion(Pred, *C);
return !TrueValues.contains(APInt::getNullValue(C->getBitWidth()));
}
static bool isKnownNonZeroFromAssume(const Value *V, const Query &Q) {
// Use of assumptions is context-sensitive. If we don't have a context, we
// cannot use them!
if (!Q.AC || !Q.CxtI)
return false;
// Note that the patterns below need to be kept in sync with the code
// in AssumptionCache::updateAffectedValues.
auto CmpExcludesZero = [V](ICmpInst *Cmp) {
auto m_V = m_CombineOr(m_Specific(V), m_PtrToInt(m_Specific(V)));
Value *RHS;
CmpInst::Predicate Pred;
if (!match(Cmp, m_c_ICmp(Pred, m_V, m_Value(RHS))))
return false;
// assume(v u> y) -> assume(v != 0)
if (Pred == ICmpInst::ICMP_UGT)
return true;
// assume(v != 0)
// We special-case this one to ensure that we handle `assume(v != null)`.
if (Pred == ICmpInst::ICMP_NE)
return match(RHS, m_Zero());
// All other predicates - rely on generic ConstantRange handling.
ConstantInt *CI;
if (!match(RHS, m_ConstantInt(CI)))
return false;
ConstantRange RHSRange(CI->getValue());
ConstantRange TrueValues =
ConstantRange::makeAllowedICmpRegion(Pred, RHSRange);
return !TrueValues.contains(APInt::getNullValue(CI->getBitWidth()));
};
if (Q.CxtI && V->getType()->isPointerTy()) {
SmallVector<Attribute::AttrKind, 2> AttrKinds{Attribute::NonNull};
if (!NullPointerIsDefined(Q.CxtI->getFunction(),
@ -651,12 +640,13 @@ static bool isKnownNonZeroFromAssume(const Value *V, const Query &Q) {
assert(I->getCalledFunction()->getIntrinsicID() == Intrinsic::assume &&
"must be an assume intrinsic");
Value *Arg = I->getArgOperand(0);
ICmpInst *Cmp = dyn_cast<ICmpInst>(Arg);
if (!Cmp)
continue;
Value *RHS;
CmpInst::Predicate Pred;
auto m_V = m_CombineOr(m_Specific(V), m_PtrToInt(m_Specific(V)));
if (!match(I->getArgOperand(0), m_c_ICmp(Pred, m_V, m_Value(RHS))))
return false;
if (CmpExcludesZero(Cmp) && isValidAssumeForContext(I, Q.CxtI, Q.DT))
if (cmpExcludesZero(Pred, RHS) && isValidAssumeForContext(I, Q.CxtI, Q.DT))
return true;
}
@ -2113,10 +2103,17 @@ static bool isKnownNonNullFromDominatingCondition(const Value *V,
}
// Consider only compare instructions uniquely controlling a branch
Value *RHS;
CmpInst::Predicate Pred;
if (!match(const_cast<User *>(U),
m_c_ICmp(Pred, m_Specific(V), m_Zero())) ||
(Pred != ICmpInst::ICMP_EQ && Pred != ICmpInst::ICMP_NE))
if (!match(U, m_c_ICmp(Pred, m_Specific(V), m_Value(RHS))))
continue;
bool NonNullIfTrue;
if (cmpExcludesZero(Pred, RHS))
NonNullIfTrue = true;
else if (Pred == ICmpInst::ICMP_EQ && match(RHS, m_Zero()))
NonNullIfTrue = false;
else
continue;
SmallVector<const User *, 4> WorkList;
@ -2133,7 +2130,7 @@ static bool isKnownNonNullFromDominatingCondition(const Value *V,
// propagate "pred != null" condition through AND because it is only
// correct to assume that all conditions of AND are met in true branch.
// TODO: Support similar logic of OR and EQ predicate?
if (Pred == ICmpInst::ICMP_NE)
if (NonNullIfTrue)
if (auto *BO = dyn_cast<BinaryOperator>(Curr))
if (BO->getOpcode() == Instruction::And) {
for (auto *BOU : BO->users())
@ -2146,11 +2143,11 @@ static bool isKnownNonNullFromDominatingCondition(const Value *V,
assert(BI->isConditional() && "uses a comparison!");
BasicBlock *NonNullSuccessor =
BI->getSuccessor(Pred == ICmpInst::ICMP_EQ ? 1 : 0);
BI->getSuccessor(NonNullIfTrue ? 0 : 1);
BasicBlockEdge Edge(BI->getParent(), NonNullSuccessor);
if (Edge.isSingleEdge() && DT->dominates(Edge, CtxI->getParent()))
return true;
} else if (Pred == ICmpInst::ICMP_NE && isGuard(Curr) &&
} else if (NonNullIfTrue && isGuard(Curr) &&
DT->dominates(cast<Instruction>(Curr), CtxI)) {
return true;
}

View File

@ -1404,24 +1404,40 @@ hd2:
; Original from PR43833
declare void @sink(i32*)
; FIXME: the sink argument should be marked nonnull as in @PR43833_simple.
define void @PR43833(i32* %0, i32 %1) {
; CHECK-LABEL: define {{[^@]+}}@PR43833
; CHECK-SAME: (i32* [[TMP0:%.*]], i32 [[TMP1:%.*]]) {
; CHECK-NEXT: [[TMP3:%.*]] = icmp sgt i32 [[TMP1]], 1
; CHECK-NEXT: br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]]
; CHECK: 4:
; CHECK-NEXT: [[TMP5:%.*]] = zext i32 [[TMP1]] to i64
; CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds i32, i32* [[TMP0]], i64 [[TMP5]]
; CHECK-NEXT: br label [[TMP8:%.*]]
; CHECK: 7:
; CHECK-NEXT: ret void
; CHECK: 8:
; CHECK-NEXT: [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ]
; CHECK-NEXT: tail call void @sink(i32* [[TMP6]])
; CHECK-NEXT: [[TMP10]] = add nuw nsw i32 [[TMP9]], 1
; CHECK-NEXT: [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]]
; CHECK-NEXT: br i1 [[TMP11]], label [[TMP7]], label [[TMP8]]
; IS________OPM-LABEL: define {{[^@]+}}@PR43833
; IS________OPM-SAME: (i32* [[TMP0:%.*]], i32 [[TMP1:%.*]]) {
; IS________OPM-NEXT: [[TMP3:%.*]] = icmp sgt i32 [[TMP1]], 1
; IS________OPM-NEXT: br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]]
; IS________OPM: 4:
; IS________OPM-NEXT: [[TMP5:%.*]] = zext i32 [[TMP1]] to i64
; IS________OPM-NEXT: [[TMP6:%.*]] = getelementptr inbounds i32, i32* [[TMP0]], i64 [[TMP5]]
; IS________OPM-NEXT: br label [[TMP8:%.*]]
; IS________OPM: 7:
; IS________OPM-NEXT: ret void
; IS________OPM: 8:
; IS________OPM-NEXT: [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ]
; IS________OPM-NEXT: tail call void @sink(i32* [[TMP6]])
; IS________OPM-NEXT: [[TMP10]] = add nuw nsw i32 [[TMP9]], 1
; IS________OPM-NEXT: [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]]
; IS________OPM-NEXT: br i1 [[TMP11]], label [[TMP7]], label [[TMP8]]
;
; IS________NPM-LABEL: define {{[^@]+}}@PR43833
; IS________NPM-SAME: (i32* [[TMP0:%.*]], i32 [[TMP1:%.*]]) {
; IS________NPM-NEXT: [[TMP3:%.*]] = icmp sgt i32 [[TMP1]], 1
; IS________NPM-NEXT: br i1 [[TMP3]], label [[TMP4:%.*]], label [[TMP7:%.*]]
; IS________NPM: 4:
; IS________NPM-NEXT: [[TMP5:%.*]] = zext i32 [[TMP1]] to i64
; IS________NPM-NEXT: [[TMP6:%.*]] = getelementptr inbounds i32, i32* [[TMP0]], i64 [[TMP5]]
; IS________NPM-NEXT: br label [[TMP8:%.*]]
; IS________NPM: 7:
; IS________NPM-NEXT: ret void
; IS________NPM: 8:
; IS________NPM-NEXT: [[TMP9:%.*]] = phi i32 [ 1, [[TMP4]] ], [ [[TMP10:%.*]], [[TMP8]] ]
; IS________NPM-NEXT: tail call void @sink(i32* nonnull [[TMP6]])
; IS________NPM-NEXT: [[TMP10]] = add nuw nsw i32 [[TMP9]], 1
; IS________NPM-NEXT: [[TMP11:%.*]] = icmp eq i32 [[TMP10]], [[TMP1]]
; IS________NPM-NEXT: br i1 [[TMP11]], label [[TMP7]], label [[TMP8]]
;
%3 = icmp sgt i32 %1, 1
br i1 %3, label %4, label %7

View File

@ -140,7 +140,7 @@ define i64 @test_sgt_zero(i64 %x) {
; CHECK-NEXT: [[C:%.*]] = icmp sgt i64 [[X:%.*]], 0
; CHECK-NEXT: br i1 [[C]], label [[NON_ZERO:%.*]], label [[EXIT:%.*]]
; CHECK: non_zero:
; CHECK-NEXT: [[CTZ:%.*]] = call i64 @llvm.ctlz.i64(i64 [[X]], i1 false), [[RNG0]]
; CHECK-NEXT: [[CTZ:%.*]] = call i64 @llvm.ctlz.i64(i64 [[X]], i1 true), [[RNG0]]
; CHECK-NEXT: ret i64 [[CTZ]]
; CHECK: exit:
; CHECK-NEXT: ret i64 -1
@ -163,7 +163,7 @@ define i64 @test_slt_neg_ten(i64 %x) {
; CHECK-NEXT: [[C:%.*]] = icmp slt i64 [[X:%.*]], -10
; CHECK-NEXT: br i1 [[C]], label [[NON_ZERO:%.*]], label [[EXIT:%.*]]
; CHECK: non_zero:
; CHECK-NEXT: [[CTZ:%.*]] = call i64 @llvm.ctlz.i64(i64 [[X]], i1 false), [[RNG0]]
; CHECK-NEXT: [[CTZ:%.*]] = call i64 @llvm.ctlz.i64(i64 [[X]], i1 true), [[RNG0]]
; CHECK-NEXT: ret i64 [[CTZ]]
; CHECK: exit:
; CHECK-NEXT: ret i64 -1
@ -209,7 +209,7 @@ define i64 @test_ugt_unknown(i64 %x, i64 %y) {
; CHECK-NEXT: [[C:%.*]] = icmp ugt i64 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: br i1 [[C]], label [[NON_ZERO:%.*]], label [[EXIT:%.*]]
; CHECK: non_zero:
; CHECK-NEXT: [[CTZ:%.*]] = call i64 @llvm.ctlz.i64(i64 [[X]], i1 false), [[RNG0]]
; CHECK-NEXT: [[CTZ:%.*]] = call i64 @llvm.ctlz.i64(i64 [[X]], i1 true), [[RNG0]]
; CHECK-NEXT: ret i64 [[CTZ]]
; CHECK: exit:
; CHECK-NEXT: ret i64 -1