[cfe-commits] r139369 - in /cfe/trunk: include/clang/Analysis/Analyses/ThreadSafety.h lib/Analysis/CMakeLists.txt lib/Analysis/ThreadSafety.cpp lib/Sema/AnalysisBasedWarnings.cpp

Chandler Carruth chandlerc at google.com
Fri Sep 9 09:41:54 PDT 2011


On Fri, Sep 9, 2011 at 9:11 AM, Caitlin Sadowski <supertri at google.com>wrote:

> Author: supertri
> Date: Fri Sep  9 11:11:56 2011
> New Revision: 139369
>
> URL: http://llvm.org/viewvc/llvm-project?rev=139369&view=rev
> Log:
> Thread Safety: Moving the analysis to a new file
>
> Added:
>    cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h
>    cfe/trunk/lib/Analysis/ThreadSafety.cpp
> Modified:
>    cfe/trunk/lib/Analysis/CMakeLists.txt
>    cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
>
> Added: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h?rev=139369&view=auto
>
> ==============================================================================
> --- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h (added)
> +++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafety.h Fri Sep  9
> 11:11:56 2011
> @@ -0,0 +1,71 @@
> +//===- ThreadSafety.h ------------------------------------------*- C++
> --*-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
>
> +//===----------------------------------------------------------------------===//
> +//
> +//
> +// A intra-procedural analysis for thread safety (e.g. deadlocks and race
> +// conditions), based off of an annotation system.
> +//
> +// See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation for the gcc
> version.
>

Can you add a FIXME to write documentation for the Clang version? It seems
poor form to just refer to the GCC documentation, especially as we already
have small divergences from it and are likely to have ever more as time
passes.


