Modified the notion of "Block-level expressions" in CFGs to include Stmt*. This

is because GNU-style Statement-expressions cause the last statement in the
statement-expression to act like an expression.

We now have two notions: block-level statements and block-level expressions.

The former are all Stmt* that appear in the list of statements in CFGBlocks. The
latter is the subset of the former; these block-level statements are used as
subexpressions somewhere in the AST. CFG::isBlockExpr() returns true for the
latter, not the former (previously isBlockExpr() always returned true for
non-Expr Stmt*).

Modified the LiveVariables analysis to also track liveness state for block-level
expressions (using the updated definition of block-level expressions).

Modified the dataflow solver so that when it records values for block-level
statements, it records the dataflow value *before* the transfer function for a
Stmt* is evaluated (not after). This is more in sync in what clients will want.

Modified CFGStmtVisitor to record the current block-level statement.

llvm-svn: 46143
This commit is contained in:
Ted Kremenek 2008-01-17 20:48:37 +00:00
parent e4d3e3c0e7
commit 85be7cf8ca
8 changed files with 58 additions and 37 deletions

View File

@ -988,7 +988,7 @@ void CFGBlock::reverseStmts() { std::reverse(Stmts.begin(),Stmts.end()); }
//===----------------------------------------------------------------------===//
namespace {
typedef llvm::DenseMap<const Expr*,unsigned> BlkExprMapTy;
typedef llvm::DenseMap<const Stmt*,unsigned> BlkExprMapTy;
}
static BlkExprMapTy* PopulateBlkExprMap(CFG& cfg) {
@ -999,23 +999,27 @@ static BlkExprMapTy* PopulateBlkExprMap(CFG& cfg) {
if (const Expr* E = dyn_cast<Expr>(*BI)) {
unsigned x = M->size();
(*M)[E] = x;
// Special handling for statement expressions. The last statement
// in the statement expression is also a block-level expr.
if (const StmtExpr* S = dyn_cast<StmtExpr>(E)) {
const CompoundStmt* C = S->getSubStmt();
if (!C->body_empty()) {
x = M->size();
(*M)[C->body_back()] = x;
}
}
}
return M;
}
bool CFG::isBlkExpr(const Stmt* S) {
assert (S != NULL);
if (const Expr* E = dyn_cast<Expr>(S)) return getBlkExprNum(E);
else return true; // Statements are by default "block-level expressions."
}
CFG::BlkExprNumTy CFG::getBlkExprNum(const Expr* E) {
assert(E != NULL);
CFG::BlkExprNumTy CFG::getBlkExprNum(const Stmt* S) {
assert(S != NULL);
if (!BlkExprMap) { BlkExprMap = (void*) PopulateBlkExprMap(*this); }
BlkExprMapTy* M = reinterpret_cast<BlkExprMapTy*>(BlkExprMap);
BlkExprMapTy::iterator I = M->find(E);
BlkExprMapTy::iterator I = M->find(S);
if (I == M->end()) return CFG::BlkExprNumTy();
else return CFG::BlkExprNumTy(I->second);

View File

@ -77,7 +77,16 @@ void TransferFuncs::Visit(Stmt *S) {
if (AD.Observer)
AD.Observer->ObserveStmt(S,AD,LiveState);
static_cast<CFGStmtVisitor<TransferFuncs>*>(this)->Visit(S);
if (S == getCurrentBlkStmt()) {
StmtVisitor<TransferFuncs,void>::Visit(S);
if (getCFG().isBlkExpr(S)) LiveState(S,AD) = Dead;
}
else if (!getCFG().isBlkExpr(S))
StmtVisitor<TransferFuncs,void>::Visit(S);
else
// For block-level expressions, mark that they are live.
LiveState(S,AD) = Alive;
}
void TransferFuncs::VisitDeclRefExpr(DeclRefExpr* DR) {
@ -186,6 +195,10 @@ bool LiveVariables::isLive(const ValTy& Live, const VarDecl* D) const {
return Live(D,getAnalysisData());
}
bool LiveVariables::isLive(const Stmt* Loc, const Stmt* StmtVal) const {
return getStmtData(Loc)(StmtVal,getAnalysisData());
}
//===----------------------------------------------------------------------===//
// printing liveness state for debugging
//

View File

@ -255,9 +255,8 @@ public:
operator unsigned() const { assert(Idx >=0); return (unsigned) Idx; }
};
bool isBlkExpr(const Stmt* S);
bool isBlkExpr(const Expr* E) { return getBlkExprNum(E); }
BlkExprNumTy getBlkExprNum(const Expr* E);
bool isBlkExpr(const Stmt* S) { return getBlkExprNum(S); }
BlkExprNumTy getBlkExprNum(const Stmt* S);
unsigned getNumBlkExprs();
unsigned getNumBlockIDs() const { return NumBlockIDs; }

View File

@ -30,11 +30,10 @@ struct LiveVariables_ValueTypes {
// We need to keep track of both declarations and CFGBlock-level expressions,
// (so that we don't explore such expressions twice), but we only need
// liveness information for declarations (hence
// ValTy = DeclBitVector_Types::ValTy instead of
// ValTy = ExprDeclBitVector_Types::ValTy).
// (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 ExprDeclBitVector_Types::AnalysisDataTy {
ObserverTy* Observer;
@ -42,7 +41,7 @@ struct LiveVariables_ValueTypes {
};
// We only keep actual dataflow state for declarations.
typedef DeclBitVector_Types::ValTy ValTy;
typedef ExprDeclBitVector_Types::ValTy ValTy;
//===-----------------------------------------------------===//
// ObserverTy - Observer for uninitialized values queries.
@ -76,6 +75,10 @@ public:
/// 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
/// before the given block-level expression (see runOnAllBlocks).
bool isLive(const Stmt* Loc, const Stmt* StmtVal) const;
/// IsLive - Return true if a variable is live according to the
/// provided livness bitvector.

View File

@ -236,8 +236,8 @@ private:
/// ProcessBlock - Process the transfer functions for a given block.
void ProcessBlock(const CFGBlock* B, bool recordStmtValues) {
for (StmtItr I=ItrTraits::StmtBegin(B), E=ItrTraits::StmtEnd(B); I!=E;++I) {
TF.BlockStmt_Visit(const_cast<Stmt*>(*I));
if (recordStmtValues) D.getStmtDataMap()[*I] = TF.getVal();
TF.BlockStmt_Visit(const_cast<Stmt*>(*I));
}
}

View File

@ -110,9 +110,9 @@ public:
/// getStmtData - Retrieves the dataflow values associated with a
/// specified Stmt. If the dataflow analysis is a forward analysis,
/// this data corresponds to the point immediately after a Stmt.
/// this data corresponds to the point immediately before a Stmt.
/// If the analysis is a backwards analysis, it is associated with
/// the point before a Stmt. This data is only computed for block-level
/// the point after a Stmt. This data is only computed for block-level
/// expressions, and only when requested when the analysis is executed.
ValTy& getStmtData(const Stmt* S) {
assert (StmtDataMap && "Dataflow values were not computed for statements.");

View File

@ -139,11 +139,11 @@ struct ExprDeclBitVector_Types {
void setCFG(CFG* c) { cfg = c; }
CFG& getCFG() { assert(cfg && "CFG should not be NULL."); return *cfg; }
bool isTracked(const Expr* E) { return cfg->isBlkExpr(E); }
bool isTracked(const Stmt* S) { return cfg->isBlkExpr(S); }
using DeclBitVector_Types::AnalysisDataTy::isTracked;
unsigned getIdx(const Expr* E) const {
CFG::BlkExprNumTy I = cfg->getBlkExprNum(E);
unsigned getIdx(const Stmt* S) const {
CFG::BlkExprNumTy I = cfg->getBlkExprNum(S);
assert(I && "expression not tracked for bitvector.");
return I;
}
@ -187,12 +187,12 @@ struct ExprDeclBitVector_Types {
}
llvm::BitVector::reference
operator()(const Expr* E, const AnalysisDataTy& AD) {
return ExprBV[AD.getIdx(E)];
operator()(const Stmt* S, const AnalysisDataTy& AD) {
return ExprBV[AD.getIdx(S)];
}
const llvm::BitVector::reference
operator()(const Expr* E, const AnalysisDataTy& AD) const {
return const_cast<ValTy&>(*this)(E,AD);
operator()(const Stmt* S, const AnalysisDataTy& AD) const {
return const_cast<ValTy&>(*this)(S,AD);
}
using DeclBitVector_Types::ValTy::operator();

View File

@ -33,7 +33,7 @@ static_cast<ImplClass*>(this)->BlockStmt_Visit ## CLASS(static_cast<CLASS*>(S));
template <typename ImplClass, typename RetTy=void>
class CFGStmtVisitor : public StmtVisitor<ImplClass,RetTy> {
Stmt* CurrentBlkExpr;
Stmt* CurrentBlkStmt;
struct NullifyStmt {
Stmt*& S;
@ -42,11 +42,13 @@ class CFGStmtVisitor : public StmtVisitor<ImplClass,RetTy> {
~NullifyStmt() { S = NULL; }
};
public:
CFGStmtVisitor() : CurrentBlkExpr(NULL) {}
public:
CFGStmtVisitor() : CurrentBlkStmt(NULL) {}
Stmt* getCurrentBlkStmt() const { return CurrentBlkStmt; }
RetTy Visit(Stmt* S) {
if (S == CurrentBlkExpr ||
if (S == CurrentBlkStmt ||
!static_cast<ImplClass*>(this)->getCFG().isBlkExpr(S))
return StmtVisitor<ImplClass,RetTy>::Visit(S);
else
@ -59,8 +61,8 @@ public:
/// is no implementation provided for a BlockStmt_XXX method, we default
/// to using StmtVisitor's Visit method.
RetTy BlockStmt_Visit(Stmt* S) {
CurrentBlkExpr = S;
NullifyStmt cleanup(CurrentBlkExpr);
CurrentBlkStmt = S;
NullifyStmt cleanup(CurrentBlkStmt);
switch (S->getStmtClass()) {
DISPATCH_CASE(CallExpr)