mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 12:41:49 +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:
parent
3540c93924
commit
d746d62b1a
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user