|
@@ -43,125 +43,21 @@
|
|
#ifndef LLVM_CLANG_THREAD_SAFETY_TIL_H
|
|
#ifndef LLVM_CLANG_THREAD_SAFETY_TIL_H
|
|
#define LLVM_CLANG_THREAD_SAFETY_TIL_H
|
|
#define LLVM_CLANG_THREAD_SAFETY_TIL_H
|
|
|
|
|
|
|
|
+#include "clang/Analysis/Analyses/ThreadSafetyUtil.h"
|
|
#include "clang/AST/ExprCXX.h"
|
|
#include "clang/AST/ExprCXX.h"
|
|
-#include "llvm/Support/AlignOf.h"
|
|
|
|
-#include "llvm/Support/Allocator.h"
|
|
|
|
|
|
+#include "llvm/ADT/StringRef.h"
|
|
|
|
|
|
#include <cassert>
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <cstddef>
|
|
#include <utility>
|
|
#include <utility>
|
|
|
|
|
|
-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(nullptr) {}
|
|
|
|
- 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 clang {
|
|
namespace threadSafety {
|
|
namespace threadSafety {
|
|
namespace til {
|
|
namespace til {
|
|
|
|
|
|
using llvm::StringRef;
|
|
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(nullptr), 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)
|
|
|
|
- : Data(A.Data), Size(A.Size), Capacity(A.Capacity) {
|
|
|
|
- A.Data = nullptr;
|
|
|
|
- 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:
|
|
|
|
- SimpleArray(const SimpleArray<T> &A) { }
|
|
|
|
-
|
|
|
|
- T *Data;
|
|
|
|
- size_t Size;
|
|
|
|
- size_t Capacity;
|
|
|
|
-};
|
|
|
|
|
|
+using clang::SourceLocation;
|
|
|
|
|
|
|
|
|
|
enum TIL_Opcode {
|
|
enum TIL_Opcode {
|
|
@@ -1302,665 +1198,8 @@ private:
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
-// 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 Traversal : 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. 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(SExprRef &E, TraversalKind K = TRV_Normal) {
|
|
|
|
- return traverse(E.get(), K);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- typename R::R_SExpr traverse(SExpr *E, 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 CopyReducer {
|
|
|
|
-public:
|
|
|
|
- CopyReducer() {}
|
|
|
|
-
|
|
|
|
- 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(CopyReducer &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 CopyReducer;
|
|
|
|
- SimpleArray<T> Elems;
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
-public:
|
|
|
|
- R_SExpr reduceNull() {
|
|
|
|
- return nullptr;
|
|
|
|
- }
|
|
|
|
- // 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, E0);
|
|
|
|
- }
|
|
|
|
- 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, std::move(Bbs.Elems));
|
|
|
|
- }
|
|
|
|
- R_SExpr reducePhi(Phi &Orig, Container<R_SExpr> &As) {
|
|
|
|
- return new (Arena) Phi(Orig, std::move(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, std::move(As.Elems),
|
|
|
|
- std::move(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 Traversal<SExprCopier, CopyReducer> {
|
|
|
|
-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 VisitReducer {
|
|
|
|
-public:
|
|
|
|
- VisitReducer() {}
|
|
|
|
-
|
|
|
|
- // 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(VisitReducer &R, unsigned N) : Success(true) {}
|
|
|
|
- void push_back(bool E) { Success = Success && E; }
|
|
|
|
-
|
|
|
|
- private:
|
|
|
|
- friend class VisitReducer;
|
|
|
|
- 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, unsigned Index) {
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
- 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 : nullptr;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- Variable *enterScope(Variable &Orig, R_SExpr E0) {
|
|
|
|
- return E0 ? &Orig : nullptr;
|
|
|
|
- }
|
|
|
|
- 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 Traversal<Self, VisitReducer> {
|
|
|
|
-public:
|
|
|
|
- SExprVisitor() : Success(true) {}
|
|
|
|
-
|
|
|
|
- bool traverse(SExpr *E, 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 Comparator {
|
|
|
|
-protected:
|
|
|
|
- Self *self() { return reinterpret_cast<Self *>(this); }
|
|
|
|
-
|
|
|
|
-public:
|
|
|
|
- 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 EqualsComparator : public Comparator<EqualsComparator> {
|
|
|
|
-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) {
|
|
|
|
- EqualsComparator Eq;
|
|
|
|
- return Eq.compare(E1, E2);
|
|
|
|
- }
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-// Pretty printer for TIL expressions
|
|
|
|
-template <typename Self, typename StreamType>
|
|
|
|
-class PrettyPrinter {
|
|
|
|
-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->destination(), SS, Prec_Other-1);
|
|
|
|
- SS << " = ";
|
|
|
|
- self()->printSExpr(E->source(), 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 A : BBI->arguments()) {
|
|
|
|
- SS << "let ";
|
|
|
|
- self()->printVariable(A, SS);
|
|
|
|
- SS << " = ";
|
|
|
|
- self()->printSExpr(A->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 til
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
} // end namespace threadSafety
|
|
} // end namespace threadSafety
|
|
} // end namespace clang
|
|
} // end namespace clang
|
|
|
|
|
|
-#endif // THREAD_SAFETY_TIL_H
|
|
|
|
|
|
+#endif // LLVM_CLANG_THREAD_SAFETY_TIL_H
|