]> granicus.if.org Git - clang/commitdiff
Refactor OSAtomic evaluation logic into OSAtomicChecker.
authorZhongxing Xu <xuzhongxing@gmail.com>
Wed, 9 Dec 2009 12:23:28 +0000 (12:23 +0000)
committerZhongxing Xu <xuzhongxing@gmail.com>
Wed, 9 Dec 2009 12:23:28 +0000 (12:23 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90968 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Analysis/PathSensitive/Checker.h
lib/Analysis/CMakeLists.txt
lib/Analysis/GRExprEngine.cpp
lib/Analysis/GRExprEngineInternalChecks.h
lib/Analysis/OSAtomicChecker.cpp [new file with mode: 0644]

index 080f4d334a322d288050fdf79b9fd1a66d154c8a..9873710976b4606044d648a5cc6891f5abf10cc3 100644 (file)
@@ -56,7 +56,11 @@ public:
       ST(st), statement(stmt), size(Dst.size()) {}
 
   ~CheckerContext();
-  
+
+  GRExprEngine &getEngine() {
+    return Eng;
+  }
+
   ConstraintManager &getConstraintManager() {
       return Eng.getConstraintManager();
   }
@@ -103,6 +107,15 @@ public:
     return N;
   }
 
+  ExplodedNode *GenerateNode(const GRState *state, ExplodedNode *pred,
+                             bool autoTransition = true) {
+   assert(statement && "Only transitions with statements currently supported");
+    ExplodedNode *N = GenerateNodeImpl(statement, state, pred, false);
+    if (N && autoTransition)
+      addTransition(N);
+    return N;
+  }
+
   ExplodedNode *GenerateNode(const GRState *state, bool autoTransition = true) {
     assert(statement && "Only transitions with statements currently supported");
     ExplodedNode *N = GenerateNodeImpl(statement, state, false);
@@ -144,7 +157,14 @@ private:
       node->markAsSink();
     return node;
   }
-  
+
+  ExplodedNode *GenerateNodeImpl(const Stmt* stmt, const GRState *state,
+                                 ExplodedNode *pred, bool markAsSink) {
+   ExplodedNode *node = B.generateNode(stmt, state, pred);
+    if (markAsSink && node)
+      node->markAsSink();
+    return node;
+  }
 };
 
 class Checker {
index c0d97f85fd9eb2a9d91f7a9dfe192e39cf8aa28a..521f1be6ec8ba340f49c82adfae5734f28381dda 100644 (file)
@@ -40,6 +40,7 @@ add_clang_library(clangAnalysis
   NoReturnFunctionChecker.cpp
   NSAutoreleasePoolChecker.cpp
   NSErrorChecker.cpp
+  OSAtomicChecker.cpp
   PathDiagnostic.cpp
   PointerArithChecker.cpp
   PointerSubChecker.cpp
index a31b57331b21fae5bbce2a077c96697196479f25..fc7bd34661ee2f940403c3d59aa57723593a639b 100644 (file)
@@ -273,6 +273,7 @@ static void RegisterInternalChecks(GRExprEngine &Eng) {
   // This is not a checker yet.
   RegisterNoReturnFunctionChecker(Eng);
   RegisterBuiltinFunctionChecker(Eng);
+  RegisterOSAtomicChecker(Eng);
 }
 
 GRExprEngine::GRExprEngine(AnalysisManager &mgr)
@@ -1448,154 +1449,6 @@ void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S,
   }
 }
 
