Skip to content

Commit 7d7bc29

Browse files
author
git apple-llvm automerger
committed
Merge commit 'b4c98fcbe150' from llvm.org/main into next
2 parents ac9626a + b4c98fc commit 7d7bc29

File tree

6 files changed

+561
-31
lines changed

6 files changed

+561
-31
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ Improvements to Clang's diagnostics
279279
- The :doc:`ThreadSafetyAnalysis` attributes ``ACQUIRED_BEFORE(...)`` and
280280
``ACQUIRED_AFTER(...)`` have been moved to the stable feature set and no
281281
longer require ``-Wthread-safety-beta`` to be used.
282+
- The :doc:`ThreadSafetyAnalysis` gains basic alias-analysis of capability
283+
pointers under ``-Wthread-safety-beta`` (still experimental), which reduces
284+
both false positives but also false negatives through more precise analysis.
282285

283286
Improvements to Clang's time-trace
284287
----------------------------------

clang/include/clang/Analysis/Analyses/ThreadSafetyCommon.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "llvm/ADT/PointerUnion.h"
3636
#include "llvm/ADT/SmallVector.h"
3737
#include "llvm/Support/Casting.h"
38+
#include <functional>
3839
#include <sstream>
3940
#include <string>
4041
#include <utility>
@@ -386,6 +387,11 @@ class SExprBuilder {
386387
SelfVar->setKind(til::Variable::VK_SFun);
387388
}
388389

390+
// Create placeholder for this: we don't know the VarDecl on construction yet.
391+
til::LiteralPtr *createThisPlaceholder() {
392+
return new (Arena) til::LiteralPtr(nullptr);
393+
}
394+
389395
// Translate a clang expression in an attribute to a til::SExpr.
390396
// Constructs the context from D, DeclExp, and SelfDecl.
391397
CapabilityExpr translateAttrExpr(const Expr *AttrExp, const NamedDecl *D,
@@ -394,8 +400,8 @@ class SExprBuilder {
394400

395401
CapabilityExpr translateAttrExpr(const Expr *AttrExp, CallingContext *Ctx);
396402

397-
// Translate a variable reference.
398-
til::LiteralPtr *createVariable(const VarDecl *VD);
403+
// Translate a VarDecl to its canonical TIL expression.
404+
til::SExpr *translateVariable(const VarDecl *VD, CallingContext *Ctx);
399405

400406
// Translate a clang statement or expression to a TIL expression.
401407
// Also performs substitution of variables; Ctx provides the context.
@@ -412,6 +418,10 @@ class SExprBuilder {
412418
const til::SCFG *getCFG() const { return Scfg; }
413419
til::SCFG *getCFG() { return Scfg; }
414420

421+
void setLookupLocalVarExpr(std::function<const Expr *(const NamedDecl *)> F) {
422+
LookupLocalVarExpr = std::move(F);
423+
}
424+
415425
private:
416426
// We implement the CFGVisitor API
417427
friend class CFGWalker;
@@ -445,6 +455,7 @@ class SExprBuilder {
445455
const AbstractConditionalOperator *C, CallingContext *Ctx);
446456

447457
til::SExpr *translateDeclStmt(const DeclStmt *S, CallingContext *Ctx);
458+
til::SExpr *translateStmtExpr(const StmtExpr *SE, CallingContext *Ctx);
448459

449460
// Map from statements in the clang CFG to SExprs in the til::SCFG.
450461
using StatementMap = llvm::DenseMap<const Stmt *, til::SExpr *>;
@@ -531,6 +542,11 @@ class SExprBuilder {
531542
std::vector<til::Phi *> IncompleteArgs;
532543
til::BasicBlock *CurrentBB = nullptr;
533544
BlockInfo *CurrentBlockInfo = nullptr;
545+
546+
// Recursion guard.
547+
llvm::DenseSet<const ValueDecl *> VarsBeingTranslated;
548+
// Context-dependent lookup of currently valid definitions of local variables.
549+
std::function<const Expr *(const NamedDecl *)> LookupLocalVarExpr;
534550
};
535551

536552
#ifndef NDEBUG

clang/lib/Analysis/ThreadSafety.cpp

Lines changed: 122 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
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 {
537538
protected:
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.
699763
void 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.
16831775
void 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.
17421834
void 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.
19122003
void 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

22182309
void 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

Comments
 (0)