forked from OSchip/llvm-project
[ValueTracking] Strengthen impliesPoison reasoning
Split impliesPoison into two recursive walks, one over V, the other over ValAssumedPoison. This allows us to reason about poison implications in a number of additional cases that are important in practice. This is a generalized form of D94859, which handles the cmp to cmp implication in particular. Differential Revision: https://reviews.llvm.org/D94866
This commit is contained in:
parent
0808c7009a
commit
051ec9f5f4
|
@ -4806,64 +4806,49 @@ bool llvm::canCreatePoison(const Operator *Op) {
|
|||
return ::canCreateUndefOrPoison(Op, /*PoisonOnly=*/true);
|
||||
}
|
||||
|
||||
bool llvm::impliesPoison(const Value *ValAssumedPoison, const Value *V) {
|
||||
// Construct a set of values which are known to be poison from the knowledge
|
||||
// that ValAssumedPoison is poison.
|
||||
SmallPtrSet<const Value *, 4> PoisonValues;
|
||||
PoisonValues.insert(ValAssumedPoison);
|
||||
const Instruction *PoisonI = dyn_cast<Instruction>(ValAssumedPoison);
|
||||
unsigned Depth = 0;
|
||||
const unsigned MaxDepth = 2;
|
||||
|
||||
while (PoisonI && Depth < MaxDepth) {
|
||||
// We'd like to know whether an operand of PoisonI is also poison.
|
||||
if (canCreatePoison(cast<Operator>(PoisonI)))
|
||||
// PoisonI can be a poison-generating instruction, so don't look further
|
||||
break;
|
||||
|
||||
const Value *NextVal = nullptr;
|
||||
bool MoreThanOneCandidate = false;
|
||||
// See which operand can be poison
|
||||
for (const auto &Op : PoisonI->operands()) {
|
||||
if (!isGuaranteedNotToBeUndefOrPoison(Op.get())) {
|
||||
// Op can be poison.
|
||||
if (NextVal) {
|
||||
// There is more than one operand that can make PoisonI poison.
|
||||
MoreThanOneCandidate = true;
|
||||
break;
|
||||
}
|
||||
NextVal = Op.get();
|
||||
}
|
||||
}
|
||||
|
||||
if (NextVal == nullptr) {
|
||||
// All operands are non-poison, so PoisonI cannot be poison.
|
||||
// Since assumption is false, return true
|
||||
return true;
|
||||
} else if (MoreThanOneCandidate)
|
||||
break;
|
||||
|
||||
Depth++;
|
||||
PoisonValues.insert(NextVal);
|
||||
PoisonI = dyn_cast<Instruction>(NextVal);
|
||||
}
|
||||
|
||||
if (PoisonValues.contains(V))
|
||||
static bool directlyImpliesPoison(const Value *ValAssumedPoison,
|
||||
const Value *V, unsigned Depth) {
|
||||
if (ValAssumedPoison == V)
|
||||
return true;
|
||||
|
||||
// Let's look one level further, by seeing its arguments if I was an
|
||||
// instruction.
|
||||
// This happens when I is e.g. 'icmp X, const' where X is in PoisonValues.
|
||||
const unsigned MaxDepth = 2;
|
||||
if (Depth >= MaxDepth)
|
||||
return false;
|
||||
|
||||
const auto *I = dyn_cast<Instruction>(V);
|
||||
if (I && propagatesPoison(cast<Operator>(I))) {
|
||||
for (const auto &Op : I->operands())
|
||||
if (PoisonValues.count(Op.get()))
|
||||
return true;
|
||||
return any_of(I->operands(), [=](const Value *Op) {
|
||||
return directlyImpliesPoison(ValAssumedPoison, Op, Depth + 1);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool impliesPoison(const Value *ValAssumedPoison, const Value *V,
|
||||
unsigned Depth) {
|
||||
if (isGuaranteedNotToBeUndefOrPoison(ValAssumedPoison))
|
||||
return true;
|
||||
|
||||
if (directlyImpliesPoison(ValAssumedPoison, V, /* Depth */ 0))
|
||||
return true;
|
||||
|
||||
const unsigned MaxDepth = 2;
|
||||
if (Depth >= MaxDepth)
|
||||
return false;
|
||||
|
||||
const auto *I = dyn_cast<Instruction>(ValAssumedPoison);
|
||||
if (I && !canCreatePoison(cast<Operator>(I))) {
|
||||
return all_of(I->operands(), [=](const Value *Op) {
|
||||
return impliesPoison(Op, V, Depth + 1);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool llvm::impliesPoison(const Value *ValAssumedPoison, const Value *V) {
|
||||
return ::impliesPoison(ValAssumedPoison, V, /* Depth */ 0);
|
||||
}
|
||||
|
||||
static bool programUndefinedIfUndefOrPoison(const Value *V,
|
||||
bool PoisonOnly);
|
||||
|
||||
|
|
|
@ -148,10 +148,9 @@ define i1 @logical_or_noundef_a(i1 noundef %a, i1 %b) {
|
|||
}
|
||||
|
||||
; Noundef on false value allows conversion to or.
|
||||
; TODO: impliesPoison doesn't handle this yet.
|
||||
define i1 @logical_or_noundef_b(i1 %a, i1 noundef %b) {
|
||||
; CHECK-LABEL: @logical_or_noundef_b(
|
||||
; CHECK-NEXT: [[RES:%.*]] = select i1 [[A:%.*]], i1 true, i1 [[B:%.*]]
|
||||
; CHECK-NEXT: [[RES:%.*]] = or i1 [[A:%.*]], [[B:%.*]]
|
||||
; CHECK-NEXT: ret i1 [[RES]]
|
||||
;
|
||||
%res = select i1 %a, i1 true, i1 %b
|
||||
|
@ -169,10 +168,9 @@ define i1 @logical_and_noundef_a(i1 noundef %a, i1 %b) {
|
|||
}
|
||||
|
||||
; Noundef on false value allows conversion to and.
|
||||
; TODO: impliesPoison doesn't handle this yet.
|
||||
define i1 @logical_and_noundef_b(i1 %a, i1 noundef %b) {
|
||||
; CHECK-LABEL: @logical_and_noundef_b(
|
||||
; CHECK-NEXT: [[RES:%.*]] = select i1 [[A:%.*]], i1 [[B:%.*]], i1 false
|
||||
; CHECK-NEXT: [[RES:%.*]] = and i1 [[A:%.*]], [[B:%.*]]
|
||||
; CHECK-NEXT: ret i1 [[RES]]
|
||||
;
|
||||
%res = select i1 %a, i1 %b, i1 false
|
||||
|
|
|
@ -748,6 +748,46 @@ TEST_F(ValueTrackingTest, impliesPoisonTest_AddNsw) {
|
|||
EXPECT_FALSE(impliesPoison(A2, A));
|
||||
}
|
||||
|
||||
TEST_F(ValueTrackingTest, impliesPoisonTest_Cmp) {
|
||||
parseAssembly("define void @test(i32 %x, i32 %y, i1 %c) {\n"
|
||||
" %A2 = icmp eq i32 %x, %y\n"
|
||||
" %A0 = icmp ult i32 %x, %y\n"
|
||||
" %A = or i1 %A0, %c\n"
|
||||
" ret void\n"
|
||||
"}");
|
||||
EXPECT_TRUE(impliesPoison(A2, A));
|
||||
}
|
||||
|
||||
TEST_F(ValueTrackingTest, impliesPoisonTest_FCmpFMF) {
|
||||
parseAssembly("define void @test(float %x, float %y, i1 %c) {\n"
|
||||
" %A2 = fcmp nnan oeq float %x, %y\n"
|
||||
" %A0 = fcmp olt float %x, %y\n"
|
||||
" %A = or i1 %A0, %c\n"
|
||||
" ret void\n"
|
||||
"}");
|
||||
EXPECT_FALSE(impliesPoison(A2, A));
|
||||
}
|
||||
|
||||
TEST_F(ValueTrackingTest, impliesPoisonTest_AddSubSameOps) {
|
||||
parseAssembly("define void @test(i32 %x, i32 %y, i1 %c) {\n"
|
||||
" %A2 = add i32 %x, %y\n"
|
||||
" %A = sub i32 %x, %y\n"
|
||||
" ret void\n"
|
||||
"}");
|
||||
EXPECT_TRUE(impliesPoison(A2, A));
|
||||
}
|
||||
|
||||
TEST_F(ValueTrackingTest, impliesPoisonTest_MaskCmp) {
|
||||
parseAssembly("define void @test(i32 %x, i32 %y, i1 %c) {\n"
|
||||
" %M2 = and i32 %x, 7\n"
|
||||
" %A2 = icmp eq i32 %M2, 1\n"
|
||||
" %M = and i32 %x, 15\n"
|
||||
" %A = icmp eq i32 %M, 3\n"
|
||||
" ret void\n"
|
||||
"}");
|
||||
EXPECT_TRUE(impliesPoison(A2, A));
|
||||
}
|
||||
|
||||
TEST_F(ValueTrackingTest, ComputeNumSignBits_Shuffle_Pointers) {
|
||||
parseAssembly(
|
||||
"define <2 x i32*> @test(<2 x i32*> %x) {\n"
|
||||
|
|
Loading…
Reference in New Issue