Teach CFGBuilder to prune trivially unreachable case statements.

llvm-svn: 126797
This commit is contained in:
Ted Kremenek 2011-03-01 23:12:55 +00:00
parent 98a3c80a3e
commit eff9a7ff91
4 changed files with 148 additions and 38 deletions

View File

@ -210,6 +210,25 @@ struct BlockScopePosPair {
LocalScope::const_iterator scopePosition; LocalScope::const_iterator scopePosition;
}; };
/// TryResult - a class representing a variant over the values
/// 'true', 'false', or 'unknown'. This is returned by tryEvaluateBool,
/// and is used by the CFGBuilder to decide if a branch condition
/// can be decided up front during CFG construction.
class TryResult {
int X;
public:
TryResult(bool b) : X(b ? 1 : 0) {}
TryResult() : X(-1) {}
bool isTrue() const { return X == 1; }
bool isFalse() const { return X == 0; }
bool isKnown() const { return X >= 0; }
void negate() {
assert(isKnown());
X ^= 0x1;
}
};
/// CFGBuilder - This class implements CFG construction from an AST. /// CFGBuilder - This class implements CFG construction from an AST.
/// The builder is stateful: an instance of the builder should be used to only /// The builder is stateful: an instance of the builder should be used to only
/// construct a single CFG. /// construct a single CFG.
@ -238,7 +257,7 @@ class CFGBuilder {
CFGBlock* SwitchTerminatedBlock; CFGBlock* SwitchTerminatedBlock;
CFGBlock* DefaultCaseBlock; CFGBlock* DefaultCaseBlock;
CFGBlock* TryTerminatedBlock; CFGBlock* TryTerminatedBlock;
// Current position in local scope. // Current position in local scope.
LocalScope::const_iterator ScopePos; LocalScope::const_iterator ScopePos;
@ -257,12 +276,17 @@ class CFGBuilder {
bool badCFG; bool badCFG;
CFG::BuildOptions BuildOpts; CFG::BuildOptions BuildOpts;
// State to track for building switch statements.
bool switchExclusivelyCovered;
Expr::EvalResult switchCond;
public: public:
explicit CFGBuilder() : cfg(new CFG()), // crew a new CFG explicit CFGBuilder() : cfg(new CFG()), // crew a new CFG
Block(NULL), Succ(NULL), Block(NULL), Succ(NULL),
SwitchTerminatedBlock(NULL), DefaultCaseBlock(NULL), SwitchTerminatedBlock(NULL), DefaultCaseBlock(NULL),
TryTerminatedBlock(NULL), badCFG(false) {} TryTerminatedBlock(NULL), badCFG(false),
switchExclusivelyCovered(false) {}
// buildCFG - Used by external clients to construct the CFG. // buildCFG - Used by external clients to construct the CFG.
CFG* buildCFG(const Decl *D, Stmt *Statement, ASTContext *C, CFG* buildCFG(const Decl *D, Stmt *Statement, ASTContext *C,
@ -387,45 +411,34 @@ private:
B->addSuccessor(S, cfg->getBumpVectorContext()); B->addSuccessor(S, cfg->getBumpVectorContext());
} }
/// TryResult - a class representing a variant over the values /// Try and evaluate an expression to an integer constant.
/// 'true', 'false', or 'unknown'. This is returned by tryEvaluateBool, bool tryEvaluate(Expr *S, Expr::EvalResult &outResult) {
/// and is used by the CFGBuilder to decide if a branch condition if (!BuildOpts.PruneTriviallyFalseEdges)
/// can be decided up front during CFG construction. return false;
class TryResult { return !S->isTypeDependent() &&
int X; !S->isValueDependent() &&
public: S->Evaluate(outResult, *Context);
TryResult(bool b) : X(b ? 1 : 0) {} }
TryResult() : X(-1) {}
bool isTrue() const { return X == 1; }
bool isFalse() const { return X == 0; }
bool isKnown() const { return X >= 0; }
void negate() {
assert(isKnown());
X ^= 0x1;
}
};
/// tryEvaluateBool - Try and evaluate the Stmt and return 0 or 1 /// tryEvaluateBool - Try and evaluate the Stmt and return 0 or 1
/// if we can evaluate to a known value, otherwise return -1. /// if we can evaluate to a known value, otherwise return -1.
TryResult tryEvaluateBool(Expr *S) { TryResult tryEvaluateBool(Expr *S) {
if (!BuildOpts.PruneTriviallyFalseEdges)
return TryResult();
Expr::EvalResult Result; Expr::EvalResult Result;
if (!S->isTypeDependent() && !S->isValueDependent() && if (!tryEvaluate(S, Result))
S->Evaluate(Result, *Context)) { return TryResult();
if (Result.Val.isInt())
return Result.Val.getInt().getBoolValue(); if (Result.Val.isInt())
if (Result.Val.isLValue()) { return Result.Val.getInt().getBoolValue();
Expr *e = Result.Val.getLValueBase();
const CharUnits &c = Result.Val.getLValueOffset(); if (Result.Val.isLValue()) {
if (!e && c.isZero()) Expr *e = Result.Val.getLValueBase();
return false; const CharUnits &c = Result.Val.getLValueOffset();
} if (!e && c.isZero())
return false;
} }
return TryResult(); return TryResult();
} }
}; };
// FIXME: Add support for dependent-sized array types in C++? // FIXME: Add support for dependent-sized array types in C++?
@ -2180,6 +2193,17 @@ CFGBlock* CFGBuilder::VisitSwitchStmt(SwitchStmt* Terminator) {
assert(Terminator->getBody() && "switch must contain a non-NULL body"); assert(Terminator->getBody() && "switch must contain a non-NULL body");
Block = NULL; Block = NULL;
// For pruning unreachable case statements, save the current state
// for tracking the condition value.
SaveAndRestore<bool> save_switchExclusivelyCovered(switchExclusivelyCovered,
false);
SaveAndRestore<Expr::EvalResult> save_switchCond(switchCond);
// Determine if the switch condition can be explicitly evaluated.
assert(Terminator->getCond() && "switch condition must be non-NULL");
tryEvaluate(Terminator->getCond(), switchCond);
// If body is not a compound statement create implicit scope // If body is not a compound statement create implicit scope
// and add destructors. // and add destructors.
if (!isa<CompoundStmt>(Terminator->getBody())) if (!isa<CompoundStmt>(Terminator->getBody()))
@ -2193,11 +2217,11 @@ CFGBlock* CFGBuilder::VisitSwitchStmt(SwitchStmt* Terminator) {
// If we have no "default:" case, the default transition is to the code // If we have no "default:" case, the default transition is to the code
// following the switch body. // following the switch body.
addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock); addSuccessor(SwitchTerminatedBlock,
switchExclusivelyCovered ? 0 : DefaultCaseBlock);
// Add the terminator and condition in the switch block. // Add the terminator and condition in the switch block.
SwitchTerminatedBlock->setTerminator(Terminator); SwitchTerminatedBlock->setTerminator(Terminator);
assert(Terminator->getCond() && "switch condition must be non-NULL");
Block = SwitchTerminatedBlock; Block = SwitchTerminatedBlock;
Block = addStmt(Terminator->getCond()); Block = addStmt(Terminator->getCond());
@ -2213,6 +2237,44 @@ CFGBlock* CFGBuilder::VisitSwitchStmt(SwitchStmt* Terminator) {
return Block; return Block;
} }
static bool shouldAddCase(bool &switchExclusivelyCovered,
const Expr::EvalResult &switchCond,
const CaseStmt *CS,
ASTContext &Ctx) {
bool addCase = false;
if (!switchExclusivelyCovered) {
if (switchCond.Val.isInt()) {
// Evaluate the LHS of the case value.
Expr::EvalResult V1;
CS->getLHS()->Evaluate(V1, Ctx);
assert(V1.Val.isInt());
const llvm::APSInt &condInt = switchCond.Val.getInt();
const llvm::APSInt &lhsInt = V1.Val.getInt();
if (condInt == lhsInt) {
addCase = true;
switchExclusivelyCovered = true;
}
else if (condInt < lhsInt) {
if (const Expr *RHS = CS->getRHS()) {
// Evaluate the RHS of the case value.
Expr::EvalResult V2;
RHS->Evaluate(V2, Ctx);
assert(V2.Val.isInt());
if (V2.Val.getInt() <= condInt) {
addCase = true;
switchExclusivelyCovered = true;
}
}
}
}
else
addCase = true;
}
return addCase;
}
CFGBlock* CFGBuilder::VisitCaseStmt(CaseStmt* CS) { CFGBlock* CFGBuilder::VisitCaseStmt(CaseStmt* CS) {
// CaseStmts are essentially labels, so they are the first statement in a // CaseStmts are essentially labels, so they are the first statement in a
@ -2232,9 +2294,12 @@ CFGBlock* CFGBuilder::VisitCaseStmt(CaseStmt* CS) {
else else
TopBlock = currentBlock; TopBlock = currentBlock;
addSuccessor(SwitchTerminatedBlock, currentBlock); addSuccessor(SwitchTerminatedBlock,
LastBlock = currentBlock; shouldAddCase(switchExclusivelyCovered, switchCond,
CS, *Context)
? currentBlock : 0);
LastBlock = currentBlock;
CS = cast<CaseStmt>(Sub); CS = cast<CaseStmt>(Sub);
Sub = CS->getSubStmt(); Sub = CS->getSubStmt();
} }
@ -2256,7 +2321,10 @@ CFGBlock* CFGBuilder::VisitCaseStmt(CaseStmt* CS) {
// Add this block to the list of successors for the block with the switch // Add this block to the list of successors for the block with the switch
// statement. // statement.
assert(SwitchTerminatedBlock); assert(SwitchTerminatedBlock);
addSuccessor(SwitchTerminatedBlock, CaseBlock); addSuccessor(SwitchTerminatedBlock,
shouldAddCase(switchExclusivelyCovered, switchCond,
CS, *Context)
? CaseBlock : 0);
// We set Block to NULL to allow lazy creation of a new block (if necessary) // We set Block to NULL to allow lazy creation of a new block (if necessary)
Block = NULL; Block = NULL;

View File

@ -1043,6 +1043,10 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
bool defaultIsFeasible = I == EI; bool defaultIsFeasible = I == EI;
for ( ; I != EI; ++I) { for ( ; I != EI; ++I) {
// Successor may be pruned out during CFG construction.
if (!I.getBlock())
continue;
const CaseStmt* Case = I.getCase(); const CaseStmt* Case = I.getCase();
// Evaluate the LHS of the case value. // Evaluate the LHS of the case value.

View File

@ -1269,3 +1269,22 @@ void pr9287_c(int type, int *p) {
} }
} }
void test_switch() {
switch (4) {
case 1: {
int *p = 0;
*p = 0xDEADBEEF; // no-warning
break;
}
case 4: {
int *p = 0;
*p = 0xDEADBEEF; // expected-warning {{null}}
break;
}
default: {
int *p = 0;
*p = 0xDEADBEEF; // no-warning
break;
}
}
}

View File

@ -127,4 +127,23 @@ int test_sizeof_as_condition(int flag) {
return sizeof(char) == sizeof(char) ? arr[2] : arr[1]; // expected-warning {{array index of '2' indexes past the end of an array (that contains 2 elements)}} return sizeof(char) == sizeof(char) ? arr[2] : arr[1]; // expected-warning {{array index of '2' indexes past the end of an array (that contains 2 elements)}}
} }
void test_switch() {
switch (4) {
case 1: {
int arr[2];
arr[2] = 1; // no-warning
break;
}
case 4: {
int arr[2]; // expected-note {{array 'arr' declared here}}
arr[2] = 1; // expected-warning {{array index of '2' indexes past the end of an array (that contains 2 elements)}}
break;
}
default: {
int arr[2];
arr[2] = 1; // no-warning
break;
}
}
}