[analyzer] Overhaul how the static analyzer expects CFGs by forcing CFGs to be linearized only when used by the static analyzer. This required a rewrite of LiveVariables, and exposed a ton of subtle bugs.

The motivation of this large change is to drastically simplify the logic in ExprEngine going forward.

Some fallout is that the output of some BugReporterVisitors is not as accurate as before; those will
need to be fixed over time.  There is also some possible performance regression as RemoveDeadBindings
will be called frequently; this can also be improved over time.

llvm-svn: 136419
This commit is contained in:
Ted Kremenek 2011-07-28 23:07:59 +00:00
parent e227f49302
commit e9fda1e48a
20 changed files with 2087 additions and 1889 deletions

View File

@ -1,4 +1,4 @@
//===- LiveVariables.h - Live Variable Analysis for Source CFGs -*- C++ --*-===//
//===- LiveVariables.h - Live Variable Analysis for Source CFGs -*- C++ --*-//
//
// The LLVM Compiler Infrastructure
//
@ -15,109 +15,89 @@
#define LLVM_CLANG_LIVEVARIABLES_H
#include "clang/AST/Decl.h"
#include "clang/Analysis/Support/BlkExprDeclBitVector.h"
#include "clang/Analysis/FlowSensitive/DataflowValues.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ImmutableSet.h"
namespace clang {
class CFG;
class CFGBlock;
class Stmt;
class DeclRefExpr;
class SourceManager;
class AnalysisContext;
struct LiveVariables_ValueTypes {
struct ObserverTy;
// We keep dataflow state for declarations and block-level expressions;
typedef StmtDeclBitVector_Types::ValTy ValTy;
// We need to keep track of both declarations and CFGBlock-level expressions,
// (so that we don't explore such expressions twice). We also want
// to compute liveness information for block-level expressions, since these
// act as "temporary" values.
struct AnalysisDataTy : public StmtDeclBitVector_Types::AnalysisDataTy {
ObserverTy* Observer;
ValTy AlwaysLive;
AnalysisContext *AC;
bool killAtAssign;
AnalysisDataTy() : Observer(NULL), AC(NULL), killAtAssign(true) {}
};
//===-----------------------------------------------------===//
// ObserverTy - Observer for uninitialized values queries.
//===-----------------------------------------------------===//
struct ObserverTy {
virtual ~ObserverTy() {}
/// ObserveStmt - A callback invoked right before invoking the
/// liveness transfer function on the given statement.
virtual void ObserveStmt(Stmt* S, const CFGBlock *currentBlock,
const AnalysisDataTy& AD,
const ValTy& V) {}
virtual void ObserverKill(DeclRefExpr* DR) {}
};
};
class LiveVariables : public DataflowValues<LiveVariables_ValueTypes,
dataflow::backward_analysis_tag> {
class LiveVariables {
public:
typedef LiveVariables_ValueTypes::ObserverTy ObserverTy;
class LivenessValues {
public:
LiveVariables(AnalysisContext &AC, bool killAtAssign = true);
llvm::ImmutableSet<const Stmt *> liveStmts;
llvm::ImmutableSet<const VarDecl *> liveDecls;
bool equals(const LivenessValues &V) const;
/// IsLive - Return true if a variable is live at the end of a
LivenessValues()
: liveStmts(0), liveDecls(0) {}
LivenessValues(llvm::ImmutableSet<const Stmt *> LiveStmts,
llvm::ImmutableSet<const VarDecl *> LiveDecls)
: liveStmts(LiveStmts), liveDecls(LiveDecls) {}
~LivenessValues() {}
bool isLive(const Stmt *S) const;
bool isLive(const VarDecl *D) const;
friend class LiveVariables;
};
struct Observer {
virtual ~Observer() {}
/// A callback invoked right before invoking the
/// liveness transfer function on the given statement.
virtual void observeStmt(const Stmt* S,
const CFGBlock *currentBlock,
const LivenessValues& V) {}
/// Called when the live variables analysis registers
/// that a variable is killed.
virtual void observerKill(const DeclRefExpr* DR) {}
};
~LiveVariables();
/// Compute the liveness information for a given CFG.
static LiveVariables *computeLiveness(AnalysisContext &analysisContext,
bool killAtAssign = true);
/// Return true if a variable is live at the end of a
/// specified block.
bool isLive(const CFGBlock* B, const VarDecl* D) const;
/// IsLive - Returns true if a variable is live at the beginning of the
bool isLive(const CFGBlock* B, const VarDecl* D);
/// Returns true if a variable is live at the beginning of the
/// the statement. This query only works if liveness information
/// has been recorded at the statement level (see runOnAllBlocks), and
/// only returns liveness information for block-level expressions.
bool isLive(const Stmt* S, const VarDecl* D) const;
/// IsLive - Returns true the block-level expression "value" is live
bool isLive(const Stmt* S, const VarDecl* D);
/// Returns true the block-level expression "value" is live
/// before the given block-level expression (see runOnAllBlocks).
bool isLive(const Stmt* Loc, const Stmt* StmtVal) const;
bool isLive(const Stmt* Loc, const Stmt* StmtVal);
/// Print to stderr the liveness information associated with
/// each basic block.
void dumpBlockLiveness(const SourceManager& M);
/// IsLive - Return true if a variable is live according to the
/// provided livness bitvector.
bool isLive(const ValTy& V, const VarDecl* D) const;
void runOnAllBlocks(Observer &obs);
/// dumpLiveness - Print to stderr the liveness information encoded
/// by a specified bitvector.
void dumpLiveness(const ValTy& V, const SourceManager& M) const;
/// dumpBlockLiveness - Print to stderr the liveness information
/// associated with each basic block.
void dumpBlockLiveness(const SourceManager& M) const;
/// getNumDecls - Return the number of variables (declarations) that
/// whose liveness status is being tracked by the dataflow
/// analysis.
unsigned getNumDecls() const { return getAnalysisData().getNumDecls(); }
/// IntializeValues - This routine can perform extra initialization, but
/// for LiveVariables this does nothing since all that logic is in
/// the constructor.
void InitializeValues(const CFG& cfg) {}
void runOnCFG(CFG& cfg);
/// runOnAllBlocks - Propagate the dataflow values once for each block,
/// starting from the current dataflow values. 'recordStmtValues' indicates
/// whether the method should store dataflow values per each individual
/// block-level expression.
void runOnAllBlocks(const CFG& cfg, ObserverTy* Obs,
bool recordStmtValues=false);
private:
LiveVariables(void *impl);
void *impl;
};
} // end namespace clang
#endif

View File

@ -545,8 +545,13 @@ public:
return alwaysAddMask[stmt->getStmtClass()];
}
BuildOptions &setAlwaysAdd(Stmt::StmtClass stmtClass) {
alwaysAddMask[stmtClass] = true;
BuildOptions &setAlwaysAdd(Stmt::StmtClass stmtClass, bool val = true) {
alwaysAddMask[stmtClass] = val;
return *this;
}
BuildOptions &setAllAlwaysAdd() {
alwaysAddMask.set();
return *this;
}

View File

@ -19,9 +19,11 @@
#include "clang/AST/Expr.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DenseMap.h"
namespace llvm {
class BumpPtrAllocator;
@ -40,10 +42,10 @@ namespace ento {
class SymExpr : public llvm::FoldingSetNode {
public:
enum Kind { BEGIN_SYMBOLS,
RegionValueKind, ConjuredKind, DerivedKind, ExtentKind,
enum Kind { RegionValueKind, ConjuredKind, DerivedKind, ExtentKind,
MetadataKind,
END_SYMBOLS,
BEGIN_SYMBOLS = RegionValueKind,
END_SYMBOLS = MetadataKind,
SymIntKind, SymSymKind };
private:
Kind K;
@ -84,7 +86,7 @@ public:
// Implement isa<T> support.
static inline bool classof(const SymExpr* SE) {
Kind k = SE->getKind();
return k > BEGIN_SYMBOLS && k < END_SYMBOLS;
return k >= BEGIN_SYMBOLS && k <= END_SYMBOLS;
}
};
@ -419,10 +421,13 @@ class SymbolReaper {
const LocationContext *LCtx;
const Stmt *Loc;
SymbolManager& SymMgr;
StoreRef reapedStore;
llvm::DenseMap<const MemRegion *, unsigned> includedRegionCache;
public:
SymbolReaper(const LocationContext *ctx, const Stmt *s, SymbolManager& symmgr)
: LCtx(ctx), Loc(s), SymMgr(symmgr) {}
SymbolReaper(const LocationContext *ctx, const Stmt *s, SymbolManager& symmgr,
StoreManager &storeMgr)
: LCtx(ctx), Loc(s), SymMgr(symmgr), reapedStore(0, storeMgr) {}
~SymbolReaper() {}
@ -431,7 +436,7 @@ public:
bool isLive(SymbolRef sym);
bool isLive(const Stmt *ExprVal) const;
bool isLive(const VarRegion *VR) const;
bool isLive(const VarRegion *VR, bool includeStoreBindings = false) const;
// markLive - Unconditionally marks a symbol as live. This should never be
// used by checkers, only by the state infrastructure such as the store and
@ -464,6 +469,10 @@ public:
bool isDead(SymbolRef sym) const {
return TheDead.count(sym);
}
/// Set to the value of the symbolic store after
/// StoreManager::removeDeadBindings has been called.
void setReapedStore(StoreRef st) { reapedStore = st; }
};
class SymbolVisitor {

View File

@ -177,25 +177,14 @@ PseudoConstantAnalysis *AnalysisContext::getPseudoConstantAnalysis() {
}
LiveVariables *AnalysisContext::getLiveVariables() {
if (!liveness) {
if (CFG *c = getCFG()) {
liveness.reset(new LiveVariables(*this));
liveness->runOnCFG(*c);
liveness->runOnAllBlocks(*c, 0, true);
}
}
if (!liveness)
liveness.reset(LiveVariables::computeLiveness(*this));
return liveness.get();
}
LiveVariables *AnalysisContext::getRelaxedLiveVariables() {
if (!relaxedLiveness)
if (CFG *c = getCFG()) {
relaxedLiveness.reset(new LiveVariables(*this, false));
relaxedLiveness->runOnCFG(*c);
relaxedLiveness->runOnAllBlocks(*c, 0, true);
}
relaxedLiveness.reset(LiveVariables::computeLiveness(*this, false));
return relaxedLiveness.get();
}

View File

@ -1,392 +1,486 @@
//=- LiveVariables.cpp - Live Variable Analysis for Source CFGs -*- 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 Live Variables analysis for source-level CFGs.
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Basic/SourceManager.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Stmt.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h"
#include "clang/Analysis/FlowSensitive/DataflowSolver.h"
#include "clang/Analysis/Support/SaveAndRestore.h"
#include "clang/Analysis/AnalysisContext.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/AST/StmtVisitor.h"
#include <deque>
#include <algorithm>
#include <vector>
using namespace clang;
//===----------------------------------------------------------------------===//
// Useful constants.
//===----------------------------------------------------------------------===//
static const bool Alive = true;
static const bool Dead = false;
//===----------------------------------------------------------------------===//
// Dataflow initialization logic.
//===----------------------------------------------------------------------===//
namespace {
class RegisterDecls
: public CFGRecStmtDeclVisitor<RegisterDecls> {
class LiveVariablesImpl {
public:
AnalysisContext &analysisContext;
std::vector<LiveVariables::LivenessValues> cfgBlockValues;
llvm::ImmutableSet<const Stmt *>::Factory SSetFact;
llvm::ImmutableSet<const VarDecl *>::Factory DSetFact;
llvm::DenseMap<const CFGBlock *, LiveVariables::LivenessValues> blocksEndToLiveness;
llvm::DenseMap<const CFGBlock *, LiveVariables::LivenessValues> blocksBeginToLiveness;
llvm::DenseMap<const Stmt *, LiveVariables::LivenessValues> stmtsToLiveness;
llvm::DenseMap<const DeclRefExpr *, unsigned> inAssignment;
const bool killAtAssign;
LiveVariables::LivenessValues
merge(LiveVariables::LivenessValues valsA,
LiveVariables::LivenessValues valsB);
LiveVariables::LivenessValues runOnBlock(const CFGBlock *block,
LiveVariables::LivenessValues val,
LiveVariables::Observer *obs = 0);
LiveVariables::AnalysisDataTy& AD;
void dumpBlockLiveness(const SourceManager& M);
typedef SmallVector<VarDecl*, 20> AlwaysLiveTy;
AlwaysLiveTy AlwaysLive;
LiveVariablesImpl(AnalysisContext &ac, bool KillAtAssign)
: analysisContext(ac), killAtAssign(KillAtAssign) {}
};
}
public:
RegisterDecls(LiveVariables::AnalysisDataTy& ad) : AD(ad) {}
~RegisterDecls() {
AD.AlwaysLive.resetValues(AD);
for (AlwaysLiveTy::iterator I = AlwaysLive.begin(), E = AlwaysLive.end();
I != E; ++ I)
AD.AlwaysLive(*I, AD) = Alive;
}
void VisitImplicitParamDecl(ImplicitParamDecl* IPD) {
// Register the VarDecl for tracking.
AD.Register(IPD);
}
void VisitVarDecl(VarDecl* VD) {
// Register the VarDecl for tracking.
AD.Register(VD);
// Does the variable have global storage? If so, it is always live.
if (VD->hasGlobalStorage())
AlwaysLive.push_back(VD);
}
CFG& getCFG() { return AD.getCFG(); }
};
} // end anonymous namespace
LiveVariables::LiveVariables(AnalysisContext &AC, bool killAtAssign) {
// Register all referenced VarDecls.
CFG &cfg = *AC.getCFG();
getAnalysisData().setCFG(cfg);
getAnalysisData().setContext(AC.getASTContext());
getAnalysisData().AC = &AC;
getAnalysisData().killAtAssign = killAtAssign;
RegisterDecls R(getAnalysisData());
cfg.VisitBlockStmts(R);
// Register all parameters even if they didn't occur in the function body.
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(AC.getDecl()))
for (FunctionDecl::param_const_iterator PI = FD->param_begin(),
PE = FD->param_end(); PI != PE; ++PI)
getAnalysisData().Register(*PI);
static LiveVariablesImpl &getImpl(void* x) {
return *((LiveVariablesImpl *) x);
}
//===----------------------------------------------------------------------===//
// Transfer functions.
// Operations and queries on LivenessValues.
//===----------------------------------------------------------------------===//
bool LiveVariables::LivenessValues::isLive(const Stmt *S) const {
return liveStmts.contains(S);
}
bool LiveVariables::LivenessValues::isLive(const VarDecl *D) const {
return liveDecls.contains(D);
}
namespace {
template <typename SET>
SET mergeSets(typename SET::Factory &F, SET A, SET B) {
for (typename SET::iterator it = B.begin(), ei = B.end(); it != ei; ++it) {
A = F.add(A, *it);
}
return A;
}
}
LiveVariables::LivenessValues
LiveVariablesImpl::merge(LiveVariables::LivenessValues valsA,
LiveVariables::LivenessValues valsB) {
return LiveVariables::LivenessValues(mergeSets(SSetFact, valsA.liveStmts, valsB.liveStmts),
mergeSets(DSetFact, valsA.liveDecls, valsB.liveDecls));
}
bool LiveVariables::LivenessValues::equals(const LivenessValues &V) const {
return liveStmts == V.liveStmts && liveDecls == V.liveDecls;
}
//===----------------------------------------------------------------------===//
// Query methods.
//===----------------------------------------------------------------------===//
static bool isAlwaysAlive(const VarDecl *D) {
return D->hasGlobalStorage();
}
bool LiveVariables::isLive(const CFGBlock *B, const VarDecl *D) {
return isAlwaysAlive(D) || getImpl(impl).blocksEndToLiveness[B].isLive(D);
}
bool LiveVariables::isLive(const Stmt *S, const VarDecl *D) {
return isAlwaysAlive(D) || getImpl(impl).stmtsToLiveness[S].isLive(D);
}
bool LiveVariables::isLive(const Stmt *Loc, const Stmt *S) {
return getImpl(impl).stmtsToLiveness[Loc].isLive(S);
}
//===----------------------------------------------------------------------===//
// Dataflow computation.
//===----------------------------------------------------------------------===//
namespace {
class TransferFuncs : public CFGRecStmtVisitor<TransferFuncs>{
LiveVariables::AnalysisDataTy& AD;
LiveVariables::ValTy LiveState;
class Worklist {
llvm::BitVector isBlockEnqueued;
std::deque<const CFGBlock *> workListContents;
public:
Worklist(CFG &cfg) : isBlockEnqueued(cfg.getNumBlockIDs()) {}
bool empty() const { return workListContents.empty(); }
const CFGBlock *getNextItem() {
const CFGBlock *block = workListContents.front();
workListContents.pop_front();
isBlockEnqueued[block->getBlockID()] = false;
return block;
}
void enqueueBlock(const CFGBlock *block) {
if (!isBlockEnqueued[block->getBlockID()]) {
isBlockEnqueued[block->getBlockID()] = true;
workListContents.push_back(block);
}
}
};
class TransferFunctions : public StmtVisitor<TransferFunctions> {
LiveVariablesImpl &LV;
LiveVariables::LivenessValues &val;
LiveVariables::Observer *observer;
const CFGBlock *currentBlock;
public:
TransferFuncs(LiveVariables::AnalysisDataTy& ad) : AD(ad), currentBlock(0) {}
TransferFunctions(LiveVariablesImpl &im,
LiveVariables::LivenessValues &Val,
LiveVariables::Observer *Observer,
const CFGBlock *CurrentBlock)
: LV(im), val(Val), observer(Observer), currentBlock(CurrentBlock) {}
LiveVariables::ValTy& getVal() { return LiveState; }
CFG& getCFG() { return AD.getCFG(); }
void VisitDeclRefExpr(DeclRefExpr* DR);
void VisitBinaryOperator(BinaryOperator* B);
void VisitBlockExpr(BlockExpr *B);
void VisitAssign(BinaryOperator* B);
void VisitDeclStmt(DeclStmt* DS);
void BlockStmt_VisitObjCForCollectionStmt(ObjCForCollectionStmt* S);
void VisitUnaryOperator(UnaryOperator* U);
void VisitBinaryOperator(BinaryOperator *BO);
void VisitBlockExpr(BlockExpr *BE);
void VisitDeclRefExpr(DeclRefExpr *DR);
void VisitDeclStmt(DeclStmt *DS);
void VisitObjCForCollectionStmt(ObjCForCollectionStmt *OS);
void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UE);
void VisitUnaryOperator(UnaryOperator *UO);
void Visit(Stmt *S);
void VisitTerminator(CFGBlock* B);
/// VisitConditionVariableInit - Handle the initialization of condition
/// variables at branches. Valid statements include IfStmt, ForStmt,
/// WhileStmt, and SwitchStmt.
void VisitConditionVariableInit(Stmt *S);
void SetTopValue(LiveVariables::ValTy& V) {
V = AD.AlwaysLive;
}
void setCurrentBlock(const CFGBlock *block) {
currentBlock = block;
}
};
void TransferFuncs::Visit(Stmt *S) {
if (S == getCurrentBlkStmt()) {
if (AD.Observer)
AD.Observer->ObserveStmt(S, currentBlock, AD, LiveState);
if (getCFG().isBlkExpr(S))
LiveState(S, AD) = Dead;
StmtVisitor<TransferFuncs,void>::Visit(S);
}
else if (!getCFG().isBlkExpr(S)) {
if (AD.Observer)
AD.Observer->ObserveStmt(S, currentBlock, AD, LiveState);
StmtVisitor<TransferFuncs,void>::Visit(S);
}
else {
// For block-level expressions, mark that they are live.
LiveState(S, AD) = Alive;
}
}
void TransferFunctions::Visit(Stmt *S) {
if (observer)
observer->observeStmt(S, currentBlock, val);
void TransferFuncs::VisitConditionVariableInit(Stmt *S) {
assert(!getCFG().isBlkExpr(S));
CFGRecStmtVisitor<TransferFuncs>::VisitConditionVariableInit(S);
}
void TransferFuncs::VisitTerminator(CFGBlock* B) {
const Stmt* E = B->getTerminatorCondition();
if (!E)
return;
assert (getCFG().isBlkExpr(E));
LiveState(E, AD) = Alive;
}
void TransferFuncs::VisitDeclRefExpr(DeclRefExpr* DR) {
if (VarDecl* V = dyn_cast<VarDecl>(DR->getDecl()))
LiveState(V, AD) = Alive;
}
StmtVisitor<TransferFunctions>::Visit(S);
void TransferFuncs::VisitBlockExpr(BlockExpr *BE) {
AnalysisContext::referenced_decls_iterator I, E;
llvm::tie(I, E) = AD.AC->getReferencedBlockVars(BE->getBlockDecl());
for ( ; I != E ; ++I) {
DeclBitVector_Types::Idx i = AD.getIdx(*I);
if (i.isValid())
LiveState.getBit(i) = Alive;
if (isa<Expr>(S)) {
val.liveStmts = LV.SSetFact.remove(val.liveStmts, S);
}
// Mark all children expressions live.
switch (S->getStmtClass()) {
default:
break;
case Stmt::StmtExprClass: {
// For statement expressions, look through the compound statement.
S = cast<StmtExpr>(S)->getSubStmt();
break;
}
case Stmt::CXXMemberCallExprClass: {
// Include the implicit "this" pointer as being live.
CXXMemberCallExpr *CE = cast<CXXMemberCallExpr>(S);
val.liveStmts =
LV.SSetFact.add(val.liveStmts,
CE->getImplicitObjectArgument()->IgnoreParens());
break;
}
// FIXME: These cases eventually shouldn't be needed.
case Stmt::ExprWithCleanupsClass: {
S = cast<ExprWithCleanups>(S)->getSubExpr();
break;
}
case Stmt::CXXBindTemporaryExprClass: {
S = cast<CXXBindTemporaryExpr>(S)->getSubExpr();
break;
}
case Stmt::MaterializeTemporaryExprClass: {
S = cast<MaterializeTemporaryExpr>(S)->GetTemporaryExpr();
break;
}
}
for (Stmt::child_iterator it = S->child_begin(), ei = S->child_end();
it != ei; ++it) {
if (Stmt *child = *it) {
if (Expr *Ex = dyn_cast<Expr>(child))
child = Ex->IgnoreParens();
val.liveStmts = LV.SSetFact.add(val.liveStmts, child);
}
}
}
void TransferFuncs::VisitBinaryOperator(BinaryOperator* B) {
if (B->isAssignmentOp()) VisitAssign(B);
else VisitStmt(B);
}
void
TransferFuncs::BlockStmt_VisitObjCForCollectionStmt(ObjCForCollectionStmt* S) {
// This is a block-level expression. Its value is 'dead' before this point.
LiveState(S, AD) = Dead;
// This represents a 'use' of the collection.
Visit(S->getCollection());
// This represents a 'kill' for the variable.
Stmt* Element = S->getElement();
DeclRefExpr* DR = 0;
VarDecl* VD = 0;
if (DeclStmt* DS = dyn_cast<DeclStmt>(Element))
VD = cast<VarDecl>(DS->getSingleDecl());
else {
Expr* ElemExpr = cast<Expr>(Element)->IgnoreParens();
if ((DR = dyn_cast<DeclRefExpr>(ElemExpr)))
VD = cast<VarDecl>(DR->getDecl());
else {
Visit(ElemExpr);
void TransferFunctions::VisitBinaryOperator(BinaryOperator *B) {
if (B->isAssignmentOp()) {
if (!LV.killAtAssign)
return;
}
}
// Assigning to a variable?
Expr *LHS = B->getLHS()->IgnoreParens();
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS))
if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
// Assignments to references don't kill the ref's address
if (VD->getType()->isReferenceType())
return;
if (VD) {
LiveState(VD, AD) = Dead;
if (AD.Observer && DR) { AD.Observer->ObserverKill(DR); }
}
}
if (!isAlwaysAlive(VD)) {
// The variable is now dead.
val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD);
}
void TransferFuncs::VisitUnaryOperator(UnaryOperator* U) {
Expr *E = U->getSubExpr();
switch (U->getOpcode()) {
case UO_PostInc:
case UO_PostDec:
case UO_PreInc:
case UO_PreDec:
// Walk through the subexpressions, blasting through ParenExprs
// until we either find a DeclRefExpr or some non-DeclRefExpr
// expression.
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E->IgnoreParens()))
if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) {
// Treat the --/++ operator as a kill.
if (AD.Observer) { AD.Observer->ObserverKill(DR); }
LiveState(VD, AD) = Alive;
return VisitDeclRefExpr(DR);
if (observer)
observer->observerKill(DR);
}
// Fall-through.
default:
return Visit(E);
}
}
void TransferFuncs::VisitAssign(BinaryOperator* B) {
Expr* LHS = B->getLHS();
// Assigning to a variable?
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS->IgnoreParens())) {
// Assignments to references don't kill the ref's address
if (DR->getDecl()->getType()->isReferenceType()) {
VisitDeclRefExpr(DR);
} else {
if (AD.killAtAssign) {
// Update liveness inforamtion.
unsigned bit = AD.getIdx(DR->getDecl());
LiveState.getDeclBit(bit) = Dead | AD.AlwaysLive.getDeclBit(bit);
if (AD.Observer) { AD.Observer->ObserverKill(DR); }
}
// Handle things like +=, etc., which also generate "uses"
// of a variable. Do this just by visiting the subexpression.
if (B->getOpcode() != BO_Assign)
VisitDeclRefExpr(DR);
}
void TransferFunctions::VisitBlockExpr(BlockExpr *BE) {
AnalysisContext::referenced_decls_iterator I, E;
llvm::tie(I, E) =
LV.analysisContext.getReferencedBlockVars(BE->getBlockDecl());
for ( ; I != E ; ++I) {
const VarDecl *VD = *I;
if (isAlwaysAlive(VD))
continue;
val.liveDecls = LV.DSetFact.add(val.liveDecls, VD);
}
else // Not assigning to a variable. Process LHS as usual.
Visit(LHS);
Visit(B->getRHS());
}
void TransferFuncs::VisitDeclStmt(DeclStmt* DS) {
// Declarations effectively "kill" a variable since they cannot
// possibly be live before they are declared.
void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *DR) {
if (const VarDecl *D = dyn_cast<VarDecl>(DR->getDecl()))
if (!isAlwaysAlive(D) && LV.inAssignment.find(DR) == LV.inAssignment.end())
val.liveDecls = LV.DSetFact.add(val.liveDecls, D);
}
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)) {
// Update liveness information by killing the VarDecl.
unsigned bit = AD.getIdx(VD);
LiveState.getDeclBit(bit) = Dead | AD.AlwaysLive.getDeclBit(bit);
// The initializer is evaluated after the variable comes into scope, but
// before the DeclStmt (which binds the value to the variable).
// Since this is a reverse dataflow analysis, we must evaluate the
// transfer function for this expression after the DeclStmt. If the
// initializer references the variable (which is bad) then we extend
// its liveness.
if (Expr* Init = VD->getInit())
Visit(Init);
if (const VariableArrayType* VT =
AD.getContext().getAsVariableArrayType(VD->getType())) {
StmtIterator I(const_cast<VariableArrayType*>(VT));
StmtIterator E;
for (; I != E; ++I) Visit(*I);
}
if (!isAlwaysAlive(VD))
val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD);
}
}
} // end anonymous namespace
void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *OS) {
// Kill the iteration variable.
DeclRefExpr *DR = 0;
const VarDecl *VD = 0;
//===----------------------------------------------------------------------===//
// Merge operator: if something is live on any successor block, it is live
// in the current block (a set union).
//===----------------------------------------------------------------------===//
namespace {
typedef StmtDeclBitVector_Types::Union Merge;
typedef DataflowSolver<LiveVariables, TransferFuncs, Merge> Solver;
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// External interface to run Liveness analysis.
//===----------------------------------------------------------------------===//
void LiveVariables::runOnCFG(CFG& cfg) {
Solver S(*this);
S.runOnCFG(cfg);
Stmt *element = OS->getElement();
if (DeclStmt *DS = dyn_cast<DeclStmt>(element)) {
VD = cast<VarDecl>(DS->getSingleDecl());
}
else if ((DR = dyn_cast<DeclRefExpr>(cast<Expr>(element)->IgnoreParens()))) {
VD = cast<VarDecl>(DR->getDecl());
}
if (VD) {
val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD);
if (observer && DR)
observer->observerKill(DR);
}
}
void LiveVariables::runOnAllBlocks(const CFG& cfg,
LiveVariables::ObserverTy* Obs,
bool recordStmtValues) {
Solver S(*this);
SaveAndRestore<LiveVariables::ObserverTy*> SRObs(getAnalysisData().Observer,
Obs);
S.runOnAllBlocks(cfg, recordStmtValues);
void TransferFunctions::
VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UE)
{
// While sizeof(var) doesn't technically extend the liveness of 'var', it
// does extent the liveness of metadata if 'var' is a VariableArrayType.
// We handle that special case here.
if (UE->getKind() != UETT_SizeOf || UE->isArgumentType())
return;
const DeclRefExpr *DR =
dyn_cast<DeclRefExpr>(UE->getArgumentExpr()->IgnoreParens());
if (!DR)
return;
const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
if (VD && VD->getType()->isVariableArrayType())
val.liveDecls = LV.DSetFact.add(val.liveDecls, VD);
}
//===----------------------------------------------------------------------===//
// liveness queries
//
bool LiveVariables::isLive(const CFGBlock* B, const VarDecl* D) const {
DeclBitVector_Types::Idx i = getAnalysisData().getIdx(D);
return i.isValid() ? getBlockData(B).getBit(i) : false;
void TransferFunctions::VisitUnaryOperator(UnaryOperator *UO) {
// Treat ++/-- as a kill.
// Note we don't actually have to do anything if we don't have an observer,
// since a ++/-- acts as both a kill and a "use".
if (!observer)
return;
switch (UO->getOpcode()) {
default:
return;
case UO_PostInc:
case UO_PostDec:
case UO_PreInc:
case UO_PreDec:
break;
}
if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreParens()))
if (isa<VarDecl>(DR->getDecl())) {
// Treat ++/-- as a kill.
observer->observerKill(DR);
}
}
bool LiveVariables::isLive(const ValTy& Live, const VarDecl* D) const {
DeclBitVector_Types::Idx i = getAnalysisData().getIdx(D);
return i.isValid() ? Live.getBit(i) : false;
LiveVariables::LivenessValues
LiveVariablesImpl::runOnBlock(const CFGBlock *block,
LiveVariables::LivenessValues val,
LiveVariables::Observer *obs) {
TransferFunctions TF(*this, val, obs, block);
// Visit the terminator (if any).
if (const Stmt *term = block->getTerminator())
TF.Visit(const_cast<Stmt*>(term));
// Apply the transfer function for all Stmts in the block.
for (CFGBlock::const_reverse_iterator it = block->rbegin(),
ei = block->rend(); it != ei; ++it) {
const CFGElement &elem = *it;
if (!isa<CFGStmt>(elem))
continue;
const Stmt *S = cast<CFGStmt>(elem).getStmt();
TF.Visit(const_cast<Stmt*>(S));
stmtsToLiveness[S] = val;
}
return val;
}
bool LiveVariables::isLive(const Stmt* Loc, const Stmt* StmtVal) const {
return getStmtData(Loc)(StmtVal,getAnalysisData());
void LiveVariables::runOnAllBlocks(LiveVariables::Observer &obs) {
const CFG *cfg = getImpl(impl).analysisContext.getCFG();
for (CFG::const_iterator it = cfg->begin(), ei = cfg->end(); it != ei; ++it)
getImpl(impl).runOnBlock(*it, getImpl(impl).blocksEndToLiveness[*it], &obs);
}
bool LiveVariables::isLive(const Stmt* Loc, const VarDecl* D) const {
return getStmtData(Loc)(D,getAnalysisData());
LiveVariables::LiveVariables(void *im) : impl(im) {}
LiveVariables::~LiveVariables() {
delete (LiveVariablesImpl*) impl;
}
//===----------------------------------------------------------------------===//
// printing liveness state for debugging
//
LiveVariables *
LiveVariables::computeLiveness(AnalysisContext &AC,
bool killAtAssign) {
void LiveVariables::dumpLiveness(const ValTy& V, const SourceManager& SM) const {
const AnalysisDataTy& AD = getAnalysisData();
// No CFG? Bail out.
CFG *cfg = AC.getCFG();
if (!cfg)
return 0;
for (AnalysisDataTy::decl_iterator I = AD.begin_decl(),
E = AD.end_decl(); I!=E; ++I)
if (V.getDeclBit(I->second)) {
llvm::errs() << " " << I->first->getIdentifier()->getName() << " <";
I->first->getLocation().dump(SM);
LiveVariablesImpl *LV = new LiveVariablesImpl(AC, killAtAssign);
// Construct the dataflow worklist. Enqueue the exit block as the
// start of the analysis.
Worklist worklist(*cfg);
llvm::BitVector everAnalyzedBlock(cfg->getNumBlockIDs());
// FIXME: we should enqueue using post order.
for (CFG::const_iterator it = cfg->begin(), ei = cfg->end(); it != ei; ++it) {
const CFGBlock *block = *it;
worklist.enqueueBlock(block);
// FIXME: Scan for DeclRefExprs using in the LHS of an assignment.
// We need to do this because we lack context in the reverse analysis
// to determine if a DeclRefExpr appears in such a context, and thus
// doesn't constitute a "use".
if (killAtAssign)
for (CFGBlock::const_iterator bi = block->begin(), be = block->end();
bi != be; ++bi) {
if (const CFGStmt *cs = bi->getAs<CFGStmt>()) {
if (BinaryOperator *BO = dyn_cast<BinaryOperator>(cs->getStmt())) {
if (BO->getOpcode() == BO_Assign) {
if (const DeclRefExpr *DR =
dyn_cast<DeclRefExpr>(BO->getLHS()->IgnoreParens())) {
LV->inAssignment[DR] = 1;
}
}
}
}
}
}
while (!worklist.empty()) {
// Dequeue blocks in FIFO order.
const CFGBlock *block = worklist.getNextItem();
// Determine if the block's end value has changed. If not, we
// have nothing left to do for this block.
LivenessValues &prevVal = LV->blocksEndToLiveness[block];
// Merge the values of all successor blocks.
LivenessValues val;
for (CFGBlock::const_succ_iterator it = block->succ_begin(),
ei = block->succ_end(); it != ei; ++it) {
if (const CFGBlock *succ = *it)
val = LV->merge(val, LV->blocksBeginToLiveness[succ]);
}
if (!everAnalyzedBlock[block->getBlockID()])
everAnalyzedBlock[block->getBlockID()] = true;
else if (prevVal.equals(val))
continue;
prevVal = val;
// Update the dataflow value for the start of this block.
LV->blocksBeginToLiveness[block] = LV->runOnBlock(block, val);
// Enqueue the value to the predecessors.
for (CFGBlock::const_pred_iterator it = block->pred_begin(),
ei = block->pred_end(); it != ei; ++it)
{
if (const CFGBlock *pred = *it)
worklist.enqueueBlock(pred);
}
}
return new LiveVariables(LV);
}
bool compare_entries(const CFGBlock *A, const CFGBlock *B) {
return A->getBlockID() < B->getBlockID();
}
bool compare_vd_entries(const Decl *A, const Decl *B) {
SourceLocation ALoc = A->getLocStart();
SourceLocation BLoc = B->getLocStart();
return ALoc.getRawEncoding() < BLoc.getRawEncoding();
}
void LiveVariables::dumpBlockLiveness(const SourceManager &M) {
getImpl(impl).dumpBlockLiveness(M);
}
void LiveVariablesImpl::dumpBlockLiveness(const SourceManager &M) {
std::vector<const CFGBlock *> vec;
for (llvm::DenseMap<const CFGBlock *, LiveVariables::LivenessValues>::iterator
it = blocksEndToLiveness.begin(), ei = blocksEndToLiveness.end();
it != ei; ++it) {
vec.push_back(it->first);
}
std::sort(vec.begin(), vec.end(), compare_entries);
std::vector<const VarDecl*> declVec;
for (std::vector<const CFGBlock *>::iterator
it = vec.begin(), ei = vec.end(); it != ei; ++it) {
llvm::errs() << "\n[ B" << (*it)->getBlockID()
<< " (live variables at block exit) ]\n";
LiveVariables::LivenessValues vals = blocksEndToLiveness[*it];
declVec.clear();
for (llvm::ImmutableSet<const VarDecl *>::iterator si =
vals.liveDecls.begin(),
se = vals.liveDecls.end(); si != se; ++si) {
declVec.push_back(*si);
}
std::sort(declVec.begin(), declVec.end(), compare_vd_entries);
for (std::vector<const VarDecl*>::iterator di = declVec.begin(),
de = declVec.end(); di != de; ++di) {
llvm::errs() << " " << (*di)->getDeclName().getAsString()
<< " <";
(*di)->getLocation().dump(M);
llvm::errs() << ">\n";
}
}
void LiveVariables::dumpBlockLiveness(const SourceManager& M) const {
for (BlockDataMapTy::const_iterator I = getBlockDataMap().begin(),
E = getBlockDataMap().end(); I!=E; ++I) {
llvm::errs() << "\n[ B" << I->first->getBlockID()
<< " (live variables at block exit) ]\n";
dumpLiveness(I->second,M);
}
llvm::errs() << "\n";
llvm::errs() << "\n";
}

View File

@ -68,12 +68,12 @@ void ReachableCode::computeReachableBlocks() {
}
namespace {
class DeadStoreObs : public LiveVariables::ObserverTy {
class DeadStoreObs : public LiveVariables::Observer {
const CFG &cfg;
ASTContext &Ctx;
BugReporter& BR;
ParentMap& Parents;
llvm::SmallPtrSet<VarDecl*, 20> Escaped;
llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
llvm::OwningPtr<ReachableCode> reachableCode;
const CFGBlock *currentBlock;
@ -82,13 +82,14 @@ class DeadStoreObs : public LiveVariables::ObserverTy {
public:
DeadStoreObs(const CFG &cfg, ASTContext &ctx,
BugReporter& br, ParentMap& parents,
llvm::SmallPtrSet<VarDecl*, 20> &escaped)
llvm::SmallPtrSet<const VarDecl*, 20> &escaped)
: cfg(cfg), Ctx(ctx), BR(br), Parents(parents),
Escaped(escaped), currentBlock(0) {}
virtual ~DeadStoreObs() {}
void Report(VarDecl* V, DeadStoreKind dsk, SourceLocation L, SourceRange R) {
void Report(const VarDecl* V, DeadStoreKind dsk,
SourceLocation L, SourceRange R) {
if (Escaped.count(V))
return;
@ -134,10 +135,9 @@ public:
BR.EmitBasicReport(BugType, "Dead store", msg, L, R);
}
void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val,
void CheckVarDecl(const VarDecl* VD, const Expr* Ex, const Expr* Val,
DeadStoreKind dsk,
const LiveVariables::AnalysisDataTy& AD,
const LiveVariables::ValTy& Live) {
const LiveVariables::LivenessValues &Live) {
if (!VD->hasLocalStorage())
return;
@ -146,30 +146,29 @@ public:
if (VD->getType()->getAs<ReferenceType>())
return;
if (!Live(VD, AD) &&
if (!Live.isLive(VD) &&
!(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>()))
Report(VD, dsk, Ex->getSourceRange().getBegin(),
Val->getSourceRange());
}
void CheckDeclRef(DeclRefExpr* DR, Expr* Val, DeadStoreKind dsk,
const LiveVariables::AnalysisDataTy& AD,
const LiveVariables::ValTy& Live) {
if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()))
CheckVarDecl(VD, DR, Val, dsk, AD, Live);
void CheckDeclRef(const DeclRefExpr* DR, const Expr* Val, DeadStoreKind dsk,
const LiveVariables::LivenessValues& Live) {
if (const VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()))
CheckVarDecl(VD, DR, Val, dsk, Live);
}
bool isIncrement(VarDecl* VD, BinaryOperator* B) {
bool isIncrement(VarDecl* VD, const BinaryOperator* B) {
if (B->isCompoundAssignmentOp())
return true;
Expr* RHS = B->getRHS()->IgnoreParenCasts();
BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
const Expr* RHS = B->getRHS()->IgnoreParenCasts();
const BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
if (!BRHS)
return false;
DeclRefExpr *DR;
const DeclRefExpr *DR;
if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts())))
if (DR->getDecl() == VD)
@ -182,9 +181,8 @@ public:
return false;
}
virtual void ObserveStmt(Stmt* S, const CFGBlock *block,
const LiveVariables::AnalysisDataTy& AD,
const LiveVariables::ValTy& Live) {
virtual void observeStmt(const Stmt* S, const CFGBlock *block,
const LiveVariables::LivenessValues &Live) {
currentBlock = block;
@ -194,7 +192,7 @@ public:
// Only cover dead stores from regular assignments. ++/-- dead stores
// have never flagged a real bug.
if (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
if (!B->isAssignmentOp()) return; // Skip non-assignments.
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()))
@ -220,26 +218,26 @@ public:
? Enclosing
: (isIncrement(VD,B) ? DeadIncrement : Standard);
CheckVarDecl(VD, DR, B->getRHS(), dsk, AD, Live);
CheckVarDecl(VD, DR, B->getRHS(), dsk, Live);
}
}
else if (UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
else if (const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
if (!U->isIncrementOp() || U->isPrefix())
return;
Stmt *parent = Parents.getParentIgnoreParenCasts(U);
const Stmt *parent = Parents.getParentIgnoreParenCasts(U);
if (!parent || !isa<ReturnStmt>(parent))
return;
Expr *Ex = U->getSubExpr()->IgnoreParenCasts();
const Expr *Ex = U->getSubExpr()->IgnoreParenCasts();
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(Ex))
CheckDeclRef(DR, U, DeadIncrement, AD, Live);
if (const DeclRefExpr* DR = dyn_cast<DeclRefExpr>(Ex))
CheckDeclRef(DR, U, DeadIncrement, Live);
}
else if (DeclStmt* DS = dyn_cast<DeclStmt>(S))
else if (const DeclStmt* DS = dyn_cast<DeclStmt>(S))
// Iterate through the decls. Warn if any initializers are complex
// expressions that are not live (never used).
for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end();
for (DeclStmt::const_decl_iterator DI=DS->decl_begin(), DE=DS->decl_end();
DI != DE; ++DI) {
VarDecl* V = dyn_cast<VarDecl>(*DI);
@ -265,7 +263,7 @@ public:
// A dead initialization is a variable that is dead after it
// is initialized. We don't flag warnings for those variables
// marked 'unused'.
if (!Live(V, AD) && V->getAttr<UnusedAttr>() == 0) {
if (!Live.isLive(V) && V->getAttr<UnusedAttr>() == 0) {
// Special case: check for initializations with constants.
//
// e.g. : int x = 0;
@ -318,7 +316,7 @@ public:
CFG& getCFG() { return *cfg; }
llvm::SmallPtrSet<VarDecl*, 20> Escaped;
llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
void VisitUnaryOperator(UnaryOperator* U) {
// Check for '&'. Any VarDecl whose value has its address-taken we
@ -351,7 +349,7 @@ public:
FindEscaped FS(&cfg);
FS.getCFG().VisitBlockStmts(FS);
DeadStoreObs A(cfg, BR.getContext(), BR, pmap, FS.Escaped);
L->runOnAllBlocks(cfg, &A);
L->runOnAllBlocks(A);
}
}
};

View File

@ -111,13 +111,19 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
// FIXME: This should be extended to include other unreachable markers,
// such as llvm_unreachable.
if (!CB->empty()) {
CFGElement First = CB->front();
if (const CFGStmt *S = First.getAs<CFGStmt>()) {
if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) {
if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable)
continue;
}
bool foundUnreachable = false;
for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end();
ci != ce; ++ci) {
if (const CFGStmt *S = (*ci).getAs<CFGStmt>())
if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) {
if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable) {
foundUnreachable = true;
break;
}
}
}
if (foundUnreachable)
continue;
}
// We found a block that wasn't covered - find the statement to report

View File

@ -35,6 +35,7 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, Diagnostic &diags,
EagerlyAssume(eager), TrimGraph(trim), InlineCall(inlinecall),
EagerlyTrimEGraph(eagerlyTrimEGraph)
{
AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd();
}
AnalysisContext *

View File

@ -39,8 +39,6 @@ const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) {
return ME->getBase()->IgnoreParenCasts();
}
else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) {
// Retrieve the base for arrays since BasicStoreManager doesn't know how
// to reason about them.
return AE->getBase();
}
@ -314,6 +312,21 @@ void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC,
return;
GRStateManager &StateMgr = BRC.getStateManager();
// Walk through nodes until we get one that matches the statement
// exactly.
while (N) {
const ProgramPoint &pp = N->getLocation();
if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) {
if (ps->getStmt() == S)
break;
}
N = *N->pred_begin();
}
if (!N)
return;
const GRState *state = N->getState();
// Walk through lvalue-to-rvalue conversions.