-//===----------------------------------------------------------------------===//
-// Transfer function: OSAtomics.
-//
-// FIXME: Eventually refactor into a more "plugin" infrastructure.
-//===----------------------------------------------------------------------===//
-
-// Mac OS X:
-// http://developer.apple.com/documentation/Darwin/Reference/Manpages/man3
-// atomic.3.html
-//
-static bool EvalOSAtomicCompareAndSwap(ExplodedNodeSet& Dst,
-                                       GRExprEngine& Engine,
-                                       GRStmtNodeBuilder& Builder,
-                                       CallExpr* CE, ExplodedNode* Pred) {
-
-  // Not enough arguments to match OSAtomicCompareAndSwap?
-  if (CE->getNumArgs() != 3)
-    return false;
-
-  ASTContext &C = Engine.getContext();
-  Expr *oldValueExpr = CE->getArg(0);
-  QualType oldValueType = C.getCanonicalType(oldValueExpr->getType());
-
-  Expr *newValueExpr = CE->getArg(1);
-  QualType newValueType = C.getCanonicalType(newValueExpr->getType());
-
-  // Do the types of 'oldValue' and 'newValue' match?
-  if (oldValueType != newValueType)
-    return false;
-
-  Expr *theValueExpr = CE->getArg(2);
-  const PointerType *theValueType =
-    theValueExpr->getType()->getAs<PointerType>();
-
-  // theValueType not a pointer?
-  if (!theValueType)
-    return false;
-
-  QualType theValueTypePointee =
-    C.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType();
-
-  // The pointee must match newValueType and oldValueType.
-  if (theValueTypePointee != newValueType)
-    return false;
-
-  static unsigned magic_load = 0;
-  static unsigned magic_store = 0;
-
-  const void *OSAtomicLoadTag = &magic_load;
-  const void *OSAtomicStoreTag = &magic_store;
-
-  // Load 'theValue'.
-  const GRState *state = Pred->getState();
-  ExplodedNodeSet Tmp;
-  SVal location = state->getSVal(theValueExpr);
-  // Here we should use the value type of the region as the load type.
-  const MemRegion *R = location.getAsRegion()->StripCasts();
-  QualType LoadTy;
-  if (R) {
-    LoadTy = cast<TypedRegion>(R)->getValueType(C);
-    // Use the region as the real load location.
-    location = loc::MemRegionVal(R);
-  }
-  Engine.EvalLoad(Tmp, theValueExpr, Pred, state, location, OSAtomicLoadTag,
-                  LoadTy);
-
-  for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end();
-       I != E; ++I) {
-
-    ExplodedNode *N = *I;
-    const GRState *stateLoad = N->getState();
-    SVal theValueVal_untested = stateLoad->getSVal(theValueExpr);
-    SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr);
-
-    // FIXME: Issue an error.
-    if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) {
-      return false;
-    }
-    
-    DefinedOrUnknownSVal theValueVal =
-      cast<DefinedOrUnknownSVal>(theValueVal_untested);
-    DefinedOrUnknownSVal oldValueVal =
-      cast<DefinedOrUnknownSVal>(oldValueVal_untested);
-
-    SValuator &SVator = Engine.getSValuator();
-
-    // Perform the comparison.
-    DefinedOrUnknownSVal Cmp = SVator.EvalEQ(stateLoad, theValueVal,
-                                             oldValueVal);
-
-    const GRState *stateEqual = stateLoad->Assume(Cmp, true);
-
-    // Were they equal?
-    if (stateEqual) {
-      // Perform the store.
-      ExplodedNodeSet TmpStore;
-      SVal val = stateEqual->getSVal(newValueExpr);
-
-      // Handle implicit value casts.
-      if (const TypedRegion *R =
-          dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
-        llvm::tie(state, val) = SVator.EvalCast(val, state, R->getValueType(C),
-                                                newValueExpr->getType());
-      }
-
-      Engine.EvalStore(TmpStore, NULL, theValueExpr, N, stateEqual, location,
-                       val, OSAtomicStoreTag);
-
-      // Now bind the result of the comparison.
-      for (ExplodedNodeSet::iterator I2 = TmpStore.begin(),
-           E2 = TmpStore.end(); I2 != E2; ++I2) {
-        ExplodedNode *predNew = *I2;
-        const GRState *stateNew = predNew->getState();
-        SVal Res = Engine.getValueManager().makeTruthVal(true, CE->getType());
-        Engine.MakeNode(Dst, CE, predNew, stateNew->BindExpr(CE, Res));
-      }
-    }
-
-    // Were they not equal?
-    if (const GRState *stateNotEqual = stateLoad->Assume(Cmp, false)) {
-      SVal Res = Engine.getValueManager().makeTruthVal(false, CE->getType());
-      Engine.MakeNode(Dst, CE, N, stateNotEqual->BindExpr(CE, Res));
-    }
-  }
-
-  return true;
-}
-
-static bool EvalOSAtomic(ExplodedNodeSet& Dst,
-                         GRExprEngine& Engine,
-                         GRStmtNodeBuilder& Builder,
-                         CallExpr* CE, SVal L,
-                         ExplodedNode* Pred) {
-  const FunctionDecl* FD = L.getAsFunctionDecl();
-  if (!FD)
-    return false;
-
-  const char *FName = FD->getNameAsCString();
-
-  // Check for compare and swap.
-  if (strncmp(FName, "OSAtomicCompareAndSwap", 22) == 0 ||
-      strncmp(FName, "objc_atomicCompareAndSwap", 25) == 0)
-    return EvalOSAtomicCompareAndSwap(Dst, Engine, Builder, CE, Pred);
-
-  // FIXME: Other atomics.
-  return false;
-}
-
 //===----------------------------------------------------------------------===//
 // Transfer function: Function calls.
 //===----------------------------------------------------------------------===//
