[cfe-commits] r155137 - /cfe/trunk/lib/Analysis/ThreadSafety.cpp

DeLesley Hutchins delesley at google.com
Thu Apr 19 09:48:43 PDT 2012


Author: delesley
Date: Thu Apr 19 11:48:43 2012
New Revision: 155137

URL: http://llvm.org/viewvc/llvm-project?rev=155137&view=rev
Log:
Refactor the thread safety analysis so that it is easier to do
path-sensitive analysis like handling of trylock expressions.

Modified:
    cfe/trunk/lib/Analysis/ThreadSafety.cpp

Modified: cfe/trunk/lib/Analysis/ThreadSafety.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafety.cpp?rev=155137&r1=155136&r2=155137&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/ThreadSafety.cpp (original)
+++ cfe/trunk/lib/Analysis/ThreadSafety.cpp Thu Apr 19 11:48:43 2012
@@ -334,7 +334,7 @@
 /// A Lockset maps each MutexID (defined above) to information about how it has
 /// been locked.
 typedef llvm::ImmutableMap<MutexID, LockData> Lockset;
-typedef llvm::ImmutableMap<NamedDecl*, unsigned> LocalVarContext;
+typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext;
 
 class LocalVariableMap;
 
@@ -398,21 +398,21 @@
   public:
     friend class LocalVariableMap;
 
-    NamedDecl *Dec;       // The original declaration for this variable.
-    Expr *Exp;            // The expression for this variable, OR
-    unsigned Ref;         // Reference to another VarDefinition
-    Context Ctx;          // The map with which Exp should be interpreted.
+    const NamedDecl *Dec;  // The original declaration for this variable.
+    const Expr *Exp;       // The expression for this variable, OR
+    unsigned Ref;          // Reference to another VarDefinition
+    Context Ctx;           // The map with which Exp should be interpreted.
 
     bool isReference() { return !Exp; }
 
   private:
     // Create ordinary variable definition
-    VarDefinition(NamedDecl *D, Expr *E, Context C)
+    VarDefinition(const NamedDecl *D, const Expr *E, Context C)
       : Dec(D), Exp(E), Ref(0), Ctx(C)
     { }
 
     // Create reference to previous definition
-    VarDefinition(NamedDecl *D, unsigned R, Context C)
+    VarDefinition(const NamedDecl *D, unsigned R, Context C)
       : Dec(D), Exp(0), Ref(R), Ctx(C)
     { }
   };
@@ -430,7 +430,7 @@
   }
 
   /// Look up a definition, within the given context.
