| //===- CFGBackEdges.cpp - Finds back edges in Clang CFGs ------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include <stack> | |
| #include <utility> | |
| #include <vector> | |
| #include "clang/Analysis/CFG.h" | |
| #include "clang/Analysis/CFGBackEdges.h" | |
| #include "llvm/ADT/DenseMap.h" | |
| namespace clang { | |
| namespace { | |
| struct VisitClockTimes { | |
| // Timestamp for when the node was visited / discovered. | |
| int Pre = -1; | |
| // Timestamp for when we finished visiting a node's successors. | |
| int Post = -1; | |
| }; | |
| } // namespace | |
| // Returns true if the CFG contains any goto statements (direct or indirect). | |
| static bool hasGotoInCFG(const CFG &CFG) { | |
| for (const CFGBlock *Block : CFG) { | |
| const Stmt *Term = Block->getTerminatorStmt(); | |
| if (Term == nullptr) | |
| continue; | |
| if (isa<GotoStmt>(Term) || isa<IndirectGotoStmt>(Term)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| llvm::DenseMap<const CFGBlock *, const CFGBlock *> | |
| findCFGBackEdges(const CFG &CFG) { | |
| // Do a simple textbook DFS with pre and post numberings to find back edges. | |
| llvm::DenseMap<const CFGBlock *, const CFGBlock *> BackEdges; | |
| std::vector<VisitClockTimes> VisitState; | |
| VisitState.resize(CFG.getNumBlockIDs()); | |
| std::stack<std::pair<const CFGBlock *, CFGBlock::const_succ_iterator>> | |
| DFSStack; | |
| int Clock = 0; | |
| const CFGBlock &Entry = CFG.getEntry(); | |
| VisitState[Entry.getBlockID()].Pre = Clock++; | |
| DFSStack.push({&Entry, Entry.succ_begin()}); | |
| while (!DFSStack.empty()) { | |
| auto &[Block, SuccIt] = DFSStack.top(); | |
| if (SuccIt == Block->succ_end()) { | |
| VisitState[Block->getBlockID()].Post = Clock++; | |
| DFSStack.pop(); | |
| continue; | |
| } | |
| const CFGBlock::AdjacentBlock &AdjacentSucc = *SuccIt++; | |
| const CFGBlock *Succ = AdjacentSucc.getReachableBlock(); | |
| // Skip unreachable blocks. | |
| if (Succ == nullptr) | |
| continue; | |
| VisitClockTimes &SuccVisitState = VisitState[Succ->getBlockID()]; | |
| if (SuccVisitState.Pre != -1) { | |
| if (SuccVisitState.Post == -1) | |
| BackEdges.insert({Block, Succ}); | |
| } else { | |
| SuccVisitState.Pre = Clock++; | |
| DFSStack.push({Succ, Succ->succ_begin()}); | |
| } | |
| } | |
| return BackEdges; | |
| } | |
| // Returns a set of CFG blocks that is the source of a backedge and is not | |
| // tracked as part of a structured loop (with `CFGBlock::getLoopTarget`). | |
| llvm::SmallDenseSet<const CFGBlock *> | |
| findNonStructuredLoopBackedgeNodes(const CFG &CFG) { | |
| llvm::SmallDenseSet<const CFGBlock *> NonStructLoopBackedgeNodes; | |
| // We should only need this if the function has gotos. | |
| if (!hasGotoInCFG(CFG)) | |
| return NonStructLoopBackedgeNodes; | |
| llvm::DenseMap<const CFGBlock *, const CFGBlock *> Backedges = | |
| findCFGBackEdges(CFG); | |
| for (const auto &[From, To] : Backedges) { | |
| if (From->getLoopTarget() == nullptr) | |
| NonStructLoopBackedgeNodes.insert(From); | |
| } | |
| return NonStructLoopBackedgeNodes; | |
| } | |
| bool isBackedgeCFGNode( | |
| const CFGBlock &B, | |
| const llvm::SmallDenseSet<const CFGBlock *> &NonStructLoopBackedgeNodes) { | |
| return B.getLoopTarget() != nullptr || | |
| NonStructLoopBackedgeNodes.contains(&B); | |
| } | |
| } // namespace clang |