View File

@ -158,8 +158,6 @@ EnvironmentManager::removeDeadBindings(Environment Env,
const GRState *ST,
SmallVectorImpl<const MemRegion*> &DRoots) {
CFG &C = *SymReaper.getLocationContext()->getCFG();
// We construct a new Environment object entirely, as this is cheaper than
// individually removing all the subexpression bindings (which will greatly
// outnumber block-level expression bindings).
@ -172,7 +170,6 @@ EnvironmentManager::removeDeadBindings(Environment Env,
I != E; ++I) {
const Stmt *BlkExpr = I.getKey();
// For recorded locations (used when evaluating loads and stores), we
// consider them live only when their associated normal expression is
// also live.
@ -182,28 +179,8 @@ EnvironmentManager::removeDeadBindings(Environment Env,
deferredLocations.push_back(std::make_pair(BlkExpr, I.getData()));
continue;
}
const SVal &X = I.getData();
// Block-level expressions in callers are assumed always live.
if (isBlockExprInCallers(BlkExpr, SymReaper.getLocationContext())) {
NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, BlkExpr, X);
if (isa<loc::MemRegionVal>(X)) {
const MemRegion* R = cast<loc::MemRegionVal>(X).getRegion();
DRoots.push_back(R);
}
// Mark all symbols in the block expr's value live.
MarkLiveCallback cb(SymReaper);
ST->scanReachableSymbols(X, cb);
continue;
}
// Not a block-level expression?
if (!C.isBlkExpr(BlkExpr))
continue;
if (SymReaper.isLive(BlkExpr)) {
// Copy the binding to the new map.
NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, BlkExpr, X);

View File

@ -244,7 +244,7 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
// Create the cleaned state.
const LocationContext *LC = EntryNode->getLocationContext();
SymbolReaper SymReaper(LC, currentStmt, SymMgr);
SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager());
if (AMgr.shouldPurgeDead()) {
const GRState *St = EntryNode->getState();

View File

@ -78,8 +78,11 @@ GRStateManager::removeDeadBindings(const GRState* state,
state, RegionRoots);
// Clean up the store.
NewState.setStore(StoreMgr->removeDeadBindings(NewState.getStore(), LCtx,
SymReaper, RegionRoots));
StoreRef newStore = StoreMgr->removeDeadBindings(NewState.getStore(), LCtx,
SymReaper, RegionRoots);
NewState.setStore(newStore);
SymReaper.setReapedStore(newStore);
state = getPersistentState(NewState);
return ConstraintMgr->removeDeadBindings(state, SymReaper);
}
@ -519,9 +522,9 @@ const GRState *GRStateManager::removeGDM(const GRState *state, void *Key) {
namespace {
class ScanReachableSymbols : public SubRegionMap::Visitor {
typedef llvm::DenseSet<const MemRegion*> VisitedRegionsTy;
typedef llvm::DenseMap<const void*, unsigned> VisitedItems;
VisitedRegionsTy visited;
VisitedItems visited;
const GRState *state;
SymbolVisitor &visitor;
llvm::OwningPtr<SubRegionMap> SRM;
@ -533,6 +536,7 @@ public:
bool scan(nonloc::CompoundVal val);
bool scan(SVal val);
bool scan(const MemRegion *R);
bool scan(const SymExpr *sym);
// From SubRegionMap::Visitor.
bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) {
@ -549,6 +553,33 @@ bool ScanReachableSymbols::scan(nonloc::CompoundVal val) {
return true;
}
bool ScanReachableSymbols::scan(const SymExpr *sym) {
unsigned &isVisited = visited[sym];
if (isVisited)
return true;
isVisited = 1;
if (const SymbolData *sData = dyn_cast<SymbolData>(sym))
if (!visitor.VisitSymbol(sData))
return false;
switch (sym->getKind()) {
case SymExpr::RegionValueKind:
case SymExpr::ConjuredKind:
case SymExpr::DerivedKind:
case SymExpr::ExtentKind:
case SymExpr::MetadataKind:
break;
case SymExpr::SymIntKind:
return scan(cast<SymIntExpr>(sym)->getLHS());
case SymExpr::SymSymKind: {
const SymSymExpr *x = cast<SymSymExpr>(sym);
return scan(x->getLHS()) && scan(x->getRHS());
}
}
return true;
}
bool ScanReachableSymbols::scan(SVal val) {
if (loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(&val))
return scan(X->getRegion());
@ -557,7 +588,10 @@ bool ScanReachableSymbols::scan(SVal val) {
return scan(X->getLoc());
if (SymbolRef Sym = val.getAsSymbol())
return visitor.VisitSymbol(Sym);
return scan(Sym);
if (const SymExpr *Sym = val.getAsSymbolicExpression())
return scan(Sym);
if (nonloc::CompoundVal *X = dyn_cast<nonloc::CompoundVal>(&val))
return scan(*X);
@ -566,10 +600,13 @@ bool ScanReachableSymbols::scan(SVal val) {
}
bool ScanReachableSymbols::scan(const MemRegion *R) {
if (isa<MemSpaceRegion>(R) || visited.count(R))
if (isa<MemSpaceRegion>(R))
return true;
visited.insert(R);
unsigned &isVisited = visited[R];
if (isVisited)
return true;
isVisited = 1;
// If this is a symbolic region, visit the symbol for the region.
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))

View File

@ -15,6 +15,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@ -272,7 +273,7 @@ static bool IsLiveRegion(SymbolReaper &Reaper, const MemRegion *MR) {
return Reaper.isLive(SR->getSymbol());
if (const VarRegion *VR = dyn_cast<VarRegion>(MR))
return Reaper.isLive(VR);
return Reaper.isLive(VR, true);
// FIXME: This is a gross over-approximation. What we really need is a way to
// tell if anything still refers to this region. Unlike SymbolicRegions,
@ -331,13 +332,35 @@ bool SymbolReaper::isLive(const Stmt* ExprVal) const {
isLive(Loc, ExprVal);
}
bool SymbolReaper::isLive(const VarRegion *VR) const {
bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
const StackFrameContext *VarContext = VR->getStackFrame();
const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame();
if (VarContext == CurrentContext)
return LCtx->getAnalysisContext()->getRelaxedLiveVariables()->
isLive(Loc, VR->getDecl());
if (VarContext == CurrentContext) {
if (LCtx->getAnalysisContext()->getRelaxedLiveVariables()->
isLive(Loc, VR->getDecl()))
return true;
if (!includeStoreBindings)
return false;
unsigned &cachedQuery =
const_cast<SymbolReaper*>(this)->includedRegionCache[VR];
if (cachedQuery) {
return cachedQuery == 1;
}
// Query the store to see if the region occurs in any live bindings.
if (Store store = reapedStore.getStore()) {
bool hasRegion =
reapedStore.getStoreManager().includedInBindings(store, VR);
cachedQuery = hasRegion ? 1 : 2;
return hasRegion;
}
return false;
}
return VarContext->isParentOf(CurrentContext);
}

File diff suppressed because it is too large Load Diff

View File

@ -44,51 +44,60 @@ TestControlFlow::TestControlFlow(bool b)
int v;
}
// CHECK: [ B2 (ENTRY) ]
// CHECK: Predecessors (0):
// CHECK: Successors (1): B1
// CHECK: [ B1 ]
// CHECK: 1:
// CHECK: 2: A([B1.1]) (Base initializer)
// CHECK: 3:
// CHECK: 4: C([B1.3]) (Base initializer)
// CHECK: 5:
// CHECK: 6: B([B1.5]) (Base initializer)
// CHECK: 7:
// CHECK: 8: A([B1.7]) (Base initializer)
// CHECK: 9: i(/*implicit*/int()) (Member initializer)
// CHECK: 10: r(this->i) (Member initializer)
// CHECK: 11:
// CHECK: 12: A a;
// CHECK: Predecessors (1): B2
// CHECK: Successors (1): B0
// CHECK: [ B0 (EXIT) ]
// CHECK: Predecessors (1): B1
// CHECK: Successors (0):
// CHECK: [ B5 (ENTRY) ]
// CHECK: Predecessors (0):
// CHECK: Successors (1): B4
// CHECK: [ B1 ]
// CHECK: 1: [B4.2] ? [B2.1] : [B3.1]
// CHECK: 2: y([B1.1]) (Member initializer)
// CHECK: 3: z(this->y) (Member initializer)
// CHECK: 4: int v;
// CHECK: Predecessors (2): B2 B3
// CHECK: Successors (1): B0
// CHECK: [ B2 ]
// CHECK: 1: 0
// CHECK: Predecessors (1): B4
// CHECK: Successors (1): B1
// CHECK: [ B3 ]
// CHECK: 1: 1
// CHECK: Predecessors (1): B4
// CHECK: Successors (1): B1
// CHECK: [ B4 ]
// CHECK: 1: x(0) (Member initializer)
// CHECK: 2: b
// CHECK: T: [B4.2] ? ... : ...
// CHECK: Predecessors (1): B5
// CHECK: Successors (2): B2 B3
// CHECK: [ B0 (EXIT) ]
// CHECK: Predecessors (1): B1
// CHECK: Successors (0):
// CHECK: [ B2 (ENTRY) ]
// CHECK: Predecessors (0):
// CHECK: Successors (1): B1
// CHECK: [ B1 ]
// CHECK: 1:
// CHECK: 2: A([B1.1]) (Base initializer)
// CHECK: 3:
// CHECK: 4: C([B1.3]) (Base initializer)
// CHECK: 5:
// CHECK: 6: B([B1.5]) (Base initializer)
// CHECK: 7:
// CHECK: 8: A([B1.7]) (Base initializer)
// CHECK: 9: /*implicit*/int()
// CHECK: 10: i([B1.9]) (Member initializer)
// CHECK: 11: this
// CHECK: 12: [B1.11]->i
// CHECK: 13: r([B1.12]) (Member initializer)
// CHECK: 14:
// CHECK: 15: A a;
// CHECK: Predecessors (1): B2
// CHECK: Successors (1): B0
// CHECK: [ B0 (EXIT) ]
// CHECK: Predecessors (1): B1
// CHECK: Successors (0):
// CHECK: [ B5 (ENTRY) ]
// CHECK: Predecessors (0):
// CHECK: Successors (1): B4
// CHECK: [ B1 ]
// CHECK: 1: [B4.4] ? [B2.1] : [B3.1]
// CHECK: 2: y([B1.1]) (Member initializer)
// CHECK: 3: this
// CHECK: 4: [B1.3]->y
// CHECK: 5: [B1.4]
// CHECK: 6: z([B1.5]) (Member initializer)
// CHECK: 7: int v;
// CHECK: Predecessors (2): B2 B3
// CHECK: Successors (1): B0
// CHECK: [ B2 ]
// CHECK: 1: 0
// CHECK: Predecessors (1): B4
// CHECK: Successors (1): B1
// CHECK: [ B3 ]
// CHECK: 1: 1
// CHECK: Predecessors (1): B4
// CHECK: Successors (1): B1
// CHECK: [ B4 ]
// CHECK: 1: 0
// CHECK: 2: x([B4.1]) (Member initializer)
// CHECK: 3: b
// CHECK: 4: [B4.3]
// CHECK: T: [B4.4] ? ... : ...
// CHECK: Predecessors (1): B5
// CHECK: Successors (2): B2 B3
// CHECK: [ B0 (EXIT) ]
// CHECK: Predecessors (1): B1
// CHECK: Successors (0):

View File

@ -94,7 +94,6 @@ void test2_ptr(int x) {
p[-1] = 1; // expected-warning{{Out of bound memory access}}
}
// ** FIXME ** Doesn't work yet because we don't support pointer arithmetic.
// Tests doing an out-of-bounds access before the start of an array using:
// - indirect pointer to buffer, manipulated using simple pointer arithmetic
// - constant integer index
@ -103,7 +102,7 @@ void test2_ptr_arith(int x) {
int buf[100];
int *p = buf;
--p;
p[0] = 1; // no-warning
p[0] = 1; // expected-warning {{Out of bound memory access (accessed memory precedes memory block)}}
}
// Tests doing an out-of-bounds access before the start of a multi-dimensional

View File

@ -69,34 +69,6 @@ void rdar8331641(int x) {
// CHECK: <key>path</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>4</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>4</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>4</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>extended_message</key>
// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
// CHECK: <key>message</key>
// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
@ -110,7 +82,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>4</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@ -193,68 +165,6 @@ void rdar8331641(int x) {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
// CHECK: <key>col</key><integer>7</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>extended_message</key>
// CHECK: <string>Null pointer value stored to &apos;p&apos;</string>
// CHECK: <key>message</key>
// CHECK: <string>Null pointer value stored to &apos;p&apos;</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>10</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>11</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
@ -331,68 +241,6 @@ void rdar8331641(int x) {
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>17</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>17</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>17</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>17</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>17</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>extended_message</key>
// CHECK: <string>Variable &apos;q&apos; initialized to a null pointer value</string>
// CHECK: <key>message</key>
// CHECK: <string>Variable &apos;q&apos; initialized to a null pointer value</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>start</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>17</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>17</integer>
// CHECK: <key>col</key><integer>8</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: <key>end</key>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>18</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>file</key><integer>0</integer>
@ -551,7 +399,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>29</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@ -559,34 +407,6 @@ void rdar8331641(int x) {
// CHECK: </array>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>event</string>
// CHECK: <key>location</key>
// CHECK: <dict>
// CHECK: <key>line</key><integer>29</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <key>ranges</key>
// CHECK: <array>
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>29</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>29</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
// CHECK: </array>
// CHECK: <key>extended_message</key>
// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
// CHECK: <key>message</key>
// CHECK: <string>Variable &apos;p&apos; initialized to a null pointer value</string>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>kind</key><string>control</string>
// CHECK: <key>edges</key>
// CHECK: <array>
@ -600,7 +420,7 @@ void rdar8331641(int x) {
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>29</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>col</key><integer>5</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@ -924,12 +744,12 @@ void rdar8331641(int x) {
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>
@ -945,12 +765,12 @@ void rdar8331641(int x) {
// CHECK: <array>
// CHECK: <dict>
// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: <dict>
// CHECK: <key>line</key><integer>56</integer>
// CHECK: <key>col</key><integer>3</integer>
// CHECK: <key>col</key><integer>10</integer>
// CHECK: <key>file</key><integer>0</integer>
// CHECK: </dict>
// CHECK: </array>

View File

@ -1446,7 +1446,7 @@ static void rdar_8724287(CFErrorRef error)
while (error_to_dump != ((void*)0)) {
CFDictionaryRef info;
info = CFErrorCopyUserInfo(error_to_dump); // expected-warning{{Potential leak of an object allocated on line 1449 and stored into 'info'}}
info = CFErrorCopyUserInfo(error_to_dump); // expected-warning{{Potential leak of an object allocated on line 1447 and stored into 'info'}}
if (info != ((void*)0)) {
}

View File

@ -59,7 +59,7 @@ int struct_test(struct baz byVal, int flag) {
typedef int (^ComparatorBlock)(int a, int b);
ComparatorBlock test_return_block(void) {
ComparatorBlock b = ^int(int a, int b){ return a > b; };
return b; // expected-warning{{Address of stack-allocated block declared on line 61 returned to caller}}
return b; // expected-warning{{Address of stack-allocated block declared on line 60 returned to caller}}
}
ComparatorBlock test_return_block_neg_aux(void);

File diff suppressed because it is too large Load Diff