Path: blob/main/contrib/llvm-project/clang/lib/Analysis/ThreadSafety.cpp
35233 views
//===- ThreadSafety.cpp ---------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// A intra-procedural analysis for thread safety (e.g. deadlocks and race9// conditions), based off of an annotation system.10//11// See http://clang.llvm.org/docs/ThreadSafetyAnalysis.html12// for more information.13//14//===----------------------------------------------------------------------===//1516#include "clang/Analysis/Analyses/ThreadSafety.h"17#include "clang/AST/Attr.h"18#include "clang/AST/Decl.h"19#include "clang/AST/DeclCXX.h"20#include "clang/AST/DeclGroup.h"21#include "clang/AST/Expr.h"22#include "clang/AST/ExprCXX.h"23#include "clang/AST/OperationKinds.h"24#include "clang/AST/Stmt.h"25#include "clang/AST/StmtVisitor.h"26#include "clang/AST/Type.h"27#include "clang/Analysis/Analyses/PostOrderCFGView.h"28#include "clang/Analysis/Analyses/ThreadSafetyCommon.h"29#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"30#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h"31#include "clang/Analysis/Analyses/ThreadSafetyUtil.h"32#include "clang/Analysis/AnalysisDeclContext.h"33#include "clang/Analysis/CFG.h"34#include "clang/Basic/Builtins.h"35#include "clang/Basic/LLVM.h"36#include "clang/Basic/OperatorKinds.h"37#include "clang/Basic/SourceLocation.h"38#include "clang/Basic/Specifiers.h"39#include "llvm/ADT/ArrayRef.h"40#include "llvm/ADT/DenseMap.h"41#include "llvm/ADT/ImmutableMap.h"42#include "llvm/ADT/STLExtras.h"43#include "llvm/ADT/SmallVector.h"44#include "llvm/ADT/StringRef.h"45#include "llvm/Support/Allocator.h"46#include "llvm/Support/Casting.h"47#include "llvm/Support/ErrorHandling.h"48#include "llvm/Support/raw_ostream.h"49#include <algorithm>50#include <cassert>51#include <functional>52#include <iterator>53#include <memory>54#include <optional>55#include <string>56#include <type_traits>57#include <utility>58#include <vector>5960using namespace clang;61using namespace threadSafety;6263// Key method definition64ThreadSafetyHandler::~ThreadSafetyHandler() = default;6566/// Issue a warning about an invalid lock expression67static void warnInvalidLock(ThreadSafetyHandler &Handler,68const Expr *MutexExp, const NamedDecl *D,69const Expr *DeclExp, StringRef Kind) {70SourceLocation Loc;71if (DeclExp)72Loc = DeclExp->getExprLoc();7374// FIXME: add a note about the attribute location in MutexExp or D75if (Loc.isValid())76Handler.handleInvalidLockExp(Loc);77}7879namespace {8081/// A set of CapabilityExpr objects, which are compiled from thread safety82/// attributes on a function.83class CapExprSet : public SmallVector<CapabilityExpr, 4> {84public:85/// Push M onto list, but discard duplicates.86void push_back_nodup(const CapabilityExpr &CapE) {87if (llvm::none_of(*this, [=](const CapabilityExpr &CapE2) {88return CapE.equals(CapE2);89}))90push_back(CapE);91}92};9394class FactManager;95class FactSet;9697/// This is a helper class that stores a fact that is known at a98/// particular point in program execution. Currently, a fact is a capability,99/// along with additional information, such as where it was acquired, whether100/// it is exclusive or shared, etc.101///102/// FIXME: this analysis does not currently support re-entrant locking.103class FactEntry : public CapabilityExpr {104public:105/// Where a fact comes from.106enum SourceKind {107Acquired, ///< The fact has been directly acquired.108Asserted, ///< The fact has been asserted to be held.109Declared, ///< The fact is assumed to be held by callers.110Managed, ///< The fact has been acquired through a scoped capability.111};112113private:114/// Exclusive or shared.115LockKind LKind : 8;116117// How it was acquired.118SourceKind Source : 8;119120/// Where it was acquired.121SourceLocation AcquireLoc;122123public:124FactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc,125SourceKind Src)126: CapabilityExpr(CE), LKind(LK), Source(Src), AcquireLoc(Loc) {}127virtual ~FactEntry() = default;128129LockKind kind() const { return LKind; }130SourceLocation loc() const { return AcquireLoc; }131132bool asserted() const { return Source == Asserted; }133bool declared() const { return Source == Declared; }134bool managed() const { return Source == Managed; }135136virtual void137handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,138SourceLocation JoinLoc, LockErrorKind LEK,139ThreadSafetyHandler &Handler) const = 0;140virtual void handleLock(FactSet &FSet, FactManager &FactMan,141const FactEntry &entry,142ThreadSafetyHandler &Handler) const = 0;143virtual void handleUnlock(FactSet &FSet, FactManager &FactMan,144const CapabilityExpr &Cp, SourceLocation UnlockLoc,145bool FullyRemove,146ThreadSafetyHandler &Handler) const = 0;147148// Return true if LKind >= LK, where exclusive > shared149bool isAtLeast(LockKind LK) const {150return (LKind == LK_Exclusive) || (LK == LK_Shared);151}152};153154using FactID = unsigned short;155156/// FactManager manages the memory for all facts that are created during157/// the analysis of a single routine.158class FactManager {159private:160std::vector<std::unique_ptr<const FactEntry>> Facts;161162public:163FactID newFact(std::unique_ptr<FactEntry> Entry) {164Facts.push_back(std::move(Entry));165return static_cast<unsigned short>(Facts.size() - 1);166}167168const FactEntry &operator[](FactID F) const { return *Facts[F]; }169};170171/// A FactSet is the set of facts that are known to be true at a172/// particular program point. FactSets must be small, because they are173/// frequently copied, and are thus implemented as a set of indices into a174/// table maintained by a FactManager. A typical FactSet only holds 1 or 2175/// locks, so we can get away with doing a linear search for lookup. Note176/// that a hashtable or map is inappropriate in this case, because lookups177/// may involve partial pattern matches, rather than exact matches.178class FactSet {179private:180using FactVec = SmallVector<FactID, 4>;181182FactVec FactIDs;183184public:185using iterator = FactVec::iterator;186using const_iterator = FactVec::const_iterator;187188iterator begin() { return FactIDs.begin(); }189const_iterator begin() const { return FactIDs.begin(); }190191iterator end() { return FactIDs.end(); }192const_iterator end() const { return FactIDs.end(); }193194bool isEmpty() const { return FactIDs.size() == 0; }195196// Return true if the set contains only negative facts197bool isEmpty(FactManager &FactMan) const {198for (const auto FID : *this) {199if (!FactMan[FID].negative())200return false;201}202return true;203}204205void addLockByID(FactID ID) { FactIDs.push_back(ID); }206207FactID addLock(FactManager &FM, std::unique_ptr<FactEntry> Entry) {208FactID F = FM.newFact(std::move(Entry));209FactIDs.push_back(F);210return F;211}212213bool removeLock(FactManager& FM, const CapabilityExpr &CapE) {214unsigned n = FactIDs.size();215if (n == 0)216return false;217218for (unsigned i = 0; i < n-1; ++i) {219if (FM[FactIDs[i]].matches(CapE)) {220FactIDs[i] = FactIDs[n-1];221FactIDs.pop_back();222return true;223}224}225if (FM[FactIDs[n-1]].matches(CapE)) {226FactIDs.pop_back();227return true;228}229return false;230}231232iterator findLockIter(FactManager &FM, const CapabilityExpr &CapE) {233return std::find_if(begin(), end(), [&](FactID ID) {234return FM[ID].matches(CapE);235});236}237238const FactEntry *findLock(FactManager &FM, const CapabilityExpr &CapE) const {239auto I = std::find_if(begin(), end(), [&](FactID ID) {240return FM[ID].matches(CapE);241});242return I != end() ? &FM[*I] : nullptr;243}244245const FactEntry *findLockUniv(FactManager &FM,246const CapabilityExpr &CapE) const {247auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {248return FM[ID].matchesUniv(CapE);249});250return I != end() ? &FM[*I] : nullptr;251}252253const FactEntry *findPartialMatch(FactManager &FM,254const CapabilityExpr &CapE) const {255auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {256return FM[ID].partiallyMatches(CapE);257});258return I != end() ? &FM[*I] : nullptr;259}260261bool containsMutexDecl(FactManager &FM, const ValueDecl* Vd) const {262auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {263return FM[ID].valueDecl() == Vd;264});265return I != end();266}267};268269class ThreadSafetyAnalyzer;270271} // namespace272273namespace clang {274namespace threadSafety {275276class BeforeSet {277private:278using BeforeVect = SmallVector<const ValueDecl *, 4>;279280struct BeforeInfo {281BeforeVect Vect;282int Visited = 0;283284BeforeInfo() = default;285BeforeInfo(BeforeInfo &&) = default;286};287288using BeforeMap =289llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>;290using CycleMap = llvm::DenseMap<const ValueDecl *, bool>;291292public:293BeforeSet() = default;294295BeforeInfo* insertAttrExprs(const ValueDecl* Vd,296ThreadSafetyAnalyzer& Analyzer);297298BeforeInfo *getBeforeInfoForDecl(const ValueDecl *Vd,299ThreadSafetyAnalyzer &Analyzer);300301void checkBeforeAfter(const ValueDecl* Vd,302const FactSet& FSet,303ThreadSafetyAnalyzer& Analyzer,304SourceLocation Loc, StringRef CapKind);305306private:307BeforeMap BMap;308CycleMap CycMap;309};310311} // namespace threadSafety312} // namespace clang313314namespace {315316class LocalVariableMap;317318using LocalVarContext = llvm::ImmutableMap<const NamedDecl *, unsigned>;319320/// A side (entry or exit) of a CFG node.321enum CFGBlockSide { CBS_Entry, CBS_Exit };322323/// CFGBlockInfo is a struct which contains all the information that is324/// maintained for each block in the CFG. See LocalVariableMap for more325/// information about the contexts.326struct CFGBlockInfo {327// Lockset held at entry to block328FactSet EntrySet;329330// Lockset held at exit from block331FactSet ExitSet;332333// Context held at entry to block334LocalVarContext EntryContext;335336// Context held at exit from block337LocalVarContext ExitContext;338339// Location of first statement in block340SourceLocation EntryLoc;341342// Location of last statement in block.343SourceLocation ExitLoc;344345// Used to replay contexts later346unsigned EntryIndex;347348// Is this block reachable?349bool Reachable = false;350351const FactSet &getSet(CFGBlockSide Side) const {352return Side == CBS_Entry ? EntrySet : ExitSet;353}354355SourceLocation getLocation(CFGBlockSide Side) const {356return Side == CBS_Entry ? EntryLoc : ExitLoc;357}358359private:360CFGBlockInfo(LocalVarContext EmptyCtx)361: EntryContext(EmptyCtx), ExitContext(EmptyCtx) {}362363public:364static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);365};366367// A LocalVariableMap maintains a map from local variables to their currently368// valid definitions. It provides SSA-like functionality when traversing the369// CFG. Like SSA, each definition or assignment to a variable is assigned a370// unique name (an integer), which acts as the SSA name for that definition.371// The total set of names is shared among all CFG basic blocks.372// Unlike SSA, we do not rewrite expressions to replace local variables declrefs373// with their SSA-names. Instead, we compute a Context for each point in the374// code, which maps local variables to the appropriate SSA-name. This map375// changes with each assignment.376//377// The map is computed in a single pass over the CFG. Subsequent analyses can378// then query the map to find the appropriate Context for a statement, and use379// that Context to look up the definitions of variables.380class LocalVariableMap {381public:382using Context = LocalVarContext;383384/// A VarDefinition consists of an expression, representing the value of the385/// variable, along with the context in which that expression should be386/// interpreted. A reference VarDefinition does not itself contain this387/// information, but instead contains a pointer to a previous VarDefinition.388struct VarDefinition {389public:390friend class LocalVariableMap;391392// The original declaration for this variable.393const NamedDecl *Dec;394395// The expression for this variable, OR396const Expr *Exp = nullptr;397398// Reference to another VarDefinition399unsigned Ref = 0;400401// The map with which Exp should be interpreted.402Context Ctx;403404bool isReference() const { return !Exp; }405406private:407// Create ordinary variable definition408VarDefinition(const NamedDecl *D, const Expr *E, Context C)409: Dec(D), Exp(E), Ctx(C) {}410411// Create reference to previous definition412VarDefinition(const NamedDecl *D, unsigned R, Context C)413: Dec(D), Ref(R), Ctx(C) {}414};415416private:417Context::Factory ContextFactory;418std::vector<VarDefinition> VarDefinitions;419std::vector<std::pair<const Stmt *, Context>> SavedContexts;420421public:422LocalVariableMap() {423// index 0 is a placeholder for undefined variables (aka phi-nodes).424VarDefinitions.push_back(VarDefinition(nullptr, 0u, getEmptyContext()));425}426427/// Look up a definition, within the given context.428const VarDefinition* lookup(const NamedDecl *D, Context Ctx) {429const unsigned *i = Ctx.lookup(D);430if (!i)431return nullptr;432assert(*i < VarDefinitions.size());433return &VarDefinitions[*i];434}435436/// Look up the definition for D within the given context. Returns437/// NULL if the expression is not statically known. If successful, also438/// modifies Ctx to hold the context of the return Expr.439const Expr* lookupExpr(const NamedDecl *D, Context &Ctx) {440const unsigned *P = Ctx.lookup(D);441if (!P)442return nullptr;443444unsigned i = *P;445while (i > 0) {446if (VarDefinitions[i].Exp) {447Ctx = VarDefinitions[i].Ctx;448return VarDefinitions[i].Exp;449}450i = VarDefinitions[i].Ref;451}452return nullptr;453}454455Context getEmptyContext() { return ContextFactory.getEmptyMap(); }456457/// Return the next context after processing S. This function is used by458/// clients of the class to get the appropriate context when traversing the459/// CFG. It must be called for every assignment or DeclStmt.460Context getNextContext(unsigned &CtxIndex, const Stmt *S, Context C) {461if (SavedContexts[CtxIndex+1].first == S) {462CtxIndex++;463Context Result = SavedContexts[CtxIndex].second;464return Result;465}466return C;467}468469void dumpVarDefinitionName(unsigned i) {470if (i == 0) {471llvm::errs() << "Undefined";472return;473}474const NamedDecl *Dec = VarDefinitions[i].Dec;475if (!Dec) {476llvm::errs() << "<<NULL>>";477return;478}479Dec->printName(llvm::errs());480llvm::errs() << "." << i << " " << ((const void*) Dec);481}482483/// Dumps an ASCII representation of the variable map to llvm::errs()484void dump() {485for (unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {486const Expr *Exp = VarDefinitions[i].Exp;487unsigned Ref = VarDefinitions[i].Ref;488489dumpVarDefinitionName(i);490llvm::errs() << " = ";491if (Exp) Exp->dump();492else {493dumpVarDefinitionName(Ref);494llvm::errs() << "\n";495}496}497}498499/// Dumps an ASCII representation of a Context to llvm::errs()500void dumpContext(Context C) {501for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {502const NamedDecl *D = I.getKey();503D->printName(llvm::errs());504llvm::errs() << " -> ";505dumpVarDefinitionName(I.getData());506llvm::errs() << "\n";507}508}509510/// Builds the variable map.511void traverseCFG(CFG *CFGraph, const PostOrderCFGView *SortedGraph,512std::vector<CFGBlockInfo> &BlockInfo);513514protected:515friend class VarMapBuilder;516517// Get the current context index518unsigned getContextIndex() { return SavedContexts.size()-1; }519520// Save the current context for later replay521void saveContext(const Stmt *S, Context C) {522SavedContexts.push_back(std::make_pair(S, C));523}524525// Adds a new definition to the given context, and returns a new context.526// This method should be called when declaring a new variable.527Context addDefinition(const NamedDecl *D, const Expr *Exp, Context Ctx) {528assert(!Ctx.contains(D));529unsigned newID = VarDefinitions.size();530Context NewCtx = ContextFactory.add(Ctx, D, newID);531VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));532return NewCtx;533}534535// Add a new reference to an existing definition.536Context addReference(const NamedDecl *D, unsigned i, Context Ctx) {537unsigned newID = VarDefinitions.size();538Context NewCtx = ContextFactory.add(Ctx, D, newID);539VarDefinitions.push_back(VarDefinition(D, i, Ctx));540return NewCtx;541}542543// Updates a definition only if that definition is already in the map.544// This method should be called when assigning to an existing variable.545Context updateDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) {546if (Ctx.contains(D)) {547unsigned newID = VarDefinitions.size();548Context NewCtx = ContextFactory.remove(Ctx, D);549NewCtx = ContextFactory.add(NewCtx, D, newID);550VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));551return NewCtx;552}553return Ctx;554}555556// Removes a definition from the context, but keeps the variable name557// as a valid variable. The index 0 is a placeholder for cleared definitions.558Context clearDefinition(const NamedDecl *D, Context Ctx) {559Context NewCtx = Ctx;560if (NewCtx.contains(D)) {561NewCtx = ContextFactory.remove(NewCtx, D);562NewCtx = ContextFactory.add(NewCtx, D, 0);563}564return NewCtx;565}566567// Remove a definition entirely frmo the context.568Context removeDefinition(const NamedDecl *D, Context Ctx) {569Context NewCtx = Ctx;570if (NewCtx.contains(D)) {571NewCtx = ContextFactory.remove(NewCtx, D);572}573return NewCtx;574}575576Context intersectContexts(Context C1, Context C2);577Context createReferenceContext(Context C);578void intersectBackEdge(Context C1, Context C2);579};580581} // namespace582583// This has to be defined after LocalVariableMap.584CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {585return CFGBlockInfo(M.getEmptyContext());586}587588namespace {589590/// Visitor which builds a LocalVariableMap591class VarMapBuilder : public ConstStmtVisitor<VarMapBuilder> {592public:593LocalVariableMap* VMap;594LocalVariableMap::Context Ctx;595596VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C)597: VMap(VM), Ctx(C) {}598599void VisitDeclStmt(const DeclStmt *S);600void VisitBinaryOperator(const BinaryOperator *BO);601};602603} // namespace604605// Add new local variables to the variable map606void VarMapBuilder::VisitDeclStmt(const DeclStmt *S) {607bool modifiedCtx = false;608const DeclGroupRef DGrp = S->getDeclGroup();609for (const auto *D : DGrp) {610if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) {611const Expr *E = VD->getInit();612613// Add local variables with trivial type to the variable map614QualType T = VD->getType();615if (T.isTrivialType(VD->getASTContext())) {616Ctx = VMap->addDefinition(VD, E, Ctx);617modifiedCtx = true;618}619}620}621if (modifiedCtx)622VMap->saveContext(S, Ctx);623}624625// Update local variable definitions in variable map626void VarMapBuilder::VisitBinaryOperator(const BinaryOperator *BO) {627if (!BO->isAssignmentOp())628return;629630Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();631632// Update the variable map and current context.633if (const auto *DRE = dyn_cast<DeclRefExpr>(LHSExp)) {634const ValueDecl *VDec = DRE->getDecl();635if (Ctx.lookup(VDec)) {636if (BO->getOpcode() == BO_Assign)637Ctx = VMap->updateDefinition(VDec, BO->getRHS(), Ctx);638else639// FIXME -- handle compound assignment operators640Ctx = VMap->clearDefinition(VDec, Ctx);641VMap->saveContext(BO, Ctx);642}643}644}645646// Computes the intersection of two contexts. The intersection is the647// set of variables which have the same definition in both contexts;648// variables with different definitions are discarded.649LocalVariableMap::Context650LocalVariableMap::intersectContexts(Context C1, Context C2) {651Context Result = C1;652for (const auto &P : C1) {653const NamedDecl *Dec = P.first;654const unsigned *i2 = C2.lookup(Dec);655if (!i2) // variable doesn't exist on second path656Result = removeDefinition(Dec, Result);657else if (*i2 != P.second) // variable exists, but has different definition658Result = clearDefinition(Dec, Result);659}660return Result;661}662663// For every variable in C, create a new variable that refers to the664// definition in C. Return a new context that contains these new variables.665// (We use this for a naive implementation of SSA on loop back-edges.)666LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {667Context Result = getEmptyContext();668for (const auto &P : C)669Result = addReference(P.first, P.second, Result);670return Result;671}672673// This routine also takes the intersection of C1 and C2, but it does so by674// altering the VarDefinitions. C1 must be the result of an earlier call to675// createReferenceContext.676void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {677for (const auto &P : C1) {678unsigned i1 = P.second;679VarDefinition *VDef = &VarDefinitions[i1];680assert(VDef->isReference());681682const unsigned *i2 = C2.lookup(P.first);683if (!i2 || (*i2 != i1))684VDef->Ref = 0; // Mark this variable as undefined685}686}687688// Traverse the CFG in topological order, so all predecessors of a block689// (excluding back-edges) are visited before the block itself. At690// each point in the code, we calculate a Context, which holds the set of691// variable definitions which are visible at that point in execution.692// Visible variables are mapped to their definitions using an array that693// contains all definitions.694//695// At join points in the CFG, the set is computed as the intersection of696// the incoming sets along each edge, E.g.697//698// { Context | VarDefinitions }699// int x = 0; { x -> x1 | x1 = 0 }700// int y = 0; { x -> x1, y -> y1 | y1 = 0, x1 = 0 }701// if (b) x = 1; { x -> x2, y -> y1 | x2 = 1, y1 = 0, ... }702// else x = 2; { x -> x3, y -> y1 | x3 = 2, x2 = 1, ... }703// ... { y -> y1 (x is unknown) | x3 = 2, x2 = 1, ... }704//705// This is essentially a simpler and more naive version of the standard SSA706// algorithm. Those definitions that remain in the intersection are from blocks707// that strictly dominate the current block. We do not bother to insert proper708// phi nodes, because they are not used in our analysis; instead, wherever709// a phi node would be required, we simply remove that definition from the710// context (E.g. x above).711//712// The initial traversal does not capture back-edges, so those need to be713// handled on a separate pass. Whenever the first pass encounters an714// incoming back edge, it duplicates the context, creating new definitions715// that refer back to the originals. (These correspond to places where SSA716// might have to insert a phi node.) On the second pass, these definitions are717// set to NULL if the variable has changed on the back-edge (i.e. a phi718// node was actually required.) E.g.719//720// { Context | VarDefinitions }721// int x = 0, y = 0; { x -> x1, y -> y1 | y1 = 0, x1 = 0 }722// while (b) { x -> x2, y -> y1 | [1st:] x2=x1; [2nd:] x2=NULL; }723// x = x+1; { x -> x3, y -> y1 | x3 = x2 + 1, ... }724// ... { y -> y1 | x3 = 2, x2 = 1, ... }725void LocalVariableMap::traverseCFG(CFG *CFGraph,726const PostOrderCFGView *SortedGraph,727std::vector<CFGBlockInfo> &BlockInfo) {728PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph);729730for (const auto *CurrBlock : *SortedGraph) {731unsigned CurrBlockID = CurrBlock->getBlockID();732CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];733734VisitedBlocks.insert(CurrBlock);735736// Calculate the entry context for the current block737bool HasBackEdges = false;738bool CtxInit = true;739for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),740PE = CurrBlock->pred_end(); PI != PE; ++PI) {741// if *PI -> CurrBlock is a back edge, so skip it742if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI)) {743HasBackEdges = true;744continue;745}746747unsigned PrevBlockID = (*PI)->getBlockID();748CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];749750if (CtxInit) {751CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;752CtxInit = false;753}754else {755CurrBlockInfo->EntryContext =756intersectContexts(CurrBlockInfo->EntryContext,757PrevBlockInfo->ExitContext);758}759}760761// Duplicate the context if we have back-edges, so we can call762// intersectBackEdges later.763if (HasBackEdges)764CurrBlockInfo->EntryContext =765createReferenceContext(CurrBlockInfo->EntryContext);766767// Create a starting context index for the current block768saveContext(nullptr, CurrBlockInfo->EntryContext);769CurrBlockInfo->EntryIndex = getContextIndex();770771// Visit all the statements in the basic block.772VarMapBuilder VMapBuilder(this, CurrBlockInfo->EntryContext);773for (const auto &BI : *CurrBlock) {774switch (BI.getKind()) {775case CFGElement::Statement: {776CFGStmt CS = BI.castAs<CFGStmt>();777VMapBuilder.Visit(CS.getStmt());778break;779}780default:781break;782}783}784CurrBlockInfo->ExitContext = VMapBuilder.Ctx;785786// Mark variables on back edges as "unknown" if they've been changed.787for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),788SE = CurrBlock->succ_end(); SI != SE; ++SI) {789// if CurrBlock -> *SI is *not* a back edge790if (*SI == nullptr || !VisitedBlocks.alreadySet(*SI))791continue;792793CFGBlock *FirstLoopBlock = *SI;794Context LoopBegin = BlockInfo[FirstLoopBlock->getBlockID()].EntryContext;795Context LoopEnd = CurrBlockInfo->ExitContext;796intersectBackEdge(LoopBegin, LoopEnd);797}798}799800// Put an extra entry at the end of the indexed context array801unsigned exitID = CFGraph->getExit().getBlockID();802saveContext(nullptr, BlockInfo[exitID].ExitContext);803}804805/// Find the appropriate source locations to use when producing diagnostics for806/// each block in the CFG.807static void findBlockLocations(CFG *CFGraph,808const PostOrderCFGView *SortedGraph,809std::vector<CFGBlockInfo> &BlockInfo) {810for (const auto *CurrBlock : *SortedGraph) {811CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];812813// Find the source location of the last statement in the block, if the814// block is not empty.815if (const Stmt *S = CurrBlock->getTerminatorStmt()) {816CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getBeginLoc();817} else {818for (CFGBlock::const_reverse_iterator BI = CurrBlock->rbegin(),819BE = CurrBlock->rend(); BI != BE; ++BI) {820// FIXME: Handle other CFGElement kinds.821if (std::optional<CFGStmt> CS = BI->getAs<CFGStmt>()) {822CurrBlockInfo->ExitLoc = CS->getStmt()->getBeginLoc();823break;824}825}826}827828if (CurrBlockInfo->ExitLoc.isValid()) {829// This block contains at least one statement. Find the source location830// of the first statement in the block.831for (const auto &BI : *CurrBlock) {832// FIXME: Handle other CFGElement kinds.833if (std::optional<CFGStmt> CS = BI.getAs<CFGStmt>()) {834CurrBlockInfo->EntryLoc = CS->getStmt()->getBeginLoc();835break;836}837}838} else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&839CurrBlock != &CFGraph->getExit()) {840// The block is empty, and has a single predecessor. Use its exit841// location.842CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =843BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;844} else if (CurrBlock->succ_size() == 1 && *CurrBlock->succ_begin()) {845// The block is empty, and has a single successor. Use its entry846// location.847CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =848BlockInfo[(*CurrBlock->succ_begin())->getBlockID()].EntryLoc;849}850}851}852853namespace {854855class LockableFactEntry : public FactEntry {856public:857LockableFactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc,858SourceKind Src = Acquired)859: FactEntry(CE, LK, Loc, Src) {}860861void862handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,863SourceLocation JoinLoc, LockErrorKind LEK,864ThreadSafetyHandler &Handler) const override {865if (!asserted() && !negative() && !isUniversal()) {866Handler.handleMutexHeldEndOfScope(getKind(), toString(), loc(), JoinLoc,867LEK);868}869}870871void handleLock(FactSet &FSet, FactManager &FactMan, const FactEntry &entry,872ThreadSafetyHandler &Handler) const override {873Handler.handleDoubleLock(entry.getKind(), entry.toString(), loc(),874entry.loc());875}876877void handleUnlock(FactSet &FSet, FactManager &FactMan,878const CapabilityExpr &Cp, SourceLocation UnlockLoc,879bool FullyRemove,880ThreadSafetyHandler &Handler) const override {881FSet.removeLock(FactMan, Cp);882if (!Cp.negative()) {883FSet.addLock(FactMan, std::make_unique<LockableFactEntry>(884!Cp, LK_Exclusive, UnlockLoc));885}886}887};888889class ScopedLockableFactEntry : public FactEntry {890private:891enum UnderlyingCapabilityKind {892UCK_Acquired, ///< Any kind of acquired capability.893UCK_ReleasedShared, ///< Shared capability that was released.894UCK_ReleasedExclusive, ///< Exclusive capability that was released.895};896897struct UnderlyingCapability {898CapabilityExpr Cap;899UnderlyingCapabilityKind Kind;900};901902SmallVector<UnderlyingCapability, 2> UnderlyingMutexes;903904public:905ScopedLockableFactEntry(const CapabilityExpr &CE, SourceLocation Loc)906: FactEntry(CE, LK_Exclusive, Loc, Acquired) {}907908void addLock(const CapabilityExpr &M) {909UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_Acquired});910}911912void addExclusiveUnlock(const CapabilityExpr &M) {913UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_ReleasedExclusive});914}915916void addSharedUnlock(const CapabilityExpr &M) {917UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_ReleasedShared});918}919920void921handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,922SourceLocation JoinLoc, LockErrorKind LEK,923ThreadSafetyHandler &Handler) const override {924for (const auto &UnderlyingMutex : UnderlyingMutexes) {925const auto *Entry = FSet.findLock(FactMan, UnderlyingMutex.Cap);926if ((UnderlyingMutex.Kind == UCK_Acquired && Entry) ||927(UnderlyingMutex.Kind != UCK_Acquired && !Entry)) {928// If this scoped lock manages another mutex, and if the underlying929// mutex is still/not held, then warn about the underlying mutex.930Handler.handleMutexHeldEndOfScope(UnderlyingMutex.Cap.getKind(),931UnderlyingMutex.Cap.toString(), loc(),932JoinLoc, LEK);933}934}935}936937void handleLock(FactSet &FSet, FactManager &FactMan, const FactEntry &entry,938ThreadSafetyHandler &Handler) const override {939for (const auto &UnderlyingMutex : UnderlyingMutexes) {940if (UnderlyingMutex.Kind == UCK_Acquired)941lock(FSet, FactMan, UnderlyingMutex.Cap, entry.kind(), entry.loc(),942&Handler);943else944unlock(FSet, FactMan, UnderlyingMutex.Cap, entry.loc(), &Handler);945}946}947948void handleUnlock(FactSet &FSet, FactManager &FactMan,949const CapabilityExpr &Cp, SourceLocation UnlockLoc,950bool FullyRemove,951ThreadSafetyHandler &Handler) const override {952assert(!Cp.negative() && "Managing object cannot be negative.");953for (const auto &UnderlyingMutex : UnderlyingMutexes) {954// Remove/lock the underlying mutex if it exists/is still unlocked; warn955// on double unlocking/locking if we're not destroying the scoped object.956ThreadSafetyHandler *TSHandler = FullyRemove ? nullptr : &Handler;957if (UnderlyingMutex.Kind == UCK_Acquired) {958unlock(FSet, FactMan, UnderlyingMutex.Cap, UnlockLoc, TSHandler);959} else {960LockKind kind = UnderlyingMutex.Kind == UCK_ReleasedShared961? LK_Shared962: LK_Exclusive;963lock(FSet, FactMan, UnderlyingMutex.Cap, kind, UnlockLoc, TSHandler);964}965}966if (FullyRemove)967FSet.removeLock(FactMan, Cp);968}969970private:971void lock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp,972LockKind kind, SourceLocation loc,973ThreadSafetyHandler *Handler) const {974if (const FactEntry *Fact = FSet.findLock(FactMan, Cp)) {975if (Handler)976Handler->handleDoubleLock(Cp.getKind(), Cp.toString(), Fact->loc(),977loc);978} else {979FSet.removeLock(FactMan, !Cp);980FSet.addLock(FactMan,981std::make_unique<LockableFactEntry>(Cp, kind, loc, Managed));982}983}984985void unlock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp,986SourceLocation loc, ThreadSafetyHandler *Handler) const {987if (FSet.findLock(FactMan, Cp)) {988FSet.removeLock(FactMan, Cp);989FSet.addLock(FactMan, std::make_unique<LockableFactEntry>(990!Cp, LK_Exclusive, loc));991} else if (Handler) {992SourceLocation PrevLoc;993if (const FactEntry *Neg = FSet.findLock(FactMan, !Cp))994PrevLoc = Neg->loc();995Handler->handleUnmatchedUnlock(Cp.getKind(), Cp.toString(), loc, PrevLoc);996}997}998};9991000/// Class which implements the core thread safety analysis routines.1001class ThreadSafetyAnalyzer {1002friend class BuildLockset;1003friend class threadSafety::BeforeSet;10041005llvm::BumpPtrAllocator Bpa;1006threadSafety::til::MemRegionRef Arena;1007threadSafety::SExprBuilder SxBuilder;10081009ThreadSafetyHandler &Handler;1010const FunctionDecl *CurrentFunction;1011LocalVariableMap LocalVarMap;1012// Maps constructed objects to `this` placeholder prior to initialization.1013llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;1014FactManager FactMan;1015std::vector<CFGBlockInfo> BlockInfo;10161017BeforeSet *GlobalBeforeSet;10181019public:1020ThreadSafetyAnalyzer(ThreadSafetyHandler &H, BeforeSet* Bset)1021: Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}10221023bool inCurrentScope(const CapabilityExpr &CapE);10241025void addLock(FactSet &FSet, std::unique_ptr<FactEntry> Entry,1026bool ReqAttr = false);1027void removeLock(FactSet &FSet, const CapabilityExpr &CapE,1028SourceLocation UnlockLoc, bool FullyRemove, LockKind Kind);10291030template <typename AttrType>1031void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, const Expr *Exp,1032const NamedDecl *D, til::SExpr *Self = nullptr);10331034template <class AttrType>1035void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, const Expr *Exp,1036const NamedDecl *D,1037const CFGBlock *PredBlock, const CFGBlock *CurrBlock,1038Expr *BrE, bool Neg);10391040const CallExpr* getTrylockCallExpr(const Stmt *Cond, LocalVarContext C,1041bool &Negate);10421043void getEdgeLockset(FactSet &Result, const FactSet &ExitSet,1044const CFGBlock* PredBlock,1045const CFGBlock *CurrBlock);10461047bool join(const FactEntry &a, const FactEntry &b, bool CanModify);10481049void intersectAndWarn(FactSet &EntrySet, const FactSet &ExitSet,1050SourceLocation JoinLoc, LockErrorKind EntryLEK,1051LockErrorKind ExitLEK);10521053void intersectAndWarn(FactSet &EntrySet, const FactSet &ExitSet,1054SourceLocation JoinLoc, LockErrorKind LEK) {1055intersectAndWarn(EntrySet, ExitSet, JoinLoc, LEK, LEK);1056}10571058void runAnalysis(AnalysisDeclContext &AC);10591060void warnIfMutexNotHeld(const FactSet &FSet, const NamedDecl *D,1061const Expr *Exp, AccessKind AK, Expr *MutexExp,1062ProtectedOperationKind POK, til::LiteralPtr *Self,1063SourceLocation Loc);1064void warnIfMutexHeld(const FactSet &FSet, const NamedDecl *D, const Expr *Exp,1065Expr *MutexExp, til::LiteralPtr *Self,1066SourceLocation Loc);10671068void checkAccess(const FactSet &FSet, const Expr *Exp, AccessKind AK,1069ProtectedOperationKind POK);1070void checkPtAccess(const FactSet &FSet, const Expr *Exp, AccessKind AK,1071ProtectedOperationKind POK);1072};10731074} // namespace10751076/// Process acquired_before and acquired_after attributes on Vd.1077BeforeSet::BeforeInfo* BeforeSet::insertAttrExprs(const ValueDecl* Vd,1078ThreadSafetyAnalyzer& Analyzer) {1079// Create a new entry for Vd.1080BeforeInfo *Info = nullptr;1081{1082// Keep InfoPtr in its own scope in case BMap is modified later and the1083// reference becomes invalid.1084std::unique_ptr<BeforeInfo> &InfoPtr = BMap[Vd];1085if (!InfoPtr)1086InfoPtr.reset(new BeforeInfo());1087Info = InfoPtr.get();1088}10891090for (const auto *At : Vd->attrs()) {1091switch (At->getKind()) {1092case attr::AcquiredBefore: {1093const auto *A = cast<AcquiredBeforeAttr>(At);10941095// Read exprs from the attribute, and add them to BeforeVect.1096for (const auto *Arg : A->args()) {1097CapabilityExpr Cp =1098Analyzer.SxBuilder.translateAttrExpr(Arg, nullptr);1099if (const ValueDecl *Cpvd = Cp.valueDecl()) {1100Info->Vect.push_back(Cpvd);1101const auto It = BMap.find(Cpvd);1102if (It == BMap.end())1103insertAttrExprs(Cpvd, Analyzer);1104}1105}1106break;1107}1108case attr::AcquiredAfter: {1109const auto *A = cast<AcquiredAfterAttr>(At);11101111// Read exprs from the attribute, and add them to BeforeVect.1112for (const auto *Arg : A->args()) {1113CapabilityExpr Cp =1114Analyzer.SxBuilder.translateAttrExpr(Arg, nullptr);1115if (const ValueDecl *ArgVd = Cp.valueDecl()) {1116// Get entry for mutex listed in attribute1117BeforeInfo *ArgInfo = getBeforeInfoForDecl(ArgVd, Analyzer);1118ArgInfo->Vect.push_back(Vd);1119}1120}1121break;1122}1123default:1124break;1125}1126}11271128return Info;1129}11301131BeforeSet::BeforeInfo *1132BeforeSet::getBeforeInfoForDecl(const ValueDecl *Vd,1133ThreadSafetyAnalyzer &Analyzer) {1134auto It = BMap.find(Vd);1135BeforeInfo *Info = nullptr;1136if (It == BMap.end())1137Info = insertAttrExprs(Vd, Analyzer);1138else1139Info = It->second.get();1140assert(Info && "BMap contained nullptr?");1141return Info;1142}11431144/// Return true if any mutexes in FSet are in the acquired_before set of Vd.1145void BeforeSet::checkBeforeAfter(const ValueDecl* StartVd,1146const FactSet& FSet,1147ThreadSafetyAnalyzer& Analyzer,1148SourceLocation Loc, StringRef CapKind) {1149SmallVector<BeforeInfo*, 8> InfoVect;11501151// Do a depth-first traversal of Vd.1152// Return true if there are cycles.1153std::function<bool (const ValueDecl*)> traverse = [&](const ValueDecl* Vd) {1154if (!Vd)1155return false;11561157BeforeSet::BeforeInfo *Info = getBeforeInfoForDecl(Vd, Analyzer);11581159if (Info->Visited == 1)1160return true;11611162if (Info->Visited == 2)1163return false;11641165if (Info->Vect.empty())1166return false;11671168InfoVect.push_back(Info);1169Info->Visited = 1;1170for (const auto *Vdb : Info->Vect) {1171// Exclude mutexes in our immediate before set.1172if (FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) {1173StringRef L1 = StartVd->getName();1174StringRef L2 = Vdb->getName();1175Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2, Loc);1176}1177// Transitively search other before sets, and warn on cycles.1178if (traverse(Vdb)) {1179if (!CycMap.contains(Vd)) {1180CycMap.insert(std::make_pair(Vd, true));1181StringRef L1 = Vd->getName();1182Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->getLocation());1183}1184}1185}1186Info->Visited = 2;1187return false;1188};11891190traverse(StartVd);11911192for (auto *Info : InfoVect)1193Info->Visited = 0;1194}11951196/// Gets the value decl pointer from DeclRefExprs or MemberExprs.1197static const ValueDecl *getValueDecl(const Expr *Exp) {1198if (const auto *CE = dyn_cast<ImplicitCastExpr>(Exp))1199return getValueDecl(CE->getSubExpr());12001201if (const auto *DR = dyn_cast<DeclRefExpr>(Exp))1202return DR->getDecl();12031204if (const auto *ME = dyn_cast<MemberExpr>(Exp))1205return ME->getMemberDecl();12061207return nullptr;1208}12091210namespace {12111212template <typename Ty>1213class has_arg_iterator_range {1214using yes = char[1];1215using no = char[2];12161217template <typename Inner>1218static yes& test(Inner *I, decltype(I->args()) * = nullptr);12191220template <typename>1221static no& test(...);12221223public:1224static const bool value = sizeof(test<Ty>(nullptr)) == sizeof(yes);1225};12261227} // namespace12281229bool ThreadSafetyAnalyzer::inCurrentScope(const CapabilityExpr &CapE) {1230const threadSafety::til::SExpr *SExp = CapE.sexpr();1231assert(SExp && "Null expressions should be ignored");12321233if (const auto *LP = dyn_cast<til::LiteralPtr>(SExp)) {1234const ValueDecl *VD = LP->clangDecl();1235// Variables defined in a function are always inaccessible.1236if (!VD || !VD->isDefinedOutsideFunctionOrMethod())1237return false;1238// For now we consider static class members to be inaccessible.1239if (isa<CXXRecordDecl>(VD->getDeclContext()))1240return false;1241// Global variables are always in scope.1242return true;1243}12441245// Members are in scope from methods of the same class.1246if (const auto *P = dyn_cast<til::Project>(SExp)) {1247if (!isa_and_nonnull<CXXMethodDecl>(CurrentFunction))1248return false;1249const ValueDecl *VD = P->clangDecl();1250return VD->getDeclContext() == CurrentFunction->getDeclContext();1251}12521253return false;1254}12551256/// Add a new lock to the lockset, warning if the lock is already there.1257/// \param ReqAttr -- true if this is part of an initial Requires attribute.1258void ThreadSafetyAnalyzer::addLock(FactSet &FSet,1259std::unique_ptr<FactEntry> Entry,1260bool ReqAttr) {1261if (Entry->shouldIgnore())1262return;12631264if (!ReqAttr && !Entry->negative()) {1265// look for the negative capability, and remove it from the fact set.1266CapabilityExpr NegC = !*Entry;1267const FactEntry *Nen = FSet.findLock(FactMan, NegC);1268if (Nen) {1269FSet.removeLock(FactMan, NegC);1270}1271else {1272if (inCurrentScope(*Entry) && !Entry->asserted())1273Handler.handleNegativeNotHeld(Entry->getKind(), Entry->toString(),1274NegC.toString(), Entry->loc());1275}1276}12771278// Check before/after constraints1279if (Handler.issueBetaWarnings() &&1280!Entry->asserted() && !Entry->declared()) {1281GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *this,1282Entry->loc(), Entry->getKind());1283}12841285// FIXME: Don't always warn when we have support for reentrant locks.1286if (const FactEntry *Cp = FSet.findLock(FactMan, *Entry)) {1287if (!Entry->asserted())1288Cp->handleLock(FSet, FactMan, *Entry, Handler);1289} else {1290FSet.addLock(FactMan, std::move(Entry));1291}1292}12931294/// Remove a lock from the lockset, warning if the lock is not there.1295/// \param UnlockLoc The source location of the unlock (only used in error msg)1296void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const CapabilityExpr &Cp,1297SourceLocation UnlockLoc,1298bool FullyRemove, LockKind ReceivedKind) {1299if (Cp.shouldIgnore())1300return;13011302const FactEntry *LDat = FSet.findLock(FactMan, Cp);1303if (!LDat) {1304SourceLocation PrevLoc;1305if (const FactEntry *Neg = FSet.findLock(FactMan, !Cp))1306PrevLoc = Neg->loc();1307Handler.handleUnmatchedUnlock(Cp.getKind(), Cp.toString(), UnlockLoc,1308PrevLoc);1309return;1310}13111312// Generic lock removal doesn't care about lock kind mismatches, but1313// otherwise diagnose when the lock kinds are mismatched.1314if (ReceivedKind != LK_Generic && LDat->kind() != ReceivedKind) {1315Handler.handleIncorrectUnlockKind(Cp.getKind(), Cp.toString(), LDat->kind(),1316ReceivedKind, LDat->loc(), UnlockLoc);1317}13181319LDat->handleUnlock(FSet, FactMan, Cp, UnlockLoc, FullyRemove, Handler);1320}13211322/// Extract the list of mutexIDs from the attribute on an expression,1323/// and push them onto Mtxs, discarding any duplicates.1324template <typename AttrType>1325void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,1326const Expr *Exp, const NamedDecl *D,1327til::SExpr *Self) {1328if (Attr->args_size() == 0) {1329// The mutex held is the "this" object.1330CapabilityExpr Cp = SxBuilder.translateAttrExpr(nullptr, D, Exp, Self);1331if (Cp.isInvalid()) {1332warnInvalidLock(Handler, nullptr, D, Exp, Cp.getKind());1333return;1334}1335//else1336if (!Cp.shouldIgnore())1337Mtxs.push_back_nodup(Cp);1338return;1339}13401341for (const auto *Arg : Attr->args()) {1342CapabilityExpr Cp = SxBuilder.translateAttrExpr(Arg, D, Exp, Self);1343if (Cp.isInvalid()) {1344warnInvalidLock(Handler, nullptr, D, Exp, Cp.getKind());1345continue;1346}1347//else1348if (!Cp.shouldIgnore())1349Mtxs.push_back_nodup(Cp);1350}1351}13521353/// Extract the list of mutexIDs from a trylock attribute. If the1354/// trylock applies to the given edge, then push them onto Mtxs, discarding1355/// any duplicates.1356template <class AttrType>1357void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,1358const Expr *Exp, const NamedDecl *D,1359const CFGBlock *PredBlock,1360const CFGBlock *CurrBlock,1361Expr *BrE, bool Neg) {1362// Find out which branch has the lock1363bool branch = false;1364if (const auto *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))1365branch = BLE->getValue();1366else if (const auto *ILE = dyn_cast_or_null<IntegerLiteral>(BrE))1367branch = ILE->getValue().getBoolValue();13681369int branchnum = branch ? 0 : 1;1370if (Neg)1371branchnum = !branchnum;13721373// If we've taken the trylock branch, then add the lock1374int i = 0;1375for (CFGBlock::const_succ_iterator SI = PredBlock->succ_begin(),1376SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) {1377if (*SI == CurrBlock && i == branchnum)1378getMutexIDs(Mtxs, Attr, Exp, D);1379}1380}13811382static bool getStaticBooleanValue(Expr *E, bool &TCond) {1383if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E)) {1384TCond = false;1385return true;1386} else if (const auto *BLE = dyn_cast<CXXBoolLiteralExpr>(E)) {1387TCond = BLE->getValue();1388return true;1389} else if (const auto *ILE = dyn_cast<IntegerLiteral>(E)) {1390TCond = ILE->getValue().getBoolValue();1391return true;1392} else if (auto *CE = dyn_cast<ImplicitCastExpr>(E))1393return getStaticBooleanValue(CE->getSubExpr(), TCond);1394return false;1395}13961397// If Cond can be traced back to a function call, return the call expression.1398// The negate variable should be called with false, and will be set to true1399// if the function call is negated, e.g. if (!mu.tryLock(...))1400const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,1401LocalVarContext C,1402bool &Negate) {1403if (!Cond)1404return nullptr;14051406if (const auto *CallExp = dyn_cast<CallExpr>(Cond)) {1407if (CallExp->getBuiltinCallee() == Builtin::BI__builtin_expect)1408return getTrylockCallExpr(CallExp->getArg(0), C, Negate);1409return CallExp;1410}1411else if (const auto *PE = dyn_cast<ParenExpr>(Cond))1412return getTrylockCallExpr(PE->getSubExpr(), C, Negate);1413else if (const auto *CE = dyn_cast<ImplicitCastExpr>(Cond))1414return getTrylockCallExpr(CE->getSubExpr(), C, Negate);1415else if (const auto *FE = dyn_cast<FullExpr>(Cond))1416return getTrylockCallExpr(FE->getSubExpr(), C, Negate);1417else if (const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {1418const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);1419return getTrylockCallExpr(E, C, Negate);1420}1421else if (const auto *UOP = dyn_cast<UnaryOperator>(Cond)) {1422if (UOP->getOpcode() == UO_LNot) {1423Negate = !Negate;1424return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);1425}1426return nullptr;1427}1428else if (const auto *BOP = dyn_cast<BinaryOperator>(Cond)) {1429if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {1430if (BOP->getOpcode() == BO_NE)1431Negate = !Negate;14321433bool TCond = false;1434if (getStaticBooleanValue(BOP->getRHS(), TCond)) {1435if (!TCond) Negate = !Negate;1436return getTrylockCallExpr(BOP->getLHS(), C, Negate);1437}1438TCond = false;1439if (getStaticBooleanValue(BOP->getLHS(), TCond)) {1440if (!TCond) Negate = !Negate;1441return getTrylockCallExpr(BOP->getRHS(), C, Negate);1442}1443return nullptr;1444}1445if (BOP->getOpcode() == BO_LAnd) {1446// LHS must have been evaluated in a different block.1447return getTrylockCallExpr(BOP->getRHS(), C, Negate);1448}1449if (BOP->getOpcode() == BO_LOr)1450return getTrylockCallExpr(BOP->getRHS(), C, Negate);1451return nullptr;1452} else if (const auto *COP = dyn_cast<ConditionalOperator>(Cond)) {1453bool TCond, FCond;1454if (getStaticBooleanValue(COP->getTrueExpr(), TCond) &&1455getStaticBooleanValue(COP->getFalseExpr(), FCond)) {1456if (TCond && !FCond)1457return getTrylockCallExpr(COP->getCond(), C, Negate);1458if (!TCond && FCond) {1459Negate = !Negate;1460return getTrylockCallExpr(COP->getCond(), C, Negate);1461}1462}1463}1464return nullptr;1465}14661467/// Find the lockset that holds on the edge between PredBlock1468/// and CurrBlock. The edge set is the exit set of PredBlock (passed1469/// as the ExitSet parameter) plus any trylocks, which are conditionally held.1470void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,1471const FactSet &ExitSet,1472const CFGBlock *PredBlock,1473const CFGBlock *CurrBlock) {1474Result = ExitSet;14751476const Stmt *Cond = PredBlock->getTerminatorCondition();1477// We don't acquire try-locks on ?: branches, only when its result is used.1478if (!Cond || isa<ConditionalOperator>(PredBlock->getTerminatorStmt()))1479return;14801481bool Negate = false;1482const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()];1483const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;14841485const auto *Exp = getTrylockCallExpr(Cond, LVarCtx, Negate);1486if (!Exp)1487return;14881489auto *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());1490if(!FunDecl || !FunDecl->hasAttrs())1491return;14921493CapExprSet ExclusiveLocksToAdd;1494CapExprSet SharedLocksToAdd;14951496// If the condition is a call to a Trylock function, then grab the attributes1497for (const auto *Attr : FunDecl->attrs()) {1498switch (Attr->getKind()) {1499case attr::TryAcquireCapability: {1500auto *A = cast<TryAcquireCapabilityAttr>(Attr);1501getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,1502Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(),1503Negate);1504break;1505};1506case attr::ExclusiveTrylockFunction: {1507const auto *A = cast<ExclusiveTrylockFunctionAttr>(Attr);1508getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,1509A->getSuccessValue(), Negate);1510break;1511}1512case attr::SharedTrylockFunction: {1513const auto *A = cast<SharedTrylockFunctionAttr>(Attr);1514getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,1515A->getSuccessValue(), Negate);1516break;1517}1518default:1519break;1520}1521}15221523// Add and remove locks.1524SourceLocation Loc = Exp->getExprLoc();1525for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd)1526addLock(Result, std::make_unique<LockableFactEntry>(ExclusiveLockToAdd,1527LK_Exclusive, Loc));1528for (const auto &SharedLockToAdd : SharedLocksToAdd)1529addLock(Result, std::make_unique<LockableFactEntry>(SharedLockToAdd,1530LK_Shared, Loc));1531}15321533namespace {15341535/// We use this class to visit different types of expressions in1536/// CFGBlocks, and build up the lockset.1537/// An expression may cause us to add or remove locks from the lockset, or else1538/// output error messages related to missing locks.1539/// FIXME: In future, we may be able to not inherit from a visitor.1540class BuildLockset : public ConstStmtVisitor<BuildLockset> {1541friend class ThreadSafetyAnalyzer;15421543ThreadSafetyAnalyzer *Analyzer;1544FactSet FSet;1545// The fact set for the function on exit.1546const FactSet &FunctionExitFSet;1547LocalVariableMap::Context LVarCtx;1548unsigned CtxIndex;15491550// helper functions15511552void checkAccess(const Expr *Exp, AccessKind AK,1553ProtectedOperationKind POK = POK_VarAccess) {1554Analyzer->checkAccess(FSet, Exp, AK, POK);1555}1556void checkPtAccess(const Expr *Exp, AccessKind AK,1557ProtectedOperationKind POK = POK_VarAccess) {1558Analyzer->checkPtAccess(FSet, Exp, AK, POK);1559}15601561void handleCall(const Expr *Exp, const NamedDecl *D,1562til::LiteralPtr *Self = nullptr,1563SourceLocation Loc = SourceLocation());1564void examineArguments(const FunctionDecl *FD,1565CallExpr::const_arg_iterator ArgBegin,1566CallExpr::const_arg_iterator ArgEnd,1567bool SkipFirstParam = false);15681569public:1570BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info,1571const FactSet &FunctionExitFSet)1572: ConstStmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),1573FunctionExitFSet(FunctionExitFSet), LVarCtx(Info.EntryContext),1574CtxIndex(Info.EntryIndex) {}15751576void VisitUnaryOperator(const UnaryOperator *UO);1577void VisitBinaryOperator(const BinaryOperator *BO);1578void VisitCastExpr(const CastExpr *CE);1579void VisitCallExpr(const CallExpr *Exp);1580void VisitCXXConstructExpr(const CXXConstructExpr *Exp);1581void VisitDeclStmt(const DeclStmt *S);1582void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Exp);1583void VisitReturnStmt(const ReturnStmt *S);1584};15851586} // namespace15871588/// Warn if the LSet does not contain a lock sufficient to protect access1589/// of at least the passed in AccessKind.1590void ThreadSafetyAnalyzer::warnIfMutexNotHeld(1591const FactSet &FSet, const NamedDecl *D, const Expr *Exp, AccessKind AK,1592Expr *MutexExp, ProtectedOperationKind POK, til::LiteralPtr *Self,1593SourceLocation Loc) {1594LockKind LK = getLockKindFromAccessKind(AK);1595CapabilityExpr Cp = SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);1596if (Cp.isInvalid()) {1597warnInvalidLock(Handler, MutexExp, D, Exp, Cp.getKind());1598return;1599} else if (Cp.shouldIgnore()) {1600return;1601}16021603if (Cp.negative()) {1604// Negative capabilities act like locks excluded1605const FactEntry *LDat = FSet.findLock(FactMan, !Cp);1606if (LDat) {1607Handler.handleFunExcludesLock(Cp.getKind(), D->getNameAsString(),1608(!Cp).toString(), Loc);1609return;1610}16111612// If this does not refer to a negative capability in the same class,1613// then stop here.1614if (!inCurrentScope(Cp))1615return;16161617// Otherwise the negative requirement must be propagated to the caller.1618LDat = FSet.findLock(FactMan, Cp);1619if (!LDat) {1620Handler.handleNegativeNotHeld(D, Cp.toString(), Loc);1621}1622return;1623}16241625const FactEntry *LDat = FSet.findLockUniv(FactMan, Cp);1626bool NoError = true;1627if (!LDat) {1628// No exact match found. Look for a partial match.1629LDat = FSet.findPartialMatch(FactMan, Cp);1630if (LDat) {1631// Warn that there's no precise match.1632std::string PartMatchStr = LDat->toString();1633StringRef PartMatchName(PartMatchStr);1634Handler.handleMutexNotHeld(Cp.getKind(), D, POK, Cp.toString(), LK, Loc,1635&PartMatchName);1636} else {1637// Warn that there's no match at all.1638Handler.handleMutexNotHeld(Cp.getKind(), D, POK, Cp.toString(), LK, Loc);1639}1640NoError = false;1641}1642// Make sure the mutex we found is the right kind.1643if (NoError && LDat && !LDat->isAtLeast(LK)) {1644Handler.handleMutexNotHeld(Cp.getKind(), D, POK, Cp.toString(), LK, Loc);1645}1646}16471648/// Warn if the LSet contains the given lock.1649void ThreadSafetyAnalyzer::warnIfMutexHeld(const FactSet &FSet,1650const NamedDecl *D, const Expr *Exp,1651Expr *MutexExp,1652til::LiteralPtr *Self,1653SourceLocation Loc) {1654CapabilityExpr Cp = SxBuilder.translateAttrExpr(MutexExp, D, Exp, Self);1655if (Cp.isInvalid()) {1656warnInvalidLock(Handler, MutexExp, D, Exp, Cp.getKind());1657return;1658} else if (Cp.shouldIgnore()) {1659return;1660}16611662const FactEntry *LDat = FSet.findLock(FactMan, Cp);1663if (LDat) {1664Handler.handleFunExcludesLock(Cp.getKind(), D->getNameAsString(),1665Cp.toString(), Loc);1666}1667}16681669/// Checks guarded_by and pt_guarded_by attributes.1670/// Whenever we identify an access (read or write) to a DeclRefExpr that is1671/// marked with guarded_by, we must ensure the appropriate mutexes are held.1672/// Similarly, we check if the access is to an expression that dereferences1673/// a pointer marked with pt_guarded_by.1674void ThreadSafetyAnalyzer::checkAccess(const FactSet &FSet, const Expr *Exp,1675AccessKind AK,1676ProtectedOperationKind POK) {1677Exp = Exp->IgnoreImplicit()->IgnoreParenCasts();16781679SourceLocation Loc = Exp->getExprLoc();16801681// Local variables of reference type cannot be re-assigned;1682// map them to their initializer.1683while (const auto *DRE = dyn_cast<DeclRefExpr>(Exp)) {1684const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()->getCanonicalDecl());1685if (VD && VD->isLocalVarDecl() && VD->getType()->isReferenceType()) {1686if (const auto *E = VD->getInit()) {1687// Guard against self-initialization. e.g., int &i = i;1688if (E == Exp)1689break;1690Exp = E;1691continue;1692}1693}1694break;1695}16961697if (const auto *UO = dyn_cast<UnaryOperator>(Exp)) {1698// For dereferences1699if (UO->getOpcode() == UO_Deref)1700checkPtAccess(FSet, UO->getSubExpr(), AK, POK);1701return;1702}17031704if (const auto *BO = dyn_cast<BinaryOperator>(Exp)) {1705switch (BO->getOpcode()) {1706case BO_PtrMemD: // .*1707return checkAccess(FSet, BO->getLHS(), AK, POK);1708case BO_PtrMemI: // ->*1709return checkPtAccess(FSet, BO->getLHS(), AK, POK);1710default:1711return;1712}1713}17141715if (const auto *AE = dyn_cast<ArraySubscriptExpr>(Exp)) {1716checkPtAccess(FSet, AE->getLHS(), AK, POK);1717return;1718}17191720if (const auto *ME = dyn_cast<MemberExpr>(Exp)) {1721if (ME->isArrow())1722checkPtAccess(FSet, ME->getBase(), AK, POK);1723else1724checkAccess(FSet, ME->getBase(), AK, POK);1725}17261727const ValueDecl *D = getValueDecl(Exp);1728if (!D || !D->hasAttrs())1729return;17301731if (D->hasAttr<GuardedVarAttr>() && FSet.isEmpty(FactMan)) {1732Handler.handleNoMutexHeld(D, POK, AK, Loc);1733}17341735for (const auto *I : D->specific_attrs<GuardedByAttr>())1736warnIfMutexNotHeld(FSet, D, Exp, AK, I->getArg(), POK, nullptr, Loc);1737}17381739/// Checks pt_guarded_by and pt_guarded_var attributes.1740/// POK is the same operationKind that was passed to checkAccess.1741void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp,1742AccessKind AK,1743ProtectedOperationKind POK) {1744while (true) {1745if (const auto *PE = dyn_cast<ParenExpr>(Exp)) {1746Exp = PE->getSubExpr();1747continue;1748}1749if (const auto *CE = dyn_cast<CastExpr>(Exp)) {1750if (CE->getCastKind() == CK_ArrayToPointerDecay) {1751// If it's an actual array, and not a pointer, then it's elements1752// are protected by GUARDED_BY, not PT_GUARDED_BY;1753checkAccess(FSet, CE->getSubExpr(), AK, POK);1754return;1755}1756Exp = CE->getSubExpr();1757continue;1758}1759break;1760}17611762// Pass by reference warnings are under a different flag.1763ProtectedOperationKind PtPOK = POK_VarDereference;1764if (POK == POK_PassByRef) PtPOK = POK_PtPassByRef;1765if (POK == POK_ReturnByRef)1766PtPOK = POK_PtReturnByRef;17671768const ValueDecl *D = getValueDecl(Exp);1769if (!D || !D->hasAttrs())1770return;17711772if (D->hasAttr<PtGuardedVarAttr>() && FSet.isEmpty(FactMan))1773Handler.handleNoMutexHeld(D, PtPOK, AK, Exp->getExprLoc());17741775for (auto const *I : D->specific_attrs<PtGuardedByAttr>())1776warnIfMutexNotHeld(FSet, D, Exp, AK, I->getArg(), PtPOK, nullptr,1777Exp->getExprLoc());1778}17791780/// Process a function call, method call, constructor call,1781/// or destructor call. This involves looking at the attributes on the1782/// corresponding function/method/constructor/destructor, issuing warnings,1783/// and updating the locksets accordingly.1784///1785/// FIXME: For classes annotated with one of the guarded annotations, we need1786/// to treat const method calls as reads and non-const method calls as writes,1787/// and check that the appropriate locks are held. Non-const method calls with1788/// the same signature as const method calls can be also treated as reads.1789///1790/// \param Exp The call expression.1791/// \param D The callee declaration.1792/// \param Self If \p Exp = nullptr, the implicit this argument or the argument1793/// of an implicitly called cleanup function.1794/// \param Loc If \p Exp = nullptr, the location.1795void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,1796til::LiteralPtr *Self, SourceLocation Loc) {1797CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;1798CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;1799CapExprSet ScopedReqsAndExcludes;18001801// Figure out if we're constructing an object of scoped lockable class1802CapabilityExpr Scp;1803if (Exp) {1804assert(!Self);1805const auto *TagT = Exp->getType()->getAs<TagType>();1806if (TagT && Exp->isPRValue()) {1807std::pair<til::LiteralPtr *, StringRef> Placeholder =1808Analyzer->SxBuilder.createThisPlaceholder(Exp);1809[[maybe_unused]] auto inserted =1810Analyzer->ConstructedObjects.insert({Exp, Placeholder.first});1811assert(inserted.second && "Are we visiting the same expression again?");1812if (isa<CXXConstructExpr>(Exp))1813Self = Placeholder.first;1814if (TagT->getDecl()->hasAttr<ScopedLockableAttr>())1815Scp = CapabilityExpr(Placeholder.first, Placeholder.second, false);1816}18171818assert(Loc.isInvalid());1819Loc = Exp->getExprLoc();1820}18211822for(const Attr *At : D->attrs()) {1823switch (At->getKind()) {1824// When we encounter a lock function, we need to add the lock to our1825// lockset.1826case attr::AcquireCapability: {1827const auto *A = cast<AcquireCapabilityAttr>(At);1828Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd1829: ExclusiveLocksToAdd,1830A, Exp, D, Self);1831break;1832}18331834// An assert will add a lock to the lockset, but will not generate1835// a warning if it is already there, and will not generate a warning1836// if it is not removed.1837case attr::AssertExclusiveLock: {1838const auto *A = cast<AssertExclusiveLockAttr>(At);18391840CapExprSet AssertLocks;1841Analyzer->getMutexIDs(AssertLocks, A, Exp, D, Self);1842for (const auto &AssertLock : AssertLocks)1843Analyzer->addLock(1844FSet, std::make_unique<LockableFactEntry>(1845AssertLock, LK_Exclusive, Loc, FactEntry::Asserted));1846break;1847}1848case attr::AssertSharedLock: {1849const auto *A = cast<AssertSharedLockAttr>(At);18501851CapExprSet AssertLocks;1852Analyzer->getMutexIDs(AssertLocks, A, Exp, D, Self);1853for (const auto &AssertLock : AssertLocks)1854Analyzer->addLock(1855FSet, std::make_unique<LockableFactEntry>(1856AssertLock, LK_Shared, Loc, FactEntry::Asserted));1857break;1858}18591860case attr::AssertCapability: {1861const auto *A = cast<AssertCapabilityAttr>(At);1862CapExprSet AssertLocks;1863Analyzer->getMutexIDs(AssertLocks, A, Exp, D, Self);1864for (const auto &AssertLock : AssertLocks)1865Analyzer->addLock(FSet, std::make_unique<LockableFactEntry>(1866AssertLock,1867A->isShared() ? LK_Shared : LK_Exclusive,1868Loc, FactEntry::Asserted));1869break;1870}18711872// When we encounter an unlock function, we need to remove unlocked1873// mutexes from the lockset, and flag a warning if they are not there.1874case attr::ReleaseCapability: {1875const auto *A = cast<ReleaseCapabilityAttr>(At);1876if (A->isGeneric())1877Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, Self);1878else if (A->isShared())1879Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, Self);1880else1881Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, Self);1882break;1883}18841885case attr::RequiresCapability: {1886const auto *A = cast<RequiresCapabilityAttr>(At);1887for (auto *Arg : A->args()) {1888Analyzer->warnIfMutexNotHeld(FSet, D, Exp,1889A->isShared() ? AK_Read : AK_Written,1890Arg, POK_FunctionCall, Self, Loc);1891// use for adopting a lock1892if (!Scp.shouldIgnore())1893Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp, D, Self);1894}1895break;1896}18971898case attr::LocksExcluded: {1899const auto *A = cast<LocksExcludedAttr>(At);1900for (auto *Arg : A->args()) {1901Analyzer->warnIfMutexHeld(FSet, D, Exp, Arg, Self, Loc);1902// use for deferring a lock1903if (!Scp.shouldIgnore())1904Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp, D, Self);1905}1906break;1907}19081909// Ignore attributes unrelated to thread-safety1910default:1911break;1912}1913}19141915// Remove locks first to allow lock upgrading/downgrading.1916// FIXME -- should only fully remove if the attribute refers to 'this'.1917bool Dtor = isa<CXXDestructorDecl>(D);1918for (const auto &M : ExclusiveLocksToRemove)1919Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Exclusive);1920for (const auto &M : SharedLocksToRemove)1921Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Shared);1922for (const auto &M : GenericLocksToRemove)1923Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Generic);19241925// Add locks.1926FactEntry::SourceKind Source =1927!Scp.shouldIgnore() ? FactEntry::Managed : FactEntry::Acquired;1928for (const auto &M : ExclusiveLocksToAdd)1929Analyzer->addLock(FSet, std::make_unique<LockableFactEntry>(M, LK_Exclusive,1930Loc, Source));1931for (const auto &M : SharedLocksToAdd)1932Analyzer->addLock(1933FSet, std::make_unique<LockableFactEntry>(M, LK_Shared, Loc, Source));19341935if (!Scp.shouldIgnore()) {1936// Add the managing object as a dummy mutex, mapped to the underlying mutex.1937auto ScopedEntry = std::make_unique<ScopedLockableFactEntry>(Scp, Loc);1938for (const auto &M : ExclusiveLocksToAdd)1939ScopedEntry->addLock(M);1940for (const auto &M : SharedLocksToAdd)1941ScopedEntry->addLock(M);1942for (const auto &M : ScopedReqsAndExcludes)1943ScopedEntry->addLock(M);1944for (const auto &M : ExclusiveLocksToRemove)1945ScopedEntry->addExclusiveUnlock(M);1946for (const auto &M : SharedLocksToRemove)1947ScopedEntry->addSharedUnlock(M);1948Analyzer->addLock(FSet, std::move(ScopedEntry));1949}1950}19511952/// For unary operations which read and write a variable, we need to1953/// check whether we hold any required mutexes. Reads are checked in1954/// VisitCastExpr.1955void BuildLockset::VisitUnaryOperator(const UnaryOperator *UO) {1956switch (UO->getOpcode()) {1957case UO_PostDec:1958case UO_PostInc:1959case UO_PreDec:1960case UO_PreInc:1961checkAccess(UO->getSubExpr(), AK_Written);1962break;1963default:1964break;1965}1966}19671968/// For binary operations which assign to a variable (writes), we need to check1969/// whether we hold any required mutexes.1970/// FIXME: Deal with non-primitive types.1971void BuildLockset::VisitBinaryOperator(const BinaryOperator *BO) {1972if (!BO->isAssignmentOp())1973return;19741975// adjust the context1976LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);19771978checkAccess(BO->getLHS(), AK_Written);1979}19801981/// Whenever we do an LValue to Rvalue cast, we are reading a variable and1982/// need to ensure we hold any required mutexes.1983/// FIXME: Deal with non-primitive types.1984void BuildLockset::VisitCastExpr(const CastExpr *CE) {1985if (CE->getCastKind() != CK_LValueToRValue)1986return;1987checkAccess(CE->getSubExpr(), AK_Read);1988}19891990void BuildLockset::examineArguments(const FunctionDecl *FD,1991CallExpr::const_arg_iterator ArgBegin,1992CallExpr::const_arg_iterator ArgEnd,1993bool SkipFirstParam) {1994// Currently we can't do anything if we don't know the function declaration.1995if (!FD)1996return;19971998// NO_THREAD_SAFETY_ANALYSIS does double duty here. Normally it1999// only turns off checking within the body of a function, but we also2000// use it to turn off checking in arguments to the function. This2001// could result in some false negatives, but the alternative is to2002// create yet another attribute.2003if (FD->hasAttr<NoThreadSafetyAnalysisAttr>())2004return;20052006const ArrayRef<ParmVarDecl *> Params = FD->parameters();2007auto Param = Params.begin();2008if (SkipFirstParam)2009++Param;20102011// There can be default arguments, so we stop when one iterator is at end().2012for (auto Arg = ArgBegin; Param != Params.end() && Arg != ArgEnd;2013++Param, ++Arg) {2014QualType Qt = (*Param)->getType();2015if (Qt->isReferenceType())2016checkAccess(*Arg, AK_Read, POK_PassByRef);2017}2018}20192020void BuildLockset::VisitCallExpr(const CallExpr *Exp) {2021if (const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {2022const auto *ME = dyn_cast<MemberExpr>(CE->getCallee());2023// ME can be null when calling a method pointer2024const CXXMethodDecl *MD = CE->getMethodDecl();20252026if (ME && MD) {2027if (ME->isArrow()) {2028// Should perhaps be AK_Written if !MD->isConst().2029checkPtAccess(CE->getImplicitObjectArgument(), AK_Read);2030} else {2031// Should perhaps be AK_Written if !MD->isConst().2032checkAccess(CE->getImplicitObjectArgument(), AK_Read);2033}2034}20352036examineArguments(CE->getDirectCallee(), CE->arg_begin(), CE->arg_end());2037} else if (const auto *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {2038OverloadedOperatorKind OEop = OE->getOperator();2039switch (OEop) {2040case OO_Equal:2041case OO_PlusEqual:2042case OO_MinusEqual:2043case OO_StarEqual:2044case OO_SlashEqual:2045case OO_PercentEqual:2046case OO_CaretEqual:2047case OO_AmpEqual:2048case OO_PipeEqual:2049case OO_LessLessEqual:2050case OO_GreaterGreaterEqual:2051checkAccess(OE->getArg(1), AK_Read);2052[[fallthrough]];2053case OO_PlusPlus:2054case OO_MinusMinus:2055checkAccess(OE->getArg(0), AK_Written);2056break;2057case OO_Star:2058case OO_ArrowStar:2059case OO_Arrow:2060case OO_Subscript:2061if (!(OEop == OO_Star && OE->getNumArgs() > 1)) {2062// Grrr. operator* can be multiplication...2063checkPtAccess(OE->getArg(0), AK_Read);2064}2065[[fallthrough]];2066default: {2067// TODO: get rid of this, and rely on pass-by-ref instead.2068const Expr *Obj = OE->getArg(0);2069checkAccess(Obj, AK_Read);2070// Check the remaining arguments. For method operators, the first2071// argument is the implicit self argument, and doesn't appear in the2072// FunctionDecl, but for non-methods it does.2073const FunctionDecl *FD = OE->getDirectCallee();2074examineArguments(FD, std::next(OE->arg_begin()), OE->arg_end(),2075/*SkipFirstParam*/ !isa<CXXMethodDecl>(FD));2076break;2077}2078}2079} else {2080examineArguments(Exp->getDirectCallee(), Exp->arg_begin(), Exp->arg_end());2081}20822083auto *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());2084if(!D || !D->hasAttrs())2085return;2086handleCall(Exp, D);2087}20882089void BuildLockset::VisitCXXConstructExpr(const CXXConstructExpr *Exp) {2090const CXXConstructorDecl *D = Exp->getConstructor();2091if (D && D->isCopyConstructor()) {2092const Expr* Source = Exp->getArg(0);2093checkAccess(Source, AK_Read);2094} else {2095examineArguments(D, Exp->arg_begin(), Exp->arg_end());2096}2097if (D && D->hasAttrs())2098handleCall(Exp, D);2099}21002101static const Expr *UnpackConstruction(const Expr *E) {2102if (auto *CE = dyn_cast<CastExpr>(E))2103if (CE->getCastKind() == CK_NoOp)2104E = CE->getSubExpr()->IgnoreParens();2105if (auto *CE = dyn_cast<CastExpr>(E))2106if (CE->getCastKind() == CK_ConstructorConversion ||2107CE->getCastKind() == CK_UserDefinedConversion)2108E = CE->getSubExpr();2109if (auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))2110E = BTE->getSubExpr();2111return E;2112}21132114void BuildLockset::VisitDeclStmt(const DeclStmt *S) {2115// adjust the context2116LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);21172118for (auto *D : S->getDeclGroup()) {2119if (auto *VD = dyn_cast_or_null<VarDecl>(D)) {2120const Expr *E = VD->getInit();2121if (!E)2122continue;2123E = E->IgnoreParens();21242125// handle constructors that involve temporaries2126if (auto *EWC = dyn_cast<ExprWithCleanups>(E))2127E = EWC->getSubExpr()->IgnoreParens();2128E = UnpackConstruction(E);21292130if (auto Object = Analyzer->ConstructedObjects.find(E);2131Object != Analyzer->ConstructedObjects.end()) {2132Object->second->setClangDecl(VD);2133Analyzer->ConstructedObjects.erase(Object);2134}2135}2136}2137}21382139void BuildLockset::VisitMaterializeTemporaryExpr(2140const MaterializeTemporaryExpr *Exp) {2141if (const ValueDecl *ExtD = Exp->getExtendingDecl()) {2142if (auto Object = Analyzer->ConstructedObjects.find(2143UnpackConstruction(Exp->getSubExpr()));2144Object != Analyzer->ConstructedObjects.end()) {2145Object->second->setClangDecl(ExtD);2146Analyzer->ConstructedObjects.erase(Object);2147}2148}2149}21502151void BuildLockset::VisitReturnStmt(const ReturnStmt *S) {2152if (Analyzer->CurrentFunction == nullptr)2153return;2154const Expr *RetVal = S->getRetValue();2155if (!RetVal)2156return;21572158// If returning by reference, check that the function requires the appropriate2159// capabilities.2160const QualType ReturnType =2161Analyzer->CurrentFunction->getReturnType().getCanonicalType();2162if (ReturnType->isLValueReferenceType()) {2163Analyzer->checkAccess(2164FunctionExitFSet, RetVal,2165ReturnType->getPointeeType().isConstQualified() ? AK_Read : AK_Written,2166POK_ReturnByRef);2167}2168}21692170/// Given two facts merging on a join point, possibly warn and decide whether to2171/// keep or replace.2172///2173/// \param CanModify Whether we can replace \p A by \p B.2174/// \return false if we should keep \p A, true if we should take \p B.2175bool ThreadSafetyAnalyzer::join(const FactEntry &A, const FactEntry &B,2176bool CanModify) {2177if (A.kind() != B.kind()) {2178// For managed capabilities, the destructor should unlock in the right mode2179// anyway. For asserted capabilities no unlocking is needed.2180if ((A.managed() || A.asserted()) && (B.managed() || B.asserted())) {2181// The shared capability subsumes the exclusive capability, if possible.2182bool ShouldTakeB = B.kind() == LK_Shared;2183if (CanModify || !ShouldTakeB)2184return ShouldTakeB;2185}2186Handler.handleExclusiveAndShared(B.getKind(), B.toString(), B.loc(),2187A.loc());2188// Take the exclusive capability to reduce further warnings.2189return CanModify && B.kind() == LK_Exclusive;2190} else {2191// The non-asserted capability is the one we want to track.2192return CanModify && A.asserted() && !B.asserted();2193}2194}21952196/// Compute the intersection of two locksets and issue warnings for any2197/// locks in the symmetric difference.2198///2199/// This function is used at a merge point in the CFG when comparing the lockset2200/// of each branch being merged. For example, given the following sequence:2201/// A; if () then B; else C; D; we need to check that the lockset after B and C2202/// are the same. In the event of a difference, we use the intersection of these2203/// two locksets at the start of D.2204///2205/// \param EntrySet A lockset for entry into a (possibly new) block.2206/// \param ExitSet The lockset on exiting a preceding block.2207/// \param JoinLoc The location of the join point for error reporting2208/// \param EntryLEK The warning if a mutex is missing from \p EntrySet.2209/// \param ExitLEK The warning if a mutex is missing from \p ExitSet.2210void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &EntrySet,2211const FactSet &ExitSet,2212SourceLocation JoinLoc,2213LockErrorKind EntryLEK,2214LockErrorKind ExitLEK) {2215FactSet EntrySetOrig = EntrySet;22162217// Find locks in ExitSet that conflict or are not in EntrySet, and warn.2218for (const auto &Fact : ExitSet) {2219const FactEntry &ExitFact = FactMan[Fact];22202221FactSet::iterator EntryIt = EntrySet.findLockIter(FactMan, ExitFact);2222if (EntryIt != EntrySet.end()) {2223if (join(FactMan[*EntryIt], ExitFact,2224EntryLEK != LEK_LockedSomeLoopIterations))2225*EntryIt = Fact;2226} else if (!ExitFact.managed()) {2227ExitFact.handleRemovalFromIntersection(ExitSet, FactMan, JoinLoc,2228EntryLEK, Handler);2229}2230}22312232// Find locks in EntrySet that are not in ExitSet, and remove them.2233for (const auto &Fact : EntrySetOrig) {2234const FactEntry *EntryFact = &FactMan[Fact];2235const FactEntry *ExitFact = ExitSet.findLock(FactMan, *EntryFact);22362237if (!ExitFact) {2238if (!EntryFact->managed() || ExitLEK == LEK_LockedSomeLoopIterations)2239EntryFact->handleRemovalFromIntersection(EntrySetOrig, FactMan, JoinLoc,2240ExitLEK, Handler);2241if (ExitLEK == LEK_LockedSomePredecessors)2242EntrySet.removeLock(FactMan, *EntryFact);2243}2244}2245}22462247// Return true if block B never continues to its successors.2248static bool neverReturns(const CFGBlock *B) {2249if (B->hasNoReturnElement())2250return true;2251if (B->empty())2252return false;22532254CFGElement Last = B->back();2255if (std::optional<CFGStmt> S = Last.getAs<CFGStmt>()) {2256if (isa<CXXThrowExpr>(S->getStmt()))2257return true;2258}2259return false;2260}22612262/// Check a function's CFG for thread-safety violations.2263///2264/// We traverse the blocks in the CFG, compute the set of mutexes that are held2265/// at the end of each block, and issue warnings for thread safety violations.2266/// Each block in the CFG is traversed exactly once.2267void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {2268// TODO: this whole function needs be rewritten as a visitor for CFGWalker.2269// For now, we just use the walker to set things up.2270threadSafety::CFGWalker walker;2271if (!walker.init(AC))2272return;22732274// AC.dumpCFG(true);2275// threadSafety::printSCFG(walker);22762277CFG *CFGraph = walker.getGraph();2278const NamedDecl *D = walker.getDecl();2279CurrentFunction = dyn_cast<FunctionDecl>(D);22802281if (D->hasAttr<NoThreadSafetyAnalysisAttr>())2282return;22832284// FIXME: Do something a bit more intelligent inside constructor and2285// destructor code. Constructors and destructors must assume unique access2286// to 'this', so checks on member variable access is disabled, but we should2287// still enable checks on other objects.2288if (isa<CXXConstructorDecl>(D))2289return; // Don't check inside constructors.2290if (isa<CXXDestructorDecl>(D))2291return; // Don't check inside destructors.22922293Handler.enterFunction(CurrentFunction);22942295BlockInfo.resize(CFGraph->getNumBlockIDs(),2296CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));22972298// We need to explore the CFG via a "topological" ordering.2299// That way, we will be guaranteed to have information about required2300// predecessor locksets when exploring a new block.2301const PostOrderCFGView *SortedGraph = walker.getSortedGraph();2302PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph);23032304CFGBlockInfo &Initial = BlockInfo[CFGraph->getEntry().getBlockID()];2305CFGBlockInfo &Final = BlockInfo[CFGraph->getExit().getBlockID()];23062307// Mark entry block as reachable2308Initial.Reachable = true;23092310// Compute SSA names for local variables2311LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);23122313// Fill in source locations for all CFGBlocks.2314findBlockLocations(CFGraph, SortedGraph, BlockInfo);23152316CapExprSet ExclusiveLocksAcquired;2317CapExprSet SharedLocksAcquired;2318CapExprSet LocksReleased;23192320// Add locks from exclusive_locks_required and shared_locks_required2321// to initial lockset. Also turn off checking for lock and unlock functions.2322// FIXME: is there a more intelligent way to check lock/unlock functions?2323if (!SortedGraph->empty() && D->hasAttrs()) {2324assert(*SortedGraph->begin() == &CFGraph->getEntry());2325FactSet &InitialLockset = Initial.EntrySet;23262327CapExprSet ExclusiveLocksToAdd;2328CapExprSet SharedLocksToAdd;23292330SourceLocation Loc = D->getLocation();2331for (const auto *Attr : D->attrs()) {2332Loc = Attr->getLocation();2333if (const auto *A = dyn_cast<RequiresCapabilityAttr>(Attr)) {2334getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,2335nullptr, D);2336} else if (const auto *A = dyn_cast<ReleaseCapabilityAttr>(Attr)) {2337// UNLOCK_FUNCTION() is used to hide the underlying lock implementation.2338// We must ignore such methods.2339if (A->args_size() == 0)2340return;2341getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,2342nullptr, D);2343getMutexIDs(LocksReleased, A, nullptr, D);2344} else if (const auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) {2345if (A->args_size() == 0)2346return;2347getMutexIDs(A->isShared() ? SharedLocksAcquired2348: ExclusiveLocksAcquired,2349A, nullptr, D);2350} else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) {2351// Don't try to check trylock functions for now.2352return;2353} else if (isa<SharedTrylockFunctionAttr>(Attr)) {2354// Don't try to check trylock functions for now.2355return;2356} else if (isa<TryAcquireCapabilityAttr>(Attr)) {2357// Don't try to check trylock functions for now.2358return;2359}2360}23612362// FIXME -- Loc can be wrong here.2363for (const auto &Mu : ExclusiveLocksToAdd) {2364auto Entry = std::make_unique<LockableFactEntry>(Mu, LK_Exclusive, Loc,2365FactEntry::Declared);2366addLock(InitialLockset, std::move(Entry), true);2367}2368for (const auto &Mu : SharedLocksToAdd) {2369auto Entry = std::make_unique<LockableFactEntry>(Mu, LK_Shared, Loc,2370FactEntry::Declared);2371addLock(InitialLockset, std::move(Entry), true);2372}2373}23742375// Compute the expected exit set.2376// By default, we expect all locks held on entry to be held on exit.2377FactSet ExpectedFunctionExitSet = Initial.EntrySet;23782379// Adjust the expected exit set by adding or removing locks, as declared2380// by *-LOCK_FUNCTION and UNLOCK_FUNCTION. The intersect below will then2381// issue the appropriate warning.2382// FIXME: the location here is not quite right.2383for (const auto &Lock : ExclusiveLocksAcquired)2384ExpectedFunctionExitSet.addLock(2385FactMan, std::make_unique<LockableFactEntry>(Lock, LK_Exclusive,2386D->getLocation()));2387for (const auto &Lock : SharedLocksAcquired)2388ExpectedFunctionExitSet.addLock(2389FactMan,2390std::make_unique<LockableFactEntry>(Lock, LK_Shared, D->getLocation()));2391for (const auto &Lock : LocksReleased)2392ExpectedFunctionExitSet.removeLock(FactMan, Lock);23932394for (const auto *CurrBlock : *SortedGraph) {2395unsigned CurrBlockID = CurrBlock->getBlockID();2396CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];23972398// Use the default initial lockset in case there are no predecessors.2399VisitedBlocks.insert(CurrBlock);24002401// Iterate through the predecessor blocks and warn if the lockset for all2402// predecessors is not the same. We take the entry lockset of the current2403// block to be the intersection of all previous locksets.2404// FIXME: By keeping the intersection, we may output more errors in future2405// for a lock which is not in the intersection, but was in the union. We2406// may want to also keep the union in future. As an example, let's say2407// the intersection contains Mutex L, and the union contains L and M.2408// Later we unlock M. At this point, we would output an error because we2409// never locked M; although the real error is probably that we forgot to2410// lock M on all code paths. Conversely, let's say that later we lock M.2411// In this case, we should compare against the intersection instead of the2412// union because the real error is probably that we forgot to unlock M on2413// all code paths.2414bool LocksetInitialized = false;2415for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),2416PE = CurrBlock->pred_end(); PI != PE; ++PI) {2417// if *PI -> CurrBlock is a back edge2418if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI))2419continue;24202421unsigned PrevBlockID = (*PI)->getBlockID();2422CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];24232424// Ignore edges from blocks that can't return.2425if (neverReturns(*PI) || !PrevBlockInfo->Reachable)2426continue;24272428// Okay, we can reach this block from the entry.2429CurrBlockInfo->Reachable = true;24302431FactSet PrevLockset;2432getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);24332434if (!LocksetInitialized) {2435CurrBlockInfo->EntrySet = PrevLockset;2436LocksetInitialized = true;2437} else {2438// Surprisingly 'continue' doesn't always produce back edges, because2439// the CFG has empty "transition" blocks where they meet with the end2440// of the regular loop body. We still want to diagnose them as loop.2441intersectAndWarn(2442CurrBlockInfo->EntrySet, PrevLockset, CurrBlockInfo->EntryLoc,2443isa_and_nonnull<ContinueStmt>((*PI)->getTerminatorStmt())2444? LEK_LockedSomeLoopIterations2445: LEK_LockedSomePredecessors);2446}2447}24482449// Skip rest of block if it's not reachable.2450if (!CurrBlockInfo->Reachable)2451continue;24522453BuildLockset LocksetBuilder(this, *CurrBlockInfo, ExpectedFunctionExitSet);24542455// Visit all the statements in the basic block.2456for (const auto &BI : *CurrBlock) {2457switch (BI.getKind()) {2458case CFGElement::Statement: {2459CFGStmt CS = BI.castAs<CFGStmt>();2460LocksetBuilder.Visit(CS.getStmt());2461break;2462}2463// Ignore BaseDtor and MemberDtor for now.2464case CFGElement::AutomaticObjectDtor: {2465CFGAutomaticObjDtor AD = BI.castAs<CFGAutomaticObjDtor>();2466const auto *DD = AD.getDestructorDecl(AC.getASTContext());2467if (!DD->hasAttrs())2468break;24692470LocksetBuilder.handleCall(nullptr, DD,2471SxBuilder.createVariable(AD.getVarDecl()),2472AD.getTriggerStmt()->getEndLoc());2473break;2474}24752476case CFGElement::CleanupFunction: {2477const CFGCleanupFunction &CF = BI.castAs<CFGCleanupFunction>();2478LocksetBuilder.handleCall(/*Exp=*/nullptr, CF.getFunctionDecl(),2479SxBuilder.createVariable(CF.getVarDecl()),2480CF.getVarDecl()->getLocation());2481break;2482}24832484case CFGElement::TemporaryDtor: {2485auto TD = BI.castAs<CFGTemporaryDtor>();24862487// Clean up constructed object even if there are no attributes to2488// keep the number of objects in limbo as small as possible.2489if (auto Object = ConstructedObjects.find(2490TD.getBindTemporaryExpr()->getSubExpr());2491Object != ConstructedObjects.end()) {2492const auto *DD = TD.getDestructorDecl(AC.getASTContext());2493if (DD->hasAttrs())2494// TODO: the location here isn't quite correct.2495LocksetBuilder.handleCall(nullptr, DD, Object->second,2496TD.getBindTemporaryExpr()->getEndLoc());2497ConstructedObjects.erase(Object);2498}2499break;2500}2501default:2502break;2503}2504}2505CurrBlockInfo->ExitSet = LocksetBuilder.FSet;25062507// For every back edge from CurrBlock (the end of the loop) to another block2508// (FirstLoopBlock) we need to check that the Lockset of Block is equal to2509// the one held at the beginning of FirstLoopBlock. We can look up the2510// Lockset held at the beginning of FirstLoopBlock in the EntryLockSets map.2511for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),2512SE = CurrBlock->succ_end(); SI != SE; ++SI) {2513// if CurrBlock -> *SI is *not* a back edge2514if (*SI == nullptr || !VisitedBlocks.alreadySet(*SI))2515continue;25162517CFGBlock *FirstLoopBlock = *SI;2518CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->getBlockID()];2519CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];2520intersectAndWarn(PreLoop->EntrySet, LoopEnd->ExitSet, PreLoop->EntryLoc,2521LEK_LockedSomeLoopIterations);2522}2523}25242525// Skip the final check if the exit block is unreachable.2526if (!Final.Reachable)2527return;25282529// FIXME: Should we call this function for all blocks which exit the function?2530intersectAndWarn(ExpectedFunctionExitSet, Final.ExitSet, Final.ExitLoc,2531LEK_LockedAtEndOfFunction, LEK_NotLockedAtEndOfFunction);25322533Handler.leaveFunction(CurrentFunction);2534}25352536/// Check a function's CFG for thread-safety violations.2537///2538/// We traverse the blocks in the CFG, compute the set of mutexes that are held2539/// at the end of each block, and issue warnings for thread safety violations.2540/// Each block in the CFG is traversed exactly once.2541void threadSafety::runThreadSafetyAnalysis(AnalysisDeclContext &AC,2542ThreadSafetyHandler &Handler,2543BeforeSet **BSet) {2544if (!*BSet)2545*BSet = new BeforeSet;2546ThreadSafetyAnalyzer Analyzer(Handler, *BSet);2547Analyzer.runAnalysis(AC);2548}25492550void threadSafety::threadSafetyCleanup(BeforeSet *Cache) { delete Cache; }25512552/// Helper function that returns a LockKind required for the given level2553/// of access.2554LockKind threadSafety::getLockKindFromAccessKind(AccessKind AK) {2555switch (AK) {2556case AK_Read :2557return LK_Shared;2558case AK_Written :2559return LK_Exclusive;2560}2561llvm_unreachable("Unknown AccessKind");2562}256325642565