> +//
>
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_CLANG_THREADSAFETY_H
> +#define LLVM_CLANG_THREADSAFETY_H
> +
> +#include "clang/Basic/SourceLocation.h"
> +#include "llvm/ADT/StringRef.h"
> +#include "clang/Analysis/AnalysisContext.h"
> +#include "clang/Sema/SemaInternal.h"
> +
> +namespace clang {
> +namespace thread_safety {
> +
> +enum ProtectedOperationKind {
> +  POK_VarDereference,
> +  POK_VarAccess,
> +  POK_FunctionCall
> +};
> +
> +enum LockKind {
> +  LK_Shared,
> +  LK_Exclusive
> +};
> +
> +enum AccessKind {
> +  AK_Read,
> +  AK_Written
> +};
> +
>

Where did all the doxygen comments go?

Because this is an interface, you should document it here in the header
really carefully.

+class ThreadSafetyHandler {
> +public:
> +  typedef llvm::StringRef Name;
> +  ThreadSafetyHandler() {}
>

Why is this needed?


> +  virtual ~ThreadSafetyHandler() {}
>

Why is this implemented? Implementing the others as no-ops is nice because
the subclass can choose which to implement, but I don't see a reason to
support creating a bare ThreadSafetyHandler, there should always be *some*
subclass used...


> +  virtual void handleUnmatchedUnlock(Name LockName, SourceLocation Loc) {}
> +  virtual void handleDoubleLock(Name LockName, SourceLocation Loc) {}
> +  virtual void handleMutexHeldEndOfScope(Name LockName, SourceLocation
> Loc){}
> +  virtual void handleNoLockLoopEntry(Name LockName, SourceLocation Loc) {}
> +  virtual void handleNoUnlock(Name LockName, Name FunName,
> +                              SourceLocation Loc) {}
> +  virtual void handleExclusiveAndShared(Name LockName, SourceLocation
> Loc1,
> +                                        SourceLocation Loc2) {}
> +  virtual void handleNoMutexHeld(const NamedDecl *D,
> ProtectedOperationKind POK,
> +                                 AccessKind AK, SourceLocation Loc) {}
> +  virtual void handleMutexNotHeld(const NamedDecl *D,
> +                                  ProtectedOperationKind POK, Name
> LockName,
> +                                  LockKind LK, SourceLocation Loc) {}
> +  virtual void handleFunExcludesLock(Name FunName, Name LockName,
> +                                     SourceLocation Loc) {}
> +};
> +
> +void runThreadSafetyAnalysis(AnalysisContext &AC, ThreadSafetyHandler
> &Handler);
> +LockKind getLockKindFromAccessKind(AccessKind AK);
> +
> +}} // end namespace clang::thread_safety
> +#endif
>
> Modified: cfe/trunk/lib/Analysis/CMakeLists.txt
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CMakeLists.txt?rev=139369&r1=139368&r2=139369&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Analysis/CMakeLists.txt (original)
> +++ cfe/trunk/lib/Analysis/CMakeLists.txt Fri Sep  9 11:11:56 2011
> @@ -13,6 +13,7 @@
>   PseudoConstantAnalysis.cpp
>   ReachableCode.cpp
>   ScanfFormatString.cpp
> +  ThreadSafety.cpp
>   UninitializedValues.cpp
>   )
>
>
> Added: cfe/trunk/lib/Analysis/ThreadSafety.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafety.cpp?rev=139369&view=auto
>
> ==============================================================================
> --- cfe/trunk/lib/Analysis/ThreadSafety.cpp (added)
> +++ cfe/trunk/lib/Analysis/ThreadSafety.cpp Fri Sep  9 11:11:56 2011
> @@ -0,0 +1,794 @@
> +//===- ThreadSafety.cpp ----------------------------------------*- C++
> --*-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
>
> +//===----------------------------------------------------------------------===//
> +//
> +// A intra-procedural analysis for thread safety (e.g. deadlocks and race
> +// conditions), based off of an annotation system.
> +//
> +// See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation for the gcc
> version.
> +//
>
> +//===----------------------------------------------------------------------===//
> +
> +#include "clang/Analysis/Analyses/ThreadSafety.h"
> +#include "clang/Sema/SemaInternal.h"
> +#include "clang/Basic/SourceManager.h"
> +#include "clang/Basic/SourceLocation.h"
> +#include "clang/AST/DeclCXX.h"
> +#include "clang/AST/ExprCXX.h"
> +#include "clang/AST/StmtCXX.h"
> +#include "clang/AST/StmtVisitor.h"
> +#include "clang/Analysis/AnalysisContext.h"
> +#include "clang/Analysis/CFG.h"
> +#include "clang/Analysis/CFGStmtMap.h"
> +#include "llvm/ADT/BitVector.h"
> +#include "llvm/ADT/FoldingSet.h"
> +#include "llvm/ADT/ImmutableMap.h"
> +#include "llvm/ADT/PostOrderIterator.h"
> +#include "llvm/ADT/SmallVector.h"
> +#include "llvm/ADT/StringRef.h"
> +#include <algorithm>
> +#include <vector>
> +
> +using namespace clang;
> +using namespace thread_safety;
> +
> +namespace {
> +/// \brief Implements a set of CFGBlocks using a BitVector.
> +///
> +/// This class contains a minimal interface, primarily dictated by the
> SetType
> +/// template parameter of the llvm::po_iterator template, as used with
> external
> +/// storage. We also use this set to keep track of which CFGBlocks we
> visit
> +/// during the analysis.
> +class CFGBlockSet {
> +  llvm::BitVector VisitedBlockIDs;
> +
> +public:
> +  // po_iterator requires this iterator, but the only interface needed is
> the
> +  // value_type typedef.
> +  struct iterator {
> +    typedef const CFGBlock *value_type;
> +  };
> +
> +  CFGBlockSet() {}
> +  CFGBlockSet(const CFG *G) : VisitedBlockIDs(G->getNumBlockIDs(), false)
> {}
> +
> +  /// \brief Set the bit associated with a particular CFGBlock.
> +  /// This is the important method for the SetType template parameter.
> +  bool insert(const CFGBlock *Block) {
> +    // Note that insert() is called by po_iterator, which doesn't check to
> make
> +    // sure that Block is non-null.  Moreover, the CFGBlock iterator will
> +    // occasionally hand out null pointers for pruned edges, so we catch
> those
> +    // here.
> +    if (Block == 0)
> +      return false;  // if an edge is trivially false.
> +    if (VisitedBlockIDs.test(Block->getBlockID()))
> +      return false;
> +    VisitedBlockIDs.set(Block->getBlockID());
> +    return true;
> +  }
> +
> +  /// \brief Check if the bit for a CFGBlock has been already set.
> +  /// This method is for tracking visited blocks in the main threadsafety
> loop.
> +  /// Block must not be null.
> +  bool alreadySet(const CFGBlock *Block) {
> +    return VisitedBlockIDs.test(Block->getBlockID());
> +  }
> +};
> +
> +/// \brief We create a helper class which we use to iterate through
> CFGBlocks in
> +/// the topological order.
> +class TopologicallySortedCFG {
> +  typedef llvm::po_iterator<const CFG*, CFGBlockSet, true>  po_iterator;
> +
> +  std::vector<const CFGBlock*> Blocks;
> +
> +public:
> +  typedef std::vector<const CFGBlock*>::reverse_iterator iterator;
> +
> +  TopologicallySortedCFG(const CFG *CFGraph) {
> +    Blocks.reserve(CFGraph->getNumBlockIDs());
> +    CFGBlockSet BSet(CFGraph);
> +
> +    for (po_iterator I = po_iterator::begin(CFGraph, BSet),
> +         E = po_iterator::end(CFGraph, BSet); I != E; ++I) {
> +      Blocks.push_back(*I);
> +    }
> +  }
> +
> +  iterator begin() {
> +    return Blocks.rbegin();
> +  }
> +
> +  iterator end() {
> +    return Blocks.rend();
> +  }
> +};
> +
> +/// \brief A MutexID object uniquely identifies a particular mutex, and
> +/// is built from an Expr* (i.e. calling a lock function).
> +///
> +/// Thread-safety analysis works by comparing lock expressions.  Within
> the
> +/// body of a function, an expression such as "x->foo->bar.mu" will
> resolve to
> +/// a particular mutex object at run-time.  Subsequent occurrences of the
> same
> +/// expression (where "same" means syntactic equality) will refer to the
> same
> +/// run-time object if three conditions hold:
> +/// (1) Local variables in the expression, such as "x" have not changed.
> +/// (2) Values on the heap that affect the expression have not changed.
> +/// (3) The expression involves only pure function calls.
> +/// The current implementation assumes, but does not verify, that multiple
> uses
> +/// of the same lock expression satisfies these criteria.
> +///
> +/// Clang introduces an additional wrinkle, which is that it is difficult
> to
> +/// derive canonical expressions, or compare expressions directly for
> equality.
> +/// Thus, we identify a mutex not by an Expr, but by the set of named
> +/// declarations that are referenced by the Expr.  In other words,
> +/// x->foo->bar.mu will be a four element vector with the Decls for
> +/// mu, bar, and foo, and x.  The vector will uniquely identify the
> expression
> +/// for all practical purposes.
> +///
> +/// Note we will need to perform substitution on "this" and function
> parameter
> +/// names when constructing a lock expression.
> +///
> +/// For example:
> +/// class C { Mutex Mu;  void lock() EXCLUSIVE_LOCK_FUNCTION(this->Mu); };
> +/// void myFunc(C *X) { ... X->lock() ... }
> +/// The original expression for the mutex acquired by myFunc is
> "this->Mu", but
> +/// "X" is substituted for "this" so we get X->Mu();
> +///
> +/// For another example:
> +/// foo(MyList *L) EXCLUSIVE_LOCKS_REQUIRED(L->Mu) { ... }
> +/// MyList *MyL;
> +/// foo(MyL);  // requires lock MyL->Mu to be held
> +class MutexID {
> +  SmallVector<NamedDecl*, 2> DeclSeq;
> +
> +  /// Build a Decl sequence representing the lock from the given
> expression.
> +  /// Recursive function that bottoms out when the final DeclRefExpr is
> reached.
> +  void buildMutexID(Expr *Exp) {
> +    if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
> +      NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
> +      DeclSeq.push_back(ND);
> +    } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
> +      NamedDecl *ND = ME->getMemberDecl();
> +      DeclSeq.push_back(ND);
> +      buildMutexID(ME->getBase());
> +    } else if (isa<CXXThisExpr>(Exp)) {
> +      return;
> +    } else {
> +      // FIXME: add diagnostic
> +      llvm::report_fatal_error("Expected lock expression!");
> +    }
> +  }
> +
> +public:
> +  MutexID(Expr *LExpr) {
> +    buildMutexID(LExpr);
> +    assert(!DeclSeq.empty());
> +  }
> +
> +  bool operator==(const MutexID &other) const {
> +    return DeclSeq == other.DeclSeq;
> +  }
> +
> +  bool operator!=(const MutexID &other) const {
> +    return !(*this == other);
> +  }
> +
> +  // SmallVector overloads Operator< to do lexicographic ordering. Note
> that
> +  // we use pointer equality (and <) to compare NamedDecls. This means the
> order
> +  // of MutexIDs in a lockset is nondeterministic. In order to output
> +  // diagnostics in a deterministic ordering, we must order all
> diagnostics to
> +  // output by SourceLocation when iterating through this lockset.
> +  bool operator<(const MutexID &other) const {
> +    return DeclSeq < other.DeclSeq;
> +  }
> +
> +  /// \brief Returns the name of the first Decl in the list for a given
> MutexID;
> +  /// e.g. the lock expression foo.bar() has name "bar".
> +  /// The caret will point unambiguously to the lock expression, so using
> this
> +  /// name in diagnostics is a way to get simple, and consistent, mutex
> names.
> +  /// We do not want to output the entire expression text for security
> reasons.
> +  StringRef getName() const {
> +    return DeclSeq.front()->getName();
> +  }
> +
> +  void Profile(llvm::FoldingSetNodeID &ID) const {
> +    for (SmallVectorImpl<NamedDecl*>::const_iterator I = DeclSeq.begin(),
> +         E = DeclSeq.end(); I != E; ++I) {
> +      ID.AddPointer(*I);
> +    }
> +  }
> +};
> +
> +/// \brief This is a helper class that stores info about the most recent
> +/// accquire of a Lock.
> +///
> +/// The main body of the analysis maps MutexIDs to LockDatas.
> +struct LockData {
> +  SourceLocation AcquireLoc;
> +
> +  /// \brief LKind stores whether a lock is held shared or exclusively.
> +  /// Note that this analysis does not currently support either re-entrant
> +  /// locking or lock "upgrading" and "downgrading" between exclusive and
> +  /// shared.
> +  ///
> +  /// FIXME: add support for re-entrant locking and lock up/downgrading
> +  LockKind LKind;
> +
> +  LockData(SourceLocation AcquireLoc, LockKind LKind)
> +    : AcquireLoc(AcquireLoc), LKind(LKind) {}
> +
> +  bool operator==(const LockData &other) const {
> +    return AcquireLoc == other.AcquireLoc && LKind == other.LKind;
> +  }
> +
> +  bool operator!=(const LockData &other) const {
> +    return !(*this == other);
> +  }
> +
> +  void Profile(llvm::FoldingSetNodeID &ID) const {
> +      ID.AddInteger(AcquireLoc.getRawEncoding());
> +      ID.AddInteger(LKind);
> +    }
> +};
> +
> +/// A Lockset maps each MutexID (defined above) to information about how
> it has
> +/// been locked.
> +typedef llvm::ImmutableMap<MutexID, LockData> Lockset;
> +
> +/// \brief We use this class to visit different types of expressions in
> +/// CFGBlocks, and build up the lockset.
> +/// An expression may cause us to add or remove locks from the lockset, or
> else
> +/// output error messages related to missing locks.
> +/// FIXME: In future, we may be able to not inherit from a visitor.
> +class BuildLockset : public StmtVisitor<BuildLockset> {
> +  ThreadSafetyHandler &Handler;
> +  Lockset LSet;
> +  Lockset::Factory &LocksetFactory;
> +
> +  // Helper functions
> +  void removeLock(SourceLocation UnlockLoc, Expr *LockExp);
> +  void addLock(SourceLocation LockLoc, Expr *LockExp, LockKind LK);
> +  const ValueDecl *getValueDecl(Expr *Exp);
> +  void warnIfMutexNotHeld (const NamedDecl *D, Expr *Exp, AccessKind AK,
> +                           Expr *MutexExp, ProtectedOperationKind POK);
> +  void checkAccess(Expr *Exp, AccessKind AK);
> +  void checkDereference(Expr *Exp, AccessKind AK);
> +
> +  template <class AttrType>
> +  void addLocksToSet(LockKind LK, Attr *Attr, CXXMemberCallExpr *Exp);
> +
> +  /// \brief Returns true if the lockset contains a lock, regardless of
> whether
> +  /// the lock is held exclusively or shared.
> +  bool locksetContains(MutexID Lock) const {
> +    return LSet.lookup(Lock);
> +  }
> +
> +  /// \brief Returns true if the lockset contains a lock with the passed
> in
> +  /// locktype.
> +  bool locksetContains(MutexID Lock, LockKind KindRequested) const {
> +    const LockData *LockHeld = LSet.lookup(Lock);
> +    return (LockHeld && KindRequested == LockHeld->LKind);
> +  }
> +
> +  /// \brief Returns true if the lockset contains a lock with at least the
> +  /// passed in locktype. So for example, if we pass in LK_Shared, this
> function
> +  /// returns true if the lock is held LK_Shared or LK_Exclusive. If we
> pass in
> +  /// LK_Exclusive, this function returns true if the lock is held
> LK_Exclusive.
> +  bool locksetContainsAtLeast(MutexID Lock, LockKind KindRequested) const
> {
> +    switch (KindRequested) {
> +      case LK_Shared:
> +        return locksetContains(Lock);
> +      case LK_Exclusive:
> +        return locksetContains(Lock, KindRequested);
> +    }
> +  }
> +
> +public:
> +  BuildLockset(ThreadSafetyHandler &Handler, Lockset LS, Lockset::Factory
> &F)
> +    : StmtVisitor<BuildLockset>(), Handler(Handler), LSet(LS),
> +      LocksetFactory(F) {}
> +
> +  Lockset getLockset() {
> +    return LSet;
> +  }
> +
> +  void VisitUnaryOperator(UnaryOperator *UO);
> +  void VisitBinaryOperator(BinaryOperator *BO);
> +  void VisitCastExpr(CastExpr *CE);
> +  void VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp);
> +};
> +
> +/// \brief Add a new lock to the lockset, warning if the lock is already
> there.
> +/// \param LockLoc The source location of the acquire
> +/// \param LockExp The lock expression corresponding to the lock to be
> added
> +void BuildLockset::addLock(SourceLocation LockLoc, Expr *LockExp,
> +                           LockKind LK) {
> +  // FIXME: deal with acquired before/after annotations
> +  MutexID Mutex(LockExp);
> +  LockData NewLock(LockLoc, LK);
> +
> +  // FIXME: Don't always warn when we have support for reentrant locks.
> +  if (locksetContains(Mutex))
> +    Handler.handleDoubleLock(Mutex.getName(), LockLoc);
> +  LSet = LocksetFactory.add(LSet, Mutex, NewLock);
> +}
> +
> +/// \brief Remove a lock from the lockset, warning if the lock is not
> there.
> +/// \param LockExp The lock expression corresponding to the lock to be
> removed
> +/// \param UnlockLoc The source location of the unlock (only used in error
> msg)
> +void BuildLockset::removeLock(SourceLocation UnlockLoc, Expr *LockExp) {
> +  MutexID Mutex(LockExp);
> +
> +  Lockset NewLSet = LocksetFactory.remove(LSet, Mutex);
> +  if(NewLSet == LSet)
> +    Handler.handleUnmatchedUnlock(Mutex.getName(), UnlockLoc);
> +
> +  LSet = NewLSet;
> +}
> +
> +/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
> +const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
> +  if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
> +    return DR->getDecl();
> +
> +  if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp))
> +    return ME->getMemberDecl();
> +
> +  return 0;
> +}
> +
> +/// \brief Warn if the LSet does not contain a lock sufficient to protect
> access
> +/// of at least the passed in AccessType.
> +void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp,
> +                                      AccessKind AK, Expr *MutexExp,
> +                                      ProtectedOperationKind POK) {
> +  LockKind LK = getLockKindFromAccessKind(AK);
> +  MutexID Mutex(MutexExp);
> +  if (!locksetContainsAtLeast(Mutex, LK))
> +    Handler.handleMutexNotHeld(D, POK, Mutex.getName(), LK,
> Exp->getExprLoc());
> +}
> +
> +
> +/// \brief This method identifies variable dereferences and checks
> pt_guarded_by
> +/// and pt_guarded_var annotations. Note that we only check these
> annotations
> +/// at the time a pointer is dereferenced.
> +/// FIXME: We need to check for other types of pointer dereferences
> +/// (e.g. [], ->) and deal with them here.
> +/// \param Exp An expression that has been read or written.
> +void BuildLockset::checkDereference(Expr *Exp, AccessKind AK) {
> +  UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp);
> +  if (!UO || UO->getOpcode() != clang::UO_Deref)
> +    return;
> +  Exp = UO->getSubExpr()->IgnoreParenCasts();
> +
> +  const ValueDecl *D = getValueDecl(Exp);
> +  if(!D || !D->hasAttrs())
> +    return;
> +
> +  if (D->getAttr<PtGuardedVarAttr>() && LSet.isEmpty())
> +    Handler.handleNoMutexHeld(D, POK_VarDereference, AK,
> Exp->getExprLoc());
> +
> +  const AttrVec &ArgAttrs = D->getAttrs();
> +  for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
> +    if (PtGuardedByAttr *PGBAttr = dyn_cast<PtGuardedByAttr>(ArgAttrs[i]))
> +      warnIfMutexNotHeld(D, Exp, AK, PGBAttr->getArg(),
> POK_VarDereference);
> +}
> +
> +/// \brief Checks guarded_by and guarded_var attributes.
> +/// Whenever we identify an access (read or write) of a DeclRefExpr or
> +/// MemberExpr, we need to check whether there are any guarded_by or
> +/// guarded_var attributes, and make sure we hold the appropriate mutexes.
> +void BuildLockset::checkAccess(Expr *Exp, AccessKind AK) {
> +  const ValueDecl *D = getValueDecl(Exp);
> +  if(!D || !D->hasAttrs())
> +    return;
> +
> +  if (D->getAttr<GuardedVarAttr>() && LSet.isEmpty())
> +    Handler.handleNoMutexHeld(D, POK_VarAccess, AK, Exp->getExprLoc());
> +
> +  const AttrVec &ArgAttrs = D->getAttrs();
> +  for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
> +    if (GuardedByAttr *GBAttr = dyn_cast<GuardedByAttr>(ArgAttrs[i]))
> +      warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarAccess);
> +}
> +
> +/// \brief For unary operations which read and write a variable, we need
> to
> +/// check whether we hold any required mutexes. Reads are checked in
> +/// VisitCastExpr.
> +void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) {
> +  switch (UO->getOpcode()) {
> +    case clang::UO_PostDec:
> +    case clang::UO_PostInc:
> +    case clang::UO_PreDec:
> +    case clang::UO_PreInc: {
> +      Expr *SubExp = UO->getSubExpr()->IgnoreParenCasts();
> +      checkAccess(SubExp, AK_Written);
> +      checkDereference(SubExp, AK_Written);
> +      break;
> +    }
> +    default:
> +      break;
> +  }
> +}
> +
> +/// For binary operations which assign to a variable (writes), we need to
> check
> +/// whether we hold any required mutexes.
> +/// FIXME: Deal with non-primitive types.
> +void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
> +  if (!BO->isAssignmentOp())
> +    return;
> +  Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
> +  checkAccess(LHSExp, AK_Written);
> +  checkDereference(LHSExp, AK_Written);
> +}
> +
> +/// Whenever we do an LValue to Rvalue cast, we are reading a variable and
> +/// need to ensure we hold any required mutexes.
> +/// FIXME: Deal with non-primitive types.
> +void BuildLockset::VisitCastExpr(CastExpr *CE) {
> +  if (CE->getCastKind() != CK_LValueToRValue)
> +    return;
> +  Expr *SubExp = CE->getSubExpr()->IgnoreParenCasts();
> +  checkAccess(SubExp, AK_Read);
> +  checkDereference(SubExp, AK_Read);
> +}
> +
> +/// \brief This function, parameterized by an attribute type, is used to
> add a
> +/// set of locks specified as attribute arguments to the lockset.
> +template <typename AttrType>
> +void BuildLockset::addLocksToSet(LockKind LK, Attr *Attr,
> +                                 CXXMemberCallExpr *Exp) {
> +  typedef typename AttrType::args_iterator iterator_type;
> +  SourceLocation ExpLocation = Exp->getExprLoc();
> +  Expr *Parent = Exp->getImplicitObjectArgument();
> +  AttrType *SpecificAttr = cast<AttrType>(Attr);
> +
> +  if (SpecificAttr->args_size() == 0) {
> +    // The mutex held is the "this" object.
> +    addLock(ExpLocation, Parent, LK);
> +    return;
> +  }
> +
> +  for (iterator_type I = SpecificAttr->args_begin(),
> +       E = SpecificAttr->args_end(); I != E; ++I)
> +    addLock(ExpLocation, *I, LK);
> +}
> +
> +/// \brief When visiting CXXMemberCallExprs we need to examine the
> attributes on
> +/// the method that is being called and add, remove or check locks in the
> +/// lockset accordingly.
> +///
> +/// FIXME: For classes annotated with one of the guarded annotations, we
> need
> +/// to treat const method calls as reads and non-const method calls as
> writes,
> +/// and check that the appropriate locks are held. Non-const method calls
> with
> +/// the same signature as const method calls can be also treated as reads.
> +///
> +/// FIXME: We need to also visit CallExprs to catch/check global
> functions.
> +void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
> +  NamedDecl *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
> +
> +  SourceLocation ExpLocation = Exp->getExprLoc();
> +  Expr *Parent = Exp->getImplicitObjectArgument();
> +
> +  if(!D || !D->hasAttrs())
> +    return;
> +
> +  AttrVec &ArgAttrs = D->getAttrs();
> +  for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
> +    Attr *Attr = ArgAttrs[i];
> +    switch (Attr->getKind()) {
> +      // When we encounter an exclusive lock function, we need to add the
> lock
> +      // to our lockset with kind exclusive.
> +      case attr::ExclusiveLockFunction:
> +        addLocksToSet<ExclusiveLockFunctionAttr>(LK_Exclusive, Attr, Exp);
> +        break;
> +
> +      // When we encounter a shared lock function, we need to add the lock
> +      // to our lockset with kind shared.
> +      case attr::SharedLockFunction:
> +        addLocksToSet<SharedLockFunctionAttr>(LK_Shared, Attr, Exp);
> +        break;
> +
> +      // When we encounter an unlock function, we need to remove unlocked
> +      // mutexes from the lockset, and flag a warning if they are not
> there.
> +      case attr::UnlockFunction: {
> +        UnlockFunctionAttr *UFAttr = cast<UnlockFunctionAttr>(Attr);
> +
> +        if (UFAttr->args_size() == 0) { // The lock held is the "this"
> object.
> +          removeLock(ExpLocation, Parent);
> +          break;
> +        }
> +
> +        for (UnlockFunctionAttr::args_iterator I = UFAttr->args_begin(),
> +             E = UFAttr->args_end(); I != E; ++I)
> +          removeLock(ExpLocation, *I);
> +        break;
> +      }
> +
> +      case attr::ExclusiveLocksRequired: {
> +        // FIXME: Also use this attribute to add required locks to the
> initial
> +        // lockset when processing a CFG for a function annotated with
> this
> +        // attribute.
> +        ExclusiveLocksRequiredAttr *ELRAttr =
> +            cast<ExclusiveLocksRequiredAttr>(Attr);
> +
> +        for (ExclusiveLocksRequiredAttr::args_iterator
> +             I = ELRAttr->args_begin(), E = ELRAttr->args_end(); I != E;
> ++I)
> +          warnIfMutexNotHeld(D, Exp, AK_Written, *I, POK_FunctionCall);
> +        break;
> +      }
> +
> +      case attr::SharedLocksRequired: {
> +        // FIXME: Also use this attribute to add required locks to the
> initial
> +        // lockset when processing a CFG for a function annotated with
> this
> +        // attribute.
> +        SharedLocksRequiredAttr *SLRAttr =
> cast<SharedLocksRequiredAttr>(Attr);
> +
> +        for (SharedLocksRequiredAttr::args_iterator I =
> SLRAttr->args_begin(),
> +             E = SLRAttr->args_end(); I != E; ++I)
> +          warnIfMutexNotHeld(D, Exp, AK_Read, *I, POK_FunctionCall);
> +        break;
> +      }
> +
> +      case attr::LocksExcluded: {
> +        LocksExcludedAttr *LEAttr = cast<LocksExcludedAttr>(Attr);
> +        for (LocksExcludedAttr::args_iterator I = LEAttr->args_begin(),
> +            E = LEAttr->args_end(); I != E; ++I) {
> +          MutexID Mutex(*I);
> +          if (locksetContains(Mutex))
> +            Handler.handleFunExcludesLock(D->getName(), Mutex.getName(),
> +                                          ExpLocation);
> +        }
> +        break;
> +      }
> +
> +      case attr::LockReturned:
> +        // FIXME: Deal with this attribute.
> +        break;
> +
> +      // Ignore other (non thread-safety) attributes
> +      default:
> +        break;
> +    }
> +  }
> +}
> +
> +} // end anonymous namespace
> +
> +/// \brief Flags a warning for each lock that is in LSet2 but not LSet1,
> or
> +/// else mutexes that are held shared in one lockset and exclusive in the
> other.
> +static Lockset warnIfNotInFirstSetOrNotSameKind(ThreadSafetyHandler
> &Handler,
> +                                                const Lockset LSet1,
> +                                                const Lockset LSet2,
> +                                                Lockset Intersection,
> +                                                Lockset::Factory &Fact) {
> +  for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I)
> {
> +    const MutexID &LSet2Mutex = I.getKey();
> +    const LockData &LSet2LockData = I.getData();
> +    if (const LockData *LD = LSet1.lookup(LSet2Mutex)) {
> +      if (LD->LKind != LSet2LockData.LKind) {
> +        Handler.handleExclusiveAndShared(LSet2Mutex.getName(),
> +                                         LSet2LockData.AcquireLoc,
> +                                         LD->AcquireLoc);
> +        if (LD->LKind != LK_Exclusive)
> +          Intersection = Fact.add(Intersection, LSet2Mutex,
> LSet2LockData);
> +      }
> +    } else {
> +      Handler.handleMutexHeldEndOfScope(LSet2Mutex.getName(),
> +                                        LSet2LockData.AcquireLoc);
> +    }
> +  }
> +  return Intersection;
> +}
> +
> +
> +/// \brief Compute the intersection of two locksets and issue warnings for
> any
> +/// locks in the symmetric difference.
> +///
> +/// This function is used at a merge point in the CFG when comparing the
> lockset
> +/// of each branch being merged. For example, given the following
> sequence:
> +/// A; if () then B; else C; D; we need to check that the lockset after B
> and C
> +/// are the same. In the event of a difference, we use the intersection of
> these
> +/// two locksets at the start of D.
> +static Lockset intersectAndWarn(ThreadSafetyHandler &Handler,
> +                                const Lockset LSet1, const Lockset LSet2,
> +                                Lockset::Factory &Fact) {
> +  Lockset Intersection = LSet1;
> +  Intersection = warnIfNotInFirstSetOrNotSameKind(Handler, LSet1, LSet2,
> +                                                  Intersection, Fact);
> +
> +  for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I)
> {
> +    if (!LSet2.contains(I.getKey())) {
> +      const MutexID &Mutex = I.getKey();
> +      const LockData &MissingLock = I.getData();
> +      Handler.handleMutexHeldEndOfScope(Mutex.getName(),
> +                                        MissingLock.AcquireLoc);
> +      Intersection = Fact.remove(Intersection, Mutex);
> +    }
> +  }
> +  return Intersection;
> +}
> +
> +/// \brief Returns the location of the first Stmt in a Block.
> +static SourceLocation getFirstStmtLocation(CFGBlock *Block) {
> +  SourceLocation Loc;
> +  for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end();
> +       BI != BE; ++BI) {
> +    if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&(*BI))) {
> +      Loc = CfgStmt->getStmt()->getLocStart();
> +      if (Loc.isValid()) return Loc;
> +    }
> +  }
> +  if (Stmt *S = Block->getTerminator().getStmt()) {
> +    Loc = S->getLocStart();
> +    if (Loc.isValid()) return Loc;
> +  }
> +  return Loc;
> +}
> +
> +/// \brief Warn about different locksets along backedges of loops.
> +/// This function is called when we encounter a back edge. At that point,
> +/// we need to verify that the lockset before taking the backedge is the
> +/// same as the lockset before entering the loop.
> +///
> +/// \param LoopEntrySet Locks before starting the loop
> +/// \param LoopReentrySet Locks in the last CFG block of the loop
> +static void warnBackEdgeUnequalLocksets(ThreadSafetyHandler &Handler,
> +                                        const Lockset LoopReentrySet,
> +                                        const Lockset LoopEntrySet,
> +                                        SourceLocation FirstLocInLoop,
> +                                        Lockset::Factory &Fact) {
> +  assert(FirstLocInLoop.isValid());
> +  // Warn for locks held at the start of the loop, but not the end.
> +  for (Lockset::iterator I = LoopEntrySet.begin(), E = LoopEntrySet.end();
> +       I != E; ++I) {
> +    if (!LoopReentrySet.contains(I.getKey())) {
> +      // We report this error at the location of the first statement in a
> loop
> +      Handler.handleNoLockLoopEntry(I.getKey().getName(), FirstLocInLoop);
> +    }
> +  }
> +
> +  // Warn for locks held at the end of the loop, but not at the start.
> +  warnIfNotInFirstSetOrNotSameKind(Handler, LoopEntrySet, LoopReentrySet,
> +                                   LoopReentrySet, Fact);
> +}
> +
> +
> +namespace clang { namespace thread_safety {
> +/// \brief Check a function's CFG for thread-safety violations.
> +///
> +/// We traverse the blocks in the CFG, compute the set of mutexes that are
> held
> +/// at the end of each block, and issue warnings for thread safety
> violations.
> +/// Each block in the CFG is traversed exactly once.
> +void runThreadSafetyAnalysis(AnalysisContext &AC,
> +                             ThreadSafetyHandler &Handler) {
> +  CFG *CFGraph = AC.getCFG();
> +  if (!CFGraph) return;
> +  const Decl *D = AC.getDecl();
> +  if (D && D->getAttr<NoThreadSafetyAnalysisAttr>()) return;
> +
> +  Lockset::Factory LocksetFactory;
> +
> +  // FIXME: Swith to SmallVector? Otherwise improve performance impact?
> +  std::vector<Lockset> EntryLocksets(CFGraph->getNumBlockIDs(),
> +                                     LocksetFactory.getEmptyMap());
> +  std::vector<Lockset> ExitLocksets(CFGraph->getNumBlockIDs(),
> +                                    LocksetFactory.getEmptyMap());
> +
> +  // We need to explore the CFG via a "topological" ordering.
> +  // That way, we will be guaranteed to have information about required
> +  // predecessor locksets when exploring a new block.
> +  TopologicallySortedCFG SortedGraph(CFGraph);
> +  CFGBlockSet VisitedBlocks(CFGraph);
> +
> +  for (TopologicallySortedCFG::iterator I = SortedGraph.begin(),
> +       E = SortedGraph.end(); I!= E; ++I) {
> +    const CFGBlock *CurrBlock = *I;
> +    int CurrBlockID = CurrBlock->getBlockID();
> +
> +    VisitedBlocks.insert(CurrBlock);
> +
> +    // Use the default initial lockset in case there are no predecessors.
> +    Lockset &Entryset = EntryLocksets[CurrBlockID];
> +    Lockset &Exitset = ExitLocksets[CurrBlockID];
> +
> +    // Iterate through the predecessor blocks and warn if the lockset for
> all
> +    // predecessors is not the same. We take the entry lockset of the
> current
> +    // block to be the intersection of all previous locksets.
> +    // FIXME: By keeping the intersection, we may output more errors in
> future
> +    // for a lock which is not in the intersection, but was in the union.
> We
> +    // may want to also keep the union in future. As an example, let's say
> +    // the intersection contains Mutex L, and the union contains L and M.
> +    // Later we unlock M. At this point, we would output an error because
> we
> +    // never locked M; although the real error is probably that we forgot
> to
> +    // lock M on all code paths. Conversely, let's say that later we lock
> M.
> +    // In this case, we should compare against the intersection instead of
> the
> +    // union because the real error is probably that we forgot to unlock M
> on
> +    // all code paths.
> +    bool LocksetInitialized = false;
> +    for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
> +         PE  = CurrBlock->pred_end(); PI != PE; ++PI) {
> +
> +      // if *PI -> CurrBlock is a back edge
> +      if (*PI == 0 || !VisitedBlocks.alreadySet(*PI))
> +        continue;
> +
> +      int PrevBlockID = (*PI)->getBlockID();
> +      if (!LocksetInitialized) {
> +        Entryset = ExitLocksets[PrevBlockID];
> +        LocksetInitialized = true;
> +      } else {
> +        Entryset = intersectAndWarn(Handler, Entryset,
> +                                    ExitLocksets[PrevBlockID],
> LocksetFactory);
> +      }
> +    }
> +
> +    BuildLockset LocksetBuilder(Handler, Entryset, LocksetFactory);
> +    for (CFGBlock::const_iterator BI = CurrBlock->begin(),
> +         BE = CurrBlock->end(); BI != BE; ++BI) {
> +      if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&*BI))
> +        LocksetBuilder.Visit(const_cast<Stmt*>(CfgStmt->getStmt()));
> +    }
> +    Exitset = LocksetBuilder.getLockset();
> +
> +    // For every back edge from CurrBlock (the end of the loop) to another
> block
> +    // (FirstLoopBlock) we need to check that the Lockset of Block is
> equal to
> +    // the one held at the beginning of FirstLoopBlock. We can look up the
> +    // Lockset held at the beginning of FirstLoopBlock in the
> EntryLockSets map.
> +    for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
> +         SE  = CurrBlock->succ_end(); SI != SE; ++SI) {
> +
> +      // if CurrBlock -> *SI is *not* a back edge
> +      if (*SI == 0 || !VisitedBlocks.alreadySet(*SI))
> +        continue;
> +
> +      CFGBlock *FirstLoopBlock = *SI;
> +      SourceLocation FirstLoopLocation =
> getFirstStmtLocation(FirstLoopBlock);
> +
> +      assert(FirstLoopLocation.isValid());
> +
> +      // Fail gracefully in release code.
> +      if (!FirstLoopLocation.isValid())
> +        continue;
> +
> +      Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()];
> +      Lockset LoopEnd = ExitLocksets[CurrBlockID];
> +      warnBackEdgeUnequalLocksets(Handler, LoopEnd, PreLoop,
> FirstLoopLocation,
> +                                  LocksetFactory);
> +    }
> +  }
> +
> +  Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()];
> +  if (!FinalLockset.isEmpty()) {
> +    for (Lockset::iterator I=FinalLockset.begin(), E=FinalLockset.end();
> +         I != E; ++I) {
> +      const MutexID &Mutex = I.getKey();
> +      const LockData &MissingLock = I.getData();
> +
> +      std::string FunName = "<unknown>";
> +      if (const NamedDecl *ContextDecl =
> dyn_cast<NamedDecl>(AC.getDecl())) {
> +        FunName = ContextDecl->getDeclName().getAsString();
> +      }
> +
> +      Handler.handleNoUnlock(Mutex.getName(), FunName,
> MissingLock.AcquireLoc);
> +    }
> +  }
> +}
> +
> +/// \brief Helper function that returns a LockKind required for the given
> level
> +/// of access.
> +LockKind getLockKindFromAccessKind(AccessKind AK) {
> +  switch (AK) {
> +    case AK_Read :
> +      return LK_Shared;
> +    case AK_Written :
> +      return LK_Exclusive;
> +  }
> +}
> +}} // end namespace clang::thread_safety
>
> Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=139369&r1=139368&r2=139369&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
> +++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Fri Sep  9 11:11:56 2011
> @@ -31,6 +31,7 @@
>  #include "clang/Analysis/CFG.h"
>  #include "clang/Analysis/Analyses/ReachableCode.h"
>  #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
> +#include "clang/Analysis/Analyses/ThreadSafety.h"
>  #include "clang/Analysis/CFGStmtMap.h"
>  #include "clang/Analysis/Analyses/UninitializedValues.h"
>  #include "llvm/ADT/BitVector.h"
> @@ -610,23 +611,6 @@
>  typedef std::pair<SourceLocation, PartialDiagnostic> DelayedDiag;
>  typedef llvm::SmallVector<DelayedDiag, 4> DiagList;
>
> -enum ProtectedOperationKind {
> -  POK_VarDereference,
> -  POK_VarAccess,
> -  POK_FunctionCall
> -};
> -
> -enum LockKind {
> -  LK_Shared,
> -  LK_Exclusive
> -};
> -
> -enum AccessKind {
> -  AK_Read,
> -  AK_Written
> -};
> -
> -
>  struct SortDiagBySourceLocation {
>   Sema &S;
>   SortDiagBySourceLocation(Sema &S) : S(S) {}
> @@ -639,39 +623,6 @@
>   }
>  };
>
> -/// \brief Helper function that returns a LockKind required for the given
> level
> -/// of access.
> -LockKind getLockKindFromAccessKind(AccessKind AK) {
> -  switch (AK) {
> -    case AK_Read :
> -      return LK_Shared;
> -    case AK_Written :
> -      return LK_Exclusive;
> -  }
> -}
> -
> -class ThreadSafetyHandler {
> -public:
> -  typedef llvm::StringRef Name;
> -  ThreadSafetyHandler() {}
> -  virtual ~ThreadSafetyHandler() {}
> -  virtual void handleUnmatchedUnlock(Name LockName, SourceLocation Loc) {}
> -  virtual void handleDoubleLock(Name LockName, SourceLocation Loc) {}
> -  virtual void handleMutexHeldEndOfScope(Name LockName, SourceLocation
> Loc){}
> -  virtual void handleNoLockLoopEntry(Name LockName, SourceLocation Loc) {}
> -  virtual void handleNoUnlock(Name LockName, Name FunName,
> -                              SourceLocation Loc) {}
> -  virtual void handleExclusiveAndShared(Name LockName, SourceLocation
> Loc1,
> -                                        SourceLocation Loc2) {}
> -  virtual void handleNoMutexHeld(const NamedDecl *D,
> ProtectedOperationKind POK,
> -                                 AccessKind AK, SourceLocation Loc) {}
> -  virtual void handleMutexNotHeld(const NamedDecl *D,
> -                                  ProtectedOperationKind POK, Name
> LockName,
> -                                  LockKind LK, SourceLocation Loc) {}
> -  virtual void handleFunExcludesLock(Name FunName, Name LockName,
> -                                     SourceLocation Loc) {}
> -};
> -
>  class ThreadSafetyReporter : public
> clang::thread_safety::ThreadSafetyHandler {
>   Sema &S;
>   DiagList Warnings;
> @@ -765,755 +716,6 @@
>  }
>  }
>
> -using namespace thread_safety;
> -
> -namespace {
> -/// \brief Implements a set of CFGBlocks using a BitVector.
> -///
> -/// This class contains a minimal interface, primarily dictated by the
> SetType
> -/// template parameter of the llvm::po_iterator template, as used with
> external
> -/// storage. We also use this set to keep track of which CFGBlocks we
> visit
> -/// during the analysis.
> -class CFGBlockSet {
> -  llvm::BitVector VisitedBlockIDs;
> -
> -public:
> -  // po_iterator requires this iterator, but the only interface needed is
> the
> -  // value_type typedef.
> -  struct iterator {
> -    typedef const CFGBlock *value_type;
> -  };
> -
> -  CFGBlockSet() {}
> -  CFGBlockSet(const CFG *G) : VisitedBlockIDs(G->getNumBlockIDs(), false)
> {}
> -
> -  /// \brief Set the bit associated with a particular CFGBlock.
> -  /// This is the important method for the SetType template parameter.
> -  bool insert(const CFGBlock *Block) {
> -    // Note that insert() is called by po_iterator, which doesn't check to
> make
> -    // sure that Block is non-null.  Moreover, the CFGBlock iterator will
> -    // occasionally hand out null pointers for pruned edges, so we catch
> those
> -    // here.
> -    if (Block == 0)
> -      return false;  // if an edge is trivially false.
> -    if (VisitedBlockIDs.test(Block->getBlockID()))
> -      return false;
> -    VisitedBlockIDs.set(Block->getBlockID());
> -    return true;
> -  }
> -
> -  /// \brief Check if the bit for a CFGBlock has been already set.
> -  /// This method is for tracking visited blocks in the main threadsafety
> loop.
> -  /// Block must not be null.
> -  bool alreadySet(const CFGBlock *Block) {
> -    return VisitedBlockIDs.test(Block->getBlockID());
> -  }
> -};
> -
> -/// \brief We create a helper class which we use to iterate through
> CFGBlocks in
> -/// the topological order.
> -class TopologicallySortedCFG {
> -  typedef llvm::po_iterator<const CFG*, CFGBlockSet, true>  po_iterator;
> -
> -  std::vector<const CFGBlock*> Blocks;
> -
> -public:
> -  typedef std::vector<const CFGBlock*>::reverse_iterator iterator;
> -
> -  TopologicallySortedCFG(const CFG *CFGraph) {
> -    Blocks.reserve(CFGraph->getNumBlockIDs());
> -    CFGBlockSet BSet(CFGraph);
> -
> -    for (po_iterator I = po_iterator::begin(CFGraph, BSet),
> -         E = po_iterator::end(CFGraph, BSet); I != E; ++I) {
> -      Blocks.push_back(*I);
> -    }
> -  }
> -
> -  iterator begin() {
> -    return Blocks.rbegin();
> -  }
> -
> -  iterator end() {
> -    return Blocks.rend();
> -  }
> -};
> -
> -/// \brief A MutexID object uniquely identifies a particular mutex, and
> -/// is built from an Expr* (i.e. calling a lock function).
> -///
> -/// Thread-safety analysis works by comparing lock expressions.  Within
> the
> -/// body of a function, an expression such as "x->foo->bar.mu" will
> resolve to
> -/// a particular mutex object at run-time.  Subsequent occurrences of the
> same
> -/// expression (where "same" means syntactic equality) will refer to the
> same
> -/// run-time object if three conditions hold:
> -/// (1) Local variables in the expression, such as "x" have not changed.
> -/// (2) Values on the heap that affect the expression have not changed.
> -/// (3) The expression involves only pure function calls.
> -/// The current implementation assumes, but does not verify, that multiple
> uses
> -/// of the same lock expression satisfies these criteria.
> -///
> -/// Clang introduces an additional wrinkle, which is that it is difficult
> to
> -/// derive canonical expressions, or compare expressions directly for
> equality.
> -/// Thus, we identify a mutex not by an Expr, but by the set of named
> -/// declarations that are referenced by the Expr.  In other words,
> -/// x->foo->bar.mu will be a four element vector with the Decls for
> -/// mu, bar, and foo, and x.  The vector will uniquely identify the
> expression
> -/// for all practical purposes.
> -///
> -/// Note we will need to perform substitution on "this" and function
> parameter
> -/// names when constructing a lock expression.
> -///
> -/// For example:
> -/// class C { Mutex Mu;  void lock() EXCLUSIVE_LOCK_FUNCTION(this->Mu); };
> -/// void myFunc(C *X) { ... X->lock() ... }
> -/// The original expression for the mutex acquired by myFunc is
> "this->Mu", but
> -/// "X" is substituted for "this" so we get X->Mu();
> -///
> -/// For another example:
> -/// foo(MyList *L) EXCLUSIVE_LOCKS_REQUIRED(L->Mu) { ... }
> -/// MyList *MyL;
> -/// foo(MyL);  // requires lock MyL->Mu to be held
> -class MutexID {
> -  SmallVector<NamedDecl*, 2> DeclSeq;
> -
> -  /// Build a Decl sequence representing the lock from the given
> expression.
> -  /// Recursive function that bottoms out when the final DeclRefExpr is
> reached.
> -  void buildMutexID(Expr *Exp) {
> -    if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
> -      NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
> -      DeclSeq.push_back(ND);
> -    } else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
> -      NamedDecl *ND = ME->getMemberDecl();
> -      DeclSeq.push_back(ND);
> -      buildMutexID(ME->getBase());
> -    } else if (isa<CXXThisExpr>(Exp)) {
> -      return;
> -    } else {
> -      // FIXME: add diagnostic
> -      llvm::report_fatal_error("Expected lock expression!");
> -    }
> -  }
> -
> -public:
> -  MutexID(Expr *LExpr) {
> -    buildMutexID(LExpr);
> -    assert(!DeclSeq.empty());
> -  }
> -
> -  bool operator==(const MutexID &other) const {
> -    return DeclSeq == other.DeclSeq;
> -  }
> -
> -  bool operator!=(const MutexID &other) const {
> -    return !(*this == other);
> -  }
> -
> -  // SmallVector overloads Operator< to do lexicographic ordering. Note
> that
> -  // we use pointer equality (and <) to compare NamedDecls. This means the
> order
> -  // of MutexIDs in a lockset is nondeterministic. In order to output
> -  // diagnostics in a deterministic ordering, we must order all
> diagnostics to
> -  // output by SourceLocation when iterating through this lockset.
> -  bool operator<(const MutexID &other) const {
> -    return DeclSeq < other.DeclSeq;
> -  }
> -
> -  /// \brief Returns the name of the first Decl in the list for a given
> MutexID;
> -  /// e.g. the lock expression foo.bar() has name "bar".
> -  /// The caret will point unambiguously to the lock expression, so using
> this
> -  /// name in diagnostics is a way to get simple, and consistent, mutex
> names.
> -  /// We do not want to output the entire expression text for security
> reasons.
> -  StringRef getName() const {
> -    return DeclSeq.front()->getName();
> -  }
> -
> -  void Profile(llvm::FoldingSetNodeID &ID) const {
> -    for (SmallVectorImpl<NamedDecl*>::const_iterator I = DeclSeq.begin(),
> -         E = DeclSeq.end(); I != E; ++I) {
> -      ID.AddPointer(*I);
> -    }
> -  }
> -};
> -
> -/// \brief This is a helper class that stores info about the most recent
> -/// accquire of a Lock.
> -///
> -/// The main body of the analysis maps MutexIDs to LockDatas.
> -struct LockData {
> -  SourceLocation AcquireLoc;
> -
> -  /// \brief LKind stores whether a lock is held shared or exclusively.
> -  /// Note that this analysis does not currently support either re-entrant
> -  /// locking or lock "upgrading" and "downgrading" between exclusive and
> -  /// shared.
> -  ///
> -  /// FIXME: add support for re-entrant locking and lock up/downgrading
> -  LockKind LKind;
> -
> -  LockData(SourceLocation AcquireLoc, LockKind LKind)
> -    : AcquireLoc(AcquireLoc), LKind(LKind) {}
> -
> -  bool operator==(const LockData &other) const {
> -    return AcquireLoc == other.AcquireLoc && LKind == other.LKind;
> -  }
> -
> -  bool operator!=(const LockData &other) const {
> -    return !(*this == other);
> -  }
> -
> -  void Profile(llvm::FoldingSetNodeID &ID) const {
> -      ID.AddInteger(AcquireLoc.getRawEncoding());
> -      ID.AddInteger(LKind);
> -    }
> -};
> -
> -/// A Lockset maps each MutexID (defined above) to information about how
> it has
> -/// been locked.
> -typedef llvm::ImmutableMap<MutexID, LockData> Lockset;
> -
> -/// \brief We use this class to visit different types of expressions in
> -/// CFGBlocks, and build up the lockset.
> -/// An expression may cause us to add or remove locks from the lockset, or
> else
> -/// output error messages related to missing locks.
> -/// FIXME: In future, we may be able to not inherit from a visitor.
> -class BuildLockset : public StmtVisitor<BuildLockset> {
> -  ThreadSafetyHandler &Handler;
> -  Lockset LSet;
> -  Lockset::Factory &LocksetFactory;
> -
> -  // Helper functions
> -  void removeLock(SourceLocation UnlockLoc, Expr *LockExp);
> -  void addLock(SourceLocation LockLoc, Expr *LockExp, LockKind LK);
> -  const ValueDecl *getValueDecl(Expr *Exp);
> -  void warnIfMutexNotHeld (const NamedDecl *D, Expr *Exp, AccessKind AK,
> -                           Expr *MutexExp, ProtectedOperationKind POK);
> -  void checkAccess(Expr *Exp, AccessKind AK);
> -  void checkDereference(Expr *Exp, AccessKind AK);
> -
> -  template <class AttrType>
> -  void addLocksToSet(LockKind LK, Attr *Attr, CXXMemberCallExpr *Exp);
> -
> -  /// \brief Returns true if the lockset contains a lock, regardless of
> whether
> -  /// the lock is held exclusively or shared.
> -  bool locksetContains(MutexID Lock) const {
> -    return LSet.lookup(Lock);
> -  }
> -
> -  /// \brief Returns true if the lockset contains a lock with the passed
> in
> -  /// locktype.
> -  bool locksetContains(MutexID Lock, LockKind KindRequested) const {
> -    const LockData *LockHeld = LSet.lookup(Lock);
> -    return (LockHeld && KindRequested == LockHeld->LKind);
> -  }
> -
> -  /// \brief Returns true if the lockset contains a lock with at least the
> -  /// passed in locktype. So for example, if we pass in LK_Shared, this
> function
> -  /// returns true if the lock is held LK_Shared or LK_Exclusive. If we
> pass in
> -  /// LK_Exclusive, this function returns true if the lock is held
> LK_Exclusive.
> -  bool locksetContainsAtLeast(MutexID Lock, LockKind KindRequested) const
> {
> -    switch (KindRequested) {
> -      case LK_Shared:
> -        return locksetContains(Lock);
> -      case LK_Exclusive:
> -        return locksetContains(Lock, KindRequested);
> -    }
> -  }
> -
> -public:
> -  BuildLockset(ThreadSafetyHandler &Handler, Lockset LS, Lockset::Factory
> &F)
> -    : StmtVisitor<BuildLockset>(), Handler(Handler), LSet(LS),
> -      LocksetFactory(F) {}
> -
> -  Lockset getLockset() {
> -    return LSet;
> -  }
> -
> -  void VisitUnaryOperator(UnaryOperator *UO);
> -  void VisitBinaryOperator(BinaryOperator *BO);
> -  void VisitCastExpr(CastExpr *CE);
> -  void VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp);
> -};
> -
> -/// \brief Add a new lock to the lockset, warning if the lock is already
> there.
> -/// \param LockLoc The source location of the acquire
> -/// \param LockExp The lock expression corresponding to the lock to be
> added
> -void BuildLockset::addLock(SourceLocation LockLoc, Expr *LockExp,
> -                           LockKind LK) {
> -  // FIXME: deal with acquired before/after annotations
> -  MutexID Mutex(LockExp);
> -  LockData NewLock(LockLoc, LK);
> -
> -  // FIXME: Don't always warn when we have support for reentrant locks.
> -  if (locksetContains(Mutex))
> -    Handler.handleDoubleLock(Mutex.getName(), LockLoc);
> -  LSet = LocksetFactory.add(LSet, Mutex, NewLock);
> -}
> -
> -/// \brief Remove a lock from the lockset, warning if the lock is not
> there.
> -/// \param LockExp The lock expression corresponding to the lock to be
> removed
> -/// \param UnlockLoc The source location of the unlock (only used in error
> msg)
> -void BuildLockset::removeLock(SourceLocation UnlockLoc, Expr *LockExp) {
> -  MutexID Mutex(LockExp);
> -
> -  Lockset NewLSet = LocksetFactory.remove(LSet, Mutex);
> -  if(NewLSet == LSet)
> -    Handler.handleUnmatchedUnlock(Mutex.getName(), UnlockLoc);
> -
> -  LSet = NewLSet;
> -}
> -
> -/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
> -const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
> -  if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
> -    return DR->getDecl();
> -
> -  if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp))
> -    return ME->getMemberDecl();
> -
> -  return 0;
> -}
> -
> -/// \brief Warn if the LSet does not contain a lock sufficient to protect
> access
> -/// of at least the passed in AccessType.
> -void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp,
> -                                      AccessKind AK, Expr *MutexExp,
> -                                      ProtectedOperationKind POK) {
> -  LockKind LK = getLockKindFromAccessKind(AK);
> -  MutexID Mutex(MutexExp);
> -  if (!locksetContainsAtLeast(Mutex, LK))
> -    Handler.handleMutexNotHeld(D, POK, Mutex.getName(), LK,
> Exp->getExprLoc());
> -}
> -
> -
> -/// \brief This method identifies variable dereferences and checks
> pt_guarded_by
> -/// and pt_guarded_var annotations. Note that we only check these
> annotations
> -/// at the time a pointer is dereferenced.
> -/// FIXME: We need to check for other types of pointer dereferences
> -/// (e.g. [], ->) and deal with them here.
> -/// \param Exp An expression that has been read or written.
> -void BuildLockset::checkDereference(Expr *Exp, AccessKind AK) {
> -  UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp);
> -  if (!UO || UO->getOpcode() != clang::UO_Deref)
> -    return;
> -  Exp = UO->getSubExpr()->IgnoreParenCasts();
> -
> -  const ValueDecl *D = getValueDecl(Exp);
> -  if(!D || !D->hasAttrs())
> -    return;
> -
> -  if (D->getAttr<PtGuardedVarAttr>() && LSet.isEmpty())
> -    Handler.handleNoMutexHeld(D, POK_VarDereference, AK,
> Exp->getExprLoc());
> -
> -  const AttrVec &ArgAttrs = D->getAttrs();
> -  for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
> -    if (PtGuardedByAttr *PGBAttr = dyn_cast<PtGuardedByAttr>(ArgAttrs[i]))
> -      warnIfMutexNotHeld(D, Exp, AK, PGBAttr->getArg(),
> POK_VarDereference);
> -}
> -
> -/// \brief Checks guarded_by and guarded_var attributes.
> -/// Whenever we identify an access (read or write) of a DeclRefExpr or
> -/// MemberExpr, we need to check whether there are any guarded_by or
> -/// guarded_var attributes, and make sure we hold the appropriate mutexes.
> -void BuildLockset::checkAccess(Expr *Exp, AccessKind AK) {
> -  const ValueDecl *D = getValueDecl(Exp);
> -  if(!D || !D->hasAttrs())
> -    return;
> -
> -  if (D->getAttr<GuardedVarAttr>() && LSet.isEmpty())
> -    Handler.handleNoMutexHeld(D, POK_VarAccess, AK, Exp->getExprLoc());
> -
> -  const AttrVec &ArgAttrs = D->getAttrs();
> -  for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
> -    if (GuardedByAttr *GBAttr = dyn_cast<GuardedByAttr>(ArgAttrs[i]))
> -      warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarAccess);
> -}
> -
> -/// \brief For unary operations which read and write a variable, we need
> to
> -/// check whether we hold any required mutexes. Reads are checked in
> -/// VisitCastExpr.
> -void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) {
> -  switch (UO->getOpcode()) {
> -    case clang::UO_PostDec:
> -    case clang::UO_PostInc:
> -    case clang::UO_PreDec:
> -    case clang::UO_PreInc: {
> -      Expr *SubExp = UO->getSubExpr()->IgnoreParenCasts();
> -      checkAccess(SubExp, AK_Written);
> -      checkDereference(SubExp, AK_Written);
> -      break;
> -    }
> -    default:
> -      break;
> -  }
> -}
> -
> -/// For binary operations which assign to a variable (writes), we need to
> check
> -/// whether we hold any required mutexes.
> -/// FIXME: Deal with non-primitive types.
> -void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
> -  if (!BO->isAssignmentOp())
> -    return;
> -  Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
> -  checkAccess(LHSExp, AK_Written);
> -  checkDereference(LHSExp, AK_Written);
> -}
> -
> -/// Whenever we do an LValue to Rvalue cast, we are reading a variable and
> -/// need to ensure we hold any required mutexes.
> -/// FIXME: Deal with non-primitive types.
> -void BuildLockset::VisitCastExpr(CastExpr *CE) {
> -  if (CE->getCastKind() != CK_LValueToRValue)
> -    return;
> -  Expr *SubExp = CE->getSubExpr()->IgnoreParenCasts();
> -  checkAccess(SubExp, AK_Read);
> -  checkDereference(SubExp, AK_Read);
> -}
> -
> -/// \brief This function, parameterized by an attribute type, is used to
> add a
> -/// set of locks specified as attribute arguments to the lockset.
> -template <typename AttrType>
> -void BuildLockset::addLocksToSet(LockKind LK, Attr *Attr,
> -                                 CXXMemberCallExpr *Exp) {
> -  typedef typename AttrType::args_iterator iterator_type;
> -  SourceLocation ExpLocation = Exp->getExprLoc();
> -  Expr *Parent = Exp->getImplicitObjectArgument();
> -  AttrType *SpecificAttr = cast<AttrType>(Attr);
> -
> -  if (SpecificAttr->args_size() == 0) {
> -    // The mutex held is the "this" object.
> -    addLock(ExpLocation, Parent, LK);
> -    return;
> -  }
> -
> -  for (iterator_type I = SpecificAttr->args_begin(),
> -       E = SpecificAttr->args_end(); I != E; ++I)
> -    addLock(ExpLocation, *I, LK);
> -}
> -
> -/// \brief When visiting CXXMemberCallExprs we need to examine the
> attributes on
> -/// the method that is being called and add, remove or check locks in the
> -/// lockset accordingly.
> -///
> -/// FIXME: For classes annotated with one of the guarded annotations, we
> need
> -/// to treat const method calls as reads and non-const method calls as
> writes,
> -/// and check that the appropriate locks are held. Non-const method calls
> with
> -/// the same signature as const method calls can be also treated as reads.
> -///
> -/// FIXME: We need to also visit CallExprs to catch/check global
> functions.
> -void BuildLockset::VisitCXXMemberCallExpr(CXXMemberCallExpr *Exp) {
> -  NamedDecl *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
> -
> -  SourceLocation ExpLocation = Exp->getExprLoc();
> -  Expr *Parent = Exp->getImplicitObjectArgument();
> -
> -  if(!D || !D->hasAttrs())
> -    return;
> -
> -  AttrVec &ArgAttrs = D->getAttrs();
> -  for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
> -    Attr *Attr = ArgAttrs[i];
> -    switch (Attr->getKind()) {
> -      // When we encounter an exclusive lock function, we need to add the
> lock
> -      // to our lockset with kind exclusive.
> -      case attr::ExclusiveLockFunction:
> -        addLocksToSet<ExclusiveLockFunctionAttr>(LK_Exclusive, Attr, Exp);
> -        break;
> -
> -      // When we encounter a shared lock function, we need to add the lock
> -      // to our lockset with kind shared.
> -      case attr::SharedLockFunction:
> -        addLocksToSet<SharedLockFunctionAttr>(LK_Shared, Attr, Exp);
> -        break;
> -
> -      // When we encounter an unlock function, we need to remove unlocked
> -      // mutexes from the lockset, and flag a warning if they are not
> there.
> -      case attr::UnlockFunction: {
> -        UnlockFunctionAttr *UFAttr = cast<UnlockFunctionAttr>(Attr);
> -
> -        if (UFAttr->args_size() == 0) { // The lock held is the "this"
> object.
> -          removeLock(ExpLocation, Parent);
> -          break;
> -        }
> -
> -        for (UnlockFunctionAttr::args_iterator I = UFAttr->args_begin(),
> -             E = UFAttr->args_end(); I != E; ++I)
> -          removeLock(ExpLocation, *I);
> -        break;
> -      }
> -
> -      case attr::ExclusiveLocksRequired: {
> -        // FIXME: Also use this attribute to add required locks to the
> initial
> -        // lockset when processing a CFG for a function annotated with
> this
> -        // attribute.
> -        ExclusiveLocksRequiredAttr *ELRAttr =
> -            cast<ExclusiveLocksRequiredAttr>(Attr);
> -
> -        for (ExclusiveLocksRequiredAttr::args_iterator
> -             I = ELRAttr->args_begin(), E = ELRAttr->args_end(); I != E;
> ++I)
> -          warnIfMutexNotHeld(D, Exp, AK_Written, *I, POK_FunctionCall);
> -        break;
> -      }
> -
> -      case attr::SharedLocksRequired: {
> -        // FIXME: Also use this attribute to add required locks to the
> initial
> -        // lockset when processing a CFG for a function annotated with
> this
> -        // attribute.
> -        SharedLocksRequiredAttr *SLRAttr =
> cast<SharedLocksRequiredAttr>(Attr);
> -
> -        for (SharedLocksRequiredAttr::args_iterator I =
> SLRAttr->args_begin(),
> -             E = SLRAttr->args_end(); I != E; ++I)
> -          warnIfMutexNotHeld(D, Exp, AK_Read, *I, POK_FunctionCall);
> -        break;
> -      }
> -
> -      case attr::LocksExcluded: {
> -        LocksExcludedAttr *LEAttr = cast<LocksExcludedAttr>(Attr);
> -        for (LocksExcludedAttr::args_iterator I = LEAttr->args_begin(),
> -            E = LEAttr->args_end(); I != E; ++I) {
> -          MutexID Mutex(*I);
> -          if (locksetContains(Mutex))
> -            Handler.handleFunExcludesLock(D->getName(), Mutex.getName(),
> -                                          ExpLocation);
> -        }
> -        break;
> -      }
> -
> -      case attr::LockReturned:
> -        // FIXME: Deal with this attribute.
> -        break;
> -
> -      // Ignore other (non thread-safety) attributes
> -      default:
> -        break;
> -    }
> -  }
> -}
> -
> -} // end anonymous namespace
> -
> -/// \brief Flags a warning for each lock that is in LSet2 but not LSet1,
> or
> -/// else mutexes that are held shared in one lockset and exclusive in the
> other.
> -static Lockset warnIfNotInFirstSetOrNotSameKind(ThreadSafetyHandler
> &Handler,
> -                                                const Lockset LSet1,
> -                                                const Lockset LSet2,
> -                                                Lockset Intersection,
> -                                                Lockset::Factory &Fact) {
> -  for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I)
> {
> -    const MutexID &LSet2Mutex = I.getKey();
> -    const LockData &LSet2LockData = I.getData();
> -    if (const LockData *LD = LSet1.lookup(LSet2Mutex)) {
> -      if (LD->LKind != LSet2LockData.LKind) {
> -        Handler.handleExclusiveAndShared(LSet2Mutex.getName(),
> -                                         LSet2LockData.AcquireLoc,
> -                                         LD->AcquireLoc);
> -        if (LD->LKind != LK_Exclusive)
> -          Intersection = Fact.add(Intersection, LSet2Mutex,
> LSet2LockData);
> -      }
> -    } else {
> -      Handler.handleMutexHeldEndOfScope(LSet2Mutex.getName(),
> -                                        LSet2LockData.AcquireLoc);
> -    }
> -  }
> -  return Intersection;
> -}
> -
> -
> -/// \brief Compute the intersection of two locksets and issue warnings for
> any
> -/// locks in the symmetric difference.
> -///
> -/// This function is used at a merge point in the CFG when comparing the
> lockset
> -/// of each branch being merged. For example, given the following
> sequence:
> -/// A; if () then B; else C; D; we need to check that the lockset after B
> and C
> -/// are the same. In the event of a difference, we use the intersection of
> these
> -/// two locksets at the start of D.
> -static Lockset intersectAndWarn(ThreadSafetyHandler &Handler,
> -                                const Lockset LSet1, const Lockset LSet2,
> -                                Lockset::Factory &Fact) {
> -  Lockset Intersection = LSet1;
> -  Intersection = warnIfNotInFirstSetOrNotSameKind(Handler, LSet1, LSet2,
> -                                                  Intersection, Fact);
> -
> -  for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I)
> {
> -    if (!LSet2.contains(I.getKey())) {
> -      const MutexID &Mutex = I.getKey();
> -      const LockData &MissingLock = I.getData();
> -      Handler.handleMutexHeldEndOfScope(Mutex.getName(),
> -                                        MissingLock.AcquireLoc);
> -      Intersection = Fact.remove(Intersection, Mutex);
> -    }
> -  }
> -  return Intersection;
> -}
> -
> -/// \brief Returns the location of the first Stmt in a Block.
> -static SourceLocation getFirstStmtLocation(CFGBlock *Block) {
> -  SourceLocation Loc;
> -  for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end();
> -       BI != BE; ++BI) {
> -    if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&(*BI))) {
> -      Loc = CfgStmt->getStmt()->getLocStart();
> -      if (Loc.isValid()) return Loc;
> -    }
> -  }
> -  if (Stmt *S = Block->getTerminator().getStmt()) {
> -    Loc = S->getLocStart();
> -    if (Loc.isValid()) return Loc;
> -  }
> -  return Loc;
> -}
> -
> -/// \brief Warn about different locksets along backedges of loops.
> -/// This function is called when we encounter a back edge. At that point,
> -/// we need to verify that the lockset before taking the backedge is the
> -/// same as the lockset before entering the loop.
> -///
> -/// \param LoopEntrySet Locks before starting the loop
> -/// \param LoopReentrySet Locks in the last CFG block of the loop
> -static void warnBackEdgeUnequalLocksets(ThreadSafetyHandler &Handler,
> -                                        const Lockset LoopReentrySet,
> -                                        const Lockset LoopEntrySet,
> -                                        SourceLocation FirstLocInLoop,
> -                                        Lockset::Factory &Fact) {
> -  assert(FirstLocInLoop.isValid());
> -  // Warn for locks held at the start of the loop, but not the end.
> -  for (Lockset::iterator I = LoopEntrySet.begin(), E = LoopEntrySet.end();
> -       I != E; ++I) {
> -    if (!LoopReentrySet.contains(I.getKey())) {
> -      // We report this error at the location of the first statement in a
> loop
> -      Handler.handleNoLockLoopEntry(I.getKey().getName(), FirstLocInLoop);
> -    }
> -  }
> -
> -  // Warn for locks held at the end of the loop, but not at the start.
> -  warnIfNotInFirstSetOrNotSameKind(Handler, LoopEntrySet, LoopReentrySet,
> -                                   LoopReentrySet, Fact);
> -}
> -
> -
> -namespace clang { namespace thread_safety {
> -/// \brief Check a function's CFG for thread-safety violations.
> -///
> -/// We traverse the blocks in the CFG, compute the set of mutexes that are
> held
> -/// at the end of each block, and issue warnings for thread safety
> violations.
> -/// Each block in the CFG is traversed exactly once.
> -void runThreadSafetyAnalysis(AnalysisContext &AC,
> -                             ThreadSafetyHandler &Handler) {
> -  CFG *CFGraph = AC.getCFG();
> -  if (!CFGraph) return;
> -  const Decl *D = AC.getDecl();
> -  if (D && D->getAttr<NoThreadSafetyAnalysisAttr>()) return;
> -
> -  Lockset::Factory LocksetFactory;
> -
> -  // FIXME: Swith to SmallVector? Otherwise improve performance impact?
> -  std::vector<Lockset> EntryLocksets(CFGraph->getNumBlockIDs(),
> -                                     LocksetFactory.getEmptyMap());
> -  std::vector<Lockset> ExitLocksets(CFGraph->getNumBlockIDs(),
> -                                    LocksetFactory.getEmptyMap());
> -
> -  // We need to explore the CFG via a "topological" ordering.
> -  // That way, we will be guaranteed to have information about required
> -  // predecessor locksets when exploring a new block.
> -  TopologicallySortedCFG SortedGraph(CFGraph);
> -  CFGBlockSet VisitedBlocks(CFGraph);
> -
> -  for (TopologicallySortedCFG::iterator I = SortedGraph.begin(),
> -       E = SortedGraph.end(); I!= E; ++I) {
> -    const CFGBlock *CurrBlock = *I;
> -    int CurrBlockID = CurrBlock->getBlockID();
> -
> -    VisitedBlocks.insert(CurrBlock);
> -
> -    // Use the default initial lockset in case there are no predecessors.
> -    Lockset &Entryset = EntryLocksets[CurrBlockID];
> -    Lockset &Exitset = ExitLocksets[CurrBlockID];
> -
> -    // Iterate through the predecessor blocks and warn if the lockset for
> all
> -    // predecessors is not the same. We take the entry lockset of the
> current
> -    // block to be the intersection of all previous locksets.
> -    // FIXME: By keeping the intersection, we may output more errors in
> future
> -    // for a lock which is not in the intersection, but was in the union.
> We
> -    // may want to also keep the union in future. As an example, let's say
> -    // the intersection contains Mutex L, and the union contains L and M.
> -    // Later we unlock M. At this point, we would output an error because
> we
> -    // never locked M; although the real error is probably that we forgot
> to
> -    // lock M on all code paths. Conversely, let's say that later we lock
> M.
> -    // In this case, we should compare against the intersection instead of
> the
> -    // union because the real error is probably that we forgot to unlock M
> on
> -    // all code paths.
> -    bool LocksetInitialized = false;
> -    for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
> -         PE  = CurrBlock->pred_end(); PI != PE; ++PI) {
> -
> -      // if *PI -> CurrBlock is a back edge
> -      if (*PI == 0 || !VisitedBlocks.alreadySet(*PI))
> -        continue;
> -
> -      int PrevBlockID = (*PI)->getBlockID();
> -      if (!LocksetInitialized) {
> -        Entryset = ExitLocksets[PrevBlockID];
> -        LocksetInitialized = true;
> -      } else {
> -        Entryset = intersectAndWarn(Handler, Entryset,
> -                                    ExitLocksets[PrevBlockID],
> LocksetFactory);
> -      }
> -    }
> -
> -    BuildLockset LocksetBuilder(Handler, Entryset, LocksetFactory);
> -    for (CFGBlock::const_iterator BI = CurrBlock->begin(),
> -         BE = CurrBlock->end(); BI != BE; ++BI) {
> -      if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&*BI))
> -        LocksetBuilder.Visit(const_cast<Stmt*>(CfgStmt->getStmt()));
> -    }
> -    Exitset = LocksetBuilder.getLockset();
> -
> -    // For every back edge from CurrBlock (the end of the loop) to another
> block
> -    // (FirstLoopBlock) we need to check that the Lockset of Block is
> equal to
> -    // the one held at the beginning of FirstLoopBlock. We can look up the
> -    // Lockset held at the beginning of FirstLoopBlock in the
> EntryLockSets map.
> -    for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
> -         SE  = CurrBlock->succ_end(); SI != SE; ++SI) {
> -
> -      // if CurrBlock -> *SI is *not* a back edge
> -      if (*SI == 0 || !VisitedBlocks.alreadySet(*SI))
> -        continue;
> -
> -      CFGBlock *FirstLoopBlock = *SI;
> -      SourceLocation FirstLoopLocation =
> getFirstStmtLocation(FirstLoopBlock);
> -
> -      assert(FirstLoopLocation.isValid());
> -
> -      // Fail gracefully in release code.
> -      if (!FirstLoopLocation.isValid())
> -        continue;
> -
> -      Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()];
> -      Lockset LoopEnd = ExitLocksets[CurrBlockID];
> -      warnBackEdgeUnequalLocksets(Handler, LoopEnd, PreLoop,
> FirstLoopLocation,
> -                                  LocksetFactory);
> -    }
> -  }
> -
> -  Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()];
> -  if (!FinalLockset.isEmpty()) {
> -    for (Lockset::iterator I=FinalLockset.begin(), E=FinalLockset.end();
> -         I != E; ++I) {
> -      const MutexID &Mutex = I.getKey();
> -      const LockData &MissingLock = I.getData();
> -
> -      std::string FunName = "<unknown>";
> -      if (const NamedDecl *ContextDecl =
> dyn_cast<NamedDecl>(AC.getDecl())) {
> -        FunName = ContextDecl->getDeclName().getAsString();
> -      }
> -
> -      Handler.handleNoUnlock(Mutex.getName(), FunName,
> MissingLock.AcquireLoc);
> -    }
> -  }
> -}
> -
> -}} // end namespace clang::thread_safety
> -
> -
>
>  //===----------------------------------------------------------------------===//
>  // AnalysisBasedWarnings - Worker object used by Sema to execute
> analysis-based
>  //  warnings on a function, method, or block.
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20110909/31ea95de/attachment.html>


More information about the cfe-commits mailing list