39
39
#include " llvm/ADT/DenseMap.h"
40
40
#include " llvm/ADT/ImmutableMap.h"
41
41
#include " llvm/ADT/STLExtras.h"
42
+ #include " llvm/ADT/ScopeExit.h"
42
43
#include " llvm/ADT/SmallVector.h"
43
44
#include " llvm/ADT/StringRef.h"
44
45
#include " llvm/Support/Allocator.h"
@@ -537,6 +538,13 @@ class LocalVariableMap {
537
538
protected:
538
539
friend class VarMapBuilder ;
539
540
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
+
540
548
// Get the current context index
541
549
unsigned getContextIndex () { return SavedContexts.size ()-1 ; }
542
550
@@ -621,6 +629,7 @@ class VarMapBuilder : public ConstStmtVisitor<VarMapBuilder> {
621
629
622
630
void VisitDeclStmt (const DeclStmt *S);
623
631
void VisitBinaryOperator (const BinaryOperator *BO);
632
+ void VisitCallExpr (const CallExpr *CE);
624
633
};
625
634
626
635
} // namespace
@@ -666,6 +675,56 @@ void VarMapBuilder::VisitBinaryOperator(const BinaryOperator *BO) {
666
675
}
667
676
}
668
677
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
+
669
728
// Computes the intersection of two contexts. The intersection is the
670
729
// set of variables which have the same definition in both contexts;
671
730
// variables with different definitions are discarded.
@@ -674,11 +733,16 @@ LocalVariableMap::intersectContexts(Context C1, Context C2) {
674
733
Context Result = C1;
675
734
for (const auto &P : C1) {
676
735
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.
679
739
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.
681
744
Result = clearDefinition (Dec, Result);
745
+ }
682
746
}
683
747
return Result;
684
748
}
@@ -698,13 +762,22 @@ LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
698
762
// createReferenceContext.
699
763
void LocalVariableMap::intersectBackEdge (Context C1, Context C2) {
700
764
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 ];
703
767
assert (VDef->isReference ());
704
768
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
+ }
708
781
}
709
782
}
710
783
@@ -1196,11 +1269,10 @@ class ThreadSafetyAnalyzer {
1196
1269
1197
1270
void warnIfMutexNotHeld (const FactSet &FSet, const NamedDecl *D,
1198
1271
const Expr *Exp, AccessKind AK, Expr *MutexExp,
1199
- ProtectedOperationKind POK, til::LiteralPtr *Self,
1272
+ ProtectedOperationKind POK, til::SExpr *Self,
1200
1273
SourceLocation Loc);
1201
1274
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);
1204
1276
1205
1277
void checkAccess (const FactSet &FSet, const Expr *Exp, AccessKind AK,
1206
1278
ProtectedOperationKind POK);
@@ -1596,6 +1668,16 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
1596
1668
const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID ()];
1597
1669
const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext ;
1598
1670
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
+
1599
1681
const auto *Exp = getTrylockCallExpr (Cond, LVarCtx, Negate);
1600
1682
if (!Exp)
1601
1683
return ;
@@ -1652,7 +1734,7 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
1652
1734
}
1653
1735
1654
1736
void handleCall (const Expr *Exp, const NamedDecl *D,
1655
- til::LiteralPtr *Self = nullptr ,
1737
+ til::SExpr *Self = nullptr ,
1656
1738
SourceLocation Loc = SourceLocation());
1657
1739
void examineArguments (const FunctionDecl *FD,
1658
1740
CallExpr::const_arg_iterator ArgBegin,
@@ -1664,7 +1746,17 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
1664
1746
const FactSet &FunctionExitFSet)
1665
1747
: ConstStmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),
1666
1748
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 ); }
1668
1760
1669
1761
void VisitUnaryOperator (const UnaryOperator *UO);
1670
1762
void VisitBinaryOperator (const BinaryOperator *BO);
@@ -1682,7 +1774,7 @@ class BuildLockset : public ConstStmtVisitor<BuildLockset> {
1682
1774
// / of at least the passed in AccessKind.
1683
1775
void ThreadSafetyAnalyzer::warnIfMutexNotHeld (
1684
1776
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,
1686
1778
SourceLocation Loc) {
1687
1779
LockKind LK = getLockKindFromAccessKind (AK);
1688
1780
CapabilityExpr Cp = SxBuilder.translateAttrExpr (MutexExp, D, Exp, Self);
@@ -1741,8 +1833,7 @@ void ThreadSafetyAnalyzer::warnIfMutexNotHeld(
1741
1833
// / Warn if the LSet contains the given lock.
1742
1834
void ThreadSafetyAnalyzer::warnIfMutexHeld (const FactSet &FSet,
1743
1835
const NamedDecl *D, const Expr *Exp,
1744
- Expr *MutexExp,
1745
- til::LiteralPtr *Self,
1836
+ Expr *MutexExp, til::SExpr *Self,
1746
1837
SourceLocation Loc) {
1747
1838
CapabilityExpr Cp = SxBuilder.translateAttrExpr (MutexExp, D, Exp, Self);
1748
1839
if (Cp.isInvalid ()) {
@@ -1910,7 +2001,7 @@ void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp,
1910
2001
// / of an implicitly called cleanup function.
1911
2002
// / \param Loc If \p Exp = nullptr, the location.
1912
2003
void BuildLockset::handleCall (const Expr *Exp, const NamedDecl *D,
1913
- til::LiteralPtr *Self, SourceLocation Loc) {
2004
+ til::SExpr *Self, SourceLocation Loc) {
1914
2005
CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
1915
2006
CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
1916
2007
CapExprSet ScopedReqsAndExcludes;
@@ -1922,7 +2013,7 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
1922
2013
const auto *TagT = Exp->getType ()->getAs <TagType>();
1923
2014
if (D->hasAttrs () && TagT && Exp->isPRValue ()) {
1924
2015
til::LiteralPtr *Placeholder =
1925
- Analyzer->SxBuilder .createVariable ( nullptr );
2016
+ Analyzer->SxBuilder .createThisPlaceholder ( );
1926
2017
[[maybe_unused]] auto inserted =
1927
2018
Analyzer->ConstructedObjects .insert ({Exp, Placeholder});
1928
2019
assert (inserted.second && " Are we visiting the same expression again?" );
@@ -2216,6 +2307,9 @@ void BuildLockset::examineArguments(const FunctionDecl *FD,
2216
2307
}
2217
2308
2218
2309
void BuildLockset::VisitCallExpr (const CallExpr *Exp) {
2310
+ // adjust the context
2311
+ LVarCtx = Analyzer->LocalVarMap .getNextContext (CtxIndex, Exp, LVarCtx);
2312
+
2219
2313
if (const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
2220
2314
const auto *ME = dyn_cast<MemberExpr>(CE->getCallee ());
2221
2315
// ME can be null when calling a method pointer
@@ -2603,7 +2697,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
2603
2697
}
2604
2698
if (UnderlyingLocks.empty ())
2605
2699
continue ;
2606
- CapabilityExpr Cp (SxBuilder.createVariable (Param), StringRef (),
2700
+ CapabilityExpr Cp (SxBuilder.translateVariable (Param, nullptr ),
2701
+ StringRef (),
2607
2702
/* Neg=*/ false , /* Reentrant=*/ false );
2608
2703
auto *ScopedEntry = FactMan.createFact <ScopedLockableFactEntry>(
2609
2704
Cp, Param->getLocation (), FactEntry::Declared,
@@ -2721,17 +2816,19 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
2721
2816
if (!DD->hasAttrs ())
2722
2817
break ;
2723
2818
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 ());
2727
2823
break ;
2728
2824
}
2729
2825
2730
2826
case CFGElement::CleanupFunction: {
2731
2827
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 ());
2735
2832
break ;
2736
2833
}
2737
2834
0 commit comments