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,14 +592,19 @@ /// the parent of I. bool programUndefinedIfPoison(const Instruction *PoisonI); - /// Return true if I can create poison from non-poison operands. + /// Return true if Op 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 /// 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), + /// For example, given `Op = shl <2 x i32> %x, <0, 32>`, this function returns + /// true. If Op raises immediate UB but never creates poison (e.g. sdiv I, 0), /// canCreatePoison returns false. - bool canCreatePoison(const Instruction *I); + bool canCreatePoison(const Operator *Op); + + /// Return true if I can create undef from non-undef & non-poison operands. + /// If ConsiderPoisonAsUndef is true, return true as well if Op can create + /// poison from non-undef & non-poison operands. + bool canCreateUndef(const Operator *Op, bool ConsiderPoisonAsUndef); /// Return true if this function can prove that V is never undef value /// or poison value. 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,23 +4665,25 @@ return llvm::any_of(GuardingBranches, AllUsesGuardedByBranch); } -bool llvm::canCreatePoison(const Instruction *I) { +bool llvm::canCreatePoison(const Operator *Op) { // 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 (auto *FP = dyn_cast(Op)) { auto FMF = FP->getFastMathFlags(); if (FMF.noNaNs() || FMF.noInfs()) return true; } - if (auto *GEP = dyn_cast(I)) + if (auto *GEP = dyn_cast(Op)) if (GEP->isInBounds()) return true; - unsigned Opcode = I->getOpcode(); + unsigned Opcode = Op->getOpcode(); // Check whether opcode is a poison-generating operation switch (Opcode) { @@ -4689,7 +4691,7 @@ 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(); @@ -4722,9 +4724,9 @@ 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; @@ -4742,16 +4744,46 @@ case Instruction::FCmp: case Instruction::GetElementPtr: return false; - default: - if (isa(I)) + 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::canCreateUndef(const Operator *Op, bool ConsiderPoisonAsUndef) { + // If ConsiderPoisonAsUndef is set and canCreatePoison(op) holds, return true. + // canCreatePoison(op) returns true if op returns poison when its operands are + // non-poison, meaning that they can be undef. + // canCreatePoison can be still used here, and it relies on the observation + // that for any operation op and value v1 v2, if `op v1 v2` returned poison + // and either v1 or v2 is undef, there exists non-undef value v1' and v2' s.t. + // `op v1' v2'` also returns poison. + // For example, `shl i32 1, undef` returns poison, as well as `shl i32 1, 33`. + // + // If a new operation is added or existing one's semantics is updated so it + // does not return poison on any non-undef operands but returns poison on + // undef operands, this should be updated. + if (ConsiderPoisonAsUndef && canCreatePoison(Op)) + return true; + + if (Op->getOpcode() == Instruction::ShuffleVector) { + // shufflevector may return undef. + ArrayRef Mask = isa(Op) ? + cast(Op)->getShuffleMask() : + cast(Op)->getShuffleMask(); + return any_of(Mask, [](int Elt) { return Elt == UndefMaskElem; }); + } + + return false; } + bool llvm::isGuaranteedNotToBeUndefOrPoison(const Value *V, const Instruction *CtxI, const DominatorTree *DT, 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,67 @@ } } -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)"}, + {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 +799,17 @@ 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(canCreateUndef(cast(&I), false), Undef) + << "Incorrect answer of canCreateUndef at instruction " << Index + << " = " << I; + EXPECT_EQ(canCreateUndef(cast(&I), true), Undef || Poison) + << "Incorrect answer of canCreateUndef at instruction " << Index + << " = " << I; Index++; } }