-  const VarDefinition* lookup(NamedDecl *D, Context Ctx) {
+  const VarDefinition* lookup(const NamedDecl *D, Context Ctx) {
     const unsigned *i = Ctx.lookup(D);
     if (!i)
       return 0;
@@ -441,7 +441,7 @@
   /// Look up the definition for D within the given context.  Returns
   /// NULL if the expression is not statically known.  If successful, also
   /// modifies Ctx to hold the context of the return Expr.
-  Expr* lookupExpr(NamedDecl *D, Context &Ctx) {
+  const Expr* lookupExpr(const NamedDecl *D, Context &Ctx) {
     const unsigned *P = Ctx.lookup(D);
     if (!P)
       return 0;
@@ -476,7 +476,7 @@
       llvm::errs() << "Undefined";
       return;
     }
-    NamedDecl *Dec = VarDefinitions[i].Dec;
+    const NamedDecl *Dec = VarDefinitions[i].Dec;
     if (!Dec) {
       llvm::errs() << "<<NULL>>";
       return;
@@ -488,7 +488,7 @@
   /// Dumps an ASCII representation of the variable map to llvm::errs()
   void dump() {
     for (unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {
-      Expr *Exp = VarDefinitions[i].Exp;
+      const Expr *Exp = VarDefinitions[i].Exp;
       unsigned Ref = VarDefinitions[i].Ref;
 
       dumpVarDefinitionName(i);
@@ -504,7 +504,7 @@
   /// Dumps an ASCII representation of a Context to llvm::errs()
   void dumpContext(Context C) {
     for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
-      NamedDecl *D = I.getKey();
+      const NamedDecl *D = I.getKey();
       D->printName(llvm::errs());
       const unsigned *i = C.lookup(D);
       llvm::errs() << " -> ";
@@ -528,7 +528,7 @@
 
   // Adds a new definition to the given context, and returns a new context.
   // This method should be called when declaring a new variable.
-  Context addDefinition(NamedDecl *D, Expr *Exp, Context Ctx) {
+  Context addDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) {
     assert(!Ctx.contains(D));
     unsigned newID = VarDefinitions.size();
     Context NewCtx = ContextFactory.add(Ctx, D, newID);
@@ -537,7 +537,7 @@
   }
 
   // Add a new reference to an existing definition.
-  Context addReference(NamedDecl *D, unsigned i, Context Ctx) {
+  Context addReference(const NamedDecl *D, unsigned i, Context Ctx) {
     unsigned newID = VarDefinitions.size();
     Context NewCtx = ContextFactory.add(Ctx, D, newID);
     VarDefinitions.push_back(VarDefinition(D, i, Ctx));
@@ -546,7 +546,7 @@
 
   // Updates a definition only if that definition is already in the map.
   // This method should be called when assigning to an existing variable.
-  Context updateDefinition(NamedDecl *D, Expr *Exp, Context Ctx) {
+  Context updateDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) {
     if (Ctx.contains(D)) {
       unsigned newID = VarDefinitions.size();
       Context NewCtx = ContextFactory.remove(Ctx, D);
@@ -559,7 +559,7 @@
 
   // Removes a definition from the context, but keeps the variable name
   // as a valid variable.  The index 0 is a placeholder for cleared definitions.
-  Context clearDefinition(NamedDecl *D, Context Ctx) {
+  Context clearDefinition(const NamedDecl *D, Context Ctx) {
     Context NewCtx = Ctx;
     if (NewCtx.contains(D)) {
       NewCtx = ContextFactory.remove(NewCtx, D);
@@ -569,7 +569,7 @@
   }
 
   // Remove a definition entirely frmo the context.
-  Context removeDefinition(NamedDecl *D, Context Ctx) {
+  Context removeDefinition(const NamedDecl *D, Context Ctx) {
     Context NewCtx = Ctx;
     if (NewCtx.contains(D)) {
       NewCtx = ContextFactory.remove(NewCtx, D);
@@ -655,7 +655,7 @@
 LocalVariableMap::intersectContexts(Context C1, Context C2) {
   Context Result = C1;
   for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) {
-    NamedDecl *Dec = I.getKey();
+    const NamedDecl *Dec = I.getKey();
     unsigned i1 = I.getData();
     const unsigned *i2 = C2.lookup(Dec);
     if (!i2)             // variable doesn't exist on second path
@@ -672,7 +672,7 @@
 LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
   Context Result = getEmptyContext();
   for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
-    NamedDecl *Dec = I.getKey();
+    const NamedDecl *Dec = I.getKey();
     unsigned i = I.getData();
     Result = addReference(Dec, i, Result);
   }
@@ -684,7 +684,7 @@
 // createReferenceContext.
 void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
   for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) {
-    NamedDecl *Dec = I.getKey();
+    const NamedDecl *Dec = I.getKey();
     unsigned i1 = I.getData();
     VarDefinition *VDef = &VarDefinitions[i1];
     assert(VDef->isReference());
@@ -869,145 +869,103 @@
 class ThreadSafetyAnalyzer {
   friend class BuildLockset;
 
-  ThreadSafetyHandler &Handler;
-  Lockset::Factory    LocksetFactory;
-  LocalVariableMap    LocalVarMap;
+  ThreadSafetyHandler       &Handler;
+  Lockset::Factory          LocksetFactory;
+  LocalVariableMap          LocalVarMap;
+  std::vector<CFGBlockInfo> BlockInfo;
 
 public:
   ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {}
 
-  Lockset intersectAndWarn(const CFGBlockInfo &Block1, CFGBlockSide Side1,
-                           const CFGBlockInfo &Block2, CFGBlockSide Side2,
-                           LockErrorKind LEK);
-
-  Lockset addLock(Lockset &LSet, Expr *MutexExp, const NamedDecl *D,
-                  LockKind LK, SourceLocation Loc);
-
-  void runAnalysis(AnalysisDeclContext &AC);
-};
-
-
-/// \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> {
-  friend class ThreadSafetyAnalyzer;
-
-  ThreadSafetyHandler &Handler;
-  Lockset::Factory &LocksetFactory;
-  LocalVariableMap &LocalVarMap;
-
-  Lockset LSet;
-  LocalVariableMap::Context LVarCtx;
-  unsigned CtxIndex;
-
-  // Helper functions
-  void addLock(const MutexID &Mutex, const LockData &LDat);
-  void removeLock(const MutexID &Mutex, SourceLocation UnlockLoc);
+  Lockset addLock(const Lockset &LSet, const MutexID &Mutex,
+                  const LockData &LDat);
+  Lockset addLock(const Lockset &LSet, Expr *MutexExp, const NamedDecl *D,
+                  const LockData &LDat);
+  Lockset removeLock(const Lockset &LSet, const MutexID &Mutex,
+                     SourceLocation UnlockLoc);
 
   template <class AttrType>
-  void addLocksToSet(LockKind LK, AttrType *Attr,
-                     Expr *Exp, NamedDecl *D, VarDecl *VD = 0);
-  void removeLocksFromSet(UnlockFunctionAttr *Attr,
-                          Expr *Exp, NamedDecl* FunDecl);
-
-  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);
-  void handleCall(Expr *Exp, NamedDecl *D, VarDecl *VD = 0);
+  Lockset addLocksToSet(const Lockset &LSet, LockKind LK, AttrType *Attr,
+                        Expr *Exp, NamedDecl *D, VarDecl *VD = 0);
+  Lockset removeLocksFromSet(const Lockset &LSet,
+                             UnlockFunctionAttr *Attr,
+                             Expr *Exp, NamedDecl* FunDecl);
 
   template <class AttrType>
-  void addTrylock(LockKind LK, AttrType *Attr, Expr *Exp, NamedDecl *FunDecl,
-                  const CFGBlock* PredBlock, const CFGBlock *CurrBlock,
-                  Expr *BrE, bool Neg);
-  CallExpr* getTrylockCallExpr(Stmt *Cond, LocalVariableMap::Context C,
-                               bool &Negate);
-  void handleTrylock(Stmt *Cond, const CFGBlock* PredBlock,
-                     const CFGBlock *CurrBlock);
+  Lockset addTrylock(const Lockset &LSet,
+                     LockKind LK, AttrType *Attr, Expr *Exp, NamedDecl *FunDecl,
+                     const CFGBlock* PredBlock, const CFGBlock *CurrBlock,
+                     Expr *BrE, bool Neg);
+  const CallExpr* getTrylockCallExpr(const Stmt *Cond, LocalVarContext C,
+                                     bool &Negate);
+  Lockset handleTrylock(const Lockset &LSet,
+                        const CFGBlock* PredBlock,
+                        const CFGBlock *CurrBlock);
 
-  /// \brief Returns true if the lockset contains a lock, regardless of whether
-  /// the lock is held exclusively or shared.
-  bool locksetContains(const MutexID &Lock) const {
-    return LSet.lookup(Lock);
-  }
-
-  /// \brief Returns true if the lockset contains a lock with the passed in
-  /// locktype.
-  bool locksetContains(const 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(const MutexID &Lock,
-                              LockKind KindRequested) const {
-    switch (KindRequested) {
-      case LK_Shared:
-        return locksetContains(Lock);
-      case LK_Exclusive:
-        return locksetContains(Lock, KindRequested);
-    }
-    llvm_unreachable("Unknown LockKind");
-  }
-
-public:
-  BuildLockset(ThreadSafetyAnalyzer *analyzer, CFGBlockInfo &Info)
-    : StmtVisitor<BuildLockset>(),
-      Handler(analyzer->Handler),
-      LocksetFactory(analyzer->LocksetFactory),
-      LocalVarMap(analyzer->LocalVarMap),
-      LSet(Info.EntrySet),
-      LVarCtx(Info.EntryContext),
-      CtxIndex(Info.EntryIndex)
-  {}
+  Lockset intersectAndWarn(const CFGBlockInfo &Block1, CFGBlockSide Side1,
+                           const CFGBlockInfo &Block2, CFGBlockSide Side2,
+                           LockErrorKind LEK);
 
-  void VisitUnaryOperator(UnaryOperator *UO);
-  void VisitBinaryOperator(BinaryOperator *BO);
-  void VisitCastExpr(CastExpr *CE);
-  void VisitCallExpr(CallExpr *Exp);
-  void VisitCXXConstructExpr(CXXConstructExpr *Exp);
-  void VisitDeclStmt(DeclStmt *S);
+  void runAnalysis(AnalysisDeclContext &AC);
 };
 
+
 /// \brief Add a new lock to the lockset, warning if the lock is already there.
 /// \param Mutex -- the Mutex expression for the lock
 /// \param LDat  -- the LockData for the lock
-void BuildLockset::addLock(const MutexID &Mutex, const LockData& LDat) {
+Lockset ThreadSafetyAnalyzer::addLock(const Lockset &LSet,
+                                      const MutexID &Mutex,
+                                      const LockData &LDat) {
   // FIXME: deal with acquired before/after annotations.
   // FIXME: Don't always warn when we have support for reentrant locks.
-  if (locksetContains(Mutex))
+  if (LSet.lookup(Mutex)) {
     Handler.handleDoubleLock(Mutex.getName(), LDat.AcquireLoc);
-  else
-    LSet = LocksetFactory.add(LSet, Mutex, LDat);
+    return LSet;
+  } else {
+    return LocksetFactory.add(LSet, Mutex, LDat);
+  }
+}
+
+/// \brief Construct a new mutex and add it to the lockset.
+Lockset ThreadSafetyAnalyzer::addLock(const Lockset &LSet,
+                                      Expr *MutexExp, const NamedDecl *D,
+                                      const LockData &LDat) {
+  MutexID Mutex(MutexExp, 0, D);
+  if (!Mutex.isValid()) {
+    MutexID::warnInvalidLock(Handler, MutexExp, 0, D);
+    return LSet;
+  }
+  return addLock(LSet, Mutex, LDat);
 }
 
+
 /// \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(const MutexID &Mutex, SourceLocation UnlockLoc) {
+Lockset ThreadSafetyAnalyzer::removeLock(const Lockset &LSet,
+                                         const MutexID &Mutex,
+                                         SourceLocation UnlockLoc) {
   const LockData *LDat = LSet.lookup(Mutex);
-  if (!LDat)
+  if (!LDat) {
     Handler.handleUnmatchedUnlock(Mutex.getName(), UnlockLoc);
+    return LSet;
+  }
   else {
+    Lockset Result = LSet;
     // For scoped-lockable vars, remove the mutex associated with this var.
     if (LDat->UnderlyingMutex.isValid())
-      removeLock(LDat->UnderlyingMutex, UnlockLoc);
-    LSet = LocksetFactory.remove(LSet, Mutex);
+      Result = removeLock(Result, LDat->UnderlyingMutex, UnlockLoc);
+    return LocksetFactory.remove(Result, Mutex);
   }
 }
 
 /// \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, AttrType *Attr,
-                                 Expr *Exp, NamedDecl* FunDecl, VarDecl *VD) {
+Lockset ThreadSafetyAnalyzer::addLocksToSet(const Lockset &LSet,
+                                            LockKind LK, AttrType *Attr,
+                                            Expr *Exp, NamedDecl* FunDecl,
+                                            VarDecl *VD) {
   typedef typename AttrType::args_iterator iterator_type;
 
   SourceLocation ExpLocation = Exp->getExprLoc();
@@ -1025,56 +983,248 @@
   if (Attr->args_size() == 0) {
     // The mutex held is the "this" object.
     MutexID Mutex(0, Exp, FunDecl);
-    if (!Mutex.isValid())
+    if (!Mutex.isValid()) {
       MutexID::warnInvalidLock(Handler, 0, Exp, FunDecl);
-    else
-      addLock(Mutex, LockData(ExpLocation, LK));
-    return;
+      return LSet;
+    }
+    else {
+      return addLock(LSet, Mutex, LockData(ExpLocation, LK));
+    }
   }
 
+  Lockset Result = LSet;
   for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) {
     MutexID Mutex(*I, Exp, FunDecl);
     if (!Mutex.isValid())
       MutexID::warnInvalidLock(Handler, *I, Exp, FunDecl);
     else {
-      addLock(Mutex, LockData(ExpLocation, LK));
+      Result = addLock(Result, Mutex, LockData(ExpLocation, LK));
       if (isScopedVar) {
         // For scoped lockable vars, map this var to its underlying mutex.
         DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation());
         MutexID SMutex(&DRE, 0, 0);
-        addLock(SMutex, LockData(VD->getLocation(), LK, Mutex));
+        Result = addLock(Result, SMutex,
+                         LockData(VD->getLocation(), LK, Mutex));
       }
     }
   }
+  return Result;
 }
 
 /// \brief This function removes a set of locks specified as attribute
 /// arguments from the lockset.
-void BuildLockset::removeLocksFromSet(UnlockFunctionAttr *Attr,
-                                      Expr *Exp, NamedDecl* FunDecl) {
+Lockset ThreadSafetyAnalyzer::removeLocksFromSet(const Lockset &LSet,
+                                                 UnlockFunctionAttr *Attr,
+                                                 Expr *Exp, NamedDecl* FunDecl) {
   SourceLocation ExpLocation;
   if (Exp) ExpLocation = Exp->getExprLoc();
 
   if (Attr->args_size() == 0) {
     // The mutex held is the "this" object.
     MutexID Mu(0, Exp, FunDecl);
-    if (!Mu.isValid())
+    if (!Mu.isValid()) {
       MutexID::warnInvalidLock(Handler, 0, Exp, FunDecl);
-    else
-      removeLock(Mu, ExpLocation);
-    return;
+      return LSet;
+    } else {
+      return removeLock(LSet, Mu, ExpLocation);
+    }
   }
 
+  Lockset Result = LSet;
   for (UnlockFunctionAttr::args_iterator I = Attr->args_begin(),
        E = Attr->args_end(); I != E; ++I) {
     MutexID Mutex(*I, Exp, FunDecl);
     if (!Mutex.isValid())
       MutexID::warnInvalidLock(Handler, *I, Exp, FunDecl);
     else
-      removeLock(Mutex, ExpLocation);
+      Result = removeLock(Result, Mutex, ExpLocation);
+  }
+  return Result;
+}
+
+
+/// \brief Add lock to set, if the current block is in the taken branch of a
+/// trylock.
+template <class AttrType>
+Lockset ThreadSafetyAnalyzer::addTrylock(const Lockset &LSet,
+                                         LockKind LK, AttrType *Attr,
+                                         Expr *Exp, NamedDecl *FunDecl,
+                                         const CFGBlock *PredBlock,
+                                         const CFGBlock *CurrBlock,
+                                         Expr *BrE, bool Neg) {
+  // Find out which branch has the lock
+  bool branch = 0;
+  if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE)) {
+    branch = BLE->getValue();
+  }
+  else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE)) {
+    branch = ILE->getValue().getBoolValue();
+  }
+  int branchnum = branch ? 0 : 1;
+  if (Neg) branchnum = !branchnum;
+
+  Lockset Result = LSet;
+  // If we've taken the trylock branch, then add the lock
+  int i = 0;
+  for (CFGBlock::const_succ_iterator SI = PredBlock->succ_begin(),
+       SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) {
+    if (*SI == CurrBlock && i == branchnum) {
+      Result = addLocksToSet(Result, LK, Attr, Exp, FunDecl, 0);
+    }
+  }
+  return Result;
+}
+
+
+// If Cond can be traced back to a function call, return the call expression.
+// The negate variable should be called with false, and will be set to true
+// if the function call is negated, e.g. if (!mu.tryLock(...))
+const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,
+                                                         LocalVarContext C,
+                                                         bool &Negate) {
+  if (!Cond)
+    return 0;
+
+  if (const CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) {
+    return CallExp;
+  }
+  else if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Cond)) {
+    return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
+  }
+  else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Cond)) {
+    const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
+    return getTrylockCallExpr(E, C, Negate);
+  }
+  else if (const UnaryOperator *UOP = dyn_cast<UnaryOperator>(Cond)) {
+    if (UOP->getOpcode() == UO_LNot) {
+      Negate = !Negate;
+      return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
+    }
+  }
+  // FIXME -- handle && and || as well.
+  return NULL;
+}
+
+
+/// \brief Process a conditional branch from a previous block to the current
+/// block, looking for trylock calls.
+Lockset ThreadSafetyAnalyzer::handleTrylock(const Lockset &LSet,
+                                            const CFGBlock *PredBlock,
+                                            const CFGBlock *CurrBlock) {
+  bool Negate = false;
+  const Stmt *Cond = PredBlock->getTerminatorCondition();
+  const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()];
+  const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
+
+  CallExpr *Exp = const_cast<CallExpr*>(
+    getTrylockCallExpr(Cond, LVarCtx, Negate));
+  if (!Exp)
+    return LSet;
+
+  NamedDecl *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
+  if(!FunDecl || !FunDecl->hasAttrs())
+    return LSet;
+
+  Lockset Result = LSet;
+
+  // If the condition is a call to a Trylock function, then grab the attributes
+  AttrVec &ArgAttrs = FunDecl->getAttrs();
+  for (unsigned i = 0; i < ArgAttrs.size(); ++i) {
+    Attr *Attr = ArgAttrs[i];
+    switch (Attr->getKind()) {
+      case attr::ExclusiveTrylockFunction: {
+        ExclusiveTrylockFunctionAttr *A =
+          cast<ExclusiveTrylockFunctionAttr>(Attr);
+        Result = addTrylock(Result, LK_Exclusive, A, Exp, FunDecl,
+                            PredBlock, CurrBlock,
+                            A->getSuccessValue(), Negate);
+        break;
+      }
+      case attr::SharedTrylockFunction: {
+        SharedTrylockFunctionAttr *A =
+          cast<SharedTrylockFunctionAttr>(Attr);
+        Result = addTrylock(Result, LK_Shared, A, Exp, FunDecl,
+                            PredBlock, CurrBlock,
+                            A->getSuccessValue(), Negate);
+        break;
+      }
+      default:
+        break;
+    }
   }
+  return Result;
 }
 
+
+/// \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> {
+  friend class ThreadSafetyAnalyzer;
+
+  ThreadSafetyAnalyzer *Analyzer;
+  Lockset LSet;
+  LocalVariableMap::Context LVarCtx;
+  unsigned CtxIndex;
+
+  // Helper functions
+  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);
+  void handleCall(Expr *Exp, NamedDecl *D, VarDecl *VD = 0);
+
+  /// \brief Returns true if the lockset contains a lock, regardless of whether
+  /// the lock is held exclusively or shared.
+  bool locksetContains(const MutexID &Lock) const {
+    return LSet.lookup(Lock);
+  }
+
+  /// \brief Returns true if the lockset contains a lock with the passed in
+  /// locktype.
+  bool locksetContains(const 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(const MutexID &Lock,
+                              LockKind KindRequested) const {
+    switch (KindRequested) {
+      case LK_Shared:
+        return locksetContains(Lock);
+      case LK_Exclusive:
+        return locksetContains(Lock, KindRequested);
+    }
+    llvm_unreachable("Unknown LockKind");
+  }
+
+public:
+  BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
+    : StmtVisitor<BuildLockset>(),
+      Analyzer(Anlzr),
+      LSet(Info.EntrySet),
+      LVarCtx(Info.EntryContext),
+      CtxIndex(Info.EntryIndex)
+  {}
+
+  void VisitUnaryOperator(UnaryOperator *UO);
+  void VisitBinaryOperator(BinaryOperator *BO);
+  void VisitCastExpr(CastExpr *CE);
+  void VisitCallExpr(CallExpr *Exp);
+  void VisitCXXConstructExpr(CXXConstructExpr *Exp);
+  void VisitDeclStmt(DeclStmt *S);
+};
+
+
 /// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs
 const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
   if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
@@ -1095,9 +1245,10 @@
 
   MutexID Mutex(MutexExp, Exp, D);
   if (!Mutex.isValid())
-    MutexID::warnInvalidLock(Handler, MutexExp, Exp, D);
+    MutexID::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D);
   else if (!locksetContainsAtLeast(Mutex, LK))
-    Handler.handleMutexNotHeld(D, POK, Mutex.getName(), LK, Exp->getExprLoc());
+    Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.getName(), LK,
+                                         Exp->getExprLoc());
 }
 
 /// \brief This method identifies variable dereferences and checks pt_guarded_by
@@ -1117,7 +1268,8 @@
     return;
 
   if (D->getAttr<PtGuardedVarAttr>() && LSet.isEmpty())
-    Handler.handleNoMutexHeld(D, POK_VarDereference, AK, Exp->getExprLoc());
+    Analyzer->Handler.handleNoMutexHeld(D, POK_VarDereference, AK,
+                                        Exp->getExprLoc());
 
   const AttrVec &ArgAttrs = D->getAttrs();
   for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
@@ -1135,7 +1287,8 @@
     return;
 
   if (D->getAttr<GuardedVarAttr>() && LSet.isEmpty())
-    Handler.handleNoMutexHeld(D, POK_VarAccess, AK, Exp->getExprLoc());
+    Analyzer->Handler.handleNoMutexHeld(D, POK_VarAccess, AK,
+                                        Exp->getExprLoc());
 
   const AttrVec &ArgAttrs = D->getAttrs();
   for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
@@ -1166,7 +1319,7 @@
       // to our lockset with kind exclusive.
       case attr::ExclusiveLockFunction: {
         ExclusiveLockFunctionAttr *A = cast<ExclusiveLockFunctionAttr>(Attr);
-        addLocksToSet(LK_Exclusive, A, Exp, D, VD);
+        LSet = Analyzer->addLocksToSet(LSet, LK_Exclusive, A, Exp, D, VD);
         break;
       }
 
@@ -1174,7 +1327,7 @@
       // to our lockset with kind shared.
       case attr::SharedLockFunction: {
         SharedLockFunctionAttr *A = cast<SharedLockFunctionAttr>(Attr);
-        addLocksToSet(LK_Shared, A, Exp, D, VD);
+        LSet = Analyzer->addLocksToSet(LSet, LK_Shared, A, Exp, D, VD);
         break;
       }
 
@@ -1182,7 +1335,7 @@
       // mutexes from the lockset, and flag a warning if they are not there.
       case attr::UnlockFunction: {
         UnlockFunctionAttr *UFAttr = cast<UnlockFunctionAttr>(Attr);
-        removeLocksFromSet(UFAttr, Exp, D);
+        LSet = Analyzer->removeLocksFromSet(LSet, UFAttr, Exp, D);
         break;
       }
 
@@ -1211,10 +1364,11 @@
             E = LEAttr->args_end(); I != E; ++I) {
           MutexID Mutex(*I, Exp, D);
           if (!Mutex.isValid())
-            MutexID::warnInvalidLock(Handler, *I, Exp, D);
+            MutexID::warnInvalidLock(Analyzer->Handler, *I, Exp, D);
           else if (locksetContains(Mutex))
-            Handler.handleFunExcludesLock(D->getName(), Mutex.getName(),
-                                          Exp->getExprLoc());
+            Analyzer->Handler.handleFunExcludesLock(D->getName(),
+                                                    Mutex.getName(),
+                                                    Exp->getExprLoc());
         }
         break;
       }
