diff options
| author | Ralf Luther <luther.ralf@gmail.com> | 2019-03-27 20:23:17 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit2@aicp-server-3> | 2019-03-27 20:23:17 +0000 |
| commit | 1ce3a9d272e564b22a1333a1e36a3d3ab7cfab01 (patch) | |
| tree | 391382eadd4fec5bb480f2e8934fa352770221d1 /clang-r353983/include/clang/Analysis/Analyses/ThreadSafetyCommon.h | |
| parent | d1d48b140bafaa8a50107292f5fce95562575765 (diff) | |
| parent | 4f56932d3416ac03f646bc1a611b3135fec2fe08 (diff) | |
Merge "Update prebuilt Clang to r353983." into p9.0HEADp9.0-backupp9.0
Diffstat (limited to 'clang-r353983/include/clang/Analysis/Analyses/ThreadSafetyCommon.h')
| -rw-r--r-- | clang-r353983/include/clang/Analysis/Analyses/ThreadSafetyCommon.h | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/clang-r353983/include/clang/Analysis/Analyses/ThreadSafetyCommon.h b/clang-r353983/include/clang/Analysis/Analyses/ThreadSafetyCommon.h new file mode 100644 index 00000000..4a58fe87 --- /dev/null +++ b/clang-r353983/include/clang/Analysis/Analyses/ThreadSafetyCommon.h @@ -0,0 +1,520 @@ +//===- ThreadSafetyCommon.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Parts of thread safety analysis that are not specific to thread safety +// itself have been factored into classes here, where they can be potentially +// used by other analyses. Currently these include: +// +// * Generalize clang CFG visitors. +// * Conversion of the clang CFG to SSA form. +// * Translation of clang Exprs to TIL SExprs +// +// UNDER CONSTRUCTION. USE AT YOUR OWN RISK. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETYCOMMON_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETYCOMMON_H + +#include "clang/AST/Decl.h" +#include "clang/Analysis/Analyses/PostOrderCFGView.h" +#include "clang/Analysis/Analyses/ThreadSafetyTIL.h" +#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h" +#include "clang/Analysis/Analyses/ThreadSafetyUtil.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +namespace clang { + +class AbstractConditionalOperator; +class ArraySubscriptExpr; +class BinaryOperator; +class CallExpr; +class CastExpr; +class CXXDestructorDecl; +class CXXMemberCallExpr; +class CXXOperatorCallExpr; +class CXXThisExpr; +class DeclRefExpr; +class DeclStmt; +class Expr; +class MemberExpr; +class Stmt; +class UnaryOperator; + +namespace threadSafety { + +// Various helper functions on til::SExpr +namespace sx { + +inline bool equals(const til::SExpr *E1, const til::SExpr *E2) { + return til::EqualsComparator::compareExprs(E1, E2); +} + +inline bool matches(const til::SExpr *E1, const til::SExpr *E2) { + // We treat a top-level wildcard as the "univsersal" lock. + // It matches everything for the purpose of checking locks, but not + // for unlocking them. + if (isa<til::Wildcard>(E1)) + return isa<til::Wildcard>(E2); + if (isa<til::Wildcard>(E2)) + return isa<til::Wildcard>(E1); + + return til::MatchComparator::compareExprs(E1, E2); +} + +inline bool partiallyMatches(const til::SExpr *E1, const til::SExpr *E2) { + const auto *PE1 = dyn_cast_or_null<til::Project>(E1); + if (!PE1) + return false; + const auto *PE2 = dyn_cast_or_null<til::Project>(E2); + if (!PE2) + return false; + return PE1->clangDecl() == PE2->clangDecl(); +} + +inline std::string toString(const til::SExpr *E) { + std::stringstream ss; + til::StdPrinter::print(E, ss); + return ss.str(); +} + +} // namespace sx + +// This class defines the interface of a clang CFG Visitor. +// CFGWalker will invoke the following methods. +// Note that methods are not virtual; the visitor is templatized. +class CFGVisitor { + // Enter the CFG for Decl D, and perform any initial setup operations. + void enterCFG(CFG *Cfg, const NamedDecl *D, const CFGBlock *First) {} + + // Enter a CFGBlock. + void enterCFGBlock(const CFGBlock *B) {} + + // Returns true if this visitor implements handlePredecessor + bool visitPredecessors() { return true; } + + // Process a predecessor edge. + void handlePredecessor(const CFGBlock *Pred) {} + + // Process a successor back edge to a previously visited block. + void handlePredecessorBackEdge(const CFGBlock *Pred) {} + + // Called just before processing statements. + void enterCFGBlockBody(const CFGBlock *B) {} + + // Process an ordinary statement. + void handleStatement(const Stmt *S) {} + + // Process a destructor call + void handleDestructorCall(const VarDecl *VD, const CXXDestructorDecl *DD) {} + + // Called after all statements have been handled. + void exitCFGBlockBody(const CFGBlock *B) {} + + // Return true + bool visitSuccessors() { return true; } + + // Process a successor edge. + void handleSuccessor(const CFGBlock *Succ) {} + + // Process a successor back edge to a previously visited block. + void handleSuccessorBackEdge(const CFGBlock *Succ) {} + + // Leave a CFGBlock. + void exitCFGBlock(const CFGBlock *B) {} + + // Leave the CFG, and perform any final cleanup operations. + void exitCFG(const CFGBlock *Last) {} +}; + +// Walks the clang CFG, and invokes methods on a given CFGVisitor. +class CFGWalker { +public: + CFGWalker() = default; + + // Initialize the CFGWalker. This setup only needs to be done once, even + // if there are multiple passes over the CFG. + bool init(AnalysisDeclContext &AC) { + ACtx = &AC; + CFGraph = AC.getCFG(); + if (!CFGraph) + return false; + + // Ignore anonymous functions. + if (!dyn_cast_or_null<NamedDecl>(AC.getDecl())) + return false; + + SortedGraph = AC.getAnalysis<PostOrderCFGView>(); + if (!SortedGraph) + return false; + + return true; + } + + // Traverse the CFG, calling methods on V as appropriate. + template <class Visitor> + void walk(Visitor &V) { + PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph); + + V.enterCFG(CFGraph, getDecl(), &CFGraph->getEntry()); + + for (const auto *CurrBlock : *SortedGraph) { + VisitedBlocks.insert(CurrBlock); + + V.enterCFGBlock(CurrBlock); + + // Process predecessors, handling back edges last + if (V.visitPredecessors()) { + SmallVector<CFGBlock*, 4> BackEdges; + // Process successors + for (CFGBlock::const_pred_iterator SI = CurrBlock->pred_begin(), + SE = CurrBlock->pred_end(); + SI != SE; ++SI) { + if (*SI == nullptr) + continue; + + if (!VisitedBlocks.alreadySet(*SI)) { + BackEdges.push_back(*SI); + continue; + } + V.handlePredecessor(*SI); + } + + for (auto *Blk : BackEdges) + V.handlePredecessorBackEdge(Blk); + } + + V.enterCFGBlockBody(CurrBlock); + + // Process statements + for (const auto &BI : *CurrBlock) { + switch (BI.getKind()) { + case CFGElement::Statement: + V.handleStatement(BI.castAs<CFGStmt>().getStmt()); + break; + + case CFGElement::AutomaticObjectDtor: { + CFGAutomaticObjDtor AD = BI.castAs<CFGAutomaticObjDtor>(); + auto *DD = const_cast<CXXDestructorDecl *>( + AD.getDestructorDecl(ACtx->getASTContext())); + auto *VD = const_cast<VarDecl *>(AD.getVarDecl()); + V.handleDestructorCall(VD, DD); + break; + } + default: + break; + } + } + + V.exitCFGBlockBody(CurrBlock); + + // Process successors, handling back edges first. + if (V.visitSuccessors()) { + SmallVector<CFGBlock*, 8> ForwardEdges; + + // Process successors + for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), + SE = CurrBlock->succ_end(); + SI != SE; ++SI) { + if (*SI == nullptr) + continue; + + if (!VisitedBlocks.alreadySet(*SI)) { + ForwardEdges.push_back(*SI); + continue; + } + V.handleSuccessorBackEdge(*SI); + } + + for (auto *Blk : ForwardEdges) + V.handleSuccessor(Blk); + } + + V.exitCFGBlock(CurrBlock); + } + V.exitCFG(&CFGraph->getExit()); + } + + const CFG *getGraph() const { return CFGraph; } + CFG *getGraph() { return CFGraph; } + + const NamedDecl *getDecl() const { + return dyn_cast<NamedDecl>(ACtx->getDecl()); + } + + const PostOrderCFGView *getSortedGraph() const { return SortedGraph; } + +private: + CFG *CFGraph = nullptr; + AnalysisDeclContext *ACtx = nullptr; + PostOrderCFGView *SortedGraph = nullptr; +}; + +// TODO: move this back into ThreadSafety.cpp +// This is specific to thread safety. It is here because +// translateAttrExpr needs it, but that should be moved too. +class CapabilityExpr { +private: + /// The capability expression. + const til::SExpr* CapExpr; + + /// True if this is a negative capability. + bool Negated; + +public: + CapabilityExpr(const til::SExpr *E, bool Neg) : CapExpr(E), Negated(Neg) {} + + const til::SExpr* sexpr() const { return CapExpr; } + bool negative() const { return Negated; } + + CapabilityExpr operator!() const { + return CapabilityExpr(CapExpr, !Negated); + } + + bool equals(const CapabilityExpr &other) const { + return (Negated == other.Negated) && sx::equals(CapExpr, other.CapExpr); + } + + bool matches(const CapabilityExpr &other) const { + return (Negated == other.Negated) && sx::matches(CapExpr, other.CapExpr); + } + + bool matchesUniv(const CapabilityExpr &CapE) const { + return isUniversal() || matches(CapE); + } + + bool partiallyMatches(const CapabilityExpr &other) const { + return (Negated == other.Negated) && + sx::partiallyMatches(CapExpr, other.CapExpr); + } + + const ValueDecl* valueDecl() const { + if (Negated || CapExpr == nullptr) + return nullptr; + if (const auto *P = dyn_cast<til::Project>(CapExpr)) + return P->clangDecl(); + if (const auto *P = dyn_cast<til::LiteralPtr>(CapExpr)) + return P->clangDecl(); + return nullptr; + } + + std::string toString() const { + if (Negated) + return "!" + sx::toString(CapExpr); + return sx::toString(CapExpr); + } + + bool shouldIgnore() const { return CapExpr == nullptr; } + + bool isInvalid() const { return sexpr() && isa<til::Undefined>(sexpr()); } + + bool isUniversal() const { return sexpr() && isa<til::Wildcard>(sexpr()); } +}; + +// Translate clang::Expr to til::SExpr. +class SExprBuilder { +public: + /// Encapsulates the lexical context of a function call. The lexical + /// context includes the arguments to the call, including the implicit object + /// argument. When an attribute containing a mutex expression is attached to + /// a method, the expression may refer to formal parameters of the method. + /// Actual arguments must be substituted for formal parameters to derive + /// the appropriate mutex expression in the lexical context where the function + /// is called. PrevCtx holds the context in which the arguments themselves + /// should be evaluated; multiple calling contexts can be chained together + /// by the lock_returned attribute. + struct CallingContext { + // The previous context; or 0 if none. + CallingContext *Prev; + + // The decl to which the attr is attached. + const NamedDecl *AttrDecl; + + // Implicit object argument -- e.g. 'this' + const Expr *SelfArg = nullptr; + + // Number of funArgs + unsigned NumArgs = 0; + + // Function arguments + const Expr *const *FunArgs = nullptr; + + // is Self referred to with -> or .? + bool SelfArrow = false; + + CallingContext(CallingContext *P, const NamedDecl *D = nullptr) + : Prev(P), AttrDecl(D) {} + }; + + SExprBuilder(til::MemRegionRef A) : Arena(A) { + // FIXME: we don't always have a self-variable. + SelfVar = new (Arena) til::Variable(nullptr); + SelfVar->setKind(til::Variable::VK_SFun); + } + + // Translate a clang expression in an attribute to a til::SExpr. + // Constructs the context from D, DeclExp, and SelfDecl. + CapabilityExpr translateAttrExpr(const Expr *AttrExp, const NamedDecl *D, + const Expr *DeclExp, VarDecl *SelfD=nullptr); + + CapabilityExpr translateAttrExpr(const Expr *AttrExp, CallingContext *Ctx); + + // Translate a clang statement or expression to a TIL expression. + // Also performs substitution of variables; Ctx provides the context. + // Dispatches on the type of S. + til::SExpr *translate(const Stmt *S, CallingContext *Ctx); + til::SCFG *buildCFG(CFGWalker &Walker); + + til::SExpr *lookupStmt(const Stmt *S); + + til::BasicBlock *lookupBlock(const CFGBlock *B) { + return BlockMap[B->getBlockID()]; + } + + const til::SCFG *getCFG() const { return Scfg; } + til::SCFG *getCFG() { return Scfg; } + +private: + // We implement the CFGVisitor API + friend class CFGWalker; + + til::SExpr *translateDeclRefExpr(const DeclRefExpr *DRE, + CallingContext *Ctx) ; + til::SExpr *translateCXXThisExpr(const CXXThisExpr *TE, CallingContext *Ctx); + til::SExpr *translateMemberExpr(const MemberExpr *ME, CallingContext *Ctx); + til::SExpr *translateObjCIVarRefExpr(const ObjCIvarRefExpr *IVRE, + CallingContext *Ctx); + til::SExpr *translateCallExpr(const CallExpr *CE, CallingContext *Ctx, + const Expr *SelfE = nullptr); + til::SExpr *translateCXXMemberCallExpr(const CXXMemberCallExpr *ME, + CallingContext *Ctx); + til::SExpr *translateCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE, + CallingContext *Ctx); + til::SExpr *translateUnaryOperator(const UnaryOperator *UO, + CallingContext *Ctx); + til::SExpr *translateBinOp(til::TIL_BinaryOpcode Op, + const BinaryOperator *BO, + CallingContext *Ctx, bool Reverse = false); + til::SExpr *translateBinAssign(til::TIL_BinaryOpcode Op, + const BinaryOperator *BO, + CallingContext *Ctx, bool Assign = false); + til::SExpr *translateBinaryOperator(const BinaryOperator *BO, + CallingContext *Ctx); + til::SExpr *translateCastExpr(const CastExpr *CE, CallingContext *Ctx); + til::SExpr *translateArraySubscriptExpr(const ArraySubscriptExpr *E, + CallingContext *Ctx); + til::SExpr *translateAbstractConditionalOperator( + const AbstractConditionalOperator *C, CallingContext *Ctx); + + til::SExpr *translateDeclStmt(const DeclStmt *S, CallingContext *Ctx); + + // Map from statements in the clang CFG to SExprs in the til::SCFG. + using StatementMap = llvm::DenseMap<const Stmt *, til::SExpr *>; + + // Map from clang local variables to indices in a LVarDefinitionMap. + using LVarIndexMap = llvm::DenseMap<const ValueDecl *, unsigned>; + + // Map from local variable indices to SSA variables (or constants). + using NameVarPair = std::pair<const ValueDecl *, til::SExpr *>; + using LVarDefinitionMap = CopyOnWriteVector<NameVarPair>; + + struct BlockInfo { + LVarDefinitionMap ExitMap; + bool HasBackEdges = false; + + // Successors yet to be processed + unsigned UnprocessedSuccessors = 0; + + // Predecessors already processed + unsigned ProcessedPredecessors = 0; + + BlockInfo() = default; + BlockInfo(BlockInfo &&) = default; + BlockInfo &operator=(BlockInfo &&) = default; + }; + + void enterCFG(CFG *Cfg, const NamedDecl *D, const CFGBlock *First); + void enterCFGBlock(const CFGBlock *B); + bool visitPredecessors() { return true; } + void handlePredecessor(const CFGBlock *Pred); + void handlePredecessorBackEdge(const CFGBlock *Pred); + void enterCFGBlockBody(const CFGBlock *B); + void handleStatement(const Stmt *S); + void handleDestructorCall(const VarDecl *VD, const CXXDestructorDecl *DD); + void exitCFGBlockBody(const CFGBlock *B); + bool visitSuccessors() { return true; } + void handleSuccessor(const CFGBlock *Succ); + void handleSuccessorBackEdge(const CFGBlock *Succ); + void exitCFGBlock(const CFGBlock *B); + void exitCFG(const CFGBlock *Last); + + void insertStmt(const Stmt *S, til::SExpr *E) { + SMap.insert(std::make_pair(S, E)); + } + + til::SExpr *getCurrentLVarDefinition(const ValueDecl *VD); + + til::SExpr *addStatement(til::SExpr *E, const Stmt *S, + const ValueDecl *VD = nullptr); + til::SExpr *lookupVarDecl(const ValueDecl *VD); + til::SExpr *addVarDecl(const ValueDecl *VD, til::SExpr *E); + til::SExpr *updateVarDecl(const ValueDecl *VD, til::SExpr *E); + + void makePhiNodeVar(unsigned i, unsigned NPreds, til::SExpr *E); + void mergeEntryMap(LVarDefinitionMap Map); + void mergeEntryMapBackEdge(); + void mergePhiNodesBackEdge(const CFGBlock *Blk); + +private: + // Set to true when parsing capability expressions, which get translated + // inaccurately in order to hack around smart pointers etc. + static const bool CapabilityExprMode = true; + + til::MemRegionRef Arena; + + // Variable to use for 'this'. May be null. + til::Variable *SelfVar = nullptr; + + til::SCFG *Scfg = nullptr; + + // Map from Stmt to TIL Variables + StatementMap SMap; + + // Indices of clang local vars. + LVarIndexMap LVarIdxMap; + + // Map from clang to til BBs. + std::vector<til::BasicBlock *> BlockMap; + + // Extra information per BB. Indexed by clang BlockID. + std::vector<BlockInfo> BBInfo; + + LVarDefinitionMap CurrentLVarMap; + std::vector<til::Phi *> CurrentArguments; + std::vector<til::SExpr *> CurrentInstructions; + std::vector<til::Phi *> IncompleteArgs; + til::BasicBlock *CurrentBB = nullptr; + BlockInfo *CurrentBlockInfo = nullptr; +}; + +// Dump an SCFG to llvm::errs(). +void printSCFG(CFGWalker &Walker); + +} // namespace threadSafety +} // namespace clang + +#endif // LLVM_CLANG_THREAD_SAFETY_COMMON_H |
