2011-03-15 11:17:07 +08:00
|
|
|
//==- UninitializedValues.cpp - Find Uninitialized Values -------*- C++ --*-==//
|
2011-01-15 10:58:47 +08:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2011-01-21 01:37:17 +08:00
|
|
|
#include <utility>
|
2011-01-15 10:58:47 +08:00
|
|
|
#include "llvm/ADT/Optional.h"
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
2011-05-31 11:56:09 +08:00
|
|
|
#include "llvm/ADT/PackedVector.h"
|
2011-01-15 10:58:47 +08:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "clang/AST/Decl.h"
|
|
|
|
#include "clang/Analysis/CFG.h"
|
2011-01-26 03:13:48 +08:00
|
|
|
#include "clang/Analysis/AnalysisContext.h"
|
2011-01-15 10:58:47 +08:00
|
|
|
#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h"
|
2011-03-15 11:17:07 +08:00
|
|
|
#include "clang/Analysis/Analyses/UninitializedValues.h"
|
2011-01-19 05:18:58 +08:00
|
|
|
#include "clang/Analysis/Support/SaveAndRestore.h"
|
2011-01-15 10:58:47 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
|
|
|
|
2011-01-27 10:29:34 +08:00
|
|
|
static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) {
|
2011-03-17 11:06:11 +08:00
|
|
|
if (vd->isLocalVarDecl() && !vd->hasGlobalStorage() &&
|
2011-04-08 04:02:56 +08:00
|
|
|
!vd->isExceptionVariable() &&
|
2011-03-17 11:06:11 +08:00
|
|
|
vd->getDeclContext() == dc) {
|
|
|
|
QualType ty = vd->getType();
|
|
|
|
return ty->isScalarType() || ty->isVectorType();
|
|
|
|
}
|
|
|
|
return false;
|
2011-01-18 12:53:25 +08:00
|
|
|
}
|
|
|
|
|
2011-01-15 10:58:47 +08:00
|
|
|
//------------------------------------------------------------------------====//
|
2011-03-15 12:57:27 +08:00
|
|
|
// DeclToIndex: a mapping from Decls we track to value indices.
|
2011-01-15 10:58:47 +08:00
|
|
|
//====------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
namespace {
|
2011-03-15 12:57:27 +08:00
|
|
|
class DeclToIndex {
|
2011-01-15 10:58:47 +08:00
|
|
|
llvm::DenseMap<const VarDecl *, unsigned> map;
|
|
|
|
public:
|
2011-03-15 12:57:27 +08:00
|
|
|
DeclToIndex() {}
|
2011-01-15 10:58:47 +08:00
|
|
|
|
|
|
|
/// 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.
|
2011-03-29 09:40:00 +08:00
|
|
|
llvm::Optional<unsigned> getValueIndex(const VarDecl *d) const;
|
2011-01-15 10:58:47 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
void DeclToIndex::computeMap(const DeclContext &dc) {
|
2011-01-15 10:58:47 +08:00
|
|
|
unsigned count = 0;
|
|
|
|
DeclContext::specific_decl_iterator<VarDecl> I(dc.decls_begin()),
|
|
|
|
E(dc.decls_end());
|
|
|
|
for ( ; I != E; ++I) {
|
|
|
|
const VarDecl *vd = *I;
|
2011-01-27 10:29:34 +08:00
|
|
|
if (isTrackedVar(vd, &dc))
|
2011-01-15 10:58:47 +08:00
|
|
|
map[vd] = count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-29 09:40:00 +08:00
|
|
|
llvm::Optional<unsigned> DeclToIndex::getValueIndex(const VarDecl *d) const {
|
|
|
|
llvm::DenseMap<const VarDecl *, unsigned>::const_iterator I = map.find(d);
|
2011-01-15 10:58:47 +08:00
|
|
|
if (I == map.end())
|
|
|
|
return llvm::Optional<unsigned>();
|
|
|
|
return I->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------====//
|
|
|
|
// CFGBlockValues: dataflow values for CFG blocks.
|
|
|
|
//====------------------------------------------------------------------------//
|
|
|
|
|
2011-03-15 12:57:38 +08:00
|
|
|
// 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-15 12:57:29 +08:00
|
|
|
|
2011-03-26 20:38:21 +08:00
|
|
|
namespace {
|
2011-03-15 12:57:29 +08:00
|
|
|
|
2011-05-31 11:56:09 +08:00
|
|
|
typedef llvm::PackedVector<Value, 2> ValueVector;
|
2011-03-15 12:57:27 +08:00
|
|
|
typedef std::pair<ValueVector *, ValueVector *> BVPair;
|
2011-01-21 01:37:17 +08:00
|
|
|
|
2011-01-15 10:58:47 +08:00
|
|
|
class CFGBlockValues {
|
|
|
|
const CFG &cfg;
|
2011-01-21 01:37:17 +08:00
|
|
|
BVPair *vals;
|
2011-03-15 12:57:27 +08:00
|
|
|
ValueVector scratch;
|
2011-03-15 13:30:12 +08:00
|
|
|
DeclToIndex declToIndex;
|
2011-01-21 01:37:17 +08:00
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
ValueVector &lazyCreate(ValueVector *&bv);
|
2011-01-15 10:58:47 +08:00
|
|
|
public:
|
|
|
|
CFGBlockValues(const CFG &cfg);
|
|
|
|
~CFGBlockValues();
|
|
|
|
|
2011-04-05 07:29:12 +08:00
|
|
|
unsigned getNumEntries() const { return declToIndex.size(); }
|
|
|
|
|
2011-01-15 10:58:47 +08:00
|
|
|
void computeSetOfDeclarations(const DeclContext &dc);
|
2011-03-15 12:57:27 +08:00
|
|
|
ValueVector &getValueVector(const CFGBlock *block,
|
2011-01-21 01:37:17 +08:00
|
|
|
const CFGBlock *dstBlock);
|
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
BVPair &getValueVectors(const CFGBlock *block, bool shouldLazyCreate);
|
2011-01-21 01:37:17 +08:00
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
void mergeIntoScratch(ValueVector const &source, bool isFirst);
|
|
|
|
bool updateValueVectorWithScratch(const CFGBlock *block);
|
|
|
|
bool updateValueVectors(const CFGBlock *block, const BVPair &newVals);
|
2011-01-15 10:58:47 +08:00
|
|
|
|
|
|
|
bool hasNoDeclarations() const {
|
2011-03-15 13:30:12 +08:00
|
|
|
return declToIndex.size() == 0;
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
|
2011-03-29 09:40:00 +08:00
|
|
|
bool hasEntry(const VarDecl *vd) const {
|
|
|
|
return declToIndex.getValueIndex(vd).hasValue();
|
|
|
|
}
|
|
|
|
|
2011-04-05 04:30:58 +08:00
|
|
|
bool hasValues(const CFGBlock *block);
|
|
|
|
|
2011-01-15 10:58:47 +08:00
|
|
|
void resetScratch();
|
2011-03-15 12:57:27 +08:00
|
|
|
ValueVector &getScratch() { return scratch; }
|
2011-01-21 01:37:17 +08:00
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
ValueVector::reference operator[](const VarDecl *vd);
|
2011-01-15 10:58:47 +08:00
|
|
|
};
|
2011-03-26 20:38:21 +08:00
|
|
|
} // end anonymous namespace
|
2011-01-15 10:58:47 +08:00
|
|
|
|
|
|
|
CFGBlockValues::CFGBlockValues(const CFG &c) : cfg(c), vals(0) {
|
|
|
|
unsigned n = cfg.getNumBlockIDs();
|
|
|
|
if (!n)
|
|
|
|
return;
|
2011-03-15 12:57:27 +08:00
|
|
|
vals = new std::pair<ValueVector*, ValueVector*>[n];
|
2011-04-28 16:19:45 +08:00
|
|
|
memset((void*)vals, 0, sizeof(*vals) * n);
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
CFGBlockValues::~CFGBlockValues() {
|
|
|
|
unsigned n = cfg.getNumBlockIDs();
|
|
|
|
if (n == 0)
|
|
|
|
return;
|
2011-01-21 01:37:17 +08:00
|
|
|
for (unsigned i = 0; i < n; ++i) {
|
|
|
|
delete vals[i].first;
|
|
|
|
delete vals[i].second;
|
|
|
|
}
|
2011-01-15 10:58:47 +08:00
|
|
|
delete [] vals;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFGBlockValues::computeSetOfDeclarations(const DeclContext &dc) {
|
2011-03-15 13:30:12 +08:00
|
|
|
declToIndex.computeMap(dc);
|
|
|
|
scratch.resize(declToIndex.size());
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
ValueVector &CFGBlockValues::lazyCreate(ValueVector *&bv) {
|
2011-01-21 01:37:17 +08:00
|
|
|
if (!bv)
|
2011-03-15 13:30:12 +08:00
|
|
|
bv = new ValueVector(declToIndex.size());
|
2011-01-15 10:58:47 +08:00
|
|
|
return *bv;
|
|
|
|
}
|
|
|
|
|
2011-01-21 01:37:17 +08:00
|
|
|
/// 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 BinaryOperator *getLogicalOperatorInChain(const CFGBlock *block) {
|
|
|
|
if (block->empty())
|
|
|
|
return 0;
|
2011-02-02 01:43:18 +08:00
|
|
|
|
2011-03-01 11:15:10 +08:00
|
|
|
const CFGStmt *cstmt = block->front().getAs<CFGStmt>();
|
2011-03-15 13:22:28 +08:00
|
|
|
if (!cstmt)
|
|
|
|
return 0;
|
|
|
|
|
2011-03-01 11:15:10 +08:00
|
|
|
BinaryOperator *b = llvm::dyn_cast_or_null<BinaryOperator>(cstmt->getStmt());
|
2011-02-02 01:43:18 +08:00
|
|
|
|
|
|
|
if (!b || !b->isLogicalOp())
|
2011-01-21 01:37:17 +08:00
|
|
|
return 0;
|
2011-02-02 01:43:18 +08:00
|
|
|
|
2011-05-11 06:10:35 +08:00
|
|
|
if (block->pred_size() == 2) {
|
|
|
|
if (block->getTerminatorCondition() == b) {
|
|
|
|
if (block->succ_size() == 2)
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
else if (block->size() == 1)
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
2011-02-02 01:43:18 +08:00
|
|
|
return 0;
|
2011-01-21 01:37:17 +08:00
|
|
|
}
|
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
ValueVector &CFGBlockValues::getValueVector(const CFGBlock *block,
|
|
|
|
const CFGBlock *dstBlock) {
|
2011-01-21 01:37:17 +08:00
|
|
|
unsigned idx = block->getBlockID();
|
2011-02-02 01:43:18 +08:00
|
|
|
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);
|
2011-01-21 01:37:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
assert(vals[idx].second == 0);
|
|
|
|
return lazyCreate(vals[idx].first);
|
|
|
|
}
|
|
|
|
|
2011-04-05 04:30:58 +08:00
|
|
|
bool CFGBlockValues::hasValues(const CFGBlock *block) {
|
|
|
|
unsigned idx = block->getBlockID();
|
|
|
|
return vals[idx].second != 0;
|
|
|
|
}
|
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
BVPair &CFGBlockValues::getValueVectors(const clang::CFGBlock *block,
|
|
|
|
bool shouldLazyCreate) {
|
2011-01-21 01:37:17 +08:00
|
|
|
unsigned idx = block->getBlockID();
|
|
|
|
lazyCreate(vals[idx].first);
|
2011-02-02 01:43:18 +08:00
|
|
|
if (shouldLazyCreate)
|
|
|
|
lazyCreate(vals[idx].second);
|
2011-01-21 01:37:17 +08:00
|
|
|
return vals[idx];
|
|
|
|
}
|
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
void CFGBlockValues::mergeIntoScratch(ValueVector const &source,
|
2011-01-15 10:58:47 +08:00
|
|
|
bool isFirst) {
|
|
|
|
if (isFirst)
|
|
|
|
scratch = source;
|
|
|
|
else
|
2011-05-31 11:56:09 +08:00
|
|
|
scratch |= source;
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
2011-02-02 01:43:18 +08:00
|
|
|
#if 0
|
2011-03-15 12:57:27 +08:00
|
|
|
static void printVector(const CFGBlock *block, ValueVector &bv,
|
2011-02-02 01:43:18 +08:00
|
|
|
unsigned num) {
|
|
|
|
|
|
|
|
llvm::errs() << block->getBlockID() << " :";
|
|
|
|
for (unsigned i = 0; i < bv.size(); ++i) {
|
|
|
|
llvm::errs() << ' ' << bv[i];
|
|
|
|
}
|
|
|
|
llvm::errs() << " : " << num << '\n';
|
|
|
|
}
|
|
|
|
#endif
|
2011-01-15 10:58:47 +08:00
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
bool CFGBlockValues::updateValueVectorWithScratch(const CFGBlock *block) {
|
|
|
|
ValueVector &dst = getValueVector(block, 0);
|
2011-01-15 10:58:47 +08:00
|
|
|
bool changed = (dst != scratch);
|
|
|
|
if (changed)
|
|
|
|
dst = scratch;
|
2011-02-02 01:43:18 +08:00
|
|
|
#if 0
|
|
|
|
printVector(block, scratch, 0);
|
|
|
|
#endif
|
2011-01-21 01:37:17 +08:00
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
bool CFGBlockValues::updateValueVectors(const CFGBlock *block,
|
2011-01-21 01:37:17 +08:00
|
|
|
const BVPair &newVals) {
|
2011-03-15 12:57:27 +08:00
|
|
|
BVPair &vals = getValueVectors(block, true);
|
2011-01-21 01:37:17 +08:00
|
|
|
bool changed = *newVals.first != *vals.first ||
|
|
|
|
*newVals.second != *vals.second;
|
|
|
|
*vals.first = *newVals.first;
|
|
|
|
*vals.second = *newVals.second;
|
2011-02-02 01:43:18 +08:00
|
|
|
#if 0
|
|
|
|
printVector(block, *vals.first, 1);
|
|
|
|
printVector(block, *vals.second, 2);
|
|
|
|
#endif
|
2011-01-15 10:58:47 +08:00
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CFGBlockValues::resetScratch() {
|
|
|
|
scratch.reset();
|
|
|
|
}
|
|
|
|
|
2011-03-15 12:57:27 +08:00
|
|
|
ValueVector::reference CFGBlockValues::operator[](const VarDecl *vd) {
|
2011-03-15 13:30:12 +08:00
|
|
|
const llvm::Optional<unsigned> &idx = declToIndex.getValueIndex(vd);
|
2011-01-15 10:58:47 +08:00
|
|
|
assert(idx.hasValue());
|
|
|
|
return scratch[idx.getValue()];
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------====//
|
|
|
|
// Worklist: worklist for dataflow analysis.
|
|
|
|
//====------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
class DataflowWorklist {
|
|
|
|
llvm::SmallVector<const CFGBlock *, 20> worklist;
|
2011-03-15 12:57:32 +08:00
|
|
|
llvm::BitVector enqueuedBlocks;
|
2011-01-15 10:58:47 +08:00
|
|
|
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();
|
2011-01-15 10:58:47 +08:00
|
|
|
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;
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
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());
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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; }
|
|
|
|
};
|
|
|
|
|
2011-07-19 22:18:48 +08:00
|
|
|
class TransferFunctions : public StmtVisitor<TransferFunctions> {
|
2011-01-15 10:58:47 +08:00
|
|
|
CFGBlockValues &vals;
|
|
|
|
const CFG &cfg;
|
2011-01-26 03:13:48 +08:00
|
|
|
AnalysisContext ∾
|
2011-01-15 10:58:47 +08:00
|
|
|
UninitVariablesHandler *handler;
|
2011-01-26 03:13:48 +08:00
|
|
|
const bool flagBlockUses;
|
2011-07-19 22:18:48 +08:00
|
|
|
|
|
|
|
/// 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;
|
2011-01-15 10:58:47 +08:00
|
|
|
public:
|
|
|
|
TransferFunctions(CFGBlockValues &vals, const CFG &cfg,
|
2011-01-26 03:13:48 +08:00
|
|
|
AnalysisContext &ac,
|
|
|
|
UninitVariablesHandler *handler,
|
|
|
|
bool flagBlockUses)
|
2011-07-19 22:18:48 +08:00
|
|
|
: vals(vals), cfg(cfg), ac(ac), handler(handler),
|
|
|
|
flagBlockUses(flagBlockUses), lastDR(0), lastLoad(0) {}
|
2011-01-15 10:58:47 +08:00
|
|
|
|
|
|
|
const CFG &getCFG() { return cfg; }
|
2011-03-15 12:57:38 +08:00
|
|
|
void reportUninit(const DeclRefExpr *ex, const VarDecl *vd,
|
|
|
|
bool isAlwaysUninit);
|
2011-01-26 03:13:48 +08:00
|
|
|
|
|
|
|
void VisitBlockExpr(BlockExpr *be);
|
2011-01-15 10:58:47 +08:00
|
|
|
void VisitDeclStmt(DeclStmt *ds);
|
2011-01-19 05:18:58 +08:00
|
|
|
void VisitDeclRefExpr(DeclRefExpr *dr);
|
2011-01-15 10:58:47 +08:00
|
|
|
void VisitUnaryOperator(UnaryOperator *uo);
|
|
|
|
void VisitBinaryOperator(BinaryOperator *bo);
|
|
|
|
void VisitCastExpr(CastExpr *ce);
|
2011-07-19 22:18:48 +08:00
|
|
|
void VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs);
|
|
|
|
void Visit(Stmt *s);
|
2011-01-27 10:29:34 +08:00
|
|
|
|
|
|
|
bool isTrackedVar(const VarDecl *vd) {
|
|
|
|
return ::isTrackedVar(vd, cast<DeclContext>(ac.getDecl()));
|
|
|
|
}
|
|
|
|
|
|
|
|
FindVarResult findBlockVarDecl(Expr *ex);
|
2011-07-19 22:18:48 +08:00
|
|
|
|
|
|
|
void ProcessUses(Stmt *s = 0);
|
2011-01-15 10:58:47 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void TransferFunctions::reportUninit(const DeclRefExpr *ex,
|
2011-03-15 12:57:38 +08:00
|
|
|
const VarDecl *vd, bool isAlwaysUnit) {
|
|
|
|
if (handler) handler->handleUseOfUninitVariable(ex, vd, isAlwaysUnit);
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
|
2011-01-27 10:29:34 +08:00
|
|
|
FindVarResult TransferFunctions::findBlockVarDecl(Expr* ex) {
|
2011-01-27 10:01:31 +08:00
|
|
|
if (DeclRefExpr* dr = dyn_cast<DeclRefExpr>(ex->IgnoreParenCasts()))
|
|
|
|
if (VarDecl *vd = dyn_cast<VarDecl>(dr->getDecl()))
|
|
|
|
if (isTrackedVar(vd))
|
2011-01-27 10:29:34 +08:00
|
|
|
return FindVarResult(vd, dr);
|
2011-01-27 10:01:31 +08:00
|
|
|
return FindVarResult(0, 0);
|
|
|
|
}
|
|
|
|
|
2011-07-19 22:18:48 +08:00
|
|
|
void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs) {
|
2011-01-27 10:01:31 +08:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2011-01-26 03:13:48 +08:00
|
|
|
void TransferFunctions::VisitBlockExpr(BlockExpr *be) {
|
|
|
|
if (!flagBlockUses || !handler)
|
|
|
|
return;
|
2011-04-01 06:32:41 +08:00
|
|
|
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 (!vd->hasLocalStorage())
|
2011-01-26 03:13:48 +08:00
|
|
|
continue;
|
2011-04-01 06:32:41 +08:00
|
|
|
if (!isTrackedVar(vd))
|
|
|
|
continue;
|
|
|
|
if (i->isByRef()) {
|
|
|
|
vals[vd] = Initialized;
|
|
|
|
continue;
|
|
|
|
}
|
2011-03-15 12:57:38 +08:00
|
|
|
Value v = vals[vd];
|
|
|
|
if (isUninitialized(v))
|
|
|
|
handler->handleUseOfUninitVariable(be, vd, isAlwaysUninit(v));
|
2011-01-26 03:13:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-19 22:18:48 +08:00
|
|
|
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))
|
|
|
|
lastDR = dr;
|
|
|
|
}
|
|
|
|
|
2011-01-15 10:58:47 +08:00
|
|
|
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)) {
|
2011-01-18 13:00:42 +08:00
|
|
|
if (isTrackedVar(vd)) {
|
2011-04-06 05:36:30 +08:00
|
|
|
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.
|
2011-07-19 22:18:48 +08:00
|
|
|
if (init == lastLoad) {
|
2011-07-20 04:33:49 +08:00
|
|
|
DeclRefExpr *DR =
|
|
|
|
cast<DeclRefExpr>(lastLoad->getSubExpr()->IgnoreParens());
|
2011-07-20 05:41:51 +08:00
|
|
|
if (DR->getDecl() == vd) {
|
|
|
|
// int x = x;
|
|
|
|
// Propagate uninitialized value, but don't immediately report
|
|
|
|
// a problem.
|
|
|
|
vals[vd] = Uninitialized;
|
|
|
|
lastLoad = 0;
|
2011-07-19 22:18:48 +08:00
|
|
|
lastDR = 0;
|
2011-07-20 05:41:51 +08:00
|
|
|
return;
|
|
|
|
}
|
2011-07-19 22:18:48 +08:00
|
|
|
}
|
2011-07-20 05:41:51 +08:00
|
|
|
|
|
|
|
// All other cases: treat the new variable as initialized.
|
|
|
|
vals[vd] = Initialized;
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
2011-01-19 05:18:58 +08:00
|
|
|
}
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TransferFunctions::VisitBinaryOperator(clang::BinaryOperator *bo) {
|
|
|
|
if (bo->isAssignmentOp()) {
|
|
|
|
const FindVarResult &res = findBlockVarDecl(bo->getLHS());
|
|
|
|
if (const VarDecl* vd = res.getDecl()) {
|
2011-03-15 12:57:32 +08:00
|
|
|
ValueVector::reference val = vals[vd];
|
2011-03-15 12:57:38 +08:00
|
|
|
if (isUninitialized(val)) {
|
2011-07-17 06:27:02 +08:00
|
|
|
if (bo->getOpcode() != BO_Assign)
|
2011-03-15 12:57:38 +08:00
|
|
|
reportUninit(res.getDeclRefExpr(), vd, isAlwaysUninit(val));
|
2011-07-17 06:27:02 +08:00
|
|
|
val = Initialized;
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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()) {
|
2011-07-19 22:18:48 +08:00
|
|
|
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;
|
2011-01-19 05:18:58 +08:00
|
|
|
|
2011-03-15 12:57:38 +08:00
|
|
|
ValueVector::reference val = vals[vd];
|
|
|
|
if (isUninitialized(val)) {
|
|
|
|
reportUninit(res.getDeclRefExpr(), vd, isAlwaysUninit(val));
|
|
|
|
// Don't cascade warnings.
|
2011-07-17 06:27:02 +08:00
|
|
|
val = Initialized;
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TransferFunctions::VisitCastExpr(clang::CastExpr *ce) {
|
|
|
|
if (ce->getCastKind() == CK_LValueToRValue) {
|
|
|
|
const FindVarResult &res = findBlockVarDecl(ce->getSubExpr());
|
2011-01-19 05:18:58 +08:00
|
|
|
if (const VarDecl *vd = res.getDecl()) {
|
2011-07-19 22:18:48 +08:00
|
|
|
assert(res.getDeclRefExpr() == lastDR);
|
|
|
|
if (isUninitialized(vals[vd])) {
|
|
|
|
// Record this load of an uninitialized value. Normally this
|
|
|
|
// results in a warning, but we delay reporting the issue
|
|
|
|
// in case it is wrapped in a void cast, etc.
|
|
|
|
lastLoad = ce;
|
2011-01-19 05:18:58 +08:00
|
|
|
}
|
|
|
|
}
|
2011-01-26 12:49:43 +08:00
|
|
|
}
|
|
|
|
else if (CStyleCastExpr *cse = dyn_cast<CStyleCastExpr>(ce)) {
|
|
|
|
if (cse->getType()->isVoidType()) {
|
|
|
|
// e.g. (void) x;
|
2011-07-19 22:18:48 +08:00
|
|
|
if (lastLoad == cse->getSubExpr()) {
|
|
|
|
// Squelch any detected load of an uninitialized value if
|
|
|
|
// we cast it to void.
|
|
|
|
lastLoad = 0;
|
|
|
|
lastDR = 0;
|
|
|
|
}
|
2011-01-26 12:49:43 +08:00
|
|
|
}
|
|
|
|
}
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
|
2011-07-19 22:18:48 +08:00
|
|
|
void TransferFunctions::Visit(clang::Stmt *s) {
|
|
|
|
StmtVisitor<TransferFunctions>::Visit(s);
|
|
|
|
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)
|
2011-01-24 01:53:04 +08:00
|
|
|
return;
|
2011-07-19 22:18:48 +08:00
|
|
|
|
|
|
|
// If we reach here, we 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.
|
2011-07-20 04:33:49 +08:00
|
|
|
DeclRefExpr *DR = cast<DeclRefExpr>(lastLoad->getSubExpr()->IgnoreParens());
|
2011-07-19 22:18:48 +08:00
|
|
|
VarDecl *VD = cast<VarDecl>(DR->getDecl());
|
|
|
|
reportUninit(DR, VD, isAlwaysUninit(vals[VD]));
|
|
|
|
lastLoad = 0;
|
|
|
|
|
|
|
|
// Prevent cascade of warnings.
|
|
|
|
vals[VD] = Initialized;
|
|
|
|
if (DR == lastDR) {
|
|
|
|
lastDR = 0;
|
|
|
|
return;
|
|
|
|
}
|
2011-01-24 01:53:04 +08:00
|
|
|
}
|
|
|
|
|
2011-07-19 22:18:48 +08:00
|
|
|
// 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;
|
2011-04-13 16:18:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-15 10:58:47 +08:00
|
|
|
//------------------------------------------------------------------------====//
|
|
|
|
// High-level "driver" logic for uninitialized values analysis.
|
|
|
|
//====------------------------------------------------------------------------//
|
|
|
|
|
2011-01-21 01:37:17 +08:00
|
|
|
static bool runOnBlock(const CFGBlock *block, const CFG &cfg,
|
2011-01-26 03:13:48 +08:00
|
|
|
AnalysisContext &ac, CFGBlockValues &vals,
|
2011-04-05 04:30:58 +08:00
|
|
|
llvm::BitVector &wasAnalyzed,
|
2011-01-26 03:13:48 +08:00
|
|
|
UninitVariablesHandler *handler = 0,
|
|
|
|
bool flagBlockUses = false) {
|
2011-01-21 01:37:17 +08:00
|
|
|
|
2011-04-05 04:30:58 +08:00
|
|
|
wasAnalyzed[block->getBlockID()] = true;
|
|
|
|
|
2011-01-21 01:37:17 +08:00
|
|
|
if (const BinaryOperator *b = getLogicalOperatorInChain(block)) {
|
2011-02-02 01:43:18 +08:00
|
|
|
CFGBlock::const_pred_iterator itr = block->pred_begin();
|
2011-03-15 12:57:27 +08:00
|
|
|
BVPair vA = vals.getValueVectors(*itr, false);
|
2011-02-02 01:43:18 +08:00
|
|
|
++itr;
|
2011-03-15 12:57:27 +08:00
|
|
|
BVPair vB = vals.getValueVectors(*itr, false);
|
2011-02-02 01:43:18 +08:00
|
|
|
|
|
|
|
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;
|
2011-01-21 05:25:31 +08:00
|
|
|
valsAB.second = &vals.getScratch();
|
2011-01-21 01:37:17 +08:00
|
|
|
}
|
2011-02-02 01:43:18 +08:00
|
|
|
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;
|
|
|
|
}
|
2011-03-15 12:57:27 +08:00
|
|
|
return vals.updateValueVectors(block, valsAB);
|
2011-01-21 01:37:17 +08:00
|
|
|
}
|
|
|
|
|
2011-02-02 01:43:18 +08:00
|
|
|
// Default behavior: merge in values of predecessor blocks.
|
2011-01-15 10:58:47 +08:00
|
|
|
vals.resetScratch();
|
|
|
|
bool isFirst = true;
|
|
|
|
for (CFGBlock::const_pred_iterator I = block->pred_begin(),
|
|
|
|
E = block->pred_end(); I != E; ++I) {
|
2011-03-15 12:57:27 +08:00
|
|
|
vals.mergeIntoScratch(vals.getValueVector(*I, block), isFirst);
|
2011-01-15 10:58:47 +08:00
|
|
|
isFirst = false;
|
|
|
|
}
|
|
|
|
// Apply the transfer function.
|
2011-01-26 03:13:48 +08:00
|
|
|
TransferFunctions tf(vals, cfg, ac, handler, flagBlockUses);
|
2011-01-15 10:58:47 +08:00
|
|
|
for (CFGBlock::const_iterator I = block->begin(), E = block->end();
|
|
|
|
I != E; ++I) {
|
|
|
|
if (const CFGStmt *cs = dyn_cast<CFGStmt>(&*I)) {
|
2011-07-19 22:18:48 +08:00
|
|
|
tf.Visit(cs->getStmt());
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
}
|
2011-07-19 22:18:48 +08:00
|
|
|
tf.ProcessUses();
|
2011-03-15 12:57:27 +08:00
|
|
|
return vals.updateValueVectorWithScratch(block);
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
|
2011-07-07 00:21:37 +08:00
|
|
|
void clang::runUninitializedVariablesAnalysis(
|
|
|
|
const DeclContext &dc,
|
|
|
|
const CFG &cfg,
|
|
|
|
AnalysisContext &ac,
|
|
|
|
UninitVariablesHandler &handler,
|
|
|
|
UninitVariablesAnalysisStats &stats) {
|
2011-01-15 10:58:47 +08:00
|
|
|
CFGBlockValues vals(cfg);
|
|
|
|
vals.computeSetOfDeclarations(dc);
|
|
|
|
if (vals.hasNoDeclarations())
|
|
|
|
return;
|
2011-04-05 07:29:12 +08:00
|
|
|
|
2011-07-07 00:21:37 +08:00
|
|
|
stats.NumVariablesAnalyzed = vals.getNumEntries();
|
|
|
|
|
2011-04-05 07:29:12 +08:00
|
|
|
// 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.
|
2011-01-15 10:58:47 +08:00
|
|
|
DataflowWorklist worklist(cfg);
|
2011-03-15 12:57:32 +08:00
|
|
|
llvm::BitVector previouslyVisited(cfg.getNumBlockIDs());
|
2011-01-15 10:58:47 +08:00
|
|
|
worklist.enqueueSuccessors(&cfg.getEntry());
|
2011-04-05 04:30:58 +08:00
|
|
|
llvm::BitVector wasAnalyzed(cfg.getNumBlockIDs(), false);
|
2011-01-15 10:58:47 +08:00
|
|
|
|
|
|
|
while (const CFGBlock *block = worklist.dequeue()) {
|
|
|
|
// Did the block change?
|
2011-07-07 00:21:37 +08:00
|
|
|
bool changed = runOnBlock(block, cfg, ac, vals, wasAnalyzed);
|
|
|
|
++stats.NumBlockVisits;
|
2011-01-15 10:58:47 +08:00
|
|
|
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) {
|
2011-07-07 00:21:37 +08:00
|
|
|
if (wasAnalyzed[(*BI)->getBlockID()]) {
|
2011-04-05 04:30:58 +08:00
|
|
|
runOnBlock(*BI, cfg, ac, vals, wasAnalyzed, &handler,
|
|
|
|
/* flagBlockUses */ true);
|
2011-07-07 00:21:37 +08:00
|
|
|
++stats.NumBlockVisits;
|
|
|
|
}
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UninitVariablesHandler::~UninitVariablesHandler() {}
|