1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 20:51:52 +01:00

[ValueTracking] isKnownNonZero() should take non-null-ness assumptions into consideration (PR43267)

Summary:
It is pretty common to assume that something is not zero.
Even optimizer itself sometimes emits such assumptions
(e.g. `addAssumeNonNull()` in `PromoteMemoryToRegister.cpp`).

But we currently don't deal with such assumptions :)
The only way `isKnownNonZero()` handles assumptions is
by calling `computeKnownBits()` which calls `computeKnownBitsFromAssume()`.
But `x != 0` does not tell us anything about set bits,
it only says that there are *some* set bits.
So naturally, `KnownBits` does not get populated,
and we fail to make use of this assumption.

I propose to deal with this special case by special-casing it
via adding a `isKnownNonZeroFromAssume()` that returns boolean
when there is an applicable assumption.

While there, we also deal with other predicates,
mainly if the comparison is with constant.

Fixes [[ https://bugs.llvm.org/show_bug.cgi?id=43267 | PR43267 ]].

Differential Revision: https://reviews.llvm.org/D71660
This commit is contained in:
Roman Lebedev 2019-12-18 20:24:38 +03:00
parent 3540c93924
commit d746d62b1a
3 changed files with 99 additions and 26 deletions

View File

@ -576,6 +576,73 @@ bool llvm::isValidAssumeForContext(const Instruction *Inv,
return !isEphemeralValueOf(Inv, CxtI);
}
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;
// Canonicalize 'v' to be on the LHS of the comparison.
if (Cmp->getOperand(1) != RHS)
Pred = CmpInst::getSwappedPredicate(Pred);
// 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()));
};
for (auto &AssumeVH : Q.AC->assumptionsFor(V)) {
if (!AssumeVH)
continue;
CallInst *I = cast<CallInst>(AssumeVH);
assert(I->getFunction() == Q.CxtI->getFunction() &&
"Got assumption for the wrong function!");
if (Q.isExcluded(I))
continue;
// Warning: This loop can end up being somewhat performance sensitive.
// We're running this loop for once for each value queried resulting in a
// runtime of ~O(#assumes * #values).
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;
if (CmpExcludesZero(Cmp) && isValidAssumeForContext(I, Q.CxtI, Q.DT))
return true;
}
return false;
}
static void computeKnownBitsFromAssume(const Value *V, KnownBits &Known,
unsigned Depth, const Query &Q) {
// Use of assumptions is context-sensitive. If we don't have a context, we
@ -2079,6 +2146,9 @@ bool isKnownNonZero(const Value *V, unsigned Depth, const Query &Q) {
}
}
if (isKnownNonZeroFromAssume(V, Q))
return true;
// Some of the tests below are recursive, so bail out if we hit the limit.
if (Depth++ >= MaxDepth)
return false;

View File

@ -252,8 +252,7 @@ define i1 @nonnull2(i32* %a) {
; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* [[A:%.*]], align 4
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[LOAD]], 0
; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]])
; CHECK-NEXT: [[RVAL:%.*]] = icmp eq i32 [[LOAD]], 0
; CHECK-NEXT: ret i1 [[RVAL]]
; CHECK-NEXT: ret i1 false
;
%load = load i32, i32* %a
%cmp = icmp ne i32 %load, 0
@ -273,10 +272,10 @@ define i1 @nonnull3(i32** %a, i1 %control) {
; CHECK: taken:
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32* [[LOAD]], null
; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]])
; CHECK-NEXT: [[RVAL:%.*]] = icmp eq i32* [[LOAD]], null
; CHECK-NEXT: ret i1 [[RVAL]]
; CHECK-NEXT: ret i1 false
; CHECK: not_taken:
; CHECK-NEXT: ret i1 true
; CHECK-NEXT: [[RVAL_2:%.*]] = icmp sgt i32* [[LOAD]], null
; CHECK-NEXT: ret i1 [[RVAL_2]]
;
entry:
%load = load i32*, i32** %a
@ -287,7 +286,8 @@ taken:
%rval = icmp eq i32* %load, null
ret i1 %rval
not_taken:
ret i1 true
%rval.2 = icmp sgt i32* %load, null
ret i1 %rval.2
}
; Make sure the above canonicalization does not trigger
@ -300,8 +300,7 @@ define i1 @nonnull4(i32** %a) {
; CHECK-NEXT: tail call void @escape(i32* [[LOAD]])
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32* [[LOAD]], null
; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]])
; CHECK-NEXT: [[RVAL:%.*]] = icmp eq i32* [[LOAD]], null
; CHECK-NEXT: ret i1 [[RVAL]]
; CHECK-NEXT: ret i1 false
;
%load = load i32*, i32** %a
;; This call may throw!
@ -353,12 +352,12 @@ define i32 @assumption_conflicts_with_known_bits(i32 %a, i32 %b) {
define void @debug_interference(i8 %x) {
; CHECK-LABEL: @debug_interference(
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i32 5, metadata !7, metadata !DIExpression()), !dbg !9
; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]])
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i32 5, metadata !7, metadata !DIExpression()), !dbg !9
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i32 5, metadata !7, metadata !DIExpression()), !dbg !9
; CHECK-NEXT: tail call void @llvm.assume(i1 false)
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i32 5, metadata !7, metadata !DIExpression()), !dbg !9
; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i32 5, metadata !7, metadata !DIExpression()), !dbg !9
; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP2]])
; CHECK-NEXT: ret void
;
%cmp1 = icmp eq i8 %x, 0

