aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Analysis/Analyses/ThreadSafetyCommon.h244
-rw-r--r--include/clang/Analysis/Analyses/ThreadSafetyOps.def44
-rw-r--r--include/clang/Analysis/Analyses/ThreadSafetyTIL.h1831
-rw-r--r--lib/Analysis/CMakeLists.txt1
-rw-r--r--lib/Analysis/ThreadSafety.cpp23
-rw-r--r--lib/Analysis/ThreadSafetyCommon.cpp407
6 files changed, 2542 insertions, 8 deletions
diff --git a/include/clang/Analysis/Analyses/ThreadSafetyCommon.h b/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
new file mode 100644
index 0000000000..92d904e59a
--- /dev/null
+++ b/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
@@ -0,0 +1,244 @@
+//===- ThreadSafetyCommon.h ------------------------------------*- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/DenseMap.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 <vector>
+
+
+namespace clang {
+namespace threadSafety {
+
+
+// Simple Visitor class for traversing a clang CFG.
+class CFGVisitor {
+public:
+ // 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) {}
+
+ // Process an ordinary statement.
+ void handleStatement(const Stmt *S) {}
+
+ // Process a destructor call
+ void handleDestructorCall(const VarDecl *VD, const CXXDestructorDecl *DD) {}
+
+ // 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() : CFGraph(0), FDecl(0), ACtx(0), SortedGraph(0) {}
+
+ ~CFGWalker() { }
+
+ // 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;
+
+ FDecl = dyn_cast_or_null<NamedDecl>(AC.getDecl());
+ if (!FDecl) // ignore anonymous functions
+ 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, FDecl, &CFGraph->getEntry());
+
+ for (const CFGBlock* CurrBlock : *SortedGraph) {
+ VisitedBlocks.insert(CurrBlock);
+
+ V.enterCFGBlock(CurrBlock);
+
+ // Process statements
+ for (CFGBlock::const_iterator BI = CurrBlock->begin(),
+ BE = CurrBlock->end();
+ BI != BE; ++BI) {
+ switch (BI->getKind()) {
+ case CFGElement::Statement: {
+ V.handleStatement(BI->castAs<CFGStmt>().getStmt());
+ break;
+ }
+ case CFGElement::AutomaticObjectDtor: {
+ CFGAutomaticObjDtor AD = BI->castAs<CFGAutomaticObjDtor>();
+ CXXDestructorDecl *DD = const_cast<CXXDestructorDecl*>(
+ AD.getDestructorDecl(ACtx->getASTContext()));
+ VarDecl *VD = const_cast<VarDecl*>(AD.getVarDecl());
+ V.handleDestructorCall(VD, DD);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Process successors
+ for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
+ SE = CurrBlock->succ_end();
+ SI != SE; ++SI) {
+ if (*SI == 0)
+ continue;
+
+ if (VisitedBlocks.alreadySet(*SI)) {
+ V.handleSuccessorBackEdge(*SI);
+ continue;
+ }
+ V.handleSuccessor(*SI);
+ }
+
+ V.exitCFGBlock(CurrBlock);
+ }
+ V.exitCFG(&CFGraph->getExit());
+ }
+
+public:
+ CFG *CFGraph;
+ const NamedDecl *FDecl;
+ AnalysisDeclContext *ACtx;
+ PostOrderCFGView *SortedGraph;
+};
+
+
+// Translate clang::Expr to til::SExpr.
+class SExprBuilder {
+public:
+ typedef llvm::DenseMap<const Stmt*, til::Variable*> StatementMap;
+
+ /// \brief 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 {
+ const NamedDecl *AttrDecl; // The decl to which the attr is attached.
+ const Expr *SelfArg; // Implicit object argument -- e.g. 'this'
+ unsigned NumArgs; // Number of funArgs
+ const Expr *const *FunArgs; // Function arguments
+ CallingContext *Prev; // The previous context; or 0 if none.
+ bool SelfArrow; // is Self referred to with -> or .?
+
+ CallingContext(const NamedDecl *D = 0, const Expr *S = 0, unsigned N = 0,
+ const Expr *const *A = 0, CallingContext *P = 0)
+ : AttrDecl(D), SelfArg(S), NumArgs(N), FunArgs(A), Prev(P),
+ SelfArrow(false)
+ {}
+ };
+
+ til::SExpr *lookupStmt(const Stmt *S);
+ void insertStmt(const Stmt *S, til::Variable *V);
+
+ // 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::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 *translateCallExpr(const CallExpr *CE, CallingContext *Ctx);
+ 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 *translateBinaryOperator(const BinaryOperator *BO,
+ CallingContext *Ctx);
+ til::SExpr *translateCastExpr(const CastExpr *CE, CallingContext *Ctx);
+ til::SExpr *translateArraySubscriptExpr(const ArraySubscriptExpr *E,
+ CallingContext *Ctx);
+ til::SExpr *translateConditionalOperator(const ConditionalOperator *C,
+ CallingContext *Ctx);
+ til::SExpr *translateBinaryConditionalOperator(
+ const BinaryConditionalOperator *C, CallingContext *Ctx);
+
+
+ SExprBuilder(til::MemRegionRef A, StatementMap *SM = 0)
+ : Arena(A), SMap(SM), SelfVar(0) {
+ // FIXME: we don't always have a self-variable.
+ SelfVar = new (Arena) til::Variable(til::Variable::VK_SFun);
+ }
+
+protected:
+ til::MemRegionRef Arena;
+ StatementMap *SMap; // Map from Stmt to TIL Variables
+ til::Variable *SelfVar; // Variable to use for 'this'
+};
+
+
+// Dump an SCFG to llvm::errs().
+void printSCFG(CFGWalker &walker);
+
+
+} // end namespace threadSafety
+
+} // end namespace clang
diff --git a/include/clang/Analysis/Analyses/ThreadSafetyOps.def b/include/clang/Analysis/Analyses/ThreadSafetyOps.def
new file mode 100644
index 0000000000..b33e2e8bf6
--- /dev/null
+++ b/include/clang/Analysis/Analyses/ThreadSafetyOps.def
@@ -0,0 +1,44 @@
+//===- ThreadSafetyTIL.h ---------------------------------------*- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the list of core opcodes for the Thread Safety
+// Typed Intermediate language. Please see ThreadSafetyTIL.h for more
+// information.
+//
+//===----------------------------------------------------------------------===//
+
+
+TIL_OPCODE_DEF(Future)
+TIL_OPCODE_DEF(Undefined)
+TIL_OPCODE_DEF(Wildcard)
+
+TIL_OPCODE_DEF(Literal)
+TIL_OPCODE_DEF(LiteralPtr)
+TIL_OPCODE_DEF(Variable)
+TIL_OPCODE_DEF(Function)
+TIL_OPCODE_DEF(SFunction)
+TIL_OPCODE_DEF(Code)
+
+TIL_OPCODE_DEF(Apply)
+TIL_OPCODE_DEF(SApply)
+TIL_OPCODE_DEF(Project)
+
+TIL_OPCODE_DEF(Call)
+TIL_OPCODE_DEF(Alloc)
+TIL_OPCODE_DEF(Load)
+TIL_OPCODE_DEF(Store)
+
+TIL_OPCODE_DEF(UnaryOp)
+TIL_OPCODE_DEF(BinaryOp)
+TIL_OPCODE_DEF(Cast)
+
+TIL_OPCODE_DEF(SCFG)
+TIL_OPCODE_DEF(Phi)
+TIL_OPCODE_DEF(Goto)
+TIL_OPCODE_DEF(Branch)
diff --git a/include/clang/Analysis/Analyses/ThreadSafetyTIL.h b/include/clang/Analysis/Analyses/ThreadSafetyTIL.h
new file mode 100644
index 0000000000..782faf6976
--- /dev/null
+++ b/include/clang/Analysis/Analyses/ThreadSafetyTIL.h
@@ -0,0 +1,1831 @@
+//===- ThreadSafetyTIL.h ---------------------------------------*- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a simple intermediate language that is used by the
+// thread safety analysis (See ThreadSafety.cpp). The thread safety analysis
+// works by comparing mutex expressions, e.g.
+//
+// class A { Mutex mu; int dat GUARDED_BY(this->mu); }
+// class B { A a; }
+//
+// void foo(B* b) {
+// (*b).a.mu.lock(); // locks (*b).a.mu
+// b->a.dat = 0; // substitute &b->a for 'this';
+// // requires lock on (&b->a)->mu
+// (b->a.mu).unlock(); // unlocks (b->a.mu)
+// }
+//
+// As illustrated by the above example, clang Exprs are not well-suited to
+// represent mutex expressions directly, since there is no easy way to compare
+// Exprs for equivalence. The thread safety analysis thus lowers clang Exprs
+// into a simple intermediate language (IL). The IL supports:
+//
+// (1) comparisons for semantic equality of expressions
+// (2) SSA renaming of variables
+// (3) wildcards and pattern matching over expressions
+// (4) hash-based expression lookup
+//
+// The IL is currently very experimental, is intended only for use within
+// the thread safety analysis, and is subject to change without notice.
+// After the API stabilizes and matures, it may be appropriate to make this
+// more generally available to other analyses.
+//
+// UNDER CONSTRUCTION. USE AT YOUR OWN RISK.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_THREAD_SAFETY_TIL_H
+#define LLVM_CLANG_THREAD_SAFETY_TIL_H
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/Type.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/AlignOf.h"
+#include "llvm/Support/Allocator.h"
+
+#include <cassert>
+#include <cstddef>
+
+namespace clang {
+namespace threadSafety {
+namespace til {
+
+
+// Simple wrapper class to abstract away from the details of memory management.
+// SExprs are allocated in pools, and deallocated all at once.
+class MemRegionRef {
+private:
+ union AlignmentType {
+ double d;
+ void *p;
+ long double dd;
+ long long ii;
+ };
+
+public:
+ MemRegionRef() : Allocator(0) {}
+ MemRegionRef(llvm::BumpPtrAllocator *A) : Allocator(A) {}
+
+ void *allocate(size_t Sz) {
+ return Allocator->Allocate(Sz, llvm::AlignOf<AlignmentType>::Alignment);
+ }
+
+ template <typename T> T *allocateT() { return Allocator->Allocate<T>(); }
+
+ template <typename T> T *allocateT(size_t NumElems) {
+ return Allocator->Allocate<T>(NumElems);
+ }
+
+private:
+ llvm::BumpPtrAllocator *Allocator;
+};
+
+
+} // end namespace til
+} // end namespace threadSafety
+} // end namespace clang
+
+
+inline void *operator new(size_t Sz,
+ clang::threadSafety::til::MemRegionRef &R) {
+ return R.allocate(Sz);
+}
+
+
+namespace clang {
+namespace threadSafety {
+namespace til {
+
+using llvm::StringRef;
+
+// A simple fixed size array class that does not manage its own memory,
+// suitable for use with bump pointer allocation.
+template <class T> class SimpleArray {
+public:
+ SimpleArray() : Data(0), Size(0), Capacity(0) {}
+ SimpleArray(T *Dat, size_t Cp, size_t Sz = 0)
+ : Data(Dat), Size(0), Capacity(Cp) {}
+ SimpleArray(MemRegionRef A, size_t Cp)
+ : Data(A.allocateT<T>(Cp)), Size(0), Capacity(Cp) {}
+ SimpleArray(SimpleArray<T> &A, bool Steal)
+ : Data(A.Data), Size(A.Size), Capacity(A.Capacity) {
+ A.Data = 0;
+ A.Size = 0;
+ A.Capacity = 0;
+ }
+
+ T *resize(size_t Ncp, MemRegionRef A) {
+ T *Odata = Data;
+ Data = A.allocateT<T>(Ncp);
+ memcpy(Data, Odata, sizeof(T) * Size);
+ return Odata;
+ }
+
+ typedef T *iterator;
+ typedef const T *const_iterator;
+
+ size_t size() const { return Size; }
+ size_t capacity() const { return Capacity; }
+
+ T &operator[](unsigned I) { return Data[I]; }
+ const T &operator[](unsigned I) const { return Data[I]; }
+
+ iterator begin() { return Data; }
+ iterator end() { return Data + Size; }
+
+ const_iterator cbegin() const { return Data; }
+ const_iterator cend() const { return Data + Size; }
+
+ void push_back(const T &Elem) {
+ assert(Size < Capacity);
+ Data[Size++] = Elem;
+ }
+
+ template <class Iter> unsigned append(Iter I, Iter E) {
+ size_t Osz = Size;
+ size_t J = Osz;
+ for (; J < Capacity && I != E; ++J, ++I)
+ Data[J] = *I;
+ Size = J;
+ return J - Osz;
+ }
+
+private:
+ T *Data;
+ size_t Size;
+ size_t Capacity;
+};
+
+
+enum TIL_Opcode {
+#define TIL_OPCODE_DEF(X) COP_##X,
+#include "clang/Analysis/Analyses/ThreadSafetyOps.def"
+#undef TIL_OPCODE_DEF
+ COP_MAX
+};
+
+
+typedef clang::BinaryOperatorKind TIL_BinaryOpcode;
+typedef clang::UnaryOperatorKind TIL_UnaryOpcode;
+typedef clang::CastKind TIL_CastOpcode;
+
+
+enum TIL_TraversalKind {
+ TRV_Normal,
+ TRV_Lazy, // subexpression may need to be traversed lazily
+ TRV_Tail // subexpression occurs in a tail position
+};
+
+
+// Base class for AST nodes in the typed intermediate language.
+class SExpr {
+public:
+ TIL_Opcode opcode() const { return static_cast<TIL_Opcode>(Opcode); }
+
+ // Subclasses of SExpr must define the following:
+ //
+ // This(const This& E, ...) {
+ // copy constructor: construct copy of E, with some additional arguments.
+ // }
+ //
+ // template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ // traverse all subexpressions, following the traversal/rewriter interface
+ // }
+ //
+ // template <class C> typename C::CType compare(CType* E, C& Cmp) {
+ // compare all subexpressions, following the comparator interface
+ // }
+
+protected:
+ SExpr(TIL_Opcode Op) : Opcode(Op), Reserved(0), Flags(0) {}
+ SExpr(const SExpr &E) : Opcode(E.Opcode), Reserved(0), Flags(E.Flags) {}
+
+ const unsigned char Opcode;
+ unsigned char Reserved;
+ unsigned short Flags;
+
+private:
+ SExpr();
+};
+
+typedef SExpr* SExprRef;
+
+
+// Contains various helper functions for SExprs.
+class ThreadSafetyTIL {
+public:
+ static const int MaxOpcode = COP_MAX;
+
+ static inline bool isTrivial(SExpr *E) {
+ unsigned Op = E->opcode();
+ return Op == COP_Variable || Op == COP_Literal;
+ }
+
+ static inline bool isLargeValue(SExpr *E) {
+ unsigned Op = E->opcode();
+ return (Op >= COP_Function && Op <= COP_Code);
+ }
+};
+
+
+// Placeholder for an expression that has not yet been created.
+// Used to implement lazy copy and rewriting strategies.
+class Future : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Future; }
+
+ enum FutureStatus {
+ FS_pending,
+ FS_evaluating,
+ FS_done
+ };
+
+ Future() : SExpr(COP_Future), Status(FS_pending), Result(0), Location(0) {}
+ virtual ~Future() {}
+
+ // Registers the location in the AST where this future is stored.
+ // Forcing the future will automatically update the AST.
+ static inline void registerLocation(SExpr **Member) {
+ if (Future *F = dyn_cast_or_null<Future>(*Member))
+ F->Location = Member;
+ }
+
+ // A lazy rewriting strategy should subclass Future and override this method.
+ virtual SExpr *create() { return 0; }
+
+ // Return the result of this future if it exists, otherwise return null.
+ SExpr *maybeGetResult() {
+ return Result;
+ }
+
+ // Return the result of this future; forcing it if necessary.
+ SExpr *result() {
+ switch (Status) {
+ case FS_pending:
+ force();
+ return Result;
+ case FS_evaluating:
+ return 0; // infinite loop; illegal recursion.
+ case FS_done:
+ return Result;
+ }
+ }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ assert(Result && "Cannot traverse Future that has not been forced.");
+ return Visitor.traverse(Result);
+ }
+
+ template <class C> typename C::CType compare(Future* E, C& Cmp) {
+ if (!Result || !E->Result)
+ return Cmp.comparePointers(this, E);
+ return Cmp.compare(Result, E->Result);
+ }
+
+private:
+ // Force the future.
+ void force() {
+ Status = FS_evaluating;
+ SExpr *R = create();
+ Result = R;
+ if (Location) {
+ *Location = R;
+ }
+ Status = FS_done;
+ }
+
+ FutureStatus Status;
+ SExpr *Result;
+ SExpr **Location;
+};
+
+
+// Placeholder for C++ expressions that cannot be represented in the TIL.
+class Undefined : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Undefined; }
+
+ Undefined(const clang::Stmt *S = 0) : SExpr(COP_Undefined), Cstmt(S) {}
+ Undefined(const Undefined &U) : SExpr(U), Cstmt(U.Cstmt) {}
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ return Visitor.reduceUndefined(*this);
+ }
+
+ template <class C> typename C::CType compare(Undefined* E, C& Cmp) {
+ return Cmp.comparePointers(Cstmt, E->Cstmt);
+ }
+
+private:
+ const clang::Stmt *Cstmt;
+};
+
+
+// Placeholder for a wildcard that matches any other expression.
+class Wildcard : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Wildcard; }
+
+ Wildcard() : SExpr(COP_Wildcard) {}
+ Wildcard(const Wildcard &W) : SExpr(W) {}
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ return Visitor.reduceWildcard(*this);
+ }
+
+ template <class C> typename C::CType compare(Wildcard* E, C& Cmp) {
+ return Cmp.trueResult();
+ }
+};
+
+
+// Base class for literal values.
+class Literal : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Literal; }
+
+ Literal(const clang::Expr *C) : SExpr(COP_Literal), Cexpr(C) {}
+ Literal(const Literal &L) : SExpr(L), Cexpr(L.Cexpr) {}
+
+ // The clang expression for this literal.
+ const clang::Expr *clangExpr() { return Cexpr; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ return Visitor.reduceLiteral(*this);
+ }
+
+ template <class C> typename C::CType compare(Literal* E, C& Cmp) {
+ // TODO -- use value, not pointer equality
+ return Cmp.comparePointers(Cexpr, E->Cexpr);
+ }
+
+private:
+ const clang::Expr *Cexpr;
+};
+
+
+// Literal pointer to an object allocated in memory.
+// At compile time, pointer literals are represented by symbolic names.
+class LiteralPtr : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_LiteralPtr; }
+
+ LiteralPtr(const clang::ValueDecl *D) : SExpr(COP_LiteralPtr), Cvdecl(D) {}
+ LiteralPtr(const LiteralPtr &R) : SExpr(R), Cvdecl(R.Cvdecl) {}
+
+ // The clang declaration for the value that this pointer points to.
+ const clang::ValueDecl *clangDecl() { return Cvdecl; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ return Visitor.reduceLiteralPtr(*this);
+ }
+
+ template <class C> typename C::CType compare(LiteralPtr* E, C& Cmp) {
+ return Cmp.comparePointers(Cvdecl, E->Cvdecl);
+ }
+
+private:
+ const clang::ValueDecl *Cvdecl;
+};
+
+
+// A named variable, e.g. "x".
+//
+// There are two distinct places in which a Variable can appear in the AST.
+// A variable declaration introduces a new variable, and can occur in 3 places:
+// Let-expressions: (Let (x = t) u)
+// Functions: (Function (x : t) u)
+// Self-applicable functions (SFunction (x) t)
+//
+// If a variable occurs in any other location, it is a reference to an existing
+// variable declaration -- e.g. 'x' in (x * y + z). To save space, we don't
+// allocate a separate AST node for variable references; a reference is just a
+// pointer to the original declaration.
+class Variable : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Variable; }
+
+ // Let-variable, function parameter, or self-variable
+ enum VariableKind {
+ VK_Let,
+ VK_Fun,
+ VK_SFun
+ };
+
+ Variable(VariableKind K, SExpr *D = 0, const clang::ValueDecl *Cvd = 0)
+ : SExpr(COP_Variable), Definition(D), Cvdecl(Cvd),
+ BlockID(0), Id(0), NumUses(0) {
+ Flags = K;
+ Future::registerLocation(&Definition);
+ }
+ Variable(const clang::ValueDecl *Cvd, SExpr *D = 0)
+ : SExpr(COP_Variable), Definition(D), Cvdecl(Cvd),
+ BlockID(0), Id(0), NumUses(0) {
+ Flags = VK_Let;
+ Future::registerLocation(&Definition);
+ }
+ Variable(const Variable &Vd, SExpr *D) // rewrite constructor
+ : SExpr(Vd), Definition(D), Cvdecl(Vd.Cvdecl),
+ BlockID(0), Id(0), NumUses(0) {
+ Flags = Vd.kind();
+ Future::registerLocation(&Definition);
+ }
+
+ VariableKind kind() const { return static_cast<VariableKind>(Flags); }
+
+ StringRef name() const { return Cvdecl ? Cvdecl->getName() : "_x"; }
+ const clang::ValueDecl *clangDecl() const { return Cvdecl; }
+
+ // Returns the definition (for let vars) or type (for parameter & self vars)
+ SExpr *definition() const { return Definition; }
+
+ void attachVar() const { ++NumUses; }
+ void detachVar() const { --NumUses; }
+
+ unsigned getID() { return Id; }
+ unsigned getBlockID() { return BlockID; }
+
+ void setID(unsigned Bid, unsigned I) {
+ BlockID = static_cast<unsigned short>(Bid);
+ Id = static_cast<unsigned short>(I);
+ }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ // This routine is only called for variable references.
+ return Visitor.reduceVariableRef(this);
+ }
+
+ template <class C> typename C::CType compare(Variable* E, C& Cmp) {
+ return Cmp.compareVariableRefs(this, E);
+ }
+
+private:
+ friend class Function;
+ friend class SFunction;
+ friend class BasicBlock;
+
+ // Function, SFunction, and BasicBlock will reset the kind.
+ void setKind(VariableKind K) { Flags = K; }
+
+ SExpr *Definition; // The TIL type or definition
+ const clang::ValueDecl *Cvdecl; // The clang declaration for this variable.
+
+ unsigned short BlockID;
+ unsigned short Id;
+ mutable int NumUses;
+};
+
+
+// A function -- a.k.a. lambda abstraction.
+// Functions with multiple arguments are created by currying,
+// e.g. (function (x: Int) (function (y: Int) (add x y)))
+class Function : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Function; }
+
+ Function(Variable *Vd, SExpr *Bd)
+ : SExpr(COP_Function), VarDecl(Vd), Body(Bd) {
+ Vd->setKind(Variable::VK_Fun);
+ }
+ Function(const Function &F, Variable *Vd, SExpr *Bd) // rewrite constructor
+ : SExpr(F), VarDecl(Vd), Body(Bd) {
+ Vd->setKind(Variable::VK_Fun);
+ }
+
+ Variable *variableDecl() const { return VarDecl; }
+ SExpr *body() const { return Body; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ // This is a variable declaration, so traverse the definition.
+ typename V::R_SExpr E0 = Visitor.traverse(VarDecl->Definition, TRV_Lazy);
+ // Tell the rewriter to enter the scope of the function.
+ Variable *Nvd = Visitor.enterScope(*VarDecl, E0);
+ typename V::R_SExpr E1 = Visitor.traverse(Body);
+ Visitor.exitScope(*VarDecl);
+ return Visitor.reduceFunction(*this, Nvd, E1);
+ }
+
+ template <class C> typename C::CType compare(Function* E, C& Cmp) {
+ typename C::CType Ct =
+ Cmp.compare(VarDecl->definition(), E->VarDecl->definition());
+ if (Cmp.notTrue(Ct))
+ return Ct;
+ Cmp.enterScope(VarDecl, E->VarDecl);
+ Ct = Cmp.compare(Body, E->Body);
+ Cmp.leaveScope();
+ return Ct;
+ }
+
+private:
+ Variable *VarDecl;
+ SExpr *Body;
+};
+
+
+// A self-applicable function.
+// A self-applicable function can be applied to itself. It's useful for
+// implementing objects and late binding
+class SFunction : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_SFunction; }
+
+ SFunction(Variable *Vd, SExpr *B)
+ : SExpr(COP_SFunction), VarDecl(Vd), Body(B) {
+ assert(Vd->Definition == 0);
+ Vd->setKind(Variable::VK_SFun);
+ Vd->Definition = this;
+ }
+ SFunction(const SFunction &F, Variable *Vd, SExpr *B) // rewrite constructor
+ : SExpr(F),
+ VarDecl(Vd),
+ Body(B) {
+ assert(Vd->Definition == 0);
+ Vd->setKind(Variable::VK_SFun);
+ Vd->Definition = this;
+ }
+
+ Variable *variableDecl() const { return VarDecl; }
+ SExpr *body() const { return Body; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ // A self-variable points to the SFunction itself.
+ // A rewrite must introduce the variable with a null definition, and update
+ // it after 'this' has been rewritten.
+ Variable *Nvd = Visitor.enterScope(*VarDecl, 0 /* def */);
+ typename V::R_SExpr E1 = Visitor.traverse(Body);
+ Visitor.exitScope(*VarDecl);
+ // A rewrite operation will call SFun constructor to set Vvd->Definition.
+ return Visitor.reduceSFunction(*this, Nvd, E1);
+ }
+
+ template <class C> typename C::CType compare(SFunction* E, C& Cmp) {
+ Cmp.enterScope(VarDecl, E->VarDecl);
+ typename C::CType Ct = Cmp.compare(Body, E->Body);
+ Cmp.leaveScope();
+ return Ct;
+ }
+
+private:
+ Variable *VarDecl;
+ SExpr *Body;
+};
+
+
+// A block of code -- e.g. the body of a function.
+class Code : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Code; }
+
+ Code(SExpr *T, SExpr *B) : SExpr(COP_Code), ReturnType(T), Body(B) {
+ Future::registerLocation(&ReturnType);
+ Future::registerLocation(&Body);
+ }
+ Code(const Code &C, SExpr *T, SExpr *B) // rewrite constructor
+ : SExpr(C),
+ ReturnType(T),
+ Body(B) {
+ Future::registerLocation(&ReturnType);
+ Future::registerLocation(&Body);
+ }
+
+ SExpr *returnType() { return ReturnType; }
+ SExpr *body() { return Body; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Nt = Visitor.traverse(ReturnType, TRV_Lazy);
+ typename V::R_SExpr Nb = Visitor.traverse(Body, TRV_Lazy);
+ return Visitor.reduceCode(*this, Nt, Nb);
+ }
+
+ template <class C> typename C::CType compare(Code* E, C& Cmp) {
+ typename C::CType Ct = Cmp.compare(ReturnType, E->ReturnType);
+ if (Cmp.notTrue(Ct))
+ return Ct;
+ return Cmp.compare(Body, E->Body);
+ }
+
+private:
+ SExpr *ReturnType;
+ SExpr *Body;
+};
+
+
+// Apply an argument to a function
+class Apply : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Apply; }
+
+ Apply(SExpr *F, SExpr *A) : SExpr(COP_Apply), Fun(F), Arg(A) {}
+ Apply(const Apply &A, SExpr *F, SExpr *Ar) // rewrite constructor
+ : SExpr(A), Fun(F), Arg(Ar)
+ {}
+
+ SExpr *fun() const { return Fun; }
+ SExpr *arg() const { return Arg; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Nf = Visitor.traverse(Fun);
+ typename V::R_SExpr Na = Visitor.traverse(Arg);
+ return Visitor.reduceApply(*this, Nf, Na);
+ }
+
+ template <class C> typename C::CType compare(Apply* E, C& Cmp) {
+ typename C::CType Ct = Cmp.compare(Fun, E->Fun);
+ if (Cmp.notTrue(Ct))
+ return Ct;
+ return Cmp.compare(Arg, E->Arg);
+ }
+
+private:
+ SExpr *Fun;
+ SExpr *Arg;
+};
+
+
+// Apply a self-argument to a self-applicable function
+class SApply : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_SApply; }
+
+ SApply(SExpr *Sf, SExpr *A = 0) : SExpr(COP_SApply), Sfun(Sf), Arg(A) {}
+ SApply(SApply &A, SExpr *Sf, SExpr *Ar = 0) // rewrite constructor
+ : SExpr(A), Sfun(Sf), Arg(Ar)
+ {}
+
+ SExpr *sfun() const { return Sfun; }
+ SExpr *arg() const { return Arg ? Arg : Sfun; }
+
+ bool isDelegation() const { return Arg == 0; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Nf = Visitor.traverse(Sfun);
+ typename V::R_SExpr Na = Arg ? Visitor.traverse(Arg) : 0;
+ return Visitor.reduceSApply(*this, Nf, Na);
+ }
+
+ template <class C> typename C::CType compare(SApply* E, C& Cmp) {
+ typename C::CType Ct = Cmp.compare(Sfun, E->Sfun);
+ if (Cmp.notTrue(Ct) || (!Arg && !E->Arg))
+ return Ct;
+ return Cmp.compare(arg(), E->arg());
+ }
+
+private:
+ SExpr *Sfun;
+ SExpr *Arg;
+};
+
+
+// Project a named slot from a C++ struct or class.
+class Project : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Project; }
+
+ Project(SExpr *R, clang::ValueDecl *Cvd)
+ : SExpr(COP_Project), Rec(R), Cvdecl(Cvd) {}
+ Project(const Project &P, SExpr *R) : SExpr(P), Rec(R), Cvdecl(P.Cvdecl) {}
+
+ SExpr *record() const { return Rec; }
+ clang::ValueDecl *clangValueDecl() const { return Cvdecl; }
+
+ StringRef slotName() const { return Cvdecl->getName(); }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Nr = Visitor.traverse(Rec);
+ return Visitor.reduceProject(*this, Nr);
+ }
+
+ template <class C> typename C::CType compare(Project* E, C& Cmp) {
+ typename C::CType Ct = Cmp.compare(Rec, E->Rec);
+ if (Cmp.notTrue(Ct))
+ return Ct;
+ return Cmp.comparePointers(Cvdecl, E->Cvdecl);
+ }
+
+private:
+ SExpr *Rec;
+ clang::ValueDecl *Cvdecl;
+};
+
+
+// Call a function (after all arguments have been applied).
+class Call : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Call; }
+
+ Call(SExpr *T, const clang::CallExpr *Ce = 0)
+ : SExpr(COP_Call), Target(T), Cexpr(Ce) {}
+ Call(const Call &C, SExpr *T) : SExpr(C), Target(T), Cexpr(C.Cexpr) {}
+
+ SExpr *target() const { return Target; }
+ const clang::CallExpr *clangCallExpr() { return Cexpr; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Nt = Visitor.traverse(Target);
+ return Visitor.reduceCall(*this, Nt);
+ }
+
+ template <class C> typename C::CType compare(Call* E, C& Cmp) {
+ return Cmp.compare(Target, E->Target);
+ }
+
+private:
+ SExpr *Target;
+ const clang::CallExpr *Cexpr;
+};
+
+
+// Allocate memory for a new value on the heap or stack.
+class Alloc : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Call; }
+
+ enum AllocKind {
+ AK_Stack,
+ AK_Heap
+ };
+
+ Alloc(SExpr* D, AllocKind K) : SExpr(COP_Alloc), Dtype(D) {
+ Flags = K;
+ }
+ Alloc(const Alloc &A, SExpr* Dt) : SExpr(A), Dtype(Dt) {
+ Flags = A.kind();
+ }
+
+ AllocKind kind() const { return static_cast<AllocKind>(Flags); }
+ SExpr* dataType() const { return Dtype; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Nd = Visitor.traverse(Dtype);
+ return Visitor.reduceAlloc(*this, Nd);
+ }
+
+ template <class C> typename C::CType compare(Alloc* E, C& Cmp) {
+ typename C::CType Ct = Cmp.compareIntegers(kind(), E->kind());
+ if (Cmp.notTrue(Ct))
+ return Ct;
+ return Cmp.compare(Dtype, E->Dtype);
+ }
+
+private:
+ SExpr* Dtype;
+};
+
+
+// Load a value from memory.
+class Load : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Load; }
+
+ Load(SExpr *P) : SExpr(COP_Load), Ptr(P) {}
+ Load(const Load &L, SExpr *P) : SExpr(L), Ptr(P) {}
+
+ SExpr *pointer() { return Ptr; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Np = Visitor.traverse(Ptr);
+ return Visitor.reduceLoad(*this, Np);
+ }
+
+ template <class C> typename C::CType compare(Load* E, C& Cmp) {
+ return Cmp.compare(Ptr, E->Ptr);
+ }
+
+private:
+ SExpr *Ptr;
+};
+
+
+// Store a value to memory.
+class Store : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Store; }
+
+ Store(SExpr *P, SExpr *V) : SExpr(COP_Store), Ptr(P) {}
+ Store(const Store &S, SExpr *P, SExpr *V) : SExpr(S), Ptr(P) {}
+
+ SExpr *pointer() const { return Ptr; }
+ SExpr *value() const { return Value; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Np = Visitor.traverse(Ptr);
+ typename V::R_SExpr Nv = Visitor.traverse(Value);
+ return Visitor.reduceStore(*this, Np, Nv);
+ }
+
+ template <class C> typename C::CType compare(Store* E, C& Cmp) {
+ typename C::CType Ct = Cmp.compare(Ptr, E->Ptr);
+ if (Cmp.notTrue(Ct))
+ return Ct;
+ return Cmp.compare(Value, E->Value);
+ }
+
+ SExpr *Ptr;
+ SExpr *Value;
+};
+
+
+// Simple unary operation -- e.g. !, ~, etc.
+class UnaryOp : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_UnaryOp; }
+
+ UnaryOp(TIL_UnaryOpcode Op, SExpr *E) : SExpr(COP_UnaryOp), Expr0(E) {
+ Flags = Op;
+ }
+ UnaryOp(const UnaryOp &U, SExpr *E) : SExpr(U) { Flags = U.Flags; }
+
+ TIL_UnaryOpcode unaryOpcode() { return static_cast<TIL_UnaryOpcode>(Flags); }
+
+ SExpr *expr() const { return Expr0; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Ne = Visitor.traverse(Expr0);
+ return Visitor.reduceUnaryOp(*this, Ne);
+ }
+
+ template <class C> typename C::CType compare(UnaryOp* E, C& Cmp) {
+ typename C::CType Ct =
+ Cmp.compareIntegers(unaryOpcode(), E->unaryOpcode());
+ if (Cmp.notTrue(Ct))
+ return Ct;
+ return Cmp.compare(Expr0, E->Expr0);
+ }
+
+private:
+ SExpr *Expr0;
+};
+
+
+// Simple binary operation -- e.g. +, -, etc.
+class BinaryOp : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_BinaryOp; }
+
+ BinaryOp(TIL_BinaryOpcode Op, SExpr *E0, SExpr *E1)
+ : SExpr(COP_BinaryOp), Expr0(E0), Expr1(E1) {
+ Flags = Op;
+ }
+ BinaryOp(const BinaryOp &B, SExpr *E0, SExpr *E1)
+ : SExpr(B), Expr0(E0), Expr1(E1) {
+ Flags = B.Flags;
+ }
+
+ TIL_BinaryOpcode binaryOpcode() {
+ return static_cast<TIL_BinaryOpcode>(Flags);
+ }
+
+ SExpr *expr0() const { return Expr0; }
+ SExpr *expr1() const { return Expr1; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Ne0 = Visitor.traverse(Expr0);
+ typename V::R_SExpr Ne1 = Visitor.traverse(Expr1);
+ return Visitor.reduceBinaryOp(*this, Ne0, Ne1);
+ }
+
+ template <class C> typename C::CType compare(BinaryOp* E, C& Cmp) {
+ typename C::CType Ct =
+ Cmp.compareIntegers(binaryOpcode(), E->binaryOpcode());
+ if (Cmp.notTrue(Ct))
+ return Ct;
+ Ct = Cmp.compare(Expr0, E->Expr0);
+ if (Cmp.notTrue(Ct))
+ return Ct;
+ return Cmp.compare(Expr1, E->Expr1);
+ }
+
+private:
+ SExpr *Expr0;
+ SExpr *Expr1;
+};
+
+
+// Cast expression
+class Cast : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Cast; }
+
+ Cast(TIL_CastOpcode Op, SExpr *E) : SExpr(COP_Cast), Expr0(E) { Flags = Op; }
+ Cast(const Cast &C, SExpr *E) : SExpr(C), Expr0(E) { Flags = C.Flags; }
+
+ TIL_BinaryOpcode castOpcode() { return static_cast<TIL_BinaryOpcode>(Flags); }
+
+ SExpr *expr() const { return Expr0; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Ne = Visitor.traverse(Expr0);
+ return Visitor.reduceCast(*this, Ne);
+ }
+
+ template <class C> typename C::CType compare(Cast* E, C& Cmp) {
+ typename C::CType Ct =
+ Cmp.compareIntegers(castOpcode(), E->castOpcode());
+ if (Cmp.notTrue(Ct))
+ return Ct;
+ return Cmp.compare(Expr0, E->Expr0);
+ }
+
+private:
+ SExpr *Expr0;
+};
+
+
+
+
+class BasicBlock;
+
+
+// An SCFG is a control-flow graph. It consists of a set of basic blocks, each
+// of which terminates in a branch to another basic block. There is one
+// entry point, and one exit point.
+class SCFG : public SExpr {
+public:
+ typedef SimpleArray<BasicBlock*> BlockArray;
+
+ static bool classof(const SExpr *E) { return E->opcode() == COP_SCFG; }
+
+ SCFG(MemRegionRef A, unsigned Nblocks)
+ : SExpr(COP_SCFG), Blocks(A, Nblocks), Entry(0), Exit(0) {}
+ SCFG(const SCFG &Cfg, BlockArray &Ba) // steals memory from ba
+ : SExpr(COP_SCFG),
+ Blocks(Ba, true),
+ Entry(0),
+ Exit(0) { /* TODO: set entry and exit! */
+ }
+
+ typedef BlockArray::iterator iterator;
+ typedef BlockArray::const_iterator const_iterator;
+
+ iterator begin() { return Blocks.begin(); }
+ iterator end() { return Blocks.end(); }
+
+ const_iterator cbegin() const { return Blocks.cbegin(); }
+ const_iterator cend() const { return Blocks.cend(); }
+
+ BasicBlock *entry() const { return Entry; }
+ BasicBlock *exit() const { return Exit; }
+
+ void add(BasicBlock *BB) { Blocks.push_back(BB); }
+ void setEntry(BasicBlock *BB) { Entry = BB; }
+ void setExit(BasicBlock *BB) { Exit = BB; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor);
+
+ template <class C> typename C::CType compare(SCFG* E, C& Cmp) {
+ // TODO -- implement CFG comparisons
+ return Cmp.comparePointers(this, E);
+ }
+
+private:
+ BlockArray Blocks;
+ BasicBlock *Entry;
+ BasicBlock *Exit;
+};
+
+
+// A basic block is part of an SCFG, and can be treated as a function in
+// continuation passing style. It consists of a sequence of phi nodes, which
+// are "arguments" to the function, followed by a sequence of instructions.
+// Both arguments and instructions define new variables. It ends with a
+// branch or goto to another basic block in the same SCFG.
+class BasicBlock {
+public:
+ typedef SimpleArray<Variable*> VarArray;
+
+ BasicBlock(MemRegionRef A, unsigned Nargs, unsigned Nins, SExpr *Term = 0)
+ : BlockID(0), Parent(0), Args(A, Nargs), Instrs(A, Nins),
+ Terminator(Term) {}
+ BasicBlock(const BasicBlock &B, VarArray &As, VarArray &Is, SExpr *T)
+ : BlockID(0), Parent(0), Args(As, true), Instrs(Is, true), Terminator(T)
+ {}
+
+ unsigned blockID() const { return BlockID; }
+ BasicBlock *parent() const { return Parent; }
+
+ const VarArray &arguments() const { return Args; }
+ VarArray &arguments() { return Args; }
+
+ const VarArray &instructions() const { return Instrs; }
+ VarArray &instructions() { return Instrs; }
+
+ const SExpr *terminator() const { return Terminator; }
+ SExpr *terminator() { return Terminator; }
+
+ void setParent(BasicBlock *P) { Parent = P; }
+ void setBlockID(unsigned i) { BlockID = i; }
+ void setTerminator(SExpr *E) { Terminator = E; }
+ void addArgument(Variable *V) { Args.push_back(V); }
+ void addInstr(Variable *V) { Args.push_back(V); }
+
+ template <class V> BasicBlock *traverse(V &Visitor) {
+ typename V::template Container<Variable*> Nas(Visitor, Args.size());
+ typename V::template Container<Variable*> Nis(Visitor, Instrs.size());
+
+ for (unsigned I = 0; I < Args.size(); ++I) {
+ typename V::R_SExpr Ne = Visitor.traverse(Args[I]->Definition);
+ Variable *Nvd = Visitor.enterScope(*Args[I], Ne);
+ Nas.push_back(Nvd);
+ }
+ for (unsigned J = 0; J < Instrs.size(); ++J) {
+ typename V::R_SExpr Ne = Visitor.traverse(Instrs[J]->Definition);
+ Variable *Nvd = Visitor.enterScope(*Instrs[J], Ne);
+ Nis.push_back(Nvd);
+ }
+ typename V::R_SExpr Nt = Visitor.traverse(Terminator);
+
+ for (unsigned J = 0, JN = Instrs.size(); J < JN; ++J)
+ Visitor.exitScope(*Instrs[JN-J]);
+ for (unsigned I = 0, IN = Instrs.size(); I < IN; ++I)
+ Visitor.exitScope(*Args[IN-I]);
+
+ return Visitor.reduceBasicBlock(*this, Nas, Nis, Nt);
+ }
+
+ template <class C> typename C::CType compare(BasicBlock* E, C& Cmp) {
+ // TODO -- implement CFG comparisons
+ return Cmp.comparePointers(this, E);
+ }
+
+private:
+ friend class SCFG;
+
+ unsigned BlockID;
+ BasicBlock *Parent; // The parent block is the enclosing lexical scope.
+ // The parent dominates this block.
+ VarArray Args; // Phi nodes
+ VarArray Instrs;
+ SExpr *Terminator;
+};
+
+
+template <class V>
+typename V::R_SExpr SCFG::traverse(V &Visitor) {
+ Visitor.enterCFG(*this);
+ typename V::template Container<BasicBlock *> Bbs(Visitor, Blocks.size());
+ for (unsigned I = 0; I < Blocks.size(); ++I) {
+ BasicBlock *Nbb = Blocks[I]->traverse(Visitor);
+ Bbs.push_back(Nbb);
+ }
+ Visitor.exitCFG(*this);
+ return Visitor.reduceSCFG(*this, Bbs);
+}
+
+
+
+class Phi : public SExpr {
+public:
+ typedef SimpleArray<SExpr *> ValArray;
+
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Phi; }
+
+ Phi(MemRegionRef A, unsigned Nvals) : SExpr(COP_Phi), Values(A, Nvals) {}
+ Phi(const Phi &P, ValArray &Vs) // steals memory of vs
+ : SExpr(COP_Phi), Values(Vs, true) {}
+
+ const ValArray &values() const { return Values; }
+ ValArray &values() { return Values; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::template Container<typename V::R_SExpr> Nvs(Visitor,
+ Values.size());
+ for (ValArray::iterator I = Values.begin(), E = Values.end(); I != E; ++I) {
+ typename V::R_SExpr Nv = Visitor.traverse(*I);
+ Nvs.push_back(Nv);
+ }
+ return Visitor.reducePhi(*this, Nvs);
+ }
+
+ template <class C> typename C::CType compare(Phi* E, C& Cmp) {
+ // TODO -- implement CFG comparisons
+ return Cmp.comparePointers(this, E);
+ }
+
+private:
+ ValArray Values;
+};
+
+
+class Goto : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Goto; }
+
+ Goto(BasicBlock *B, unsigned Index)
+ : SExpr(COP_Goto), TargetBlock(B) {}
+ Goto(const Goto &G, BasicBlock *B, unsigned Index)
+ : SExpr(COP_Goto), TargetBlock(B) {}
+
+ BasicBlock *targetBlock() const { return TargetBlock; }
+ unsigned index() const { return Index; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ // TODO -- rewrite indices properly
+ BasicBlock *Ntb = Visitor.reduceBasicBlockRef(TargetBlock);
+ return Visitor.reduceGoto(*this, Ntb, Index);
+ }
+
+ template <class C> typename C::CType compare(Goto* E, C& Cmp) {
+ // TODO -- implement CFG comparisons
+ return Cmp.comparePointers(this, E);
+ }
+
+private:
+ BasicBlock *TargetBlock;
+ unsigned Index; // Index into Phi nodes of target block.
+};
+
+
+class Branch : public SExpr {
+public:
+ static bool classof(const SExpr *E) { return E->opcode() == COP_Branch; }
+
+ Branch(SExpr *C, BasicBlock *T, BasicBlock *E)
+ : SExpr(COP_Branch), Condition(C), ThenBlock(T), ElseBlock(E) {}
+ Branch(const Branch &Br, SExpr *C, BasicBlock *T, BasicBlock *E)
+ : SExpr(COP_Branch), Condition(C), ThenBlock(T), ElseBlock(E) {}
+
+ SExpr *condition() { return Condition; }
+ BasicBlock *thenBlock() { return ThenBlock; }
+ BasicBlock *elseBlock() { return ElseBlock; }
+
+ template <class V> typename V::R_SExpr traverse(V &Visitor) {
+ typename V::R_SExpr Nc = Visitor.traverse(Condition);
+ BasicBlock *Ntb = Visitor.reduceBasicBlockRef(ThenBlock);
+ BasicBlock *Nte = Visitor.reduceBasicBlockRef(ElseBlock);
+ return Visitor.reduceBranch(*this, Nc, Ntb, Nte);
+ }
+
+ template <class C> typename C::CType compare(Branch* E, C& Cmp) {
+ // TODO -- implement CFG comparisons
+ return Cmp.comparePointers(this, E);
+ }
+
+private:
+ SExpr *Condition;
+ BasicBlock *ThenBlock;
+ BasicBlock *ElseBlock;
+};
+
+
+
+// Defines an interface used to traverse SExprs. Traversals have been made as
+// generic as possible, and are intended to handle any kind of pass over the
+// AST, e.g. visiters, copying, non-destructive rewriting, destructive
+// (in-place) rewriting, hashing, typing, etc.
+//
+// Traversals implement the functional notion of a "fold" operation on SExprs.
+// Each SExpr class provides a traverse method, which does the following:
+// * e->traverse(v):
+// // compute a result r_i for each subexpression e_i
+// for (i = 1..n) r_i = v.traverse(e_i);
+// // combine results into a result for e, where X is the class of e
+// return v.reduceX(*e, r_1, .. r_n).
+//
+// A visitor can control the traversal by overriding the following methods:
+// * v.traverse(e):
+// return v.traverseByCase(e), which returns v.traverseX(e)
+// * v.traverseX(e): (X is the class of e)
+// return e->traverse(v).
+// * v.reduceX(*e, r_1, .. r_n):
+// compute a result for a node of type X
+//
+// The reduceX methods control the kind of traversal (visitor, copy, etc.).
+// These are separated into a separate class R for the purpose of code reuse.
+// The full reducer interface also has methods to handle scopes
+template <class Self, class R> class TILTraversal : public R {
+public:
+ Self *self() { return reinterpret_cast<Self *>(this); }
+
+ // Traverse an expression -- returning a result of type R_SExpr.
+ // Override this method to do something for every expression, regardless
+ // of which kind it is. TIL_TraversalKind indicates the context in which
+ // the expression occurs, and can be:
+ // TRV_Normal
+ // TRV_Lazy -- e may need to be traversed lazily, using a Future.
+ // TRV_Tail -- e occurs in a tail position
+ typename R::R_SExpr traverse(SExpr *E, TIL_TraversalKind K = TRV_Normal) {
+ return traverseByCase(E);
+ }
+
+ // Helper method to call traverseX(e) on the appropriate type.
+ typename R::R_SExpr traverseByCase(SExpr *E) {
+ switch (E->opcode()) {
+#define TIL_OPCODE_DEF(X) \
+ case COP_##X: \
+ return self()->traverse##X(cast<X>(E));
+#include "clang/Analysis/Analyses/ThreadSafetyOps.def"
+#undef TIL_OPCODE_DEF
+ case COP_MAX:
+ return self()->reduceNull();
+ }
+ }
+
+// Traverse e, by static dispatch on the type "X" of e.
+// Override these methods to do something for a particular kind of term.
+#define TIL_OPCODE_DEF(X) \
+ typename R::R_SExpr traverse##X(X *e) { return e->traverse(*self()); }
+#include "clang/Analysis/Analyses/ThreadSafetyOps.def"
+#undef TIL_OPCODE_DEF
+};
+
+
+// Implements a Reducer that makes a deep copy of an SExpr.
+// The default behavior of reduce##X(...) is to create a copy of the original.
+// Subclasses can override reduce##X to implement non-destructive rewriting
+// passes.
+class TILCopyReducer {
+public:
+ TILCopyReducer() {}
+
+ void setArena(MemRegionRef A) { Arena = A; }
+
+ // R_SExpr is the result type for a traversal.
+ // A copy or non-destructive rewrite returns a newly allocated term.
+ typedef SExpr *R_SExpr;
+
+ // Container is a minimal interface used to store results when traversing
+ // SExprs of variable arity, such as Phi, Goto, and SCFG.
+ template <class T> class Container {
+ public:
+ // Allocate a new container with a capacity for n elements.
+ Container(TILCopyReducer &R, unsigned N) : Elems(R.Arena, N) {}
+
+ // Push a new element onto the container.
+ void push_back(T E) { Elems.push_back(E); }
+
+ private:
+ friend class TILCopyReducer;
+ SimpleArray<T> Elems;
+ };
+
+public:
+ R_SExpr reduceNull() {
+ return 0;
+ }
+ // R_SExpr reduceFuture(...) is never used.
+
+ R_SExpr reduceUndefined(Undefined &Orig) {
+ return new (Arena) Undefined(Orig);
+ }
+ R_SExpr reduceWildcard(Wildcard &Orig) {
+ return new (Arena) Wildcard(Orig);
+ }
+
+ R_SExpr reduceLiteral(Literal &Orig) {
+ return new (Arena) Literal(Orig);
+ }
+ R_SExpr reduceLiteralPtr(LiteralPtr &Orig) {
+ return new (Arena) LiteralPtr(Orig);
+ }
+
+ R_SExpr reduceFunction(Function &Orig, Variable *Nvd, R_SExpr E0) {
+ return new (Arena) Function(Orig, Nvd, E0);
+ }
+ R_SExpr reduceSFunction(SFunction &Orig, Variable *Nvd, R_SExpr E0) {
+ return new (Arena) SFunction(Orig, Nvd, E0);
+ }
+ R_SExpr reduceCode(Code &Orig, R_SExpr E0, R_SExpr E1) {
+ return new (Arena) Code(Orig, E0, E1);
+ }
+
+ R_SExpr reduceApply(Apply &Orig, R_SExpr E0, R_SExpr E1) {
+ return new (Arena) Apply(Orig, E0, E1);
+ }
+ R_SExpr reduceSApply(SApply &Orig, R_SExpr E0, R_SExpr E1) {
+ return new (Arena) SApply(Orig, E0, E1);
+ }
+ R_SExpr reduceProject(Project &Orig, R_SExpr E0) {
+ return new (Arena) Project(Orig, E0);
+ }
+ R_SExpr reduceCall(Call &Orig, R_SExpr E0) {
+ return new (Arena) Call(Orig, E0);
+ }
+
+ R_SExpr reduceAlloc(Alloc &Orig, R_SExpr E0) {
+ return new (Arena) Alloc(Orig, E0);
+ }
+ R_SExpr reduceLoad(Load &Orig, R_SExpr E0) {
+ return new (Arena) Load(Orig, E0);
+ }
+ R_SExpr reduceStore(Store &Orig, R_SExpr E0, R_SExpr E1) {
+ return new (Arena) Store(Orig, E0, E1);
+ }
+ R_SExpr reduceUnaryOp(UnaryOp &Orig, R_SExpr E0) {
+ return new (Arena) UnaryOp(Orig);
+ }
+ R_SExpr reduceBinaryOp(BinaryOp &Orig, R_SExpr E0, R_SExpr E1) {
+ return new (Arena) BinaryOp(Orig, E0, E1);
+ }
+ R_SExpr reduceCast(Cast &Orig, R_SExpr E0) {
+ return new (Arena) Cast(Orig, E0);
+ }
+
+ R_SExpr reduceSCFG(SCFG &Orig, Container<BasicBlock *> Bbs) {
+ return new (Arena) SCFG(Orig, Bbs.Elems);
+ }
+ R_SExpr reducePhi(Phi &Orig, Container<R_SExpr> As) {
+ return new (Arena) Phi(Orig, As.Elems);
+ }
+ R_SExpr reduceGoto(Goto &Orig, BasicBlock *B, unsigned Index) {
+ return new (Arena) Goto(Orig, B, Index);
+ }
+ R_SExpr reduceBranch(Branch &O, R_SExpr C, BasicBlock *B0, BasicBlock *B1) {
+ return new (Arena) Branch(O, C, B0, B1);
+ }
+
+ BasicBlock *reduceBasicBlock(BasicBlock &Orig, Container<Variable *> &As,
+ Container<Variable *> &Is, R_SExpr T) {
+ return new (Arena) BasicBlock(Orig, As.Elems, Is.Elems, T);
+ }
+
+ // Create a new variable from orig, and push it onto the lexical scope.
+ Variable *enterScope(Variable &Orig, R_SExpr E0) {
+ return new (Arena) Variable(Orig, E0);
+ }
+ // Exit the lexical scope of orig.
+ void exitScope(const Variable &Orig) {}
+
+ void enterCFG(SCFG &Cfg) {}
+ void exitCFG(SCFG &Cfg) {}
+
+ // Map Variable references to their rewritten definitions.
+ Variable *reduceVariableRef(Variable *Ovd) { return Ovd; }
+
+ // Map BasicBlock references to their rewritten defs.
+ BasicBlock *reduceBasicBlockRef(BasicBlock *Obb) { return Obb; }
+
+private:
+ MemRegionRef Arena;
+};
+
+
+class SExprCopier : public TILTraversal<SExprCopier, TILCopyReducer> {
+public:
+ SExprCopier(MemRegionRef A) { setArena(A); }
+
+ // Create a copy of e in region a.
+ static SExpr *copy(SExpr *E, MemRegionRef A) {
+ SExprCopier Copier(A);
+ return Copier.traverse(E);
+ }
+};
+
+
+// Implements a Reducer that visits each subexpression, and returns either
+// true or false.
+class TILVisitReducer {
+public:
+ TILVisitReducer() {}
+
+ // A visitor returns a bool, representing success or failure.
+ typedef bool R_SExpr;
+
+ // A visitor "container" is a single bool, which accumulates success.
+ template <class T> class Container {
+ public:
+ Container(TILVisitReducer &R, unsigned N) : Success(true) {}
+ void push_back(bool E) { Success = Success && E; }
+
+ private:
+ friend class TILVisitReducer;
+ bool Success;
+ };
+
+public:
+ R_SExpr reduceNull() { return true; }
+ R_SExpr reduceUndefined(Undefined &Orig) { return true; }
+ R_SExpr reduceWildcard(Wildcard &Orig) { return true; }
+
+ R_SExpr reduceLiteral(Literal &Orig) { return true; }
+ R_SExpr reduceLiteralPtr(Literal &Orig) { return true; }
+
+ R_SExpr reduceFunction(Function &Orig, Variable *Nvd, R_SExpr E0) {
+ return Nvd && E0;
+ }
+ R_SExpr reduceSFunction(SFunction &Orig, Variable *Nvd, R_SExpr E0) {
+ return Nvd && E0;
+ }
+ R_SExpr reduceCode(Code &Orig, R_SExpr E0, R_SExpr E1) {
+ return E0 && E1;
+ }
+ R_SExpr reduceApply(Apply &Orig, R_SExpr E0, R_SExpr E1) {
+ return E0 && E1;
+ }
+ R_SExpr reduceSApply(SApply &Orig, R_SExpr E0, R_SExpr E1) {
+ return E0 && E1;
+ }
+ R_SExpr reduceProject(Project &Orig, R_SExpr E0) { return E0; }
+ R_SExpr reduceCall(Call &Orig, R_SExpr E0) { return E0; }
+ R_SExpr reduceAlloc(Alloc &Orig, R_SExpr E0) { return E0; }
+ R_SExpr reduceLoad(Load &Orig, R_SExpr E0) { return E0; }
+ R_SExpr reduceStore(Store &Orig, R_SExpr E0, R_SExpr E1) { return E0 && E1; }
+ R_SExpr reduceUnaryOp(UnaryOp &Orig, R_SExpr E0) { return E0; }
+ R_SExpr reduceBinaryOp(BinaryOp &Orig, R_SExpr E0, R_SExpr E1) {
+ return E0 && E1;
+ }
+ R_SExpr reduceCast(Cast &Orig, R_SExpr E0) { return E0; }
+
+ R_SExpr reduceSCFG(SCFG &Orig, Container<BasicBlock *> Bbs) {
+ return Bbs.Success;
+ }
+ R_SExpr reducePhi(Phi &Orig, Container<R_SExpr> As) {
+ return As.Success;
+ }
+ R_SExpr reduceGoto(Goto &Orig, BasicBlock *B, Container<R_SExpr> As) {
+ return As.Success;
+ }
+ R_SExpr reduceBranch(Branch &O, R_SExpr C, BasicBlock *B0, BasicBlock *B1) {
+ return C;
+ }
+
+ BasicBlock *reduceBasicBlock(BasicBlock &Orig, Container<Variable *> &As,
+ Container<Variable *> &Is, R_SExpr T) {
+ return (As.Success && Is.Success && T) ? &Orig : 0;
+ }
+
+ Variable *enterScope(Variable &Orig, R_SExpr E0) { return E0 ? &Orig : 0; }
+ void exitScope(const Variable &Orig) {}
+
+ void enterCFG(SCFG &Cfg) {}
+ void exitCFG(SCFG &Cfg) {}
+
+ Variable *reduceVariableRef(Variable *Ovd) { return Ovd; }
+
+ BasicBlock *reduceBasicBlockRef(BasicBlock *Obb) { return Obb; }
+};
+
+
+// A visitor will visit each node, and halt if any reducer returns false.
+template <class Self>
+class SExprVisitor : public TILTraversal<Self, TILVisitReducer> {
+public:
+ SExprVisitor() : Success(true) {}
+
+ bool traverse(SExpr *E, TIL_TraversalKind K = TRV_Normal) {
+ Success = Success && this->traverseByCase(E);
+ return Success;
+ }
+
+ static bool visit(SExpr *E) {
+ SExprVisitor Visitor;
+ return Visitor.traverse(E);
+ }
+
+private:
+ bool Success;
+};
+
+
+// Basic class for comparison operations over expressions.
+template <typename Self>
+class TILComparator {
+public:
+ Self *self() { return reinterpret_cast<Self *>(this); }
+
+ bool compareByCase(SExpr *E1, SExpr* E2) {
+ switch (E1->opcode()) {
+#define TIL_OPCODE_DEF(X) \
+ case COP_##X: \
+ return cast<X>(E1)->compare(cast<X>(E2), *self());
+#include "clang/Analysis/Analyses/ThreadSafetyOps.def"
+#undef TIL_OPCODE_DEF
+ case COP_MAX:
+ return false;
+ }
+ }
+};
+
+
+class TILEqualsComparator : public TILComparator<TILEqualsComparator> {
+public:
+ // Result type for the comparison, e.g. bool for simple equality,
+ // or int for lexigraphic comparison (-1, 0, 1). Must have one value which
+ // denotes "true".
+ typedef bool CType;
+
+ CType trueResult() { return true; }
+ bool notTrue(CType ct) { return !ct; }
+
+ bool compareIntegers(unsigned i, unsigned j) { return i == j; }
+ bool comparePointers(const void* P, const void* Q) { return P == Q; }
+
+ bool compare(SExpr *E1, SExpr* E2) {
+ if (E1->opcode() != E2->opcode())
+ return false;
+ return compareByCase(E1, E2);
+ }
+
+ // TODO -- handle alpha-renaming of variables
+ void enterScope(Variable* V1, Variable* V2) { }
+ void leaveScope() { }
+
+ bool compareVariableRefs(Variable* V1, Variable* V2) {
+ return V1 == V2;
+ }
+
+ static bool compareExprs(SExpr *E1, SExpr* E2) {
+ TILEqualsComparator Eq;
+ return Eq.compareExprs(E1, E2);
+ }
+};
+
+
+// Pretty printer for TIL expressions
+template <typename Self, typename StreamType>
+class TILPrettyPrinter {
+public:
+ static void print(SExpr *E, StreamType &SS) {
+ Self printer;
+ printer.printSExpr(E, SS, Prec_MAX);
+ }
+
+protected:
+ Self *self() { return reinterpret_cast<Self *>(this); }
+
+ void newline(StreamType &SS) {
+ SS << "\n";
+ }
+
+ // TODO: further distinguish between binary operations.
+ static const unsigned Prec_Atom = 0;
+ static const unsigned Prec_Postfix = 1;
+ static const unsigned Prec_Unary = 2;
+ static const unsigned Prec_Binary = 3;
+ static const unsigned Prec_Other = 4;
+ static const unsigned Prec_Decl = 5;
+ static const unsigned Prec_MAX = 6;
+
+ // Return the precedence of a given node, for use in pretty printing.
+ unsigned precedence(SExpr *E) {
+ switch (E->opcode()) {
+ case COP_Future: return Prec_Atom;
+ case COP_Undefined: return Prec_Atom;
+ case COP_Wildcard: return Prec_Atom;
+
+ case COP_Literal: return Prec_Atom;
+ case COP_LiteralPtr: return Prec_Atom;
+ case COP_Variable: return Prec_Atom;
+ case COP_Function: return Prec_Decl;
+ case COP_SFunction: return Prec_Decl;
+ case COP_Code: return Prec_Decl;
+
+ case COP_Apply: return Prec_Postfix;
+ case COP_SApply: return Prec_Postfix;
+ case COP_Project: return Prec_Postfix;
+
+ case COP_Call: return Prec_Postfix;
+ case COP_Alloc: return Prec_Other;
+ case COP_Load: return Prec_Postfix;
+ case COP_Store: return Prec_Other;
+
+ case COP_UnaryOp: return Prec_Unary;
+ case COP_BinaryOp: return Prec_Binary;
+ case COP_Cast: return Prec_Unary;
+
+ case COP_SCFG: return Prec_Decl;
+ case COP_Phi: return Prec_Atom;
+ case COP_Goto: return Prec_Atom;
+ case COP_Branch: return Prec_Atom;
+ case COP_MAX: return Prec_MAX;
+ }
+ return Prec_MAX;
+ }
+
+ void printSExpr(SExpr *E, StreamType &SS, unsigned P) {
+ if (!E) {
+ self()->printNull(SS);
+ return;
+ }
+ if (self()->precedence(E) > P) {
+ // Wrap expr in () if necessary.
+ SS << "(";
+ self()->printSExpr(E, SS, Prec_MAX);
+ SS << ")";
+ return;
+ }
+
+ switch (E->opcode()) {
+#define TIL_OPCODE_DEF(X) \
+ case COP_##X: \
+ self()->print##X(cast<X>(E), SS); \
+ return;
+#include "clang/Analysis/Analyses/ThreadSafetyOps.def"
+#undef TIL_OPCODE_DEF
+ case COP_MAX:
+ return;
+ }
+ }
+
+ void printNull(StreamType &SS) {
+ SS << "#null";
+ }
+
+ void printFuture(Future *E, StreamType &SS) {
+ self()->printSExpr(E->maybeGetResult(), SS, Prec_Atom);
+ }
+
+ void printUndefined(Undefined *E, StreamType &SS) {
+ SS << "#undefined";
+ }
+
+ void printWildcard(Wildcard *E, StreamType &SS) {
+ SS << "_";
+ }
+
+ void printLiteral(Literal *E, StreamType &SS) {
+ // TODO: actually pretty print the literal.
+ SS << "#lit";
+ }
+
+ void printLiteralPtr(LiteralPtr *E, StreamType &SS) {
+ SS << E->clangDecl()->getName();
+ }
+
+ void printVariable(Variable *E, StreamType &SS) {
+ SS << E->name() << E->getBlockID() << "_" << E->getID();
+ }
+
+ void printFunction(Function *E, StreamType &SS, unsigned sugared = 0) {
+ switch (sugared) {
+ default:
+ SS << "\\("; // Lambda
+ case 1:
+ SS << "("; // Slot declarations
+ break;
+ case 2:
+ SS << ", "; // Curried functions
+ break;
+ }
+ self()->printVariable(E->variableDecl(), SS);
+ SS << ": ";
+ self()->printSExpr(E->variableDecl()->definition(), SS, Prec_MAX);
+
+ SExpr *B = E->body();
+ if (B && B->opcode() == COP_Function)
+ self()->printFunction(cast<Function>(B), SS, 2);
+ else
+ self()->printSExpr(B, SS, Prec_Decl);
+ }
+
+ void printSFunction(SFunction *E, StreamType &SS) {
+ SS << "@";
+ self()->printVariable(E->variableDecl(), SS);
+ SS << " ";
+ self()->printSExpr(E->body(), SS, Prec_Decl);
+ }
+
+ void printCode(Code *E, StreamType &SS) {
+ SS << ": ";
+ self()->printSExpr(E->returnType(), SS, Prec_Decl-1);
+ SS << " = ";
+ self()->printSExpr(E->body(), SS, Prec_Decl);
+ }
+
+ void printApply(Apply *E, StreamType &SS, bool sugared = false) {
+ SExpr *F = E->fun();
+ if (F->opcode() == COP_Apply) {
+ printApply(cast<Apply>(F), SS, true);
+ SS << ", ";
+ } else {
+ self()->printSExpr(F, SS, Prec_Postfix);
+ SS << "(";
+ }
+ self()->printSExpr(E->arg(), SS, Prec_MAX);
+ if (!sugared)
+ SS << ")$";
+ }
+
+ void printSApply(SApply *E, StreamType &SS) {
+ self()->printSExpr(E->sfun(), SS, Prec_Postfix);
+ SS << "@(";
+ self()->printSExpr(E->arg(), SS, Prec_MAX);
+ SS << ")";
+ }
+
+ void printProject(Project *E, StreamType &SS) {
+ self()->printSExpr(E->record(), SS, Prec_Postfix);
+ SS << ".";
+ SS << E->slotName();
+ }
+
+ void printCall(Call *E, StreamType &SS) {
+ SExpr *T = E->target();
+ if (T->opcode() == COP_Apply) {
+ self()->printApply(cast<Apply>(T), SS, true);
+ SS << ")";
+ }
+ else {
+ self()->printSExpr(T, SS, Prec_Postfix);
+ SS << "()";
+ }
+ }
+
+ void printAlloc(Alloc *E, StreamType &SS) {
+ SS << "#alloc ";
+ self()->printSExpr(E->dataType(), SS, Prec_Other-1);
+ }
+
+ void printLoad(Load *E, StreamType &SS) {
+ self()->printSExpr(E->pointer(), SS, Prec_Postfix);
+ SS << "^";
+ }
+
+ void printStore(Store *E, StreamType &SS) {
+ self()->printSExpr(E->pointer(), SS, Prec_Other-1);
+ SS << " = ";
+ self()->printSExpr(E->value(), SS, Prec_Other-1);
+ }
+
+ void printUnaryOp(UnaryOp *E, StreamType &SS) {
+ self()->printSExpr(E->expr(), SS, Prec_Unary);
+ }
+
+ void printBinaryOp(BinaryOp *E, StreamType &SS) {
+ self()->printSExpr(E->expr0(), SS, Prec_Binary-1);
+ SS << " " << clang::BinaryOperator::getOpcodeStr(E->binaryOpcode()) << " ";
+ self()->printSExpr(E->expr1(), SS, Prec_Binary-1);
+ }
+
+ void printCast(Cast *E, StreamType &SS) {
+ SS << "~";
+ self()->printSExpr(E->expr(), SS, Prec_Unary);
+ }
+
+ void printSCFG(SCFG *E, StreamType &SS) {
+ SS << "#CFG {\n";
+ for (auto BBI : *E) {
+ SS << "BB_" << BBI->blockID() << ":";
+ newline(SS);
+ for (auto I : BBI->arguments()) {
+ SS << "let ";
+ self()->printVariable(I, SS);
+ SS << " = ";
+ self()->printSExpr(I->definition(), SS, Prec_MAX);
+ SS << ";";
+ newline(SS);
+ }
+ for (auto I : BBI->instructions()) {
+ SS << "let ";
+ self()->printVariable(I, SS);
+ SS << " = ";
+ self()->printSExpr(I->definition(), SS, Prec_MAX);
+ SS << ";";
+ newline(SS);
+ }
+ SExpr *T = BBI->terminator();
+ if (T) {
+ self()->printSExpr(T, SS, Prec_MAX);
+ SS << ";";
+ newline(SS);
+ }
+ newline(SS);
+ }
+ SS << "}";
+ newline(SS);
+ }
+
+ void printPhi(Phi *E, StreamType &SS) {
+ SS << "#phi(";
+ unsigned i = 0;
+ for (auto V : E->values()) {
+ ++i;
+ if (i > 0)
+ SS << ", ";
+ self()->printSExpr(V, SS, Prec_MAX);
+ }
+ SS << ")";
+ }
+
+ void printGoto(Goto *E, StreamType &SS) {
+ SS << "#goto BB_";
+ SS << E->targetBlock()->blockID();
+ SS << ":";
+ SS << E->index();
+ }
+
+ void printBranch(Branch *E, StreamType &SS) {
+ SS << "#branch (";
+ self()->printSExpr(E->condition(), SS, Prec_MAX);
+ SS << ") BB_";
+ SS << E->thenBlock()->blockID();
+ SS << " BB_";
+ SS << E->elseBlock()->blockID();
+ }
+};
+
+} // end namespace til
+
+
+
+} // end namespace threadSafety
+} // end namespace clang
+
+#endif // THREAD_SAFETY_TIL_H
diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt
index 9630bc0be0..f3e4358974 100644
--- a/lib/Analysis/CMakeLists.txt
+++ b/lib/Analysis/CMakeLists.txt
@@ -22,6 +22,7 @@ add_clang_library(clangAnalysis
PseudoConstantAnalysis.cpp
ReachableCode.cpp
ScanfFormatString.cpp
+ ThreadSafetyCommon.cpp
ThreadSafety.cpp
UninitializedValues.cpp
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index fd804eba99..9cc2a55f0a 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -10,18 +10,20 @@
// A intra-procedural analysis for thread safety (e.g. deadlocks and race
// conditions), based off of an annotation system.
//
-// See http://clang.llvm.org/docs/LanguageExtensions.html#thread-safety-annotation-checking
+// See http://clang.llvm.org/docs/ThreadSafetyAnalysis.html
// for more information.
//
//===----------------------------------------------------------------------===//
-#include "clang/Analysis/Analyses/ThreadSafety.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/Analyses/ThreadSafety.h"
+#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
+#include "clang/Analysis/Analyses/ThreadSafetyCommon.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
@@ -2362,16 +2364,21 @@ inline bool neverReturns(const CFGBlock* B) {
/// at the end of each block, and issue warnings for thread safety violations.
/// Each block in the CFG is traversed exactly once.
void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
- CFG *CFGraph = AC.getCFG();
- if (!CFGraph) return;
- const NamedDecl *D = dyn_cast_or_null<NamedDecl>(AC.getDecl());
+ // TODO: this whole function needs be rewritten as a visitor for CFGWalker.
+ // For now, we just use the walker to set things up.
+ threadSafety::CFGWalker walker;
+ if (!walker.init(AC))
+ return;
// AC.dumpCFG(true);
+ // threadSafety::printSCFG(walker);
+
+ CFG *CFGraph = walker.CFGraph;
+ const NamedDecl *D = walker.FDecl;
- if (!D)
- return; // Ignore anonymous functions for now.
if (D->hasAttr<NoThreadSafetyAnalysisAttr>())
return;
+
// FIXME: Do something a bit more intelligent inside constructor and
// destructor code. Constructors and destructors must assume unique access
// to 'this', so checks on member variable access is disabled, but we should
@@ -2387,7 +2394,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
// We need to explore the CFG via a "topological" ordering.
// That way, we will be guaranteed to have information about required
// predecessor locksets when exploring a new block.
- PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
+ PostOrderCFGView *SortedGraph = walker.SortedGraph;
PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph);
// Mark entry block as reachable
diff --git a/lib/Analysis/ThreadSafetyCommon.cpp b/lib/Analysis/ThreadSafetyCommon.cpp
new file mode 100644
index 0000000000..cd416e57c9
--- /dev/null
+++ b/lib/Analysis/ThreadSafetyCommon.cpp
@@ -0,0 +1,407 @@
+//===- ThreadSafetyCommon.cpp ----------------------------------*- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of the interfaces declared in ThreadSafetyCommon.h
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
+#include "clang/Analysis/Analyses/ThreadSafetyCommon.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/DenseMap.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 <vector>
+
+
+namespace clang {
+namespace threadSafety {
+
+typedef SExprBuilder::CallingContext CallingContext;
+
+
+til::SExpr *SExprBuilder::lookupStmt(const Stmt *S) {
+ if (!SMap)
+ return 0;
+ auto It = SMap->find(S);
+ if (It != SMap->end())
+ return It->second;
+ return 0;
+}
+
+void SExprBuilder::insertStmt(const Stmt *S, til::Variable *V) {
+ SMap->insert(std::make_pair(S, V));
+}
+
+
+// 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 *SExprBuilder::translate(const Stmt *S, CallingContext *Ctx) {
+ // Check if S has already been translated and cached.
+ // This handles the lookup of SSA names for DeclRefExprs here.
+ if (til::SExpr *E = lookupStmt(S))
+ return E;
+
+ switch (S->getStmtClass()) {
+ case Stmt::DeclRefExprClass:
+ return translateDeclRefExpr(cast<DeclRefExpr>(S), Ctx);
+ case Stmt::CXXThisExprClass:
+ return translateCXXThisExpr(cast<CXXThisExpr>(S), Ctx);
+ case Stmt::MemberExprClass:
+ return translateMemberExpr(cast<MemberExpr>(S), Ctx);
+ case Stmt::CallExprClass:
+ return translateCallExpr(cast<CallExpr>(S), Ctx);
+ case Stmt::CXXMemberCallExprClass:
+ return translateCXXMemberCallExpr(cast<CXXMemberCallExpr>(S), Ctx);
+ case Stmt::CXXOperatorCallExprClass:
+ return translateCXXOperatorCallExpr(cast<CXXOperatorCallExpr>(S), Ctx);
+ case Stmt::UnaryOperatorClass:
+ return translateUnaryOperator(cast<UnaryOperator>(S), Ctx);
+ case Stmt::BinaryOperatorClass:
+ return translateBinaryOperator(cast<BinaryOperator>(S), Ctx);
+
+ case Stmt::ArraySubscriptExprClass:
+ return translateArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Ctx);
+ case Stmt::ConditionalOperatorClass:
+ return translateConditionalOperator(cast<ConditionalOperator>(S), Ctx);
+ case Stmt::BinaryConditionalOperatorClass:
+ return translateBinaryConditionalOperator(
+ cast<BinaryConditionalOperator>(S), Ctx);
+
+ // We treat these as no-ops
+ case Stmt::ParenExprClass:
+ return translate(cast<ParenExpr>(S)->getSubExpr(), Ctx);
+ case Stmt::ExprWithCleanupsClass:
+ return translate(cast<ExprWithCleanups>(S)->getSubExpr(), Ctx);
+ case Stmt::CXXBindTemporaryExprClass:
+ return translate(cast<CXXBindTemporaryExpr>(S)->getSubExpr(), Ctx);
+
+ // Collect all literals
+ case Stmt::CharacterLiteralClass:
+ case Stmt::CXXNullPtrLiteralExprClass:
+ case Stmt::GNUNullExprClass:
+ case Stmt::CXXBoolLiteralExprClass:
+ case Stmt::FloatingLiteralClass:
+ case Stmt::ImaginaryLiteralClass:
+ case Stmt::IntegerLiteralClass:
+ case Stmt::StringLiteralClass:
+ case Stmt::ObjCStringLiteralClass:
+ return new (Arena) til::Literal(cast<Expr>(S));
+ default:
+ break;
+ }
+ if (const CastExpr *CE = dyn_cast<CastExpr>(S))
+ return translateCastExpr(CE, Ctx);
+
+ return new (Arena) til::Undefined(S);
+}
+
+
+til::SExpr *SExprBuilder::translateDeclRefExpr(const DeclRefExpr *DRE,
+ CallingContext *Ctx) {
+ const ValueDecl *VD = cast<ValueDecl>(DRE->getDecl()->getCanonicalDecl());
+
+ // Function parameters require substitution and/or renaming.
+ if (const ParmVarDecl *PV = dyn_cast_or_null<ParmVarDecl>(VD)) {
+ const FunctionDecl *FD =
+ cast<FunctionDecl>(PV->getDeclContext())->getCanonicalDecl();
+ unsigned I = PV->getFunctionScopeIndex();
+
+ if (Ctx && Ctx->FunArgs && FD == Ctx->AttrDecl->getCanonicalDecl()) {
+ // Substitute call arguments for references to function parameters
+ assert(I < Ctx->NumArgs);
+ return translate(Ctx->FunArgs[I], Ctx->Prev);
+ }
+ // Map the param back to the param of the original function declaration
+ // for consistent comparisons.
+ VD = FD->getParamDecl(I);
+ }
+
+ // For non-local variables, treat it as a referenced to a named object.
+ return new (Arena) til::LiteralPtr(VD);
+}
+
+
+til::SExpr *SExprBuilder::translateCXXThisExpr(const CXXThisExpr *TE,
+ CallingContext *Ctx) {
+ // Substitute for 'this'
+ if (Ctx && Ctx->SelfArg)
+ return translate(Ctx->SelfArg, Ctx->Prev);
+ assert(SelfVar && "We have no variable for 'this'!");
+ return SelfVar;
+}
+
+
+til::SExpr *SExprBuilder::translateMemberExpr(const MemberExpr *ME,
+ CallingContext *Ctx) {
+ til::SExpr *E = translate(ME->getBase(), Ctx);
+ E = new (Arena) til::SApply(E);
+ return new (Arena) til::Project(E, ME->getMemberDecl());
+}
+
+
+til::SExpr *SExprBuilder::translateCallExpr(const CallExpr *CE,
+ CallingContext *Ctx) {
+ // TODO -- Lock returned
+ til::SExpr *E = translate(CE->getCallee(), Ctx);
+ for (unsigned I = 0, N = CE->getNumArgs(); I < N; ++I) {
+ til::SExpr *A = translate(CE->getArg(I), Ctx);
+ E = new (Arena) til::Apply(E, A);
+ }
+ return new (Arena) til::Call(E, CE);
+}
+
+
+til::SExpr *SExprBuilder::translateCXXMemberCallExpr(
+ const CXXMemberCallExpr *ME, CallingContext *Ctx) {
+ return translateCallExpr(cast<CallExpr>(ME), Ctx);
+}
+
+
+til::SExpr *SExprBuilder::translateCXXOperatorCallExpr(
+ const CXXOperatorCallExpr *OCE, CallingContext *Ctx) {
+ return translateCallExpr(cast<CallExpr>(OCE), Ctx);
+}
+
+
+til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO,
+ CallingContext *Ctx) {
+ switch (UO->getOpcode()) {
+ case UO_PostInc:
+ case UO_PostDec:
+ case UO_PreInc:
+ case UO_PreDec:
+ return new (Arena) til::Undefined(UO);
+
+ // We treat these as no-ops
+ case UO_AddrOf:
+ case UO_Deref:
+ case UO_Plus:
+ return translate(UO->getSubExpr(), Ctx);
+
+ case UO_Minus:
+ case UO_Not:
+ case UO_LNot:
+ case UO_Real:
+ case UO_Imag:
+ case UO_Extension: {
+ til::SExpr *E0 = translate(UO->getSubExpr(), Ctx);
+ return new (Arena) til::UnaryOp(UO->getOpcode(), E0);
+ }
+ }
+ return new (Arena) til::Undefined(UO);
+}
+
+til::SExpr *SExprBuilder::translateBinaryOperator(const BinaryOperator *BO,
+ CallingContext *Ctx) {
+ switch (BO->getOpcode()) {
+ case BO_PtrMemD:
+ case BO_PtrMemI:
+ return new (Arena) til::Undefined(BO);
+
+ case BO_Mul:
+ case BO_Div:
+ case BO_Rem:
+ case BO_Add:
+ case BO_Sub:
+ case BO_Shl:
+ case BO_Shr:
+ case BO_LT:
+ case BO_GT:
+ case BO_LE:
+ case BO_GE:
+ case BO_EQ:
+ case BO_NE:
+ case BO_And:
+ case BO_Xor:
+ case BO_Or:
+ case BO_LAnd:
+ case BO_LOr: {
+ til::SExpr *E0 = translate(BO->getLHS(), Ctx);
+ til::SExpr *E1 = translate(BO->getRHS(), Ctx);
+ return new (Arena) til::BinaryOp(BO->getOpcode(), E0, E1);
+ }
+ case BO_Assign: {
+ til::SExpr *E0 = translate(BO->getLHS(), Ctx);
+ til::SExpr *E1 = translate(BO->getRHS(), Ctx);
+ return new (Arena) til::Store(E0, E1);
+ }
+ case BO_MulAssign:
+ case BO_DivAssign:
+ case BO_RemAssign:
+ case BO_AddAssign:
+ case BO_SubAssign:
+ case BO_ShlAssign:
+ case BO_ShrAssign:
+ case BO_AndAssign:
+ case BO_XorAssign:
+ case BO_OrAssign:
+ return new (Arena) til::Undefined(BO);
+
+ case BO_Comma:
+ // TODO: handle LHS
+ return translate(BO->getRHS(), Ctx);
+ }
+
+ return new (Arena) til::Undefined(BO);
+}
+
+
+til::SExpr *SExprBuilder::translateCastExpr(const CastExpr *CE,
+ CallingContext *Ctx) {
+ til::SExpr *E0 = translate(CE->getSubExpr(), Ctx);
+
+ clang::CastKind K = CE->getCastKind();
+ switch (K) {
+ case CK_LValueToRValue:
+ return new (Arena) til::Load(E0);
+
+ case CK_NoOp:
+ case CK_DerivedToBase:
+ case CK_UncheckedDerivedToBase:
+ case CK_ArrayToPointerDecay:
+ case CK_FunctionToPointerDecay:
+ return E0;
+
+ default:
+ return new (Arena) til::Cast(K, E0);
+ }
+}
+
+
+til::SExpr *SExprBuilder::translateArraySubscriptExpr(
+ const ArraySubscriptExpr *E, CallingContext *Ctx) {
+ return new (Arena) til::Undefined(E);
+}
+
+
+til::SExpr *SExprBuilder::translateConditionalOperator(
+ const ConditionalOperator *C, CallingContext *Ctx) {
+ return new (Arena) til::Undefined(C);
+}
+
+
+til::SExpr *SExprBuilder::translateBinaryConditionalOperator(
+ const BinaryConditionalOperator *C, CallingContext *Ctx) {
+ return new (Arena) til::Undefined(C);
+}
+
+
+// Build a complete SCFG from a clang CFG.
+class SCFGBuilder : public CFGVisitor {
+public:
+ // return true if E should be included in the SCFG
+ bool includeExpr(til::SExpr* E) {
+ if (!E)
+ return false;
+ if (E->opcode() == til::COP_Variable)
+ return false;
+ if (E->opcode() == til::COP_LiteralPtr)
+ return false;
+ return true;
+ }
+
+ // Enter the CFG for Decl D, and perform any initial setup operations.
+ void enterCFG(CFG *Cfg, const NamedDecl *D, const CFGBlock *First) {
+ Scfg = new til::SCFG(Arena, Cfg->getNumBlockIDs());
+ CallCtx = new SExprBuilder::CallingContext(D);
+ }
+
+ // Enter a CFGBlock.
+ void enterCFGBlock(const CFGBlock *B) {
+ CurrentBB = new til::BasicBlock(Arena, 0, B->size());
+ CurrentBB->setBlockID(CurrentBlockID);
+ CurrentVarID = 0;
+ Scfg->add(CurrentBB);
+ }
+
+ // Process an ordinary statement.
+ void handleStatement(const Stmt *S) {
+ til::SExpr *E = BuildEx.translate(S, CallCtx);
+ if (includeExpr(E)) {
+ til::Variable *V = new til::Variable(til::Variable::VK_Let, E);
+ V->setID(CurrentBlockID, CurrentVarID++);
+ CurrentBB->addInstr(V);
+ BuildEx.insertStmt(S, V);
+ }
+ }
+
+ // Process a destructor call
+ void handleDestructorCall(const VarDecl *VD, const CXXDestructorDecl *DD) {}
+
+ // 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) {
+ CurrentBlockID++;
+ CurrentBB = 0;
+ }
+
+ // Leave the CFG, and perform any final cleanup operations.
+ void exitCFG(const CFGBlock *Last) {}
+
+ SCFGBuilder(til::MemRegionRef A)
+ : Arena(A), Scfg(0), CurrentBB(0), CurrentBlockID(0),
+ BuildEx(A, new SExprBuilder::StatementMap())
+ { }
+
+ til::SCFG *getCFG() const { return Scfg; }
+
+private:
+ til::MemRegionRef Arena;
+ til::SCFG *Scfg;
+ til::BasicBlock *CurrentBB;
+ unsigned CurrentBlockID;
+ unsigned CurrentVarID;
+
+ SExprBuilder BuildEx;
+ SExprBuilder::CallingContext *CallCtx;
+};
+
+
+
+class LLVMPrinter :
+ public til::TILPrettyPrinter<LLVMPrinter, llvm::raw_ostream> {
+};
+
+
+void printSCFG(CFGWalker &walker) {
+ llvm::BumpPtrAllocator Bpa;
+ til::MemRegionRef Arena(&Bpa);
+ SCFGBuilder builder(Arena);
+ // CFGVisitor visitor;
+ walker.walk(builder);
+ LLVMPrinter::print(builder.getCFG(), llvm::errs());
+}
+
+
+
+} // end namespace threadSafety
+
+} // end namespace clang