xref: /src/contrib/llvm-project/llvm/lib/Transforms/Instrumentation/BlockCoverageInference.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
17fa27ce4SDimitry Andric //===-- BlockCoverageInference.cpp - Minimal Execution Coverage -*- C++ -*-===//
27fa27ce4SDimitry Andric //
37fa27ce4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47fa27ce4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
57fa27ce4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67fa27ce4SDimitry Andric //
77fa27ce4SDimitry Andric //===----------------------------------------------------------------------===//
87fa27ce4SDimitry Andric //
97fa27ce4SDimitry Andric // Our algorithm works by first identifying a subset of nodes that must always
107fa27ce4SDimitry Andric // be instrumented. We call these nodes ambiguous because knowing the coverage
117fa27ce4SDimitry Andric // of all remaining nodes is not enough to infer their coverage status.
127fa27ce4SDimitry Andric //
137fa27ce4SDimitry Andric // In general a node v is ambiguous if there exists two entry-to-terminal paths
147fa27ce4SDimitry Andric // P_1 and P_2 such that:
157fa27ce4SDimitry Andric //   1. v not in P_1 but P_1 visits a predecessor of v, and
167fa27ce4SDimitry Andric //   2. v not in P_2 but P_2 visits a successor of v.
177fa27ce4SDimitry Andric //
187fa27ce4SDimitry Andric // If a node v is not ambiguous, then if condition 1 fails, we can infer v’s
197fa27ce4SDimitry Andric // coverage from the coverage of its predecessors, or if condition 2 fails, we
207fa27ce4SDimitry Andric // can infer v’s coverage from the coverage of its successors.
217fa27ce4SDimitry Andric //
227fa27ce4SDimitry Andric // Sadly, there are example CFGs where it is not possible to infer all nodes
237fa27ce4SDimitry Andric // from the ambiguous nodes alone. Our algorithm selects a minimum number of
247fa27ce4SDimitry Andric // extra nodes to add to the ambiguous nodes to form a valid instrumentation S.
257fa27ce4SDimitry Andric //
267fa27ce4SDimitry Andric // Details on this algorithm can be found in https://arxiv.org/abs/2208.13907
277fa27ce4SDimitry Andric //
287fa27ce4SDimitry Andric //===----------------------------------------------------------------------===//
297fa27ce4SDimitry Andric 
307fa27ce4SDimitry Andric #include "llvm/Transforms/Instrumentation/BlockCoverageInference.h"
317fa27ce4SDimitry Andric #include "llvm/ADT/DepthFirstIterator.h"
327fa27ce4SDimitry Andric #include "llvm/ADT/Statistic.h"
337fa27ce4SDimitry Andric #include "llvm/Support/CRC.h"
347fa27ce4SDimitry Andric #include "llvm/Support/Debug.h"
357fa27ce4SDimitry Andric #include "llvm/Support/GraphWriter.h"
367fa27ce4SDimitry Andric #include "llvm/Support/raw_ostream.h"
377fa27ce4SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h"
387fa27ce4SDimitry Andric 
397fa27ce4SDimitry Andric using namespace llvm;
407fa27ce4SDimitry Andric 
417fa27ce4SDimitry Andric #define DEBUG_TYPE "pgo-block-coverage"
427fa27ce4SDimitry Andric 
437fa27ce4SDimitry Andric STATISTIC(NumFunctions, "Number of total functions that BCI has processed");
447fa27ce4SDimitry Andric STATISTIC(NumIneligibleFunctions,
457fa27ce4SDimitry Andric           "Number of functions for which BCI cannot run on");
467fa27ce4SDimitry Andric STATISTIC(NumBlocks, "Number of total basic blocks that BCI has processed");
477fa27ce4SDimitry Andric STATISTIC(NumInstrumentedBlocks,
487fa27ce4SDimitry Andric           "Number of basic blocks instrumented for coverage");
497fa27ce4SDimitry Andric 
BlockCoverageInference(const Function & F,bool ForceInstrumentEntry)507fa27ce4SDimitry Andric BlockCoverageInference::BlockCoverageInference(const Function &F,
517fa27ce4SDimitry Andric                                                bool ForceInstrumentEntry)
527fa27ce4SDimitry Andric     : F(F), ForceInstrumentEntry(ForceInstrumentEntry) {
537fa27ce4SDimitry Andric   findDependencies();
547fa27ce4SDimitry Andric   assert(!ForceInstrumentEntry || shouldInstrumentBlock(F.getEntryBlock()));
557fa27ce4SDimitry Andric 
567fa27ce4SDimitry Andric   ++NumFunctions;
577fa27ce4SDimitry Andric   for (auto &BB : F) {
587fa27ce4SDimitry Andric     ++NumBlocks;
597fa27ce4SDimitry Andric     if (shouldInstrumentBlock(BB))
607fa27ce4SDimitry Andric       ++NumInstrumentedBlocks;
617fa27ce4SDimitry Andric   }
627fa27ce4SDimitry Andric }
637fa27ce4SDimitry Andric 
647fa27ce4SDimitry Andric BlockCoverageInference::BlockSet
getDependencies(const BasicBlock & BB) const657fa27ce4SDimitry Andric BlockCoverageInference::getDependencies(const BasicBlock &BB) const {
667fa27ce4SDimitry Andric   assert(BB.getParent() == &F);
677fa27ce4SDimitry Andric   BlockSet Dependencies;
687fa27ce4SDimitry Andric   auto It = PredecessorDependencies.find(&BB);
697fa27ce4SDimitry Andric   if (It != PredecessorDependencies.end())
707fa27ce4SDimitry Andric     Dependencies.set_union(It->second);
717fa27ce4SDimitry Andric   It = SuccessorDependencies.find(&BB);
727fa27ce4SDimitry Andric   if (It != SuccessorDependencies.end())
737fa27ce4SDimitry Andric     Dependencies.set_union(It->second);
747fa27ce4SDimitry Andric   return Dependencies;
757fa27ce4SDimitry Andric }
767fa27ce4SDimitry Andric 
getInstrumentedBlocksHash() const777fa27ce4SDimitry Andric uint64_t BlockCoverageInference::getInstrumentedBlocksHash() const {
787fa27ce4SDimitry Andric   JamCRC JC;
797fa27ce4SDimitry Andric   uint64_t Index = 0;
807fa27ce4SDimitry Andric   for (auto &BB : F) {
817fa27ce4SDimitry Andric     if (shouldInstrumentBlock(BB)) {
827fa27ce4SDimitry Andric       uint8_t Data[8];
837fa27ce4SDimitry Andric       support::endian::write64le(Data, Index);
847fa27ce4SDimitry Andric       JC.update(Data);
857fa27ce4SDimitry Andric     }
867fa27ce4SDimitry Andric     Index++;
877fa27ce4SDimitry Andric   }
887fa27ce4SDimitry Andric   return JC.getCRC();
897fa27ce4SDimitry Andric }
907fa27ce4SDimitry Andric 
shouldInstrumentBlock(const BasicBlock & BB) const917fa27ce4SDimitry Andric bool BlockCoverageInference::shouldInstrumentBlock(const BasicBlock &BB) const {
927fa27ce4SDimitry Andric   assert(BB.getParent() == &F);
937fa27ce4SDimitry Andric   auto It = PredecessorDependencies.find(&BB);
947fa27ce4SDimitry Andric   if (It != PredecessorDependencies.end() && It->second.size())
957fa27ce4SDimitry Andric     return false;
967fa27ce4SDimitry Andric   It = SuccessorDependencies.find(&BB);
977fa27ce4SDimitry Andric   if (It != SuccessorDependencies.end() && It->second.size())
987fa27ce4SDimitry Andric     return false;
997fa27ce4SDimitry Andric   return true;
1007fa27ce4SDimitry Andric }
1017fa27ce4SDimitry Andric 
findDependencies()1027fa27ce4SDimitry Andric void BlockCoverageInference::findDependencies() {
1037fa27ce4SDimitry Andric   assert(PredecessorDependencies.empty() && SuccessorDependencies.empty());
1047fa27ce4SDimitry Andric   // Empirical analysis shows that this algorithm finishes within 5 seconds for
1057fa27ce4SDimitry Andric   // functions with fewer than 1.5K blocks.
1067fa27ce4SDimitry Andric   if (F.hasFnAttribute(Attribute::NoReturn) || F.size() > 1500) {
1077fa27ce4SDimitry Andric     ++NumIneligibleFunctions;
1087fa27ce4SDimitry Andric     return;
1097fa27ce4SDimitry Andric   }
1107fa27ce4SDimitry Andric 
1117fa27ce4SDimitry Andric   SmallVector<const BasicBlock *, 4> TerminalBlocks;
1127fa27ce4SDimitry Andric   for (auto &BB : F)
1137fa27ce4SDimitry Andric     if (succ_empty(&BB))
1147fa27ce4SDimitry Andric       TerminalBlocks.push_back(&BB);
1157fa27ce4SDimitry Andric 
1167fa27ce4SDimitry Andric   // Traverse the CFG backwards from the terminal blocks to make sure every
1177fa27ce4SDimitry Andric   // block can reach some terminal block. Otherwise this algorithm will not work
1187fa27ce4SDimitry Andric   // and we must fall back to instrumenting every block.
1197fa27ce4SDimitry Andric   df_iterator_default_set<const BasicBlock *> Visited;
1207fa27ce4SDimitry Andric   for (auto *BB : TerminalBlocks)
1217fa27ce4SDimitry Andric     for (auto *N : inverse_depth_first_ext(BB, Visited))
1227fa27ce4SDimitry Andric       (void)N;
1237fa27ce4SDimitry Andric   if (F.size() != Visited.size()) {
1247fa27ce4SDimitry Andric     ++NumIneligibleFunctions;
1257fa27ce4SDimitry Andric     return;
1267fa27ce4SDimitry Andric   }
1277fa27ce4SDimitry Andric 
1287fa27ce4SDimitry Andric   // The current implementation for computing `PredecessorDependencies` and
1297fa27ce4SDimitry Andric   // `SuccessorDependencies` runs in quadratic time with respect to the number
1307fa27ce4SDimitry Andric   // of basic blocks. While we do have a more complicated linear time algorithm
1317fa27ce4SDimitry Andric   // in https://arxiv.org/abs/2208.13907 we do not know if it will give a
1327fa27ce4SDimitry Andric   // significant speedup in practice given that most functions tend to be
1337fa27ce4SDimitry Andric   // relatively small in size for intended use cases.
1347fa27ce4SDimitry Andric   auto &EntryBlock = F.getEntryBlock();
1357fa27ce4SDimitry Andric   for (auto &BB : F) {
1367fa27ce4SDimitry Andric     // The set of blocks that are reachable while avoiding BB.
1377fa27ce4SDimitry Andric     BlockSet ReachableFromEntry, ReachableFromTerminal;
1387fa27ce4SDimitry Andric     getReachableAvoiding(EntryBlock, BB, /*IsForward=*/true,
1397fa27ce4SDimitry Andric                          ReachableFromEntry);
1407fa27ce4SDimitry Andric     for (auto *TerminalBlock : TerminalBlocks)
1417fa27ce4SDimitry Andric       getReachableAvoiding(*TerminalBlock, BB, /*IsForward=*/false,
1427fa27ce4SDimitry Andric                            ReachableFromTerminal);
1437fa27ce4SDimitry Andric 
1447fa27ce4SDimitry Andric     auto Preds = predecessors(&BB);
1457fa27ce4SDimitry Andric     bool HasSuperReachablePred = llvm::any_of(Preds, [&](auto *Pred) {
1467fa27ce4SDimitry Andric       return ReachableFromEntry.count(Pred) &&
1477fa27ce4SDimitry Andric              ReachableFromTerminal.count(Pred);
1487fa27ce4SDimitry Andric     });
1497fa27ce4SDimitry Andric     if (!HasSuperReachablePred)
1507fa27ce4SDimitry Andric       for (auto *Pred : Preds)
1517fa27ce4SDimitry Andric         if (ReachableFromEntry.count(Pred))
1527fa27ce4SDimitry Andric           PredecessorDependencies[&BB].insert(Pred);
1537fa27ce4SDimitry Andric 
1547fa27ce4SDimitry Andric     auto Succs = successors(&BB);
1557fa27ce4SDimitry Andric     bool HasSuperReachableSucc = llvm::any_of(Succs, [&](auto *Succ) {
1567fa27ce4SDimitry Andric       return ReachableFromEntry.count(Succ) &&
1577fa27ce4SDimitry Andric              ReachableFromTerminal.count(Succ);
1587fa27ce4SDimitry Andric     });
1597fa27ce4SDimitry Andric     if (!HasSuperReachableSucc)
1607fa27ce4SDimitry Andric       for (auto *Succ : Succs)
1617fa27ce4SDimitry Andric         if (ReachableFromTerminal.count(Succ))
1627fa27ce4SDimitry Andric           SuccessorDependencies[&BB].insert(Succ);
1637fa27ce4SDimitry Andric   }
1647fa27ce4SDimitry Andric 
1657fa27ce4SDimitry Andric   if (ForceInstrumentEntry) {
1667fa27ce4SDimitry Andric     // Force the entry block to be instrumented by clearing the blocks it can
1677fa27ce4SDimitry Andric     // infer coverage from.
1687fa27ce4SDimitry Andric     PredecessorDependencies[&EntryBlock].clear();
1697fa27ce4SDimitry Andric     SuccessorDependencies[&EntryBlock].clear();
1707fa27ce4SDimitry Andric   }
1717fa27ce4SDimitry Andric 
1727fa27ce4SDimitry Andric   // Construct a graph where blocks are connected if there is a mutual
1737fa27ce4SDimitry Andric   // dependency between them. This graph has a special property that it contains
1747fa27ce4SDimitry Andric   // only paths.
1757fa27ce4SDimitry Andric   DenseMap<const BasicBlock *, BlockSet> AdjacencyList;
1767fa27ce4SDimitry Andric   for (auto &BB : F) {
1777fa27ce4SDimitry Andric     for (auto *Succ : successors(&BB)) {
1787fa27ce4SDimitry Andric       if (SuccessorDependencies[&BB].count(Succ) &&
1797fa27ce4SDimitry Andric           PredecessorDependencies[Succ].count(&BB)) {
1807fa27ce4SDimitry Andric         AdjacencyList[&BB].insert(Succ);
1817fa27ce4SDimitry Andric         AdjacencyList[Succ].insert(&BB);
1827fa27ce4SDimitry Andric       }
1837fa27ce4SDimitry Andric     }
1847fa27ce4SDimitry Andric   }
1857fa27ce4SDimitry Andric 
1867fa27ce4SDimitry Andric   // Given a path with at least one node, return the next node on the path.
1877fa27ce4SDimitry Andric   auto getNextOnPath = [&](BlockSet &Path) -> const BasicBlock * {
1887fa27ce4SDimitry Andric     assert(Path.size());
1897fa27ce4SDimitry Andric     auto &Neighbors = AdjacencyList[Path.back()];
1907fa27ce4SDimitry Andric     if (Path.size() == 1) {
1917fa27ce4SDimitry Andric       // This is the first node on the path, return its neighbor.
1927fa27ce4SDimitry Andric       assert(Neighbors.size() == 1);
1937fa27ce4SDimitry Andric       return Neighbors.front();
1947fa27ce4SDimitry Andric     } else if (Neighbors.size() == 2) {
1957fa27ce4SDimitry Andric       // This is the middle of the path, find the neighbor that is not on the
1967fa27ce4SDimitry Andric       // path already.
1977fa27ce4SDimitry Andric       assert(Path.size() >= 2);
1987fa27ce4SDimitry Andric       return Path.count(Neighbors[0]) ? Neighbors[1] : Neighbors[0];
1997fa27ce4SDimitry Andric     }
2007fa27ce4SDimitry Andric     // This is the end of the path.
2017fa27ce4SDimitry Andric     assert(Neighbors.size() == 1);
2027fa27ce4SDimitry Andric     return nullptr;
2037fa27ce4SDimitry Andric   };
2047fa27ce4SDimitry Andric 
2057fa27ce4SDimitry Andric   // Remove all cycles in the inferencing graph.
2067fa27ce4SDimitry Andric   for (auto &BB : F) {
2077fa27ce4SDimitry Andric     if (AdjacencyList[&BB].size() == 1) {
2087fa27ce4SDimitry Andric       // We found the head of some path.
2097fa27ce4SDimitry Andric       BlockSet Path;
2107fa27ce4SDimitry Andric       Path.insert(&BB);
2117fa27ce4SDimitry Andric       while (const BasicBlock *Next = getNextOnPath(Path))
2127fa27ce4SDimitry Andric         Path.insert(Next);
2137fa27ce4SDimitry Andric       LLVM_DEBUG(dbgs() << "Found path: " << getBlockNames(Path) << "\n");
2147fa27ce4SDimitry Andric 
2157fa27ce4SDimitry Andric       // Remove these nodes from the graph so we don't discover this path again.
2167fa27ce4SDimitry Andric       for (auto *BB : Path)
2177fa27ce4SDimitry Andric         AdjacencyList[BB].clear();
2187fa27ce4SDimitry Andric 
2197fa27ce4SDimitry Andric       // Finally, remove the cycles.
2207fa27ce4SDimitry Andric       if (PredecessorDependencies[Path.front()].size()) {
2217fa27ce4SDimitry Andric         for (auto *BB : Path)
2227fa27ce4SDimitry Andric           if (BB != Path.back())
2237fa27ce4SDimitry Andric             SuccessorDependencies[BB].clear();
2247fa27ce4SDimitry Andric       } else {
2257fa27ce4SDimitry Andric         for (auto *BB : Path)
2267fa27ce4SDimitry Andric           if (BB != Path.front())
2277fa27ce4SDimitry Andric             PredecessorDependencies[BB].clear();
2287fa27ce4SDimitry Andric       }
2297fa27ce4SDimitry Andric     }
2307fa27ce4SDimitry Andric   }
2317fa27ce4SDimitry Andric   LLVM_DEBUG(dump(dbgs()));
2327fa27ce4SDimitry Andric }
2337fa27ce4SDimitry Andric 
getReachableAvoiding(const BasicBlock & Start,const BasicBlock & Avoid,bool IsForward,BlockSet & Reachable) const2347fa27ce4SDimitry Andric void BlockCoverageInference::getReachableAvoiding(const BasicBlock &Start,
2357fa27ce4SDimitry Andric                                                   const BasicBlock &Avoid,
2367fa27ce4SDimitry Andric                                                   bool IsForward,
2377fa27ce4SDimitry Andric                                                   BlockSet &Reachable) const {
2387fa27ce4SDimitry Andric   df_iterator_default_set<const BasicBlock *> Visited;
2397fa27ce4SDimitry Andric   Visited.insert(&Avoid);
2407fa27ce4SDimitry Andric   if (IsForward) {
2417fa27ce4SDimitry Andric     auto Range = depth_first_ext(&Start, Visited);
2427fa27ce4SDimitry Andric     Reachable.insert(Range.begin(), Range.end());
2437fa27ce4SDimitry Andric   } else {
2447fa27ce4SDimitry Andric     auto Range = inverse_depth_first_ext(&Start, Visited);
2457fa27ce4SDimitry Andric     Reachable.insert(Range.begin(), Range.end());
2467fa27ce4SDimitry Andric   }
2477fa27ce4SDimitry Andric }
2487fa27ce4SDimitry Andric 
2497fa27ce4SDimitry Andric namespace llvm {
2507fa27ce4SDimitry Andric class DotFuncBCIInfo {
2517fa27ce4SDimitry Andric private:
2527fa27ce4SDimitry Andric   const BlockCoverageInference *BCI;
2537fa27ce4SDimitry Andric   const DenseMap<const BasicBlock *, bool> *Coverage;
2547fa27ce4SDimitry Andric 
2557fa27ce4SDimitry Andric public:
DotFuncBCIInfo(const BlockCoverageInference * BCI,const DenseMap<const BasicBlock *,bool> * Coverage)2567fa27ce4SDimitry Andric   DotFuncBCIInfo(const BlockCoverageInference *BCI,
2577fa27ce4SDimitry Andric                  const DenseMap<const BasicBlock *, bool> *Coverage)
2587fa27ce4SDimitry Andric       : BCI(BCI), Coverage(Coverage) {}
2597fa27ce4SDimitry Andric 
getFunction()2607fa27ce4SDimitry Andric   const Function &getFunction() { return BCI->F; }
2617fa27ce4SDimitry Andric 
isInstrumented(const BasicBlock * BB) const2627fa27ce4SDimitry Andric   bool isInstrumented(const BasicBlock *BB) const {
2637fa27ce4SDimitry Andric     return BCI->shouldInstrumentBlock(*BB);
2647fa27ce4SDimitry Andric   }
2657fa27ce4SDimitry Andric 
isCovered(const BasicBlock * BB) const2667fa27ce4SDimitry Andric   bool isCovered(const BasicBlock *BB) const {
2677fa27ce4SDimitry Andric     return Coverage && Coverage->lookup(BB);
2687fa27ce4SDimitry Andric   }
2697fa27ce4SDimitry Andric 
isDependent(const BasicBlock * Src,const BasicBlock * Dest) const2707fa27ce4SDimitry Andric   bool isDependent(const BasicBlock *Src, const BasicBlock *Dest) const {
2717fa27ce4SDimitry Andric     return BCI->getDependencies(*Src).count(Dest);
2727fa27ce4SDimitry Andric   }
2737fa27ce4SDimitry Andric };
2747fa27ce4SDimitry Andric 
2757fa27ce4SDimitry Andric template <>
2767fa27ce4SDimitry Andric struct GraphTraits<DotFuncBCIInfo *> : public GraphTraits<const BasicBlock *> {
getEntryNodellvm::GraphTraits2777fa27ce4SDimitry Andric   static NodeRef getEntryNode(DotFuncBCIInfo *Info) {
2787fa27ce4SDimitry Andric     return &(Info->getFunction().getEntryBlock());
2797fa27ce4SDimitry Andric   }
2807fa27ce4SDimitry Andric 
2817fa27ce4SDimitry Andric   // nodes_iterator/begin/end - Allow iteration over all nodes in the graph
2827fa27ce4SDimitry Andric   using nodes_iterator = pointer_iterator<Function::const_iterator>;
2837fa27ce4SDimitry Andric 
nodes_beginllvm::GraphTraits2847fa27ce4SDimitry Andric   static nodes_iterator nodes_begin(DotFuncBCIInfo *Info) {
2857fa27ce4SDimitry Andric     return nodes_iterator(Info->getFunction().begin());
2867fa27ce4SDimitry Andric   }
2877fa27ce4SDimitry Andric 
nodes_endllvm::GraphTraits2887fa27ce4SDimitry Andric   static nodes_iterator nodes_end(DotFuncBCIInfo *Info) {
2897fa27ce4SDimitry Andric     return nodes_iterator(Info->getFunction().end());
2907fa27ce4SDimitry Andric   }
2917fa27ce4SDimitry Andric 
sizellvm::GraphTraits2927fa27ce4SDimitry Andric   static size_t size(DotFuncBCIInfo *Info) {
2937fa27ce4SDimitry Andric     return Info->getFunction().size();
2947fa27ce4SDimitry Andric   }
2957fa27ce4SDimitry Andric };
2967fa27ce4SDimitry Andric 
2977fa27ce4SDimitry Andric template <>
2987fa27ce4SDimitry Andric struct DOTGraphTraits<DotFuncBCIInfo *> : public DefaultDOTGraphTraits {
2997fa27ce4SDimitry Andric 
DOTGraphTraitsllvm::DOTGraphTraits3007fa27ce4SDimitry Andric   DOTGraphTraits(bool IsSimple = false) : DefaultDOTGraphTraits(IsSimple) {}
3017fa27ce4SDimitry Andric 
getGraphNamellvm::DOTGraphTraits3027fa27ce4SDimitry Andric   static std::string getGraphName(DotFuncBCIInfo *Info) {
3037fa27ce4SDimitry Andric     return "BCI CFG for " + Info->getFunction().getName().str();
3047fa27ce4SDimitry Andric   }
3057fa27ce4SDimitry Andric 
getNodeLabelllvm::DOTGraphTraits3067fa27ce4SDimitry Andric   std::string getNodeLabel(const BasicBlock *Node, DotFuncBCIInfo *Info) {
3077fa27ce4SDimitry Andric     return Node->getName().str();
3087fa27ce4SDimitry Andric   }
3097fa27ce4SDimitry Andric 
getEdgeAttributesllvm::DOTGraphTraits3107fa27ce4SDimitry Andric   std::string getEdgeAttributes(const BasicBlock *Src, const_succ_iterator I,
3117fa27ce4SDimitry Andric                                 DotFuncBCIInfo *Info) {
3127fa27ce4SDimitry Andric     const BasicBlock *Dest = *I;
3137fa27ce4SDimitry Andric     if (Info->isDependent(Src, Dest))
3147fa27ce4SDimitry Andric       return "color=red";
3157fa27ce4SDimitry Andric     if (Info->isDependent(Dest, Src))
3167fa27ce4SDimitry Andric       return "color=blue";
3177fa27ce4SDimitry Andric     return "";
3187fa27ce4SDimitry Andric   }
3197fa27ce4SDimitry Andric 
getNodeAttributesllvm::DOTGraphTraits3207fa27ce4SDimitry Andric   std::string getNodeAttributes(const BasicBlock *Node, DotFuncBCIInfo *Info) {
3217fa27ce4SDimitry Andric     std::string Result;
3227fa27ce4SDimitry Andric     if (Info->isInstrumented(Node))
3237fa27ce4SDimitry Andric       Result += "style=filled,fillcolor=gray";
3247fa27ce4SDimitry Andric     if (Info->isCovered(Node))
3257fa27ce4SDimitry Andric       Result += std::string(Result.empty() ? "" : ",") + "color=red";
3267fa27ce4SDimitry Andric     return Result;
3277fa27ce4SDimitry Andric   }
3287fa27ce4SDimitry Andric };
3297fa27ce4SDimitry Andric 
3307fa27ce4SDimitry Andric } // namespace llvm
3317fa27ce4SDimitry Andric 
viewBlockCoverageGraph(const DenseMap<const BasicBlock *,bool> * Coverage) const3327fa27ce4SDimitry Andric void BlockCoverageInference::viewBlockCoverageGraph(
3337fa27ce4SDimitry Andric     const DenseMap<const BasicBlock *, bool> *Coverage) const {
3347fa27ce4SDimitry Andric   DotFuncBCIInfo Info(this, Coverage);
3357fa27ce4SDimitry Andric   WriteGraph(&Info, "BCI", false,
3367fa27ce4SDimitry Andric              "Block Coverage Inference for " + F.getName());
3377fa27ce4SDimitry Andric }
3387fa27ce4SDimitry Andric 
dump(raw_ostream & OS) const3397fa27ce4SDimitry Andric void BlockCoverageInference::dump(raw_ostream &OS) const {
3407fa27ce4SDimitry Andric   OS << "Minimal block coverage for function \'" << F.getName()
3417fa27ce4SDimitry Andric      << "\' (Instrumented=*)\n";
3427fa27ce4SDimitry Andric   for (auto &BB : F) {
3437fa27ce4SDimitry Andric     OS << (shouldInstrumentBlock(BB) ? "* " : "  ") << BB.getName() << "\n";
3447fa27ce4SDimitry Andric     auto It = PredecessorDependencies.find(&BB);
3457fa27ce4SDimitry Andric     if (It != PredecessorDependencies.end() && It->second.size())
3467fa27ce4SDimitry Andric       OS << "    PredDeps = " << getBlockNames(It->second) << "\n";
3477fa27ce4SDimitry Andric     It = SuccessorDependencies.find(&BB);
3487fa27ce4SDimitry Andric     if (It != SuccessorDependencies.end() && It->second.size())
3497fa27ce4SDimitry Andric       OS << "    SuccDeps = " << getBlockNames(It->second) << "\n";
3507fa27ce4SDimitry Andric   }
3517fa27ce4SDimitry Andric   OS << "  Instrumented Blocks Hash = 0x"
3527fa27ce4SDimitry Andric      << Twine::utohexstr(getInstrumentedBlocksHash()) << "\n";
3537fa27ce4SDimitry Andric }
3547fa27ce4SDimitry Andric 
3557fa27ce4SDimitry Andric std::string
getBlockNames(ArrayRef<const BasicBlock * > BBs)3567fa27ce4SDimitry Andric BlockCoverageInference::getBlockNames(ArrayRef<const BasicBlock *> BBs) {
3577fa27ce4SDimitry Andric   std::string Result;
3587fa27ce4SDimitry Andric   raw_string_ostream OS(Result);
3597fa27ce4SDimitry Andric   OS << "[";
3607fa27ce4SDimitry Andric   if (!BBs.empty()) {
3617fa27ce4SDimitry Andric     OS << BBs.front()->getName();
3627fa27ce4SDimitry Andric     BBs = BBs.drop_front();
3637fa27ce4SDimitry Andric   }
3647fa27ce4SDimitry Andric   for (auto *BB : BBs)
3657fa27ce4SDimitry Andric     OS << ", " << BB->getName();
3667fa27ce4SDimitry Andric   OS << "]";
3677fa27ce4SDimitry Andric   return OS.str();
3687fa27ce4SDimitry Andric }
369