@@ -1227,103 +1381,6 @@
 }
 
 
-/// \brief Add lock to set, if the current block is in the taken branch of a
-/// trylock.
-template <class AttrType>
-void BuildLockset::addTrylock(LockKind LK, AttrType *Attr, Expr *Exp,
-                              NamedDecl *FunDecl, const CFGBlock *PredBlock,
-                              const CFGBlock *CurrBlock, Expr *BrE, bool Neg) {
-  // Find out which branch has the lock
-  bool branch = 0;
-  if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE)) {
-    branch = BLE->getValue();
-  }
-  else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE)) {
-    branch = ILE->getValue().getBoolValue();
-  }
-  int branchnum = branch ? 0 : 1;
-  if (Neg) branchnum = !branchnum;
-
-  // If we've taken the trylock branch, then add the lock
-  int i = 0;
-  for (CFGBlock::const_succ_iterator SI = PredBlock->succ_begin(),
-       SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) {
-    if (*SI == CurrBlock && i == branchnum) {
-      addLocksToSet(LK, Attr, Exp, FunDecl, 0);
-    }
-  }
-}
-
-
-// If Cond can be traced back to a function call, return the call expression.
-// The negate variable should be called with false, and will be set to true
-// if the function call is negated, e.g. if (!mu.tryLock(...))
-CallExpr* BuildLockset::getTrylockCallExpr(Stmt *Cond,
-                               LocalVariableMap::Context C,
-                               bool &Negate) {
-  if (!Cond)
-    return 0;
-
-  if (CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) {
-    return CallExp;
-  }
-  else if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Cond)) {
-    return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
-  }
-  else if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Cond)) {
-    Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
-    return getTrylockCallExpr(E, C, Negate);
-  }
-  else if (UnaryOperator *UOP = dyn_cast<UnaryOperator>(Cond)) {
-    if (UOP->getOpcode() == UO_LNot) {
-      Negate = !Negate;
-      return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
-    }
-  }
-  // FIXME -- handle && and || as well.
-  return NULL;
-}
-
-
-/// \brief Process a conditional branch from a previous block to the current
-/// block, looking for trylock calls.
-void BuildLockset::handleTrylock(Stmt *Cond, const CFGBlock *PredBlock,
-                                 const CFGBlock *CurrBlock) {
-  bool Negate = false;
-  CallExpr *Exp = getTrylockCallExpr(Cond, LVarCtx, Negate);
-  if (!Exp)
-    return;
-
-  NamedDecl *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
-  if(!FunDecl || !FunDecl->hasAttrs())
-    return;
-
-  // If the condition is a call to a Trylock function, then grab the attributes
-  AttrVec &ArgAttrs = FunDecl->getAttrs();
-  for (unsigned i = 0; i < ArgAttrs.size(); ++i) {
-    Attr *Attr = ArgAttrs[i];
-    switch (Attr->getKind()) {
-      case attr::ExclusiveTrylockFunction: {
-        ExclusiveTrylockFunctionAttr *A =
-          cast<ExclusiveTrylockFunctionAttr>(Attr);
-        addTrylock(LK_Exclusive, A, Exp, FunDecl, PredBlock, CurrBlock,
-                   A->getSuccessValue(), Negate);
-        break;
-      }
-      case attr::SharedTrylockFunction: {
-        SharedTrylockFunctionAttr *A =
-          cast<SharedTrylockFunctionAttr>(Attr);
-        addTrylock(LK_Shared, A, Exp, FunDecl, PredBlock, CurrBlock,
-                   A->getSuccessValue(), Negate);
-        break;
-      }
-      default:
-        break;
-    }
-  }
-}
-
-
 /// \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.
