#include "clang/Analysis/Analyses/ThreadSafety.h"
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/OperatorKinds.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <utility>
#include <vector>
using namespace clang;
using namespace thread_safety;
ThreadSafetyHandler::~ThreadSafetyHandler() {}
namespace {
class SExpr {
private:
enum ExprOp {
EOP_Nop, EOP_Wildcard, EOP_Universal, EOP_This, EOP_NVar, EOP_LVar, EOP_Dot, EOP_Call, EOP_MCall, EOP_Index, EOP_Unary, EOP_Binary, EOP_Unknown };
class SExprNode {
private:
unsigned char Op; unsigned char Flags; unsigned short Sz; const void* Data;
public:
SExprNode(ExprOp O, unsigned F, const void* D)
: Op(static_cast<unsigned char>(O)),
Flags(static_cast<unsigned char>(F)), Sz(1), Data(D)
{ }
unsigned size() const { return Sz; }
void setSize(unsigned S) { Sz = S; }
ExprOp kind() const { return static_cast<ExprOp>(Op); }
const NamedDecl* getNamedDecl() const {
assert(Op == EOP_NVar || Op == EOP_LVar || Op == EOP_Dot);
return reinterpret_cast<const NamedDecl*>(Data);
}
const NamedDecl* getFunctionDecl() const {
assert(Op == EOP_Call || Op == EOP_MCall);
return reinterpret_cast<const NamedDecl*>(Data);
}
bool isArrow() const { return Op == EOP_Dot && Flags == 1; }
void setArrow(bool A) { Flags = A ? 1 : 0; }
unsigned arity() const {
switch (Op) {
case EOP_Nop: return 0;
case EOP_Wildcard: return 0;
case EOP_Universal: return 0;
case EOP_NVar: return 0;
case EOP_LVar: return 0;
case EOP_This: return 0;
case EOP_Dot: return 1;
case EOP_Call: return Flags+1; case EOP_MCall: return Flags+1; case EOP_Index: return 2;
case EOP_Unary: return 1;
case EOP_Binary: return 2;
case EOP_Unknown: return Flags;
}
return 0;
}
bool operator==(const SExprNode& Other) const {
return (Op == Other.Op &&
Data == Other.Data);
}
bool operator!=(const SExprNode& Other) const {
return !(*this == Other);
}
bool matches(const SExprNode& Other) const {
return (*this == Other) ||
(Op == EOP_Wildcard) ||
(Other.Op == EOP_Wildcard);
}
};
struct CallingContext {
const NamedDecl* AttrDecl; Expr* SelfArg; bool SelfArrow; unsigned NumArgs; Expr** FunArgs; CallingContext* PrevCtx;
CallingContext(const NamedDecl *D = 0, Expr *S = 0,
unsigned N = 0, Expr **A = 0, CallingContext *P = 0)
: AttrDecl(D), SelfArg(S), SelfArrow(false),
NumArgs(N), FunArgs(A), PrevCtx(P)
{ }
};
typedef SmallVector<SExprNode, 4> NodeVector;
private:
NodeVector NodeVec;
private:
unsigned makeNop() {
NodeVec.push_back(SExprNode(EOP_Nop, 0, 0));
return NodeVec.size()-1;
}
unsigned makeWildcard() {
NodeVec.push_back(SExprNode(EOP_Wildcard, 0, 0));
return NodeVec.size()-1;
}
unsigned makeUniversal() {
NodeVec.push_back(SExprNode(EOP_Universal, 0, 0));
return NodeVec.size()-1;
}
unsigned makeNamedVar(const NamedDecl *D) {
NodeVec.push_back(SExprNode(EOP_NVar, 0, D));
return NodeVec.size()-1;
}
unsigned makeLocalVar(const NamedDecl *D) {
NodeVec.push_back(SExprNode(EOP_LVar, 0, D));
return NodeVec.size()-1;
}
unsigned makeThis() {
NodeVec.push_back(SExprNode(EOP_This, 0, 0));
return NodeVec.size()-1;
}
unsigned makeDot(const NamedDecl *D, bool Arrow) {
NodeVec.push_back(SExprNode(EOP_Dot, Arrow ? 1 : 0, D));
return NodeVec.size()-1;
}
unsigned makeCall(unsigned NumArgs, const NamedDecl *D) {
NodeVec.push_back(SExprNode(EOP_Call, NumArgs, D));
return NodeVec.size()-1;
}
const CXXMethodDecl* getFirstVirtualDecl(const CXXMethodDecl *D) {
while (true) {
D = D->getCanonicalDecl();
CXXMethodDecl::method_iterator I = D->begin_overridden_methods(),
E = D->end_overridden_methods();
if (I == E)
return D; D = *I; }
return 0;
}
unsigned makeMCall(unsigned NumArgs, const CXXMethodDecl *D) {
NodeVec.push_back(SExprNode(EOP_MCall, NumArgs, getFirstVirtualDecl(D)));
return NodeVec.size()-1;
}
unsigned makeIndex() {
NodeVec.push_back(SExprNode(EOP_Index, 0, 0));
return NodeVec.size()-1;
}
unsigned makeUnary() {
NodeVec.push_back(SExprNode(EOP_Unary, 0, 0));
return NodeVec.size()-1;
}
unsigned makeBinary() {
NodeVec.push_back(SExprNode(EOP_Binary, 0, 0));
return NodeVec.size()-1;
}
unsigned makeUnknown(unsigned Arity) {
NodeVec.push_back(SExprNode(EOP_Unknown, Arity, 0));
return NodeVec.size()-1;
}
unsigned buildSExpr(Expr *Exp, CallingContext* CallCtx, int* NDeref = 0) {
if (!Exp)
return 0;
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) {
NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl());
ParmVarDecl *PV = dyn_cast_or_null<ParmVarDecl>(ND);
if (PV) {
FunctionDecl *FD =
cast<FunctionDecl>(PV->getDeclContext())->getCanonicalDecl();
unsigned i = PV->getFunctionScopeIndex();
if (CallCtx && CallCtx->FunArgs &&
FD == CallCtx->AttrDecl->getCanonicalDecl()) {
assert(i < CallCtx->NumArgs);
return buildSExpr(CallCtx->FunArgs[i], CallCtx->PrevCtx, NDeref);
}
makeNamedVar(FD->getParamDecl(i));
return 1;
}
makeNamedVar(ND);
return 1;
} else if (isa<CXXThisExpr>(Exp)) {
if (CallCtx && CallCtx->SelfArg) {
if (!CallCtx->SelfArrow && NDeref)
--(*NDeref);
return buildSExpr(CallCtx->SelfArg, CallCtx->PrevCtx, NDeref);
}
else {
makeThis();
return 1;
}
} else if (MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) {
NamedDecl *ND = ME->getMemberDecl();
int ImplicitDeref = ME->isArrow() ? 1 : 0;
unsigned Root = makeDot(ND, false);
unsigned Sz = buildSExpr(ME->getBase(), CallCtx, &ImplicitDeref);
NodeVec[Root].setArrow(ImplicitDeref > 0);
NodeVec[Root].setSize(Sz + 1);
return Sz + 1;
} else if (CXXMemberCallExpr *CMCE = dyn_cast<CXXMemberCallExpr>(Exp)) {
CXXMethodDecl* MD =
cast<CXXMethodDecl>(CMCE->getMethodDecl()->getMostRecentDecl());
if (LockReturnedAttr* At = MD->getAttr<LockReturnedAttr>()) {
CallingContext LRCallCtx(CMCE->getMethodDecl());
LRCallCtx.SelfArg = CMCE->getImplicitObjectArgument();
LRCallCtx.SelfArrow =
dyn_cast<MemberExpr>(CMCE->getCallee())->isArrow();
LRCallCtx.NumArgs = CMCE->getNumArgs();
LRCallCtx.FunArgs = CMCE->getArgs();
LRCallCtx.PrevCtx = CallCtx;
return buildSExpr(At->getArg(), &LRCallCtx);
}
if (CMCE->getMethodDecl()->getNameAsString() == "get" &&
CMCE->getNumArgs() == 0) {
if (NDeref && dyn_cast<MemberExpr>(CMCE->getCallee())->isArrow())
++(*NDeref);
return buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx, NDeref);
}
unsigned NumCallArgs = CMCE->getNumArgs();
unsigned Root = makeMCall(NumCallArgs, CMCE->getMethodDecl());
unsigned Sz = buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx);
Expr** CallArgs = CMCE->getArgs();
for (unsigned i = 0; i < NumCallArgs; ++i) {
Sz += buildSExpr(CallArgs[i], CallCtx);
}
NodeVec[Root].setSize(Sz + 1);
return Sz + 1;
} else if (CallExpr *CE = dyn_cast<CallExpr>(Exp)) {
FunctionDecl* FD =
cast<FunctionDecl>(CE->getDirectCallee()->getMostRecentDecl());
if (LockReturnedAttr* At = FD->getAttr<LockReturnedAttr>()) {
CallingContext LRCallCtx(CE->getDirectCallee());
LRCallCtx.NumArgs = CE->getNumArgs();
LRCallCtx.FunArgs = CE->getArgs();
LRCallCtx.PrevCtx = CallCtx;
return buildSExpr(At->getArg(), &LRCallCtx);
}
if (CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(CE)) {
OverloadedOperatorKind k = OE->getOperator();
if (k == OO_Star) {
if (NDeref) ++(*NDeref);
return buildSExpr(OE->getArg(0), CallCtx, NDeref);
}
else if (k == OO_Arrow) {
return buildSExpr(OE->getArg(0), CallCtx, NDeref);
}
}
unsigned NumCallArgs = CE->getNumArgs();
unsigned Root = makeCall(NumCallArgs, 0);
unsigned Sz = buildSExpr(CE->getCallee(), CallCtx);
Expr** CallArgs = CE->getArgs();
for (unsigned i = 0; i < NumCallArgs; ++i) {
Sz += buildSExpr(CallArgs[i], CallCtx);
}
NodeVec[Root].setSize(Sz+1);
return Sz+1;
} else if (BinaryOperator *BOE = dyn_cast<BinaryOperator>(Exp)) {
unsigned Root = makeBinary();
unsigned Sz = buildSExpr(BOE->getLHS(), CallCtx);
Sz += buildSExpr(BOE->getRHS(), CallCtx);
NodeVec[Root].setSize(Sz);
return Sz;
} else if (UnaryOperator *UOE = dyn_cast<UnaryOperator>(Exp)) {
if (UOE->getOpcode() == UO_Deref) {
if (NDeref) ++(*NDeref);
return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref);
}
if (UOE->getOpcode() == UO_AddrOf) {
if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(UOE->getSubExpr())) {
if (DRE->getDecl()->isCXXInstanceMember()) {
unsigned Root = makeDot(DRE->getDecl(), false);
makeWildcard();
NodeVec[Root].setSize(2);
return 2;
}
}
if (NDeref) --(*NDeref);
return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref);
}
unsigned Root = makeUnary();
unsigned Sz = buildSExpr(UOE->getSubExpr(), CallCtx);
NodeVec[Root].setSize(Sz);
return Sz;
} else if (ArraySubscriptExpr *ASE = dyn_cast<ArraySubscriptExpr>(Exp)) {
unsigned Root = makeIndex();
unsigned Sz = buildSExpr(ASE->getBase(), CallCtx);
Sz += buildSExpr(ASE->getIdx(), CallCtx);
NodeVec[Root].setSize(Sz);
return Sz;
} else if (AbstractConditionalOperator *CE =
dyn_cast<AbstractConditionalOperator>(Exp)) {
unsigned Root = makeUnknown(3);
unsigned Sz = buildSExpr(CE->getCond(), CallCtx);
Sz += buildSExpr(CE->getTrueExpr(), CallCtx);
Sz += buildSExpr(CE->getFalseExpr(), CallCtx);
NodeVec[Root].setSize(Sz);
return Sz;
} else if (ChooseExpr *CE = dyn_cast<ChooseExpr>(Exp)) {
unsigned Root = makeUnknown(3);
unsigned Sz = buildSExpr(CE->getCond(), CallCtx);
Sz += buildSExpr(CE->getLHS(), CallCtx);
Sz += buildSExpr(CE->getRHS(), CallCtx);
NodeVec[Root].setSize(Sz);
return Sz;
} else if (CastExpr *CE = dyn_cast<CastExpr>(Exp)) {
return buildSExpr(CE->getSubExpr(), CallCtx, NDeref);
} else if (ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) {
return buildSExpr(PE->getSubExpr(), CallCtx, NDeref);
} else if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Exp)) {
return buildSExpr(EWC->getSubExpr(), CallCtx, NDeref);
} else if (CXXBindTemporaryExpr *E = dyn_cast<CXXBindTemporaryExpr>(Exp)) {
return buildSExpr(E->getSubExpr(), CallCtx, NDeref);
} else if (isa<CharacterLiteral>(Exp) ||
isa<CXXNullPtrLiteralExpr>(Exp) ||
isa<GNUNullExpr>(Exp) ||
isa<CXXBoolLiteralExpr>(Exp) ||
isa<FloatingLiteral>(Exp) ||
isa<ImaginaryLiteral>(Exp) ||
isa<IntegerLiteral>(Exp) ||
isa<StringLiteral>(Exp) ||
isa<ObjCStringLiteral>(Exp)) {
makeNop();
return 1; } else {
makeNop();
return 1; }
}
void buildSExprFromExpr(Expr *MutexExp, Expr *DeclExp, const NamedDecl *D) {
CallingContext CallCtx(D);
if (MutexExp) {
if (StringLiteral* SLit = dyn_cast<StringLiteral>(MutexExp)) {
if (SLit->getString() == StringRef("*"))
makeUniversal();
else
makeNop();
return;
}
}
if (DeclExp == 0) {
buildSExpr(MutexExp, 0);
return;
}
if (MemberExpr *ME = dyn_cast<MemberExpr>(DeclExp)) {
CallCtx.SelfArg = ME->getBase();
CallCtx.SelfArrow = ME->isArrow();
} else if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(DeclExp)) {
CallCtx.SelfArg = CE->getImplicitObjectArgument();
CallCtx.SelfArrow = dyn_cast<MemberExpr>(CE->getCallee())->isArrow();
CallCtx.NumArgs = CE->getNumArgs();
CallCtx.FunArgs = CE->getArgs();
} else if (CallExpr *CE = dyn_cast<CallExpr>(DeclExp)) {
CallCtx.NumArgs = CE->getNumArgs();
CallCtx.FunArgs = CE->getArgs();
} else if (CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(DeclExp)) {
CallCtx.SelfArg = 0; CallCtx.NumArgs = CE->getNumArgs();
CallCtx.FunArgs = CE->getArgs();
} else if (D && isa<CXXDestructorDecl>(D)) {
CallCtx.SelfArg = DeclExp;
}
if (MutexExp == 0) {
buildSExpr(CallCtx.SelfArg, 0);
return;
}
buildSExpr(MutexExp, &CallCtx);
}
unsigned getNextSibling(unsigned i) const {
return i + NodeVec[i].size();
}
public:
explicit SExpr(clang::Decl::EmptyShell e) { NodeVec.clear(); }
SExpr(Expr* MutexExp, Expr *DeclExp, const NamedDecl* D) {
buildSExprFromExpr(MutexExp, DeclExp, D);
}
bool isValid() const {
return !NodeVec.empty();
}
bool shouldIgnore() const {
assert(NodeVec.size() > 0 && "Invalid Mutex");
return NodeVec[0].kind() == EOP_Nop;
}
bool isUniversal() const {
assert(NodeVec.size() > 0 && "Invalid Mutex");
return NodeVec[0].kind() == EOP_Universal;
}
static void warnInvalidLock(ThreadSafetyHandler &Handler, Expr* MutexExp,
Expr *DeclExp, const NamedDecl* D) {
SourceLocation Loc;
if (DeclExp)
Loc = DeclExp->getExprLoc();
if (Loc.isValid())
Handler.handleInvalidLockExp(Loc);
}
bool operator==(const SExpr &other) const {
return NodeVec == other.NodeVec;
}
bool operator!=(const SExpr &other) const {
return !(*this == other);
}
bool matches(const SExpr &Other, unsigned i = 0, unsigned j = 0) const {
if (NodeVec[i].matches(Other.NodeVec[j])) {
unsigned ni = NodeVec[i].arity();
unsigned nj = Other.NodeVec[j].arity();
unsigned n = (ni < nj) ? ni : nj;
bool Result = true;
unsigned ci = i+1; unsigned cj = j+1; for (unsigned k = 0; k < n;
++k, ci=getNextSibling(ci), cj = Other.getNextSibling(cj)) {
Result = Result && matches(Other, ci, cj);
}
return Result;
}
return false;
}
bool partiallyMatches(const SExpr &Other) const {
if (NodeVec[0].kind() == EOP_Dot)
return NodeVec[0].matches(Other.NodeVec[0]);
return false;
}
std::string toString(unsigned i = 0) const {
assert(isValid());
if (i >= NodeVec.size())
return "";
const SExprNode* N = &NodeVec[i];
switch (N->kind()) {
case EOP_Nop:
return "_";
case EOP_Wildcard:
return "(?)";
case EOP_Universal:
return "*";
case EOP_This:
return "this";
case EOP_NVar:
case EOP_LVar: {
return N->getNamedDecl()->getNameAsString();
}
case EOP_Dot: {
if (NodeVec[i+1].kind() == EOP_Wildcard) {
std::string S = "&";
S += N->getNamedDecl()->getQualifiedNameAsString();
return S;
}
std::string FieldName = N->getNamedDecl()->getNameAsString();
if (NodeVec[i+1].kind() == EOP_This)
return FieldName;
std::string S = toString(i+1);
if (N->isArrow())
return S + "->" + FieldName;
else
return S + "." + FieldName;
}
case EOP_Call: {
std::string S = toString(i+1) + "(";
unsigned NumArgs = N->arity()-1;
unsigned ci = getNextSibling(i+1);
for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) {
S += toString(ci);
if (k+1 < NumArgs) S += ",";
}
S += ")";
return S;
}
case EOP_MCall: {
std::string S = "";
if (NodeVec[i+1].kind() != EOP_This)
S = toString(i+1) + ".";
if (const NamedDecl *D = N->getFunctionDecl())
S += D->getNameAsString() + "(";
else
S += "#(";
unsigned NumArgs = N->arity()-1;
unsigned ci = getNextSibling(i+1);
for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) {
S += toString(ci);
if (k+1 < NumArgs) S += ",";
}
S += ")";
return S;
}
case EOP_Index: {
std::string S1 = toString(i+1);
std::string S2 = toString(i+1 + NodeVec[i+1].size());
return S1 + "[" + S2 + "]";
}
case EOP_Unary: {
std::string S = toString(i+1);
return "#" + S;
}
case EOP_Binary: {
std::string S1 = toString(i+1);
std::string S2 = toString(i+1 + NodeVec[i+1].size());
return "(" + S1 + "#" + S2 + ")";
}
case EOP_Unknown: {
unsigned NumChildren = N->arity();
if (NumChildren == 0)
return "(...)";
std::string S = "(";
unsigned ci = i+1;
for (unsigned j = 0; j < NumChildren; ++j, ci = getNextSibling(ci)) {
S += toString(ci);
if (j+1 < NumChildren) S += "#";
}
S += ")";
return S;
}
}
return "";
}
};
class MutexIDList : public SmallVector<SExpr, 3> {
public:
bool contains(const SExpr& M) {
for (iterator I=begin(),E=end(); I != E; ++I)
if ((*I) == M) return true;
return false;
}
void push_back_nodup(const SExpr& M) {
if (!contains(M)) push_back(M);
}
};
struct LockData {
SourceLocation AcquireLoc;
LockKind LKind;
bool Managed; SExpr UnderlyingMutex;
LockData(SourceLocation AcquireLoc, LockKind LKind, bool M = false)
: AcquireLoc(AcquireLoc), LKind(LKind), Managed(M),
UnderlyingMutex(Decl::EmptyShell())
{}
LockData(SourceLocation AcquireLoc, LockKind LKind, const SExpr &Mu)
: AcquireLoc(AcquireLoc), LKind(LKind), Managed(false),
UnderlyingMutex(Mu)
{}
bool operator==(const LockData &other) const {
return AcquireLoc == other.AcquireLoc && LKind == other.LKind;
}
bool operator!=(const LockData &other) const {
return !(*this == other);
}
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(AcquireLoc.getRawEncoding());
ID.AddInteger(LKind);
}
bool isAtLeast(LockKind LK) {
return (LK == LK_Shared) || (LKind == LK_Exclusive);
}
};
struct FactEntry {
SExpr MutID;
LockData LDat;
FactEntry(const SExpr& M, const LockData& L)
: MutID(M), LDat(L)
{ }
};
typedef unsigned short FactID;
class FactManager {
private:
std::vector<FactEntry> Facts;
public:
FactID newLock(const SExpr& M, const LockData& L) {
Facts.push_back(FactEntry(M,L));
return static_cast<unsigned short>(Facts.size() - 1);
}
const FactEntry& operator[](FactID F) const { return Facts[F]; }
FactEntry& operator[](FactID F) { return Facts[F]; }
};
class FactSet {
private:
typedef SmallVector<FactID, 4> FactVec;
FactVec FactIDs;
public:
typedef FactVec::iterator iterator;
typedef FactVec::const_iterator const_iterator;
iterator begin() { return FactIDs.begin(); }
const_iterator begin() const { return FactIDs.begin(); }
iterator end() { return FactIDs.end(); }
const_iterator end() const { return FactIDs.end(); }
bool isEmpty() const { return FactIDs.size() == 0; }
FactID addLock(FactManager& FM, const SExpr& M, const LockData& L) {
FactID F = FM.newLock(M, L);
FactIDs.push_back(F);
return F;
}
bool removeLock(FactManager& FM, const SExpr& M) {
unsigned n = FactIDs.size();
if (n == 0)
return false;
for (unsigned i = 0; i < n-1; ++i) {
if (FM[FactIDs[i]].MutID.matches(M)) {
FactIDs[i] = FactIDs[n-1];
FactIDs.pop_back();
return true;
}
}
if (FM[FactIDs[n-1]].MutID.matches(M)) {
FactIDs.pop_back();
return true;
}
return false;
}
LockData* findLock(FactManager &FM, const SExpr &M) const {
for (const_iterator I = begin(), E = end(); I != E; ++I) {
const SExpr &Exp = FM[*I].MutID;
if (Exp.matches(M))
return &FM[*I].LDat;
}
return 0;
}
LockData* findLockUniv(FactManager &FM, const SExpr &M) const {
for (const_iterator I = begin(), E = end(); I != E; ++I) {
const SExpr &Exp = FM[*I].MutID;
if (Exp.matches(M) || Exp.isUniversal())
return &FM[*I].LDat;
}
return 0;
}
FactEntry* findPartialMatch(FactManager &FM, const SExpr &M) const {
for (const_iterator I=begin(), E=end(); I != E; ++I) {
const SExpr& Exp = FM[*I].MutID;
if (Exp.partiallyMatches(M)) return &FM[*I];
}
return 0;
}
};
typedef llvm::ImmutableMap<SExpr, LockData> Lockset;
typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext;
class LocalVariableMap;
enum CFGBlockSide { CBS_Entry, CBS_Exit };
struct CFGBlockInfo {
FactSet EntrySet; FactSet ExitSet; LocalVarContext EntryContext; LocalVarContext ExitContext; SourceLocation EntryLoc; SourceLocation ExitLoc; unsigned EntryIndex; bool Reachable;
const FactSet &getSet(CFGBlockSide Side) const {
return Side == CBS_Entry ? EntrySet : ExitSet;
}
SourceLocation getLocation(CFGBlockSide Side) const {
return Side == CBS_Entry ? EntryLoc : ExitLoc;
}
private:
CFGBlockInfo(LocalVarContext EmptyCtx)
: EntryContext(EmptyCtx), ExitContext(EmptyCtx), Reachable(false)
{ }
public:
static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
};
class LocalVariableMap {
public:
typedef LocalVarContext Context;
struct VarDefinition {
public:
friend class LocalVariableMap;
const NamedDecl *Dec; const Expr *Exp; unsigned Ref; Context Ctx;
bool isReference() { return !Exp; }
private:
VarDefinition(const NamedDecl *D, const Expr *E, Context C)
: Dec(D), Exp(E), Ref(0), Ctx(C)
{ }
VarDefinition(const NamedDecl *D, unsigned R, Context C)
: Dec(D), Exp(0), Ref(R), Ctx(C)
{ }
};
private:
Context::Factory ContextFactory;
std::vector<VarDefinition> VarDefinitions;
std::vector<unsigned> CtxIndices;
std::vector<std::pair<Stmt*, Context> > SavedContexts;
public:
LocalVariableMap() {
VarDefinitions.push_back(VarDefinition(0, 0u, getEmptyContext()));
}
const VarDefinition* lookup(const NamedDecl *D, Context Ctx) {
const unsigned *i = Ctx.lookup(D);
if (!i)
return 0;
assert(*i < VarDefinitions.size());
return &VarDefinitions[*i];
}
const Expr* lookupExpr(const NamedDecl *D, Context &Ctx) {
const unsigned *P = Ctx.lookup(D);
if (!P)
return 0;
unsigned i = *P;
while (i > 0) {
if (VarDefinitions[i].Exp) {
Ctx = VarDefinitions[i].Ctx;
return VarDefinitions[i].Exp;
}
i = VarDefinitions[i].Ref;
}
return 0;
}
Context getEmptyContext() { return ContextFactory.getEmptyMap(); }
Context getNextContext(unsigned &CtxIndex, Stmt *S, Context C) {
if (SavedContexts[CtxIndex+1].first == S) {
CtxIndex++;
Context Result = SavedContexts[CtxIndex].second;
return Result;
}
return C;
}
void dumpVarDefinitionName(unsigned i) {
if (i == 0) {
llvm::errs() << "Undefined";
return;
}
const NamedDecl *Dec = VarDefinitions[i].Dec;
if (!Dec) {
llvm::errs() << "<<NULL>>";
return;
}
Dec->printName(llvm::errs());
llvm::errs() << "." << i << " " << ((const void*) Dec);
}
void dump() {
for (unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {
const Expr *Exp = VarDefinitions[i].Exp;
unsigned Ref = VarDefinitions[i].Ref;
dumpVarDefinitionName(i);
llvm::errs() << " = ";
if (Exp) Exp->dump();
else {
dumpVarDefinitionName(Ref);
llvm::errs() << "\n";
}
}
}
void dumpContext(Context C) {
for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
const NamedDecl *D = I.getKey();
D->printName(llvm::errs());
const unsigned *i = C.lookup(D);
llvm::errs() << " -> ";
dumpVarDefinitionName(*i);
llvm::errs() << "\n";
}
}
void traverseCFG(CFG *CFGraph, PostOrderCFGView *SortedGraph,
std::vector<CFGBlockInfo> &BlockInfo);
protected:
unsigned getContextIndex() { return SavedContexts.size()-1; }
void saveContext(Stmt *S, Context C) {
SavedContexts.push_back(std::make_pair(S,C));
}
Context addDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) {
assert(!Ctx.contains(D));
unsigned newID = VarDefinitions.size();
Context NewCtx = ContextFactory.add(Ctx, D, newID);
VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
return NewCtx;
}
Context addReference(const NamedDecl *D, unsigned i, Context Ctx) {
unsigned newID = VarDefinitions.size();
Context NewCtx = ContextFactory.add(Ctx, D, newID);
VarDefinitions.push_back(VarDefinition(D, i, Ctx));
return NewCtx;
}
Context updateDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) {
if (Ctx.contains(D)) {
unsigned newID = VarDefinitions.size();
Context NewCtx = ContextFactory.remove(Ctx, D);
NewCtx = ContextFactory.add(NewCtx, D, newID);
VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
return NewCtx;
}
return Ctx;
}
Context clearDefinition(const NamedDecl *D, Context Ctx) {
Context NewCtx = Ctx;
if (NewCtx.contains(D)) {
NewCtx = ContextFactory.remove(NewCtx, D);
NewCtx = ContextFactory.add(NewCtx, D, 0);
}
return NewCtx;
}
Context removeDefinition(const NamedDecl *D, Context Ctx) {
Context NewCtx = Ctx;
if (NewCtx.contains(D)) {
NewCtx = ContextFactory.remove(NewCtx, D);
}
return NewCtx;
}
Context intersectContexts(Context C1, Context C2);
Context createReferenceContext(Context C);
void intersectBackEdge(Context C1, Context C2);
friend class VarMapBuilder;
};
CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
return CFGBlockInfo(M.getEmptyContext());
}
class VarMapBuilder : public StmtVisitor<VarMapBuilder> {
public:
LocalVariableMap* VMap;
LocalVariableMap::Context Ctx;
VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C)
: VMap(VM), Ctx(C) {}
void VisitDeclStmt(DeclStmt *S);
void VisitBinaryOperator(BinaryOperator *BO);
};
void VarMapBuilder::VisitDeclStmt(DeclStmt *S) {
bool modifiedCtx = false;
DeclGroupRef DGrp = S->getDeclGroup();
for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) {
if (VarDecl *VD = dyn_cast_or_null<VarDecl>(*I)) {
Expr *E = VD->getInit();
QualType T = VD->getType();
if (T.isTrivialType(VD->getASTContext())) {
Ctx = VMap->addDefinition(VD, E, Ctx);
modifiedCtx = true;
}
}
}
if (modifiedCtx)
VMap->saveContext(S, Ctx);
}
void VarMapBuilder::VisitBinaryOperator(BinaryOperator *BO) {
if (!BO->isAssignmentOp())
return;
Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(LHSExp)) {
ValueDecl *VDec = DRE->getDecl();
if (Ctx.lookup(VDec)) {
if (BO->getOpcode() == BO_Assign)
Ctx = VMap->updateDefinition(VDec, BO->getRHS(), Ctx);
else
Ctx = VMap->clearDefinition(VDec, Ctx);
VMap->saveContext(BO, Ctx);
}
}
}
LocalVariableMap::Context
LocalVariableMap::intersectContexts(Context C1, Context C2) {
Context Result = C1;
for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) {
const NamedDecl *Dec = I.getKey();
unsigned i1 = I.getData();
const unsigned *i2 = C2.lookup(Dec);
if (!i2) Result = removeDefinition(Dec, Result);
else if (*i2 != i1) Result = clearDefinition(Dec, Result);
}
return Result;
}
LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
Context Result = getEmptyContext();
for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
const NamedDecl *Dec = I.getKey();
unsigned i = I.getData();
Result = addReference(Dec, i, Result);
}
return Result;
}
void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) {
const NamedDecl *Dec = I.getKey();
unsigned i1 = I.getData();
VarDefinition *VDef = &VarDefinitions[i1];
assert(VDef->isReference());
const unsigned *i2 = C2.lookup(Dec);
if (!i2 || (*i2 != i1))
VDef->Ref = 0; }
}
void LocalVariableMap::traverseCFG(CFG *CFGraph,
PostOrderCFGView *SortedGraph,
std::vector<CFGBlockInfo> &BlockInfo) {
PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph);
CtxIndices.resize(CFGraph->getNumBlockIDs());
for (PostOrderCFGView::iterator I = SortedGraph->begin(),
E = SortedGraph->end(); I!= E; ++I) {
const CFGBlock *CurrBlock = *I;
int CurrBlockID = CurrBlock->getBlockID();
CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
VisitedBlocks.insert(CurrBlock);
bool HasBackEdges = false;
bool CtxInit = true;
for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
PE = CurrBlock->pred_end(); PI != PE; ++PI) {
if (*PI == 0 || !VisitedBlocks.alreadySet(*PI)) {
HasBackEdges = true;
continue;
}
int PrevBlockID = (*PI)->getBlockID();
CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
if (CtxInit) {
CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;
CtxInit = false;
}
else {
CurrBlockInfo->EntryContext =
intersectContexts(CurrBlockInfo->EntryContext,
PrevBlockInfo->ExitContext);
}
}
if (HasBackEdges)
CurrBlockInfo->EntryContext =
createReferenceContext(CurrBlockInfo->EntryContext);
saveContext(0, CurrBlockInfo->EntryContext);
CurrBlockInfo->EntryIndex = getContextIndex();
VarMapBuilder VMapBuilder(this, CurrBlockInfo->EntryContext);
for (CFGBlock::const_iterator BI = CurrBlock->begin(),
BE = CurrBlock->end(); BI != BE; ++BI) {
switch (BI->getKind()) {
case CFGElement::Statement: {
const CFGStmt *CS = cast<CFGStmt>(&*BI);
VMapBuilder.Visit(const_cast<Stmt*>(CS->getStmt()));
break;
}
default:
break;
}
}
CurrBlockInfo->ExitContext = VMapBuilder.Ctx;
for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
SE = CurrBlock->succ_end(); SI != SE; ++SI) {
if (*SI == 0 || !VisitedBlocks.alreadySet(*SI))
continue;
CFGBlock *FirstLoopBlock = *SI;
Context LoopBegin = BlockInfo[FirstLoopBlock->getBlockID()].EntryContext;
Context LoopEnd = CurrBlockInfo->ExitContext;
intersectBackEdge(LoopBegin, LoopEnd);
}
}
unsigned exitID = CFGraph->getExit().getBlockID();
saveContext(0, BlockInfo[exitID].ExitContext);
}
static void findBlockLocations(CFG *CFGraph,
PostOrderCFGView *SortedGraph,
std::vector<CFGBlockInfo> &BlockInfo) {
for (PostOrderCFGView::iterator I = SortedGraph->begin(),
E = SortedGraph->end(); I!= E; ++I) {
const CFGBlock *CurrBlock = *I;
CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];
if (const Stmt *S = CurrBlock->getTerminator()) {
CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getLocStart();
} else {
for (CFGBlock::const_reverse_iterator BI = CurrBlock->rbegin(),
BE = CurrBlock->rend(); BI != BE; ++BI) {
if (const CFGStmt *CS = dyn_cast<CFGStmt>(&*BI)) {
CurrBlockInfo->ExitLoc = CS->getStmt()->getLocStart();
break;
}
}
}
if (!CurrBlockInfo->ExitLoc.isInvalid()) {
for (CFGBlock::const_iterator BI = CurrBlock->begin(),
BE = CurrBlock->end(); BI != BE; ++BI) {
if (const CFGStmt *CS = dyn_cast<CFGStmt>(&*BI)) {
CurrBlockInfo->EntryLoc = CS->getStmt()->getLocStart();
break;
}
}
} else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&
CurrBlock != &CFGraph->getExit()) {
CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;
}
}
}
class ThreadSafetyAnalyzer {
friend class BuildLockset;
ThreadSafetyHandler &Handler;
LocalVariableMap LocalVarMap;
FactManager FactMan;
std::vector<CFGBlockInfo> BlockInfo;
public:
ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {}
void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat);
void removeLock(FactSet &FSet, const SExpr &Mutex,
SourceLocation UnlockLoc, bool FullyRemove=false);
template <typename AttrType>
void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,
const NamedDecl *D);
template <class AttrType>
void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp,
const NamedDecl *D,
const CFGBlock *PredBlock, const CFGBlock *CurrBlock,
Expr *BrE, bool Neg);
const CallExpr* getTrylockCallExpr(const Stmt *Cond, LocalVarContext C,
bool &Negate);
void getEdgeLockset(FactSet &Result, const FactSet &ExitSet,
const CFGBlock* PredBlock,
const CFGBlock *CurrBlock);
void intersectAndWarn(FactSet &FSet1, const FactSet &FSet2,
SourceLocation JoinLoc,
LockErrorKind LEK1, LockErrorKind LEK2,
bool Modify=true);
void intersectAndWarn(FactSet &FSet1, const FactSet &FSet2,
SourceLocation JoinLoc, LockErrorKind LEK1,
bool Modify=true) {
intersectAndWarn(FSet1, FSet2, JoinLoc, LEK1, LEK1, Modify);
}
void runAnalysis(AnalysisDeclContext &AC);
};
void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex,
const LockData &LDat) {
if (Mutex.shouldIgnore())
return;
if (FSet.findLock(FactMan, Mutex)) {
Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc);
} else {
FSet.addLock(FactMan, Mutex, LDat);
}
}
void ThreadSafetyAnalyzer::removeLock(FactSet &FSet,
const SExpr &Mutex,
SourceLocation UnlockLoc,
bool FullyRemove) {
if (Mutex.shouldIgnore())
return;
const LockData *LDat = FSet.findLock(FactMan, Mutex);
if (!LDat) {
Handler.handleUnmatchedUnlock(Mutex.toString(), UnlockLoc);
return;
}
if (LDat->UnderlyingMutex.isValid()) {
if (FullyRemove) {
if (FSet.findLock(FactMan, LDat->UnderlyingMutex))
FSet.removeLock(FactMan, LDat->UnderlyingMutex);
} else {
if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) {
Handler.handleUnmatchedUnlock(LDat->UnderlyingMutex.toString(),
UnlockLoc);
}
FSet.removeLock(FactMan, LDat->UnderlyingMutex);
return;
}
}
FSet.removeLock(FactMan, Mutex);
}
template <typename AttrType>
void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,
Expr *Exp, const NamedDecl *D) {
typedef typename AttrType::args_iterator iterator_type;
if (Attr->args_size() == 0) {
SExpr Mu(0, Exp, D);
if (!Mu.isValid())
SExpr::warnInvalidLock(Handler, 0, Exp, D);
else
Mtxs.push_back_nodup(Mu);
return;
}
for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) {
SExpr Mu(*I, Exp, D);
if (!Mu.isValid())
SExpr::warnInvalidLock(Handler, *I, Exp, D);
else
Mtxs.push_back_nodup(Mu);
}
}
template <class AttrType>
void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr,
Expr *Exp, const NamedDecl *D,
const CFGBlock *PredBlock,
const CFGBlock *CurrBlock,
Expr *BrE, bool Neg) {
bool branch = 0;
if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE)) {
branch = BLE->getValue();
}
else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE)) {
branch = ILE->getValue().getBoolValue();
}
int branchnum = branch ? 0 : 1;
if (Neg) branchnum = !branchnum;
int i = 0;
for (CFGBlock::const_succ_iterator SI = PredBlock->succ_begin(),
SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) {
if (*SI == CurrBlock && i == branchnum) {
getMutexIDs(Mtxs, Attr, Exp, D);
}
}
}
bool getStaticBooleanValue(Expr* E, bool& TCond) {
if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E)) {
TCond = false;
return true;
} else if (CXXBoolLiteralExpr *BLE = dyn_cast<CXXBoolLiteralExpr>(E)) {
TCond = BLE->getValue();
return true;
} else if (IntegerLiteral *ILE = dyn_cast<IntegerLiteral>(E)) {
TCond = ILE->getValue().getBoolValue();
return true;
} else if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E)) {
return getStaticBooleanValue(CE->getSubExpr(), TCond);
}
return false;
}
const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,
LocalVarContext C,
bool &Negate) {
if (!Cond)
return 0;
if (const CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) {
return CallExp;
}
else if (const ParenExpr *PE = dyn_cast<ParenExpr>(Cond)) {
return getTrylockCallExpr(PE->getSubExpr(), C, Negate);
}
else if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Cond)) {
return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
}
else if (const ExprWithCleanups* EWC = dyn_cast<ExprWithCleanups>(Cond)) {
return getTrylockCallExpr(EWC->getSubExpr(), C, Negate);
}
else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Cond)) {
const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
return getTrylockCallExpr(E, C, Negate);
}
else if (const UnaryOperator *UOP = dyn_cast<UnaryOperator>(Cond)) {
if (UOP->getOpcode() == UO_LNot) {
Negate = !Negate;
return getTrylockCallExpr(UOP->getSubExpr(), C, Negate);
}
return 0;
}
else if (const BinaryOperator *BOP = dyn_cast<BinaryOperator>(Cond)) {
if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {
if (BOP->getOpcode() == BO_NE)
Negate = !Negate;
bool TCond = false;
if (getStaticBooleanValue(BOP->getRHS(), TCond)) {
if (!TCond) Negate = !Negate;
return getTrylockCallExpr(BOP->getLHS(), C, Negate);
}
else if (getStaticBooleanValue(BOP->getLHS(), TCond)) {
if (!TCond) Negate = !Negate;
return getTrylockCallExpr(BOP->getRHS(), C, Negate);
}
return 0;
}
return 0;
}
return 0;
}
void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
const FactSet &ExitSet,
const CFGBlock *PredBlock,
const CFGBlock *CurrBlock) {
Result = ExitSet;
if (!PredBlock->getTerminatorCondition())
return;
bool Negate = false;
const Stmt *Cond = PredBlock->getTerminatorCondition();
const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()];
const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
CallExpr *Exp =
const_cast<CallExpr*>(getTrylockCallExpr(Cond, LVarCtx, Negate));
if (!Exp)
return;
NamedDecl *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
if(!FunDecl || !FunDecl->hasAttrs())
return;
MutexIDList ExclusiveLocksToAdd;
MutexIDList SharedLocksToAdd;
AttrVec &ArgAttrs = FunDecl->getAttrs();
for (unsigned i = 0; i < ArgAttrs.size(); ++i) {
Attr *Attr = ArgAttrs[i];
switch (Attr->getKind()) {
case attr::ExclusiveTrylockFunction: {
ExclusiveTrylockFunctionAttr *A =
cast<ExclusiveTrylockFunctionAttr>(Attr);
getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl,
PredBlock, CurrBlock, A->getSuccessValue(), Negate);
break;
}
case attr::SharedTrylockFunction: {
SharedTrylockFunctionAttr *A =
cast<SharedTrylockFunctionAttr>(Attr);
getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl,
PredBlock, CurrBlock, A->getSuccessValue(), Negate);
break;
}
default:
break;
}
}
SourceLocation Loc = Exp->getExprLoc();
for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
addLock(Result, ExclusiveLocksToAdd[i],
LockData(Loc, LK_Exclusive));
}
for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
addLock(Result, SharedLocksToAdd[i],
LockData(Loc, LK_Shared));
}
}
class BuildLockset : public StmtVisitor<BuildLockset> {
friend class ThreadSafetyAnalyzer;
ThreadSafetyAnalyzer *Analyzer;
FactSet FSet;
LocalVariableMap::Context LVarCtx;
unsigned CtxIndex;
const ValueDecl *getValueDecl(Expr *Exp);
void warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp, AccessKind AK,
Expr *MutexExp, ProtectedOperationKind POK);
void warnIfMutexHeld(const NamedDecl *D, Expr *Exp, Expr *MutexExp);
void checkAccess(Expr *Exp, AccessKind AK);
void checkDereference(Expr *Exp, AccessKind AK);
void handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD = 0);
public:
BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
: StmtVisitor<BuildLockset>(),
Analyzer(Anlzr),
FSet(Info.EntrySet),
LVarCtx(Info.EntryContext),
CtxIndex(Info.EntryIndex)
{}
void VisitUnaryOperator(UnaryOperator *UO);
void VisitBinaryOperator(BinaryOperator *BO);
void VisitCastExpr(CastExpr *CE);
void VisitCallExpr(CallExpr *Exp);
void VisitCXXConstructExpr(CXXConstructExpr *Exp);
void VisitDeclStmt(DeclStmt *S);
};
const ValueDecl *BuildLockset::getValueDecl(Expr *Exp) {
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp))
return DR->getDecl();
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp))
return ME->getMemberDecl();
return 0;
}
void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, Expr *Exp,
AccessKind AK, Expr *MutexExp,
ProtectedOperationKind POK) {
LockKind LK = getLockKindFromAccessKind(AK);
SExpr Mutex(MutexExp, Exp, D);
if (!Mutex.isValid()) {
SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D);
return;
} else if (Mutex.shouldIgnore()) {
return;
}
LockData* LDat = FSet.findLockUniv(Analyzer->FactMan, Mutex);
bool NoError = true;
if (!LDat) {
FactEntry* FEntry = FSet.findPartialMatch(Analyzer->FactMan, Mutex);
if (FEntry) {
LDat = &FEntry->LDat;
std::string PartMatchStr = FEntry->MutID.toString();
StringRef PartMatchName(PartMatchStr);
Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK,
Exp->getExprLoc(), &PartMatchName);
} else {
Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK,
Exp->getExprLoc());
}
NoError = false;
}
if (NoError && LDat && !LDat->isAtLeast(LK))
Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK,
Exp->getExprLoc());
}
void BuildLockset::warnIfMutexHeld(const NamedDecl *D, Expr* Exp,
Expr *MutexExp) {
SExpr Mutex(MutexExp, Exp, D);
if (!Mutex.isValid()) {
SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D);
return;
}
LockData* LDat = FSet.findLock(Analyzer->FactMan, Mutex);
if (LDat) {
std::string DeclName = D->getNameAsString();
StringRef DeclNameSR (DeclName);
Analyzer->Handler.handleFunExcludesLock(DeclNameSR, Mutex.toString(),
Exp->getExprLoc());
}
}
void BuildLockset::checkDereference(Expr *Exp, AccessKind AK) {
UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp);
if (!UO || UO->getOpcode() != clang::UO_Deref)
return;
Exp = UO->getSubExpr()->IgnoreParenCasts();
const ValueDecl *D = getValueDecl(Exp);
if(!D || !D->hasAttrs())
return;
if (D->getAttr<PtGuardedVarAttr>() && FSet.isEmpty())
Analyzer->Handler.handleNoMutexHeld(D, POK_VarDereference, AK,
Exp->getExprLoc());
const AttrVec &ArgAttrs = D->getAttrs();
for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
if (PtGuardedByAttr *PGBAttr = dyn_cast<PtGuardedByAttr>(ArgAttrs[i]))
warnIfMutexNotHeld(D, Exp, AK, PGBAttr->getArg(), POK_VarDereference);
}
void BuildLockset::checkAccess(Expr *Exp, AccessKind AK) {
const ValueDecl *D = getValueDecl(Exp);
if(!D || !D->hasAttrs())
return;
if (D->getAttr<GuardedVarAttr>() && FSet.isEmpty())
Analyzer->Handler.handleNoMutexHeld(D, POK_VarAccess, AK,
Exp->getExprLoc());
const AttrVec &ArgAttrs = D->getAttrs();
for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i)
if (GuardedByAttr *GBAttr = dyn_cast<GuardedByAttr>(ArgAttrs[i]))
warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarAccess);
}
void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
const AttrVec &ArgAttrs = D->getAttrs();
MutexIDList ExclusiveLocksToAdd;
MutexIDList SharedLocksToAdd;
MutexIDList LocksToRemove;
for(unsigned i = 0; i < ArgAttrs.size(); ++i) {
Attr *At = const_cast<Attr*>(ArgAttrs[i]);
switch (At->getKind()) {
case attr::ExclusiveLockFunction: {
ExclusiveLockFunctionAttr *A = cast<ExclusiveLockFunctionAttr>(At);
Analyzer->getMutexIDs(ExclusiveLocksToAdd, A, Exp, D);
break;
}
case attr::SharedLockFunction: {
SharedLockFunctionAttr *A = cast<SharedLockFunctionAttr>(At);
Analyzer->getMutexIDs(SharedLocksToAdd, A, Exp, D);
break;
}
case attr::UnlockFunction: {
UnlockFunctionAttr *A = cast<UnlockFunctionAttr>(At);
Analyzer->getMutexIDs(LocksToRemove, A, Exp, D);
break;
}
case attr::ExclusiveLocksRequired: {
ExclusiveLocksRequiredAttr *A = cast<ExclusiveLocksRequiredAttr>(At);
for (ExclusiveLocksRequiredAttr::args_iterator
I = A->args_begin(), E = A->args_end(); I != E; ++I)
warnIfMutexNotHeld(D, Exp, AK_Written, *I, POK_FunctionCall);
break;
}
case attr::SharedLocksRequired: {
SharedLocksRequiredAttr *A = cast<SharedLocksRequiredAttr>(At);
for (SharedLocksRequiredAttr::args_iterator I = A->args_begin(),
E = A->args_end(); I != E; ++I)
warnIfMutexNotHeld(D, Exp, AK_Read, *I, POK_FunctionCall);
break;
}
case attr::LocksExcluded: {
LocksExcludedAttr *A = cast<LocksExcludedAttr>(At);
for (LocksExcludedAttr::args_iterator I = A->args_begin(),
E = A->args_end(); I != E; ++I) {
warnIfMutexHeld(D, Exp, *I);
}
break;
}
default:
break;
}
}
bool isScopedVar = false;
if (VD) {
if (const CXXConstructorDecl *CD = dyn_cast<const CXXConstructorDecl>(D)) {
const CXXRecordDecl* PD = CD->getParent();
if (PD && PD->getAttr<ScopedLockableAttr>())
isScopedVar = true;
}
}
SourceLocation Loc = Exp->getExprLoc();
for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
Analyzer->addLock(FSet, ExclusiveLocksToAdd[i],
LockData(Loc, LK_Exclusive, isScopedVar));
}
for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
Analyzer->addLock(FSet, SharedLocksToAdd[i],
LockData(Loc, LK_Shared, isScopedVar));
}
if (isScopedVar) {
SourceLocation MLoc = VD->getLocation();
DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation());
SExpr SMutex(&DRE, 0, 0);
for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive,
ExclusiveLocksToAdd[i]));
}
for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared,
SharedLocksToAdd[i]));
}
}
bool Dtor = isa<CXXDestructorDecl>(D);
for (unsigned i=0,n=LocksToRemove.size(); i<n; ++i) {
Analyzer->removeLock(FSet, LocksToRemove[i], Loc, Dtor);
}
}
void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) {
switch (UO->getOpcode()) {
case clang::UO_PostDec:
case clang::UO_PostInc:
case clang::UO_PreDec:
case clang::UO_PreInc: {
Expr *SubExp = UO->getSubExpr()->IgnoreParenCasts();
checkAccess(SubExp, AK_Written);
checkDereference(SubExp, AK_Written);
break;
}
default:
break;
}
}
void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
if (!BO->isAssignmentOp())
return;
LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);
Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
checkAccess(LHSExp, AK_Written);
checkDereference(LHSExp, AK_Written);
}
void BuildLockset::VisitCastExpr(CastExpr *CE) {
if (CE->getCastKind() != CK_LValueToRValue)
return;
Expr *SubExp = CE->getSubExpr()->IgnoreParenCasts();
checkAccess(SubExp, AK_Read);
checkDereference(SubExp, AK_Read);
}
void BuildLockset::VisitCallExpr(CallExpr *Exp) {
NamedDecl *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
if(!D || !D->hasAttrs())
return;
handleCall(Exp, D);
}
void BuildLockset::VisitCXXConstructExpr(CXXConstructExpr *Exp) {
}
void BuildLockset::VisitDeclStmt(DeclStmt *S) {
LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
DeclGroupRef DGrp = S->getDeclGroup();
for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) {
Decl *D = *I;
if (VarDecl *VD = dyn_cast_or_null<VarDecl>(D)) {
Expr *E = VD->getInit();
if (ExprWithCleanups *EWC = dyn_cast_or_null<ExprWithCleanups>(E))
E = EWC->getSubExpr();
if (CXXConstructExpr *CE = dyn_cast_or_null<CXXConstructExpr>(E)) {
NamedDecl *CtorD = dyn_cast_or_null<NamedDecl>(CE->getConstructor());
if (!CtorD || !CtorD->hasAttrs())
return;
handleCall(CE, CtorD, VD);
}
}
}
}
void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1,
const FactSet &FSet2,
SourceLocation JoinLoc,
LockErrorKind LEK1,
LockErrorKind LEK2,
bool Modify) {
FactSet FSet1Orig = FSet1;
for (FactSet::const_iterator I = FSet2.begin(), E = FSet2.end();
I != E; ++I) {
const SExpr &FSet2Mutex = FactMan[*I].MutID;
const LockData &LDat2 = FactMan[*I].LDat;
if (const LockData *LDat1 = FSet1.findLock(FactMan, FSet2Mutex)) {
if (LDat1->LKind != LDat2.LKind) {
Handler.handleExclusiveAndShared(FSet2Mutex.toString(),
LDat2.AcquireLoc,
LDat1->AcquireLoc);
if (Modify && LDat1->LKind != LK_Exclusive) {
FSet1.removeLock(FactMan, FSet2Mutex);
FSet1.addLock(FactMan, FSet2Mutex, LDat2);
}
}
} else {
if (LDat2.UnderlyingMutex.isValid()) {
if (FSet2.findLock(FactMan, LDat2.UnderlyingMutex)) {
Handler.handleMutexHeldEndOfScope(LDat2.UnderlyingMutex.toString(),
LDat2.AcquireLoc,
JoinLoc, LEK1);
}
}
else if (!LDat2.Managed && !FSet2Mutex.isUniversal())
Handler.handleMutexHeldEndOfScope(FSet2Mutex.toString(),
LDat2.AcquireLoc,
JoinLoc, LEK1);
}
}
for (FactSet::const_iterator I = FSet1.begin(), E = FSet1.end();
I != E; ++I) {
const SExpr &FSet1Mutex = FactMan[*I].MutID;
const LockData &LDat1 = FactMan[*I].LDat;
if (!FSet2.findLock(FactMan, FSet1Mutex)) {
if (LDat1.UnderlyingMutex.isValid()) {
if (FSet1Orig.findLock(FactMan, LDat1.UnderlyingMutex)) {
Handler.handleMutexHeldEndOfScope(LDat1.UnderlyingMutex.toString(),
LDat1.AcquireLoc,
JoinLoc, LEK1);
}
}
else if (!LDat1.Managed && !FSet1Mutex.isUniversal())
Handler.handleMutexHeldEndOfScope(FSet1Mutex.toString(),
LDat1.AcquireLoc,
JoinLoc, LEK2);
if (Modify)
FSet1.removeLock(FactMan, FSet1Mutex);
}
}
}
void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
CFG *CFGraph = AC.getCFG();
if (!CFGraph) return;
const NamedDecl *D = dyn_cast_or_null<NamedDecl>(AC.getDecl());
if (!D)
return; if (D->getAttr<NoThreadSafetyAnalysisAttr>())
return;
if (isa<CXXConstructorDecl>(D))
return; if (isa<CXXDestructorDecl>(D))
return;
BlockInfo.resize(CFGraph->getNumBlockIDs(),
CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));
PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph);
BlockInfo[CFGraph->getEntry().getBlockID()].Reachable = true;
LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);
findBlockLocations(CFGraph, SortedGraph, BlockInfo);
if (!SortedGraph->empty() && D->hasAttrs()) {
const CFGBlock *FirstBlock = *SortedGraph->begin();
FactSet &InitialLockset = BlockInfo[FirstBlock->getBlockID()].EntrySet;
const AttrVec &ArgAttrs = D->getAttrs();
MutexIDList ExclusiveLocksToAdd;
MutexIDList SharedLocksToAdd;
SourceLocation Loc = D->getLocation();
for (unsigned i = 0; i < ArgAttrs.size(); ++i) {
Attr *Attr = ArgAttrs[i];
Loc = Attr->getLocation();
if (ExclusiveLocksRequiredAttr *A
= dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) {
getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D);
} else if (SharedLocksRequiredAttr *A
= dyn_cast<SharedLocksRequiredAttr>(Attr)) {
getMutexIDs(SharedLocksToAdd, A, (Expr*) 0, D);
} else if (isa<UnlockFunctionAttr>(Attr)) {
return;
} else if (isa<ExclusiveLockFunctionAttr>(Attr)) {
return;
} else if (isa<SharedLockFunctionAttr>(Attr)) {
return;
} else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) {
return;
} else if (isa<SharedTrylockFunctionAttr>(Attr)) {
return;
}
}
for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) {
addLock(InitialLockset, ExclusiveLocksToAdd[i],
LockData(Loc, LK_Exclusive));
}
for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) {
addLock(InitialLockset, SharedLocksToAdd[i],
LockData(Loc, LK_Shared));
}
}
for (PostOrderCFGView::iterator I = SortedGraph->begin(),
E = SortedGraph->end(); I!= E; ++I) {
const CFGBlock *CurrBlock = *I;
int CurrBlockID = CurrBlock->getBlockID();
CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
VisitedBlocks.insert(CurrBlock);
bool LocksetInitialized = false;
llvm::SmallVector<CFGBlock*, 8> SpecialBlocks;
for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
PE = CurrBlock->pred_end(); PI != PE; ++PI) {
if (*PI == 0 || !VisitedBlocks.alreadySet(*PI))
continue;
int PrevBlockID = (*PI)->getBlockID();
CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
if ((*PI)->hasNoReturnElement() || !PrevBlockInfo->Reachable)
continue;
CurrBlockInfo->Reachable = true;
if (const Stmt *Terminator = (*PI)->getTerminator()) {
if (isa<ContinueStmt>(Terminator) || isa<BreakStmt>(Terminator)) {
SpecialBlocks.push_back(*PI);
continue;
}
}
FactSet PrevLockset;
getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);
if (!LocksetInitialized) {
CurrBlockInfo->EntrySet = PrevLockset;
LocksetInitialized = true;
} else {
intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
CurrBlockInfo->EntryLoc,
LEK_LockedSomePredecessors);
}
}
if (!CurrBlockInfo->Reachable)
continue;
for (unsigned SpecialI = 0, SpecialN = SpecialBlocks.size();
SpecialI < SpecialN; ++SpecialI) {
CFGBlock *PrevBlock = SpecialBlocks[SpecialI];
int PrevBlockID = PrevBlock->getBlockID();
CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
if (!LocksetInitialized) {
CurrBlockInfo->EntrySet = PrevBlockInfo->ExitSet;
LocksetInitialized = true;
} else {
const Stmt *Terminator = PrevBlock->getTerminator();
bool IsLoop = Terminator && isa<ContinueStmt>(Terminator);
FactSet PrevLockset;
getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet,
PrevBlock, CurrBlock);
intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset,
PrevBlockInfo->ExitLoc,
IsLoop ? LEK_LockedSomeLoopIterations
: LEK_LockedSomePredecessors,
false);
}
}
BuildLockset LocksetBuilder(this, *CurrBlockInfo);
for (CFGBlock::const_iterator BI = CurrBlock->begin(),
BE = CurrBlock->end(); BI != BE; ++BI) {
switch (BI->getKind()) {
case CFGElement::Statement: {
const CFGStmt *CS = cast<CFGStmt>(&*BI);
LocksetBuilder.Visit(const_cast<Stmt*>(CS->getStmt()));
break;
}
case CFGElement::AutomaticObjectDtor: {
const CFGAutomaticObjDtor *AD = cast<CFGAutomaticObjDtor>(&*BI);
CXXDestructorDecl *DD = const_cast<CXXDestructorDecl*>(
AD->getDestructorDecl(AC.getASTContext()));
if (!DD->hasAttrs())
break;
VarDecl *VD = const_cast<VarDecl*>(AD->getVarDecl());
DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue,
AD->getTriggerStmt()->getLocEnd());
LocksetBuilder.handleCall(&DRE, DD);
break;
}
default:
break;
}
}
CurrBlockInfo->ExitSet = LocksetBuilder.FSet;
for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
SE = CurrBlock->succ_end(); SI != SE; ++SI) {
if (*SI == 0 || !VisitedBlocks.alreadySet(*SI))
continue;
CFGBlock *FirstLoopBlock = *SI;
CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->getBlockID()];
CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];
intersectAndWarn(LoopEnd->ExitSet, PreLoop->EntrySet,
PreLoop->EntryLoc,
LEK_LockedSomeLoopIterations,
false);
}
}
CFGBlockInfo *Initial = &BlockInfo[CFGraph->getEntry().getBlockID()];
CFGBlockInfo *Final = &BlockInfo[CFGraph->getExit().getBlockID()];
if (!Final->Reachable)
return;
intersectAndWarn(Initial->EntrySet, Final->ExitSet,
Final->ExitLoc,
LEK_LockedAtEndOfFunction,
LEK_NotLockedAtEndOfFunction,
false);
}
}
namespace clang {
namespace thread_safety {
void runThreadSafetyAnalysis(AnalysisDeclContext &AC,
ThreadSafetyHandler &Handler) {
ThreadSafetyAnalyzer Analyzer(Handler);
Analyzer.runAnalysis(AC);
}
LockKind getLockKindFromAccessKind(AccessKind AK) {
switch (AK) {
case AK_Read :
return LK_Shared;
case AK_Written :
return LK_Exclusive;
}
llvm_unreachable("Unknown AccessKind");
}
}}