[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