@@ -1351,7 +1408,7 @@
     return;
 
   // adjust the context
-  LVarCtx = LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
+  LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
 
   Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
   checkAccess(LHSExp, AK_Written);
@@ -1383,7 +1440,7 @@
 
 void BuildLockset::VisitDeclStmt(DeclStmt *S) {
   // adjust the context
-  LVarCtx = LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
+  LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
 
   DeclGroupRef DGrp = S->getDeclGroup();
   for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) {
@@ -1450,17 +1507,6 @@
   return Intersection;
 }
 
-Lockset ThreadSafetyAnalyzer::addLock(Lockset &LSet, Expr *MutexExp,
-                                      const NamedDecl *D,
-                                      LockKind LK, SourceLocation Loc) {
-  MutexID Mutex(MutexExp, 0, D);
-  if (!Mutex.isValid()) {
-    MutexID::warnInvalidLock(Handler, MutexExp, 0, D);
-    return LSet;
-  }
-  LockData NewLock(Loc, LK);
-  return LocksetFactory.add(LSet, Mutex, NewLock);
-}
 
 /// \brief Check a function's CFG for thread-safety violations.
 ///
@@ -1485,7 +1531,7 @@
   if (isa<CXXDestructorDecl>(D))
     return;  // Don't check inside destructors.
 
