From 82b2a1dadae8546e9c6ce44e6201f2c1291dfd2b Mon Sep 17 00:00:00 2001 From: Tom Care Date: Wed, 25 Aug 2010 22:37:26 +0000 Subject: [PATCH] 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 --- .../Analyses/PseudoConstantAnalysis.h | 1 + clang/lib/Analysis/PseudoConstantAnalysis.cpp | 77 +++++++++++-------- clang/test/Analysis/idempotent-operations.c | 28 +++++-- 3 files changed, 70 insertions(+), 36 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/PseudoConstantAnalysis.h b/clang/include/clang/Analysis/Analyses/PseudoConstantAnalysis.h index ea26fd4a418c..cb73850b08c7 100644 --- a/clang/include/clang/Analysis/Analyses/PseudoConstantAnalysis.h +++ b/clang/include/clang/Analysis/Analyses/PseudoConstantAnalysis.h @@ -30,6 +30,7 @@ public: private: void RunAnalysis(); + inline static const Decl *getDecl(const Expr *E); // for storing the result of analyzed ValueDecls void *NonConstantsImpl; diff --git a/clang/lib/Analysis/PseudoConstantAnalysis.cpp b/clang/lib/Analysis/PseudoConstantAnalysis.cpp index 42ebe9c2371f..e8d056e77cdd 100644 --- a/clang/lib/Analysis/PseudoConstantAnalysis.cpp +++ b/clang/lib/Analysis/PseudoConstantAnalysis.cpp @@ -64,6 +64,16 @@ bool PseudoConstantAnalysis::wasReferenced(const VarDecl *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(E)) + return DR->getDecl(); + else if (const BlockDeclRefExpr *BDR = dyn_cast(E)) + return BDR->getDecl(); + else + return 0; +} + void PseudoConstantAnalysis::RunAnalysis() { std::deque WorkList; VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl; @@ -77,28 +87,28 @@ void PseudoConstantAnalysis::RunAnalysis() { WorkList.pop_front(); switch (Head->getStmtClass()) { - // Case 1: Assignment operators modifying ValueDecl + // Case 1: Assignment operators modifying VarDecls case Stmt::BinaryOperatorClass: { const BinaryOperator *BO = cast(Head); - const Expr *LHS = BO->getLHS()->IgnoreParenCasts(); - const DeclRefExpr *DR = dyn_cast(LHS); + // Look for a Decl on the LHS + const Decl *LHSDecl = getDecl(BO->getLHS()->IgnoreParenCasts()); - // We only care about DeclRefExprs on the LHS - if (!DR) + if (!LHSDecl) break; // 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 // written to. switch (BO->getOpcode()) { + // Self-assignments don't count as use of a variable case BO_Assign: { - const Expr *RHS = BO->getRHS()->IgnoreParenCasts(); - if (const DeclRefExpr *RHSDecl = dyn_cast(RHS)) { - // Self-assignments don't count as use of a variable - if (DR->getDecl() == RHSDecl->getDecl()) - // Do not visit the children - continue; - } + // 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 + continue; } case BO_AddAssign: @@ -110,8 +120,8 @@ void PseudoConstantAnalysis::RunAnalysis() { case BO_XorAssign: case BO_ShlAssign: case BO_ShrAssign: { + const VarDecl *VD = dyn_cast(LHSDecl); // The DeclRefExpr is being assigned to - mark it as non-constant - const VarDecl *VD = dyn_cast(DR->getDecl()); if (VD) NonConstants->insert(VD); break; @@ -126,14 +136,11 @@ void PseudoConstantAnalysis::RunAnalysis() { // Case 2: Pre/post increment/decrement and address of case Stmt::UnaryOperatorClass: { const UnaryOperator *UO = cast(Head); - const Expr *SubExpr = UO->getSubExpr()->IgnoreParenImpCasts(); - const DeclRefExpr *DR = dyn_cast(SubExpr); - // We only care about DeclRefExprs in the subexpression - if (!DR) - break; + // Look for a DeclRef in the subexpression + const Decl *D = getDecl(UO->getSubExpr()->IgnoreParenCasts()); - // 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 // addressOf. switch (UO->getOpcode()) { @@ -141,11 +148,11 @@ void PseudoConstantAnalysis::RunAnalysis() { case UO_PostInc: case UO_PreDec: 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: { // If we are taking the address of the DeclRefExpr, assume it is // non-constant. - const VarDecl *VD = dyn_cast(DR->getDecl()); + const VarDecl *VD = dyn_cast(D); if (VD) NonConstants->insert(VD); break; @@ -161,8 +168,8 @@ void PseudoConstantAnalysis::RunAnalysis() { case Stmt::DeclStmtClass: { const DeclStmt *DS = cast(Head); // 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(); - I != E; ++I) { + for (DeclStmt::const_decl_iterator I = DS->decl_begin(), + E = DS->decl_end(); I != E; ++I) { // We only care about VarDecls const VarDecl *VD = dyn_cast(*I); if (!VD) @@ -172,23 +179,24 @@ void PseudoConstantAnalysis::RunAnalysis() { if (!VD->getType().getTypePtr()->isReferenceType()) 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 // list - if (const DeclRefExpr *DR = dyn_cast(VD->getInit())) - if (const VarDecl *RefVD = dyn_cast(DR->getDecl())) { - NonConstants->insert(RefVD); - continue; - } + if (const VarDecl *RefVD = dyn_cast(D)) { + NonConstants->insert(RefVD); + continue; + } } break; } // Case 4: Block variable references case Stmt::BlockDeclRefExprClass: { - // Any block variables are assumed to be non-constant const BlockDeclRefExpr *BDR = cast(Head); if (const VarDecl *VD = dyn_cast(BDR->getDecl())) { - NonConstants->insert(VD); + // Add the Decl to the used list UsedVars->insert(VD); continue; } @@ -199,12 +207,21 @@ void PseudoConstantAnalysis::RunAnalysis() { case Stmt::DeclRefExprClass: { const DeclRefExpr *DR = cast(Head); if (const VarDecl *VD = dyn_cast(DR->getDecl())) { + // Add the Decl to the used list UsedVars->insert(VD); continue; } break; } + // Case 6: Block expressions + case Stmt::BlockExprClass: { + const BlockExpr *B = cast(Head); + // Add the body of the block to the list + WorkList.push_back(B->getBody()); + continue; + } + default: break; } // switch (head->getStmtClass()) diff --git a/clang/test/Analysis/idempotent-operations.c b/clang/test/Analysis/idempotent-operations.c index a730d0312707..09df6dece0b3 100644 --- a/clang/test/Analysis/idempotent-operations.c +++ b/clang/test/Analysis/idempotent-operations.c @@ -112,18 +112,34 @@ unsigned false4() { int c = 42; 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) int width = height; 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 int false5() { static int test = 0;