49 changes: 30 additions & 19 deletions clang/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,15 @@ class PrettyPrinter {
SS << "\n";
}

void printBlockLabel(StreamType & SS, BasicBlock *BB, unsigned index) {
if (!BB) {
SS << "BB_null";
return;
}
SS << "BB_";
SS << BB->blockID();
}

// TODO: further distinguish between binary operations.
static const unsigned Prec_Atom = 0;
static const unsigned Prec_Postfix = 1;
Expand Down Expand Up @@ -560,9 +569,11 @@ class PrettyPrinter {

void printSApply(SApply *E, StreamType &SS) {
self()->printSExpr(E->sfun(), SS, Prec_Postfix);
SS << "@(";
self()->printSExpr(E->arg(), SS, Prec_MAX);
SS << ")";
if (E->isDelegation()) {
SS << "@(";
self()->printSExpr(E->arg(), SS, Prec_MAX);
SS << ")";
}
}

void printProject(Project *E, StreamType &SS) {
Expand All @@ -584,7 +595,7 @@ class PrettyPrinter {
}

void printAlloc(Alloc *E, StreamType &SS) {
SS << "#alloc ";
SS << "new ";
self()->printSExpr(E->dataType(), SS, Prec_Other-1);
}

Expand All @@ -595,7 +606,7 @@ class PrettyPrinter {

void printStore(Store *E, StreamType &SS) {
self()->printSExpr(E->destination(), SS, Prec_Other-1);
SS << " = ";
SS << " := ";
self()->printSExpr(E->source(), SS, Prec_Other-1);
}

Expand Down Expand Up @@ -628,9 +639,11 @@ class PrettyPrinter {
newline(SS);
}
for (auto I : BBI->instructions()) {
SS << "let ";
self()->printVariable(I, SS);
SS << " = ";
if (I->definition()->opcode() != COP_Store) {
SS << "let ";
self()->printVariable(I, SS);
SS << " = ";
}
self()->printSExpr(I->definition(), SS, Prec_MAX);
SS << ";";
newline(SS);
Expand All @@ -648,31 +661,29 @@ class PrettyPrinter {
}

void printPhi(Phi *E, StreamType &SS) {
SS << "#phi(";
SS << "phi(";
unsigned i = 0;
for (auto V : E->values()) {
++i;
if (i > 0)
SS << ", ";
self()->printSExpr(V, SS, Prec_MAX);
++i;
}
SS << ")";
}

void printGoto(Goto *E, StreamType &SS) {
SS << "#goto BB_";
SS << E->targetBlock()->blockID();
SS << ":";
SS << E->index();
SS << "goto ";
printBlockLabel(SS, E->targetBlock(), E->index());
}

void printBranch(Branch *E, StreamType &SS) {
SS << "#branch (";
SS << "branch (";
self()->printSExpr(E->condition(), SS, Prec_MAX);
SS << ") BB_";
SS << E->thenBlock()->blockID();
SS << " BB_";
SS << E->elseBlock()->blockID();
SS << ") ";
printBlockLabel(SS, E->thenBlock(), E->thenIndex());
SS << " ";
printBlockLabel(SS, E->elseBlock(), E->elseIndex());
}
};

Expand Down
149 changes: 145 additions & 4 deletions clang/include/clang/Analysis/Analyses/ThreadSafetyUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ 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) {}
: Data(Dat), Size(Sz), Capacity(Cp) {}
SimpleArray(MemRegionRef A, size_t Cp)
: Data(A.allocateT<T>(Cp)), Size(0), Capacity(Cp) {}
SimpleArray(SimpleArray<T> &&A)
Expand All @@ -101,8 +101,14 @@ template <class T> class SimpleArray {
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]; }
T &operator[](unsigned i) {
assert(i < Sz && "Array index out of bounds.");
return Data[i];
}
const T &operator[](unsigned i) const {
assert(i < Size && "Array index out of bounds.");
return Data[i];
}

iterator begin() { return Data; }
iterator end() { return Data + Size; }
Expand All @@ -115,6 +121,14 @@ template <class T> class SimpleArray {
Data[Size++] = Elem;
}

void setValues(unsigned Sz, const T& C) {
assert(Sz < Capacity);
Size = Sz;
for (unsigned i = 0; i < Sz; ++i) {
Data[i] = C;
}
}

template <class Iter> unsigned append(Iter I, Iter E) {
size_t Osz = Size;
size_t J = Osz;
Expand All @@ -132,8 +146,135 @@ template <class T> class SimpleArray {
size_t Capacity;
};


} // end namespace til


// A copy on write vector.
// The vector can be in one of three states:
// * invalid -- no operations are permitted.
// * read-only -- read operations are permitted.
// * writable -- read and write operations are permitted.
// The init(), destroy(), and makeWritable() methods will change state.
template<typename T>
class CopyOnWriteVector {
private:
class VectorData {
public:
VectorData() : NumRefs(1) { }
VectorData(const VectorData &VD) : NumRefs(1), Vect(VD.Vect) { }

unsigned NumRefs;
std::vector<T> Vect;
};

public:
CopyOnWriteVector() : Data(0) { }
CopyOnWriteVector(const CopyOnWriteVector &V) = delete;
CopyOnWriteVector(CopyOnWriteVector &&V) : Data(V.Data) {
V.Data = 0;
}
~CopyOnWriteVector() {
destroy();
}

// Returns true if this holds a valid vector.
bool valid() { return Data; }

// Returns true if this vector is writable.
bool writable() { return Data && Data->NumRefs == 1; }

// If this vector is not valid, initialize it to a valid vector.
void init() {
if (!Data) {
Data = new VectorData();
}
}

// Destroy this vector; thus making it invalid.
void destroy() {
if (!Data)
return;
if (Data->NumRefs <= 1)
delete Data;
else
--Data->NumRefs;
Data = 0;
}

// Make this vector writable, creating a copy if needed.
void makeWritable() {
if (!Data) {
Data = new VectorData();
return;
}
if (Data->NumRefs == 1)
return; // already writeable.
--Data->NumRefs;
Data = new VectorData(*Data);
}

// Create a lazy copy of this vector.
CopyOnWriteVector clone() { return CopyOnWriteVector(Data); }

// No copy constructor or copy assignment. Use clone() with move assignment.
void operator=(const CopyOnWriteVector &V) = delete;

void operator=(CopyOnWriteVector &&V) {
destroy();
Data = V.Data;
V.Data = 0;
}

typedef typename std::vector<T>::const_iterator iterator;

const std::vector<T> &elements() const { return Data->Vect; }

iterator begin() const { return elements().cbegin(); }
iterator end() const { return elements().cend(); }

const T& operator[](unsigned i) const { return elements()[i]; }

unsigned size() const { return Data ? elements().size() : 0; }

// Return true if V and this vector refer to the same data.
bool sameAs(const CopyOnWriteVector& V) { return Data == V.Data; }

// Clear vector. The vector must be writable.
void clear() {
assert(writable() && "Vector is not writable!");
Data->Vect.clear();
}

// Push a new element onto the end. The vector must be writable.
void push_back(const T& Elem) {
assert(writable() && "Vector is not writable!");
Data->Vect.push_back(Elem);
}

// Gets a mutable reference to the element at index(i).
// The vector must be writable.
T& elem(unsigned i) {
assert(writable() && "Vector is not writable!");
return Data->Vect[i];
}

// Drops elements from the back until the vector has size i.
void downsize(unsigned i) {
assert(writable() && "Vector is not writable!");
Data->Vect.erase(Data->Vect.begin() + i, Data->Vect.end());
}

private:
CopyOnWriteVector(VectorData *D) : Data(D) {
if (!Data)
return;
++Data->NumRefs;
}

VectorData *Data;
};


} // end namespace threadSafety
} // end namespace clang

