aboutsummaryrefslogtreecommitdiff
path: root/lib/Analysis/ThreadSafety.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Analysis/ThreadSafety.cpp')
-rw-r--r--lib/Analysis/ThreadSafety.cpp150
1 files changed, 91 insertions, 59 deletions
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index 7bb54d6ad3..479d9a301f 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -784,7 +784,7 @@ struct LockData {
/// \brief A FactEntry stores a single fact that is known at a particular point
/// in the program execution. Currently, this is information regarding a lock
-/// that is held at that point.
+/// that is held at that point.
struct FactEntry {
SExpr MutID;
LockData LDat;
@@ -797,7 +797,7 @@ struct FactEntry {
typedef unsigned short FactID;
-/// \brief FactManager manages the memory for all facts that are created during
+/// \brief FactManager manages the memory for all facts that are created during
/// the analysis of a single routine.
class FactManager {
private:
@@ -815,9 +815,9 @@ public:
/// \brief A FactSet is the set of facts that are known to be true at a
-/// particular program point. FactSets must be small, because they are
+/// particular program point. FactSets must be small, because they are
/// frequently copied, and are thus implemented as a set of indices into a
-/// table maintained by a FactManager. A typical FactSet only holds 1 or 2
+/// table maintained by a FactManager. A typical FactSet only holds 1 or 2
/// locks, so we can get away with doing a linear search for lookup. Note
/// that a hashtable or map is inappropriate in this case, because lookups
/// may involve partial pattern matches, rather than exact matches.
@@ -1858,13 +1858,11 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK) {
return;
}
- if (Analyzer->Handler.issueBetaWarnings()) {
- if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
- if (ME->isArrow())
- checkPtAccess(ME->getBase(), AK);
- else
- checkAccess(ME->getBase(), AK);
- }
+ if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
+ if (ME->isArrow())
+ checkPtAccess(ME->getBase(), AK);
+ else
+ checkAccess(ME->getBase(), AK);
}
const ValueDecl *D = getValueDecl(Exp);
@@ -2065,40 +2063,38 @@ void BuildLockset::VisitCastExpr(CastExpr *CE) {
void BuildLockset::VisitCallExpr(CallExpr *Exp) {
- if (Analyzer->Handler.issueBetaWarnings()) {
- if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
- MemberExpr *ME = dyn_cast<MemberExpr>(CE->getCallee());
- // ME can be null when calling a method pointer
- CXXMethodDecl *MD = CE->getMethodDecl();
-
- if (ME && MD) {
- if (ME->isArrow()) {
- if (MD->isConst()) {
- checkPtAccess(CE->getImplicitObjectArgument(), AK_Read);
- } else { // FIXME -- should be AK_Written
- checkPtAccess(CE->getImplicitObjectArgument(), AK_Read);
- }
- } else {
- if (MD->isConst())
- checkAccess(CE->getImplicitObjectArgument(), AK_Read);
- else // FIXME -- should be AK_Written
- checkAccess(CE->getImplicitObjectArgument(), AK_Read);
+ if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
+ MemberExpr *ME = dyn_cast<MemberExpr>(CE->getCallee());
+ // ME can be null when calling a method pointer
+ CXXMethodDecl *MD = CE->getMethodDecl();
+
+ if (ME && MD) {
+ if (ME->isArrow()) {
+ if (MD->isConst()) {
+ checkPtAccess(CE->getImplicitObjectArgument(), AK_Read);
+ } else { // FIXME -- should be AK_Written
+ checkPtAccess(CE->getImplicitObjectArgument(), AK_Read);
}
+ } else {
+ if (MD->isConst())
+ checkAccess(CE->getImplicitObjectArgument(), AK_Read);
+ else // FIXME -- should be AK_Written
+ checkAccess(CE->getImplicitObjectArgument(), AK_Read);
}
- } else if (CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
- switch (OE->getOperator()) {
- case OO_Equal: {
- const Expr *Target = OE->getArg(0);
- const Expr *Source = OE->getArg(1);
- checkAccess(Target, AK_Written);
- checkAccess(Source, AK_Read);
- break;
- }
- default: {
- const Expr *Source = OE->getArg(0);
- checkAccess(Source, AK_Read);
- break;
- }
+ }
+ } else if (CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
+ switch (OE->getOperator()) {
+ case OO_Equal: {
+ const Expr *Target = OE->getArg(0);
+ const Expr *Source = OE->getArg(1);
+ checkAccess(Target, AK_Written);
+ checkAccess(Source, AK_Read);
+ break;
+ }
+ default: {
+ const Expr *Source = OE->getArg(0);
+ checkAccess(Source, AK_Read);
+ break;
}
}
}
@@ -2109,12 +2105,10 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) {
}
void BuildLockset::VisitCXXConstructExpr(CXXConstructExpr *Exp) {
- if (Analyzer->Handler.issueBetaWarnings()) {
- const CXXConstructorDecl *D = Exp->getConstructor();
- if (D && D->isCopyConstructor()) {
- const Expr* Source = Exp->getArg(0);
- checkAccess(Source, AK_Read);
- }
+ const CXXConstructorDecl *D = Exp->getConstructor();
+ if (D && D->isCopyConstructor()) {
+ const Expr* Source = Exp->getArg(0);
+ checkAccess(Source, AK_Read);
}
// FIXME -- only handles constructors in DeclStmt below.
}
@@ -2285,6 +2279,10 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
// Fill in source locations for all CFGBlocks.
findBlockLocations(CFGraph, SortedGraph, BlockInfo);
+ MutexIDList ExclusiveLocksAcquired;
+ MutexIDList SharedLocksAcquired;
+ MutexIDList LocksReleased;
+
// Add locks from exclusive_locks_required and shared_locks_required
// to initial lockset. Also turn off checking for lock and unlock functions.
// FIXME: is there a more intelligent way to check lock/unlock functions?
@@ -2306,15 +2304,30 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
} else if (SharedLocksRequiredAttr *A
= dyn_cast<SharedLocksRequiredAttr>(Attr)) {
getMutexIDs(SharedLocksToAdd, A, (Expr*) 0, D);
- } else if (isa<UnlockFunctionAttr>(Attr)) {
- // Don't try to check unlock functions for now
- return;
- } else if (isa<ExclusiveLockFunctionAttr>(Attr)) {
- // Don't try to check lock functions for now
- return;
- } else if (isa<SharedLockFunctionAttr>(Attr)) {
- // Don't try to check lock functions for now
- return;
+ } else if (UnlockFunctionAttr *A = dyn_cast<UnlockFunctionAttr>(Attr)) {
+ if (!Handler.issueBetaWarnings())
+ return;
+ // UNLOCK_FUNCTION() is used to hide the underlying lock implementation.
+ // We must ignore such methods.
+ if (A->args_size() == 0)
+ return;
+ // FIXME -- deal with exclusive vs. shared unlock functions?
+ getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D);
+ getMutexIDs(LocksReleased, A, (Expr*) 0, D);
+ } else if (ExclusiveLockFunctionAttr *A
+ = dyn_cast<ExclusiveLockFunctionAttr>(Attr)) {
+ if (!Handler.issueBetaWarnings())
+ return;
+ if (A->args_size() == 0)
+ return;
+ getMutexIDs(ExclusiveLocksAcquired, A, (Expr*) 0, D);
+ } else if (SharedLockFunctionAttr *A
+ = dyn_cast<SharedLockFunctionAttr>(Attr)) {
+ if (!Handler.issueBetaWarnings())
+ return;
+ if (A->args_size() == 0)
+ return;
+ getMutexIDs(SharedLocksAcquired, A, (Expr*) 0, D);
} else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) {
// Don't try to check trylock functions for now
return;
@@ -2497,8 +2510,27 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
if (!Final->Reachable)
return;
+ // By default, we expect all locks held on entry to be held on exit.
+ FactSet ExpectedExitSet = Initial->EntrySet;
+
+ // Adjust the expected exit set by adding or removing locks, as declared
+ // by *-LOCK_FUNCTION and UNLOCK_FUNCTION. The intersect below will then
+ // issue the appropriate warning.
+ // FIXME: the location here is not quite right.
+ for (unsigned i=0,n=ExclusiveLocksAcquired.size(); i<n; ++i) {
+ ExpectedExitSet.addLock(FactMan, ExclusiveLocksAcquired[i],
+ LockData(D->getLocation(), LK_Exclusive));
+ }
+ for (unsigned i=0,n=SharedLocksAcquired.size(); i<n; ++i) {
+ ExpectedExitSet.addLock(FactMan, SharedLocksAcquired[i],
+ LockData(D->getLocation(), LK_Shared));
+ }
+ for (unsigned i=0,n=LocksReleased.size(); i<n; ++i) {
+ ExpectedExitSet.removeLock(FactMan, LocksReleased[i]);
+ }
+
// FIXME: Should we call this function for all blocks which exit the function?
- intersectAndWarn(Initial->EntrySet, Final->ExitSet,
+ intersectAndWarn(ExpectedExitSet, Final->ExitSet,
Final->ExitLoc,
LEK_LockedAtEndOfFunction,
LEK_NotLockedAtEndOfFunction,