-  std::vector<CFGBlockInfo> BlockInfo(CFGraph->getNumBlockIDs(),
+  BlockInfo.resize(CFGraph->getNumBlockIDs(),
     CFGBlockInfo::getEmptyBlockInfo(LocksetFactory, LocalVarMap));
 
   // We need to explore the CFG via a "topological" ordering.
@@ -1515,17 +1561,15 @@
         for (SharedLocksRequiredAttr::args_iterator
              SLRIter = SLRAttr->args_begin(),
              SLREnd = SLRAttr->args_end(); SLRIter != SLREnd; ++SLRIter)
-          InitialLockset = addLock(InitialLockset,
-                                   *SLRIter, D, LK_Shared,
-                                   AttrLoc);
+          InitialLockset = addLock(InitialLockset, *SLRIter, D,
+                                   LockData(AttrLoc, LK_Shared));
       } else if (ExclusiveLocksRequiredAttr *ELRAttr
                    = dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) {
         for (ExclusiveLocksRequiredAttr::args_iterator
              ELRIter = ELRAttr->args_begin(),
              ELREnd = ELRAttr->args_end(); ELRIter != ELREnd; ++ELRIter)
-          InitialLockset = addLock(InitialLockset,
-                                   *ELRIter, D, LK_Exclusive,
-                                   AttrLoc);
+          InitialLockset = addLock(InitialLockset, *ELRIter, D,
+                                   LockData(AttrLoc, LK_Exclusive));
       } else if (isa<UnlockFunctionAttr>(Attr)) {
         // Don't try to check unlock functions for now
         return;
@@ -1626,17 +1670,19 @@
       }
     }
 
-    BuildLockset LocksetBuilder(this, *CurrBlockInfo);
-    CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
-                                  PE = CurrBlock->pred_end();
-    if (PI != PE) {
+    // If the previous block ended in a trylock, then grab any extra mutexes
+    // from the trylock.
+    for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
+         PE = CurrBlock->pred_end(); PI != PE; ++PI) {
       // If the predecessor ended in a branch, then process any trylocks.
-      // FIXME -- check to make sure there's only one predecessor.
-      if (Stmt *TCE = (*PI)->getTerminatorCondition()) {
-        LocksetBuilder.handleTrylock(TCE, *PI, CurrBlock);
+      if ((*PI)->getTerminatorCondition()) {
+        CurrBlockInfo->EntrySet = handleTrylock(CurrBlockInfo->EntrySet,
+                                                *PI, CurrBlock);
       }
     }
 
+    BuildLockset LocksetBuilder(this, *CurrBlockInfo);
+
     // Visit all the statements in the basic block.
     for (CFGBlock::const_iterator BI = CurrBlock->begin(),
          BE = CurrBlock->end(); BI != BE; ++BI) {





More information about the cfe-commits mailing list