Improved the handling of blocks and block variables in PseudoConstantAnalysis

- Removed the assumption that __block vars are all non-constant
- Simplified some repetitive code in RunAnalysis
- Added block walking support
- Code/comments cleanup
- Separated out test for block pseudoconstants

llvm-svn: 112098
This commit is contained in:
Tom Care 2010-08-25 22:37:26 +00:00
parent a0d7e434c0
commit 82b2a1dada
3 changed files with 70 additions and 36 deletions

View File

@ -30,6 +30,7 @@ public:
private: private:
void RunAnalysis(); void RunAnalysis();
inline static const Decl *getDecl(const Expr *E);
// for storing the result of analyzed ValueDecls // for storing the result of analyzed ValueDecls
void *NonConstantsImpl; void *NonConstantsImpl;

View File

@ -64,6 +64,16 @@ bool PseudoConstantAnalysis::wasReferenced(const VarDecl *VD) {
return UsedVars->count(VD); return UsedVars->count(VD);
} }
// Returns a Decl from a (Block)DeclRefExpr (if any)
const Decl *PseudoConstantAnalysis::getDecl(const Expr *E) {
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
return DR->getDecl();
else if (const BlockDeclRefExpr *BDR = dyn_cast<BlockDeclRefExpr>(E))
return BDR->getDecl();
else
return 0;
}
void PseudoConstantAnalysis::RunAnalysis() { void PseudoConstantAnalysis::RunAnalysis() {
std::deque<const Stmt *> WorkList; std::deque<const Stmt *> WorkList;
VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl; VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
@ -77,28 +87,28 @@ void PseudoConstantAnalysis::RunAnalysis() {
WorkList.pop_front(); WorkList.pop_front();
switch (Head->getStmtClass()) { switch (Head->getStmtClass()) {
// Case 1: Assignment operators modifying ValueDecl // Case 1: Assignment operators modifying VarDecls
case Stmt::BinaryOperatorClass: { case Stmt::BinaryOperatorClass: {
const BinaryOperator *BO = cast<BinaryOperator>(Head); const BinaryOperator *BO = cast<BinaryOperator>(Head);
const Expr *LHS = BO->getLHS()->IgnoreParenCasts(); // Look for a Decl on the LHS
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS); const Decl *LHSDecl = getDecl(BO->getLHS()->IgnoreParenCasts());
// We only care about DeclRefExprs on the LHS if (!LHSDecl)
if (!DR)
break; break;
// We found a binary operator with a DeclRefExpr on the LHS. We now check // We found a binary operator with a DeclRefExpr on the LHS. We now check
// for any of the assignment operators, implying that this Decl is being // for any of the assignment operators, implying that this Decl is being
// written to. // written to.
switch (BO->getOpcode()) { switch (BO->getOpcode()) {
case BO_Assign: {
const Expr *RHS = BO->getRHS()->IgnoreParenCasts();
if (const DeclRefExpr *RHSDecl = dyn_cast<DeclRefExpr>(RHS)) {
// Self-assignments don't count as use of a variable // Self-assignments don't count as use of a variable
if (DR->getDecl() == RHSDecl->getDecl()) case BO_Assign: {
// Look for a DeclRef on the RHS
const Decl *RHSDecl = getDecl(BO->getRHS()->IgnoreParenCasts());
// If the Decls match, we have self-assignment
if (LHSDecl == RHSDecl)
// Do not visit the children // Do not visit the children
continue; continue;
}
} }
case BO_AddAssign: case BO_AddAssign:
@ -110,8 +120,8 @@ void PseudoConstantAnalysis::RunAnalysis() {
case BO_XorAssign: case BO_XorAssign:
case BO_ShlAssign: case BO_ShlAssign:
case BO_ShrAssign: { case BO_ShrAssign: {
const VarDecl *VD = dyn_cast<VarDecl>(LHSDecl);
// The DeclRefExpr is being assigned to - mark it as non-constant // The DeclRefExpr is being assigned to - mark it as non-constant
const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
if (VD) if (VD)
NonConstants->insert(VD); NonConstants->insert(VD);
break; break;
@ -126,14 +136,11 @@ void PseudoConstantAnalysis::RunAnalysis() {
// Case 2: Pre/post increment/decrement and address of // Case 2: Pre/post increment/decrement and address of
case Stmt::UnaryOperatorClass: { case Stmt::UnaryOperatorClass: {
const UnaryOperator *UO = cast<UnaryOperator>(Head); const UnaryOperator *UO = cast<UnaryOperator>(Head);
const Expr *SubExpr = UO->getSubExpr()->IgnoreParenImpCasts();
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(SubExpr);
// We only care about DeclRefExprs in the subexpression // Look for a DeclRef in the subexpression
if (!DR) const Decl *D = getDecl(UO->getSubExpr()->IgnoreParenCasts());
break;
// We found a unary operator with a DeclRefExpr as a subexpression. We now // We found a unary operator with a DeclRef as a subexpression. We now
// check for any of the increment/decrement operators, as well as // check for any of the increment/decrement operators, as well as
// addressOf. // addressOf.
switch (UO->getOpcode()) { switch (UO->getOpcode()) {
@ -141,11 +148,11 @@ void PseudoConstantAnalysis::RunAnalysis() {
case UO_PostInc: case UO_PostInc:
case UO_PreDec: case UO_PreDec:
case UO_PreInc: case UO_PreInc:
// The DeclRefExpr is being changed - mark it as non-constant // The DeclRef is being changed - mark it as non-constant
case UO_AddrOf: { case UO_AddrOf: {
// If we are taking the address of the DeclRefExpr, assume it is // If we are taking the address of the DeclRefExpr, assume it is
// non-constant. // non-constant.
const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); const VarDecl *VD = dyn_cast<VarDecl>(D);
if (VD) if (VD)
NonConstants->insert(VD); NonConstants->insert(VD);
break; break;
@ -161,8 +168,8 @@ void PseudoConstantAnalysis::RunAnalysis() {
case Stmt::DeclStmtClass: { case Stmt::DeclStmtClass: {
const DeclStmt *DS = cast<DeclStmt>(Head); const DeclStmt *DS = cast<DeclStmt>(Head);
// Iterate over each decl and see if any of them contain reference decls // Iterate over each decl and see if any of them contain reference decls
for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end(); for (DeclStmt::const_decl_iterator I = DS->decl_begin(),
I != E; ++I) { E = DS->decl_end(); I != E; ++I) {
// We only care about VarDecls // We only care about VarDecls
const VarDecl *VD = dyn_cast<VarDecl>(*I); const VarDecl *VD = dyn_cast<VarDecl>(*I);
if (!VD) if (!VD)
@ -172,10 +179,12 @@ void PseudoConstantAnalysis::RunAnalysis() {
if (!VD->getType().getTypePtr()->isReferenceType()) if (!VD->getType().getTypePtr()->isReferenceType())
continue; continue;
// Try to find a Decl in the initializer
const Decl *D = getDecl(VD->getInit()->IgnoreParenCasts());
// If the reference is to another var, add the var to the non-constant // If the reference is to another var, add the var to the non-constant
// list // list
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(VD->getInit())) if (const VarDecl *RefVD = dyn_cast<VarDecl>(D)) {
if (const VarDecl *RefVD = dyn_cast<VarDecl>(DR->getDecl())) {
NonConstants->insert(RefVD); NonConstants->insert(RefVD);
continue; continue;
} }
@ -185,10 +194,9 @@ void PseudoConstantAnalysis::RunAnalysis() {
// Case 4: Block variable references // Case 4: Block variable references
case Stmt::BlockDeclRefExprClass: { case Stmt::BlockDeclRefExprClass: {
// Any block variables are assumed to be non-constant
const BlockDeclRefExpr *BDR = cast<BlockDeclRefExpr>(Head); const BlockDeclRefExpr *BDR = cast<BlockDeclRefExpr>(Head);
if (const VarDecl *VD = dyn_cast<VarDecl>(BDR->getDecl())) { if (const VarDecl *VD = dyn_cast<VarDecl>(BDR->getDecl())) {
NonConstants->insert(VD); // Add the Decl to the used list
UsedVars->insert(VD); UsedVars->insert(VD);
continue; continue;
} }
@ -199,12 +207,21 @@ void PseudoConstantAnalysis::RunAnalysis() {
case Stmt::DeclRefExprClass: { case Stmt::DeclRefExprClass: {
const DeclRefExpr *DR = cast<DeclRefExpr>(Head); const DeclRefExpr *DR = cast<DeclRefExpr>(Head);
if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
// Add the Decl to the used list
UsedVars->insert(VD); UsedVars->insert(VD);
continue; continue;
} }
break; break;
} }
// Case 6: Block expressions
case Stmt::BlockExprClass: {
const BlockExpr *B = cast<BlockExpr>(Head);
// Add the body of the block to the list
WorkList.push_back(B->getBody());
continue;
}
default: default:
break; break;
} // switch (head->getStmtClass()) } // switch (head->getStmtClass())

View File

@ -112,18 +112,34 @@ unsigned false4() {
int c = 42; int c = 42;
test(height * c); // no-warning test(height * c); // no-warning
// Pseudo-constant (blockvar)
__block int a = 0;
int b = 10;
a *= b; // no-warning
test(a);
// Pseudo-constant (never changes after decl) // Pseudo-constant (never changes after decl)
int width = height; int width = height;
return width * 10; // no-warning return width * 10; // no-warning
} }
// Block pseudoconstants
void false4a() {
// Pseudo-constant
__block int a = 1;
int b = 10;
__block int c = 0;
b *= a; // no-warning
^{
// Psuedoconstant block var
test(b * c); // no-warning
// Non-pseudoconstant block var
int d = 0;
test(b * d); // expected-warning{{The right operand to '*' is always 0}}
d = 5;
test(d);
}();
test(a + b);
}
// Static vars are common false positives // Static vars are common false positives
int false5() { int false5() {
static int test = 0; static int test = 0;