View File

@ -10,8 +10,7 @@ define i1 @nonnull0_true(i8* %x) {
; CHECK-LABEL: @nonnull0_true(
; CHECK-NEXT: [[A:%.*]] = icmp ne i8* [[X:%.*]], null
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[Q:%.*]] = icmp ne i8* [[X]], null
; CHECK-NEXT: ret i1 [[Q]]
; CHECK-NEXT: ret i1 true
;
%a = icmp ne i8* %x, null
call void @llvm.assume(i1 %a)
@ -24,8 +23,7 @@ define i1 @nonnull1_true(i8* %x) {
; CHECK-NEXT: [[INTPTR:%.*]] = ptrtoint i8* [[X:%.*]] to i64
; CHECK-NEXT: [[A:%.*]] = icmp ne i64 [[INTPTR]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[Q:%.*]] = icmp ne i8* [[X]], null
; CHECK-NEXT: ret i1 [[Q]]
; CHECK-NEXT: ret i1 true
;
%intptr = ptrtoint i8* %x to i64
%a = icmp ne i64 %intptr, 0
@ -38,14 +36,24 @@ define i1 @nonnull2_true(i8 %x, i8 %y) {
; CHECK-LABEL: @nonnull2_true(
; CHECK-NEXT: [[A:%.*]] = icmp ugt i8 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[Q:%.*]] = icmp ne i8 [[X]], 0
; CHECK-NEXT: ret i1 [[Q]]
; CHECK-NEXT: ret i1 true
;
%a = icmp ugt i8 %x, %y
call void @llvm.assume(i1 %a)
%q = icmp ne i8 %x, 0
ret i1 %q
}
define i1 @nonnull2_true_swapped(i8 %x, i8 %y) {
; CHECK-LABEL: @nonnull2_true_swapped(
; CHECK-NEXT: [[A:%.*]] = icmp ult i8 [[Y:%.*]], [[X:%.*]]
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: ret i1 true
;
%a = icmp ult i8 %y, %x
call void @llvm.assume(i1 %a)
%q = icmp ne i8 %x, 0
ret i1 %q
}
define i1 @nonnull3_unknown(i8 %x) {
; CHECK-LABEL: @nonnull3_unknown(
@ -61,8 +69,7 @@ define i1 @nonnull4_true(i8 %x) {
; CHECK-LABEL: @nonnull4_true(
; CHECK-NEXT: [[A:%.*]] = icmp uge i8 [[X:%.*]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[Q:%.*]] = icmp ne i8 [[X]], 0
; CHECK-NEXT: ret i1 [[Q]]
; CHECK-NEXT: ret i1 true
;
%a = icmp uge i8 %x, 1
call void @llvm.assume(i1 %a)
@ -86,8 +93,7 @@ define i1 @nonnull6_true(i8 %x) {
; CHECK-LABEL: @nonnull6_true(
; CHECK-NEXT: [[A:%.*]] = icmp sgt i8 [[X:%.*]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[Q:%.*]] = icmp ne i8 [[X]], 0
; CHECK-NEXT: ret i1 [[Q]]
; CHECK-NEXT: ret i1 true
;
%a = icmp sgt i8 %x, 0
call void @llvm.assume(i1 %a)
@ -98,8 +104,7 @@ define i1 @nonnull7_true(i8 %x) {
; CHECK-LABEL: @nonnull7_true(
; CHECK-NEXT: [[A:%.*]] = icmp sgt i8 [[X:%.*]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[Q:%.*]] = icmp ne i8 [[X]], 0
; CHECK-NEXT: ret i1 [[Q]]
; CHECK-NEXT: ret i1 true
;
%a = icmp sgt i8 %x, 1
call void @llvm.assume(i1 %a)
@ -135,8 +140,7 @@ define i1 @nonnull10_true(i8 %x) {
; CHECK-LABEL: @nonnull10_true(
; CHECK-NEXT: [[A:%.*]] = icmp sge i8 [[X:%.*]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[A]])
; CHECK-NEXT: [[Q:%.*]] = icmp ne i8 [[X]], 0
; CHECK-NEXT: ret i1 [[Q]]
; CHECK-NEXT: ret i1 true
;
%a = icmp sge i8 %x, 1
call void @llvm.assume(i1 %a)