diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h --- a/llvm/include/llvm/Analysis/ValueTracking.h +++ b/llvm/include/llvm/Analysis/ValueTracking.h @@ -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 #include @@ -591,18 +592,25 @@ /// 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. diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -4665,31 +4665,30 @@ 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(I) && - (I->hasNoSignedWrap() || I->hasNoUnsignedWrap())) - return true; - if (isa(I) && I->isExact()) - return true; - if (auto *FP = dyn_cast(I)) { + if (const auto *OvOp = dyn_cast(Op)) { + if (OvOp->hasNoSignedWrap() || OvOp->hasNoUnsignedWrap()) + return true; + } + if (const auto *ExactOp = dyn_cast(Op)) + if (ExactOp->isExact()) + return true; + if (const auto *FP = dyn_cast(Op)) { auto FMF = FP->getFastMathFlags(); if (FMF.noNaNs() || FMF.noInfs()) return true; } - if (auto *GEP = dyn_cast(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(I->getOperand(1))) { + if (auto *C = dyn_cast(Op->getOperand(1))) { SmallVector ShiftAmounts; if (auto *FVTy = dyn_cast(C->getType())) { unsigned NumElts = FVTy->getNumElements(); @@ -4715,41 +4714,62 @@ 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(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(I->getOperand(0)->getType()); - unsigned IdxOp = I->getOpcode() == Instruction::InsertElement ? 2 : 1; - auto *Idx = dyn_cast(I->getOperand(IdxOp)); + auto *VTy = cast(Op->getOperand(0)->getType()); + unsigned IdxOp = Op->getOpcode() == Instruction::InsertElement ? 2 : 1; + auto *Idx = dyn_cast(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 Mask = isa(Op) + ? cast(Op)->getShuffleMask() + : cast(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(I)) + case Instruction::GetElementPtr: { + const auto *GEP = cast(Op); + return GEP->isInBounds(); + } + default: { + const auto *CE = dyn_cast(Op); + if (isa(Op) || (CE && CE->isCast())) return false; - else if (isa(I)) + else if (isa(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, diff --git a/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp b/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp --- a/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp +++ b/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp @@ -297,7 +297,7 @@ for (Value *V : I.operands()) Checks.push_back(getPoisonFor(ValToPoison, V)); - if (canCreatePoison(&I)) + if (canCreatePoison(cast(&I))) generateCreationChecks(I, Checks); ValToPoison[&I] = buildOrChain(B, Checks); } diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp --- a/llvm/unittests/Analysis/ValueTrackingTest.cpp +++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp @@ -722,61 +722,71 @@ } } -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, %svx, i8* %p) {\n"; std::string AsmTail = " ret void\n}"; - // (can create poison?, IR instruction) - SmallVector, 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, "}, - {false, "shl i32 %x, 31"}, - {true, "shl i32 %x, 32"}, - {false, "shl <4 x i32> %vx, "}, - {true, "shl <4 x i32> %vx, "}, - {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, "}, - {true, "ashr <4 x i32> %vx, "}, - {true, "ashr exact <4 x i32> %vx, "}, - {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 %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 %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, 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, "}, + {false, false, "shl i32 %x, 31"}, + {true, false, "shl i32 %x, 32"}, + {false, false, "shl <4 x i32> %vx, "}, + {true, false, "shl <4 x i32> %vx, "}, + {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, "}, + {true, false, "ashr <4 x i32> %vx, "}, + {true, false, "ashr exact <4 x i32> %vx, "}, + {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 %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 %svx, i32 %x, i32 4"}, + {false, false, "freeze i32 %x"}, + {false, false, + "shufflevector <4 x i32> %vx, <4 x i32> %vx2, " + "<4 x i32> "}, + {false, true, + "shufflevector <4 x i32> %vx, <4 x i32> %vx2, " + "<4 x i32> "}, + {false, true, + "shufflevector %svx, " + " %svx, 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 @@ for (auto &I : BB) { if (isa(&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(&I)), Poison) + << "Incorrect answer of canCreatePoison at instruction " << Index + << " = " << I; + EXPECT_EQ(canCreateUndefOrPoison(cast(&I)), Undef || Poison) + << "Incorrect answer of canCreateUndef at instruction " << Index + << " = " << I; Index++; } }