Thread Safety Analysis: reorganized SExpr header files. No change in
functionality. llvm-svn: 205936
This commit is contained in:
parent
b49fee0b41
commit
7e615c2f65
|
@ -26,6 +26,7 @@
|
|||
#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
|
||||
#include "clang/Analysis/AnalysisContext.h"
|
||||
#include "clang/Basic/OperatorKinds.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
@ -206,15 +207,16 @@ protected:
|
|||
|
||||
public:
|
||||
SExprBuilder(til::MemRegionRef A, StatementMap *SM = nullptr)
|
||||
: Arena(A), SMap(SM), SelfVar(nullptr) {
|
||||
: Arena(A), SMap(SM), SelfVar(nullptr), CurrentBlock(nullptr) {
|
||||
// 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'
|
||||
StatementMap *SMap; // Map from Stmt to TIL Variables
|
||||
til::Variable *SelfVar; // Variable to use for 'this'. May be null.
|
||||
til::BasicBlock* CurrentBlock; // Current basic block. May be null.
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -43,125 +43,21 @@
|
|||
#ifndef 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 "llvm/Support/AlignOf.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#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 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(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 {
|
||||
|
@ -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 threadSafety
|
||||
} // end namespace clang
|
||||
|
||||
#endif // THREAD_SAFETY_TIL_H
|
||||
#endif // LLVM_CLANG_THREAD_SAFETY_TIL_H
|
||||
|
|
|
@ -0,0 +1,684 @@
|
|||
//===- ThreadSafetyTraverse.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 framework for doing generic traversals and rewriting
|
||||
// operations over the Thread Safety TIL.
|
||||
//
|
||||
// UNDER CONSTRUCTION. USE AT YOUR OWN RISK.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_THREAD_SAFETY_TRAVERSE_H
|
||||
#define LLVM_CLANG_THREAD_SAFETY_TRAVERSE_H
|
||||
|
||||
#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
|
||||
|
||||
namespace clang {
|
||||
namespace threadSafety {
|
||||
namespace til {
|
||||
|
||||
// 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 threadSafety
|
||||
} // end namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_THREAD_SAFETY_TRAVERSE_H
|
|
@ -0,0 +1,140 @@
|
|||
//===- ThreadSafetyUtil.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 some basic utility classes for use by ThreadSafetyTIL.h
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_THREAD_SAFETY_UTIL_H
|
||||
#define LLVM_CLANG_THREAD_SAFETY_UTIL_H
|
||||
|
||||
#include "llvm/Support/AlignOf.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#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 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(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;
|
||||
};
|
||||
|
||||
|
||||
} // end namespace til
|
||||
} // end namespace threadSafety
|
||||
} // end namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_THREAD_SAFETY_UTIL_H
|
|
@ -23,6 +23,7 @@
|
|||
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
|
||||
#include "clang/Analysis/Analyses/ThreadSafety.h"
|
||||
#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
|
||||
#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h"
|
||||
#include "clang/Analysis/Analyses/ThreadSafetyCommon.h"
|
||||
#include "clang/Analysis/AnalysisContext.h"
|
||||
#include "clang/Analysis/CFG.h"
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "clang/AST/StmtCXX.h"
|
||||
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
|
||||
#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
|
||||
#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h"
|
||||
#include "clang/Analysis/AnalysisContext.h"
|
||||
#include "clang/Analysis/CFG.h"
|
||||
#include "clang/Basic/OperatorKinds.h"
|
||||
|
@ -26,6 +27,7 @@
|
|||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
@ -299,8 +301,14 @@ til::SExpr *SExprBuilder::translateBinaryConditionalOperator(
|
|||
return new (Arena) til::Undefined(C);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Build a complete SCFG from a clang CFG.
|
||||
class SCFGBuilder {
|
||||
class BBInfo {
|
||||
|
||||
};
|
||||
|
||||
void addStatement(til::SExpr* E, const Stmt *S) {
|
||||
if (!E)
|
||||
return;
|
||||
|
|
Loading…
Reference in New Issue