39#include "llvm/ADT/DenseMap.h"
40#include "llvm/ADT/ImmutableMap.h"
41#include "llvm/ADT/STLExtras.h"
42#include "llvm/ADT/ScopeExit.h"
43#include "llvm/ADT/SmallVector.h"
44#include "llvm/ADT/StringRef.h"
45#include "llvm/Support/Allocator.h"
46#include "llvm/Support/Casting.h"
47#include "llvm/Support/ErrorHandling.h"
48#include "llvm/Support/TrailingObjects.h"
49#include "llvm/Support/raw_ostream.h"
74 QualType T = Param->getType().getNonReferenceType();
75 return T->isFunctionPointerType() ||
T->isFunctionType();
81 const Expr *DeclExp, StringRef Kind) {
95class CapExprSet :
public SmallVector<CapabilityExpr, 4> {
98 void push_back_nodup(
const CapabilityExpr &CapE) {
99 if (llvm::none_of(*
this, [=](
const CapabilityExpr &CapE2) {
100 return CapE.
equals(CapE2);
115 enum FactEntryKind { Lockable, ScopedLockable };
126 const FactEntryKind Kind : 8;
132 SourceKind Source : 8;
135 SourceLocation AcquireLoc;
138 ~FactEntry() =
default;
141 FactEntry(FactEntryKind FK,
const CapabilityExpr &CE,
LockKind LK,
142 SourceLocation Loc, SourceKind Src)
143 : CapabilityExpr(CE), Kind(FK), LKind(LK), Source(Src), AcquireLoc(Loc) {}
146 SourceLocation loc()
const {
return AcquireLoc; }
147 FactEntryKind getFactEntryKind()
const {
return Kind; }
149 bool asserted()
const {
return Source == Asserted; }
150 bool declared()
const {
return Source == Declared; }
151 bool managed()
const {
return Source == Managed; }
154 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
156 ThreadSafetyHandler &Handler)
const = 0;
157 virtual void handleLock(FactSet &FSet, FactManager &FactMan,
158 const FactEntry &entry,
159 ThreadSafetyHandler &Handler)
const = 0;
160 virtual void handleUnlock(FactSet &FSet, FactManager &FactMan,
161 const CapabilityExpr &Cp, SourceLocation UnlockLoc,
163 ThreadSafetyHandler &Handler)
const = 0;
171using FactID =
unsigned short;
177 llvm::BumpPtrAllocator &Alloc;
178 std::vector<const FactEntry *> Facts;
181 FactManager(llvm::BumpPtrAllocator &Alloc) : Alloc(Alloc) {}
183 template <
typename T,
typename... ArgTypes>
184 T *createFact(ArgTypes &&...Args) {
185 static_assert(std::is_trivially_destructible_v<T>);
186 return T::create(Alloc, std::forward<ArgTypes>(Args)...);
189 FactID newFact(
const FactEntry *Entry) {
190 Facts.push_back(Entry);
191 assert(Facts.size() - 1 <= std::numeric_limits<FactID>::max() &&
192 "FactID space exhausted");
193 return static_cast<unsigned short>(Facts.size() - 1);
196 const FactEntry &operator[](FactID F)
const {
return *Facts[F]; }
208 using FactVec = SmallVector<FactID, 4>;
213 using iterator = FactVec::iterator;
214 using const_iterator = FactVec::const_iterator;
216 iterator begin() {
return FactIDs.begin(); }
217 const_iterator begin()
const {
return FactIDs.begin(); }
219 iterator end() {
return FactIDs.end(); }
220 const_iterator end()
const {
return FactIDs.end(); }
222 bool isEmpty()
const {
return FactIDs.size() == 0; }
225 bool isEmpty(FactManager &FactMan)
const {
226 for (
const auto FID : *
this) {
227 if (!FactMan[FID].negative())
233 void addLockByID(FactID ID) { FactIDs.push_back(ID); }
235 FactID addLock(FactManager &FM,
const FactEntry *Entry) {
236 FactID F = FM.newFact(Entry);
237 FactIDs.push_back(F);
241 bool removeLock(FactManager& FM,
const CapabilityExpr &CapE) {
242 unsigned n = FactIDs.size();
246 for (
unsigned i = 0; i < n-1; ++i) {
247 if (FM[FactIDs[i]].
matches(CapE)) {
248 FactIDs[i] = FactIDs[n-1];
253 if (FM[FactIDs[n-1]].
matches(CapE)) {
260 std::optional<FactID> replaceLock(FactManager &FM, iterator It,
261 const FactEntry *Entry) {
264 FactID F = FM.newFact(Entry);
269 std::optional<FactID> replaceLock(FactManager &FM,
const CapabilityExpr &CapE,
270 const FactEntry *Entry) {
271 return replaceLock(FM, findLockIter(FM, CapE), Entry);
274 iterator findLockIter(FactManager &FM,
const CapabilityExpr &CapE) {
275 return llvm::find_if(*
this,
276 [&](FactID ID) {
return FM[
ID].matches(CapE); });
279 const FactEntry *findLock(FactManager &FM,
const CapabilityExpr &CapE)
const {
281 llvm::find_if(*
this, [&](FactID ID) {
return FM[
ID].matches(CapE); });
282 return I != end() ? &FM[*I] :
nullptr;
285 const FactEntry *findLockUniv(FactManager &FM,
286 const CapabilityExpr &CapE)
const {
287 auto I = llvm::find_if(
288 *
this, [&](FactID ID) ->
bool {
return FM[
ID].matchesUniv(CapE); });
289 return I != end() ? &FM[*I] :
nullptr;
292 const FactEntry *findPartialMatch(FactManager &FM,
293 const CapabilityExpr &CapE)
const {
294 auto I = llvm::find_if(*
this, [&](FactID ID) ->
bool {
295 return FM[
ID].partiallyMatches(CapE);
297 return I != end() ? &FM[*I] :
nullptr;
300 bool containsMutexDecl(FactManager &FM,
const ValueDecl* Vd)
const {
301 auto I = llvm::find_if(
302 *
this, [&](FactID ID) ->
bool {
return FM[
ID].valueDecl() == Vd; });
307class ThreadSafetyAnalyzer;
322 BeforeInfo() =
default;
323 BeforeInfo(BeforeInfo &&) =
default;
327 llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>;
328 using CycleMap = llvm::DenseMap<const ValueDecl *, bool>;
334 ThreadSafetyAnalyzer& Analyzer);
337 ThreadSafetyAnalyzer &Analyzer);
341 ThreadSafetyAnalyzer& Analyzer,
354class LocalVariableMap;
356using LocalVarContext = llvm::ImmutableMap<const NamedDecl *, unsigned>;
359enum CFGBlockSide { CBS_Entry, CBS_Exit };
372 LocalVarContext EntryContext;
375 LocalVarContext ExitContext;
378 SourceLocation EntryLoc;
381 SourceLocation ExitLoc;
387 bool Reachable =
false;
389 const FactSet &getSet(CFGBlockSide Side)
const {
390 return Side == CBS_Entry ? EntrySet : ExitSet;
393 SourceLocation getLocation(CFGBlockSide Side)
const {
394 return Side == CBS_Entry ? EntryLoc : ExitLoc;
398 CFGBlockInfo(LocalVarContext EmptyCtx)
399 : EntryContext(EmptyCtx), ExitContext(EmptyCtx) {}
402 static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
418class LocalVariableMap {
420 using Context = LocalVarContext;
426 struct VarDefinition {
428 friend class LocalVariableMap;
431 const NamedDecl *Dec;
434 const Expr *Exp =
nullptr;
437 unsigned DirectRef = 0;
440 unsigned CanonicalRef = 0;
445 bool isReference()
const {
return !Exp; }
447 void invalidateRef() { DirectRef = CanonicalRef = 0; }
451 VarDefinition(
const NamedDecl *D,
const Expr *E, Context
C)
452 : Dec(D), Exp(E), Ctx(
C) {}
455 VarDefinition(
const NamedDecl *D,
unsigned DirectRef,
unsigned CanonicalRef,
457 : Dec(D), DirectRef(DirectRef), CanonicalRef(CanonicalRef), Ctx(
C) {}
461 Context::Factory ContextFactory;
462 std::vector<VarDefinition> VarDefinitions;
463 std::vector<std::pair<const Stmt *, Context>> SavedContexts;
468 VarDefinitions.push_back(VarDefinition(
nullptr, 0, 0, getEmptyContext()));
472 const VarDefinition* lookup(
const NamedDecl *D, Context Ctx) {
473 const unsigned *i = Ctx.lookup(D);
476 assert(*i < VarDefinitions.size());
477 return &VarDefinitions[*i];
483 const Expr* lookupExpr(
const NamedDecl *D, Context &Ctx) {
484 const unsigned *P = Ctx.lookup(D);
490 if (VarDefinitions[i].Exp) {
491 Ctx = VarDefinitions[i].Ctx;
492 return VarDefinitions[i].Exp;
494 i = VarDefinitions[i].DirectRef;
499 Context getEmptyContext() {
return ContextFactory.getEmptyMap(); }
504 const Context &getNextContext(
unsigned &CtxIndex,
const Stmt *S,
506 if (SavedContexts[CtxIndex + 1].first == S) {
508 const Context &
Result = SavedContexts[CtxIndex].second;
514 void dumpVarDefinitionName(
unsigned i) {
516 llvm::errs() <<
"Undefined";
519 const NamedDecl *
Dec = VarDefinitions[i].Dec;
521 llvm::errs() <<
"<<NULL>>";
524 Dec->printName(llvm::errs());
525 llvm::errs() <<
"." << i <<
" " << ((
const void*) Dec);
530 for (
unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {
531 const Expr *Exp = VarDefinitions[i].Exp;
532 unsigned Ref = VarDefinitions[i].DirectRef;
534 dumpVarDefinitionName(i);
535 llvm::errs() <<
" = ";
536 if (Exp) Exp->
dump();
538 dumpVarDefinitionName(Ref);
539 llvm::errs() <<
"\n";
545 void dumpContext(Context
C) {
546 for (Context::iterator I =
C.begin(), E =
C.end(); I != E; ++I) {
547 const NamedDecl *D = I.getKey();
549 llvm::errs() <<
" -> ";
550 dumpVarDefinitionName(I.getData());
551 llvm::errs() <<
"\n";
556 void traverseCFG(CFG *CFGraph,
const PostOrderCFGView *SortedGraph,
557 std::vector<CFGBlockInfo> &BlockInfo);
560 friend class VarMapBuilder;
563 unsigned getCanonicalDefinitionID(
unsigned ID)
const {
564 while (ID > 0 && VarDefinitions[ID].isReference())
565 ID = VarDefinitions[
ID].CanonicalRef;
570 unsigned getContextIndex() {
return SavedContexts.size()-1; }
573 void saveContext(
const Stmt *S, Context
C) {
574 SavedContexts.push_back(std::make_pair(S,
C));
579 Context addDefinition(
const NamedDecl *D,
const Expr *Exp, Context Ctx) {
580 assert(!Ctx.contains(D));
581 unsigned newID = VarDefinitions.size();
582 Context NewCtx = ContextFactory.add(Ctx, D, newID);
583 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
588 Context addReference(
const NamedDecl *D,
unsigned Ref, Context Ctx) {
589 unsigned newID = VarDefinitions.size();
590 Context NewCtx = ContextFactory.add(Ctx, D, newID);
591 VarDefinitions.push_back(
592 VarDefinition(D, Ref, getCanonicalDefinitionID(Ref), Ctx));
598 Context updateDefinition(
const NamedDecl *D, Expr *Exp, Context Ctx) {
599 if (Ctx.contains(D)) {
600 unsigned newID = VarDefinitions.size();
601 Context NewCtx = ContextFactory.remove(Ctx, D);
602 NewCtx = ContextFactory.add(NewCtx, D, newID);
603 VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
611 Context clearDefinition(
const NamedDecl *D, Context Ctx) {
612 Context NewCtx = Ctx;
613 if (NewCtx.contains(D)) {
614 NewCtx = ContextFactory.remove(NewCtx, D);
615 NewCtx = ContextFactory.add(NewCtx, D, 0);
621 Context removeDefinition(
const NamedDecl *D, Context Ctx) {
622 Context NewCtx = Ctx;
623 if (NewCtx.contains(D)) {
624 NewCtx = ContextFactory.remove(NewCtx, D);
629 Context intersectContexts(Context C1, Context C2);
630 Context createReferenceContext(Context
C);
631 void intersectBackEdge(Context C1, Context C2);
637CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
638 return CFGBlockInfo(M.getEmptyContext());
644class VarMapBuilder :
public ConstStmtVisitor<VarMapBuilder> {
646 LocalVariableMap* VMap;
647 LocalVariableMap::Context Ctx;
649 VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context
C)
650 : VMap(VM), Ctx(
C) {}
652 void VisitDeclStmt(
const DeclStmt *S);
653 void VisitBinaryOperator(
const BinaryOperator *BO);
654 void VisitCallExpr(
const CallExpr *CE);
660void VarMapBuilder::VisitDeclStmt(
const DeclStmt *S) {
661 bool modifiedCtx =
false;
663 for (
const auto *D : DGrp) {
664 if (
const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
665 const Expr *E = VD->getInit();
668 QualType
T = VD->getType();
669 if (
T.isTrivialType(VD->getASTContext())) {
670 Ctx = VMap->addDefinition(VD, E, Ctx);
676 VMap->saveContext(S, Ctx);
680void VarMapBuilder::VisitBinaryOperator(
const BinaryOperator *BO) {
687 if (
const auto *DRE = dyn_cast<DeclRefExpr>(LHSExp)) {
688 const ValueDecl *VDec = DRE->getDecl();
689 if (Ctx.lookup(VDec)) {
691 Ctx = VMap->updateDefinition(VDec, BO->
getRHS(), Ctx);
694 Ctx = VMap->clearDefinition(VDec, Ctx);
695 VMap->saveContext(BO, Ctx);
701void VarMapBuilder::VisitCallExpr(
const CallExpr *CE) {
711 if (II->isStr(
"bind") || II->isStr(
"bind_front"))
717 for (
unsigned Idx = 0; Idx < CE->
getNumArgs(); ++Idx) {
723 QualType ParamType = PVD->
getType();
726 const ValueDecl *VDec =
nullptr;
729 if (
const auto *DRE = dyn_cast<DeclRefExpr>(Arg))
730 VDec = DRE->getDecl();
734 if (
const auto *UO = dyn_cast<UnaryOperator>(Arg)) {
735 if (UO->getOpcode() == UO_AddrOf) {
736 const Expr *SubE = UO->getSubExpr()->IgnoreParenCasts();
737 if (
const auto *DRE = dyn_cast<DeclRefExpr>(SubE))
738 VDec = DRE->getDecl();
744 Ctx = VMap->clearDefinition(VDec, Ctx);
748 VMap->saveContext(CE, Ctx);
754LocalVariableMap::Context
755LocalVariableMap::intersectContexts(Context C1, Context C2) {
757 for (
const auto &P : C1) {
758 const NamedDecl *
Dec = P.first;
759 const unsigned *I2 = C2.lookup(Dec);
763 }
else if (getCanonicalDefinitionID(P.second) !=
764 getCanonicalDefinitionID(*I2)) {
776LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context
C) {
777 Context
Result = getEmptyContext();
778 for (
const auto &P :
C)
786void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
787 for (
const auto &P : C1) {
788 const unsigned I1 = P.second;
789 VarDefinition *VDef = &VarDefinitions[I1];
790 assert(VDef->isReference());
792 const unsigned *I2 = C2.lookup(P.first);
795 VDef->invalidateRef();
801 if (VDef->CanonicalRef != getCanonicalDefinitionID(*I2))
802 VDef->invalidateRef();
843void LocalVariableMap::traverseCFG(CFG *CFGraph,
844 const PostOrderCFGView *SortedGraph,
845 std::vector<CFGBlockInfo> &BlockInfo) {
846 PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph);
848 for (
const auto *CurrBlock : *SortedGraph) {
849 unsigned CurrBlockID = CurrBlock->getBlockID();
850 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
852 VisitedBlocks.insert(CurrBlock);
855 bool HasBackEdges =
false;
858 PE = CurrBlock->pred_end(); PI != PE; ++PI) {
860 if (*PI ==
nullptr || !VisitedBlocks.alreadySet(*PI)) {
865 unsigned PrevBlockID = (*PI)->getBlockID();
866 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
869 CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;
873 CurrBlockInfo->EntryContext =
874 intersectContexts(CurrBlockInfo->EntryContext,
875 PrevBlockInfo->ExitContext);
882 CurrBlockInfo->EntryContext =
883 createReferenceContext(CurrBlockInfo->EntryContext);
886 saveContext(
nullptr, CurrBlockInfo->EntryContext);
887 CurrBlockInfo->EntryIndex = getContextIndex();
890 VarMapBuilder VMapBuilder(
this, CurrBlockInfo->EntryContext);
891 for (
const auto &BI : *CurrBlock) {
892 switch (BI.getKind()) {
894 CFGStmt CS = BI.castAs<CFGStmt>();
895 VMapBuilder.Visit(CS.
getStmt());
902 CurrBlockInfo->ExitContext = VMapBuilder.Ctx;
906 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
908 if (*SI ==
nullptr || !VisitedBlocks.alreadySet(*SI))
911 CFGBlock *FirstLoopBlock = *SI;
912 Context LoopBegin = BlockInfo[FirstLoopBlock->
getBlockID()].EntryContext;
913 Context LoopEnd = CurrBlockInfo->ExitContext;
914 intersectBackEdge(LoopBegin, LoopEnd);
920 saveContext(
nullptr, BlockInfo[exitID].ExitContext);
927 std::vector<CFGBlockInfo> &BlockInfo) {
928 for (
const auto *CurrBlock : *SortedGraph) {
929 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];
933 if (
const Stmt *S = CurrBlock->getTerminatorStmt()) {
934 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->
getBeginLoc();
937 BE = CurrBlock->rend(); BI != BE; ++BI) {
939 if (std::optional<CFGStmt> CS = BI->getAs<
CFGStmt>()) {
940 CurrBlockInfo->ExitLoc = CS->getStmt()->getBeginLoc();
946 if (CurrBlockInfo->ExitLoc.
isValid()) {
949 for (
const auto &BI : *CurrBlock) {
951 if (std::optional<CFGStmt> CS = BI.getAs<
CFGStmt>()) {
952 CurrBlockInfo->EntryLoc = CS->getStmt()->getBeginLoc();
956 }
else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&
957 CurrBlock != &CFGraph->
getExit()) {
960 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
961 BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;
962 }
else if (CurrBlock->succ_size() == 1 && *CurrBlock->succ_begin()) {
965 CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
966 BlockInfo[(*CurrBlock->succ_begin())->getBlockID()].EntryLoc;
973class LockableFactEntry final :
public FactEntry {
978 unsigned int ReentrancyDepth = 0;
980 LockableFactEntry(
const CapabilityExpr &CE,
LockKind LK, SourceLocation Loc,
982 : FactEntry(Lockable, CE, LK, Loc, Src) {}
985 static LockableFactEntry *
create(llvm::BumpPtrAllocator &Alloc,
986 const LockableFactEntry &
Other) {
990 static LockableFactEntry *
create(llvm::BumpPtrAllocator &Alloc,
991 const CapabilityExpr &CE,
LockKind LK,
993 SourceKind Src = Acquired) {
994 return new (
Alloc) LockableFactEntry(CE, LK, Loc, Src);
997 unsigned int getReentrancyDepth()
const {
return ReentrancyDepth; }
1000 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
1002 ThreadSafetyHandler &Handler)
const override {
1003 if (!asserted() && !negative() && !isUniversal()) {
1009 void handleLock(FactSet &FSet, FactManager &FactMan,
const FactEntry &entry,
1010 ThreadSafetyHandler &Handler)
const override {
1011 if (
const FactEntry *RFact = tryReenter(FactMan, entry.kind())) {
1013 FSet.replaceLock(FactMan, entry, RFact);
1020 void handleUnlock(FactSet &FSet, FactManager &FactMan,
1021 const CapabilityExpr &Cp, SourceLocation UnlockLoc,
1023 ThreadSafetyHandler &Handler)
const override {
1024 FSet.removeLock(FactMan, Cp);
1026 if (
const FactEntry *RFact = leaveReentrant(FactMan)) {
1028 FSet.addLock(FactMan, RFact);
1030 FSet.addLock(FactMan, FactMan.createFact<LockableFactEntry>(
1037 const FactEntry *tryReenter(FactManager &FactMan,
1041 if (
kind() != ReenterKind)
1043 auto *NewFact = FactMan.createFact<LockableFactEntry>(*this);
1044 NewFact->ReentrancyDepth++;
1050 const FactEntry *leaveReentrant(FactManager &FactMan)
const {
1051 if (!ReentrancyDepth)
1053 assert(reentrant());
1054 auto *NewFact = FactMan.createFact<LockableFactEntry>(*this);
1055 NewFact->ReentrancyDepth--;
1059 static bool classof(
const FactEntry *A) {
1060 return A->getFactEntryKind() == Lockable;
1064enum UnderlyingCapabilityKind {
1067 UCK_ReleasedExclusive,
1070struct UnderlyingCapability {
1072 UnderlyingCapabilityKind Kind;
1075class ScopedLockableFactEntry final
1077 private llvm::TrailingObjects<ScopedLockableFactEntry,
1078 UnderlyingCapability> {
1079 friend TrailingObjects;
1082 const unsigned ManagedCapacity;
1083 unsigned ManagedSize = 0;
1085 ScopedLockableFactEntry(
const CapabilityExpr &CE, SourceLocation Loc,
1086 SourceKind Src,
unsigned ManagedCapacity)
1087 : FactEntry(ScopedLockable, CE,
LK_Exclusive, Loc, Src),
1088 ManagedCapacity(ManagedCapacity) {}
1090 void addManaged(
const CapabilityExpr &M, UnderlyingCapabilityKind UCK) {
1091 assert(ManagedSize < ManagedCapacity);
1092 new (getTrailingObjects() + ManagedSize) UnderlyingCapability{M, UCK};
1096 ArrayRef<UnderlyingCapability> getManaged()
const {
1097 return getTrailingObjects(ManagedSize);
1101 static ScopedLockableFactEntry *
create(llvm::BumpPtrAllocator &Alloc,
1102 const CapabilityExpr &CE,
1103 SourceLocation Loc, SourceKind Src,
1104 unsigned ManagedCapacity) {
1106 Alloc.Allocate(totalSizeToAlloc<UnderlyingCapability>(ManagedCapacity),
1107 alignof(ScopedLockableFactEntry));
1108 return new (
Storage) ScopedLockableFactEntry(CE, Loc, Src, ManagedCapacity);
1111 CapExprSet getUnderlyingMutexes()
const {
1112 CapExprSet UnderlyingMutexesSet;
1113 for (
const UnderlyingCapability &UnderlyingMutex : getManaged())
1114 UnderlyingMutexesSet.push_back(UnderlyingMutex.Cap);
1115 return UnderlyingMutexesSet;
1122 void addLock(
const CapabilityExpr &M) { addManaged(M, UCK_Acquired); }
1124 void addExclusiveUnlock(
const CapabilityExpr &M) {
1125 addManaged(M, UCK_ReleasedExclusive);
1128 void addSharedUnlock(
const CapabilityExpr &M) {
1129 addManaged(M, UCK_ReleasedShared);
1134 handleRemovalFromIntersection(
const FactSet &FSet, FactManager &FactMan,
1136 ThreadSafetyHandler &Handler)
const override {
1140 for (
const auto &UnderlyingMutex : getManaged()) {
1141 const auto *Entry = FSet.findLock(FactMan, UnderlyingMutex.Cap);
1142 if ((UnderlyingMutex.Kind == UCK_Acquired && Entry) ||
1143 (UnderlyingMutex.Kind != UCK_Acquired && !Entry)) {
1147 UnderlyingMutex.Cap.toString(), loc(),
1153 void handleLock(FactSet &FSet, FactManager &FactMan,
const FactEntry &entry,
1154 ThreadSafetyHandler &Handler)
const override {
1155 for (
const auto &UnderlyingMutex : getManaged()) {
1156 if (UnderlyingMutex.Kind == UCK_Acquired)
1157 lock(FSet, FactMan, UnderlyingMutex.Cap, entry.kind(), entry.loc(),
1160 unlock(FSet, FactMan, UnderlyingMutex.Cap, entry.loc(), &Handler);
1164 void handleUnlock(FactSet &FSet, FactManager &FactMan,
1165 const CapabilityExpr &Cp, SourceLocation UnlockLoc,
1167 ThreadSafetyHandler &Handler)
const override {
1168 assert(!Cp.
negative() &&
"Managing object cannot be negative.");
1169 for (
const auto &UnderlyingMutex : getManaged()) {
1172 ThreadSafetyHandler *TSHandler = FullyRemove ?
nullptr : &Handler;
1173 if (UnderlyingMutex.Kind == UCK_Acquired) {
1174 unlock(FSet, FactMan, UnderlyingMutex.Cap, UnlockLoc, TSHandler);
1176 LockKind kind = UnderlyingMutex.Kind == UCK_ReleasedShared
1179 lock(FSet, FactMan, UnderlyingMutex.Cap,
kind, UnlockLoc, TSHandler);
1183 FSet.removeLock(FactMan, Cp);
1186 static bool classof(
const FactEntry *A) {
1187 return A->getFactEntryKind() == ScopedLockable;
1191 void lock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp,
1193 ThreadSafetyHandler *Handler)
const {
1194 if (
const auto It = FSet.findLockIter(FactMan, Cp); It != FSet.end()) {
1196 if (
const FactEntry *RFact = Fact.tryReenter(FactMan,
kind)) {
1198 FSet.replaceLock(FactMan, It, RFact);
1199 }
else if (Handler) {
1203 FSet.removeLock(FactMan, !Cp);
1204 FSet.addLock(FactMan, FactMan.createFact<LockableFactEntry>(Cp,
kind, loc,
1209 void unlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp,
1210 SourceLocation loc, ThreadSafetyHandler *Handler)
const {
1211 if (
const auto It = FSet.findLockIter(FactMan, Cp); It != FSet.end()) {
1213 if (
const FactEntry *RFact = Fact.leaveReentrant(FactMan)) {
1215 FSet.replaceLock(FactMan, It, RFact);
1221 FactMan.createFact<LockableFactEntry>(!Cp,
LK_Exclusive, loc));
1222 }
else if (Handler) {
1223 SourceLocation PrevLoc;
1224 if (
const FactEntry *Neg = FSet.findLock(FactMan, !Cp))
1225 PrevLoc =
Neg->loc();
1232class ThreadSafetyAnalyzer {
1233 friend class BuildLockset;
1234 friend class threadSafety::BeforeSet;
1236 llvm::BumpPtrAllocator Bpa;
1237 threadSafety::til::MemRegionRef Arena;
1238 threadSafety::SExprBuilder SxBuilder;
1240 ThreadSafetyHandler &Handler;
1241 const FunctionDecl *CurrentFunction;
1242 LocalVariableMap LocalVarMap;
1244 llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;
1245 FactManager FactMan;
1246 std::vector<CFGBlockInfo> BlockInfo;
1248 BeforeSet *GlobalBeforeSet;
1251 ThreadSafetyAnalyzer(ThreadSafetyHandler &H, BeforeSet *Bset)
1252 : Arena(&Bpa), SxBuilder(Arena), Handler(H), FactMan(Bpa),
1253 GlobalBeforeSet(Bset) {}
1255 bool inCurrentScope(
const CapabilityExpr &CapE);
1257 void addLock(FactSet &FSet,
const FactEntry *Entry,
bool ReqAttr =
false);
1258 void removeLock(FactSet &FSet,
const CapabilityExpr &CapE,
1259 SourceLocation UnlockLoc,
bool FullyRemove,
LockKind Kind);
1261 template <
typename AttrType>
1262 void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
const Expr *Exp,
1263 const NamedDecl *D, til::SExpr *
Self =
nullptr);
1265 template <
class AttrType>
1266 void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
const Expr *Exp,
1268 const CFGBlock *PredBlock,
const CFGBlock *CurrBlock,
1269 Expr *BrE,
bool Neg);
1271 const CallExpr* getTrylockCallExpr(
const Stmt *
Cond, LocalVarContext
C,
1274 using TerminatorTrylockCall =
1275 std::tuple<
const CallExpr *,
const NamedDecl *,
1278 TerminatorTrylockCall getTerminatorTrylockCall(
const CFGBlock *
Block,
1281 void getEdgeLockset(FactSet &
Result,
const FactSet &ExitSet,
1282 const CFGBlock* PredBlock,
1283 const CFGBlock *CurrBlock);
1285 void getTerminatorTrylockCaps(
const CFGBlock *
Block, CapExprSet &Caps);
1287 bool join(
const FactEntry &A,
const FactEntry &B, SourceLocation JoinLoc,
1290 void intersectAndWarn(FactSet &EntrySet,
const FactSet &ExitSet,
1293 const CapExprSet *TrylockRebranchCaps =
nullptr);
1295 void intersectAndWarn(FactSet &EntrySet,
const FactSet &ExitSet,
1297 intersectAndWarn(EntrySet, ExitSet, JoinLoc, LEK, LEK);
1300 void runAnalysis(AnalysisDeclContext &AC);
1302 void warnIfMutexNotHeld(
const FactSet &FSet,
const NamedDecl *D,
1303 const Expr *Exp,
AccessKind AK, Expr *MutexExp,
1305 SourceLocation Loc);
1306 void warnIfAnyMutexNotHeldForRead(
const FactSet &FSet,
const NamedDecl *D,
1308 llvm::ArrayRef<Expr *> Args,
1310 SourceLocation Loc);
1311 void warnIfMutexHeld(
const FactSet &FSet,
const NamedDecl *D,
const Expr *Exp,
1312 Expr *MutexExp, til::SExpr *
Self, SourceLocation Loc);
1314 void checkAccess(
const FactSet &FSet,
const Expr *Exp,
AccessKind AK,
1316 void checkPtAccess(
const FactSet &FSet,
const Expr *Exp,
AccessKind AK,
1324 ThreadSafetyAnalyzer& Analyzer) {
1326 BeforeInfo *Info =
nullptr;
1330 std::unique_ptr<BeforeInfo> &InfoPtr = BMap[Vd];
1332 InfoPtr.reset(
new BeforeInfo());
1333 Info = InfoPtr.get();
1336 for (
const auto *At : Vd->
attrs()) {
1337 switch (At->getKind()) {
1338 case attr::AcquiredBefore: {
1342 for (
const auto *Arg : A->args()) {
1344 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1346 Info->Vect.push_back(Cpvd);
1347 const auto It = BMap.find(Cpvd);
1348 if (It == BMap.end())
1354 case attr::AcquiredAfter: {
1358 for (
const auto *Arg : A->args()) {
1360 Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1364 ArgInfo->Vect.push_back(Vd);
1377BeforeSet::BeforeInfo *
1379 ThreadSafetyAnalyzer &Analyzer) {
1380 auto It = BMap.find(Vd);
1381 BeforeInfo *Info =
nullptr;
1382 if (It == BMap.end())
1385 Info = It->second.get();
1386 assert(Info &&
"BMap contained nullptr?");
1392 const FactSet& FSet,
1393 ThreadSafetyAnalyzer& Analyzer,
1405 if (Info->Visited == 1)
1408 if (Info->Visited == 2)
1411 if (Info->Vect.empty())
1414 InfoVect.push_back(Info);
1416 for (
const auto *Vdb : Info->Vect) {
1418 if (FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) {
1419 StringRef L1 = StartVd->
getName();
1420 StringRef L2 = Vdb->getName();
1421 Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2, Loc);
1425 if (CycMap.try_emplace(Vd,
true).second) {
1427 Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->
getLocation());
1437 for (
auto *Info : InfoVect)
1443 if (
const auto *CE = dyn_cast<ImplicitCastExpr>(Exp))
1446 if (
const auto *DR = dyn_cast<DeclRefExpr>(Exp))
1447 return DR->getDecl();
1449 if (
const auto *ME = dyn_cast<MemberExpr>(Exp))
1450 return ME->getMemberDecl();
1455bool ThreadSafetyAnalyzer::inCurrentScope(
const CapabilityExpr &CapE) {
1456 const threadSafety::til::SExpr *SExp = CapE.
sexpr();
1457 assert(SExp &&
"Null expressions should be ignored");
1459 if (
const auto *LP = dyn_cast<til::LiteralPtr>(SExp)) {
1460 const ValueDecl *VD = LP->clangDecl();
1472 if (
const auto *P = dyn_cast<til::Project>(SExp)) {
1473 if (!isa_and_nonnull<CXXMethodDecl>(CurrentFunction))
1475 const ValueDecl *VD = P->clangDecl();
1484void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
const FactEntry *Entry,
1486 if (Entry->shouldIgnore())
1489 if (!ReqAttr && !Entry->negative()) {
1491 CapabilityExpr NegC = !*Entry;
1492 const FactEntry *Nen = FSet.findLock(FactMan, NegC);
1494 FSet.removeLock(FactMan, NegC);
1497 if (inCurrentScope(*Entry) && !Entry->asserted() && !Entry->reentrant())
1504 if (!Entry->asserted() && !Entry->declared()) {
1506 Entry->loc(), Entry->getKind());
1509 if (
const FactEntry *Cp = FSet.findLock(FactMan, *Entry)) {
1510 if (!Entry->asserted())
1511 Cp->handleLock(FSet, FactMan, *Entry, Handler);
1513 FSet.addLock(FactMan, Entry);
1519void ThreadSafetyAnalyzer::removeLock(FactSet &FSet,
const CapabilityExpr &Cp,
1520 SourceLocation UnlockLoc,
1521 bool FullyRemove,
LockKind ReceivedKind) {
1525 const FactEntry *LDat = FSet.findLock(FactMan, Cp);
1527 SourceLocation PrevLoc;
1528 if (
const FactEntry *Neg = FSet.findLock(FactMan, !Cp))
1529 PrevLoc =
Neg->loc();
1537 if (ReceivedKind !=
LK_Generic && LDat->kind() != ReceivedKind) {
1539 ReceivedKind, LDat->loc(), UnlockLoc);
1542 LDat->handleUnlock(FSet, FactMan, Cp, UnlockLoc, FullyRemove, Handler);
1547template <
typename AttrType>
1548void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
1549 const Expr *Exp,
const NamedDecl *D,
1551 if (Attr->args_size() == 0) {
1560 Mtxs.push_back_nodup(Cp);
1564 for (
const auto *Arg : Attr->args()) {
1572 Mtxs.push_back_nodup(Cp);
1579template <
class AttrType>
1580void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
1581 const Expr *Exp,
const NamedDecl *D,
1582 const CFGBlock *PredBlock,
1583 const CFGBlock *CurrBlock,
1584 Expr *BrE,
bool Neg) {
1586 bool branch =
false;
1587 if (
const auto *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))
1588 branch = BLE->getValue();
1589 else if (
const auto *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
1590 branch = ILE->getValue().getBoolValue();
1592 int branchnum = branch ? 0 : 1;
1594 branchnum = !branchnum;
1599 SE = PredBlock->
succ_end(); SI != SE && i < 2; ++SI, ++i) {
1600 if (*SI == CurrBlock && i == branchnum)
1601 getMutexIDs(Mtxs, Attr, Exp, D);
1609 }
else if (
const auto *BLE = dyn_cast<CXXBoolLiteralExpr>(E)) {
1610 TCond = BLE->getValue();
1612 }
else if (
const auto *ILE = dyn_cast<IntegerLiteral>(E)) {
1613 TCond = ILE->getValue().getBoolValue();
1615 }
else if (
auto *CE = dyn_cast<ImplicitCastExpr>(E))
1623const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(
const Stmt *
Cond,
1629 if (
const auto *CallExp = dyn_cast<CallExpr>(
Cond)) {
1630 if (CallExp->getBuiltinCallee() == Builtin::BI__builtin_expect)
1631 return getTrylockCallExpr(CallExp->getArg(0),
C, Negate);
1634 else if (
const auto *PE = dyn_cast<ParenExpr>(
Cond))
1635 return getTrylockCallExpr(PE->getSubExpr(),
C, Negate);
1636 else if (
const auto *CE = dyn_cast<ImplicitCastExpr>(
Cond))
1637 return getTrylockCallExpr(CE->getSubExpr(),
C, Negate);
1638 else if (
const auto *FE = dyn_cast<FullExpr>(
Cond))
1639 return getTrylockCallExpr(FE->getSubExpr(),
C, Negate);
1640 else if (
const auto *DRE = dyn_cast<DeclRefExpr>(
Cond)) {
1641 const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(),
C);
1642 return getTrylockCallExpr(E,
C, Negate);
1644 else if (
const auto *UOP = dyn_cast<UnaryOperator>(
Cond)) {
1645 if (UOP->getOpcode() == UO_LNot) {
1647 return getTrylockCallExpr(UOP->getSubExpr(),
C, Negate);
1651 else if (
const auto *BOP = dyn_cast<BinaryOperator>(
Cond)) {
1652 if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
1653 if (BOP->getOpcode() == BO_NE)
1658 if (!TCond) Negate = !Negate;
1659 return getTrylockCallExpr(BOP->getLHS(),
C, Negate);
1663 if (!TCond) Negate = !Negate;
1664 return getTrylockCallExpr(BOP->getRHS(),
C, Negate);
1668 if (BOP->getOpcode() == BO_LAnd) {
1670 return getTrylockCallExpr(BOP->getRHS(),
C, Negate);
1672 if (BOP->getOpcode() == BO_LOr)
1673 return getTrylockCallExpr(BOP->getRHS(),
C, Negate);
1675 }
else if (
const auto *COP = dyn_cast<ConditionalOperator>(
Cond)) {
1679 if (TCond && !FCond)
1680 return getTrylockCallExpr(COP->getCond(),
C, Negate);
1681 if (!TCond && FCond) {
1683 return getTrylockCallExpr(COP->getCond(),
C, Negate);
1686 }
else if (
const auto *SE = dyn_cast<StmtExpr>(
Cond)) {
1687 if (
const auto *CS = SE->getSubStmt(); CS && !CS->body_empty()) {
1688 if (
const auto *E = dyn_cast<Expr>(CS->body_back()))
1689 return getTrylockCallExpr(E,
C, Negate);
1701ThreadSafetyAnalyzer::TerminatorTrylockCall
1702ThreadSafetyAnalyzer::getTerminatorTrylockCall(
const CFGBlock *
Block,
1704 assert(!Negate &&
"Must be called with Negate initialized to false");
1706 const Stmt *
Cond =
Block->getTerminatorCondition();
1711 if (
const auto *COp =
1712 dyn_cast_if_present<ConditionalOperator>(
Block->getTerminatorStmt()))
1713 if (!COp->getType()->isVoidType())
1716 const LocalVarContext &LVarCtx = BlockInfo[
Block->getBlockID()].ExitContext;
1718 std::optional<llvm::scope_exit<
std::function<void()>>> Cleanup;
1722 [
this, Ctx = LVarCtx](
const NamedDecl *D)
mutable ->
const Expr * {
1723 return LocalVarMap.lookupExpr(D, Ctx);
1728 const auto *Exp = getTrylockCallExpr(
Cond, LVarCtx, Negate);
1732 auto *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
1733 if (!FunDecl || !FunDecl->hasAttr<TryAcquireCapabilityAttr>())
1736 return {Exp, FunDecl, std::move(Cleanup)};
1742void ThreadSafetyAnalyzer::getEdgeLockset(FactSet &
Result,
1743 const FactSet &ExitSet,
1744 const CFGBlock *PredBlock,
1745 const CFGBlock *CurrBlock) {
1748 bool Negate =
false;
1749 auto [Exp, FunDecl, Cleanup] = getTerminatorTrylockCall(PredBlock, Negate);
1753 CapExprSet ExclusiveLocksToAdd;
1754 CapExprSet SharedLocksToAdd;
1757 for (
const auto *Attr : FunDecl->specific_attrs<TryAcquireCapabilityAttr>())
1758 getMutexIDs(Attr->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, Attr,
1759 Exp, FunDecl, PredBlock, CurrBlock, Attr->getSuccessValue(),
1764 for (
const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)
1765 addLock(
Result, FactMan.createFact<LockableFactEntry>(ExclusiveLockToAdd,
1767 for (
const auto &SharedLockToAdd : SharedLocksToAdd)
1768 addLock(
Result, FactMan.createFact<LockableFactEntry>(SharedLockToAdd,
1775void ThreadSafetyAnalyzer::getTerminatorTrylockCaps(
const CFGBlock *
Block,
1777 bool Negate =
false;
1778 auto [Exp, FunDecl, Cleanup] = getTerminatorTrylockCall(
Block, Negate);
1782 for (
const auto *Attr : FunDecl->specific_attrs<TryAcquireCapabilityAttr>())
1783 getMutexIDs(Caps, Attr, Exp, FunDecl);
1793class BuildLockset :
public ConstStmtVisitor<BuildLockset> {
1794 friend class ThreadSafetyAnalyzer;
1796 ThreadSafetyAnalyzer *Analyzer;
1799 const FactSet &FunctionExitFSet;
1819 class DualLocalVarContext {
1821 enum Point :
char {
Pre = 0,
Post = 1 };
1823 class ContextSwitchScope {
1824 DualLocalVarContext &DC;
1828 ContextSwitchScope(DualLocalVarContext &DC, Point LastPoint)
1829 : DC(DC), LastPoint(LastPoint) {}
1830 ContextSwitchScope(
const ContextSwitchScope &) =
delete;
1831 ContextSwitchScope &operator=(
const ContextSwitchScope &) =
delete;
1832 ~ContextSwitchScope() { DC.switchContextTo(LastPoint); }
1836 [[nodiscard]] ContextSwitchScope switchToContextForScope(Point P) {
1837 Point PriorPoint = CurrPoint;
1839 return ContextSwitchScope(*
this, PriorPoint);
1849 void moveToNextContext(
const Stmt *S) {
1852 const LocalVariableMap::Context &NewPostCtx =
1853 S ? Analyzer.LocalVarMap.getNextContext(CtxIndex, S, *PrePost[Post])
1856 PrePost[
Post] = &NewPostCtx;
1857 switchContextTo(Post);
1862 DualLocalVarContext(ThreadSafetyAnalyzer &Analyzer,
unsigned EntryIdx,
1863 const LocalVariableMap::Context *EntryContext)
1864 : Analyzer(Analyzer), PrePost{EntryContext, EntryContext},
1865 CurrPoint(
Post), CtxIndex(EntryIdx) {
1866 assert(EntryContext);
1867 switchContextTo(Post);
1871 ThreadSafetyAnalyzer &Analyzer;
1874 std::array<const LocalVariableMap::Context *, 2> PrePost;
1878 void switchContextTo(Point P) {
1883 Analyzer = &Analyzer](
const NamedDecl *D)
mutable ->
const Expr * {
1884 return Analyzer->LocalVarMap.lookupExpr(D, Ctx);
1890 DualLocalVarContext LVarCtx;
1894 void updateLocalVarMapCtx(
const Stmt *S) { LVarCtx.moveToNextContext(S); }
1898 void checkAccess(
const Expr *Exp,
AccessKind AK,
1900 Analyzer->checkAccess(FSet, Exp, AK, POK);
1902 void checkPtAccess(
const Expr *Exp,
AccessKind AK,
1904 Analyzer->checkPtAccess(FSet, Exp, AK, POK);
1907 void handleCall(
const Expr *Exp,
const NamedDecl *D,
1908 til::SExpr *
Self =
nullptr,
1909 SourceLocation Loc = SourceLocation());
1910 void examineArguments(
const FunctionDecl *FD,
1913 bool SkipFirstParam =
false);
1916 BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info,
1917 const FactSet &FunctionExitFSet)
1918 : ConstStmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),
1919 FunctionExitFSet(FunctionExitFSet),
1920 LVarCtx(*Analyzer, Info.EntryIndex, &Info.EntryContext) {
1921 updateLocalVarMapCtx(
nullptr);
1925 BuildLockset(
const BuildLockset &) =
delete;
1926 BuildLockset &operator=(
const BuildLockset &) =
delete;
1928 void VisitUnaryOperator(
const UnaryOperator *UO);
1929 void VisitBinaryOperator(
const BinaryOperator *BO);
1930 void VisitCastExpr(
const CastExpr *CE);
1931 void VisitCallExpr(
const CallExpr *Exp);
1932 void VisitCXXConstructExpr(
const CXXConstructExpr *Exp);
1933 void VisitDeclStmt(
const DeclStmt *S);
1934 void VisitMaterializeTemporaryExpr(
const MaterializeTemporaryExpr *Exp);
1935 void VisitReturnStmt(
const ReturnStmt *S);
1942void ThreadSafetyAnalyzer::warnIfMutexNotHeld(
1943 const FactSet &FSet,
const NamedDecl *D,
const Expr *Exp,
AccessKind AK,
1945 SourceLocation Loc) {
1957 const FactEntry *LDat = FSet.findLock(FactMan, !Cp);
1960 (!Cp).toString(), Loc);
1966 if (!inCurrentScope(Cp))
1970 LDat = FSet.findLock(FactMan, Cp);
1977 const FactEntry *LDat = FSet.findLockUniv(FactMan, Cp);
1978 bool NoError =
true;
1981 LDat = FSet.findPartialMatch(FactMan, Cp);
1984 std::string PartMatchStr = LDat->toString();
1985 StringRef PartMatchName(PartMatchStr);
1995 if (NoError && LDat && !LDat->isAtLeast(LK)) {
2000void ThreadSafetyAnalyzer::warnIfAnyMutexNotHeldForRead(
2001 const FactSet &FSet,
const NamedDecl *D,
const Expr *Exp,
2003 SourceLocation Loc) {
2004 SmallVector<CapabilityExpr, 2> Caps;
2005 for (
auto *Arg : Args) {
2013 const FactEntry *LDat = FSet.findLockUniv(FactMan, Cp);
2023 SmallVector<std::string, 2> NameStorage;
2024 SmallVector<StringRef, 2> Names;
2025 for (
const auto &Cp : Caps) {
2026 NameStorage.push_back(Cp.
toString());
2027 Names.push_back(NameStorage.back());
2033void ThreadSafetyAnalyzer::warnIfMutexHeld(
const FactSet &FSet,
2034 const NamedDecl *D,
const Expr *Exp,
2035 Expr *MutexExp, til::SExpr *
Self,
2036 SourceLocation Loc) {
2045 const FactEntry *LDat = FSet.findLock(FactMan, Cp);
2057void ThreadSafetyAnalyzer::checkAccess(
const FactSet &FSet,
const Expr *Exp,
2066 while (
const auto *DRE = dyn_cast<DeclRefExpr>(Exp)) {
2067 const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()->getCanonicalDecl());
2069 if (
const auto *E = VD->getInit()) {
2080 if (
const auto *UO = dyn_cast<UnaryOperator>(Exp)) {
2082 if (UO->getOpcode() == UO_Deref)
2083 checkPtAccess(FSet, UO->getSubExpr(), AK, POK);
2087 if (
const auto *BO = dyn_cast<BinaryOperator>(Exp)) {
2090 return checkAccess(FSet, BO->
getLHS(), AK, POK);
2092 return checkPtAccess(FSet, BO->
getLHS(), AK, POK);
2098 if (
const auto *AE = dyn_cast<ArraySubscriptExpr>(Exp)) {
2099 checkPtAccess(FSet, AE->getLHS(), AK, POK);
2103 if (
const auto *ME = dyn_cast<MemberExpr>(Exp)) {
2105 checkPtAccess(FSet, ME->getBase(), AK, POK);
2107 checkAccess(FSet, ME->getBase(), AK, POK);
2114 if (D->
hasAttr<GuardedVarAttr>() && FSet.isEmpty(FactMan)) {
2119 if (AK ==
AK_Written || I->args_size() == 1) {
2122 for (
auto *Arg : I->args())
2123 warnIfMutexNotHeld(FSet, D, Exp, AK, Arg, POK,
nullptr, Loc);
2127 warnIfAnyMutexNotHeldForRead(FSet, D, Exp, I->args(), POK, Loc);
2134void ThreadSafetyAnalyzer::checkPtAccess(
const FactSet &FSet,
const Expr *Exp,
2140 if (
const auto *PE = dyn_cast<ParenExpr>(Exp)) {
2141 Exp = PE->getSubExpr();
2144 if (
const auto *CE = dyn_cast<CastExpr>(Exp)) {
2145 if (CE->getCastKind() == CK_ArrayToPointerDecay) {
2148 checkAccess(FSet, CE->getSubExpr(), AK, POK);
2151 Exp = CE->getSubExpr();
2157 if (
const auto *UO = dyn_cast<UnaryOperator>(Exp)) {
2158 if (UO->getOpcode() == UO_AddrOf) {
2161 checkAccess(FSet, UO->getSubExpr(), AK, POK);
2189 if (D->
hasAttr<PtGuardedVarAttr>() && FSet.isEmpty(FactMan))
2193 if (AK ==
AK_Written || I->args_size() == 1) {
2196 for (
auto *Arg : I->args())
2197 warnIfMutexNotHeld(FSet, D, Exp, AK, Arg, PtPOK,
nullptr,
2202 warnIfAnyMutexNotHeldForRead(FSet, D, Exp, I->args(), PtPOK,
2223void BuildLockset::handleCall(
const Expr *Exp,
const NamedDecl *D,
2224 til::SExpr *
Self, SourceLocation Loc) {
2226 updateLocalVarMapCtx(Exp);
2235 auto PreContextForThisScope =
2236 LVarCtx.switchToContextForScope(DualLocalVarContext::Pre);
2237 CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
2238 CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
2239 CapExprSet ScopedReqsAndExcludes;
2247 til::LiteralPtr *Placeholder =
2249 [[maybe_unused]]
auto inserted =
2250 Analyzer->ConstructedObjects.insert({Exp, Placeholder});
2251 assert(inserted.second &&
"Are we visiting the same expression again?");
2254 if (TagT->getDecl()->getMostRecentDecl()->hasAttr<ScopedLockableAttr>())
2255 Scp = CapabilityExpr(Placeholder, Exp->
getType(),
false);
2262 for(
const Attr *At : D->
attrs()) {
2263 switch (At->getKind()) {
2266 case attr::AcquireCapability: {
2267 auto PostContextForThisScope =
2268 LVarCtx.switchToContextForScope(DualLocalVarContext::Post);
2270 Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
2271 : ExclusiveLocksToAdd,
2279 case attr::AssertCapability: {
2280 auto PostContextForThisScope =
2281 LVarCtx.switchToContextForScope(DualLocalVarContext::Post);
2283 CapExprSet AssertLocks;
2284 Analyzer->getMutexIDs(AssertLocks, A, Exp, D,
Self);
2285 for (
const auto &AssertLock : AssertLocks)
2287 FSet, Analyzer->FactMan.createFact<LockableFactEntry>(
2289 Loc, FactEntry::Asserted));
2295 case attr::ReleaseCapability: {
2298 Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D,
Self);
2299 else if (A->isShared())
2300 Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D,
Self);
2302 Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D,
Self);
2306 case attr::RequiresCapability: {
2308 for (
auto *Arg : A->args()) {
2309 Analyzer->warnIfMutexNotHeld(FSet, D, Exp,
2314 Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp, D,
Self);
2319 case attr::LocksExcluded: {
2321 for (
auto *Arg : A->args()) {
2322 Analyzer->warnIfMutexHeld(FSet, D, Exp, Arg,
Self, Loc);
2325 Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp, D,
Self);
2336 std::optional<CallExpr::const_arg_range> Args;
2338 if (
const auto *CE = dyn_cast<CallExpr>(Exp))
2339 Args = CE->arguments();
2340 else if (
const auto *CE = dyn_cast<CXXConstructExpr>(Exp))
2341 Args = CE->arguments();
2343 llvm_unreachable(
"Unknown call kind");
2345 const auto *CalledFunction = dyn_cast<FunctionDecl>(D);
2346 if (CalledFunction && Args.has_value()) {
2347 for (
auto [Param, Arg] : zip(CalledFunction->parameters(), *Args)) {
2350 CapExprSet DeclaredLocks;
2351 for (
const Attr *At : Param->attrs()) {
2352 switch (At->getKind()) {
2353 case attr::AcquireCapability: {
2355 Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
2356 : ExclusiveLocksToAdd,
2358 Analyzer->getMutexIDs(DeclaredLocks, A, Exp, D,
Self);
2362 case attr::ReleaseCapability: {
2365 Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D,
Self);
2366 else if (A->isShared())
2367 Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D,
Self);
2369 Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D,
Self);
2370 Analyzer->getMutexIDs(DeclaredLocks, A, Exp, D,
Self);
2374 case attr::RequiresCapability: {
2376 for (
auto *Arg : A->args())
2377 Analyzer->warnIfMutexNotHeld(FSet, D, Exp,
2380 Analyzer->getMutexIDs(DeclaredLocks, A, Exp, D,
Self);
2384 case attr::LocksExcluded: {
2386 for (
auto *Arg : A->args())
2387 Analyzer->warnIfMutexHeld(FSet, D, Exp, Arg,
Self, Loc);
2388 Analyzer->getMutexIDs(DeclaredLocks, A, Exp, D,
Self);
2396 if (DeclaredLocks.empty())
2398 CapabilityExpr Cp(Analyzer->SxBuilder.
translate(Arg,
nullptr),
2399 StringRef(
"mutex"),
false,
false);
2400 if (
const auto *CBTE = dyn_cast<CXXBindTemporaryExpr>(Arg->
IgnoreCasts());
2402 if (
auto Object = Analyzer->ConstructedObjects.find(CBTE->getSubExpr());
2403 Object != Analyzer->ConstructedObjects.end())
2404 Cp = CapabilityExpr(
Object->second, StringRef(
"mutex"),
false,
2407 const FactEntry *Fact = FSet.findLock(Analyzer->FactMan, Cp);
2415 for (
const auto &[a, b] :
2416 zip_longest(DeclaredLocks, Scope->getUnderlyingMutexes())) {
2417 if (!a.has_value()) {
2420 b.value().getKind(), b.value().toString());
2421 }
else if (!b.has_value()) {
2424 a.value().getKind(), a.value().toString());
2425 }
else if (!a.value().equals(b.value())) {
2428 a.value().getKind(), a.value().toString(), b.value().toString());
2437 for (
const auto &M : ExclusiveLocksToRemove)
2438 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Exclusive);
2439 for (
const auto &M : SharedLocksToRemove)
2440 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Shared);
2441 for (
const auto &M : GenericLocksToRemove)
2442 Analyzer->removeLock(FSet, M, Loc, Dtor,
LK_Generic);
2445 FactEntry::SourceKind Source =
2446 !Scp.
shouldIgnore() ? FactEntry::Managed : FactEntry::Acquired;
2447 for (
const auto &M : ExclusiveLocksToAdd)
2448 Analyzer->addLock(FSet, Analyzer->FactMan.createFact<LockableFactEntry>(
2450 for (
const auto &M : SharedLocksToAdd)
2451 Analyzer->addLock(FSet, Analyzer->FactMan.createFact<LockableFactEntry>(
2456 auto *ScopedEntry = Analyzer->FactMan.createFact<ScopedLockableFactEntry>(
2457 Scp, Loc, FactEntry::Acquired,
2458 ExclusiveLocksToAdd.size() + SharedLocksToAdd.size() +
2459 ScopedReqsAndExcludes.size() + ExclusiveLocksToRemove.size() +
2460 SharedLocksToRemove.size());
2461 for (
const auto &M : ExclusiveLocksToAdd)
2462 ScopedEntry->addLock(M);
2463 for (
const auto &M : SharedLocksToAdd)
2464 ScopedEntry->addLock(M);
2465 for (
const auto &M : ScopedReqsAndExcludes)
2466 ScopedEntry->addLock(M);
2467 for (
const auto &M : ExclusiveLocksToRemove)
2468 ScopedEntry->addExclusiveUnlock(M);
2469 for (
const auto &M : SharedLocksToRemove)
2470 ScopedEntry->addSharedUnlock(M);
2471 Analyzer->addLock(FSet, ScopedEntry);
2478void BuildLockset::VisitUnaryOperator(
const UnaryOperator *UO) {
2494void BuildLockset::VisitBinaryOperator(
const BinaryOperator *BO) {
2498 updateLocalVarMapCtx(BO);
2504void BuildLockset::VisitCastExpr(
const CastExpr *CE) {
2510void BuildLockset::examineArguments(
const FunctionDecl *FD,
2513 bool SkipFirstParam) {
2523 if (FD->
hasAttr<NoThreadSafetyAnalysisAttr>())
2526 const ArrayRef<ParmVarDecl *> Params = FD->
parameters();
2527 auto Param = Params.begin();
2532 for (
auto Arg = ArgBegin; Param != Params.end() && Arg != ArgEnd;
2534 QualType Qt = (*Param)->getType();
2542void BuildLockset::VisitCallExpr(
const CallExpr *Exp) {
2543 if (
const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
2544 const auto *ME = dyn_cast<MemberExpr>(CE->getCallee());
2546 const CXXMethodDecl *MD = CE->getMethodDecl();
2549 if (ME->isArrow()) {
2551 checkPtAccess(CE->getImplicitObjectArgument(),
AK_Read);
2554 checkAccess(CE->getImplicitObjectArgument(),
AK_Read);
2558 examineArguments(CE->getDirectCallee(), CE->arg_begin(), CE->arg_end());
2559 }
else if (
const auto *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
2567 case OO_PercentEqual:
2571 case OO_LessLessEqual:
2572 case OO_GreaterGreaterEqual:
2573 checkAccess(OE->getArg(1),
AK_Read);
2583 if (!(OEop == OO_Star && OE->getNumArgs() > 1)) {
2585 checkPtAccess(OE->getArg(0),
AK_Read);
2590 const Expr *Obj = OE->getArg(0);
2595 const FunctionDecl *FD = OE->getDirectCallee();
2596 examineArguments(FD, std::next(OE->arg_begin()), OE->arg_end(),
2605 auto *D = dyn_cast_or_null<NamedDecl>(Exp->
getCalleeDecl());
2612 updateLocalVarMapCtx(Exp);
2615void BuildLockset::VisitCXXConstructExpr(
const CXXConstructExpr *Exp) {
2618 const Expr* Source = Exp->
getArg(0);
2628 if (
auto *CE = dyn_cast<CastExpr>(E))
2631 if (
auto *CE = dyn_cast<CastExpr>(E))
2632 if (CE->
getCastKind() == CK_ConstructorConversion ||
2635 if (
auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))
2636 E = BTE->getSubExpr();
2640void BuildLockset::VisitDeclStmt(
const DeclStmt *S) {
2642 if (
auto *VD = dyn_cast_or_null<VarDecl>(D)) {
2643 const Expr *E = VD->getInit();
2649 if (
auto *EWC = dyn_cast<ExprWithCleanups>(E))
2653 if (
auto Object = Analyzer->ConstructedObjects.find(E);
2654 Object != Analyzer->ConstructedObjects.end()) {
2655 Object->second->setClangDecl(VD);
2656 Analyzer->ConstructedObjects.erase(
Object);
2660 updateLocalVarMapCtx(S);
2663void BuildLockset::VisitMaterializeTemporaryExpr(
2664 const MaterializeTemporaryExpr *Exp) {
2666 if (
auto Object = Analyzer->ConstructedObjects.find(
2668 Object != Analyzer->ConstructedObjects.end()) {
2669 Object->second->setClangDecl(ExtD);
2670 Analyzer->ConstructedObjects.erase(
Object);
2675void BuildLockset::VisitReturnStmt(
const ReturnStmt *S) {
2676 if (Analyzer->CurrentFunction ==
nullptr)
2684 const QualType ReturnType =
2687 Analyzer->checkAccess(
2688 FunctionExitFSet, RetVal,
2692 Analyzer->checkPtAccess(
2693 FunctionExitFSet, RetVal,
2703bool ThreadSafetyAnalyzer::join(
const FactEntry &A,
const FactEntry &B,
2704 SourceLocation JoinLoc,
2708 unsigned int ReentrancyDepthA = 0;
2709 unsigned int ReentrancyDepthB = 0;
2711 if (
const auto *LFE = dyn_cast<LockableFactEntry>(&A))
2712 ReentrancyDepthA = LFE->getReentrancyDepth();
2713 if (
const auto *LFE = dyn_cast<LockableFactEntry>(&B))
2714 ReentrancyDepthB = LFE->getReentrancyDepth();
2716 if (ReentrancyDepthA != ReentrancyDepthB) {
2722 return CanModify && ReentrancyDepthA < ReentrancyDepthB;
2723 }
else if (A.kind() != B.kind()) {
2726 if ((A.managed() || A.asserted()) && (B.managed() || B.asserted())) {
2728 bool ShouldTakeB = B.kind() ==
LK_Shared;
2729 if (CanModify || !ShouldTakeB)
2738 return CanModify && A.asserted() && !B.asserted();
2761void ThreadSafetyAnalyzer::intersectAndWarn(
2762 FactSet &EntrySet,
const FactSet &ExitSet, SourceLocation JoinLoc,
2764 const CapExprSet *TrylockRebranchCaps) {
2765 FactSet EntrySetOrig = EntrySet;
2767 auto IsTrylockRebranched = [TrylockRebranchCaps](
const FactEntry &FE) {
2768 return TrylockRebranchCaps &&
2769 llvm::any_of(*TrylockRebranchCaps, [&FE](
const CapabilityExpr &CE) {
2775 for (
const auto &Fact : ExitSet) {
2776 const FactEntry &ExitFact = FactMan[Fact];
2778 FactSet::iterator EntryIt = EntrySet.findLockIter(FactMan, ExitFact);
2779 if (EntryIt != EntrySet.end()) {
2780 if (
join(FactMan[*EntryIt], ExitFact, JoinLoc, EntryLEK))
2783 !IsTrylockRebranched(ExitFact)) {
2784 ExitFact.handleRemovalFromIntersection(ExitSet, FactMan, JoinLoc,
2790 for (
const auto &Fact : EntrySetOrig) {
2791 const FactEntry *EntryFact = &FactMan[Fact];
2792 const FactEntry *ExitFact = ExitSet.findLock(FactMan, *EntryFact);
2797 !IsTrylockRebranched(*EntryFact))
2798 EntryFact->handleRemovalFromIntersection(EntrySetOrig, FactMan, JoinLoc,
2801 EntrySet.removeLock(FactMan, *EntryFact);
2814 if (std::optional<CFGStmt> S =
Last.getAs<
CFGStmt>()) {
2826void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
2829 threadSafety::CFGWalker walker;
2830 if (!walker.
init(AC))
2837 const NamedDecl *D = walker.
getDecl();
2838 CurrentFunction = dyn_cast<FunctionDecl>(D);
2840 if (D->
hasAttr<NoThreadSafetyAnalysisAttr>())
2855 CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));
2861 PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph);
2867 Initial.Reachable =
true;
2870 LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);
2875 CapExprSet ExclusiveLocksAcquired;
2876 CapExprSet SharedLocksAcquired;
2877 CapExprSet LocksReleased;
2882 if (!SortedGraph->
empty()) {
2884 FactSet &InitialLockset = Initial.EntrySet;
2886 CapExprSet ExclusiveLocksToAdd;
2887 CapExprSet SharedLocksToAdd;
2890 for (
const auto *Attr : D->
attrs()) {
2891 Loc = Attr->getLocation();
2892 if (
const auto *A = dyn_cast<RequiresCapabilityAttr>(Attr)) {
2893 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2895 }
else if (
const auto *A = dyn_cast<ReleaseCapabilityAttr>(Attr)) {
2898 if (A->args_size() == 0)
2900 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2902 getMutexIDs(LocksReleased, A,
nullptr, D);
2903 }
else if (
const auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) {
2904 if (A->args_size() == 0)
2906 getMutexIDs(A->isShared() ? SharedLocksAcquired
2907 : ExclusiveLocksAcquired,
2914 ArrayRef<ParmVarDecl *> Params;
2915 if (CurrentFunction)
2917 else if (
auto CurrentMethod = dyn_cast<ObjCMethodDecl>(D))
2918 Params = CurrentMethod->getCanonicalDecl()->parameters();
2920 llvm_unreachable(
"Unknown function kind");
2921 for (
const ParmVarDecl *Param : Params) {
2924 CapExprSet UnderlyingLocks;
2925 for (
const auto *Attr : Param->attrs()) {
2926 Loc = Attr->getLocation();
2927 if (
const auto *A = dyn_cast<ReleaseCapabilityAttr>(Attr)) {
2928 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2930 getMutexIDs(LocksReleased, A,
nullptr, Param);
2931 getMutexIDs(UnderlyingLocks, A,
nullptr, Param);
2932 }
else if (
const auto *A = dyn_cast<RequiresCapabilityAttr>(Attr)) {
2933 getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2935 getMutexIDs(UnderlyingLocks, A,
nullptr, Param);
2936 }
else if (
const auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) {
2937 getMutexIDs(A->isShared() ? SharedLocksAcquired
2938 : ExclusiveLocksAcquired,
2940 getMutexIDs(UnderlyingLocks, A,
nullptr, Param);
2941 }
else if (
const auto *A = dyn_cast<LocksExcludedAttr>(Attr)) {
2942 getMutexIDs(UnderlyingLocks, A,
nullptr, Param);
2945 if (UnderlyingLocks.empty())
2950 auto *ScopedEntry = FactMan.createFact<ScopedLockableFactEntry>(
2951 Cp, Param->getLocation(), FactEntry::Declared,
2952 UnderlyingLocks.size());
2953 for (
const CapabilityExpr &M : UnderlyingLocks)
2954 ScopedEntry->addLock(M);
2955 addLock(InitialLockset, ScopedEntry,
true);
2959 for (
const auto &Mu : ExclusiveLocksToAdd) {
2960 const auto *Entry = FactMan.createFact<LockableFactEntry>(
2962 addLock(InitialLockset, Entry,
true);
2964 for (
const auto &Mu : SharedLocksToAdd) {
2965 const auto *Entry = FactMan.createFact<LockableFactEntry>(
2966 Mu,
LK_Shared, Loc, FactEntry::Declared);
2967 addLock(InitialLockset, Entry,
true);
2973 FactSet ExpectedFunctionExitSet = Initial.EntrySet;
2979 for (
const auto &Lock : ExclusiveLocksAcquired)
2980 ExpectedFunctionExitSet.addLock(
2981 FactMan, FactMan.createFact<LockableFactEntry>(Lock,
LK_Exclusive,
2983 for (
const auto &Lock : SharedLocksAcquired)
2984 ExpectedFunctionExitSet.addLock(
2985 FactMan, FactMan.createFact<LockableFactEntry>(Lock,
LK_Shared,
2987 for (
const auto &Lock : LocksReleased)
2988 ExpectedFunctionExitSet.removeLock(FactMan, Lock);
2990 for (
const auto *CurrBlock : *SortedGraph) {
2991 unsigned CurrBlockID = CurrBlock->
getBlockID();
2992 CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
2995 VisitedBlocks.insert(CurrBlock);
3010 bool LocksetInitialized =
false;
3013 CapExprSet TerminatorTrylockCaps;
3014 bool TerminatorTrylockCapsComputed =
false;
3016 PE = CurrBlock->
pred_end(); PI != PE; ++PI) {
3018 if (*PI ==
nullptr || !VisitedBlocks.alreadySet(*PI))
3021 unsigned PrevBlockID = (*PI)->getBlockID();
3022 CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
3029 CurrBlockInfo->Reachable =
true;
3031 FactSet PrevLockset;
3032 getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);
3034 if (!LocksetInitialized) {
3035 CurrBlockInfo->EntrySet = PrevLockset;
3036 LocksetInitialized =
true;
3041 if (isa_and_nonnull<ContinueStmt>((*PI)->getTerminatorStmt())) {
3043 intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
3044 CurrBlockInfo->EntryLoc,
3050 if (!TerminatorTrylockCapsComputed) {
3052 getTerminatorTrylockCaps(CurrBlock, TerminatorTrylockCaps);
3053 TerminatorTrylockCapsComputed =
true;
3055 intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
3063 if (!CurrBlockInfo->Reachable)
3066 BuildLockset LocksetBuilder(
this, *CurrBlockInfo, ExpectedFunctionExitSet);
3069 for (
const auto &BI : *CurrBlock) {
3070 switch (BI.getKind()) {
3072 CFGStmt CS = BI.castAs<CFGStmt>();
3073 LocksetBuilder.Visit(CS.
getStmt());
3078 CFGAutomaticObjDtor AD = BI.castAs<CFGAutomaticObjDtor>();
3084 if (isa_and_nonnull<ParmVarDecl>(AD.
getVarDecl()))
3086 if (!DD || !DD->hasAttrs())
3089 LocksetBuilder.handleCall(
3097 const CFGCleanupFunction &
CF = BI.castAs<CFGCleanupFunction>();
3098 LocksetBuilder.handleCall(
3099 nullptr,
CF.getFunctionDecl(),
3101 CF.getVarDecl()->getLocation());
3106 auto TD = BI.castAs<CFGTemporaryDtor>();
3110 if (
auto Object = ConstructedObjects.find(
3111 TD.getBindTemporaryExpr()->getSubExpr());
3112 Object != ConstructedObjects.end()) {
3116 LocksetBuilder.handleCall(
nullptr, DD,
Object->second,
3117 TD.getBindTemporaryExpr()->getEndLoc());
3118 ConstructedObjects.erase(
Object);
3126 CurrBlockInfo->ExitSet = LocksetBuilder.FSet;
3133 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
3135 if (*SI ==
nullptr || !VisitedBlocks.alreadySet(*SI))
3138 CFGBlock *FirstLoopBlock = *SI;
3139 CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->
getBlockID()];
3140 CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];
3141 intersectAndWarn(PreLoop->EntrySet, LoopEnd->ExitSet, PreLoop->EntryLoc,
3147 if (!Final.Reachable)
3151 intersectAndWarn(ExpectedFunctionExitSet, Final.ExitSet, Final.ExitLoc,
3167 ThreadSafetyAnalyzer Analyzer(Handler, *BSet);
3168 Analyzer.runAnalysis(AC);
3182 llvm_unreachable(
"Unknown AccessKind");
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
Defines enum values for all the target-independent builtin functions.
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
static Decl::Kind getKind(const Decl *D)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::Expr interface and subclasses for C++ expressions.
Result
Implement __builtin_bit_cast and related operations.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
Defines an enumeration for C++ overloaded operators.
static std::string toString(const clang::SanitizerSet &Sanitizers)
Produce a string containing comma-separated names of sanitizers in Sanitizers set.
Defines the clang::SourceLocation class and associated facilities.
Defines various enumerations that describe declaration and type specifiers.
static void warnInvalidLock(ThreadSafetyHandler &Handler, const Expr *MutexExp, const NamedDecl *D, const Expr *DeclExp, StringRef Kind)
Issue a warning about an invalid lock expression.
static bool isCallbackParam(const ParmVarDecl *Param)
True if capability attributes on Param describe the function reached through it rather than the argum...
static bool getStaticBooleanValue(Expr *E, bool &TCond)
static bool neverReturns(const CFGBlock *B)
static void findBlockLocations(CFG *CFGraph, const PostOrderCFGView *SortedGraph, std::vector< CFGBlockInfo > &BlockInfo)
Find the appropriate source locations to use when producing diagnostics for each block in the CFG.
static const ValueDecl * getValueDecl(const Expr *Exp)
Gets the value decl pointer from DeclRefExprs or MemberExprs.
static const Expr * UnpackConstruction(const Expr *E)
C Language Family Type Representation.
AnalysisDeclContext contains the context data for the function, method or block under analysis.
ASTContext & getASTContext() const
static bool isAssignmentOp(Opcode Opc)
const VarDecl * getVarDecl() const
const Stmt * getTriggerStmt() const
Represents a single basic block in a source-level CFG.
bool hasNoReturnElement() const
ElementList::const_reverse_iterator const_reverse_iterator
succ_iterator succ_begin()
AdjacentBlocks::const_iterator const_pred_iterator
pred_iterator pred_begin()
unsigned getBlockID() const
AdjacentBlocks::const_iterator const_succ_iterator
Represents a top-level expression in a basic block.
const CXXDestructorDecl * getDestructorDecl(ASTContext &astContext) const
const Stmt * getStmt() const
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
unsigned getNumBlockIDs() const
Returns the total number of BlockIDs allocated (which start at 0).
Expr * getArg(unsigned Arg)
Return the specified argument.
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
bool isCopyConstructor(unsigned &TypeQuals) const
Whether this constructor is a copy constructor (C++ [class.copy]p2, which can be used to copy the cla...
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
ConstExprIterator const_arg_iterator
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
CastKind getCastKind() const
const DeclGroupRef getDeclGroup() const
SourceLocation getBeginLoc() const LLVM_READONLY
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
SourceLocation getLocation() const
bool isDefinedOutsideFunctionOrMethod() const
isDefinedOutsideFunctionOrMethod - This predicate returns true if this scoped decl is defined outside...
DeclContext * getDeclContext()
This represents one expression.
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Expr * IgnoreImplicit() LLVM_READONLY
Skip past any implicit AST nodes which might surround this expression until reaching a fixed point.
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Expr * IgnoreCasts() LLVM_READONLY
Skip past any casts which might surround this expression until reaching a fixed point.
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
const ParmVarDecl * getParamDecl(unsigned i) const
QualType getReturnType() const
ArrayRef< ParmVarDecl * > parameters() const
FunctionDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Expr * getSubExpr() const
Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue.
ValueDecl * getExtendingDecl()
Get the declaration which triggered the lifetime-extension of this temporary, if any.
This represents a decl that may have a name.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
virtual void printName(raw_ostream &OS, const PrintingPolicy &Policy) const
Pretty-print the unqualified name of this declaration.
Represents a parameter to a function.
A (possibly-)qualified type.
QualType getCanonicalType() const
bool isConstQualified() const
Determine whether this type is const-qualified.
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
Stmt - This represents one statement.
SourceLocation getEndLoc() const LLVM_READONLY
void dump() const
Dumps the specified AST fragment and all subtrees to llvm::errs().
bool isPointerType() const
bool isReferenceType() const
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
bool isLValueReferenceType() const
const T * getAs() const
Member-template getAs<specific type>'.
Expr * getSubExpr() const
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
void checkBeforeAfter(const ValueDecl *Vd, const FactSet &FSet, ThreadSafetyAnalyzer &Analyzer, SourceLocation Loc, StringRef CapKind)
Return true if any mutexes in FSet are in the acquired_before set of Vd.
BeforeInfo * insertAttrExprs(const ValueDecl *Vd, ThreadSafetyAnalyzer &Analyzer)
Process acquired_before and acquired_after attributes on Vd.
BeforeInfo * getBeforeInfoForDecl(const ValueDecl *Vd, ThreadSafetyAnalyzer &Analyzer)
const PostOrderCFGView * getSortedGraph() const
const NamedDecl * getDecl() const
bool init(AnalysisDeclContext &AC)
const CFG * getGraph() const
bool shouldIgnore() const
bool equals(const CapabilityExpr &other) const
const til::SExpr * sexpr() const
std::string toString() const
const ValueDecl * valueDecl() const
StringRef getKind() const
CapabilityExpr translateAttrExpr(const Expr *AttrExp, const NamedDecl *D, const Expr *DeclExp, til::SExpr *Self=nullptr)
Translate a clang expression in an attribute to a til::SExpr.
void setLookupLocalVarExpr(std::function< const Expr *(const NamedDecl *)> F)
til::SExpr * translate(const Stmt *S, CallingContext *Ctx)
til::LiteralPtr * createThisPlaceholder()
til::SExpr * translateVariable(const VarDecl *VD, CallingContext *Ctx)
Handler class for thread safety warnings.
virtual ~ThreadSafetyHandler()
virtual void handleExpectMoreUnderlyingMutexes(SourceLocation Loc, SourceLocation DLoc, Name ScopeName, StringRef Kind, Name Expected)
Warn when we get fewer underlying mutexes than expected.
virtual void handleInvalidLockExp(SourceLocation Loc)
Warn about lock expressions which fail to resolve to lockable objects.
virtual void handleUnmatchedUnderlyingMutexes(SourceLocation Loc, SourceLocation DLoc, Name ScopeName, StringRef Kind, Name Expected, Name Actual)
Warn when an actual underlying mutex of a scoped lockable does not match the expected.
virtual void handleExpectFewerUnderlyingMutexes(SourceLocation Loc, SourceLocation DLoc, Name ScopeName, StringRef Kind, Name Actual)
Warn when we get more underlying mutexes than expected.
virtual void enterFunction(const FunctionDecl *FD)
Called by the analysis when starting analysis of a function.
virtual void handleIncorrectUnlockKind(StringRef Kind, Name LockName, LockKind Expected, LockKind Received, SourceLocation LocLocked, SourceLocation LocUnlock)
Warn about an unlock function call that attempts to unlock a lock with the incorrect lock kind.
virtual void handleMutexHeldEndOfScope(StringRef Kind, Name LockName, SourceLocation LocLocked, SourceLocation LocEndOfScope, LockErrorKind LEK, bool ReentrancyMismatch=false)
Warn about situations where a mutex is sometimes held and sometimes not.
virtual void leaveFunction(const FunctionDecl *FD)
Called by the analysis when finishing analysis of a function.
virtual void handleExclusiveAndShared(StringRef Kind, Name LockName, SourceLocation Loc1, SourceLocation Loc2)
Warn when a mutex is held exclusively and shared at the same point.
virtual void handleMutexNotHeld(StringRef Kind, const NamedDecl *D, ProtectedOperationKind POK, Name LockName, LockKind LK, SourceLocation Loc, Name *PossibleMatch=nullptr)
Warn when a protected operation occurs while the specific mutex protecting the operation is not locke...
virtual void handleFunExcludesLock(StringRef Kind, Name FunName, Name LockName, SourceLocation Loc)
Warn when a function is called while an excluded mutex is locked.
virtual void handleGuardedByAnyReadNotHeld(const NamedDecl *D, ProtectedOperationKind POK, ArrayRef< StringRef > LockNames, SourceLocation Loc)
Warn when a read of a multi-capability guarded_by variable occurs while none of the listed capabiliti...
virtual void handleNoMutexHeld(const NamedDecl *D, ProtectedOperationKind POK, AccessKind AK, SourceLocation Loc)
Warn when a protected operation occurs while no locks are held.
virtual void handleUnmatchedUnlock(StringRef Kind, Name LockName, SourceLocation Loc, SourceLocation LocPreviousUnlock)
Warn about unlock function calls that do not have a prior matching lock expression.
virtual void handleNegativeNotHeld(StringRef Kind, Name LockName, Name Neg, SourceLocation Loc)
Warn when acquiring a lock that the negative capability is not held.
virtual void handleDoubleLock(StringRef Kind, Name LockName, SourceLocation LocLocked, SourceLocation LocDoubleLock)
Warn about lock function calls for locks which are already held.
internal::Matcher< T > traverse(TraversalKind TK, const internal::Matcher< T > &InnerMatcher)
Causes all nested matchers to be matched with the specified traversal kind.
@ CF
Indicates that the tracked object is a CF object.
bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc)
bool Dec(InterpState &S, CodePtr OpPC, bool CanOverflow)
1) Pops a pointer from the stack 2) Load the value from the pointer 3) Writes the value decreased by ...
bool Neg(InterpState &S, CodePtr OpPC)
SetTy< T > join(SetTy< T > A, SetTy< T > B, typename SetTy< T >::Factory &F)
Computes the union of two ImmutableSets.
utils::ID< struct FactTag > FactID
std::unique_ptr< DiagnosticConsumer > create(StringRef OutputFile, DiagnosticOptions &DiagOpts, bool MergeChildRecords=false)
Returns a DiagnosticConsumer that serializes diagnostics to a bitcode file.
bool matches(const til::SExpr *E1, const til::SExpr *E2)
LockKind getLockKindFromAccessKind(AccessKind AK)
Helper function that returns a LockKind required for the given level of access.
LockErrorKind
This enum distinguishes between different situations where we warn due to inconsistent locking.
@ LEK_NotLockedAtEndOfFunction
Expecting a capability to be held at the end of function.
@ LEK_LockedSomePredecessors
A capability is locked in some but not all predecessors of a CFGBlock.
@ LEK_LockedAtEndOfFunction
A capability is still locked at the end of a function.
@ LEK_LockedSomeLoopIterations
A capability is locked for some but not all loop iterations.
void threadSafetyCleanup(BeforeSet *Cache)
AccessKind
This enum distinguishes between different ways to access (read or write) a variable.
@ AK_Written
Writing a variable.
@ AK_Read
Reading a variable.
LockKind
This enum distinguishes between different kinds of lock actions.
@ LK_Shared
Shared/reader lock of a mutex.
@ LK_Exclusive
Exclusive/writer lock of a mutex.
@ LK_Generic
Can be either Shared or Exclusive.
void runThreadSafetyAnalysis(AnalysisDeclContext &AC, ThreadSafetyHandler &Handler, BeforeSet **Bset)
Check a function's CFG for thread-safety violations.
ProtectedOperationKind
This enum distinguishes between different kinds of operations that may need to be protected by locks.
@ POK_PtPassByRef
Passing a pt-guarded variable by reference.
@ POK_PassPointer
Passing pointer to a guarded variable.
@ POK_VarDereference
Dereferencing a variable (e.g. p in *p = 5;)
@ POK_PassByRef
Passing a guarded variable by reference.
@ POK_ReturnByRef
Returning a guarded variable by reference.
@ POK_PtPassPointer
Passing a pt-guarded pointer.
@ POK_PtReturnPointer
Returning a pt-guarded pointer.
@ POK_VarAccess
Reading or writing a variable (e.g. x in x = 5;)
@ POK_FunctionCall
Making a function call (e.g. fool())
@ POK_ReturnPointer
Returning pointer to a guarded variable.
@ POK_PtReturnByRef
Returning a pt-guarded variable by reference.
The JSON file list parser is used to communicate input to InstallAPI.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
bool isa(CodeGen::Address addr)
static bool classof(const OMPClause *T)
@ Self
'self' clause, allowed on Compute and Combined Constructs, plus 'update'.
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
@ Result
The result type of a method or function.
const FunctionProtoType * T
U cast(CodeGen::Address addr)
@ Other
Other implicit parameter.
int const char * function