//===- ThreadSafetyTIL.cpp -------------------------------------*- C++ --*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT in the llvm repository for details. // //===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/ThreadSafetyTIL.h" #include "clang/Analysis/Analyses/ThreadSafetyTraverse.h" using namespace clang; using namespace threadSafety; using namespace til; StringRef til::getUnaryOpcodeString(TIL_UnaryOpcode Op) { switch (Op) { case UOP_Minus: return "-"; case UOP_BitNot: return "~"; case UOP_LogicNot: return "!"; } return ""; } StringRef til::getBinaryOpcodeString(TIL_BinaryOpcode Op) { switch (Op) { case BOP_Mul: return "*"; case BOP_Div: return "/"; case BOP_Rem: return "%"; case BOP_Add: return "+"; case BOP_Sub: return "-"; case BOP_Shl: return "<<"; case BOP_Shr: return ">>"; case BOP_BitAnd: return "&"; case BOP_BitXor: return "^"; case BOP_BitOr: return "|"; case BOP_Eq: return "=="; case BOP_Neq: return "!="; case BOP_Lt: return "<"; case BOP_Leq: return "<="; case BOP_LogicAnd: return "&&"; case BOP_LogicOr: return "||"; } return ""; } SExpr* Future::force() { Status = FS_evaluating; Result = compute(); Status = FS_done; return Result; } unsigned BasicBlock::addPredecessor(BasicBlock *Pred) { unsigned Idx = Predecessors.size(); Predecessors.reserveCheck(1, Arena); Predecessors.push_back(Pred); for (SExpr *E : Args) { if (Phi* Ph = dyn_cast(E)) { Ph->values().reserveCheck(1, Arena); Ph->values().push_back(nullptr); } } return Idx; } void BasicBlock::reservePredecessors(unsigned NumPreds) { Predecessors.reserve(NumPreds, Arena); for (SExpr *E : Args) { if (Phi* Ph = dyn_cast(E)) { Ph->values().reserve(NumPreds, Arena); } } } // If E is a variable, then trace back through any aliases or redundant // Phi nodes to find the canonical definition. const SExpr *til::getCanonicalVal(const SExpr *E) { while (true) { if (auto *V = dyn_cast(E)) { if (V->kind() == Variable::VK_Let) { E = V->definition(); continue; } } if (const Phi *Ph = dyn_cast(E)) { if (Ph->status() == Phi::PH_SingleVal) { E = Ph->values()[0]; continue; } } break; } return E; } // If E is a variable, then trace back through any aliases or redundant // Phi nodes to find the canonical definition. // The non-const version will simplify incomplete Phi nodes. SExpr *til::simplifyToCanonicalVal(SExpr *E) { while (true) { if (auto *V = dyn_cast(E)) { if (V->kind() != Variable::VK_Let) return V; // Eliminate redundant variables, e.g. x = y, or x = 5, // but keep anything more complicated. if (til::ThreadSafetyTIL::isTrivial(V->definition())) { E = V->definition(); continue; } return V; } if (auto *Ph = dyn_cast(E)) { if (Ph->status() == Phi::PH_Incomplete) simplifyIncompleteArg(Ph); // Eliminate redundant Phi nodes. if (Ph->status() == Phi::PH_SingleVal) { E = Ph->values()[0]; continue; } } return E; } } // Trace the arguments of an incomplete Phi node to see if they have the same // canonical definition. If so, mark the Phi node as redundant. // getCanonicalVal() will recursively call simplifyIncompletePhi(). void til::simplifyIncompleteArg(til::Phi *Ph) { assert(Ph && Ph->status() == Phi::PH_Incomplete); // eliminate infinite recursion -- assume that this node is not redundant. Ph->setStatus(Phi::PH_MultiVal); SExpr *E0 = simplifyToCanonicalVal(Ph->values()[0]); for (unsigned i=1, n=Ph->values().size(); ivalues()[i]); if (Ei == Ph) continue; // Recursive reference to itself. Don't count. if (Ei != E0) { return; // Status is already set to MultiVal. } } Ph->setStatus(Phi::PH_SingleVal); } // Renumbers the arguments and instructions to have unique, sequential IDs. int BasicBlock::renumberInstrs(int ID) { for (auto *Arg : Args) Arg->setID(this, ID++); for (auto *Instr : Instrs) Instr->setID(this, ID++); TermInstr->setID(this, ID++); return ID; } // Sorts the CFGs blocks using a reverse post-order depth-first traversal. // Each block will be written into the Blocks array in order, and its BlockID // will be set to the index in the array. Sorting should start from the entry // block, and ID should be the total number of blocks. int BasicBlock::topologicalSort(SimpleArray& Blocks, int ID) { if (Visited) return ID; Visited = true; for (auto *Block : successors()) ID = Block->topologicalSort(Blocks, ID); // set ID and update block array in place. // We may lose pointers to unreachable blocks. assert(ID > 0); BlockID = --ID; Blocks[BlockID] = this; return ID; } // Performs a reverse topological traversal, starting from the exit block and // following back-edges. The dominator is serialized before any predecessors, // which guarantees that all blocks are serialized after their dominator and // before their post-dominator (because it's a reverse topological traversal). // ID should be initially set to 0. // // This sort assumes that (1) dominators have been computed, (2) there are no // critical edges, and (3) the entry block is reachable from the exit block // and no blocks are accessable via traversal of back-edges from the exit that // weren't accessable via forward edges from the entry. int BasicBlock::topologicalFinalSort(SimpleArray& Blocks, int ID) { // Visited is assumed to have been set by the topologicalSort. This pass // assumes !Visited means that we've visited this node before. if (!Visited) return ID; Visited = false; if (DominatorNode.Parent) ID = DominatorNode.Parent->topologicalFinalSort(Blocks, ID); for (auto *Pred : Predecessors) ID = Pred->topologicalFinalSort(Blocks, ID); assert(static_cast(ID) < Blocks.size()); BlockID = ID++; Blocks[BlockID] = this; return ID; } // Computes the immediate dominator of the current block. Assumes that all of // its predecessors have already computed their dominators. This is achieved // by visiting the nodes in topological order. void BasicBlock::computeDominator() { BasicBlock *Candidate = nullptr; // Walk backwards from each predecessor to find the common dominator node. for (auto *Pred : Predecessors) { // Skip back-edges if (Pred->BlockID >= BlockID) continue; // If we don't yet have a candidate for dominator yet, take this one. if (Candidate == nullptr) { Candidate = Pred; continue; } // Walk the alternate and current candidate back to find a common ancestor. auto *Alternate = Pred; while (Alternate != Candidate) { if (Candidate->BlockID > Alternate->BlockID) Candidate = Candidate->DominatorNode.Parent; else Alternate = Alternate->DominatorNode.Parent; } } DominatorNode.Parent = Candidate; DominatorNode.SizeOfSubTree = 1; } // Computes the immediate post-dominator of the current block. Assumes that all // of its successors have already computed their post-dominators. This is // achieved visiting the nodes in reverse topological order. void BasicBlock::computePostDominator() { BasicBlock *Candidate = nullptr; // Walk back from each predecessor to find the common post-dominator node. for (auto *Succ : successors()) { // Skip back-edges if (Succ->BlockID <= BlockID) continue; // If we don't yet have a candidate for post-dominator yet, take this one. if (Candidate == nullptr) { Candidate = Succ; continue; } // Walk the alternate and current candidate back to find a common ancestor. auto *Alternate = Succ; while (Alternate != Candidate) { if (Candidate->BlockID < Alternate->BlockID) Candidate = Candidate->PostDominatorNode.Parent; else Alternate = Alternate->PostDominatorNode.Parent; } } PostDominatorNode.Parent = Candidate; PostDominatorNode.SizeOfSubTree = 1; } // Renumber instructions in all blocks void SCFG::renumberInstrs() { int InstrID = 0; for (auto *Block : Blocks) InstrID = Block->renumberInstrs(InstrID); } static inline void computeNodeSize(BasicBlock *B, BasicBlock::TopologyNode BasicBlock::*TN) { BasicBlock::TopologyNode *N = &(B->*TN); if (N->Parent) { BasicBlock::TopologyNode *P = &(N->Parent->*TN); // Initially set ID relative to the (as yet uncomputed) parent ID N->NodeID = P->SizeOfSubTree; P->SizeOfSubTree += N->SizeOfSubTree; } } static inline void computeNodeID(BasicBlock *B, BasicBlock::TopologyNode BasicBlock::*TN) { BasicBlock::TopologyNode *N = &(B->*TN); if (N->Parent) { BasicBlock::TopologyNode *P = &(N->Parent->*TN); N->NodeID += P->NodeID; // Fix NodeIDs relative to starting node. } } // Normalizes a CFG. Normalization has a few major components: // 1) Removing unreachable blocks. // 2) Computing dominators and post-dominators // 3) Topologically sorting the blocks into the "Blocks" array. void SCFG::computeNormalForm() { // Topologically sort the blocks starting from the entry block. int NumUnreachableBlocks = Entry->topologicalSort(Blocks, Blocks.size()); if (NumUnreachableBlocks > 0) { // If there were unreachable blocks shift everything down, and delete them. for (size_t I = NumUnreachableBlocks, E = Blocks.size(); I < E; ++I) { size_t NI = I - NumUnreachableBlocks; Blocks[NI] = Blocks[I]; Blocks[NI]->BlockID = NI; // FIXME: clean up predecessor pointers to unreachable blocks? } Blocks.drop(NumUnreachableBlocks); } // Compute dominators. for (auto *Block : Blocks) Block->computeDominator(); // Once dominators have been computed, the final sort may be performed. int NumBlocks = Exit->topologicalFinalSort(Blocks, 0); assert(static_cast(NumBlocks) == Blocks.size()); (void) NumBlocks; // Renumber the instructions now that we have a final sort. renumberInstrs(); // Compute post-dominators and compute the sizes of each node in the // dominator tree. for (auto *Block : Blocks.reverse()) { Block->computePostDominator(); computeNodeSize(Block, &BasicBlock::DominatorNode); } // Compute the sizes of each node in the post-dominator tree and assign IDs in // the dominator tree. for (auto *Block : Blocks) { computeNodeID(Block, &BasicBlock::DominatorNode); computeNodeSize(Block, &BasicBlock::PostDominatorNode); } // Assign IDs in the post-dominator tree. for (auto *Block : Blocks.reverse()) { computeNodeID(Block, &BasicBlock::PostDominatorNode); } }