@@ -1686,8 +1539,7 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, ExplodedNode* Pred,
         // FIXME: Allow us to chain together transfer functions.
         assert(Builder && "GRStmtNodeBuilder must be defined.");
     
-        if (!EvalOSAtomic(DstTmp3, *this, *Builder, CE, L, Pred))
-          getTF().EvalCall(DstTmp3, *this, *Builder, CE, L, Pred);
+        getTF().EvalCall(DstTmp3, *this, *Builder, CE, L, Pred);
 
         // Handle the case where no nodes where generated.  Auto-generate that
         // contains the updated state if we aren't generating sinks.
index 5950efaa69474361448393d1a55da57674e3a215..e2354ed098880791e4d61af834cda9693ab75751 100644 (file)
@@ -39,6 +39,6 @@ void RegisterUndefResultChecker(GRExprEngine &Eng);
 
 void RegisterNoReturnFunctionChecker(GRExprEngine &Eng);
 void RegisterBuiltinFunctionChecker(GRExprEngine &Eng);
-
+void RegisterOSAtomicChecker(GRExprEngine &Eng);
 } // end clang namespace
 #endif
diff --git a/lib/Analysis/OSAtomicChecker.cpp b/lib/Analysis/OSAtomicChecker.cpp
new file mode 100644 (file)
index 0000000..03e9e38
--- /dev/null
@@ -0,0 +1,187 @@
+//=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker evaluates OSAtomic functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GRExprEngineInternalChecks.h"
+#include "clang/Analysis/PathSensitive/Checker.h"
+#include "clang/Basic/Builtins.h"
+#include "llvm/ADT/StringSwitch.h"
+
+using namespace clang;
+
+namespace {
+
+class OSAtomicChecker : public Checker {
+public:
+  static void *getTag() { static int tag = 0; return &tag; }
+  virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE);
+
+private:
+  bool EvalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE);
+};
+
+}
+
+void clang::RegisterOSAtomicChecker(GRExprEngine &Eng) {
+  Eng.registerCheck(new OSAtomicChecker());
+}
+
+bool OSAtomicChecker::EvalCallExpr(CheckerContext &C,const CallExpr *CE) {
+  const GRState *state = C.getState();
+  const Expr *Callee = CE->getCallee();
+  SVal L = state->getSVal(Callee);
+
+  const FunctionDecl* FD = L.getAsFunctionDecl();
+  if (!FD)
+    return false;
+
+  const char *FName = FD->getNameAsCString();
+
+  // Check for compare and swap.
+  if (strncmp(FName, "OSAtomicCompareAndSwap", 22) == 0 ||
+      strncmp(FName, "objc_atomicCompareAndSwap", 25) == 0)
+    return EvalOSAtomicCompareAndSwap(C, CE);
+
+  // FIXME: Other atomics.
+  return false;
+}
+
+bool OSAtomicChecker::EvalOSAtomicCompareAndSwap(CheckerContext &C, 
+                                                 const CallExpr *CE) {
+  // Not enough arguments to match OSAtomicCompareAndSwap?
+  if (CE->getNumArgs() != 3)
+    return false;
+
+  ASTContext &Ctx = C.getASTContext();
+  const Expr *oldValueExpr = CE->getArg(0);
+  QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType());
+
+  const Expr *newValueExpr = CE->getArg(1);
+  QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType());
+
+  // Do the types of 'oldValue' and 'newValue' match?
+  if (oldValueType != newValueType)
+    return false;
+
+  const Expr *theValueExpr = CE->getArg(2);
+  const PointerType *theValueType=theValueExpr->getType()->getAs<PointerType>();
+
+  // theValueType not a pointer?
+  if (!theValueType)
+    return false;
+
+  QualType theValueTypePointee =
+    Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType();
+
+  // The pointee must match newValueType and oldValueType.
+  if (theValueTypePointee != newValueType)
+    return false;
+
+  static unsigned magic_load = 0;
+  static unsigned magic_store = 0;
+
+  const void *OSAtomicLoadTag = &magic_load;
+  const void *OSAtomicStoreTag = &magic_store;
+
+  // Load 'theValue'.
+  GRExprEngine &Engine = C.getEngine();
+  const GRState *state = C.getState();
+  ExplodedNodeSet Tmp;
+  SVal location = state->getSVal(theValueExpr);
+  // Here we should use the value type of the region as the load type.
+  const MemRegion *R = location.getAsRegion()->StripCasts();
+  QualType LoadTy;
+  if (R) {
+    LoadTy = cast<TypedRegion>(R)->getValueType(Ctx);
+    location = loc::MemRegionVal(R);
+  }
+  Engine.EvalLoad(Tmp, const_cast<Expr *>(theValueExpr), C.getPredecessor(), 
+                  state, location, OSAtomicLoadTag, LoadTy);
+
+  if (Tmp.empty()) {
+    // If no nodes were generated, other checkers must generated sinks. But 
+    // since the builder state was restored, we set it manually to prevent 
+    // auto transition.
+    // FIXME: there should be a better approach.
+    C.getNodeBuilder().BuildSinks = true;
+    return true;
+  }
+  for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end();
+       I != E; ++I) {
+
+    ExplodedNode *N = *I;
+    const GRState *stateLoad = N->getState();
+    SVal theValueVal_untested = stateLoad->getSVal(theValueExpr);
+    SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr);
+
+    // FIXME: Issue an error.
+    if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) {
+      return false;
+    }
+    
+    DefinedOrUnknownSVal theValueVal =
+      cast<DefinedOrUnknownSVal>(theValueVal_untested);
+    DefinedOrUnknownSVal oldValueVal =
+      cast<DefinedOrUnknownSVal>(oldValueVal_untested);
+
+    SValuator &SVator = Engine.getSValuator();
+
+    // Perform the comparison.
+    DefinedOrUnknownSVal Cmp = SVator.EvalEQ(stateLoad,theValueVal,oldValueVal);
+
+    const GRState *stateEqual = stateLoad->Assume(Cmp, true);
+
+    // Were they equal?
+    if (stateEqual) {
+      // Perform the store.
+      ExplodedNodeSet TmpStore;
+      SVal val = stateEqual->getSVal(newValueExpr);
+
+      // Handle implicit value casts.
+      if (const TypedRegion *R =
+          dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
+        llvm::tie(state, val) = SVator.EvalCast(val, state,R->getValueType(Ctx),
+                                                newValueExpr->getType());
+      }
+
+      Engine.EvalStore(TmpStore, NULL, const_cast<Expr *>(theValueExpr), N, 
+                       stateEqual, location, val, OSAtomicStoreTag);
+
+      if (TmpStore.empty()) {
+        // If no nodes were generated, other checkers must generated sinks. But 
+        // since the builder state was restored, we set it manually to prevent 
+        // auto transition.
+        // FIXME: there should be a better approach.
+        C.getNodeBuilder().BuildSinks = true;
+        return true;
+      }
+
+      // Now bind the result of the comparison.
+      for (ExplodedNodeSet::iterator I2 = TmpStore.begin(),
+           E2 = TmpStore.end(); I2 != E2; ++I2) {
+        ExplodedNode *predNew = *I2;
+        const GRState *stateNew = predNew->getState();
+        SVal Res = Engine.getValueManager().makeTruthVal(true, CE->getType());
+        C.GenerateNode(stateNew->BindExpr(CE, Res), predNew);
+      }
+    }
+
+    // Were they not equal?
+    if (const GRState *stateNotEqual = stateLoad->Assume(Cmp, false)) {
+      SVal Res = Engine.getValueManager().makeTruthVal(false, CE->getType());
+      C.GenerateNode(stateNotEqual->BindExpr(CE, Res), N);
+    }
+  }
+
+  return true;
+}