Expand Down
385 changes: 296 additions & 89 deletions clang/lib/Analysis/ThreadSafetyCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"

#include <algorithm>
#include <climits>
#include <vector>


Expand All @@ -38,23 +40,30 @@ typedef SExprBuilder::CallingContext CallingContext;


til::SExpr *SExprBuilder::lookupStmt(const Stmt *S) {
if (!SMap)
return nullptr;
auto It = SMap->find(S);
if (It != SMap->end())
auto It = SMap.find(S);
if (It != SMap.end())
return It->second;
return nullptr;
}

void SExprBuilder::insertStmt(const Stmt *S, til::Variable *V) {
SMap->insert(std::make_pair(S, V));
SMap.insert(std::make_pair(S, V));
}


til::SCFG *SExprBuilder::buildCFG(CFGWalker &Walker) {
Walker.walk(*this);
return Scfg;
}


// 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) {
if (!S)
return nullptr;

// 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))
Expand Down Expand Up @@ -105,6 +114,9 @@ til::SExpr *SExprBuilder::translate(const Stmt *S, CallingContext *Ctx) {
case Stmt::StringLiteralClass:
case Stmt::ObjCStringLiteralClass:
return new (Arena) til::Literal(cast<Expr>(S));

case Stmt::DeclStmtClass:
return translateDeclStmt(cast<DeclStmt>(S), Ctx);
default:
break;
}
Expand Down Expand Up @@ -209,6 +221,7 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO,
return new (Arena) til::Undefined(UO);
}


