[cfe-commits] r147672 - in /cfe/trunk: lib/Analysis/ThreadSafety.cpp test/SemaCXX/warn-thread-safety-analysis.cpp

DeLesley Hutchins delesley at google.com
Fri Jan 6 11:16:50 PST 2012


Author: delesley
Date: Fri Jan  6 13:16:50 2012
New Revision: 147672

URL: http://llvm.org/viewvc/llvm-project?rev=147672&view=rev
Log:
Thread safety analysis: added support for trylock attribute.

Modified:
    cfe/trunk/lib/Analysis/ThreadSafety.cpp
    cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp

Modified: cfe/trunk/lib/Analysis/ThreadSafety.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafety.cpp?rev=147672&r1=147671&r2=147672&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/ThreadSafety.cpp (original)
+++ cfe/trunk/lib/Analysis/ThreadSafety.cpp Fri Jan  6 13:16:50 2012
@@ -379,16 +379,19 @@
   }
 
   /// Look up the definition for D within the given context.  Returns
-  /// NULL if the expression is not statically known.
-  Expr* lookupExpr(NamedDecl *D, Context Ctx) {
+  /// 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 unsigned *P = Ctx.lookup(D);
     if (!P)
       return 0;
 
     unsigned i = *P;
     while (i > 0) {
-      if (VarDefinitions[i].Exp)
+      if (VarDefinitions[i].Exp) {
+        Ctx = VarDefinitions[i].Ctx;
         return VarDefinitions[i].Exp;
+      }
       i = VarDefinitions[i].Ref;
     }
     return 0;
@@ -811,6 +814,15 @@
   void checkDereference(Expr *Exp, AccessKind AK);
   void handleCall(Expr *Exp, NamedDecl *D, VarDecl *VD = 0);
 
+  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);
+
   /// \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 {
@@ -1108,6 +1120,104 @@
   }
 }
 
+
+/// \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.
@@ -1339,6 +1449,16 @@
     }
 
     BuildLockset LocksetBuilder(this, *CurrBlockInfo);
+    CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
+                                  PE = CurrBlock->pred_end();
+    if (PI != PE) {
+      // 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);
+      }
+    }
+
     // Visit all the statements in the basic block.
     for (CFGBlock::const_iterator BI = CurrBlock->begin(),
          BE = CurrBlock->end(); BI != BE; ++BI) {

Modified: cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp?rev=147672&r1=147671&r2=147672&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp Fri Jan  6 13:16:50 2012
@@ -1605,6 +1605,7 @@
 } // end namespace test_scoped_lockable
 
 
+
 namespace FunctionAttrTest {
 
 class Foo {
@@ -1627,3 +1628,103 @@
 };  // end namespace FunctionAttrTest
 
 
+struct TestTryLock {
+  Mutex mu;
+  int a GUARDED_BY(mu);
+  bool cond;
+
+  void foo1() {
+    if (mu.TryLock()) {
+      a = 1;
+      mu.Unlock();
+    }
+  }
+
+  void foo2() {
+    if (!mu.TryLock()) return;
+    a = 2;
+    mu.Unlock();
+  }
+
+  void foo3() {
+    bool b = mu.TryLock();
+    if (b) {
+      a = 3;
+      mu.Unlock();
+    }
+  }
+
+  void foo4() {
+    bool b = mu.TryLock();
+    if (!b) return;
+    a = 4;
+    mu.Unlock();
+  }
+
+  void foo5() {
+    while (mu.TryLock()) {
+      a = a + 1;
+      mu.Unlock();
+    }
+  }
+
+  void foo6() {
+    bool b = mu.TryLock();
+    b = !b;
+    if (b) return;
+    a = 6;
+    mu.Unlock();
+  }
+
+  void foo7() {
+    bool b1 = mu.TryLock();
+    bool b2 = !b1;
+    bool b3 = !b2;
+    if (b3) {
+      a = 7;
+      mu.Unlock();
+    }
+  }
+
+  // Test use-def chains: join points
+  void foo8() {
+    bool b  = mu.TryLock();
+    bool b2 = b;
+    if (cond)
+      b = true;
+    if (b) {    // b should be unknown at this point, becuase of the join point
+      a = 8;    // expected-warning {{writing variable 'a' requires locking 'mu' exclusively}}
+    }
+    if (b2) {   // b2 should be known at this point.
+      a = 8;
+      mu.Unlock();
+    }
+  }
+
+  // Test use-def-chains: back edges
+  void foo9() {
+    bool b = mu.TryLock();
+
+    for (int i = 0; i < 10; ++i);
+
+    if (b) {  // b is still known, because the loop doesn't alter it
+      a = 9;
+      mu.Unlock();
+    }
+  }
+
+  // Test use-def chains: back edges
+  void foo10() {
+    bool b = mu.TryLock();
+
+    while (cond) {
+      if (b) {   // b should be uknown at this point b/c of the loop
+        a = 10;  // expected-warning {{writing variable 'a' requires locking 'mu' exclusively}}
+      }
+      b = !b;
+    }
+  }
+};  // end TestTrylock
+
+
+





More information about the cfe-commits mailing list