Rework CFG edges to encode potentially unreachable edges, instead of just making them NULL.

This is to support some analyses, like -Wunreachable-code, that
will need to recover the original unprunned CFG edges in order
to suppress issues that aren't really bugs in practice.

There are two important changes here:

- AdjacentBlock replaces CFGBlock* for CFG successors/predecessors.
  This has the size of 2 pointers, instead of 1.  This is unlikely
  to have a significant memory impact on Sema since a single
  CFG usually exists at one time, but could impact the memory
  usage of the static analyzer.  This could possibly be optimized
  down to a single pointer with some cleverness.

- Predecessors can now contain null predecessors, which means
  some analyses doing a reverse traversal will need to take into
  account.  This already exists for successors, which contain
  successor slots for specific branch kinds (e.g., 'if') that
  expect a fixed number of successors, even if a branch is
  not reachable.

llvm-svn: 202325
This commit is contained in:
Ted Kremenek 2014-02-27 00:24:00 +00:00
parent 2eab2bd86d
commit 4b6fee6cc4
4 changed files with 113 additions and 12 deletions

View File

@ -412,9 +412,64 @@ class CFGBlock {
/// of the CFG.
unsigned BlockID;
public:
/// This class represents a potential adjacent block in the CFG. It encodes
/// whether or not the block is actually reachable, or can be proved to be
/// trivially unreachable. For some cases it allows one to encode scenarios
/// where a block was substituted because the original (now alternate) block
/// is unreachable.
class AdjacentBlock {
enum Kind {
AB_Normal,
AB_Unreachable,
AB_Alternate
};
CFGBlock *ReachableBlock;
llvm::PointerIntPair<CFGBlock*, 2> UnreachableBlock;
public:
/// Construct an AdjacentBlock with a possibly unreachable block.
AdjacentBlock(CFGBlock *B, bool IsReachable);
/// Construct an AdjacentBlock with a reachable block and an alternate
/// unreachable block.
AdjacentBlock(CFGBlock *B, CFGBlock *AlternateBlock);
/// Get the reachable block, if one exists.
CFGBlock *getReachableBlock() const {
return ReachableBlock;
}
/// Get the potentially unreachable block.
CFGBlock *getPossiblyUnreachableBlock() const {
return UnreachableBlock.getPointer();
}
/// Provide an implicit conversion to CFGBlock* so that
/// AdjacentBlock can be substituted for CFGBlock*.
operator CFGBlock*() const {
return getReachableBlock();
}
CFGBlock& operator *() const {
return *getReachableBlock();
}
CFGBlock* operator ->() const {
return getReachableBlock();
}
bool isReachable() const {
Kind K = (Kind) UnreachableBlock.getInt();
return K == AB_Normal || K == AB_Alternate;
}
};
private:
/// Predecessors/Successors - Keep track of the predecessor / successor
/// CFG blocks.
typedef BumpVector<CFGBlock*> AdjacentBlocks;
typedef BumpVector<AdjacentBlock> AdjacentBlocks;
AdjacentBlocks Preds;
AdjacentBlocks Succs;
@ -504,9 +559,11 @@ public:
class FilterOptions {
public:
FilterOptions() {
IgnoreNullPredecessors = 1;
IgnoreDefaultsWithCoveredEnums = 0;
}
unsigned IgnoreNullPredecessors : 1;
unsigned IgnoreDefaultsWithCoveredEnums : 1;
};
@ -588,11 +645,8 @@ public:
OS << "BB#" << getBlockID();
}
void addSuccessor(CFGBlock *Block, BumpVectorContext &C) {
if (Block)
Block->Preds.push_back(this, C);
Succs.push_back(Block, C);
}
/// Adds a (potentially unreachable) successor block to the current block.
void addSuccessor(AdjacentBlock Succ, BumpVectorContext &C);
void appendStmt(Stmt *statement, BumpVectorContext &C) {
Elements.push_back(CFGStmt(statement), C);

View File

@ -483,8 +483,16 @@ private:
void prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk,
LocalScope::const_iterator B, LocalScope::const_iterator E);
void addSuccessor(CFGBlock *B, CFGBlock *S) {
B->addSuccessor(S, cfg->getBumpVectorContext());
void addSuccessor(CFGBlock *B, CFGBlock *S, bool IsReachable = true) {
B->addSuccessor(CFGBlock::AdjacentBlock(S, IsReachable),
cfg->getBumpVectorContext());
}
/// Add a reachable successor to a block, with the alternate variant that is
/// unreachable.
void addSuccessor(CFGBlock *B, CFGBlock *ReachableBlock, CFGBlock *AltBlock) {
B->addSuccessor(CFGBlock::AdjacentBlock(ReachableBlock, AltBlock),
cfg->getBumpVectorContext());
}
/// Try and evaluate an expression to an integer constant.
@ -3495,13 +3503,37 @@ bool CFGImplicitDtor::isNoReturn(ASTContext &astContext) const {
}
//===----------------------------------------------------------------------===//
// Filtered walking of the CFG.
// CFGBlock operations.
//===----------------------------------------------------------------------===//
CFGBlock::AdjacentBlock::AdjacentBlock(CFGBlock *B, bool IsReachable)
: ReachableBlock(IsReachable ? B : 0),
UnreachableBlock(!IsReachable ? B : 0,
B && IsReachable ? AB_Normal : AB_Unreachable) {}
CFGBlock::AdjacentBlock::AdjacentBlock(CFGBlock *B, CFGBlock *AlternateBlock)
: ReachableBlock(B),
UnreachableBlock(B == AlternateBlock ? 0 : AlternateBlock,
B == AlternateBlock ? AB_Alternate : AB_Normal) {}
void CFGBlock::addSuccessor(AdjacentBlock Succ,
BumpVectorContext &C) {
if (CFGBlock *B = Succ.getReachableBlock())
B->Preds.push_back(CFGBlock::AdjacentBlock(this, Succ.isReachable()), C);
if (CFGBlock *UnreachableB = Succ.getPossiblyUnreachableBlock())
UnreachableB->Preds.push_back(CFGBlock::AdjacentBlock(this, false), C);
Succs.push_back(Succ, C);
}
bool CFGBlock::FilterEdge(const CFGBlock::FilterOptions &F,
const CFGBlock *From, const CFGBlock *To) {
if (To && F.IgnoreDefaultsWithCoveredEnums) {
if (F.IgnoreNullPredecessors && !From)
return true;
if (To && From && F.IgnoreDefaultsWithCoveredEnums) {
// If the 'To' has no label or is labeled but the label isn't a
// CaseStmt then filter this edge.
if (const SwitchStmt *S =
@ -3963,7 +3995,16 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
if (i % 10 == 8)
OS << "\n ";
OS << " B" << (*I)->getBlockID();
CFGBlock *B = *I;
bool Reachable = true;
if (!B) {
Reachable = false;
B = I->getPossiblyUnreachableBlock();
}
OS << " B" << B->getBlockID();
if (!Reachable)
OS << "(Unreachable)";
}
if (ShowColors)

View File

@ -69,7 +69,8 @@ void CFGReverseBlockReachabilityAnalysis::mapReachability(const CFGBlock *Dst) {
// Add the predecessors to the worklist.
for (CFGBlock::const_pred_iterator i = block->pred_begin(),
e = block->pred_end(); i != e; ++i) {
worklist.push_back(*i);
if (*i)
worklist.push_back(*i);
}
}
}

View File

@ -535,6 +535,9 @@ public:
for (CFGBlock::const_pred_iterator I = B->pred_begin(), E = B->pred_end();
I != E; ++I) {
const CFGBlock *Pred = *I;
if (!Pred)
continue;
Value AtPredExit = vals.getValue(Pred, B, vd);
if (AtPredExit == Initialized)
// This block initializes the variable.
@ -751,6 +754,8 @@ static bool runOnBlock(const CFGBlock *block, const CFG &cfg,
for (CFGBlock::const_pred_iterator I = block->pred_begin(),
E = block->pred_end(); I != E; ++I) {
const CFGBlock *pred = *I;
if (!pred)
continue;
if (wasAnalyzed[pred->getBlockID()]) {
vals.mergeIntoScratch(vals.getValueVector(pred), isFirst);
isFirst = false;