mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-10-18 18:42:46 +02:00
[ValueTracking] Add canCreateUndefOrPoison & let canCreatePoison use Operator
This patch - adds `canCreateUndefOrPoison` - refactors `canCreatePoison` so it can deal with constantexprs `canCreateUndefOrPoison` will be used at D83926. Reviewed By: nikic, jdoerfert Differential Revision: https://reviews.llvm.org/D84007
This commit is contained in:
parent
6e422269bf
commit
563c63fea8
@ -21,6 +21,7 @@
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/InstrTypes.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/IR/Operator.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
@ -591,18 +592,25 @@ class Value;
|
||||
/// the parent of I.
|
||||
bool programUndefinedIfPoison(const Instruction *PoisonI);
|
||||
|
||||
/// Return true if I can create poison from non-poison operands.
|
||||
/// For vectors, canCreatePoison returns true if there is potential poison in
|
||||
/// any element of the result when vectors without poison are given as
|
||||
/// canCreateUndefOrPoison returns true if Op can create undef or poison from
|
||||
/// non-undef & non-poison operands.
|
||||
/// For vectors, canCreateUndefOrPoison returns true if there is potential
|
||||
/// poison or undef in any element of the result when vectors without
|
||||
/// undef/poison poison are given as operands.
|
||||
/// For example, given `Op = shl <2 x i32> %x, <0, 32>`, this function returns
|
||||
/// true. If Op raises immediate UB but never creates poison or undef
|
||||
/// (e.g. sdiv I, 0), canCreatePoison returns false.
|
||||
///
|
||||
/// canCreatePoison returns true if Op can create poison from non-poison
|
||||
/// operands.
|
||||
/// For example, given `I = shl <2 x i32> %x, <0, 32>`, this function returns
|
||||
/// true. If I raises immediate UB but never creates poison (e.g. sdiv I, 0),
|
||||
/// canCreatePoison returns false.
|
||||
bool canCreatePoison(const Instruction *I);
|
||||
bool canCreateUndefOrPoison(const Operator *Op);
|
||||
bool canCreatePoison(const Operator *Op);
|
||||
|
||||
/// Return true if this function can prove that V is never undef value
|
||||
/// or poison value.
|
||||
//
|
||||
/// Note that this is different from canCreateUndefOrPoison because the
|
||||
/// function assumes Op's operands are not poison/undef.
|
||||
///
|
||||
/// If CtxI and DT are specified this method performs flow-sensitive analysis
|
||||
/// and returns true if it is guaranteed to be never undef or poison
|
||||
/// immediately before the CtxI.
|
||||
|
@ -4665,31 +4665,30 @@ bool llvm::isOverflowIntrinsicNoWrap(const WithOverflowInst *WO,
|
||||
return llvm::any_of(GuardingBranches, AllUsesGuardedByBranch);
|
||||
}
|
||||
|
||||
bool llvm::canCreatePoison(const Instruction *I) {
|
||||
static bool canCreateUndefOrPoison(const Operator *Op, bool PoisonOnly) {
|
||||
// See whether I has flags that may create poison
|
||||
if (isa<OverflowingBinaryOperator>(I) &&
|
||||
(I->hasNoSignedWrap() || I->hasNoUnsignedWrap()))
|
||||
return true;
|
||||
if (isa<PossiblyExactOperator>(I) && I->isExact())
|
||||
return true;
|
||||
if (auto *FP = dyn_cast<FPMathOperator>(I)) {
|
||||
if (const auto *OvOp = dyn_cast<OverflowingBinaryOperator>(Op)) {
|
||||
if (OvOp->hasNoSignedWrap() || OvOp->hasNoUnsignedWrap())
|
||||
return true;
|
||||
}
|
||||
if (const auto *ExactOp = dyn_cast<PossiblyExactOperator>(Op))
|
||||
if (ExactOp->isExact())
|
||||
return true;
|
||||
if (const auto *FP = dyn_cast<FPMathOperator>(Op)) {
|
||||
auto FMF = FP->getFastMathFlags();
|
||||
if (FMF.noNaNs() || FMF.noInfs())
|
||||
return true;
|
||||
}
|
||||
if (auto *GEP = dyn_cast<GetElementPtrInst>(I))
|
||||
if (GEP->isInBounds())
|
||||
return true;
|
||||
|
||||
unsigned Opcode = I->getOpcode();
|
||||
unsigned Opcode = Op->getOpcode();
|
||||
|
||||
// Check whether opcode is a poison-generating operation
|
||||
// Check whether opcode is a poison/undef-generating operation
|
||||
switch (Opcode) {
|
||||
case Instruction::Shl:
|
||||
case Instruction::AShr:
|
||||
case Instruction::LShr: {
|
||||
// Shifts return poison if shiftwidth is larger than the bitwidth.
|
||||
if (auto *C = dyn_cast<Constant>(I->getOperand(1))) {
|
||||
if (auto *C = dyn_cast<Constant>(Op->getOperand(1))) {
|
||||
SmallVector<Constant *, 4> ShiftAmounts;
|
||||
if (auto *FVTy = dyn_cast<FixedVectorType>(C->getType())) {
|
||||
unsigned NumElts = FVTy->getNumElements();
|
||||
@ -4715,41 +4714,62 @@ bool llvm::canCreatePoison(const Instruction *I) {
|
||||
return true;
|
||||
case Instruction::Call:
|
||||
case Instruction::CallBr:
|
||||
case Instruction::Invoke:
|
||||
// Function calls can return a poison value even if args are non-poison
|
||||
// values.
|
||||
return true;
|
||||
case Instruction::Invoke: {
|
||||
const auto *CB = cast<CallBase>(Op);
|
||||
return !CB->hasRetAttr(Attribute::NoUndef);
|
||||
}
|
||||
case Instruction::InsertElement:
|
||||
case Instruction::ExtractElement: {
|
||||
// If index exceeds the length of the vector, it returns poison
|
||||
auto *VTy = cast<VectorType>(I->getOperand(0)->getType());
|
||||
unsigned IdxOp = I->getOpcode() == Instruction::InsertElement ? 2 : 1;
|
||||
auto *Idx = dyn_cast<ConstantInt>(I->getOperand(IdxOp));
|
||||
auto *VTy = cast<VectorType>(Op->getOperand(0)->getType());
|
||||
unsigned IdxOp = Op->getOpcode() == Instruction::InsertElement ? 2 : 1;
|
||||
auto *Idx = dyn_cast<ConstantInt>(Op->getOperand(IdxOp));
|
||||
if (!Idx || Idx->getZExtValue() >= VTy->getElementCount().Min)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
case Instruction::ShuffleVector: {
|
||||
// shufflevector may return undef.
|
||||
if (PoisonOnly)
|
||||
return false;
|
||||
ArrayRef<int> Mask = isa<ConstantExpr>(Op)
|
||||
? cast<ConstantExpr>(Op)->getShuffleMask()
|
||||
: cast<ShuffleVectorInst>(Op)->getShuffleMask();
|
||||
return any_of(Mask, [](int Elt) { return Elt == UndefMaskElem; });
|
||||
}
|
||||
case Instruction::FNeg:
|
||||
case Instruction::PHI:
|
||||
case Instruction::Select:
|
||||
case Instruction::URem:
|
||||
case Instruction::SRem:
|
||||
case Instruction::ShuffleVector:
|
||||
case Instruction::ExtractValue:
|
||||
case Instruction::InsertValue:
|
||||
case Instruction::Freeze:
|
||||
case Instruction::ICmp:
|
||||
case Instruction::FCmp:
|
||||
case Instruction::GetElementPtr:
|
||||
return false;
|
||||
default:
|
||||
if (isa<CastInst>(I))
|
||||
case Instruction::GetElementPtr: {
|
||||
const auto *GEP = cast<GEPOperator>(Op);
|
||||
return GEP->isInBounds();
|
||||
}
|
||||
default: {
|
||||
const auto *CE = dyn_cast<ConstantExpr>(Op);
|
||||
if (isa<CastInst>(Op) || (CE && CE->isCast()))
|
||||
return false;
|
||||
else if (isa<BinaryOperator>(I))
|
||||
else if (isa<BinaryOperator>(Op))
|
||||
return false;
|
||||
// Be conservative and return true.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool llvm::canCreateUndefOrPoison(const Operator *Op) {
|
||||
return ::canCreateUndefOrPoison(Op, /*PoisonOnly=*/false);
|
||||
}
|
||||
|
||||
bool llvm::canCreatePoison(const Operator *Op) {
|
||||
return ::canCreateUndefOrPoison(Op, /*PoisonOnly=*/true);
|
||||
}
|
||||
|
||||
bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V,
|
||||
|
@ -297,7 +297,7 @@ static bool rewrite(Function &F) {
|
||||
for (Value *V : I.operands())
|
||||
Checks.push_back(getPoisonFor(ValToPoison, V));
|
||||
|
||||
if (canCreatePoison(&I))
|
||||
if (canCreatePoison(cast<Operator>(&I)))
|
||||
generateCreationChecks(I, Checks);
|
||||
ValToPoison[&I] = buildOrChain(B, Checks);
|
||||
}
|
||||
|
@ -722,61 +722,71 @@ TEST(ValueTracking, propagatesPoison) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ValueTracking, canCreatePoison) {
|
||||
TEST(ValueTracking, canCreatePoisonOrUndef) {
|
||||
std::string AsmHead =
|
||||
"declare i32 @g(i32)\n"
|
||||
"define void @f(i32 %x, i32 %y, float %fx, float %fy, i1 %cond, "
|
||||
"<4 x i32> %vx, <4 x i32> %vx2, <vscale x 4 x i32> %svx, i8* %p) {\n";
|
||||
std::string AsmTail = " ret void\n}";
|
||||
// (can create poison?, IR instruction)
|
||||
SmallVector<std::pair<bool, std::string>, 32> Data = {
|
||||
{false, "add i32 %x, %y"},
|
||||
{true, "add nsw nuw i32 %x, %y"},
|
||||
{true, "shl i32 %x, %y"},
|
||||
{true, "shl <4 x i32> %vx, %vx2"},
|
||||
{true, "shl nsw i32 %x, %y"},
|
||||
{true, "shl nsw <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},
|
||||
{false, "shl i32 %x, 31"},
|
||||
{true, "shl i32 %x, 32"},
|
||||
{false, "shl <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},
|
||||
{true, "shl <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 32>"},
|
||||
{true, "ashr i32 %x, %y"},
|
||||
{true, "ashr exact i32 %x, %y"},
|
||||
{false, "ashr i32 %x, 31"},
|
||||
{true, "ashr exact i32 %x, 31"},
|
||||
{false, "ashr <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},
|
||||
{true, "ashr <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 32>"},
|
||||
{true, "ashr exact <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},
|
||||
{true, "lshr i32 %x, %y"},
|
||||
{true, "lshr exact i32 %x, 31"},
|
||||
{false, "udiv i32 %x, %y"},
|
||||
{true, "udiv exact i32 %x, %y"},
|
||||
{false, "getelementptr i8, i8* %p, i32 %x"},
|
||||
{true, "getelementptr inbounds i8, i8* %p, i32 %x"},
|
||||
{true, "fneg nnan float %fx"},
|
||||
{false, "fneg float %fx"},
|
||||
{false, "fadd float %fx, %fy"},
|
||||
{true, "fadd nnan float %fx, %fy"},
|
||||
{false, "urem i32 %x, %y"},
|
||||
{true, "fptoui float %fx to i32"},
|
||||
{true, "fptosi float %fx to i32"},
|
||||
{false, "bitcast float %fx to i32"},
|
||||
{false, "select i1 %cond, i32 %x, i32 %y"},
|
||||
{true, "select nnan i1 %cond, float %fx, float %fy"},
|
||||
{true, "extractelement <4 x i32> %vx, i32 %x"},
|
||||
{false, "extractelement <4 x i32> %vx, i32 3"},
|
||||
{true, "extractelement <vscale x 4 x i32> %svx, i32 4"},
|
||||
{true, "insertelement <4 x i32> %vx, i32 %x, i32 %y"},
|
||||
{false, "insertelement <4 x i32> %vx, i32 %x, i32 3"},
|
||||
{true, "insertelement <vscale x 4 x i32> %svx, i32 %x, i32 4"},
|
||||
{false, "freeze i32 %x"},
|
||||
{true, "call i32 @g(i32 %x)"},
|
||||
{true, "fcmp nnan oeq float %fx, %fy"},
|
||||
{false, "fcmp oeq float %fx, %fy"}};
|
||||
// (can create poison?, can create undef?, IR instruction)
|
||||
SmallVector<std::tuple<bool, bool, std::string>, 32> Data = {
|
||||
{false, false, "add i32 %x, %y"},
|
||||
{true, false, "add nsw nuw i32 %x, %y"},
|
||||
{true, false, "shl i32 %x, %y"},
|
||||
{true, false, "shl <4 x i32> %vx, %vx2"},
|
||||
{true, false, "shl nsw i32 %x, %y"},
|
||||
{true, false, "shl nsw <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},
|
||||
{false, false, "shl i32 %x, 31"},
|
||||
{true, false, "shl i32 %x, 32"},
|
||||
{false, false, "shl <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},
|
||||
{true, false, "shl <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 32>"},
|
||||
{true, false, "ashr i32 %x, %y"},
|
||||
{true, false, "ashr exact i32 %x, %y"},
|
||||
{false, false, "ashr i32 %x, 31"},
|
||||
{true, false, "ashr exact i32 %x, 31"},
|
||||
{false, false, "ashr <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},
|
||||
{true, false, "ashr <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 32>"},
|
||||
{true, false, "ashr exact <4 x i32> %vx, <i32 0, i32 1, i32 2, i32 3>"},
|
||||
{true, false, "lshr i32 %x, %y"},
|
||||
{true, false, "lshr exact i32 %x, 31"},
|
||||
{false, false, "udiv i32 %x, %y"},
|
||||
{true, false, "udiv exact i32 %x, %y"},
|
||||
{false, false, "getelementptr i8, i8* %p, i32 %x"},
|
||||
{true, false, "getelementptr inbounds i8, i8* %p, i32 %x"},
|
||||
{true, false, "fneg nnan float %fx"},
|
||||
{false, false, "fneg float %fx"},
|
||||
{false, false, "fadd float %fx, %fy"},
|
||||
{true, false, "fadd nnan float %fx, %fy"},
|
||||
{false, false, "urem i32 %x, %y"},
|
||||
{true, false, "fptoui float %fx to i32"},
|
||||
{true, false, "fptosi float %fx to i32"},
|
||||
{false, false, "bitcast float %fx to i32"},
|
||||
{false, false, "select i1 %cond, i32 %x, i32 %y"},
|
||||
{true, false, "select nnan i1 %cond, float %fx, float %fy"},
|
||||
{true, false, "extractelement <4 x i32> %vx, i32 %x"},
|
||||
{false, false, "extractelement <4 x i32> %vx, i32 3"},
|
||||
{true, false, "extractelement <vscale x 4 x i32> %svx, i32 4"},
|
||||
{true, false, "insertelement <4 x i32> %vx, i32 %x, i32 %y"},
|
||||
{false, false, "insertelement <4 x i32> %vx, i32 %x, i32 3"},
|
||||
{true, false, "insertelement <vscale x 4 x i32> %svx, i32 %x, i32 4"},
|
||||
{false, false, "freeze i32 %x"},
|
||||
{false, false,
|
||||
"shufflevector <4 x i32> %vx, <4 x i32> %vx2, "
|
||||
"<4 x i32> <i32 0, i32 1, i32 2, i32 3>"},
|
||||
{false, true,
|
||||
"shufflevector <4 x i32> %vx, <4 x i32> %vx2, "
|
||||
"<4 x i32> <i32 0, i32 1, i32 2, i32 undef>"},
|
||||
{false, true,
|
||||
"shufflevector <vscale x 4 x i32> %svx, "
|
||||
"<vscale x 4 x i32> %svx, <vscale x 4 x i32> undef"},
|
||||
{true, false, "call i32 @g(i32 %x)"},
|
||||
{false, false, "call noundef i32 @g(i32 %x)"},
|
||||
{true, false, "fcmp nnan oeq float %fx, %fy"},
|
||||
{false, false, "fcmp oeq float %fx, %fy"}};
|
||||
|
||||
std::string AssemblyStr = AsmHead;
|
||||
for (auto &Itm : Data)
|
||||
AssemblyStr += Itm.second + "\n";
|
||||
AssemblyStr += std::get<2>(Itm) + "\n";
|
||||
AssemblyStr += AsmTail;
|
||||
|
||||
LLVMContext Context;
|
||||
@ -793,8 +803,14 @@ TEST(ValueTracking, canCreatePoison) {
|
||||
for (auto &I : BB) {
|
||||
if (isa<ReturnInst>(&I))
|
||||
break;
|
||||
EXPECT_EQ(canCreatePoison(&I), Data[Index].first)
|
||||
<< "Incorrect answer at instruction " << Index << " = " << I;
|
||||
bool Poison = std::get<0>(Data[Index]);
|
||||
bool Undef = std::get<1>(Data[Index]);
|
||||
EXPECT_EQ(canCreatePoison(cast<Operator>(&I)), Poison)
|
||||
<< "Incorrect answer of canCreatePoison at instruction " << Index
|
||||
<< " = " << I;
|
||||
EXPECT_EQ(canCreateUndefOrPoison(cast<Operator>(&I)), Undef || Poison)
|
||||
<< "Incorrect answer of canCreateUndef at instruction " << Index
|
||||
<< " = " << I;
|
||||
Index++;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user