3939#include " llvm/ADT/DenseMap.h"
4040#include " llvm/ADT/ImmutableMap.h"
4141#include " llvm/ADT/STLExtras.h"
42+ #include " llvm/ADT/ScopeExit.h"
4243#include " llvm/ADT/SmallVector.h"
4344#include " llvm/ADT/StringRef.h"
4445#include " llvm/Support/Allocator.h"
@@ -537,6 +538,13 @@ class LocalVariableMap {
537538protected:
538539 friend class VarMapBuilder ;
539540
541+ // Resolve any definition ID down to its non-reference base ID.
542+ unsigned getCanonicalDefinitionID (unsigned ID) {
543+ while (ID > 0 && VarDefinitions[ID].isReference ())
544+ ID = VarDefinitions[ID].Ref ;
545+ return ID;
546+ }
547+
540548 // Get the current context index
541549 unsigned getContextIndex () { return SavedContexts.size ()-1 ; }
542550
@@ -621,6 +629,7 @@ class VarMapBuilder : public ConstStmtVisitor<VarMapBuilder> {
621629
622630 void VisitDeclStmt (const DeclStmt *S);
623631 void VisitBinaryOperator (const BinaryOperator *BO);
632+ void VisitCallExpr (const CallExpr *CE);
624633};
625634
626635} // namespace
@@ -666,6 +675,56 @@ void VarMapBuilder::VisitBinaryOperator(const BinaryOperator *BO) {
666675 }
667676}
668677
678+ // Invalidates local variable definitions if variable escaped.
679+ void VarMapBuilder::VisitCallExpr (const CallExpr *CE) {
680+ const FunctionDecl *FD = CE->getDirectCallee ();
681+ if (!FD)
682+ return ;
683+
684+ // Heuristic for likely-benign functions that pass by mutable reference. This
685+ // is needed to avoid a slew of false positives due to mutable reference
686+ // passing where the captured reference is usually passed on by-value.
687+ if (const IdentifierInfo *II = FD->getIdentifier ()) {
688+ // Any kind of std::bind-like functions.
689+ if (II->isStr (" bind" ) || II->isStr (" bind_front" ))
690+ return ;
691+ }
692+
693+ // Invalidate local variable definitions that are passed by non-const
694+ // reference or non-const pointer.
695+ for (unsigned Idx = 0 ; Idx < CE->getNumArgs (); ++Idx) {
696+ if (Idx >= FD->getNumParams ())
697+ break ;
698+
699+ const Expr *Arg = CE->getArg (Idx)->IgnoreParenImpCasts ();
700+ const ParmVarDecl *PVD = FD->getParamDecl (Idx);
701+ QualType ParamType = PVD->getType ();
702+
703+ // Potential reassignment if passed by non-const reference / pointer.
704+ const ValueDecl *VDec = nullptr ;
705+ if (ParamType->isReferenceType () &&
706+ !ParamType->getPointeeType ().isConstQualified ()) {
707+ if (const auto *DRE = dyn_cast<DeclRefExpr>(Arg))
708+ VDec = DRE->getDecl ();
709+ } else if (ParamType->isPointerType () &&
710+ !ParamType->getPointeeType ().isConstQualified ()) {
711+ Arg = Arg->IgnoreParenCasts ();
712+ if (const auto *UO = dyn_cast<UnaryOperator>(Arg)) {
713+ if (UO->getOpcode () == UO_AddrOf) {
714+ const Expr *SubE = UO->getSubExpr ()->IgnoreParenCasts ();
715+ if (const auto *DRE = dyn_cast<DeclRefExpr>(SubE))
716+ VDec = DRE->getDecl ();
717+ }
718+ }
719+ }
720+
721+ if (VDec && Ctx.lookup (VDec)) {
722+ Ctx = VMap->clearDefinition (VDec, Ctx);
723+ VMap->saveContext (CE, Ctx);
724+ }
725+ }
726+ }
727+
669728// Computes the intersection of two contexts. The intersection is the
670729// set of variables which have the same definition in both contexts;
671730// variables with different definitions are discarded.
@@ -674,11 +733,16 @@ LocalVariableMap::intersectContexts(Context C1, Context C2) {
674733 Context Result = C1;
675734 for (const auto &P : C1) {
676735 const NamedDecl *Dec = P.first ;
677- const unsigned *i2 = C2.lookup (Dec);
678- if (!i2) // variable doesn't exist on second path
736+ const unsigned *I2 = C2.lookup (Dec);
737+ if (!I2) {
738+ // The variable doesn't exist on second path.
679739 Result = removeDefinition (Dec, Result);
680- else if (*i2 != P.second ) // variable exists, but has different definition
740+ } else if (getCanonicalDefinitionID (P.second ) !=
741+ getCanonicalDefinitionID (*I2)) {
742+ // If canonical definitions mismatch the underlying definitions are
743+ // different, invalidate.
681744 Result = clearDefinition (Dec, Result);
745+ }
682746 }
683747 return Result;
684748}
@@ -698,13 +762,22 @@ LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
698762// createReferenceContext.
699763void LocalVariableMap::intersectBackEdge (Context C1, Context C2) {
700764 for (const auto &P : C1) {
701- unsigned i1 = P.second ;
702- VarDefinition *VDef = &VarDefinitions[i1 ];
765+ const unsigned I1 = P.second ;
766+ VarDefinition *VDef = &VarDefinitions[I1 ];
703767 assert (VDef->isReference ());
704768
705- const unsigned *i2 = C2.lookup (P.first );
706- if (!i2 || (*i2 != i1))
707- VDef->Ref = 0 ; // Mark this variable as undefined
769+ const unsigned *I2 = C2.lookup (P.first );
770+ if (!I2) {
771+ // Variable does not exist at the end of the loop, invalidate.
772+ VDef->Ref = 0 ;
773+ continue ;
774+ }
775+
776+ // Compare the canonical IDs. This correctly handles chains of references
777+ // and determines if the variable is truly loop-invariant.
778+ if (getCanonicalDefinitionID (VDef->Ref ) != getCanonicalDefinitionID (*I2)) {
779+ VDef->Ref = 0 ; // Mark this variable as undefined
780+ }
708781 }
709782}
710783
@@ -1196,11 +1269,10 @@ class ThreadSafetyAnalyzer {
11961269
11971270 void warnIfMutexNotHeld (const FactSet &FSet, const NamedDecl *D,
11981271 const Expr *Exp, AccessKind AK, Expr *MutexExp,
1199- ProtectedOperationKind POK, til::LiteralPtr *Self,
1272+ ProtectedOperationKind POK, til::SExpr *Self,
12001273 SourceLocation Loc);
12011274 void warnIfMutexHeld (const FactSet &FSet, const NamedDecl *D, const Expr *Exp,
1202- Expr *MutexExp, til::LiteralPtr *Self,
1203- SourceLocation Loc);
1275+ Expr *MutexExp, til::SExpr *Self, SourceLocation Loc);
12041276
12051277 void checkAccess (const FactSet &FSet, const Expr *Exp, AccessKind AK,
12061278 ProtectedOperationKind POK);
@@ -1596,6 +1668,16 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
15961668 const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID ()];
15971669 const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext ;
15981670
1671+ // Temporarily set the lookup context for SExprBuilder.
1672+ SxBuilder.setLookupLocalVarExpr ([&](const NamedDecl *D) -> const Expr * {
1673+ if (!Handler.issueBetaWarnings ())
1674+ return nullptr ;
1675+ auto Ctx = LVarCtx;
1676+ return LocalVarMap.lookupExpr (D, Ctx);
1677+ });
1678+ auto Cleanup = llvm::make_scope_exit (
1679+ [this ] { SxBuilder.setLookupLocalVarExpr (nullptr ); });
1680+
15991681 const auto *Exp = getTrylockCallExpr (Cond, LVarCtx, Negate);
16001682 if (!Exp)
16011683 return ;
@@ -1652,7 +1734,7 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
16521734 }
16531735
16541736 void handleCall (const Expr *Exp, const NamedDecl *D,
1655- til::LiteralPtr *Self = nullptr ,
1737+ til::SExpr *Self = nullptr ,
16561738 SourceLocation Loc = SourceLocation());
16571739 void examineArguments (const FunctionDecl *FD,
16581740 CallExpr::const_arg_iterator ArgBegin,
@@ -1664,7 +1746,17 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
16641746 const FactSet &FunctionExitFSet)
16651747 : ConstStmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),
16661748 FunctionExitFSet (FunctionExitFSet), LVarCtx(Info.EntryContext),
1667- CtxIndex (Info.EntryIndex) {}
1749+ CtxIndex (Info.EntryIndex) {
1750+ Analyzer->SxBuilder .setLookupLocalVarExpr (
1751+ [this ](const NamedDecl *D) -> const Expr * {
1752+ if (!Analyzer->Handler .issueBetaWarnings ())
1753+ return nullptr ;
1754+ auto Ctx = LVarCtx;
1755+ return Analyzer->LocalVarMap .lookupExpr (D, Ctx);
1756+ });
1757+ }
1758+
1759+ ~BuildLockset () { Analyzer->SxBuilder .setLookupLocalVarExpr (nullptr ); }
16681760
16691761 void VisitUnaryOperator (const UnaryOperator *UO);
16701762 void VisitBinaryOperator (const BinaryOperator *BO);
@@ -1682,7 +1774,7 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
16821774// / of at least the passed in AccessKind.
16831775void ThreadSafetyAnalyzer::warnIfMutexNotHeld (
16841776 const FactSet &FSet, const NamedDecl *D, const Expr *Exp, AccessKind AK,
1685- Expr *MutexExp, ProtectedOperationKind POK, til::LiteralPtr *Self,
1777+ Expr *MutexExp, ProtectedOperationKind POK, til::SExpr *Self,
16861778 SourceLocation Loc) {
16871779 LockKind LK = getLockKindFromAccessKind (AK);
16881780 CapabilityExpr Cp = SxBuilder.translateAttrExpr (MutexExp, D, Exp, Self);
@@ -1741,8 +1833,7 @@ void ThreadSafetyAnalyzer::warnIfMutexNotHeld(
17411833// / Warn if the LSet contains the given lock.
17421834void ThreadSafetyAnalyzer::warnIfMutexHeld (const FactSet &FSet,
17431835 const NamedDecl *D, const Expr *Exp,
1744- Expr *MutexExp,
1745- til::LiteralPtr *Self,
1836+ Expr *MutexExp, til::SExpr *Self,
17461837 SourceLocation Loc) {
17471838 CapabilityExpr Cp = SxBuilder.translateAttrExpr (MutexExp, D, Exp, Self);
17481839 if (Cp.isInvalid ()) {
@@ -1910,7 +2001,7 @@ void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp,
19102001// / of an implicitly called cleanup function.
19112002// / \param Loc If \p Exp = nullptr, the location.
19122003void BuildLockset::handleCall (const Expr *Exp, const NamedDecl *D,
1913- til::LiteralPtr *Self, SourceLocation Loc) {
2004+ til::SExpr *Self, SourceLocation Loc) {
19142005 CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
19152006 CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
19162007 CapExprSet ScopedReqsAndExcludes;
@@ -1922,7 +2013,7 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
19222013 const auto *TagT = Exp->getType ()->getAs <TagType>();
19232014 if (D->hasAttrs () && TagT && Exp->isPRValue ()) {
19242015 til::LiteralPtr *Placeholder =
1925- Analyzer->SxBuilder .createVariable ( nullptr );
2016+ Analyzer->SxBuilder .createThisPlaceholder ( );
19262017 [[maybe_unused]] auto inserted =
19272018 Analyzer->ConstructedObjects .insert ({Exp, Placeholder});
19282019 assert (inserted.second && " Are we visiting the same expression again?" );
@@ -2216,6 +2307,9 @@ void BuildLockset::examineArguments(const FunctionDecl *FD,
22162307}
22172308
22182309void BuildLockset::VisitCallExpr (const CallExpr *Exp) {
2310+ // adjust the context
2311+ LVarCtx = Analyzer->LocalVarMap .getNextContext (CtxIndex, Exp, LVarCtx);
2312+
22192313 if (const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
22202314 const auto *ME = dyn_cast<MemberExpr>(CE->getCallee ());
22212315 // ME can be null when calling a method pointer
@@ -2603,7 +2697,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
26032697 }
26042698 if (UnderlyingLocks.empty ())
26052699 continue ;
2606- CapabilityExpr Cp (SxBuilder.createVariable (Param), StringRef (),
2700+ CapabilityExpr Cp (SxBuilder.translateVariable (Param, nullptr ),
2701+ StringRef (),
26072702 /* Neg=*/ false , /* Reentrant=*/ false );
26082703 auto *ScopedEntry = FactMan.createFact <ScopedLockableFactEntry>(
26092704 Cp, Param->getLocation (), FactEntry::Declared,
@@ -2721,17 +2816,19 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
27212816 if (!DD->hasAttrs ())
27222817 break ;
27232818
2724- LocksetBuilder.handleCall (nullptr , DD,
2725- SxBuilder.createVariable (AD.getVarDecl ()),
2726- AD.getTriggerStmt ()->getEndLoc ());
2819+ LocksetBuilder.handleCall (
2820+ nullptr , DD,
2821+ SxBuilder.translateVariable (AD.getVarDecl (), nullptr ),
2822+ AD.getTriggerStmt ()->getEndLoc ());
27272823 break ;
27282824 }
27292825
27302826 case CFGElement::CleanupFunction: {
27312827 const CFGCleanupFunction &CF = BI.castAs <CFGCleanupFunction>();
2732- LocksetBuilder.handleCall (/* Exp=*/ nullptr , CF.getFunctionDecl (),
2733- SxBuilder.createVariable (CF.getVarDecl ()),
2734- CF.getVarDecl ()->getLocation ());
2828+ LocksetBuilder.handleCall (
2829+ /* Exp=*/ nullptr , CF.getFunctionDecl (),
2830+ SxBuilder.translateVariable (CF.getVarDecl (), nullptr ),
2831+ CF.getVarDecl ()->getLocation ());
27352832 break ;
27362833 }
27372834
0 commit comments