r205728 - Thread Safety Analysis: update to internal SExpr handling.
Aaron Ballman
aaron at aaronballman.com
Mon Apr 7 14:50:31 PDT 2014
Since you're already worrying about const-correctness, I'll leave
those comments out for now. Also, since this is new code, clang-format
would be appropriate since there's a fair number of formatting issues.
Other comments below.
On Mon, Apr 7, 2014 at 2:09 PM, DeLesley Hutchins <delesley at google.com> wrote:
> Author: delesley
> Date: Mon Apr 7 13:09:54 2014
> New Revision: 205728
>
> URL: http://llvm.org/viewvc/llvm-project?rev=205728&view=rev
> Log:
> Thread Safety Analysis: update to internal SExpr handling.
> This patch is the first part of a significant refactoring that seeks to restore
> sanity to way thread safety analysis deals with capability expressions. The
> current patch merely provides an outline of the structure of the new system.
> It's not yet connected to the actual analysis, so there's no change in
> functionality.
>
> Added:
> cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
> cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyOps.def
> cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h
> cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp
> Modified:
> cfe/trunk/lib/Analysis/CMakeLists.txt
> cfe/trunk/lib/Analysis/ThreadSafety.cpp
>
> Added: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h?rev=205728&view=auto
> ==============================================================================
> --- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h (added)
> +++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyCommon.h Mon Apr 7 13:09:54 2014
> @@ -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>
It seems like a whole lot of these could be forward declares.
> +
> +
> +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) {}
> +};
These all look very much like they should be virtual methods.
> +
> +
> +// Walks the clang CFG, and invokes methods on a given CFGVisitor.
> +class CFGWalker {
> +public:
> + CFGWalker() : CFGraph(0), FDecl(0), ACtx(0), SortedGraph(0) {}
nullptr instead of 0?
> +
> + ~CFGWalker() { }
Why is this required?
> +
> + // Initialize the CFGWalker. This setup only needs to be done once, even
> + // if there are multiple passes over the CFG.
> + bool init(AnalysisDeclContext &AC) {
Why is this not a constructor when it only needs to be done once?
> + ACtx = &AC;
It smells a bit like ACtx should be a reference member, otherwise the
pointer may wind up dangling a bit more easily.
> + 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) {
Range-based for loop?
> + 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());
Getting rid of these const_casts would be wonderful, if possible.
> + 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;
Why are these all public?
> +};
> +
> +
> +// 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);
It seems like a lot of these methods shouldn't be public.
> +
> +
> + 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:
Why 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);
This seems like it'd be more reasonable as a dump() method on
CFGWalker than a stand-alone function.
> +
> +
> +} // end namespace threadSafety
> +
> +} // end namespace clang
>
> Added: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyOps.def
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyOps.def?rev=205728&view=auto
> ==============================================================================
> --- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyOps.def (added)
> +++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyOps.def Mon Apr 7 13:09:54 2014
> @@ -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)
>
> Added: cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h?rev=205728&view=auto
> ==============================================================================
> --- cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h (added)
> +++ cfe/trunk/include/clang/Analysis/Analyses/ThreadSafetyTIL.h Mon Apr 7 13:09:54 2014
Everything in this file is in the til namespace, yet it has an odd
mixture of naming conventions used. Eg) til::TILPrettyPrinter, vs
til::TIL_Opcode vs til::ThreadSafetyTIL -- I think all of the
capitalized TILs should likely be dropped as the namespace should
suffice.
> @@ -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>
Can any of these be forward declares as well?
> +
> +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.
Can an existing container class suffice? I'm not keen on adding
another container data type, much less exposing it like this.
> +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) {}
You're kind of missing a virtual destructor here. Future inherits from
SExpr and exposes virtual functions, so polymorphic deletion of a
Future through a static type of SExpr would be problematic.
> +
> + const unsigned char Opcode;
> + unsigned char Reserved;
> + unsigned short Flags;
> +
> +private:
> + SExpr();
> +};
> +
> +typedef SExpr* SExprRef;
This isn't a typical pattern used in the code base, and it makes it
harder to write const-correct code.
> +
> +
> +// 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; }
nullptr instead of 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.
nullptr, and should probably assert.
> + 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; }
expr() instead of clangExpr()? A bit of a bikeshed, but the name is
just kind of odd to me.
> +
> + 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; }
decl() instead of clangDecl() for the same reasoning as above?
> +
> + 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"; }
Why _x?
> + const clang::ValueDecl *clangDecl() const { return Cvdecl; }
decl() instead of clangDecl?
> +
> + // 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; }
Should there be an assert that NumUses >= 0 when detaching (to catch
mismatches)?
> +
> + 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;
Seems like NumUses should be unsigned.
> +};
> +
> +
> +// 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; }
callExpr() instead of clangCallExpr()?
> +
> + 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
I would really rather not see a constructor which steals memory from
something else -- could it be a factory function instead, so it's at
least a little more readily apparent to callers?
> + : 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) {
Range-based for loop?
> + 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) {
Same here.
> + 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]);
This would be more clear with reverse iterators (until we get a
reverse range adapter).
> +
> + 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) {
Range-based for loop
> + 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) {}
Still worried about memory theft...
> +
> + 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() {}
Why is this needed?
> +
> + void setArena(MemRegionRef A) { Arena = A; }
This seems like it could be used as a constructor parameter instead.
> +
> + // 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:
public is redundant.
> + R_SExpr reduceNull() {
> + return 0;
nullptr instead of 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:
public is redundant.
> + 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); }
Does this need to be 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 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:
Why not private?
> + 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;
Why not an enumeration instead?
> +
> + // 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) {
should be const auto * (similar comments apply elsewhere in this class).
> + 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
>
> Modified: cfe/trunk/lib/Analysis/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CMakeLists.txt?rev=205728&r1=205727&r2=205728&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Analysis/CMakeLists.txt (original)
> +++ cfe/trunk/lib/Analysis/CMakeLists.txt Mon Apr 7 13:09:54 2014
> @@ -22,6 +22,7 @@ add_clang_library(clangAnalysis
> PseudoConstantAnalysis.cpp
> ReachableCode.cpp
> ScanfFormatString.cpp
> + ThreadSafetyCommon.cpp
> ThreadSafety.cpp
> UninitializedValues.cpp
>
>
> Modified: cfe/trunk/lib/Analysis/ThreadSafety.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafety.cpp?rev=205728&r1=205727&r2=205728&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Analysis/ThreadSafety.cpp (original)
> +++ cfe/trunk/lib/Analysis/ThreadSafety.cpp Mon Apr 7 13:09:54 2014
> @@ -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*
> /// 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);
These two lines can be removed entirely instead of left commented out.
> +
> + 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(A
> // 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
>
> Added: cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp?rev=205728&view=auto
> ==============================================================================
> --- cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp (added)
> +++ cfe/trunk/lib/Analysis/ThreadSafetyCommon.cpp Mon Apr 7 13:09:54 2014
> @@ -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) {
Seems like CallExpr could use an arguments() range function that could
be used here. ;-)
> + 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) {
This should be private. That goes for other members of this class as well.
> + 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);
These are being leaked. unique_ptr perhaps?
> + }
> +
> + // Enter a CFGBlock.
> + void enterCFGBlock(const CFGBlock *B) {
> + CurrentBB = new til::BasicBlock(Arena, 0, B->size());
> + CurrentBB->setBlockID(CurrentBlockID);
> + CurrentVarID = 0;
> + Scfg->add(CurrentBB);
All of these blocks wind up being leaked.
> + }
> +
> + // 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);
All of these are leaked as well.
> + 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
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
~Aaron
More information about the cfe-commits
mailing list