llvm-project/clang/lib/Analysis/UninitializedValues.cpp

725 lines
22 KiB
C++
Raw Normal View History

//==- UninitializedValues.cpp - Find Uninitialized Values -------*- C++ --*-==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements uninitialized values analysis for source-level CFGs.
//
//===----------------------------------------------------------------------===//
#include <utility>
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/PackedVector.h"
#include "llvm/ADT/DenseMap.h"
#include "clang/AST/Decl.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h"
#include "clang/Analysis/Analyses/UninitializedValues.h"
#include "clang/Analysis/Support/SaveAndRestore.h"
using namespace clang;
static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) {
if (vd->isLocalVarDecl() && !vd->hasGlobalStorage() &&
!vd->isExceptionVariable() &&
vd->getDeclContext() == dc) {
QualType ty = vd->getType();
return ty->isScalarType() || ty->isVectorType();
}
return false;
}
//------------------------------------------------------------------------====//
// DeclToIndex: a mapping from Decls we track to value indices.
//====------------------------------------------------------------------------//
namespace {
class DeclToIndex {
llvm::DenseMap<const VarDecl *, unsigned> map;
public:
DeclToIndex() {}
/// Compute the actual mapping from declarations to bits.
void computeMap(const DeclContext &dc);
/// Return the number of declarations in the map.
unsigned size() const { return map.size(); }
/// Returns the bit vector index for a given declaration.
llvm::Optional<unsigned> getValueIndex(const VarDecl *d) const;
};
}
void DeclToIndex::computeMap(const DeclContext &dc) {
unsigned count = 0;
DeclContext::specific_decl_iterator<VarDecl> I(dc.decls_begin()),
E(dc.decls_end());
for ( ; I != E; ++I) {
const VarDecl *vd = *I;
if (isTrackedVar(vd, &dc))
map[vd] = count++;
}
}
llvm::Optional<unsigned> DeclToIndex::getValueIndex(const VarDecl *d) const {
llvm::DenseMap<const VarDecl *, unsigned>::const_iterator I = map.find(d);
if (I == map.end())
return llvm::Optional<unsigned>();
return I->second;
}
//------------------------------------------------------------------------====//
// CFGBlockValues: dataflow values for CFG blocks.
//====------------------------------------------------------------------------//
// These values are defined in such a way that a merge can be done using
// a bitwise OR.
enum Value { Unknown = 0x0, /* 00 */
Initialized = 0x1, /* 01 */
Uninitialized = 0x2, /* 10 */
MayUninitialized = 0x3 /* 11 */ };
static bool isUninitialized(const Value v) {
return v >= Uninitialized;
}
static bool isAlwaysUninit(const Value v) {
return v == Uninitialized;
}
2011-03-26 20:38:21 +08:00
namespace {
typedef llvm::PackedVector<Value, 2> ValueVector;
typedef std::pair<ValueVector *, ValueVector *> BVPair;
class CFGBlockValues {
const CFG &cfg;
BVPair *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);
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;
}
2011-08-20 09:15:28 +08:00
void resetScratch();
ValueVector &getScratch() { return scratch; }
ValueVector::reference operator[](const VarDecl *vd);
};
2011-03-26 20:38:21 +08:00
} // 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() {
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;
}
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;
const CFGStmt *cstmt = block->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];
}
#if 0
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';
}
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";
}
#endif
void CFGBlockValues::mergeIntoScratch(ValueVector const &source,
bool isFirst) {
if (isFirst)
scratch = source;
else
scratch |= source;
}
bool CFGBlockValues::updateValueVectorWithScratch(const CFGBlock *block) {
ValueVector &dst = getValueVector(block, 0);
bool changed = (dst != scratch);
if (changed)
dst = scratch;
#if 0
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();
}
ValueVector::reference CFGBlockValues::operator[](const VarDecl *vd) {
const llvm::Optional<unsigned> &idx = declToIndex.getValueIndex(vd);
assert(idx.hasValue());
return scratch[idx.getValue()];
}
//------------------------------------------------------------------------====//
// Worklist: worklist for dataflow analysis.
//====------------------------------------------------------------------------//
namespace {
class DataflowWorklist {
SmallVector<const CFGBlock *, 20> worklist;
llvm::BitVector enqueuedBlocks;
public:
DataflowWorklist(const CFG &cfg) : enqueuedBlocks(cfg.getNumBlockIDs()) {}
void enqueueSuccessors(const CFGBlock *block);
const CFGBlock *dequeue();
};
}
void DataflowWorklist::enqueueSuccessors(const clang::CFGBlock *block) {
Make the worklist in the uninitialized values checker actually a queue. Previously, despite the names 'enqueue' and 'dequeue', it behaved as a stack and visited blocks in a LIFO fashion. This interacts badly with extremely broad CFGs *inside* of a loop (such as a large switch inside a state machine) where every block updates a different variable. When encountering such a CFG, the checker visited blocks in essentially a "depth first" order due to the stack-like behavior of the work list. Combined with each block updating a different variable, the saturation logic of the checker caused it to re-traverse blocks [1,N-1] of the broad CFG inside the loop after traversing block N. These re-traversals were to propagate the variable values derived from block N. Assuming approximately the same number of variables as inner blocks exist, the end result is O(N^2) updates. By making this a queue, we also make the traversal essentially "breadth-first" across each of the N inner blocks of the loop. Then all of this state is propagated around to all N inner blocks of the loop. The result is O(N) updates. The truth is in the numbers: Before, gcc.c: 96409 block visits (max: 61546, avg: 591) After, gcc.c: 69958 block visits (max: 33090, avg: 429) Before, PR10183: 2540494 block vists (max: 2536495, avg: 37360) After, PR10183: 137803 block visits (max: 134406, avg: 2026) The nearly 20x reduction in work for PR10183 corresponds to a roughly 100x speedup in compile time. I've tested it on all the code I can get my hands on, and I've seen no slowdowns due to this change. Where I've collected stats, the ammount of work done is on average less. I'll also commit shortly some synthetic test cases useful in analyzing the performance of CFG-based warnings. Submitting this based on Doug's feedback that post-commit review should be good. Ted, please review! Hopefully this helps compile times until then. llvm-svn: 134697
2011-07-08 19:19:06 +08:00
unsigned OldWorklistSize = worklist.size();
for (CFGBlock::const_succ_iterator I = block->succ_begin(),
E = block->succ_end(); I != E; ++I) {
Make the worklist in the uninitialized values checker actually a queue. Previously, despite the names 'enqueue' and 'dequeue', it behaved as a stack and visited blocks in a LIFO fashion. This interacts badly with extremely broad CFGs *inside* of a loop (such as a large switch inside a state machine) where every block updates a different variable. When encountering such a CFG, the checker visited blocks in essentially a "depth first" order due to the stack-like behavior of the work list. Combined with each block updating a different variable, the saturation logic of the checker caused it to re-traverse blocks [1,N-1] of the broad CFG inside the loop after traversing block N. These re-traversals were to propagate the variable values derived from block N. Assuming approximately the same number of variables as inner blocks exist, the end result is O(N^2) updates. By making this a queue, we also make the traversal essentially "breadth-first" across each of the N inner blocks of the loop. Then all of this state is propagated around to all N inner blocks of the loop. The result is O(N) updates. The truth is in the numbers: Before, gcc.c: 96409 block visits (max: 61546, avg: 591) After, gcc.c: 69958 block visits (max: 33090, avg: 429) Before, PR10183: 2540494 block vists (max: 2536495, avg: 37360) After, PR10183: 137803 block visits (max: 134406, avg: 2026) The nearly 20x reduction in work for PR10183 corresponds to a roughly 100x speedup in compile time. I've tested it on all the code I can get my hands on, and I've seen no slowdowns due to this change. Where I've collected stats, the ammount of work done is on average less. I'll also commit shortly some synthetic test cases useful in analyzing the performance of CFG-based warnings. Submitting this based on Doug's feedback that post-commit review should be good. Ted, please review! Hopefully this helps compile times until then. llvm-svn: 134697
2011-07-08 19:19:06 +08:00
const CFGBlock *Successor = *I;
if (!Successor || enqueuedBlocks[Successor->getBlockID()])
continue;
worklist.push_back(Successor);
enqueuedBlocks[Successor->getBlockID()] = true;
}
Make the worklist in the uninitialized values checker actually a queue. Previously, despite the names 'enqueue' and 'dequeue', it behaved as a stack and visited blocks in a LIFO fashion. This interacts badly with extremely broad CFGs *inside* of a loop (such as a large switch inside a state machine) where every block updates a different variable. When encountering such a CFG, the checker visited blocks in essentially a "depth first" order due to the stack-like behavior of the work list. Combined with each block updating a different variable, the saturation logic of the checker caused it to re-traverse blocks [1,N-1] of the broad CFG inside the loop after traversing block N. These re-traversals were to propagate the variable values derived from block N. Assuming approximately the same number of variables as inner blocks exist, the end result is O(N^2) updates. By making this a queue, we also make the traversal essentially "breadth-first" across each of the N inner blocks of the loop. Then all of this state is propagated around to all N inner blocks of the loop. The result is O(N) updates. The truth is in the numbers: Before, gcc.c: 96409 block visits (max: 61546, avg: 591) After, gcc.c: 69958 block visits (max: 33090, avg: 429) Before, PR10183: 2540494 block vists (max: 2536495, avg: 37360) After, PR10183: 137803 block visits (max: 134406, avg: 2026) The nearly 20x reduction in work for PR10183 corresponds to a roughly 100x speedup in compile time. I've tested it on all the code I can get my hands on, and I've seen no slowdowns due to this change. Where I've collected stats, the ammount of work done is on average less. I'll also commit shortly some synthetic test cases useful in analyzing the performance of CFG-based warnings. Submitting this based on Doug's feedback that post-commit review should be good. Ted, please review! Hopefully this helps compile times until then. llvm-svn: 134697
2011-07-08 19:19:06 +08:00
if (OldWorklistSize == 0 || OldWorklistSize == worklist.size())
return;
// Rotate the newly added blocks to the start of the worklist so that it forms
// a proper queue when we pop off the end of the worklist.
std::rotate(worklist.begin(), worklist.begin() + OldWorklistSize,
worklist.end());
}
const CFGBlock *DataflowWorklist::dequeue() {
if (worklist.empty())
return 0;
const CFGBlock *b = worklist.back();
worklist.pop_back();
enqueuedBlocks[b->getBlockID()] = false;
return b;
}
//------------------------------------------------------------------------====//
// Transfer function for uninitialized values analysis.
//====------------------------------------------------------------------------//
namespace {
class FindVarResult {
const VarDecl *vd;
const DeclRefExpr *dr;
public:
FindVarResult(VarDecl *vd, DeclRefExpr *dr) : vd(vd), dr(dr) {}
const DeclRefExpr *getDeclRefExpr() const { return dr; }
const VarDecl *getDecl() const { return vd; }
};
class TransferFunctions : public StmtVisitor<TransferFunctions> {
CFGBlockValues &vals;
const CFG &cfg;
AnalysisContext &ac;
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,
AnalysisContext &ac,
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);
void VisitBlockExpr(BlockExpr *be);
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;
}
}
break;
}
return Ex;
}
void TransferFunctions::reportUninit(const DeclRefExpr *ex,
const VarDecl *vd, bool isAlwaysUnit) {
if (handler) handler->handleUseOfUninitVariable(ex, vd, isAlwaysUnit);
}
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::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 (vd)
vals[vd] = Initialized;
}
void TransferFunctions::VisitBlockExpr(BlockExpr *be) {
const BlockDecl *bd = be->getBlockDecl();
for (BlockDecl::capture_const_iterator i = bd->capture_begin(),
e = bd->capture_end() ; i != e; ++i) {
const VarDecl *vd = i->getVariable();
if (!isTrackedVar(vd))
continue;
if (i->isByRef()) {
vals[vd] = Initialized;
continue;
}
Value v = vals[vd];
if (handler && isUninitialized(v))
handler->handleUseOfUninitVariable(be, vd, isAlwaysUninit(v));
}
}
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::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::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::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::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,
AnalysisContext &ac, CFGBlockValues &vals,
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();
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);
isFirst = false;
}
}
// Apply the transfer function.
TransferFunctions tf(vals, cfg, ac, 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);
}
Build up statistics about the work done for analysis based warnings. Special detail is added for uninitialized variable analysis as this has serious performance problems than need to be tracked. Computing some of this data is expensive, for example walking the CFG to determine its size. To avoid doing that unless the stats data is going to be used, we thread a bit into the Sema object to track whether detailed stats should be collected or not. This bit is used to avoid computations whereever the computations are likely to be more expensive than checking the state of the flag. Thus, counters are in some cases unconditionally updated, but the more expensive (and less frequent) aggregation steps are skipped. With this patch, we're able to see that for 'gcc.c': *** Analysis Based Warnings Stats: 232 functions analyzed (0 w/o CFGs). 7151 CFG blocks built. 30 average CFG blocks per function. 1167 max CFG blocks per function. 163 functions analyzed for uninitialiazed variables 640 variables analyzed. 3 average variables per function. 94 max variables per function. 96409 block visits. 591 average block visits per function. 61546 max block visits per function. And for the reduced testcase in PR10183: *** Analysis Based Warnings Stats: 98 functions analyzed (0 w/o CFGs). 8526 CFG blocks built. 87 average CFG blocks per function. 7277 max CFG blocks per function. 68 functions analyzed for uninitialiazed variables 1359 variables analyzed. 19 average variables per function. 1196 max variables per function. 2540494 block visits. 37360 average block visits per function. 2536495 max block visits per function. That last number is the somewhat scary one that indicates the problem in PR10183. llvm-svn: 134494
2011-07-07 00:21:37 +08:00
void clang::runUninitializedVariablesAnalysis(
const DeclContext &dc,
const CFG &cfg,
AnalysisContext &ac,
UninitVariablesHandler &handler,
UninitVariablesAnalysisStats &stats) {
CFGBlockValues vals(cfg);
vals.computeSetOfDeclarations(dc);
if (vals.hasNoDeclarations())
return;
Build up statistics about the work done for analysis based warnings. Special detail is added for uninitialized variable analysis as this has serious performance problems than need to be tracked. Computing some of this data is expensive, for example walking the CFG to determine its size. To avoid doing that unless the stats data is going to be used, we thread a bit into the Sema object to track whether detailed stats should be collected or not. This bit is used to avoid computations whereever the computations are likely to be more expensive than checking the state of the flag. Thus, counters are in some cases unconditionally updated, but the more expensive (and less frequent) aggregation steps are skipped. With this patch, we're able to see that for 'gcc.c': *** Analysis Based Warnings Stats: 232 functions analyzed (0 w/o CFGs). 7151 CFG blocks built. 30 average CFG blocks per function. 1167 max CFG blocks per function. 163 functions analyzed for uninitialiazed variables 640 variables analyzed. 3 average variables per function. 94 max variables per function. 96409 block visits. 591 average block visits per function. 61546 max block visits per function. And for the reduced testcase in PR10183: *** Analysis Based Warnings Stats: 98 functions analyzed (0 w/o CFGs). 8526 CFG blocks built. 87 average CFG blocks per function. 7277 max CFG blocks per function. 68 functions analyzed for uninitialiazed variables 1359 variables analyzed. 19 average variables per function. 1196 max variables per function. 2540494 block visits. 37360 average block visits per function. 2536495 max block visits per function. That last number is the somewhat scary one that indicates the problem in PR10183. llvm-svn: 134494
2011-07-07 00:21:37 +08:00
stats.NumVariablesAnalyzed = vals.getNumEntries();
// 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;
}
}
}
// Proceed with the workist.
DataflowWorklist worklist(cfg);
llvm::BitVector previouslyVisited(cfg.getNumBlockIDs());
worklist.enqueueSuccessors(&cfg.getEntry());
llvm::BitVector wasAnalyzed(cfg.getNumBlockIDs(), false);
wasAnalyzed[cfg.getEntry().getBlockID()] = true;
while (const CFGBlock *block = worklist.dequeue()) {
// Did the block change?
Build up statistics about the work done for analysis based warnings. Special detail is added for uninitialized variable analysis as this has serious performance problems than need to be tracked. Computing some of this data is expensive, for example walking the CFG to determine its size. To avoid doing that unless the stats data is going to be used, we thread a bit into the Sema object to track whether detailed stats should be collected or not. This bit is used to avoid computations whereever the computations are likely to be more expensive than checking the state of the flag. Thus, counters are in some cases unconditionally updated, but the more expensive (and less frequent) aggregation steps are skipped. With this patch, we're able to see that for 'gcc.c': *** Analysis Based Warnings Stats: 232 functions analyzed (0 w/o CFGs). 7151 CFG blocks built. 30 average CFG blocks per function. 1167 max CFG blocks per function. 163 functions analyzed for uninitialiazed variables 640 variables analyzed. 3 average variables per function. 94 max variables per function. 96409 block visits. 591 average block visits per function. 61546 max block visits per function. And for the reduced testcase in PR10183: *** Analysis Based Warnings Stats: 98 functions analyzed (0 w/o CFGs). 8526 CFG blocks built. 87 average CFG blocks per function. 7277 max CFG blocks per function. 68 functions analyzed for uninitialiazed variables 1359 variables analyzed. 19 average variables per function. 1196 max variables per function. 2540494 block visits. 37360 average block visits per function. 2536495 max block visits per function. That last number is the somewhat scary one that indicates the problem in PR10183. llvm-svn: 134494
2011-07-07 00:21:37 +08:00
bool changed = runOnBlock(block, cfg, ac, vals, wasAnalyzed);
++stats.NumBlockVisits;
if (changed || !previouslyVisited[block->getBlockID()])
worklist.enqueueSuccessors(block);
previouslyVisited[block->getBlockID()] = true;
}
// Run through the blocks one more time, and report uninitialized variabes.
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);
Build up statistics about the work done for analysis based warnings. Special detail is added for uninitialized variable analysis as this has serious performance problems than need to be tracked. Computing some of this data is expensive, for example walking the CFG to determine its size. To avoid doing that unless the stats data is going to be used, we thread a bit into the Sema object to track whether detailed stats should be collected or not. This bit is used to avoid computations whereever the computations are likely to be more expensive than checking the state of the flag. Thus, counters are in some cases unconditionally updated, but the more expensive (and less frequent) aggregation steps are skipped. With this patch, we're able to see that for 'gcc.c': *** Analysis Based Warnings Stats: 232 functions analyzed (0 w/o CFGs). 7151 CFG blocks built. 30 average CFG blocks per function. 1167 max CFG blocks per function. 163 functions analyzed for uninitialiazed variables 640 variables analyzed. 3 average variables per function. 94 max variables per function. 96409 block visits. 591 average block visits per function. 61546 max block visits per function. And for the reduced testcase in PR10183: *** Analysis Based Warnings Stats: 98 functions analyzed (0 w/o CFGs). 8526 CFG blocks built. 87 average CFG blocks per function. 7277 max CFG blocks per function. 68 functions analyzed for uninitialiazed variables 1359 variables analyzed. 19 average variables per function. 1196 max variables per function. 2540494 block visits. 37360 average block visits per function. 2536495 max block visits per function. That last number is the somewhat scary one that indicates the problem in PR10183. llvm-svn: 134494
2011-07-07 00:21:37 +08:00
++stats.NumBlockVisits;
}
}
}
UninitVariablesHandler::~UninitVariablesHandler() {}