r174676 - [analyzer] Don't reinitialize static globals more than once along a path

Anna Zaks ganna at apple.com
Thu Feb 7 15:05:37 PST 2013


Author: zaks
Date: Thu Feb  7 17:05:37 2013
New Revision: 174676

URL: http://llvm.org/viewvc/llvm-project?rev=174676&view=rev
Log:
[analyzer] Don't reinitialize static globals more than once along a path

This patch makes sure that we do not reinitialize static globals when
the function is called more than once along a path. The motivation is
code with initialization patterns that rely on 2 static variables, where
one of them has an initializer while the other does not. Currently, we
reset the static variables with initializers on every visit to the
function along a path.

Added:
    cfe/trunk/test/Analysis/global_region_invalidation.mm
Modified:
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp
    cfe/trunk/test/Analysis/global-region-invalidation.c

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp?rev=174676&r1=174675&r2=174676&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp Thu Feb  7 17:05:37 2013
@@ -423,37 +423,53 @@ void ExprEngine::VisitCompoundLiteralExp
     B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV));
 }
 
+/// The GDM component containing the set of global variables which have been
+/// previously initialized with explicit initializers.
+REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet,
+                                 llvm::ImmutableSet<const VarDecl *> )
+
 void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
                                ExplodedNodeSet &Dst) {
-  
-  // FIXME: static variables may have an initializer, but the second
-  //  time a function is called those values may not be current.
-  //  This may need to be reflected in the CFG.
-  
   // Assumption: The CFG has one DeclStmt per Decl.
   const Decl *D = *DS->decl_begin();
-  
-  if (!D || !isa<VarDecl>(D)) {
+  const VarDecl *VD = dyn_cast_or_null<VarDecl>(D);
+
+  if (!D || !VD) {
     //TODO:AZ: remove explicit insertion after refactoring is done.
     Dst.insert(Pred);
     return;
   }
+
+  // Check if a value has been previously initialized. There will be an entry in
+  // the set for variables with global storage which have been previously
+  // initialized.
+  if (VD->hasGlobalStorage())
+    if (Pred->getState()->contains<InitializedGlobalsSet>(VD)) {
+      Dst.insert(Pred);
+      return;
+    }
   
   // FIXME: all pre/post visits should eventually be handled by ::Visit().
   ExplodedNodeSet dstPreVisit;
   getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this);
   
   StmtNodeBuilder B(dstPreVisit, Dst, *currBldrCtx);
-  const VarDecl *VD = dyn_cast<VarDecl>(D);
   for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
        I!=E; ++I) {
     ExplodedNode *N = *I;
     ProgramStateRef state = N->getState();
-    
-    // Decls without InitExpr are not initialized explicitly.
     const LocationContext *LC = N->getLocationContext();
-    
+
+    // Decls without InitExpr are not initialized explicitly.
     if (const Expr *InitEx = VD->getInit()) {
+
+      // Note in the state that the initialization has occurred.
+      ExplodedNode *UpdatedN = N;
+      if (VD->hasGlobalStorage()) {
+        state = state->add<InitializedGlobalsSet>(VD);
+        UpdatedN = B.generateNode(DS, N, state);
+      }
+
       SVal InitVal = state->getSVal(InitEx, LC);
 
       if (InitVal == state->getLValue(VD, LC) ||
@@ -461,7 +477,7 @@ void ExprEngine::VisitDeclStmt(const Dec
            isa<CXXConstructExpr>(InitEx->IgnoreImplicit()))) {
         // We constructed the object directly in the variable.
         // No need to bind anything.
-        B.generateNode(DS, N, state);
+        B.generateNode(DS, UpdatedN, state);
       } else {
         // We bound the temp obj region to the CXXConstructExpr. Now recover
         // the lazy compound value when the variable is not a reference.
@@ -482,9 +498,11 @@ void ExprEngine::VisitDeclStmt(const Dec
           InitVal = svalBuilder.conjureSymbolVal(0, InitEx, LC, Ty,
                                                  currBldrCtx->blockCount());
         }
-        B.takeNodes(N);
+
+
+        B.takeNodes(UpdatedN);
         ExplodedNodeSet Dst2;
-        evalBind(Dst2, DS, N, state->getLValue(VD, LC), InitVal, true);
+        evalBind(Dst2, DS, UpdatedN, state->getLValue(VD, LC), InitVal, true);
         B.addNodes(Dst2);
       }
     }

Modified: cfe/trunk/test/Analysis/global-region-invalidation.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/global-region-invalidation.c?rev=174676&r1=174675&r2=174676&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/global-region-invalidation.c (original)
+++ cfe/trunk/test/Analysis/global-region-invalidation.c Thu Feb  7 17:05:37 2013
@@ -84,3 +84,27 @@ void testAnalyzerEvalIsPure() {
   }
 }
 
+// Test that static variables with initializers do not get reinitialized on
+// recursive calls.
+void Function2(void);
+int *getPtr();
+void Function1(void) {
+  static unsigned flag;
+  static int *p = 0;
+  if (!flag) {
+    flag = 1;
+    p = getPtr();
+  }
+  int m = *p; // no-warning: p is never null.
+  m++;
+  Function2();
+}
+void Function2(void) {
+    Function1();
+}
+
+void SetToNonZero(void) {
+  static int g = 5;
+  clang_analyzer_eval(g == 5); // expected-warning{{TRUE}}
+}
+

Added: cfe/trunk/test/Analysis/global_region_invalidation.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/global_region_invalidation.mm?rev=174676&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/global_region_invalidation.mm (added)
+++ cfe/trunk/test/Analysis/global_region_invalidation.mm Thu Feb  7 17:05:37 2013
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_eval(int);
+
+void use(int);
+id foo(int x) {
+  if (x)
+    return 0;
+  static id p = foo(1); 
+    clang_analyzer_eval(p == 0); // expected-warning{{TRUE}}
+  return p;
+}
\ No newline at end of file





More information about the cfe-commits mailing list