summaryrefslogtreecommitdiffstats
path: root/lib/Analysis/UninitializedValues.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Analysis/UninitializedValues.cpp')
-rw-r--r--lib/Analysis/UninitializedValues.cpp793
1 files changed, 416 insertions, 377 deletions
diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp
index 1c7e6b6..858be45 100644
--- a/lib/Analysis/UninitializedValues.cpp
+++ b/lib/Analysis/UninitializedValues.cpp
@@ -16,6 +16,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/PackedVector.h"
#include "llvm/ADT/DenseMap.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/AnalysisContext.h"
@@ -25,6 +26,8 @@
using namespace clang;
+#define DEBUG_LOGGING 0
+
static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) {
if (vd->isLocalVarDecl() && !vd->hasGlobalStorage() &&
!vd->isExceptionVariable() &&
@@ -95,143 +98,79 @@ static bool isAlwaysUninit(const Value v) {
namespace {
typedef llvm::PackedVector<Value, 2> ValueVector;
-typedef std::pair<ValueVector *, ValueVector *> BVPair;
class CFGBlockValues {
const CFG &cfg;
- BVPair *vals;
+ std::vector<ValueVector*> vals;
ValueVector scratch;
DeclToIndex declToIndex;
-
- ValueVector &lazyCreate(ValueVector *&bv);
public:
CFGBlockValues(const CFG &cfg);
~CFGBlockValues();
-
+
unsigned getNumEntries() const { return declToIndex.size(); }
void computeSetOfDeclarations(const DeclContext &dc);
- ValueVector &getValueVector(const CFGBlock *block,
- const CFGBlock *dstBlock);
-
- BVPair &getValueVectors(const CFGBlock *block, bool shouldLazyCreate);
+ ValueVector &getValueVector(const CFGBlock *block) {
+ return *vals[block->getBlockID()];
+ }
+ void setAllScratchValues(Value V);
void mergeIntoScratch(ValueVector const &source, bool isFirst);
bool updateValueVectorWithScratch(const CFGBlock *block);
- bool updateValueVectors(const CFGBlock *block, const BVPair &newVals);
bool hasNoDeclarations() const {
return declToIndex.size() == 0;
}
void resetScratch();
- ValueVector &getScratch() { return scratch; }
ValueVector::reference operator[](const VarDecl *vd);
+
+ Value getValue(const CFGBlock *block, const CFGBlock *dstBlock,
+ const VarDecl *vd) {
+ const llvm::Optional<unsigned> &idx = declToIndex.getValueIndex(vd);
+ assert(idx.hasValue());
+ return getValueVector(block)[idx.getValue()];
+ }
};
} // end anonymous namespace
-CFGBlockValues::CFGBlockValues(const CFG &c) : cfg(c), vals(0) {
- unsigned n = cfg.getNumBlockIDs();
- if (!n)
- return;
- vals = new std::pair<ValueVector*, ValueVector*>[n];
- memset((void*)vals, 0, sizeof(*vals) * n);
-}
+CFGBlockValues::CFGBlockValues(const CFG &c) : cfg(c), vals(0) {}
CFGBlockValues::~CFGBlockValues() {
- unsigned n = cfg.getNumBlockIDs();
- if (n == 0)
- return;
- for (unsigned i = 0; i < n; ++i) {
- delete vals[i].first;
- delete vals[i].second;
- }
- delete [] vals;
+ for (std::vector<ValueVector*>::iterator I = vals.begin(), E = vals.end();
+ I != E; ++I)
+ delete *I;
}
void CFGBlockValues::computeSetOfDeclarations(const DeclContext &dc) {
declToIndex.computeMap(dc);
- scratch.resize(declToIndex.size());
-}
-
-ValueVector &CFGBlockValues::lazyCreate(ValueVector *&bv) {
- if (!bv)
- bv = new ValueVector(declToIndex.size());
- return *bv;
-}
-
-/// This function pattern matches for a '&&' or '||' that appears at
-/// the beginning of a CFGBlock that also (1) has a terminator and
-/// (2) has no other elements. If such an expression is found, it is returned.
-static const BinaryOperator *getLogicalOperatorInChain(const CFGBlock *block) {
- if (block->empty())
- return 0;
-
- CFGElement front = block->front();
- const CFGStmt *cstmt = front.getAs<CFGStmt>();
- if (!cstmt)
- return 0;
-
- const BinaryOperator *b = dyn_cast_or_null<BinaryOperator>(cstmt->getStmt());
-
- if (!b || !b->isLogicalOp())
- return 0;
-
- if (block->pred_size() == 2) {
- if (block->getTerminatorCondition() == b) {
- if (block->succ_size() == 2)
- return b;
- }
- else if (block->size() == 1)
- return b;
- }
-
- return 0;
-}
-
-ValueVector &CFGBlockValues::getValueVector(const CFGBlock *block,
- const CFGBlock *dstBlock) {
- unsigned idx = block->getBlockID();
- if (dstBlock && getLogicalOperatorInChain(block)) {
- if (*block->succ_begin() == dstBlock)
- return lazyCreate(vals[idx].first);
- assert(*(block->succ_begin()+1) == dstBlock);
- return lazyCreate(vals[idx].second);
- }
-
- assert(vals[idx].second == 0);
- return lazyCreate(vals[idx].first);
-}
-
-BVPair &CFGBlockValues::getValueVectors(const clang::CFGBlock *block,
- bool shouldLazyCreate) {
- unsigned idx = block->getBlockID();
- lazyCreate(vals[idx].first);
- if (shouldLazyCreate)
- lazyCreate(vals[idx].second);
- return vals[idx];
+ unsigned decls = declToIndex.size();
+ scratch.resize(decls);
+ unsigned n = cfg.getNumBlockIDs();
+ if (!n)
+ return;
+ vals.resize(n);
+ for (unsigned i = 0; i < n; ++i)
+ vals[i] = new ValueVector(decls);
}
-#if 0
+#if DEBUG_LOGGING
static void printVector(const CFGBlock *block, ValueVector &bv,
unsigned num) {
-
llvm::errs() << block->getBlockID() << " :";
for (unsigned i = 0; i < bv.size(); ++i) {
llvm::errs() << ' ' << bv[i];
}
llvm::errs() << " : " << num << '\n';
}
+#endif
-static void printVector(const char *name, ValueVector const &bv) {
- llvm::errs() << name << " : ";
- for (unsigned i = 0; i < bv.size(); ++i) {
- llvm::errs() << ' ' << bv[i];
- }
- llvm::errs() << "\n";
+void CFGBlockValues::setAllScratchValues(Value V) {
+ for (unsigned I = 0, E = scratch.size(); I != E; ++I)
+ scratch[I] = V;
}
-#endif
void CFGBlockValues::mergeIntoScratch(ValueVector const &source,
bool isFirst) {
@@ -242,30 +181,16 @@ void CFGBlockValues::mergeIntoScratch(ValueVector const &source,
}
bool CFGBlockValues::updateValueVectorWithScratch(const CFGBlock *block) {
- ValueVector &dst = getValueVector(block, 0);
+ ValueVector &dst = getValueVector(block);
bool changed = (dst != scratch);
if (changed)
dst = scratch;
-#if 0
+#if DEBUG_LOGGING
printVector(block, scratch, 0);
#endif
return changed;
}
-bool CFGBlockValues::updateValueVectors(const CFGBlock *block,
- const BVPair &newVals) {
- BVPair &vals = getValueVectors(block, true);
- bool changed = *newVals.first != *vals.first ||
- *newVals.second != *vals.second;
- *vals.first = *newVals.first;
- *vals.second = *newVals.second;
-#if 0
- printVector(block, *vals.first, 1);
- printVector(block, *vals.second, 2);
-#endif
- return changed;
-}
-
void CFGBlockValues::resetScratch() {
scratch.reset();
}
@@ -321,7 +246,7 @@ const CFGBlock *DataflowWorklist::dequeue() {
}
//------------------------------------------------------------------------====//
-// Transfer function for uninitialized values analysis.
+// Classification of DeclRefExprs as use or initialization.
//====------------------------------------------------------------------------//
namespace {
@@ -329,106 +254,339 @@ class FindVarResult {
const VarDecl *vd;
const DeclRefExpr *dr;
public:
- FindVarResult(VarDecl *vd, DeclRefExpr *dr) : vd(vd), dr(dr) {}
-
+ FindVarResult(const VarDecl *vd, const DeclRefExpr *dr) : vd(vd), dr(dr) {}
+
const DeclRefExpr *getDeclRefExpr() const { return dr; }
const VarDecl *getDecl() const { return vd; }
};
-
+
+static const Expr *stripCasts(ASTContext &C, const Expr *Ex) {
+ while (Ex) {
+ Ex = Ex->IgnoreParenNoopCasts(C);
+ if (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) {
+ if (CE->getCastKind() == CK_LValueBitCast) {
+ Ex = CE->getSubExpr();
+ continue;
+ }
+ }
+ break;
+ }
+ return Ex;
+}
+
+/// If E is an expression comprising a reference to a single variable, find that
+/// variable.
+static FindVarResult findVar(const Expr *E, const DeclContext *DC) {
+ if (const DeclRefExpr *DRE =
+ dyn_cast<DeclRefExpr>(stripCasts(DC->getParentASTContext(), E)))
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()))
+ if (isTrackedVar(VD, DC))
+ return FindVarResult(VD, DRE);
+ return FindVarResult(0, 0);
+}
+
+/// \brief Classify each DeclRefExpr as an initialization or a use. Any
+/// DeclRefExpr which isn't explicitly classified will be assumed to have
+/// escaped the analysis and will be treated as an initialization.
+class ClassifyRefs : public StmtVisitor<ClassifyRefs> {
+public:
+ enum Class {
+ Init,
+ Use,
+ SelfInit,
+ Ignore
+ };
+
+private:
+ const DeclContext *DC;
+ llvm::DenseMap<const DeclRefExpr*, Class> Classification;
+
+ bool isTrackedVar(const VarDecl *VD) const {
+ return ::isTrackedVar(VD, DC);
+ }
+
+ void classify(const Expr *E, Class C);
+
+public:
+ ClassifyRefs(AnalysisDeclContext &AC) : DC(cast<DeclContext>(AC.getDecl())) {}
+
+ void VisitDeclStmt(DeclStmt *DS);
+ void VisitUnaryOperator(UnaryOperator *UO);
+ void VisitBinaryOperator(BinaryOperator *BO);
+ void VisitCallExpr(CallExpr *CE);
+ void VisitCastExpr(CastExpr *CE);
+
+ void operator()(Stmt *S) { Visit(S); }
+
+ Class get(const DeclRefExpr *DRE) const {
+ llvm::DenseMap<const DeclRefExpr*, Class>::const_iterator I
+ = Classification.find(DRE);
+ if (I != Classification.end())
+ return I->second;
+
+ const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl());
+ if (!VD || !isTrackedVar(VD))
+ return Ignore;
+
+ return Init;
+ }
+};
+}
+
+static const DeclRefExpr *getSelfInitExpr(VarDecl *VD) {
+ if (Expr *Init = VD->getInit()) {
+ const DeclRefExpr *DRE
+ = dyn_cast<DeclRefExpr>(stripCasts(VD->getASTContext(), Init));
+ if (DRE && DRE->getDecl() == VD)
+ return DRE;
+ }
+ return 0;
+}
+
+void ClassifyRefs::classify(const Expr *E, Class C) {
+ FindVarResult Var = findVar(E, DC);
+ if (const DeclRefExpr *DRE = Var.getDeclRefExpr())
+ Classification[DRE] = std::max(Classification[DRE], C);
+}
+
+void ClassifyRefs::VisitDeclStmt(DeclStmt *DS) {
+ for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end();
+ DI != DE; ++DI) {
+ VarDecl *VD = dyn_cast<VarDecl>(*DI);
+ if (VD && isTrackedVar(VD))
+ if (const DeclRefExpr *DRE = getSelfInitExpr(VD))
+ Classification[DRE] = SelfInit;
+ }
+}
+
+void ClassifyRefs::VisitBinaryOperator(BinaryOperator *BO) {
+ // Ignore the evaluation of a DeclRefExpr on the LHS of an assignment. If this
+ // is not a compound-assignment, we will treat it as initializing the variable
+ // when TransferFunctions visits it. A compound-assignment does not affect
+ // whether a variable is uninitialized, and there's no point counting it as a
+ // use.
+ if (BO->isCompoundAssignmentOp())
+ classify(BO->getLHS(), Use);
+ else if (BO->getOpcode() == BO_Assign)
+ classify(BO->getLHS(), Ignore);
+}
+
+void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) {
+ // Increment and decrement are uses despite there being no lvalue-to-rvalue
+ // conversion.
+ if (UO->isIncrementDecrementOp())
+ classify(UO->getSubExpr(), Use);
+}
+
+void ClassifyRefs::VisitCallExpr(CallExpr *CE) {
+ // If a value is passed by const reference to a function, we should not assume
+ // that it is initialized by the call, and we conservatively do not assume
+ // that it is used.
+ for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end();
+ I != E; ++I)
+ if ((*I)->getType().isConstQualified() && (*I)->isGLValue())
+ classify(*I, Ignore);
+}
+
+void ClassifyRefs::VisitCastExpr(CastExpr *CE) {
+ if (CE->getCastKind() == CK_LValueToRValue)
+ classify(CE->getSubExpr(), Use);
+ else if (CStyleCastExpr *CSE = dyn_cast<CStyleCastExpr>(CE)) {
+ if (CSE->getType()->isVoidType()) {
+ // Squelch any detected load of an uninitialized value if
+ // we cast it to void.
+ // e.g. (void) x;
+ classify(CSE->getSubExpr(), Ignore);
+ }
+ }
+}
+
+//------------------------------------------------------------------------====//
+// Transfer function for uninitialized values analysis.
+//====------------------------------------------------------------------------//
+
+namespace {
class TransferFunctions : public StmtVisitor<TransferFunctions> {
CFGBlockValues &vals;
const CFG &cfg;
+ const CFGBlock *block;
AnalysisDeclContext &ac;
+ const ClassifyRefs &classification;
UninitVariablesHandler *handler;
-
- /// The last DeclRefExpr seen when analyzing a block. Used to
- /// cheat when detecting cases when the address of a variable is taken.
- DeclRefExpr *lastDR;
-
- /// The last lvalue-to-rvalue conversion of a variable whose value
- /// was uninitialized. Normally this results in a warning, but it is
- /// possible to either silence the warning in some cases, or we
- /// propagate the uninitialized value.
- CastExpr *lastLoad;
-
- /// For some expressions, we want to ignore any post-processing after
- /// visitation.
- bool skipProcessUses;
-
+
public:
TransferFunctions(CFGBlockValues &vals, const CFG &cfg,
- AnalysisDeclContext &ac,
+ const CFGBlock *block, AnalysisDeclContext &ac,
+ const ClassifyRefs &classification,
UninitVariablesHandler *handler)
- : vals(vals), cfg(cfg), ac(ac), handler(handler),
- lastDR(0), lastLoad(0),
- skipProcessUses(false) {}
-
- void reportUninit(const DeclRefExpr *ex, const VarDecl *vd,
- bool isAlwaysUninit);
+ : vals(vals), cfg(cfg), block(block), ac(ac),
+ classification(classification), handler(handler) {}
+ void reportUse(const Expr *ex, const VarDecl *vd);
+
+ void VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS);
void VisitBlockExpr(BlockExpr *be);
+ void VisitCallExpr(CallExpr *ce);
void VisitDeclStmt(DeclStmt *ds);
void VisitDeclRefExpr(DeclRefExpr *dr);
- void VisitUnaryOperator(UnaryOperator *uo);
void VisitBinaryOperator(BinaryOperator *bo);
- void VisitCastExpr(CastExpr *ce);
- void VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs);
- void Visit(Stmt *s);
-
+
bool isTrackedVar(const VarDecl *vd) {
return ::isTrackedVar(vd, cast<DeclContext>(ac.getDecl()));
}
-
- FindVarResult findBlockVarDecl(Expr *ex);
-
- void ProcessUses(Stmt *s = 0);
-};
-}
-static const Expr *stripCasts(ASTContext &C, const Expr *Ex) {
- while (Ex) {
- Ex = Ex->IgnoreParenNoopCasts(C);
- if (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) {
- if (CE->getCastKind() == CK_LValueBitCast) {
- Ex = CE->getSubExpr();
- continue;
+ FindVarResult findVar(const Expr *ex) {
+ return ::findVar(ex, cast<DeclContext>(ac.getDecl()));
+ }
+
+ UninitUse getUninitUse(const Expr *ex, const VarDecl *vd, Value v) {
+ UninitUse Use(ex, isAlwaysUninit(v));
+
+ assert(isUninitialized(v));
+ if (Use.getKind() == UninitUse::Always)
+ return Use;
+
+ // If an edge which leads unconditionally to this use did not initialize
+ // the variable, we can say something stronger than 'may be uninitialized':
+ // we can say 'either it's used uninitialized or you have dead code'.
+ //
+ // We track the number of successors of a node which have been visited, and
+ // visit a node once we have visited all of its successors. Only edges where
+ // the variable might still be uninitialized are followed. Since a variable
+ // can't transfer from being initialized to being uninitialized, this will
+ // trace out the subgraph which inevitably leads to the use and does not
+ // initialize the variable. We do not want to skip past loops, since their
+ // non-termination might be correlated with the initialization condition.
+ //
+ // For example:
+ //
+ // void f(bool a, bool b) {
+ // block1: int n;
+ // if (a) {
+ // block2: if (b)
+ // block3: n = 1;
+ // block4: } else if (b) {
+ // block5: while (!a) {
+ // block6: do_work(&a);
+ // n = 2;
+ // }
+ // }
+ // block7: if (a)
+ // block8: g();
+ // block9: return n;
+ // }
+ //
+ // Starting from the maybe-uninitialized use in block 9:
+ // * Block 7 is not visited because we have only visited one of its two
+ // successors.
+ // * Block 8 is visited because we've visited its only successor.
+ // From block 8:
+ // * Block 7 is visited because we've now visited both of its successors.
+ // From block 7:
+ // * Blocks 1, 2, 4, 5, and 6 are not visited because we didn't visit all
+ // of their successors (we didn't visit 4, 3, 5, 6, and 5, respectively).
+ // * Block 3 is not visited because it initializes 'n'.
+ // Now the algorithm terminates, having visited blocks 7 and 8, and having
+ // found the frontier is blocks 2, 4, and 5.
+ //
+ // 'n' is definitely uninitialized for two edges into block 7 (from blocks 2
+ // and 4), so we report that any time either of those edges is taken (in
+ // each case when 'b == false'), 'n' is used uninitialized.
+ llvm::SmallVector<const CFGBlock*, 32> Queue;
+ llvm::SmallVector<unsigned, 32> SuccsVisited(cfg.getNumBlockIDs(), 0);
+ Queue.push_back(block);
+ // Specify that we've already visited all successors of the starting block.
+ // This has the dual purpose of ensuring we never add it to the queue, and
+ // of marking it as not being a candidate element of the frontier.
+ SuccsVisited[block->getBlockID()] = block->succ_size();
+ while (!Queue.empty()) {
+ const CFGBlock *B = Queue.back();
+ Queue.pop_back();
+ for (CFGBlock::const_pred_iterator I = B->pred_begin(), E = B->pred_end();
+ I != E; ++I) {
+ const CFGBlock *Pred = *I;
+ if (vals.getValue(Pred, B, vd) == Initialized)
+ // This block initializes the variable.
+ continue;
+
+ unsigned &SV = SuccsVisited[Pred->getBlockID()];
+ if (!SV) {
+ // When visiting the first successor of a block, mark all NULL
+ // successors as having been visited.
+ for (CFGBlock::const_succ_iterator SI = Pred->succ_begin(),
+ SE = Pred->succ_end();
+ SI != SE; ++SI)
+ if (!*SI)
+ ++SV;
+ }
+
+ if (++SV == Pred->succ_size())
+ // All paths from this block lead to the use and don't initialize the
+ // variable.
+ Queue.push_back(Pred);
+ }
+ }
+
+ // Scan the frontier, looking for blocks where the variable was
+ // uninitialized.
+ for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) {
+ const CFGBlock *Block = *BI;
+ unsigned BlockID = Block->getBlockID();
+ const Stmt *Term = Block->getTerminator();
+ if (SuccsVisited[BlockID] && SuccsVisited[BlockID] < Block->succ_size() &&
+ Term) {
+ // This block inevitably leads to the use. If we have an edge from here
+ // to a post-dominator block, and the variable is uninitialized on that
+ // edge, we have found a bug.
+ for (CFGBlock::const_succ_iterator I = Block->succ_begin(),
+ E = Block->succ_end(); I != E; ++I) {
+ const CFGBlock *Succ = *I;
+ if (Succ && SuccsVisited[Succ->getBlockID()] >= Succ->succ_size() &&
+ vals.getValue(Block, Succ, vd) == Uninitialized) {
+ // Switch cases are a special case: report the label to the caller
+ // as the 'terminator', not the switch statement itself. Suppress
+ // situations where no label matched: we can't be sure that's
+ // possible.
+ if (isa<SwitchStmt>(Term)) {
+ const Stmt *Label = Succ->getLabel();
+ if (!Label || !isa<SwitchCase>(Label))
+ // Might not be possible.
+ continue;
+ UninitUse::Branch Branch;
+ Branch.Terminator = Label;
+ Branch.Output = 0; // Ignored.
+ Use.addUninitBranch(Branch);
+ } else {
+ UninitUse::Branch Branch;
+ Branch.Terminator = Term;
+ Branch.Output = I - Block->succ_begin();
+ Use.addUninitBranch(Branch);
+ }
+ }
+ }
}
}
- break;
- }
- return Ex;
-}
-void TransferFunctions::reportUninit(const DeclRefExpr *ex,
- const VarDecl *vd, bool isAlwaysUnit) {
- if (handler) handler->handleUseOfUninitVariable(ex, vd, isAlwaysUnit);
+ return Use;
+ }
+};
}
-FindVarResult TransferFunctions::findBlockVarDecl(Expr *ex) {
- if (DeclRefExpr *dr = dyn_cast<DeclRefExpr>(ex->IgnoreParenCasts()))
- if (VarDecl *vd = dyn_cast<VarDecl>(dr->getDecl()))
- if (isTrackedVar(vd))
- return FindVarResult(vd, dr);
- return FindVarResult(0, 0);
+void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) {
+ if (!handler)
+ return;
+ Value v = vals[vd];
+ if (isUninitialized(v))
+ handler->handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v));
}
-void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs) {
+void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) {
// This represents an initialization of the 'element' value.
- Stmt *element = fs->getElement();
- const VarDecl *vd = 0;
-
- if (DeclStmt *ds = dyn_cast<DeclStmt>(element)) {
- vd = cast<VarDecl>(ds->getSingleDecl());
- if (!isTrackedVar(vd))
- vd = 0;
- } else {
- // Initialize the value of the reference variable.
- const FindVarResult &res = findBlockVarDecl(cast<Expr>(element));
- vd = res.getDecl();
+ if (DeclStmt *DS = dyn_cast<DeclStmt>(FS->getElement())) {
+ const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
+ if (isTrackedVar(VD))
+ vals[VD] = Initialized;
}
-
- if (vd)
- vals[vd] = Initialized;
}
void TransferFunctions::VisitBlockExpr(BlockExpr *be) {
@@ -442,231 +600,112 @@ void TransferFunctions::VisitBlockExpr(BlockExpr *be) {
vals[vd] = Initialized;
continue;
}
- Value v = vals[vd];
- if (handler && isUninitialized(v))
- handler->handleUseOfUninitVariable(be, vd, isAlwaysUninit(v));
+ reportUse(be, vd);
}
}
-void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) {
- // Record the last DeclRefExpr seen. This is an lvalue computation.
- // We use this value to later detect if a variable "escapes" the analysis.
- if (const VarDecl *vd = dyn_cast<VarDecl>(dr->getDecl()))
- if (isTrackedVar(vd)) {
- ProcessUses();
- lastDR = dr;
- }
-}
-
-void TransferFunctions::VisitDeclStmt(DeclStmt *ds) {
- for (DeclStmt::decl_iterator DI = ds->decl_begin(), DE = ds->decl_end();
- DI != DE; ++DI) {
- if (VarDecl *vd = dyn_cast<VarDecl>(*DI)) {
- if (isTrackedVar(vd)) {
- if (Expr *init = vd->getInit()) {
- // If the initializer consists solely of a reference to itself, we
- // explicitly mark the variable as uninitialized. This allows code
- // like the following:
- //
- // int x = x;
- //
- // to deliberately leave a variable uninitialized. Different analysis
- // clients can detect this pattern and adjust their reporting
- // appropriately, but we need to continue to analyze subsequent uses
- // of the variable.
- if (init == lastLoad) {
- const DeclRefExpr *DR
- = cast<DeclRefExpr>(stripCasts(ac.getASTContext(),
- lastLoad->getSubExpr()));
- if (DR->getDecl() == vd) {
- // int x = x;
- // Propagate uninitialized value, but don't immediately report
- // a problem.
- vals[vd] = Uninitialized;
- lastLoad = 0;
- lastDR = 0;
- if (handler)
- handler->handleSelfInit(vd);
- return;
- }
- }
-
- // All other cases: treat the new variable as initialized.
- // This is a minor optimization to reduce the propagation
- // of the analysis, since we will have already reported
- // the use of the uninitialized value (which visiting the
- // initializer).
- vals[vd] = Initialized;
- }
- }
- }
- }
+void TransferFunctions::VisitCallExpr(CallExpr *ce) {
+ // After a call to a function like setjmp or vfork, any variable which is
+ // initialized anywhere within this function may now be initialized. For now,
+ // just assume such a call initializes all variables.
+ // FIXME: Only mark variables as initialized if they have an initializer which
+ // is reachable from here.
+ Decl *Callee = ce->getCalleeDecl();
+ if (Callee && Callee->hasAttr<ReturnsTwiceAttr>())
+ vals.setAllScratchValues(Initialized);
}
-void TransferFunctions::VisitBinaryOperator(clang::BinaryOperator *bo) {
- if (bo->isAssignmentOp()) {
- const FindVarResult &res = findBlockVarDecl(bo->getLHS());
- if (const VarDecl *vd = res.getDecl()) {
- ValueVector::reference val = vals[vd];
- if (isUninitialized(val)) {
- if (bo->getOpcode() != BO_Assign)
- reportUninit(res.getDeclRefExpr(), vd, isAlwaysUninit(val));
- else
- val = Initialized;
- }
- }
+void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) {
+ switch (classification.get(dr)) {
+ case ClassifyRefs::Ignore:
+ break;
+ case ClassifyRefs::Use:
+ reportUse(dr, cast<VarDecl>(dr->getDecl()));
+ break;
+ case ClassifyRefs::Init:
+ vals[cast<VarDecl>(dr->getDecl())] = Initialized;
+ break;
+ case ClassifyRefs::SelfInit:
+ if (handler)
+ handler->handleSelfInit(cast<VarDecl>(dr->getDecl()));
+ break;
}
}
-void TransferFunctions::VisitUnaryOperator(clang::UnaryOperator *uo) {
- switch (uo->getOpcode()) {
- case clang::UO_PostDec:
- case clang::UO_PostInc:
- case clang::UO_PreDec:
- case clang::UO_PreInc: {
- const FindVarResult &res = findBlockVarDecl(uo->getSubExpr());
- if (const VarDecl *vd = res.getDecl()) {
- assert(res.getDeclRefExpr() == lastDR);
- // We null out lastDR to indicate we have fully processed it
- // and we don't want the auto-value setting in Visit().
- lastDR = 0;
-
- ValueVector::reference val = vals[vd];
- if (isUninitialized(val))
- reportUninit(res.getDeclRefExpr(), vd, isAlwaysUninit(val));
- }
- break;
- }
- default:
- break;
+void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) {
+ if (BO->getOpcode() == BO_Assign) {
+ FindVarResult Var = findVar(BO->getLHS());
+ if (const VarDecl *VD = Var.getDecl())
+ vals[VD] = Initialized;
}
}
-void TransferFunctions::VisitCastExpr(clang::CastExpr *ce) {
- if (ce->getCastKind() == CK_LValueToRValue) {
- const FindVarResult &res = findBlockVarDecl(ce->getSubExpr());
- if (res.getDecl()) {
- assert(res.getDeclRefExpr() == lastDR);
- lastLoad = ce;
- }
- }
- else if (ce->getCastKind() == CK_NoOp ||
- ce->getCastKind() == CK_LValueBitCast) {
- skipProcessUses = true;
- }
- else if (CStyleCastExpr *cse = dyn_cast<CStyleCastExpr>(ce)) {
- if (cse->getType()->isVoidType()) {
- // e.g. (void) x;
- if (lastLoad == cse->getSubExpr()) {
- // Squelch any detected load of an uninitialized value if
- // we cast it to void.
- lastLoad = 0;
- lastDR = 0;
+void TransferFunctions::VisitDeclStmt(DeclStmt *DS) {
+ for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end();
+ DI != DE; ++DI) {
+ VarDecl *VD = dyn_cast<VarDecl>(*DI);
+ if (VD && isTrackedVar(VD)) {
+ if (getSelfInitExpr(VD)) {
+ // If the initializer consists solely of a reference to itself, we
+ // explicitly mark the variable as uninitialized. This allows code
+ // like the following:
+ //
+ // int x = x;
+ //
+ // to deliberately leave a variable uninitialized. Different analysis
+ // clients can detect this pattern and adjust their reporting
+ // appropriately, but we need to continue to analyze subsequent uses
+ // of the variable.
+ vals[VD] = Uninitialized;
+ } else if (VD->getInit()) {
+ // Treat the new variable as initialized.
+ vals[VD] = Initialized;
+ } else {
+ // No initializer: the variable is now uninitialized. This matters
+ // for cases like:
+ // while (...) {
+ // int n;
+ // use(n);
+ // n = 0;
+ // }
+ // FIXME: Mark the variable as uninitialized whenever its scope is
+ // left, since its scope could be re-entered by a jump over the
+ // declaration.
+ vals[VD] = Uninitialized;
}
}
}
}
-void TransferFunctions::Visit(clang::Stmt *s) {
- skipProcessUses = false;
- StmtVisitor<TransferFunctions>::Visit(s);
- if (!skipProcessUses)
- ProcessUses(s);
-}
-
-void TransferFunctions::ProcessUses(Stmt *s) {
- // This method is typically called after visiting a CFGElement statement
- // in the CFG. We delay processing of reporting many loads of uninitialized
- // values until here.
- if (lastLoad) {
- // If we just visited the lvalue-to-rvalue cast, there is nothing
- // left to do.
- if (lastLoad == s)
- return;
-
- const DeclRefExpr *DR =
- cast<DeclRefExpr>(stripCasts(ac.getASTContext(),
- lastLoad->getSubExpr()));
- const VarDecl *VD = cast<VarDecl>(DR->getDecl());
-
- // If we reach here, we may have seen a load of an uninitialized value
- // and it hasn't been casted to void or otherwise handled. In this
- // situation, report the incident.
- if (isUninitialized(vals[VD]))
- reportUninit(DR, VD, isAlwaysUninit(vals[VD]));
-
- lastLoad = 0;
-
- if (DR == lastDR) {
- lastDR = 0;
- return;
- }
- }
-
- // Any other uses of 'lastDR' involve taking an lvalue of variable.
- // In this case, it "escapes" the analysis.
- if (lastDR && lastDR != s) {
- vals[cast<VarDecl>(lastDR->getDecl())] = Initialized;
- lastDR = 0;
- }
-}
-
//------------------------------------------------------------------------====//
// High-level "driver" logic for uninitialized values analysis.
//====------------------------------------------------------------------------//
static bool runOnBlock(const CFGBlock *block, const CFG &cfg,
AnalysisDeclContext &ac, CFGBlockValues &vals,
+ const ClassifyRefs &classification,
llvm::BitVector &wasAnalyzed,
UninitVariablesHandler *handler = 0) {
-
wasAnalyzed[block->getBlockID()] = true;
-
- if (const BinaryOperator *b = getLogicalOperatorInChain(block)) {
- CFGBlock::const_pred_iterator itr = block->pred_begin();
- BVPair vA = vals.getValueVectors(*itr, false);
- ++itr;
- BVPair vB = vals.getValueVectors(*itr, false);
-
- BVPair valsAB;
-
- if (b->getOpcode() == BO_LAnd) {
- // Merge the 'F' bits from the first and second.
- vals.mergeIntoScratch(*(vA.second ? vA.second : vA.first), true);
- vals.mergeIntoScratch(*(vB.second ? vB.second : vB.first), false);
- valsAB.first = vA.first;
- valsAB.second = &vals.getScratch();
- } else {
- // Merge the 'T' bits from the first and second.
- assert(b->getOpcode() == BO_LOr);
- vals.mergeIntoScratch(*vA.first, true);
- vals.mergeIntoScratch(*vB.first, false);
- valsAB.first = &vals.getScratch();
- valsAB.second = vA.second ? vA.second : vA.first;
- }
- return vals.updateValueVectors(block, valsAB);
- }
-
- // Default behavior: merge in values of predecessor blocks.
vals.resetScratch();
+ // Merge in values of predecessor blocks.
bool isFirst = true;
for (CFGBlock::const_pred_iterator I = block->pred_begin(),
E = block->pred_end(); I != E; ++I) {
const CFGBlock *pred = *I;
if (wasAnalyzed[pred->getBlockID()]) {
- vals.mergeIntoScratch(vals.getValueVector(pred, block), isFirst);
+ vals.mergeIntoScratch(vals.getValueVector(pred), isFirst);
isFirst = false;
}
}
// Apply the transfer function.
- TransferFunctions tf(vals, cfg, ac, handler);
+ TransferFunctions tf(vals, cfg, block, ac, classification, handler);
for (CFGBlock::const_iterator I = block->begin(), E = block->end();
I != E; ++I) {
if (const CFGStmt *cs = dyn_cast<CFGStmt>(&*I)) {
tf.Visit(const_cast<Stmt*>(cs->getStmt()));
}
}
- tf.ProcessUses();
return vals.updateValueVectorWithScratch(block);
}
@@ -683,17 +722,16 @@ void clang::runUninitializedVariablesAnalysis(
stats.NumVariablesAnalyzed = vals.getNumEntries();
+ // Precompute which expressions are uses and which are initializations.
+ ClassifyRefs classification(ac);
+ cfg.VisitBlockStmts(classification);
+
// Mark all variables uninitialized at the entry.
const CFGBlock &entry = cfg.getEntry();
- for (CFGBlock::const_succ_iterator i = entry.succ_begin(),
- e = entry.succ_end(); i != e; ++i) {
- if (const CFGBlock *succ = *i) {
- ValueVector &vec = vals.getValueVector(&entry, succ);
- const unsigned n = vals.getNumEntries();
- for (unsigned j = 0; j < n ; ++j) {
- vec[j] = Uninitialized;
- }
- }
+ ValueVector &vec = vals.getValueVector(&entry);
+ const unsigned n = vals.getNumEntries();
+ for (unsigned j = 0; j < n ; ++j) {
+ vec[j] = Uninitialized;
}
// Proceed with the workist.
@@ -705,7 +743,8 @@ void clang::runUninitializedVariablesAnalysis(
while (const CFGBlock *block = worklist.dequeue()) {
// Did the block change?
- bool changed = runOnBlock(block, cfg, ac, vals, wasAnalyzed);
+ bool changed = runOnBlock(block, cfg, ac, vals,
+ classification, wasAnalyzed);
++stats.NumBlockVisits;
if (changed || !previouslyVisited[block->getBlockID()])
worklist.enqueueSuccessors(block);
@@ -716,7 +755,7 @@ void clang::runUninitializedVariablesAnalysis(
for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) {
const CFGBlock *block = *BI;
if (wasAnalyzed[block->getBlockID()]) {
- runOnBlock(block, cfg, ac, vals, wasAnalyzed, &handler);
+ runOnBlock(block, cfg, ac, vals, classification, wasAnalyzed, &handler);
++stats.NumBlockVisits;
}
}
OpenPOWER on IntegriCloud