// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*-- // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines the methods for CFRefCount, which implements // a reference count checker for Core Foundation (Mac OS X). // //===----------------------------------------------------------------------===// #include "GRSimpleVals.h" #include "clang/Analysis/PathSensitive/ValueState.h" #include "clang/Basic/Diagnostic.h" #include "clang/Analysis/LocalCheckers.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableMap.h" using namespace clang; namespace { enum ArgEffect { IncRef, DecRef, DoNothing }; typedef std::vector ArgEffects; } namespace llvm { template <> struct FoldingSetTrait { static void Profile(const ArgEffects& X, FoldingSetNodeID ID) { for (ArgEffects::const_iterator I = X.begin(), E = X.end(); I!= E; ++I) ID.AddInteger((unsigned) *I); } static void Profile(ArgEffects& X, FoldingSetNodeID ID) { Profile(X, ID); } }; } // end llvm namespace namespace { class RetEffect { public: enum Kind { Alias = 0x0, OwnedSymbol = 0x1, NotOwnedSymbol = 0x2 }; private: unsigned Data; RetEffect(Kind k, unsigned D) { Data = (Data << 2) | (unsigned) k; } public: Kind getKind() const { return (Kind) (Data & 0x3); } unsigned getValue() const { assert(getKind() == Alias); return Data & ~0x3; } static RetEffect MakeAlias(unsigned Idx) { return RetEffect(Alias, Idx); } static RetEffect MakeOwned() { return RetEffect(OwnedSymbol, 0); } static RetEffect MakeNotOwned() { return RetEffect(NotOwnedSymbol, 0); } operator Kind() const { return getKind(); } void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger(Data); } }; class CFRefSummary : public llvm::FoldingSetNode { ArgEffects* Args; RetEffect Ret; public: CFRefSummary(ArgEffects* A, RetEffect R) : Args(A), Ret(R) {} unsigned getNumArgs() const { return Args->size(); } typedef ArgEffects::const_iterator arg_iterator; arg_iterator begin_args() const { return Args->begin(); } arg_iterator end_args() const { return Args->end(); } static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects* A, RetEffect R) { ID.AddPointer(A); ID.Add(R); } void Profile(llvm::FoldingSetNodeID& ID) const { Profile(ID, Args, Ret); } }; class CFRefSummaryManager { typedef llvm::FoldingSet > AESetTy; typedef llvm::FoldingSet SummarySetTy; typedef llvm::DenseMap SummaryMapTy; SummarySetTy SummarySet; SummaryMapTy SummaryMap; AESetTy AESet; llvm::BumpPtrAllocator BPAlloc; ArgEffects ScratchArgs; public: CFRefSummaryManager() {} ~CFRefSummaryManager(); CFRefSummary* getSummary(FunctionDecl* FD); }; } // end anonymous namespace //===----------------------------------------------------------------------===// // Implementation of checker data structures. //===----------------------------------------------------------------------===// CFRefSummaryManager::~CFRefSummaryManager() { // FIXME: The ArgEffects could eventually be allocated from BPAlloc, // mitigating the need to do explicit cleanup of the // Argument-Effect summaries. for (AESetTy::iterator I = AESet.begin(), E = AESet.end(); I!=E; ++I) I->getValue().~ArgEffects(); } CFRefSummary* CFRefSummaryManager::getSummary(FunctionDecl* FD) { { // Look into our cache of summaries to see if we have already computed // a summary for this FunctionDecl. SummaryMapTy::iterator I = SummaryMap.find(FD); if (I != SummaryMap.end()) return I->second; } // return NULL; } //===----------------------------------------------------------------------===// // Transfer functions. //===----------------------------------------------------------------------===// typedef unsigned RefState; // FIXME namespace { class CFRefCount : public GRSimpleVals { typedef llvm::ImmutableMap RefBindings; typedef RefBindings::Factory RefBFactoryTy; CFRefSummaryManager Summaries; RefBFactoryTy RefBFactory; static RefBindings GetRefBindings(ValueState& StImpl) { return RefBindings((RefBindings::TreeTy*) StImpl.CheckerState); } static void SetRefBindings(ValueState& StImpl, RefBindings B) { StImpl.CheckerState = B.getRoot(); } RefBindings Remove(RefBindings B, SymbolID sym) { return RefBFactory.Remove(B, sym); } RefBindings Update(RefBindings B, SymbolID sym, CFRefSummary* Summ, unsigned ArgIdx); public: CFRefCount() {} virtual ~CFRefCount() {} // Calls. virtual void EvalCall(ExplodedNodeSet& Dst, ValueStateManager& StateMgr, GRStmtNodeBuilder& Builder, BasicValueFactory& BasicVals, CallExpr* CE, LVal L, ExplodedNode* Pred); }; } // end anonymous namespace void CFRefCount::EvalCall(ExplodedNodeSet& Dst, ValueStateManager& StateMgr, GRStmtNodeBuilder& Builder, BasicValueFactory& BasicVals, CallExpr* CE, LVal L, ExplodedNode* Pred) { // FIXME: Support calls to things other than lval::FuncVal. At the very // least we should stop tracking ref-state for ref-counted objects passed // to these functions. assert (isa(L) && "Not yet implemented."); // Get the summary. lval::FuncVal FV = cast(L); FunctionDecl* FD = FV.getDecl(); CFRefSummary* Summ = Summaries.getSummary(FD); // Get the state. ValueState* St = Builder.GetState(Pred); // Evaluate the effects of the call. ValueState StVals = *St; if (!Summ) { // This function has no summary. Invalidate all reference-count state // for arguments passed to this function, and also nuke the values of // arguments passed-by-reference. ValueState StVals = *St; for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { RVal V = StateMgr.GetRVal(St, *I); if (isa(V)) { SymbolID Sym = cast(V).getSymbol(); RefBindings B = GetRefBindings(StVals); SetRefBindings(StVals, Remove(B, Sym)); } if (isa(V)) StateMgr.Unbind(StVals, cast(V)); } } else { // This function has a summary. Evaluate the effect of the arguments. unsigned idx = 0; for (CallExpr::arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; ++I, ++idx) { RVal V = StateMgr.GetRVal(St, *I); if (isa(V)) { SymbolID Sym = cast(V).getSymbol(); RefBindings B = GetRefBindings(StVals); SetRefBindings(StVals, Update(B, Sym, Summ, idx)); } } } St = StateMgr.getPersistentState(StVals); Builder.Nodify(Dst, CE, Pred, St); } CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym, CFRefSummary* Summ, unsigned ArgIdx){ assert (Summ); // FIXME: Implement. return B; } //===----------------------------------------------------------------------===// // Driver for the CFRefCount Checker. //===----------------------------------------------------------------------===// namespace clang { void CheckCFRefCount(CFG& cfg, FunctionDecl& FD, ASTContext& Ctx, Diagnostic& Diag) { if (Diag.hasErrorOccurred()) return; // FIXME: Refactor some day so this becomes a single function invocation. GRCoreEngine Engine(cfg, FD, Ctx); GRExprEngine* CS = &Engine.getCheckerState(); CFRefCount TF; CS->setTransferFunctions(TF); Engine.ExecuteWorkList(20000); } } // end clang namespace