til::SExpr *SExprBuilder::translateBinaryOperator(const BinaryOperator *BO,
CallingContext *Ctx) {
switch (BO->getOpcode()) {
Expand Down Expand Up @@ -238,10 +251,17 @@ til::SExpr *SExprBuilder::translateBinaryOperator(const BinaryOperator *BO,
til::BinaryOp(BO->getOpcode(), translate(BO->getLHS(), Ctx),
translate(BO->getRHS(), Ctx));

case BO_Assign:
return new (Arena)
til::Store(translate(BO->getLHS(), Ctx), translate(BO->getRHS(), Ctx));

case BO_Assign: {
const Expr *LHS = BO->getLHS();
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(LHS)) {
const Expr *RHS = BO->getRHS();
til::SExpr *E1 = translate(RHS, Ctx);
return updateVarDecl(DRE->getDecl(), E1);
}
til::SExpr *E0 = translate(LHS, Ctx);
til::SExpr *E1 = translate(BO->getRHS(), Ctx);
return new (Arena) til::Store(E0, E1);
}
case BO_MulAssign:
case BO_DivAssign:
case BO_RemAssign:
Expand All @@ -265,145 +285,332 @@ til::SExpr *SExprBuilder::translateBinaryOperator(const BinaryOperator *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:
case CK_LValueToRValue: {
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CE->getSubExpr())) {
til::SExpr *E0 = lookupVarDecl(DRE->getDecl());
if (E0)
return E0;
}
til::SExpr *E0 = translate(CE->getSubExpr(), Ctx);
return new (Arena) til::Load(E0);

}
case CK_NoOp:
case CK_DerivedToBase:
case CK_UncheckedDerivedToBase:
case CK_ArrayToPointerDecay:
case CK_FunctionToPointerDecay:
case CK_FunctionToPointerDecay: {
til::SExpr *E0 = translate(CE->getSubExpr(), Ctx);
return E0;

default:
}
default: {
til::SExpr *E0 = translate(CE->getSubExpr(), Ctx);
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);
}


til::SExpr *
SExprBuilder::translateDeclStmt(const DeclStmt *S, CallingContext *Ctx) {
DeclGroupRef DGrp = S->getDeclGroup();
for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) {
if (VarDecl *VD = dyn_cast_or_null<VarDecl>(*I)) {
Expr *E = VD->getInit();
til::SExpr* SE = translate(E, Ctx);

// Add local variables with trivial type to the variable map
QualType T = VD->getType();
if (T.isTrivialType(VD->getASTContext())) {
return addVarDecl(VD, SE);
}
else {
// TODO: add alloca
}
}
}
return nullptr;
}

