diff options
author | Aaron Ballman <aaron@aaronballman.com> | 2018-08-10 17:33:47 +0000 |
---|---|---|
committer | Aaron Ballman <aaron@aaronballman.com> | 2018-08-10 17:33:47 +0000 |
commit | a97e4dc899d2e8acf7681b58826ab75076279783 (patch) | |
tree | a2e08692613bbad1ee535e9685418c38ca34c02c /clang/lib | |
parent | cd27070a0522f9553bba8a5afb830e224b00aff3 (diff) | |
download | bcm5719-llvm-a97e4dc899d2e8acf7681b58826ab75076279783.tar.gz bcm5719-llvm-a97e4dc899d2e8acf7681b58826ab75076279783.zip |
Allow relockable scopes with thread safety attributes.
Patch by Aaron Puchert
llvm-svn: 339456
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Analysis/ThreadSafety.cpp | 82 |
1 files changed, 68 insertions, 14 deletions
diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp index a8c17d5070d..98840011295 100644 --- a/clang/lib/Analysis/ThreadSafety.cpp +++ b/clang/lib/Analysis/ThreadSafety.cpp @@ -86,8 +86,8 @@ static void warnInvalidLock(ThreadSafetyHandler &Handler, namespace { -/// A set of CapabilityInfo objects, which are compiled from the -/// requires attributes on a function. +/// A set of CapabilityExpr objects, which are compiled from thread safety +/// attributes on a function. class CapExprSet : public SmallVector<CapabilityExpr, 4> { public: /// Push M onto list, but discard duplicates. @@ -944,6 +944,23 @@ public: if (FullyRemove) FSet.removeLock(FactMan, Cp); } + + void Relock(FactSet &FSet, FactManager &FactMan, LockKind LK, + SourceLocation RelockLoc, ThreadSafetyHandler &Handler, + StringRef DiagKind) const { + for (const auto *UnderlyingMutex : UnderlyingMutexes) { + CapabilityExpr UnderCp(UnderlyingMutex, false); + + // We're relocking the underlying mutexes. Warn on double locking. + if (FSet.findLock(FactMan, UnderCp)) + Handler.handleDoubleLock(DiagKind, UnderCp.toString(), RelockLoc); + else { + FSet.removeLock(FactMan, !UnderCp); + FSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(UnderCp, LK, + RelockLoc)); + } + } + } }; /// Class which implements the core thread safety analysis routines. @@ -974,6 +991,9 @@ public: void removeLock(FactSet &FSet, const CapabilityExpr &CapE, SourceLocation UnlockLoc, bool FullyRemove, LockKind Kind, StringRef DiagKind); + void relockScopedLock(FactSet &FSet, const CapabilityExpr &CapE, + SourceLocation RelockLoc, LockKind Kind, + StringRef DiagKind); template <typename AttrType> void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, Expr *Exp, @@ -1285,6 +1305,27 @@ void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const CapabilityExpr &Cp, DiagKind); } +void ThreadSafetyAnalyzer::relockScopedLock(FactSet &FSet, + const CapabilityExpr &Cp, + SourceLocation RelockLoc, + LockKind Kind, StringRef DiagKind) { + if (Cp.shouldIgnore()) + return; + + const FactEntry *LDat = FSet.findLock(FactMan, Cp); + if (!LDat) { + // FIXME: It's possible to manually destruct the scope and then relock it. + // Should that be a separate warning? For now we pretend nothing happened. + // It's undefined behavior, so not related to thread safety. + return; + } + + // We should only land here if Cp is a scoped capability, so if we have any + // fact, it must be a ScopedLockableFactEntry. + const auto *SLDat = static_cast<const ScopedLockableFactEntry *>(LDat); + SLDat->Relock(FSet, FactMan, Kind, RelockLoc, Handler, DiagKind); +} + /// Extract the list of mutexIDs from the attribute on an expression, /// and push them onto Mtxs, discarding any duplicates. template <typename AttrType> @@ -1726,14 +1767,19 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) { StringRef CapDiagKind = "mutex"; // Figure out if we're constructing an object of scoped lockable class - bool isScopedVar = false; + bool isScopedConstructor = false, isScopedMemberCall = false; if (VD) { if (const auto *CD = dyn_cast<const CXXConstructorDecl>(D)) { const CXXRecordDecl* PD = CD->getParent(); if (PD && PD->hasAttr<ScopedLockableAttr>()) - isScopedVar = true; + isScopedConstructor = true; } } + if (const auto *MCD = dyn_cast<const CXXMemberCallExpr>(Exp)) { + const CXXRecordDecl *PD = MCD->getRecordDecl(); + if (PD && PD->hasAttr<ScopedLockableAttr>()) + isScopedMemberCall = true; + } for(const Attr *At : D->attrs()) { switch (At->getKind()) { @@ -1813,7 +1859,7 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) { POK_FunctionCall, ClassifyDiagnostic(A), Exp->getExprLoc()); // use for adopting a lock - if (isScopedVar) { + if (isScopedConstructor) { Analyzer->getMutexIDs(A->isShared() ? ScopedSharedReqs : ScopedExclusiveReqs, A, Exp, D, VD); @@ -1846,16 +1892,24 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) { Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Generic, CapDiagKind); // Add locks. - for (const auto &M : ExclusiveLocksToAdd) - Analyzer->addLock(FSet, llvm::make_unique<LockableFactEntry>( - M, LK_Exclusive, Loc, isScopedVar), - CapDiagKind); - for (const auto &M : SharedLocksToAdd) - Analyzer->addLock(FSet, llvm::make_unique<LockableFactEntry>( - M, LK_Shared, Loc, isScopedVar), - CapDiagKind); + if (isScopedMemberCall) { + // If the call is on a scoped capability, we need to relock instead. + for (const auto &M : ExclusiveLocksToAdd) + Analyzer->relockScopedLock(FSet, M, Loc, LK_Exclusive, CapDiagKind); + for (const auto &M : SharedLocksToAdd) + Analyzer->relockScopedLock(FSet, M, Loc, LK_Shared, CapDiagKind); + } else { + for (const auto &M : ExclusiveLocksToAdd) + Analyzer->addLock(FSet, llvm::make_unique<LockableFactEntry>( + M, LK_Exclusive, Loc, isScopedConstructor), + CapDiagKind); + for (const auto &M : SharedLocksToAdd) + Analyzer->addLock(FSet, llvm::make_unique<LockableFactEntry>( + M, LK_Shared, Loc, isScopedConstructor), + CapDiagKind); + } - if (isScopedVar) { + if (isScopedConstructor) { // Add the managing object as a dummy mutex, mapped to the underlying mutex. SourceLocation MLoc = VD->getLocation(); DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation()); |