From: Zhongxing Xu Date: Wed, 9 Dec 2009 12:23:28 +0000 (+0000) Subject: Refactor OSAtomic evaluation logic into OSAtomicChecker. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1ec4e976113ac56c0b3d96f484613d1dc62c3f85;p=clang Refactor OSAtomic evaluation logic into OSAtomicChecker. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90968 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Analysis/PathSensitive/Checker.h b/include/clang/Analysis/PathSensitive/Checker.h index 080f4d334a..9873710976 100644 --- a/include/clang/Analysis/PathSensitive/Checker.h +++ b/include/clang/Analysis/PathSensitive/Checker.h @@ -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 { diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index c0d97f85fd..521f1be6ec 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -40,6 +40,7 @@ add_clang_library(clangAnalysis NoReturnFunctionChecker.cpp NSAutoreleasePoolChecker.cpp NSErrorChecker.cpp + OSAtomicChecker.cpp PathDiagnostic.cpp PointerArithChecker.cpp PointerSubChecker.cpp diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index a31b57331b..fc7bd34661 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -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(); - - // 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(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(theValueVal_untested); - DefinedOrUnknownSVal oldValueVal = - cast(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(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. diff --git a/lib/Analysis/GRExprEngineInternalChecks.h b/lib/Analysis/GRExprEngineInternalChecks.h index 5950efaa69..e2354ed098 100644 --- a/lib/Analysis/GRExprEngineInternalChecks.h +++ b/lib/Analysis/GRExprEngineInternalChecks.h @@ -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 index 0000000000..03e9e38206 --- /dev/null +++ b/lib/Analysis/OSAtomicChecker.cpp @@ -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(); + + // 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(R)->getValueType(Ctx); + location = loc::MemRegionVal(R); + } + Engine.EvalLoad(Tmp, const_cast(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(theValueVal_untested); + DefinedOrUnknownSVal oldValueVal = + cast(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(location.getAsRegion())) { + llvm::tie(state, val) = SVator.EvalCast(val, state,R->getValueType(Ctx), + newValueExpr->getType()); + } + + Engine.EvalStore(TmpStore, NULL, const_cast(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; +}