;
62using namespacethreadSafety;
70 const Expr*DeclExp, StringRef Kind) {
84classCapExprSet :
public SmallVector<CapabilityExpr, 4> {
89 returnCapE.
equals(CapE2);
106 enumFactEntryKind { Lockable, ScopedLockable };
117 constFactEntryKind
Kind: 8;
123SourceKind Source : 8;
132 virtual~FactEntry() =
default;
136FactEntryKind getFactEntryKind()
const{
return Kind; }
138 boolasserted()
const{
returnSource == Asserted; }
139 booldeclared()
const{
returnSource == Declared; }
140 boolmanaged()
const{
returnSource == Managed; }
143handleRemovalFromIntersection(
constFactSet &FSet, FactManager &FactMan,
146 virtual voidhandleLock(FactSet &FSet, FactManager &FactMan,
147 constFactEntry &entry,
149 virtual voidhandleUnlock(FactSet &FSet, FactManager &FactMan,
155 boolisAtLeast(
LockKindLK)
const{
160usingFactID =
unsignedshort;
166std::vector<std::unique_ptr<const FactEntry>> Facts;
169FactID newFact(std::unique_ptr<FactEntry> Entry) {
170Facts.push_back(std::move(Entry));
171 return static_cast<unsigned short>(Facts.size() - 1);
174 constFactEntry &operator[](FactID F)
const{
return*Facts[F]; }
191 usingiterator = FactVec::iterator;
192 usingconst_iterator = FactVec::const_iterator;
194iterator begin() {
returnFactIDs.begin(); }
195const_iterator begin()
const{
returnFactIDs.begin(); }
197iterator end() {
returnFactIDs.end(); }
198const_iterator end()
const{
returnFactIDs.end(); }
200 boolisEmpty()
const{
returnFactIDs.size() == 0; }
203 boolisEmpty(FactManager &FactMan)
const{
204 for(
const autoFID : *
this) {
205 if(!FactMan[FID].negative())
211 voidaddLockByID(FactID ID) { FactIDs.push_back(ID); }
213FactID addLock(FactManager &FM, std::unique_ptr<FactEntry> Entry) {
214FactID F = FM.newFact(std::move(Entry));
215FactIDs.push_back(F);
220 unsignedn = FactIDs.size();
224 for(
unsignedi = 0; i < n-1; ++i) {
225 if(FM[FactIDs[i]].
matches(CapE)) {
226FactIDs[i] = FactIDs[n-1];
231 if(FM[FactIDs[n-1]].
matches(CapE)) {
238iterator findLockIter(FactManager &FM,
const CapabilityExpr&CapE) {
239 returnstd::find_if(begin(), end(), [&](FactID ID) {
240 returnFM[
ID].matches(CapE);
244 constFactEntry *findLock(FactManager &FM,
const CapabilityExpr&CapE)
const{
245 autoI = std::find_if(begin(), end(), [&](FactID ID) {
246 returnFM[
ID].matches(CapE);
248 returnI != end() ? &FM[*I] :
nullptr;
251 constFactEntry *findLockUniv(FactManager &FM,
253 autoI = std::find_if(begin(), end(), [&](FactID ID) ->
bool{
254 returnFM[
ID].matchesUniv(CapE);
256 returnI != end() ? &FM[*I] :
nullptr;
259 constFactEntry *findPartialMatch(FactManager &FM,
261 autoI = std::find_if(begin(), end(), [&](FactID ID) ->
bool{
262 returnFM[
ID].partiallyMatches(CapE);
264 returnI != end() ? &FM[*I] :
nullptr;
267 boolcontainsMutexDecl(FactManager &FM,
const ValueDecl* Vd)
const{
268 autoI = std::find_if(begin(), end(), [&](FactID ID) ->
bool{
269 returnFM[
ID].valueDecl() == Vd;
275classThreadSafetyAnalyzer;
280namespacethreadSafety {
290BeforeInfo() =
default;
291BeforeInfo(BeforeInfo &&) =
default;
295llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>;
296 usingCycleMap = llvm::DenseMap<const ValueDecl *, bool>;
302ThreadSafetyAnalyzer& Analyzer);
305ThreadSafetyAnalyzer &Analyzer);
309ThreadSafetyAnalyzer& Analyzer,
322classLocalVariableMap;
324usingLocalVarContext = llvm::ImmutableMap<const NamedDecl *, unsigned>;
327enumCFGBlockSide { CBS_Entry, CBS_Exit };
340LocalVarContext EntryContext;
343LocalVarContext ExitContext;
355 boolReachable =
false;
357 constFactSet &getSet(CFGBlockSide Side)
const{
358 returnSide == CBS_Entry ? EntrySet : ExitSet;
362 returnSide == CBS_Entry ? EntryLoc : ExitLoc;
366CFGBlockInfo(LocalVarContext EmptyCtx)
367: EntryContext(EmptyCtx), ExitContext(EmptyCtx) {}
370 staticCFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
386classLocalVariableMap {
388 usingContext = LocalVarContext;
394 structVarDefinition {
396 friend classLocalVariableMap;
402 const Expr*Exp =
nullptr;
410 boolisReference()
const{
return!Exp; }
415:
Dec(
D), Exp(
E), Ctx(
C) {}
418VarDefinition(
const NamedDecl*
D,
unsignedR, Context
C)
419:
Dec(
D), Ref(R), Ctx(
C) {}
423Context::Factory ContextFactory;
424std::vector<VarDefinition> VarDefinitions;
425std::vector<std::pair<const Stmt *, Context>> SavedContexts;
430VarDefinitions.push_back(VarDefinition(
nullptr, 0u, getEmptyContext()));
434 constVarDefinition* lookup(
const NamedDecl*
D, Context Ctx) {
435 const unsigned*i = Ctx.lookup(
D);
438assert(*i < VarDefinitions.size());
439 return&VarDefinitions[*i];
446 const unsigned*
P= Ctx.lookup(
D);
452 if(VarDefinitions[i].Exp) {
453Ctx = VarDefinitions[i].Ctx;
454 returnVarDefinitions[i].Exp;
456i = VarDefinitions[i].Ref;
461Context getEmptyContext() {
returnContextFactory.getEmptyMap(); }
466Context getNextContext(
unsigned&CtxIndex,
const Stmt*S, Context
C) {
467 if(SavedContexts[CtxIndex+1].first == S) {
469Context Result = SavedContexts[CtxIndex].second;
475 voiddumpVarDefinitionName(
unsignedi) {
477llvm::errs() <<
"Undefined";
482llvm::errs() <<
"<<NULL>>";
485 Dec->printName(llvm::errs());
486llvm::errs() <<
"."<< i <<
" "<< ((
const void*) Dec);
491 for(
unsignedi = 1, e = VarDefinitions.size(); i < e; ++i) {
492 const Expr*Exp = VarDefinitions[i].Exp;
493 unsignedRef = VarDefinitions[i].Ref;
495dumpVarDefinitionName(i);
496llvm::errs() <<
" = ";
497 if(Exp) Exp->
dump();
499dumpVarDefinitionName(Ref);
500llvm::errs() <<
"\n";
506 voiddumpContext(Context
C) {
507 for(Context::iterator I =
C.begin(),
E=
C.end(); I !=
E; ++I) {
509 D->printName(llvm::errs());
510llvm::errs() <<
" -> ";
511dumpVarDefinitionName(I.getData());
512llvm::errs() <<
"\n";
518std::vector<CFGBlockInfo> &BlockInfo);
521 friend classVarMapBuilder;
524 unsignedgetContextIndex() {
returnSavedContexts.size()-1; }
527 voidsaveContext(
const Stmt*S, Context
C) {
528SavedContexts.push_back(std::make_pair(S,
C));
533Context addDefinition(
const NamedDecl*
D,
const Expr*Exp, Context Ctx) {
534assert(!Ctx.contains(
D));
535 unsignednewID = VarDefinitions.size();
536Context NewCtx = ContextFactory.add(Ctx,
D, newID);
537VarDefinitions.push_back(VarDefinition(
D, Exp, Ctx));
542Context addReference(
const NamedDecl*
D,
unsignedi, Context Ctx) {
543 unsignednewID = VarDefinitions.size();
544Context NewCtx = ContextFactory.add(Ctx,
D, newID);
545VarDefinitions.push_back(VarDefinition(
D, i, Ctx));
551Context updateDefinition(
const NamedDecl*
D,
Expr*Exp, Context Ctx) {
552 if(Ctx.contains(
D)) {
553 unsignednewID = VarDefinitions.size();
554Context NewCtx = ContextFactory.remove(Ctx,
D);
555NewCtx = ContextFactory.add(NewCtx,
D, newID);
556VarDefinitions.push_back(VarDefinition(
D, Exp, Ctx));
564Context clearDefinition(
const NamedDecl*
D, Context Ctx) {
565Context NewCtx = Ctx;
566 if(NewCtx.contains(
D)) {
567NewCtx = ContextFactory.remove(NewCtx,
D);
568NewCtx = ContextFactory.add(NewCtx,
D, 0);
574Context removeDefinition(
const NamedDecl*
D, Context Ctx) {
575Context NewCtx = Ctx;
576 if(NewCtx.contains(
D)) {
577NewCtx = ContextFactory.remove(NewCtx,
D);
582Context intersectContexts(Context C1, Context C2);
583Context createReferenceContext(Context
C);
584 voidintersectBackEdge(Context C1, Context C2);
590CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
591 returnCFGBlockInfo(M.getEmptyContext());
599LocalVariableMap* VMap;
600LocalVariableMap::Context Ctx;
602VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context
C)
603: VMap(VM), Ctx(
C) {}
605 voidVisitDeclStmt(
const DeclStmt*S);
612voidVarMapBuilder::VisitDeclStmt(
const DeclStmt*S) {
613 boolmodifiedCtx =
false;
615 for(
const auto*
D: DGrp) {
616 if(
const auto*VD = dyn_cast_or_null<VarDecl>(
D)) {
617 const Expr*
E= VD->getInit();
621 if(
T.isTrivialType(VD->getASTContext())) {
622Ctx = VMap->addDefinition(VD,
E, Ctx);
628VMap->saveContext(S, Ctx);
632voidVarMapBuilder::VisitBinaryOperator(
const BinaryOperator*BO) {
639 if(
const auto*DRE = dyn_cast<DeclRefExpr>(LHSExp)) {
641 if(Ctx.lookup(VDec)) {
643Ctx = VMap->updateDefinition(VDec, BO->
getRHS(), Ctx);
646Ctx = VMap->clearDefinition(VDec, Ctx);
647VMap->saveContext(BO, Ctx);
655LocalVariableMap::Context
656LocalVariableMap::intersectContexts(Context C1, Context C2) {
658 for(
const auto&
P: C1) {
660 const unsigned*i2 = C2.lookup(Dec);
662Result = removeDefinition(Dec, Result);
663 else if(*i2 !=
P.second)
664Result = clearDefinition(Dec, Result);
672LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context
C) {
673Context Result = getEmptyContext();
674 for(
const auto&
P:
C)
675Result = addReference(
P.first,
P.second, Result);
682voidLocalVariableMap::intersectBackEdge(Context C1, Context C2) {
683 for(
const auto&
P: C1) {
684 unsignedi1 =
P.second;
685VarDefinition *VDef = &VarDefinitions[i1];
686assert(VDef->isReference());
688 const unsigned*i2 = C2.lookup(
P.first);
689 if(!i2 || (*i2 != i1))
731voidLocalVariableMap::traverseCFG(
CFG*CFGraph,
733std::vector<CFGBlockInfo> &BlockInfo) {
736 for(
const auto*CurrBlock : *SortedGraph) {
737 unsignedCurrBlockID = CurrBlock->getBlockID();
738CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
740VisitedBlocks.insert(CurrBlock);
743 boolHasBackEdges =
false;
744 boolCtxInit =
true;
746PE = CurrBlock->pred_end(); PI != PE; ++PI) {
748 if(*PI ==
nullptr|| !VisitedBlocks.alreadySet(*PI)) {
749HasBackEdges =
true;
753 unsignedPrevBlockID = (*PI)->getBlockID();
754CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
757CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;
761CurrBlockInfo->EntryContext =
762intersectContexts(CurrBlockInfo->EntryContext,
763PrevBlockInfo->ExitContext);
770CurrBlockInfo->EntryContext =
771createReferenceContext(CurrBlockInfo->EntryContext);
774saveContext(
nullptr, CurrBlockInfo->EntryContext);
775CurrBlockInfo->EntryIndex = getContextIndex();
778VarMapBuilder VMapBuilder(
this, CurrBlockInfo->EntryContext);
779 for(
const auto&BI : *CurrBlock) {
780 switch(BI.getKind()) {
783VMapBuilder.Visit(CS.
getStmt());
790CurrBlockInfo->ExitContext = VMapBuilder.Ctx;
794SE = CurrBlock->succ_end(); SI != SE; ++SI) {
796 if(*SI ==
nullptr|| !VisitedBlocks.alreadySet(*SI))
800Context LoopBegin = BlockInfo[FirstLoopBlock->
getBlockID()].EntryContext;
801Context LoopEnd = CurrBlockInfo->ExitContext;
802intersectBackEdge(LoopBegin, LoopEnd);
808saveContext(
nullptr, BlockInfo[exitID].ExitContext);
815std::vector<CFGBlockInfo> &BlockInfo) {
816 for(
const auto*CurrBlock : *SortedGraph) {
817CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];
821 if(
const Stmt*S = CurrBlock->getTerminatorStmt()) {
822CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getBeginLoc();
825BE = CurrBlock->rend(); BI != BE; ++BI) {
827 if(std::optional<CFGStmt> CS = BI->getAs<
CFGStmt>()) {
828CurrBlockInfo->ExitLoc = CS->getStmt()->getBeginLoc();
834 if(CurrBlockInfo->ExitLoc.isValid()) {
837 for(
const auto&BI : *CurrBlock) {
839 if(std::optional<CFGStmt> CS = BI.getAs<
CFGStmt>()) {
840CurrBlockInfo->EntryLoc = CS->getStmt()->getBeginLoc();
844}
else if(CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&
845CurrBlock != &CFGraph->
getExit()) {
848CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
849BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;
850}
else if(CurrBlock->succ_size() == 1 && *CurrBlock->succ_begin()) {
853CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
854BlockInfo[(*CurrBlock->succ_begin())->getBlockID()].EntryLoc;
861classLockableFactEntry :
publicFactEntry {
864SourceKind Src = Acquired)
865: FactEntry(Lockable, CE, LK,
Loc, Src) {}
868handleRemovalFromIntersection(
constFactSet &FSet, FactManager &FactMan,
871 if(!asserted() && !negative() && !isUniversal()) {
877 voidhandleLock(FactSet &FSet, FactManager &FactMan,
constFactEntry &entry,
883 voidhandleUnlock(FactSet &FSet, FactManager &FactMan,
887FSet.removeLock(FactMan, Cp);
889FSet.addLock(FactMan, std::make_unique<LockableFactEntry>(
894 static boolclassof(
constFactEntry *A) {
895 returnA->getFactEntryKind() == Lockable;
899classScopedLockableFactEntry :
publicFactEntry {
901 enumUnderlyingCapabilityKind {
904UCK_ReleasedExclusive,
907 structUnderlyingCapability {
909UnderlyingCapabilityKind
Kind;
919CapExprSet getUnderlyingMutexes()
const{
920CapExprSet UnderlyingMutexesSet;
921 for(
constUnderlyingCapability &UnderlyingMutex : UnderlyingMutexes)
922UnderlyingMutexesSet.push_back(UnderlyingMutex.Cap);
923 returnUnderlyingMutexesSet;
927UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_Acquired});
931UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_ReleasedExclusive});
935UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_ReleasedShared});
939handleRemovalFromIntersection(
constFactSet &FSet, FactManager &FactMan,
945 for(
const auto&UnderlyingMutex : UnderlyingMutexes) {
946 const auto*Entry = FSet.findLock(FactMan, UnderlyingMutex.Cap);
947 if((UnderlyingMutex.Kind == UCK_Acquired && Entry) ||
948(UnderlyingMutex.Kind != UCK_Acquired && !Entry)) {
952UnderlyingMutex.Cap.toString(), loc(),
958 voidhandleLock(FactSet &FSet, FactManager &FactMan,
constFactEntry &entry,
960 for(
const auto&UnderlyingMutex : UnderlyingMutexes) {
961 if(UnderlyingMutex.Kind == UCK_Acquired)
962lock(FSet, FactMan, UnderlyingMutex.Cap, entry.kind(), entry.loc(),
965unlock(FSet, FactMan, UnderlyingMutex.Cap, entry.loc(), &Handler);
969 voidhandleUnlock(FactSet &FSet, FactManager &FactMan,
973assert(!Cp.
negative() &&
"Managing object cannot be negative.");
974 for(
const auto&UnderlyingMutex : UnderlyingMutexes) {
978 if(UnderlyingMutex.Kind == UCK_Acquired) {
979unlock(FSet, FactMan, UnderlyingMutex.Cap, UnlockLoc, TSHandler);
981 LockKind kind= UnderlyingMutex.Kind == UCK_ReleasedShared
984lock(FSet, FactMan, UnderlyingMutex.Cap, kind, UnlockLoc, TSHandler);
988FSet.removeLock(FactMan, Cp);
991 static boolclassof(
constFactEntry *A) {
992 returnA->getFactEntryKind() == ScopedLockable;
996 voidlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr&Cp,
999 if(
constFactEntry *Fact = FSet.findLock(FactMan, Cp)) {
1004FSet.removeLock(FactMan, !Cp);
1005FSet.addLock(FactMan,
1006std::make_unique<LockableFactEntry>(Cp, kind, loc, Managed));
1010 voidunlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr&Cp,
1012 if(FSet.findLock(FactMan, Cp)) {
1013FSet.removeLock(FactMan, Cp);
1014FSet.addLock(FactMan, std::make_unique<LockableFactEntry>(
1016}
else if(Handler) {
1018 if(
constFactEntry *Neg = FSet.findLock(FactMan, !Cp))
1019PrevLoc =
Neg->loc();
1026classThreadSafetyAnalyzer {
1027 friend classBuildLockset;
1030llvm::BumpPtrAllocator Bpa;
1036LocalVariableMap LocalVarMap;
1038llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;
1039FactManager FactMan;
1040std::vector<CFGBlockInfo> BlockInfo;
1046: Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
1050 voidaddLock(FactSet &FSet, std::unique_ptr<FactEntry> Entry,
1051 boolReqAttr =
false);
1055 template<
typenameAttrType>
1056 voidgetMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
const Expr*Exp,
1059 template<
classAttrType>
1060 voidgetMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
const Expr*Exp,
1063 Expr*BrE,
boolNeg);
1065 const CallExpr* getTrylockCallExpr(
const Stmt*Cond, LocalVarContext
C,
1068 voidgetEdgeLockset(FactSet &Result,
constFactSet &ExitSet,
1072 booljoin(
constFactEntry &a,
constFactEntry &
b,
boolCanModify);
1074 voidintersectAndWarn(FactSet &EntrySet,
constFactSet &ExitSet,
1078 voidintersectAndWarn(FactSet &EntrySet,
constFactSet &ExitSet,
1080intersectAndWarn(EntrySet, ExitSet, JoinLoc, LEK, LEK);
1085 voidwarnIfMutexNotHeld(
constFactSet &FSet,
const NamedDecl*
D,
1089 voidwarnIfMutexHeld(
constFactSet &FSet,
const NamedDecl*
D,
const Expr*Exp,
1093 voidcheckAccess(
constFactSet &FSet,
const Expr*Exp,
AccessKindAK,
1095 voidcheckPtAccess(
constFactSet &FSet,
const Expr*Exp,
AccessKindAK,
1103ThreadSafetyAnalyzer& Analyzer) {
1105BeforeInfo *Info =
nullptr;
1109std::unique_ptr<BeforeInfo> &InfoPtr = BMap[Vd];
1111InfoPtr.reset(
newBeforeInfo());
1112Info = InfoPtr.get();
1115 for(
const auto*At : Vd->
attrs()) {
1116 switch(At->getKind()) {
1117 caseattr::AcquiredBefore: {
1118 const auto*A = cast<AcquiredBeforeAttr>(At);
1121 for(
const auto*Arg : A->args()) {
1123Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1125Info->Vect.push_back(Cpvd);
1126 const autoIt = BMap.find(Cpvd);
1127 if(It == BMap.end())
1133 caseattr::AcquiredAfter: {
1134 const auto*A = cast<AcquiredAfterAttr>(At);
1137 for(
const auto*Arg : A->args()) {
1139Analyzer.SxBuilder.translateAttrExpr(Arg,
nullptr);
1143ArgInfo->Vect.push_back(Vd);
1156BeforeSet::BeforeInfo *
1158ThreadSafetyAnalyzer &Analyzer) {
1159 autoIt = BMap.find(Vd);
1160BeforeInfo *Info =
nullptr;
1161 if(It == BMap.end())
1164Info = It->second.get();
1165assert(Info &&
"BMap contained nullptr?");
1171 constFactSet& FSet,
1172ThreadSafetyAnalyzer& Analyzer,
1184 if(Info->Visited == 1)
1187 if(Info->Visited == 2)
1190 if(Info->Vect.empty())
1193InfoVect.push_back(Info);
1195 for(
const auto*Vdb : Info->Vect) {
1197 if(FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) {
1198StringRef L1 = StartVd->
getName();
1199StringRef L2 = Vdb->getName();
1200Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2,
Loc);
1204 if(CycMap.try_emplace(Vd,
true).second) {
1205StringRef L1 = Vd->
getName();
1206Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->
getLocation());
1216 for(
auto*Info : InfoVect)
1222 if(
const auto*CE = dyn_cast<ImplicitCastExpr>(Exp))
1225 if(
const auto*DR = dyn_cast<DeclRefExpr>(Exp))
1226 returnDR->getDecl();
1228 if(
const auto*ME = dyn_cast<MemberExpr>(Exp))
1229 returnME->getMemberDecl();
1236template<
typenameTy>
1237classhas_arg_iterator_range {
1238 usingyes =
char[1];
1239 usingno =
char[2];
1241 template<
typenameInner>
1242 staticyes& test(Inner *I,
decltype(I->args()) * =
nullptr);
1244 template<
typename>
1245 staticno& test(...);
1248 static const boolvalue =
sizeof(test<Ty>(
nullptr)) ==
sizeof(yes);
1253boolThreadSafetyAnalyzer::inCurrentScope(
const CapabilityExpr&CapE) {
1255assert(SExp &&
"Null expressions should be ignored");
1257 if(
const auto*LP = dyn_cast<til::LiteralPtr>(SExp)) {
1270 if(
const auto*
P= dyn_cast<til::Project>(SExp)) {
1271 if(!isa_and_nonnull<CXXMethodDecl>(CurrentFunction))
1282voidThreadSafetyAnalyzer::addLock(FactSet &FSet,
1283std::unique_ptr<FactEntry> Entry,
1285 if(Entry->shouldIgnore())
1288 if(!ReqAttr && !Entry->negative()) {
1291 constFactEntry *Nen = FSet.findLock(FactMan, NegC);
1293FSet.removeLock(FactMan, NegC);
1296 if(inCurrentScope(*Entry) && !Entry->asserted())
1304!Entry->asserted() && !Entry->declared()) {
1305GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *
this,
1306Entry->loc(), Entry->getKind());
1310 if(
constFactEntry *Cp = FSet.findLock(FactMan, *Entry)) {
1311 if(!Entry->asserted())
1312Cp->handleLock(FSet, FactMan, *Entry, Handler);
1314FSet.addLock(FactMan, std::move(Entry));
1320voidThreadSafetyAnalyzer::removeLock(FactSet &FSet,
const CapabilityExpr&Cp,
1322 boolFullyRemove,
LockKindReceivedKind) {
1326 constFactEntry *LDat = FSet.findLock(FactMan, Cp);
1329 if(
constFactEntry *Neg = FSet.findLock(FactMan, !Cp))
1330PrevLoc =
Neg->loc();
1338 if(ReceivedKind !=
LK_Generic&& LDat->kind() != ReceivedKind) {
1340ReceivedKind, LDat->loc(), UnlockLoc);
1343LDat->handleUnlock(FSet, FactMan, Cp, UnlockLoc, FullyRemove, Handler);
1348template<
typenameAttrType>
1349voidThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1352 if(
Attr->args_size() == 0) {
1361Mtxs.push_back_nodup(Cp);
1365 for(
const auto*Arg :
Attr->args()) {
1373Mtxs.push_back_nodup(Cp);
1380template<
classAttrType>
1381voidThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *
Attr,
1385 Expr*BrE,
boolNeg) {
1387 boolbranch =
false;
1388 if(
const auto*BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))
1389branch = BLE->getValue();
1390 else if(
const auto*ILE = dyn_cast_or_null<IntegerLiteral>(BrE))
1391branch = ILE->getValue().getBoolValue();
1393 intbranchnum = branch ? 0 : 1;
1395branchnum = !branchnum;
1400SE = PredBlock->
succ_end(); SI != SE && i < 2; ++SI, ++i) {
1401 if(*SI == CurrBlock && i == branchnum)
1402getMutexIDs(Mtxs,
Attr, Exp,
D);
1407 if(isa<CXXNullPtrLiteralExpr>(
E) || isa<GNUNullExpr>(
E)) {
1410}
else if(
const auto*BLE = dyn_cast<CXXBoolLiteralExpr>(
E)) {
1411TCond = BLE->getValue();
1413}
else if(
const auto*ILE = dyn_cast<IntegerLiteral>(
E)) {
1414TCond = ILE->getValue().getBoolValue();
1416}
else if(
auto*CE = dyn_cast<ImplicitCastExpr>(
E))
1424const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(
const Stmt*Cond,
1430 if(
const auto*CallExp = dyn_cast<CallExpr>(Cond)) {
1431 if(CallExp->getBuiltinCallee() == Builtin::BI__builtin_expect)
1432 returngetTrylockCallExpr(CallExp->getArg(0),
C, Negate);
1435 else if(
const auto*PE = dyn_cast<ParenExpr>(Cond))
1436 returngetTrylockCallExpr(PE->getSubExpr(),
C, Negate);
1437 else if(
const auto*CE = dyn_cast<ImplicitCastExpr>(Cond))
1438 returngetTrylockCallExpr(CE->getSubExpr(),
C, Negate);
1439 else if(
const auto*FE = dyn_cast<FullExpr>(Cond))
1440 returngetTrylockCallExpr(FE->getSubExpr(),
C, Negate);
1441 else if(
const auto*DRE = dyn_cast<DeclRefExpr>(Cond)) {
1442 const Expr*
E= LocalVarMap.lookupExpr(DRE->getDecl(),
C);
1443 returngetTrylockCallExpr(
E,
C, Negate);
1445 else if(
const auto*UOP = dyn_cast<UnaryOperator>(Cond)) {
1446 if(UOP->getOpcode() == UO_LNot) {
1448 returngetTrylockCallExpr(UOP->getSubExpr(),
C, Negate);
1452 else if(
const auto*BOP = dyn_cast<BinaryOperator>(Cond)) {
1453 if(BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
1454 if(BOP->getOpcode() == BO_NE)
1457 boolTCond =
false;
1459 if(!TCond) Negate = !Negate;
1460 returngetTrylockCallExpr(BOP->getLHS(),
C, Negate);
1464 if(!TCond) Negate = !Negate;
1465 returngetTrylockCallExpr(BOP->getRHS(),
C, Negate);
1469 if(BOP->getOpcode() == BO_LAnd) {
1471 returngetTrylockCallExpr(BOP->getRHS(),
C, Negate);
1473 if(BOP->getOpcode() == BO_LOr)
1474 returngetTrylockCallExpr(BOP->getRHS(),
C, Negate);
1476}
else if(
const auto*COP = dyn_cast<ConditionalOperator>(Cond)) {
1480 if(TCond && !FCond)
1481 returngetTrylockCallExpr(COP->getCond(),
C, Negate);
1482 if(!TCond && FCond) {
1484 returngetTrylockCallExpr(COP->getCond(),
C, Negate);
1494voidThreadSafetyAnalyzer::getEdgeLockset(FactSet&
Result,
1495 constFactSet &ExitSet,
1505 boolNegate =
false;
1506 constCFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->
getBlockID()];
1507 constLocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
1509 const auto*Exp = getTrylockCallExpr(Cond, LVarCtx, Negate);
1513 auto*FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
1514 if(!FunDecl || !FunDecl->hasAttrs())
1517CapExprSet ExclusiveLocksToAdd;
1518CapExprSet SharedLocksToAdd;
1521 for(
const auto*
Attr: FunDecl->attrs()) {
1523 caseattr::TryAcquireCapability: {
1524 auto*A = cast<TryAcquireCapabilityAttr>(
Attr);
1525getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
1526Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(),
1530 caseattr::ExclusiveTrylockFunction: {
1531 const auto*A = cast<ExclusiveTrylockFunctionAttr>(
Attr);
1532getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
1533A->getSuccessValue(), Negate);
1536 caseattr::SharedTrylockFunction: {
1537 const auto*A = cast<SharedTrylockFunctionAttr>(
Attr);
1538getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,
1539A->getSuccessValue(), Negate);
1549 for(
const auto&ExclusiveLockToAdd : ExclusiveLocksToAdd)
1550addLock(
Result, std::make_unique<LockableFactEntry>(ExclusiveLockToAdd,
1552 for(
const auto&SharedLockToAdd : SharedLocksToAdd)
1553addLock(
Result, std::make_unique<LockableFactEntry>(SharedLockToAdd,
1565 friend classThreadSafetyAnalyzer;
1567ThreadSafetyAnalyzer *Analyzer;
1570 constFactSet &FunctionExitFSet;
1571LocalVariableMap::Context LVarCtx;
1578Analyzer->checkAccess(FSet, Exp, AK, POK);
1582Analyzer->checkPtAccess(FSet, Exp, AK, POK);
1591 boolSkipFirstParam =
false);
1594BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info,
1595 constFactSet &FunctionExitFSet)
1597FunctionExitFSet(FunctionExitFSet), LVarCtx(Info.EntryContext),
1598CtxIndex(Info.EntryIndex) {}
1602 voidVisitCastExpr(
const CastExpr*CE);
1603 voidVisitCallExpr(
const CallExpr*Exp);
1605 voidVisitDeclStmt(
const DeclStmt*S);
1614voidThreadSafetyAnalyzer::warnIfMutexNotHeld(
1629 constFactEntry *LDat = FSet.findLock(FactMan, !Cp);
1632(!Cp).toString(),
Loc);
1638 if(!inCurrentScope(Cp))
1642LDat = FSet.findLock(FactMan, Cp);
1649 constFactEntry *LDat = FSet.findLockUniv(FactMan, Cp);
1650 boolNoError =
true;
1653LDat = FSet.findPartialMatch(FactMan, Cp);
1656std::string PartMatchStr = LDat->toString();
1657StringRef PartMatchName(PartMatchStr);
1667 if(NoError && LDat && !LDat->isAtLeast(LK)) {
1673voidThreadSafetyAnalyzer::warnIfMutexHeld(
constFactSet &FSet,
1686 constFactEntry *LDat = FSet.findLock(FactMan, Cp);
1698voidThreadSafetyAnalyzer::checkAccess(
constFactSet &FSet,
const Expr*Exp,
1707 while(
const auto*DRE = dyn_cast<DeclRefExpr>(Exp)) {
1708 const auto*VD = dyn_cast<VarDecl>(DRE->getDecl()->getCanonicalDecl());
1710 if(
const auto*
E= VD->getInit()) {
1721 if(
const auto*UO = dyn_cast<UnaryOperator>(Exp)) {
1723 if(UO->getOpcode() == UO_Deref)
1724checkPtAccess(FSet, UO->getSubExpr(), AK, POK);
1728 if(
const auto*BO = dyn_cast<BinaryOperator>(Exp)) {
1731 returncheckAccess(FSet, BO->
getLHS(), AK, POK);
1733 returncheckPtAccess(FSet, BO->
getLHS(), AK, POK);
1739 if(
const auto*AE = dyn_cast<ArraySubscriptExpr>(Exp)) {
1740checkPtAccess(FSet, AE->getLHS(), AK, POK);
1744 if(
const auto*ME = dyn_cast<MemberExpr>(Exp)) {
1746checkPtAccess(FSet, ME->getBase(), AK, POK);
1748checkAccess(FSet, ME->getBase(), AK, POK);
1755 if(
D->
hasAttr<GuardedVarAttr>() && FSet.isEmpty(FactMan)) {
1760warnIfMutexNotHeld(FSet,
D, Exp, AK, I->getArg(), POK,
nullptr,
Loc);
1765voidThreadSafetyAnalyzer::checkPtAccess(
constFactSet &FSet,
const Expr*Exp,
1769 if(
const auto*PE = dyn_cast<ParenExpr>(Exp)) {
1770Exp = PE->getSubExpr();
1773 if(
const auto*CE = dyn_cast<CastExpr>(Exp)) {
1774 if(CE->getCastKind() == CK_ArrayToPointerDecay) {
1777checkAccess(FSet, CE->getSubExpr(), AK, POK);
1780Exp = CE->getSubExpr();
1796 if(
D->
hasAttr<PtGuardedVarAttr>() && FSet.isEmpty(FactMan))
1800warnIfMutexNotHeld(FSet,
D, Exp, AK, I->getArg(), PtPOK,
nullptr,
1819voidBuildLockset::handleCall(
const Expr*Exp,
const NamedDecl*
D,
1821CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
1822CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
1823CapExprSet ScopedReqsAndExcludes;
1831std::pair<til::LiteralPtr *, StringRef> Placeholder =
1832Analyzer->SxBuilder.createThisPlaceholder(Exp);
1833[[maybe_unused]]
autoinserted =
1834Analyzer->ConstructedObjects.insert({Exp, Placeholder.first});
1835assert(inserted.second &&
"Are we visiting the same expression again?");
1836 if(isa<CXXConstructExpr>(Exp))
1837 Self= Placeholder.first;
1838 if(TagT->getDecl()->hasAttr<ScopedLockableAttr>())
1839Scp =
CapabilityExpr(Placeholder.first, Placeholder.second,
false);
1847 switch(At->getKind()) {
1850 caseattr::AcquireCapability: {
1851 const auto*A = cast<AcquireCapabilityAttr>(At);
1852Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
1853: ExclusiveLocksToAdd,
1861 caseattr::AssertExclusiveLock: {
1862 const auto*A = cast<AssertExclusiveLockAttr>(At);
1864CapExprSet AssertLocks;
1865Analyzer->getMutexIDs(AssertLocks, A, Exp,
D,
Self);
1866 for(
const auto&AssertLock : AssertLocks)
1868FSet, std::make_unique<LockableFactEntry>(
1872 caseattr::AssertSharedLock: {
1873 const auto*A = cast<AssertSharedLockAttr>(At);
1875CapExprSet AssertLocks;
1876Analyzer->getMutexIDs(AssertLocks, A, Exp,
D,
Self);
1877 for(
const auto&AssertLock : AssertLocks)
1879FSet, std::make_unique<LockableFactEntry>(
1884 caseattr::AssertCapability: {
1885 const auto*A = cast<AssertCapabilityAttr>(At);
1886CapExprSet AssertLocks;
1887Analyzer->getMutexIDs(AssertLocks, A, Exp,
D,
Self);
1888 for(
const auto&AssertLock : AssertLocks)
1889Analyzer->addLock(FSet, std::make_unique<LockableFactEntry>(
1892 Loc, FactEntry::Asserted));
1898 caseattr::ReleaseCapability: {
1899 const auto*A = cast<ReleaseCapabilityAttr>(At);
1901Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp,
D,
Self);
1902 else if(A->isShared())
1903Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp,
D,
Self);
1905Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp,
D,
Self);
1909 caseattr::RequiresCapability: {
1910 const auto*A = cast<RequiresCapabilityAttr>(At);
1911 for(
auto*Arg : A->args()) {
1912Analyzer->warnIfMutexNotHeld(FSet,
D, Exp,
1917Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp,
D,
Self);
1922 caseattr::LocksExcluded: {
1923 const auto*A = cast<LocksExcludedAttr>(At);
1924 for(
auto*Arg : A->args()) {
1925Analyzer->warnIfMutexHeld(FSet,
D, Exp, Arg,
Self,
Loc);
1928Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp,
D,
Self);
1939std::optional<CallExpr::const_arg_range> Args;
1941 if(
const auto*CE = dyn_cast<CallExpr>(Exp))
1942Args = CE->arguments();
1943 else if(
const auto*CE = dyn_cast<CXXConstructExpr>(Exp))
1944Args = CE->arguments();
1946llvm_unreachable(
"Unknown call kind");
1948 const auto*CalledFunction = dyn_cast<FunctionDecl>(
D);
1949 if(CalledFunction && Args.has_value()) {
1950 for(
auto[Param, Arg] : zip(CalledFunction->parameters(), *Args)) {
1951CapExprSet DeclaredLocks;
1952 for(
const Attr*At : Param->attrs()) {
1953 switch(At->getKind()) {
1954 caseattr::AcquireCapability: {
1955 const auto*A = cast<AcquireCapabilityAttr>(At);
1956Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd
1957: ExclusiveLocksToAdd,
1959Analyzer->getMutexIDs(DeclaredLocks, A, Exp,
D,
Self);
1963 caseattr::ReleaseCapability: {
1964 const auto*A = cast<ReleaseCapabilityAttr>(At);
1966Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp,
D,
Self);
1967 else if(A->isShared())
1968Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp,
D,
Self);
1970Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp,
D,
Self);
1971Analyzer->getMutexIDs(DeclaredLocks, A, Exp,
D,
Self);
1975 caseattr::RequiresCapability: {
1976 const auto*A = cast<RequiresCapabilityAttr>(At);
1977 for(
auto*Arg : A->args())
1978Analyzer->warnIfMutexNotHeld(FSet,
D, Exp,
1981Analyzer->getMutexIDs(DeclaredLocks, A, Exp,
D,
Self);
1985 caseattr::LocksExcluded: {
1986 const auto*A = cast<LocksExcludedAttr>(At);
1987 for(
auto*Arg : A->args())
1988Analyzer->warnIfMutexHeld(FSet,
D, Exp, Arg,
Self,
Loc);
1989Analyzer->getMutexIDs(DeclaredLocks, A, Exp,
D,
Self);
1997 if(DeclaredLocks.empty())
2000StringRef(
"mutex"),
false);
2001 if(
const auto*CBTE = dyn_cast<CXXBindTemporaryExpr>(Arg->IgnoreCasts());
2003 if(
autoObject = Analyzer->ConstructedObjects.find(CBTE->getSubExpr());
2004Object != Analyzer->ConstructedObjects.end())
2007 constFactEntry *Fact = FSet.findLock(Analyzer->FactMan, Cp);
2014 const auto*
Scope= cast<ScopedLockableFactEntry>(Fact);
2015 for(
const auto&[a,
b] :
2016zip_longest(DeclaredLocks,
Scope->getUnderlyingMutexes())) {
2017 if(!a.has_value()) {
2018Analyzer->Handler.handleExpectFewerUnderlyingMutexes(
2020 b.value().getKind(),
b.value().toString());
2021}
else if(!
b.has_value()) {
2022Analyzer->Handler.handleExpectMoreUnderlyingMutexes(
2024a.value().getKind(), a.value().toString());
2025}
else if(!a.value().equals(
b.value())) {
2026Analyzer->Handler.handleUnmatchedUnderlyingMutexes(
2028a.value().getKind(), a.value().toString(),
b.value().toString());
2036 boolDtor = isa<CXXDestructorDecl>(
D);
2037 for(
const auto&M : ExclusiveLocksToRemove)
2039 for(
const auto&M : SharedLocksToRemove)
2041 for(
const auto&M : GenericLocksToRemove)
2045FactEntry::SourceKind Source =
2046!Scp.
shouldIgnore() ? FactEntry::Managed : FactEntry::Acquired;
2047 for(
const auto&M : ExclusiveLocksToAdd)
2048Analyzer->addLock(FSet, std::make_unique<LockableFactEntry>(M,
LK_Exclusive,
2050 for(
const auto&M : SharedLocksToAdd)
2052FSet, std::make_unique<LockableFactEntry>(M,
LK_Shared,
Loc, Source));
2056 autoScopedEntry = std::make_unique<ScopedLockableFactEntry>(
2057Scp,
Loc, FactEntry::Acquired);
2058 for(
const auto&M : ExclusiveLocksToAdd)
2059ScopedEntry->addLock(M);
2060 for(
const auto&M : SharedLocksToAdd)
2061ScopedEntry->addLock(M);
2062 for(
const auto&M : ScopedReqsAndExcludes)
2063ScopedEntry->addLock(M);
2064 for(
const auto&M : ExclusiveLocksToRemove)
2065ScopedEntry->addExclusiveUnlock(M);
2066 for(
const auto&M : SharedLocksToRemove)
2067ScopedEntry->addSharedUnlock(M);
2068Analyzer->addLock(FSet, std::move(ScopedEntry));
2075voidBuildLockset::VisitUnaryOperator(
const UnaryOperator*UO) {
2091voidBuildLockset::VisitBinaryOperator(
const BinaryOperator*BO) {
2096LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
2104voidBuildLockset::VisitCastExpr(
const CastExpr*CE) {
2110voidBuildLockset::examineArguments(
const FunctionDecl*FD,
2113 boolSkipFirstParam) {
2123 if(FD->
hasAttr<NoThreadSafetyAnalysisAttr>())
2127 autoParam = Params.begin();
2132 for(
autoArg = ArgBegin; Param != Params.end() && Arg != ArgEnd;
2140voidBuildLockset::VisitCallExpr(
const CallExpr*Exp) {
2141 if(
const auto*CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
2142 const auto*ME = dyn_cast<MemberExpr>(CE->getCallee());
2147 if(ME->isArrow()) {
2149checkPtAccess(CE->getImplicitObjectArgument(),
AK_Read);
2152checkAccess(CE->getImplicitObjectArgument(),
AK_Read);
2156examineArguments(CE->getDirectCallee(), CE->arg_begin(), CE->arg_end());
2157}
else if(
const auto*OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
2165 caseOO_PercentEqual:
2169 caseOO_LessLessEqual:
2170 caseOO_GreaterGreaterEqual:
2171checkAccess(OE->getArg(1),
AK_Read);
2181 if(!(OEop == OO_Star && OE->getNumArgs() > 1)) {
2183checkPtAccess(OE->getArg(0),
AK_Read);
2188 const Expr*Obj = OE->getArg(0);
2194examineArguments(FD, std::next(OE->arg_begin()), OE->arg_end(),
2195!isa<CXXMethodDecl>(FD));
2206handleCall(Exp,
D);
2211 if(
D&&
D->isCopyConstructor()) {
2213checkAccess(Source,
AK_Read);
2218handleCall(Exp,
D);
2222 if(
auto*CE = dyn_cast<CastExpr>(
E))
2225 if(
auto*CE = dyn_cast<CastExpr>(
E))
2226 if(CE->
getCastKind() == CK_ConstructorConversion ||
2229 if(
auto*BTE = dyn_cast<CXXBindTemporaryExpr>(
E))
2230 E= BTE->getSubExpr();
2234voidBuildLockset::VisitDeclStmt(
const DeclStmt*S) {
2236LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
2238 for(
auto*
D: S->getDeclGroup()) {
2239 if(
auto*VD = dyn_cast_or_null<VarDecl>(
D)) {
2240 const Expr*
E= VD->getInit();
2246 if(
auto*EWC = dyn_cast<ExprWithCleanups>(
E))
2250 if(
autoObject = Analyzer->ConstructedObjects.find(
E);
2251Object != Analyzer->ConstructedObjects.end()) {
2252 Object->second->setClangDecl(VD);
2253Analyzer->ConstructedObjects.erase(Object);
2259voidBuildLockset::VisitMaterializeTemporaryExpr(
2262 if(
autoObject = Analyzer->ConstructedObjects.find(
2264Object != Analyzer->ConstructedObjects.end()) {
2265 Object->second->setClangDecl(ExtD);
2266Analyzer->ConstructedObjects.erase(Object);
2271voidBuildLockset::VisitReturnStmt(
const ReturnStmt*S) {
2272 if(Analyzer->CurrentFunction ==
nullptr)
2274 const Expr*RetVal = S->getRetValue();
2281Analyzer->CurrentFunction->getReturnType().getCanonicalType();
2283Analyzer->checkAccess(
2284FunctionExitFSet, RetVal,
2295boolThreadSafetyAnalyzer::join(
constFactEntry &A,
constFactEntry &B,
2297 if(A.kind() != B.kind()) {
2300 if((A.managed() || A.asserted()) && (B.managed() || B.asserted())) {
2302 boolShouldTakeB = B.kind() ==
LK_Shared;
2303 if(CanModify || !ShouldTakeB)
2312 returnCanModify && A.asserted() && !B.asserted();
2330voidThreadSafetyAnalyzer::intersectAndWarn(FactSet &EntrySet,
2331 constFactSet &ExitSet,
2335FactSet EntrySetOrig = EntrySet;
2338 for(
const auto&Fact : ExitSet) {
2339 constFactEntry &ExitFact = FactMan[Fact];
2341FactSet::iterator EntryIt = EntrySet.findLockIter(FactMan, ExitFact);
2342 if(EntryIt != EntrySet.end()) {
2343 if(join(FactMan[*EntryIt], ExitFact,
2347ExitFact.handleRemovalFromIntersection(ExitSet, FactMan, JoinLoc,
2353 for(
const auto&Fact : EntrySetOrig) {
2354 constFactEntry *EntryFact = &FactMan[Fact];
2355 constFactEntry *ExitFact = ExitSet.findLock(FactMan, *EntryFact);
2360EntryFact->handleRemovalFromIntersection(EntrySetOrig, FactMan, JoinLoc,
2363EntrySet.removeLock(FactMan, *EntryFact);
2376 if(std::optional<CFGStmt> S =
Last.getAs<
CFGStmt>()) {
2377 if(isa<CXXThrowExpr>(S->getStmt()))
2392 if(!walker.
init(AC))
2400CurrentFunction = dyn_cast<FunctionDecl>(
D);
2402 if(
D->
hasAttr<NoThreadSafetyAnalysisAttr>())
2409 if(isa<CXXConstructorDecl>(
D))
2411 if(isa<CXXDestructorDecl>(
D))
2417CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));
2429Initial.Reachable =
true;
2432LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);
2437CapExprSet ExclusiveLocksAcquired;
2438CapExprSet SharedLocksAcquired;
2439CapExprSet LocksReleased;
2444 if(!SortedGraph->
empty()) {
2446FactSet &InitialLockset = Initial.EntrySet;
2448CapExprSet ExclusiveLocksToAdd;
2449CapExprSet SharedLocksToAdd;
2454 if(
const auto*A = dyn_cast<RequiresCapabilityAttr>(
Attr)) {
2455getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2457}
else if(
const auto*A = dyn_cast<ReleaseCapabilityAttr>(
Attr)) {
2460 if(A->args_size() == 0)
2462getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2464getMutexIDs(LocksReleased, A,
nullptr,
D);
2465}
else if(
const auto*A = dyn_cast<AcquireCapabilityAttr>(
Attr)) {
2466 if(A->args_size() == 0)
2468getMutexIDs(A->isShared() ? SharedLocksAcquired
2469: ExclusiveLocksAcquired,
2471}
else if(isa<ExclusiveTrylockFunctionAttr>(
Attr)) {
2474}
else if(isa<SharedTrylockFunctionAttr>(
Attr)) {
2477}
else if(isa<TryAcquireCapabilityAttr>(
Attr)) {
2483 if(CurrentFunction)
2485 else if(
autoCurrentMethod = dyn_cast<ObjCMethodDecl>(
D))
2486Params = CurrentMethod->getCanonicalDecl()->parameters();
2488llvm_unreachable(
"Unknown function kind");
2490CapExprSet UnderlyingLocks;
2491 for(
const auto*
Attr: Param->attrs()) {
2493 if(
const auto*A = dyn_cast<ReleaseCapabilityAttr>(
Attr)) {
2494getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2496getMutexIDs(LocksReleased, A,
nullptr, Param);
2497getMutexIDs(UnderlyingLocks, A,
nullptr, Param);
2498}
else if(
const auto*A = dyn_cast<RequiresCapabilityAttr>(
Attr)) {
2499getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2501getMutexIDs(UnderlyingLocks, A,
nullptr, Param);
2502}
else if(
const auto*A = dyn_cast<AcquireCapabilityAttr>(
Attr)) {
2503getMutexIDs(A->isShared() ? SharedLocksAcquired
2504: ExclusiveLocksAcquired,
2505A,
nullptr, Param);
2506getMutexIDs(UnderlyingLocks, A,
nullptr, Param);
2507}
else if(
const auto*A = dyn_cast<LocksExcludedAttr>(
Attr)) {
2508getMutexIDs(UnderlyingLocks, A,
nullptr, Param);
2511 if(UnderlyingLocks.empty())
2513 CapabilityExprCp(SxBuilder.createVariable(Param), StringRef(),
false);
2514 autoScopedEntry = std::make_unique<ScopedLockableFactEntry>(
2515Cp, Param->getLocation(), FactEntry::Declared);
2517ScopedEntry->addLock(M);
2518addLock(InitialLockset, std::move(ScopedEntry),
true);
2522 for(
const auto&Mu : ExclusiveLocksToAdd) {
2523 autoEntry = std::make_unique<LockableFactEntry>(Mu,
LK_Exclusive,
Loc,
2524FactEntry::Declared);
2525addLock(InitialLockset, std::move(Entry),
true);
2527 for(
const auto&Mu : SharedLocksToAdd) {
2528 autoEntry = std::make_unique<LockableFactEntry>(Mu,
LK_Shared,
Loc,
2529FactEntry::Declared);
2530addLock(InitialLockset, std::move(Entry),
true);
2536FactSet ExpectedFunctionExitSet = Initial.EntrySet;
2542 for(
const auto&Lock : ExclusiveLocksAcquired)
2543ExpectedFunctionExitSet.addLock(
2544FactMan, std::make_unique<LockableFactEntry>(Lock,
LK_Exclusive,
2546 for(
const auto&Lock : SharedLocksAcquired)
2547ExpectedFunctionExitSet.addLock(
2550 for(
const auto&Lock : LocksReleased)
2551ExpectedFunctionExitSet.removeLock(FactMan, Lock);
2553 for(
const auto*CurrBlock : *SortedGraph) {
2554 unsignedCurrBlockID = CurrBlock->
getBlockID();
2555CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
2558VisitedBlocks.insert(CurrBlock);
2573 boolLocksetInitialized =
false;
2575PE = CurrBlock->
pred_end(); PI != PE; ++PI) {
2577 if(*PI ==
nullptr|| !VisitedBlocks.alreadySet(*PI))
2580 unsignedPrevBlockID = (*PI)->getBlockID();
2581CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
2588CurrBlockInfo->Reachable =
true;
2590FactSet PrevLockset;
2591getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);
2593 if(!LocksetInitialized) {
2594CurrBlockInfo->EntrySet = PrevLockset;
2595LocksetInitialized =
true;
2601CurrBlockInfo->EntrySet, PrevLockset, CurrBlockInfo->EntryLoc,
2602isa_and_nonnull<ContinueStmt>((*PI)->getTerminatorStmt())
2609 if(!CurrBlockInfo->Reachable)
2612BuildLockset LocksetBuilder(
this, *CurrBlockInfo, ExpectedFunctionExitSet);
2615 for(
const auto&BI : *CurrBlock) {
2616 switch(BI.getKind()) {
2619LocksetBuilder.Visit(CS.
getStmt());
2626 if(!DD->hasAttrs())
2629LocksetBuilder.handleCall(
nullptr, DD,
2637LocksetBuilder.handleCall(
nullptr,
CF.getFunctionDecl(),
2638SxBuilder.createVariable(
CF.getVarDecl()),
2639 CF.getVarDecl()->getLocation());
2648 if(
autoObject = ConstructedObjects.find(
2649TD.getBindTemporaryExpr()->getSubExpr());
2650 Object!= ConstructedObjects.end()) {
2654LocksetBuilder.handleCall(
nullptr, DD,
Object->second,
2655TD.getBindTemporaryExpr()->getEndLoc());
2656ConstructedObjects.erase(Object);
2664CurrBlockInfo->ExitSet = LocksetBuilder.FSet;
2671SE = CurrBlock->succ_end(); SI != SE; ++SI) {
2673 if(*SI ==
nullptr|| !VisitedBlocks.alreadySet(*SI))
2677CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->
getBlockID()];
2678CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];
2679intersectAndWarn(PreLoop->EntrySet, LoopEnd->ExitSet, PreLoop->EntryLoc,
2685 if(!Final.Reachable)
2689intersectAndWarn(ExpectedFunctionExitSet, Final.ExitSet, Final.ExitLoc,
2705ThreadSafetyAnalyzer Analyzer(Handler, *BSet);
2706Analyzer.runAnalysis(AC);
2720llvm_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.
enum clang::sema::@1704::IndirectLocalPathEntry::EntryKind Kind
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.
llvm::DenseSet< const void * > Visited
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
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 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.
Attr - This represents one attribute.
attr::Kind getKind() const
SourceLocation getLocation() const
A builtin binary operation expression such as "x + y" or "x <= y".
static bool isAssignmentOp(Opcode Opc)
Represents C++ object destructor implicitly generated for automatic object or temporary bound to cons...
const VarDecl * getVarDecl() const
const Stmt * getTriggerStmt() const
Represents a single basic block in a source-level CFG.
bool hasNoReturnElement() const
succ_iterator succ_begin()
Stmt * getTerminatorStmt()
AdjacentBlocks::const_iterator const_pred_iterator
pred_iterator pred_begin()
unsigned getBlockID() const
Stmt * getTerminatorCondition(bool StripParens=true)
AdjacentBlocks::const_iterator const_succ_iterator
Represents a top-level expression in a basic block.
T castAs() const
Convert to the specified CFGElement type, asserting that this CFGElement is of the desired type.
const CXXDestructorDecl * getDestructorDecl(ASTContext &astContext) const
const Stmt * getStmt() const
Represents C++ object destructor implicitly generated at the end of full expression for temporary obj...
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).
Represents a call to a C++ constructor.
Expr * getArg(unsigned Arg)
Return the specified argument.
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Represents a C++ constructor within a class.
Represents a static or instance method of a struct/union/class.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
CastKind getCastKind() const
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
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 * 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.
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Represents a function declaration or definition.
ArrayRef< ParmVarDecl * > parameters() const
FunctionDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Represents a prvalue temporary that is written into memory so that a reference can bind to it.
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.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Represents a parameter to a function.
Implements a set of CFGBlocks using a BitVector.
A (possibly-)qualified type.
bool isConstQualified() const
Determine whether this type is const-qualified.
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Scope - A scope is a transient data structure that is used while parsing the program.
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 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>'.
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
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
Handler class for thread safety warnings.
virtual ~ThreadSafetyHandler()
virtual void handleInvalidLockExp(SourceLocation Loc)
Warn about lock expressions which fail to resolve to lockable objects.
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 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 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 handleMutexHeldEndOfScope(StringRef Kind, Name LockName, SourceLocation LocLocked, SourceLocation LocEndOfScope, LockErrorKind LEK)
Warn about situations where a mutex is sometimes held and sometimes not.
virtual void handleDoubleLock(StringRef Kind, Name LockName, SourceLocation LocLocked, SourceLocation LocDoubleLock)
Warn about lock function calls for locks which are already held.
A Literal pointer to an object allocated in memory.
Base class for AST nodes in the typed intermediate language.
internal::Matcher< T > traverse(TraversalKind TK, const internal::Matcher< T > &InnerMatcher)
Causes all nested matchers to be matched with the specified traversal kind.
unsigned kind
All of the diagnostics that can be emitted by the frontend.
@ CF
Indicates that the tracked object is a CF object.
bool Dec(InterpState &S, CodePtr OpPC)
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)
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.
@ LEK_NotLockedAtEndOfFunction
@ LEK_LockedSomePredecessors
@ LEK_LockedAtEndOfFunction
@ LEK_LockedSomeLoopIterations
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_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_VarAccess
Reading or writing a variable (e.g. x in x = 5;)
@ POK_FunctionCall
Making a function call (e.g. fool())
@ 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.
@ Self
'self' clause, allowed on Compute and Combined Constructs, plus 'update'.
@ Result
The result type of a method or function.
const FunctionProtoType * T
Iterator for iterating over Stmt * arrays that contain only T *.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4