/// ScanReachableFromBlock - Mark all blocks reachable from Start. /// Returns the total number of blocks that were marked reachable. unsigned ScanReachableFromBlock(const CFGBlock &Start, llvm::BitVector &Reachable) { unsigned count = 0; llvm::SmallVector<const CFGBlock*, 32> WL; // Prep work queue Reachable.set(Start.getBlockID()); ++count; WL.push_back(&Start); // Find the reachable blocks from 'Start'. CFGBlock::FilterOptions FO; FO.IgnoreDefaultsWithCoveredEnums = 1; while (!WL.empty()) { const CFGBlock *item = WL.back(); WL.pop_back(); // Look at the successors and mark then reachable. for (CFGBlock::filtered_succ_iterator I= item->filtered_succ_start_end(FO); I.hasMore(); ++I) if (const CFGBlock *B = *I) { unsigned blockID = B->getBlockID(); if (!Reachable[blockID]) { Reachable.set(blockID); ++count; WL.push_back(B); } } } return count; }
void Dominator::printBlockSet(BlkSetTy &blkSet) { OS << "{ "; for (BlkSetTy::iterator I = blkSet.begin(), E = blkSet.end(); I != E; ++I) { CFGBlock *block = *I; if (I != blkSet.begin()) OS << ", "; OS << "B" << block->getBlockID(); } OS << " }"; }
void Dominator::printDomMap() { OS << "=== Dominators ===\n"; for (BlkToBlkSetTy::reverse_iterator I = DomMap.rbegin(), E = DomMap.rend(); I != E; ++I) { CFGBlock *cfgBlock = (*I).first; OS << " [ B" << cfgBlock->getBlockID(); if (cfgBlock == &cfg.getEntry()) OS << " (ENTRY)"; else if (cfgBlock == &cfg.getExit()) OS << " (EXIT)"; OS << " ]\n"; BlkSetTy &doms = (*I).second; OS << " Dominators (" << doms.size() << "): "; printBlockSet(doms); OS << "\n"; } OS << "\n"; }
void Dominator::printDominatorTree() { OS << "=== Dominator Tree ===\n"; for (CFG::iterator I = cfg.begin(), E = cfg.end(); I != E; ++I) { CFGBlock *cfgBlock = *I; OS << " [ B" << cfgBlock->getBlockID(); if (cfgBlock == &cfg.getEntry()) OS << " (ENTRY)"; else if (cfgBlock == &cfg.getExit()) OS << " (EXIT)"; OS << " ]\n"; OS << " Immediate dominator: "; if (ImmediateDomMap[cfgBlock]) OS << "B" << ImmediateDomMap[cfgBlock]->getBlockID(); OS << "\n"; BlkSetTy &children = ChildrenMap[cfgBlock]; OS << " Children (" << children.size() << "): "; printBlockSet(children); OS << "\n"; } OS << "\n"; }
/// \brief Check a function's CFG for thread-safety violations. /// /// We traverse the blocks in the CFG, compute the set of mutexes that are held /// at the end of each block, and issue warnings for thread safety violations. /// Each block in the CFG is traversed exactly once. void runThreadSafetyAnalysis(AnalysisContext &AC, ThreadSafetyHandler &Handler) { CFG *CFGraph = AC.getCFG(); if (!CFGraph) return; const NamedDecl *D = dyn_cast_or_null<NamedDecl>(AC.getDecl()); if (!D) return; // Ignore anonymous functions for now. if (D->getAttr<NoThreadSafetyAnalysisAttr>()) return; Lockset::Factory LocksetFactory; // FIXME: Swith to SmallVector? Otherwise improve performance impact? std::vector<Lockset> EntryLocksets(CFGraph->getNumBlockIDs(), LocksetFactory.getEmptyMap()); std::vector<Lockset> ExitLocksets(CFGraph->getNumBlockIDs(), LocksetFactory.getEmptyMap()); // 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. TopologicallySortedCFG SortedGraph(CFGraph); CFGBlockSet VisitedBlocks(CFGraph); if (!SortedGraph.empty() && D->hasAttrs()) { const CFGBlock *FirstBlock = *SortedGraph.begin(); Lockset &InitialLockset = EntryLocksets[FirstBlock->getBlockID()]; const AttrVec &ArgAttrs = D->getAttrs(); for(unsigned i = 0; i < ArgAttrs.size(); ++i) { Attr *Attr = ArgAttrs[i]; SourceLocation AttrLoc = Attr->getLocation(); if (SharedLocksRequiredAttr *SLRAttr = dyn_cast<SharedLocksRequiredAttr>(Attr)) { for (SharedLocksRequiredAttr::args_iterator SLRIter = SLRAttr->args_begin(), SLREnd = SLRAttr->args_end(); SLRIter != SLREnd; ++SLRIter) InitialLockset = addLock(Handler, LocksetFactory, InitialLockset, *SLRIter, D, LK_Shared, AttrLoc); } else if (ExclusiveLocksRequiredAttr *ELRAttr = dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) { for (ExclusiveLocksRequiredAttr::args_iterator ELRIter = ELRAttr->args_begin(), ELREnd = ELRAttr->args_end(); ELRIter != ELREnd; ++ELRIter) InitialLockset = addLock(Handler, LocksetFactory, InitialLockset, *ELRIter, D, LK_Exclusive, AttrLoc); } } } for (TopologicallySortedCFG::iterator I = SortedGraph.begin(), E = SortedGraph.end(); I!= E; ++I) { const CFGBlock *CurrBlock = *I; int CurrBlockID = CurrBlock->getBlockID(); VisitedBlocks.insert(CurrBlock); // Use the default initial lockset in case there are no predecessors. Lockset &Entryset = EntryLocksets[CurrBlockID]; Lockset &Exitset = ExitLocksets[CurrBlockID]; // Iterate through the predecessor blocks and warn if the lockset for all // predecessors is not the same. We take the entry lockset of the current // block to be the intersection of all previous locksets. // FIXME: By keeping the intersection, we may output more errors in future // for a lock which is not in the intersection, but was in the union. We // may want to also keep the union in future. As an example, let's say // the intersection contains Mutex L, and the union contains L and M. // Later we unlock M. At this point, we would output an error because we // never locked M; although the real error is probably that we forgot to // lock M on all code paths. Conversely, let's say that later we lock M. // In this case, we should compare against the intersection instead of the // union because the real error is probably that we forgot to unlock M on // all code paths. bool LocksetInitialized = false; for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(), PE = CurrBlock->pred_end(); PI != PE; ++PI) { // if *PI -> CurrBlock is a back edge if (*PI == 0 || !VisitedBlocks.alreadySet(*PI)) continue; int PrevBlockID = (*PI)->getBlockID(); if (!LocksetInitialized) { Entryset = ExitLocksets[PrevBlockID]; LocksetInitialized = true; } else { Entryset = intersectAndWarn(Handler, Entryset, ExitLocksets[PrevBlockID], LocksetFactory, LEK_LockedSomePredecessors); } } BuildLockset LocksetBuilder(Handler, Entryset, LocksetFactory); for (CFGBlock::const_iterator BI = CurrBlock->begin(), BE = CurrBlock->end(); BI != BE; ++BI) { if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&*BI)) LocksetBuilder.Visit(const_cast<Stmt*>(CfgStmt->getStmt())); } Exitset = LocksetBuilder.getLockset(); // For every back edge from CurrBlock (the end of the loop) to another block // (FirstLoopBlock) we need to check that the Lockset of Block is equal to // the one held at the beginning of FirstLoopBlock. We can look up the // Lockset held at the beginning of FirstLoopBlock in the EntryLockSets map. for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), SE = CurrBlock->succ_end(); SI != SE; ++SI) { // if CurrBlock -> *SI is *not* a back edge if (*SI == 0 || !VisitedBlocks.alreadySet(*SI)) continue; CFGBlock *FirstLoopBlock = *SI; Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()]; Lockset LoopEnd = ExitLocksets[CurrBlockID]; intersectAndWarn(Handler, LoopEnd, PreLoop, LocksetFactory, LEK_LockedSomeLoopIterations); } } Lockset InitialLockset = EntryLocksets[CFGraph->getEntry().getBlockID()]; Lockset FinalLockset = ExitLocksets[CFGraph->getExit().getBlockID()]; // FIXME: Should we call this function for all blocks which exit the function? intersectAndWarn(Handler, InitialLockset, FinalLockset, LocksetFactory, LEK_LockedAtEndOfFunction); }