summaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp')
-rw-r--r--llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp73
1 files changed, 36 insertions, 37 deletions
diff --git a/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp b/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp
index 71ecfd9a26429..85e096112fca1 100644
--- a/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp
+++ b/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp
@@ -12,26 +12,24 @@
// LangRef. There are obvious parallels to the sanitizer tools, but this pass
// is focused purely on the semantics of LLVM IR, not any particular source
// language. If you're looking for something to see if your C/C++ contains
-// UB, this is not it.
-//
+// UB, this is not it.
+//
// The rewritten semantics of each instruction will include the following
-// components:
+// components:
//
// 1) The original instruction, unmodified.
// 2) A propagation rule which translates dynamic information about the poison
// state of each input to whether the dynamic output of the instruction
// produces poison.
-// 3) A flag validation rule which validates any poison producing flags on the
+// 3) A creation rule which validates any poison producing flags on the
// instruction itself (e.g. checks for overflow on nsw).
// 4) A check rule which traps (to a handler function) if this instruction must
// execute undefined behavior given the poison state of it's inputs.
//
-// At the moment, the UB detection is done in a best effort manner; that is,
-// the resulting code may produce a false negative result (not report UB when
-// it actually exists according to the LangRef spec), but should never produce
-// a false positive (report UB where it doesn't exist). The intention is to
-// eventually support a "strict" mode which never dynamically reports a false
-// negative at the cost of rejecting some valid inputs to translation.
+// This is a must analysis based transform; that is, the resulting code may
+// produce a false negative result (not report UB when actually exists
+// according to the LangRef spec), but should never produce a false positive
+// (report UB where it doesn't exist).
//
// Use cases for this pass include:
// - Understanding (and testing!) the implications of the definition of poison
@@ -40,7 +38,7 @@
// are well defined on the specific input used.
// - Finding/confirming poison specific miscompiles by checking the poison
// status of an input/IR pair is the same before and after an optimization
-// transform.
+// transform.
// - Checking that a bugpoint reduction does not introduce UB which didn't
// exist in the original program being reduced.
//
@@ -56,7 +54,7 @@
// moment, all arguments and return values are assumed not to be poison.
// - Undef is not modeled. In particular, the optimizer's freedom to pick
// concrete values for undef bits so as to maximize potential for producing
-// poison is not modeled.
+// poison is not modeled.
//
//===----------------------------------------------------------------------===//
@@ -103,10 +101,10 @@ static Value *buildOrChain(IRBuilder<> &B, ArrayRef<Value*> Ops) {
return Accum;
}
-static void generatePoisonChecksForBinOp(Instruction &I,
- SmallVector<Value*, 2> &Checks) {
+static void generateCreationChecksForBinOp(Instruction &I,
+ SmallVectorImpl<Value*> &Checks) {
assert(isa<BinaryOperator>(I));
-
+
IRBuilder<> B(&I);
Value *LHS = I.getOperand(0);
Value *RHS = I.getOperand(1);
@@ -183,22 +181,28 @@ static void generatePoisonChecksForBinOp(Instruction &I,
};
}
-static Value* generatePoisonChecks(Instruction &I) {
+/// Given an instruction which can produce poison on non-poison inputs
+/// (i.e. canCreatePoison returns true), generate runtime checks to produce
+/// boolean indicators of when poison would result.
+static void generateCreationChecks(Instruction &I,
+ SmallVectorImpl<Value*> &Checks) {
IRBuilder<> B(&I);
- SmallVector<Value*, 2> Checks;
if (isa<BinaryOperator>(I) && !I.getType()->isVectorTy())
- generatePoisonChecksForBinOp(I, Checks);
+ generateCreationChecksForBinOp(I, Checks);
- // Handle non-binops seperately
+ // Handle non-binops separately
switch (I.getOpcode()) {
default:
+ // Note there are a couple of missing cases here, once implemented, this
+ // should become an llvm_unreachable.
break;
case Instruction::ExtractElement: {
Value *Vec = I.getOperand(0);
- if (Vec->getType()->getVectorIsScalable())
+ auto *VecVTy = dyn_cast<FixedVectorType>(Vec->getType());
+ if (!VecVTy)
break;
Value *Idx = I.getOperand(1);
- unsigned NumElts = Vec->getType()->getVectorNumElements();
+ unsigned NumElts = VecVTy->getNumElements();
Value *Check =
B.CreateICmp(ICmpInst::ICMP_UGE, Idx,
ConstantInt::get(Idx->getType(), NumElts));
@@ -207,10 +211,11 @@ static Value* generatePoisonChecks(Instruction &I) {
}
case Instruction::InsertElement: {
Value *Vec = I.getOperand(0);
- if (Vec->getType()->getVectorIsScalable())
+ auto *VecVTy = dyn_cast<FixedVectorType>(Vec->getType());
+ if (!VecVTy)
break;
Value *Idx = I.getOperand(2);
- unsigned NumElts = Vec->getType()->getVectorNumElements();
+ unsigned NumElts = VecVTy->getNumElements();
Value *Check =
B.CreateICmp(ICmpInst::ICMP_UGE, Idx,
ConstantInt::get(Idx->getType(), NumElts));
@@ -218,7 +223,6 @@ static Value* generatePoisonChecks(Instruction &I) {
break;
}
};
- return buildOrChain(B, Checks);
}
static Value *getPoisonFor(DenseMap<Value *, Value *> &ValToPoison, Value *V) {
@@ -262,24 +266,23 @@ static bool rewrite(Function &F) {
for (BasicBlock &BB : F)
for (auto I = BB.begin(); isa<PHINode>(&*I); I++) {
auto *OldPHI = cast<PHINode>(&*I);
- auto *NewPHI = PHINode::Create(Int1Ty,
- OldPHI->getNumIncomingValues());
+ auto *NewPHI = PHINode::Create(Int1Ty, OldPHI->getNumIncomingValues());
for (unsigned i = 0; i < OldPHI->getNumIncomingValues(); i++)
NewPHI->addIncoming(UndefValue::get(Int1Ty),
OldPHI->getIncomingBlock(i));
NewPHI->insertBefore(OldPHI);
ValToPoison[OldPHI] = NewPHI;
}
-
+
for (BasicBlock &BB : F)
for (Instruction &I : BB) {
if (isa<PHINode>(I)) continue;
IRBuilder<> B(cast<Instruction>(&I));
-
+
// Note: There are many more sources of documented UB, but this pass only
// attempts to find UB triggered by propagation of poison.
- if (Value *Op = const_cast<Value*>(getGuaranteedNonFullPoisonOp(&I)))
+ if (Value *Op = const_cast<Value*>(getGuaranteedNonPoisonOp(&I)))
CreateAssertNot(B, getPoisonFor(ValToPoison, Op));
if (LocalCheck)
@@ -290,12 +293,12 @@ static bool rewrite(Function &F) {
}
SmallVector<Value*, 4> Checks;
- if (propagatesFullPoison(&I))
+ if (propagatesPoison(&I))
for (Value *V : I.operands())
Checks.push_back(getPoisonFor(ValToPoison, V));
- if (auto *Check = generatePoisonChecks(I))
- Checks.push_back(Check);
+ if (canCreatePoison(&I))
+ generateCreationChecks(I, Checks);
ValToPoison[&I] = buildOrChain(B, Checks);
}
@@ -328,7 +331,6 @@ PreservedAnalyses PoisonCheckingPass::run(Function &F,
return rewrite(F) ? PreservedAnalyses::none() : PreservedAnalyses::all();
}
-
/* Major TODO Items:
- Control dependent poison UB
- Strict mode - (i.e. must analyze every operand)
@@ -338,10 +340,7 @@ PreservedAnalyses PoisonCheckingPass::run(Function &F,
Instructions w/Unclear Semantics:
- shufflevector - It would seem reasonable for an out of bounds mask element
- to produce poison, but the LangRef does not state.
- - and/or - It would seem reasonable for poison to propagate from both
- arguments, but LangRef doesn't state and propagatesFullPoison doesn't
- include these two.
+ to produce poison, but the LangRef does not state.
- all binary ops w/vector operands - The likely interpretation would be that
any element overflowing should produce poison for the entire result, but
the LangRef does not state.