// Build a complete SCFG from a clang CFG.
class SCFGBuilder {
class BBInfo {

};
// If (E) is non-trivial, then add it to the current basic block, and
// update the statement map so that S refers to E. Returns a new variable
// that refers to E.
// If E is trivial returns E.
til::SExpr *SExprBuilder::addStatement(til::SExpr* E, const Stmt *S,
const ValueDecl *VD) {
if (!E)
return nullptr;
if (til::ThreadSafetyTIL::isTrivial(E))
return E;

void addStatement(til::SExpr* E, const Stmt *S) {
if (!E)
return;
if (til::ThreadSafetyTIL::isTrivial(E))
return;
til::Variable *V = new (Arena) til::Variable(E, VD);
V->setID(CurrentBlockID, CurrentVarID++);
CurrentBB->addInstr(V);
if (S)
insertStmt(S, V);
return V;
}

til::Variable *V = new (Arena) til::Variable(til::Variable::VK_Let, E);
V->setID(CurrentBlockID, CurrentVarID++);
CurrentBB->addInstr(V);
if (S)
BuildEx.insertStmt(S, V);
}

public:
// Enter the CFG for Decl D, and perform any initial setup operations.
void enterCFG(CFG *Cfg, const NamedDecl *D, const CFGBlock *First) {
Scfg = new (Arena) til::SCFG(Arena, Cfg->getNumBlockIDs());
CallCtx = new SExprBuilder::CallingContext(D);
}
// Returns the current value of VD, if known, and nullptr otherwise.
til::SExpr *SExprBuilder::lookupVarDecl(const ValueDecl *VD) {
auto It = IdxMap.find(VD);
if (It != IdxMap.end())
return CurrentNameMap[It->second].second;
return nullptr;
}


// Enter a CFGBlock.
void enterCFGBlock(const CFGBlock *B) {
CurrentBB = new (Arena) til::BasicBlock(Arena, 0, B->size());
CurrentBB->setBlockID(CurrentBlockID);
CurrentVarID = 0;
Scfg->add(CurrentBB);
// if E is a til::Variable, update its clangDecl.
inline void maybeUpdateVD(til::SExpr *E, const ValueDecl *VD) {
if (!E)
return;
if (til::Variable *V = dyn_cast<til::Variable>(E)) {
if (!V->clangDecl())
V->setClangDecl(VD);
}
}

// Adds a new variable declaration.
til::SExpr *SExprBuilder::addVarDecl(const ValueDecl *VD, til::SExpr *E) {
maybeUpdateVD(E, VD);
IdxMap.insert(std::make_pair(VD, CurrentNameMap.size()));
CurrentNameMap.makeWritable();
CurrentNameMap.push_back(std::make_pair(VD, E));
return E;
}

// Process an ordinary statement.
void handleStatement(const Stmt *S) {
til::SExpr *E = BuildEx.translate(S, CallCtx);
addStatement(E, S);

// Updates a current variable declaration. (E.g. by assignment)
til::SExpr *SExprBuilder::updateVarDecl(const ValueDecl *VD, til::SExpr *E) {
maybeUpdateVD(E, VD);
auto It = IdxMap.find(VD);
if (It == IdxMap.end()) {
til::SExpr *Ptr = new (Arena) til::LiteralPtr(VD);
til::SExpr *St = new (Arena) til::Store(Ptr, E);
return St;
}
CurrentNameMap.makeWritable();
CurrentNameMap.elem(It->second).second = E;
return E;
}


// Process a destructor call
void handleDestructorCall(const VarDecl *VD, const CXXDestructorDecl *DD) {
til::SExpr *Sf = new (Arena) til::LiteralPtr(VD);
til::SExpr *Dr = new (Arena) til::LiteralPtr(DD);
til::SExpr *Ap = new (Arena) til::Apply(Dr, Sf);
til::SExpr *E = new (Arena) til::Call(Ap);
addStatement(E, nullptr);
// Merge values from Map into the current entry map.
void SExprBuilder::mergeEntryMap(NameVarMap Map) {
assert(CurrentBlockInfo && "Not processing a block!");

if (!CurrentNameMap.valid()) {
// Steal Map, using copy-on-write.
CurrentNameMap = std::move(Map);
return;
}
if (CurrentNameMap.sameAs(Map))
return; // Easy merge: maps from different predecessors are unchanged.

unsigned ESz = CurrentNameMap.size();
unsigned MSz = Map.size();
unsigned Sz = std::max(ESz, MSz);
bool W = CurrentNameMap.writable();
for (unsigned i=0; i<Sz; ++i) {
if (CurrentNameMap[i].first != Map[i].first) {
if (!W)
CurrentNameMap.makeWritable();
CurrentNameMap.downsize(i);
break;
}
if (CurrentNameMap[i].second != Map[i].second) {
til::Variable *V =
dyn_cast<til::Variable>(CurrentNameMap[i].second);
if (V && V->getBlockID() == CurrentBB->blockID()) {
// We already have a Phi node, so add the new variable.
til::Phi *Ph = dyn_cast<til::Phi>(V->definition());
assert(Ph && "Expecting Phi node.");
Ph->values()[CurrentArgIndex] = Map[i].second;
}
else {
if (!W)
CurrentNameMap.makeWritable();
unsigned NPreds = CurrentBB->numPredecessors();
assert(CurrentArgIndex > 0 && CurrentArgIndex < NPreds);

// Make a new phi node. All phi args up to the current index must
// be the same, and equal to the current NameMap value.
auto *Ph = new (Arena) til::Phi(Arena, NPreds);
Ph->values().setValues(NPreds, nullptr);
for (unsigned PIdx = 0; PIdx < CurrentArgIndex; ++PIdx)
Ph->values()[PIdx] = CurrentNameMap[i].second;
Ph->values()[CurrentArgIndex] = Map[i].second;

// Add phi node to current basic block.
auto *Var = new (Arena) til::Variable(Ph, CurrentNameMap[i].first);
Var->setID(CurrentBlockID, CurrentVarID++);
CurrentBB->addArgument(Var);
CurrentNameMap.elem(i).second = Var;
}
}
}
if (ESz > MSz) {
if (!W)
CurrentNameMap.makeWritable();
CurrentNameMap.downsize(Map.size());
}
}


// Process a successor edge.
void handleSuccessor(const CFGBlock *Succ) {}

// Process a successor back edge to a previously visited block.
void handleSuccessorBackEdge(const CFGBlock *Succ) {}
void SExprBuilder::enterCFG(CFG *Cfg, const NamedDecl *D,
const CFGBlock *First) {
// Perform initial setup operations.
unsigned NBlocks = Cfg->getNumBlockIDs();
Scfg = new (Arena) til::SCFG(Arena, NBlocks);

// Leave a CFGBlock.
void exitCFGBlock(const CFGBlock *B) {
CurrentBlockID++;
CurrentBB = 0;
// allocate all basic blocks immediately, to handle forward references.
BlockMap.reserve(NBlocks);
BBInfo.resize(NBlocks);
for (auto *B : *Cfg) {
auto *BB = new (Arena) til::BasicBlock(Arena, 0, B->size());
BlockMap.push_back(BB);
}
CallCtx = new SExprBuilder::CallingContext(D);
}



void SExprBuilder::enterCFGBlock(const CFGBlock *B) {
// Intialize TIL basic block and add it to the CFG.
CurrentBB = BlockMap[B->getBlockID()];
CurrentBB->setBlockID(CurrentBlockID);
CurrentBB->setNumPredecessors(B->pred_size());
Scfg->add(CurrentBB);

CurrentBlockInfo = &BBInfo[B->getBlockID()];
CurrentVarID = 0;
CurrentArgIndex = 0;

assert(!CurrentNameMap.valid() && "CurrentNameMap already initialized.");
}


void SExprBuilder::handlePredecessor(const CFGBlock *Pred) {
// Compute CurrentNameMap on entry from ExitMaps of predecessors

BlockInfo *PredInfo = &BBInfo[Pred->getBlockID()];
assert(PredInfo->SuccessorsToProcess > 0);

if (--PredInfo->SuccessorsToProcess == 0)
mergeEntryMap(std::move(PredInfo->ExitMap));
else
mergeEntryMap(PredInfo->ExitMap.clone());

++CurrentArgIndex;
}


void SExprBuilder::handlePredecessorBackEdge(const CFGBlock *Pred) {
CurrentBlockInfo->HasBackEdges = true;
}


// Leave the CFG, and perform any final cleanup operations.
void exitCFG(const CFGBlock *Last) {
delete CallCtx;
CallCtx = nullptr;
void SExprBuilder::enterCFGBlockBody(const CFGBlock *B) { }


void SExprBuilder::handleStatement(const Stmt *S) {
til::SExpr *E = translate(S, CallCtx);
addStatement(E, S);
}


void SExprBuilder::handleDestructorCall(const VarDecl *VD,
const CXXDestructorDecl *DD) {
til::SExpr *Sf = new (Arena) til::LiteralPtr(VD);
til::SExpr *Dr = new (Arena) til::LiteralPtr(DD);
til::SExpr *Ap = new (Arena) til::Apply(Dr, Sf);
til::SExpr *E = new (Arena) til::Call(Ap);
addStatement(E, nullptr);
}



void SExprBuilder::exitCFGBlockBody(const CFGBlock *B) {
unsigned N = B->succ_size();
auto It = B->succ_begin();
if (N == 1) {
til::BasicBlock *BB = *It ? BlockMap[(*It)->getBlockID()] : nullptr;
// TODO: set index
til::SExpr *Tm = new (Arena) til::Goto(BB, 0);
CurrentBB->setTerminator(Tm);
}
else if (N == 2) {
til::SExpr *C = translate(B->getTerminatorCondition(true), CallCtx);
til::BasicBlock *BB1 = *It ? BlockMap[(*It)->getBlockID()] : nullptr;
++It;
til::BasicBlock *BB2 = *It ? BlockMap[(*It)->getBlockID()] : nullptr;
// TODO: set conditional, set index
til::SExpr *Tm = new (Arena) til::Branch(C, BB1, BB2);
CurrentBB->setTerminator(Tm);
}
}

SCFGBuilder(til::MemRegionRef A)
: Arena(A), Scfg(nullptr), CurrentBB(nullptr), CurrentBlockID(0),
CurrentVarID(0), CallCtx(nullptr),
SMap(new SExprBuilder::StatementMap()), BuildEx(A, SMap) {}
~SCFGBuilder() { delete SMap; }

til::SCFG *getCFG() const { return Scfg; }
void SExprBuilder::handleSuccessor(const CFGBlock *Succ) {
++CurrentBlockInfo->SuccessorsToProcess;
}

private:
til::MemRegionRef Arena;
til::SCFG *Scfg;
til::BasicBlock *CurrentBB;
unsigned CurrentBlockID;
unsigned CurrentVarID;

SExprBuilder::CallingContext *CallCtx;
SExprBuilder::StatementMap *SMap;
SExprBuilder BuildEx;
};
void SExprBuilder::handleSuccessorBackEdge(const CFGBlock *Succ) {

}


void SExprBuilder::exitCFGBlock(const CFGBlock *B) {
CurrentBlockInfo->ExitMap = std::move(CurrentNameMap);
CurrentBlockID++;
CurrentBB = nullptr;
CurrentBlockInfo = nullptr;
}


void SExprBuilder::exitCFG(const CFGBlock *Last) {
CurrentBlockID = 0;
CurrentVarID = 0;
CurrentArgIndex = 0;
}



class LLVMPrinter :
public til::PrettyPrinter<LLVMPrinter, llvm::raw_ostream> {
class LLVMPrinter : public til::PrettyPrinter<LLVMPrinter, llvm::raw_ostream> {
};


void printSCFG(CFGWalker &walker) {
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());
SExprBuilder builder(Arena);
til::SCFG *Cfg = builder.buildCFG(Walker);
LLVMPrinter::print(Cfg, llvm::errs());
}


Expand Down