xref: /freebsd-src/contrib/llvm-project/llvm/lib/Transforms/Instrumentation/BlockCoverageInference.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
1*06c3fb27SDimitry Andric //===-- BlockCoverageInference.cpp - Minimal Execution Coverage -*- C++ -*-===//
2*06c3fb27SDimitry Andric //
3*06c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*06c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*06c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*06c3fb27SDimitry Andric //
7*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
8*06c3fb27SDimitry Andric //
9*06c3fb27SDimitry Andric // Our algorithm works by first identifying a subset of nodes that must always
10*06c3fb27SDimitry Andric // be instrumented. We call these nodes ambiguous because knowing the coverage
11*06c3fb27SDimitry Andric // of all remaining nodes is not enough to infer their coverage status.
12*06c3fb27SDimitry Andric //
13*06c3fb27SDimitry Andric // In general a node v is ambiguous if there exists two entry-to-terminal paths
14*06c3fb27SDimitry Andric // P_1 and P_2 such that:
15*06c3fb27SDimitry Andric //   1. v not in P_1 but P_1 visits a predecessor of v, and
16*06c3fb27SDimitry Andric //   2. v not in P_2 but P_2 visits a successor of v.
17*06c3fb27SDimitry Andric //
18*06c3fb27SDimitry Andric // If a node v is not ambiguous, then if condition 1 fails, we can infer v’s
19*06c3fb27SDimitry Andric // coverage from the coverage of its predecessors, or if condition 2 fails, we
20*06c3fb27SDimitry Andric // can infer v’s coverage from the coverage of its successors.
21*06c3fb27SDimitry Andric //
22*06c3fb27SDimitry Andric // Sadly, there are example CFGs where it is not possible to infer all nodes
23*06c3fb27SDimitry Andric // from the ambiguous nodes alone. Our algorithm selects a minimum number of
24*06c3fb27SDimitry Andric // extra nodes to add to the ambiguous nodes to form a valid instrumentation S.
25*06c3fb27SDimitry Andric //
26*06c3fb27SDimitry Andric // Details on this algorithm can be found in https://arxiv.org/abs/2208.13907
27*06c3fb27SDimitry Andric //
28*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
29*06c3fb27SDimitry Andric 
30*06c3fb27SDimitry Andric #include "llvm/Transforms/Instrumentation/BlockCoverageInference.h"
31*06c3fb27SDimitry Andric #include "llvm/ADT/DepthFirstIterator.h"
32*06c3fb27SDimitry Andric #include "llvm/ADT/Statistic.h"
33*06c3fb27SDimitry Andric #include "llvm/Support/CRC.h"
34*06c3fb27SDimitry Andric #include "llvm/Support/Debug.h"
35*06c3fb27SDimitry Andric #include "llvm/Support/GraphWriter.h"
36*06c3fb27SDimitry Andric #include "llvm/Support/raw_ostream.h"
37*06c3fb27SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h"
38*06c3fb27SDimitry Andric 
39*06c3fb27SDimitry Andric using namespace llvm;
40*06c3fb27SDimitry Andric 
41*06c3fb27SDimitry Andric #define DEBUG_TYPE "pgo-block-coverage"
42*06c3fb27SDimitry Andric 
43*06c3fb27SDimitry Andric STATISTIC(NumFunctions, "Number of total functions that BCI has processed");
44*06c3fb27SDimitry Andric STATISTIC(NumIneligibleFunctions,
45*06c3fb27SDimitry Andric           "Number of functions for which BCI cannot run on");
46*06c3fb27SDimitry Andric STATISTIC(NumBlocks, "Number of total basic blocks that BCI has processed");
47*06c3fb27SDimitry Andric STATISTIC(NumInstrumentedBlocks,
48*06c3fb27SDimitry Andric           "Number of basic blocks instrumented for coverage");
49*06c3fb27SDimitry Andric 
BlockCoverageInference(const Function & F,bool ForceInstrumentEntry)50*06c3fb27SDimitry Andric BlockCoverageInference::BlockCoverageInference(const Function &F,
51*06c3fb27SDimitry Andric                                                bool ForceInstrumentEntry)
52*06c3fb27SDimitry Andric     : F(F), ForceInstrumentEntry(ForceInstrumentEntry) {
53*06c3fb27SDimitry Andric   findDependencies();
54*06c3fb27SDimitry Andric   assert(!ForceInstrumentEntry || shouldInstrumentBlock(F.getEntryBlock()));
55*06c3fb27SDimitry Andric 
56*06c3fb27SDimitry Andric   ++NumFunctions;
57*06c3fb27SDimitry Andric   for (auto &BB : F) {
58*06c3fb27SDimitry Andric     ++NumBlocks;
59*06c3fb27SDimitry Andric     if (shouldInstrumentBlock(BB))
60*06c3fb27SDimitry Andric       ++NumInstrumentedBlocks;
61*06c3fb27SDimitry Andric   }
62*06c3fb27SDimitry Andric }
63*06c3fb27SDimitry Andric 
64*06c3fb27SDimitry Andric BlockCoverageInference::BlockSet
getDependencies(const BasicBlock & BB) const65*06c3fb27SDimitry Andric BlockCoverageInference::getDependencies(const BasicBlock &BB) const {
66*06c3fb27SDimitry Andric   assert(BB.getParent() == &F);
67*06c3fb27SDimitry Andric   BlockSet Dependencies;
68*06c3fb27SDimitry Andric   auto It = PredecessorDependencies.find(&BB);
69*06c3fb27SDimitry Andric   if (It != PredecessorDependencies.end())
70*06c3fb27SDimitry Andric     Dependencies.set_union(It->second);
71*06c3fb27SDimitry Andric   It = SuccessorDependencies.find(&BB);
72*06c3fb27SDimitry Andric   if (It != SuccessorDependencies.end())
73*06c3fb27SDimitry Andric     Dependencies.set_union(It->second);
74*06c3fb27SDimitry Andric   return Dependencies;
75*06c3fb27SDimitry Andric }
76*06c3fb27SDimitry Andric 
getInstrumentedBlocksHash() const77*06c3fb27SDimitry Andric uint64_t BlockCoverageInference::getInstrumentedBlocksHash() const {
78*06c3fb27SDimitry Andric   JamCRC JC;
79*06c3fb27SDimitry Andric   uint64_t Index = 0;
80*06c3fb27SDimitry Andric   for (auto &BB : F) {
81*06c3fb27SDimitry Andric     if (shouldInstrumentBlock(BB)) {
82*06c3fb27SDimitry Andric       uint8_t Data[8];
83*06c3fb27SDimitry Andric       support::endian::write64le(Data, Index);
84*06c3fb27SDimitry Andric       JC.update(Data);
85*06c3fb27SDimitry Andric     }
86*06c3fb27SDimitry Andric     Index++;
87*06c3fb27SDimitry Andric   }
88*06c3fb27SDimitry Andric   return JC.getCRC();
89*06c3fb27SDimitry Andric }
90*06c3fb27SDimitry Andric 
shouldInstrumentBlock(const BasicBlock & BB) const91*06c3fb27SDimitry Andric bool BlockCoverageInference::shouldInstrumentBlock(const BasicBlock &BB) const {
92*06c3fb27SDimitry Andric   assert(BB.getParent() == &F);
93*06c3fb27SDimitry Andric   auto It = PredecessorDependencies.find(&BB);
94*06c3fb27SDimitry Andric   if (It != PredecessorDependencies.end() && It->second.size())
95*06c3fb27SDimitry Andric     return false;
96*06c3fb27SDimitry Andric   It = SuccessorDependencies.find(&BB);
97*06c3fb27SDimitry Andric   if (It != SuccessorDependencies.end() && It->second.size())
98*06c3fb27SDimitry Andric     return false;
99*06c3fb27SDimitry Andric   return true;
100*06c3fb27SDimitry Andric }
101*06c3fb27SDimitry Andric 
findDependencies()102*06c3fb27SDimitry Andric void BlockCoverageInference::findDependencies() {
103*06c3fb27SDimitry Andric   assert(PredecessorDependencies.empty() && SuccessorDependencies.empty());
104*06c3fb27SDimitry Andric   // Empirical analysis shows that this algorithm finishes within 5 seconds for
105*06c3fb27SDimitry Andric   // functions with fewer than 1.5K blocks.
106*06c3fb27SDimitry Andric   if (F.hasFnAttribute(Attribute::NoReturn) || F.size() > 1500) {
107*06c3fb27SDimitry Andric     ++NumIneligibleFunctions;
108*06c3fb27SDimitry Andric     return;
109*06c3fb27SDimitry Andric   }
110*06c3fb27SDimitry Andric 
111*06c3fb27SDimitry Andric   SmallVector<const BasicBlock *, 4> TerminalBlocks;
112*06c3fb27SDimitry Andric   for (auto &BB : F)
113*06c3fb27SDimitry Andric     if (succ_empty(&BB))
114*06c3fb27SDimitry Andric       TerminalBlocks.push_back(&BB);
115*06c3fb27SDimitry Andric 
116*06c3fb27SDimitry Andric   // Traverse the CFG backwards from the terminal blocks to make sure every
117*06c3fb27SDimitry Andric   // block can reach some terminal block. Otherwise this algorithm will not work
118*06c3fb27SDimitry Andric   // and we must fall back to instrumenting every block.
119*06c3fb27SDimitry Andric   df_iterator_default_set<const BasicBlock *> Visited;
120*06c3fb27SDimitry Andric   for (auto *BB : TerminalBlocks)
121*06c3fb27SDimitry Andric     for (auto *N : inverse_depth_first_ext(BB, Visited))
122*06c3fb27SDimitry Andric       (void)N;
123*06c3fb27SDimitry Andric   if (F.size() != Visited.size()) {
124*06c3fb27SDimitry Andric     ++NumIneligibleFunctions;
125*06c3fb27SDimitry Andric     return;
126*06c3fb27SDimitry Andric   }
127*06c3fb27SDimitry Andric 
128*06c3fb27SDimitry Andric   // The current implementation for computing `PredecessorDependencies` and
129*06c3fb27SDimitry Andric   // `SuccessorDependencies` runs in quadratic time with respect to the number
130*06c3fb27SDimitry Andric   // of basic blocks. While we do have a more complicated linear time algorithm
131*06c3fb27SDimitry Andric   // in https://arxiv.org/abs/2208.13907 we do not know if it will give a
132*06c3fb27SDimitry Andric   // significant speedup in practice given that most functions tend to be
133*06c3fb27SDimitry Andric   // relatively small in size for intended use cases.
134*06c3fb27SDimitry Andric   auto &EntryBlock = F.getEntryBlock();
135*06c3fb27SDimitry Andric   for (auto &BB : F) {
136*06c3fb27SDimitry Andric     // The set of blocks that are reachable while avoiding BB.
137*06c3fb27SDimitry Andric     BlockSet ReachableFromEntry, ReachableFromTerminal;
138*06c3fb27SDimitry Andric     getReachableAvoiding(EntryBlock, BB, /*IsForward=*/true,
139*06c3fb27SDimitry Andric                          ReachableFromEntry);
140*06c3fb27SDimitry Andric     for (auto *TerminalBlock : TerminalBlocks)
141*06c3fb27SDimitry Andric       getReachableAvoiding(*TerminalBlock, BB, /*IsForward=*/false,
142*06c3fb27SDimitry Andric                            ReachableFromTerminal);
143*06c3fb27SDimitry Andric 
144*06c3fb27SDimitry Andric     auto Preds = predecessors(&BB);
145*06c3fb27SDimitry Andric     bool HasSuperReachablePred = llvm::any_of(Preds, [&](auto *Pred) {
146*06c3fb27SDimitry Andric       return ReachableFromEntry.count(Pred) &&
147*06c3fb27SDimitry Andric              ReachableFromTerminal.count(Pred);
148*06c3fb27SDimitry Andric     });
149*06c3fb27SDimitry Andric     if (!HasSuperReachablePred)
150*06c3fb27SDimitry Andric       for (auto *Pred : Preds)
151*06c3fb27SDimitry Andric         if (ReachableFromEntry.count(Pred))
152*06c3fb27SDimitry Andric           PredecessorDependencies[&BB].insert(Pred);
153*06c3fb27SDimitry Andric 
154*06c3fb27SDimitry Andric     auto Succs = successors(&BB);
155*06c3fb27SDimitry Andric     bool HasSuperReachableSucc = llvm::any_of(Succs, [&](auto *Succ) {
156*06c3fb27SDimitry Andric       return ReachableFromEntry.count(Succ) &&
157*06c3fb27SDimitry Andric              ReachableFromTerminal.count(Succ);
158*06c3fb27SDimitry Andric     });
159*06c3fb27SDimitry Andric     if (!HasSuperReachableSucc)
160*06c3fb27SDimitry Andric       for (auto *Succ : Succs)
161*06c3fb27SDimitry Andric         if (ReachableFromTerminal.count(Succ))
162*06c3fb27SDimitry Andric           SuccessorDependencies[&BB].insert(Succ);
163*06c3fb27SDimitry Andric   }
164*06c3fb27SDimitry Andric 
165*06c3fb27SDimitry Andric   if (ForceInstrumentEntry) {
166*06c3fb27SDimitry Andric     // Force the entry block to be instrumented by clearing the blocks it can
167*06c3fb27SDimitry Andric     // infer coverage from.
168*06c3fb27SDimitry Andric     PredecessorDependencies[&EntryBlock].clear();
169*06c3fb27SDimitry Andric     SuccessorDependencies[&EntryBlock].clear();
170*06c3fb27SDimitry Andric   }
171*06c3fb27SDimitry Andric 
172*06c3fb27SDimitry Andric   // Construct a graph where blocks are connected if there is a mutual
173*06c3fb27SDimitry Andric   // dependency between them. This graph has a special property that it contains
174*06c3fb27SDimitry Andric   // only paths.
175*06c3fb27SDimitry Andric   DenseMap<const BasicBlock *, BlockSet> AdjacencyList;
176*06c3fb27SDimitry Andric   for (auto &BB : F) {
177*06c3fb27SDimitry Andric     for (auto *Succ : successors(&BB)) {
178*06c3fb27SDimitry Andric       if (SuccessorDependencies[&BB].count(Succ) &&
179*06c3fb27SDimitry Andric           PredecessorDependencies[Succ].count(&BB)) {
180*06c3fb27SDimitry Andric         AdjacencyList[&BB].insert(Succ);
181*06c3fb27SDimitry Andric         AdjacencyList[Succ].insert(&BB);
182*06c3fb27SDimitry Andric       }
183*06c3fb27SDimitry Andric     }
184*06c3fb27SDimitry Andric   }
185*06c3fb27SDimitry Andric 
186*06c3fb27SDimitry Andric   // Given a path with at least one node, return the next node on the path.
187*06c3fb27SDimitry Andric   auto getNextOnPath = [&](BlockSet &Path) -> const BasicBlock * {
188*06c3fb27SDimitry Andric     assert(Path.size());
189*06c3fb27SDimitry Andric     auto &Neighbors = AdjacencyList[Path.back()];
190*06c3fb27SDimitry Andric     if (Path.size() == 1) {
191*06c3fb27SDimitry Andric       // This is the first node on the path, return its neighbor.
192*06c3fb27SDimitry Andric       assert(Neighbors.size() == 1);
193*06c3fb27SDimitry Andric       return Neighbors.front();
194*06c3fb27SDimitry Andric     } else if (Neighbors.size() == 2) {
195*06c3fb27SDimitry Andric       // This is the middle of the path, find the neighbor that is not on the
196*06c3fb27SDimitry Andric       // path already.
197*06c3fb27SDimitry Andric       assert(Path.size() >= 2);
198*06c3fb27SDimitry Andric       return Path.count(Neighbors[0]) ? Neighbors[1] : Neighbors[0];
199*06c3fb27SDimitry Andric     }
200*06c3fb27SDimitry Andric     // This is the end of the path.
201*06c3fb27SDimitry Andric     assert(Neighbors.size() == 1);
202*06c3fb27SDimitry Andric     return nullptr;
203*06c3fb27SDimitry Andric   };
204*06c3fb27SDimitry Andric 
205*06c3fb27SDimitry Andric   // Remove all cycles in the inferencing graph.
206*06c3fb27SDimitry Andric   for (auto &BB : F) {
207*06c3fb27SDimitry Andric     if (AdjacencyList[&BB].size() == 1) {
208*06c3fb27SDimitry Andric       // We found the head of some path.
209*06c3fb27SDimitry Andric       BlockSet Path;
210*06c3fb27SDimitry Andric       Path.insert(&BB);
211*06c3fb27SDimitry Andric       while (const BasicBlock *Next = getNextOnPath(Path))
212*06c3fb27SDimitry Andric         Path.insert(Next);
213*06c3fb27SDimitry Andric       LLVM_DEBUG(dbgs() << "Found path: " << getBlockNames(Path) << "\n");
214*06c3fb27SDimitry Andric 
215*06c3fb27SDimitry Andric       // Remove these nodes from the graph so we don't discover this path again.
216*06c3fb27SDimitry Andric       for (auto *BB : Path)
217*06c3fb27SDimitry Andric         AdjacencyList[BB].clear();
218*06c3fb27SDimitry Andric 
219*06c3fb27SDimitry Andric       // Finally, remove the cycles.
220*06c3fb27SDimitry Andric       if (PredecessorDependencies[Path.front()].size()) {
221*06c3fb27SDimitry Andric         for (auto *BB : Path)
222*06c3fb27SDimitry Andric           if (BB != Path.back())
223*06c3fb27SDimitry Andric             SuccessorDependencies[BB].clear();
224*06c3fb27SDimitry Andric       } else {
225*06c3fb27SDimitry Andric         for (auto *BB : Path)
226*06c3fb27SDimitry Andric           if (BB != Path.front())
227*06c3fb27SDimitry Andric             PredecessorDependencies[BB].clear();
228*06c3fb27SDimitry Andric       }
229*06c3fb27SDimitry Andric     }
230*06c3fb27SDimitry Andric   }
231*06c3fb27SDimitry Andric   LLVM_DEBUG(dump(dbgs()));
232*06c3fb27SDimitry Andric }
233*06c3fb27SDimitry Andric 
getReachableAvoiding(const BasicBlock & Start,const BasicBlock & Avoid,bool IsForward,BlockSet & Reachable) const234*06c3fb27SDimitry Andric void BlockCoverageInference::getReachableAvoiding(const BasicBlock &Start,
235*06c3fb27SDimitry Andric                                                   const BasicBlock &Avoid,
236*06c3fb27SDimitry Andric                                                   bool IsForward,
237*06c3fb27SDimitry Andric                                                   BlockSet &Reachable) const {
238*06c3fb27SDimitry Andric   df_iterator_default_set<const BasicBlock *> Visited;
239*06c3fb27SDimitry Andric   Visited.insert(&Avoid);
240*06c3fb27SDimitry Andric   if (IsForward) {
241*06c3fb27SDimitry Andric     auto Range = depth_first_ext(&Start, Visited);
242*06c3fb27SDimitry Andric     Reachable.insert(Range.begin(), Range.end());
243*06c3fb27SDimitry Andric   } else {
244*06c3fb27SDimitry Andric     auto Range = inverse_depth_first_ext(&Start, Visited);
245*06c3fb27SDimitry Andric     Reachable.insert(Range.begin(), Range.end());
246*06c3fb27SDimitry Andric   }
247*06c3fb27SDimitry Andric }
248*06c3fb27SDimitry Andric 
249*06c3fb27SDimitry Andric namespace llvm {
250*06c3fb27SDimitry Andric class DotFuncBCIInfo {
251*06c3fb27SDimitry Andric private:
252*06c3fb27SDimitry Andric   const BlockCoverageInference *BCI;
253*06c3fb27SDimitry Andric   const DenseMap<const BasicBlock *, bool> *Coverage;
254*06c3fb27SDimitry Andric 
255*06c3fb27SDimitry Andric public:
DotFuncBCIInfo(const BlockCoverageInference * BCI,const DenseMap<const BasicBlock *,bool> * Coverage)256*06c3fb27SDimitry Andric   DotFuncBCIInfo(const BlockCoverageInference *BCI,
257*06c3fb27SDimitry Andric                  const DenseMap<const BasicBlock *, bool> *Coverage)
258*06c3fb27SDimitry Andric       : BCI(BCI), Coverage(Coverage) {}
259*06c3fb27SDimitry Andric 
getFunction()260*06c3fb27SDimitry Andric   const Function &getFunction() { return BCI->F; }
261*06c3fb27SDimitry Andric 
isInstrumented(const BasicBlock * BB) const262*06c3fb27SDimitry Andric   bool isInstrumented(const BasicBlock *BB) const {
263*06c3fb27SDimitry Andric     return BCI->shouldInstrumentBlock(*BB);
264*06c3fb27SDimitry Andric   }
265*06c3fb27SDimitry Andric 
isCovered(const BasicBlock * BB) const266*06c3fb27SDimitry Andric   bool isCovered(const BasicBlock *BB) const {
267*06c3fb27SDimitry Andric     return Coverage && Coverage->lookup(BB);
268*06c3fb27SDimitry Andric   }
269*06c3fb27SDimitry Andric 
isDependent(const BasicBlock * Src,const BasicBlock * Dest) const270*06c3fb27SDimitry Andric   bool isDependent(const BasicBlock *Src, const BasicBlock *Dest) const {
271*06c3fb27SDimitry Andric     return BCI->getDependencies(*Src).count(Dest);
272*06c3fb27SDimitry Andric   }
273*06c3fb27SDimitry Andric };
274*06c3fb27SDimitry Andric 
275*06c3fb27SDimitry Andric template <>
276*06c3fb27SDimitry Andric struct GraphTraits<DotFuncBCIInfo *> : public GraphTraits<const BasicBlock *> {
getEntryNodellvm::GraphTraits277*06c3fb27SDimitry Andric   static NodeRef getEntryNode(DotFuncBCIInfo *Info) {
278*06c3fb27SDimitry Andric     return &(Info->getFunction().getEntryBlock());
279*06c3fb27SDimitry Andric   }
280*06c3fb27SDimitry Andric 
281*06c3fb27SDimitry Andric   // nodes_iterator/begin/end - Allow iteration over all nodes in the graph
282*06c3fb27SDimitry Andric   using nodes_iterator = pointer_iterator<Function::const_iterator>;
283*06c3fb27SDimitry Andric 
nodes_beginllvm::GraphTraits284*06c3fb27SDimitry Andric   static nodes_iterator nodes_begin(DotFuncBCIInfo *Info) {
285*06c3fb27SDimitry Andric     return nodes_iterator(Info->getFunction().begin());
286*06c3fb27SDimitry Andric   }
287*06c3fb27SDimitry Andric 
nodes_endllvm::GraphTraits288*06c3fb27SDimitry Andric   static nodes_iterator nodes_end(DotFuncBCIInfo *Info) {
289*06c3fb27SDimitry Andric     return nodes_iterator(Info->getFunction().end());
290*06c3fb27SDimitry Andric   }
291*06c3fb27SDimitry Andric 
sizellvm::GraphTraits292*06c3fb27SDimitry Andric   static size_t size(DotFuncBCIInfo *Info) {
293*06c3fb27SDimitry Andric     return Info->getFunction().size();
294*06c3fb27SDimitry Andric   }
295*06c3fb27SDimitry Andric };
296*06c3fb27SDimitry Andric 
297*06c3fb27SDimitry Andric template <>
298*06c3fb27SDimitry Andric struct DOTGraphTraits<DotFuncBCIInfo *> : public DefaultDOTGraphTraits {
299*06c3fb27SDimitry Andric 
DOTGraphTraitsllvm::DOTGraphTraits300*06c3fb27SDimitry Andric   DOTGraphTraits(bool IsSimple = false) : DefaultDOTGraphTraits(IsSimple) {}
301*06c3fb27SDimitry Andric 
getGraphNamellvm::DOTGraphTraits302*06c3fb27SDimitry Andric   static std::string getGraphName(DotFuncBCIInfo *Info) {
303*06c3fb27SDimitry Andric     return "BCI CFG for " + Info->getFunction().getName().str();
304*06c3fb27SDimitry Andric   }
305*06c3fb27SDimitry Andric 
getNodeLabelllvm::DOTGraphTraits306*06c3fb27SDimitry Andric   std::string getNodeLabel(const BasicBlock *Node, DotFuncBCIInfo *Info) {
307*06c3fb27SDimitry Andric     return Node->getName().str();
308*06c3fb27SDimitry Andric   }
309*06c3fb27SDimitry Andric 
getEdgeAttributesllvm::DOTGraphTraits310*06c3fb27SDimitry Andric   std::string getEdgeAttributes(const BasicBlock *Src, const_succ_iterator I,
311*06c3fb27SDimitry Andric                                 DotFuncBCIInfo *Info) {
312*06c3fb27SDimitry Andric     const BasicBlock *Dest = *I;
313*06c3fb27SDimitry Andric     if (Info->isDependent(Src, Dest))
314*06c3fb27SDimitry Andric       return "color=red";
315*06c3fb27SDimitry Andric     if (Info->isDependent(Dest, Src))
316*06c3fb27SDimitry Andric       return "color=blue";
317*06c3fb27SDimitry Andric     return "";
318*06c3fb27SDimitry Andric   }
319*06c3fb27SDimitry Andric 
getNodeAttributesllvm::DOTGraphTraits320*06c3fb27SDimitry Andric   std::string getNodeAttributes(const BasicBlock *Node, DotFuncBCIInfo *Info) {
321*06c3fb27SDimitry Andric     std::string Result;
322*06c3fb27SDimitry Andric     if (Info->isInstrumented(Node))
323*06c3fb27SDimitry Andric       Result += "style=filled,fillcolor=gray";
324*06c3fb27SDimitry Andric     if (Info->isCovered(Node))
325*06c3fb27SDimitry Andric       Result += std::string(Result.empty() ? "" : ",") + "color=red";
326*06c3fb27SDimitry Andric     return Result;
327*06c3fb27SDimitry Andric   }
328*06c3fb27SDimitry Andric };
329*06c3fb27SDimitry Andric 
330*06c3fb27SDimitry Andric } // namespace llvm
331*06c3fb27SDimitry Andric 
viewBlockCoverageGraph(const DenseMap<const BasicBlock *,bool> * Coverage) const332*06c3fb27SDimitry Andric void BlockCoverageInference::viewBlockCoverageGraph(
333*06c3fb27SDimitry Andric     const DenseMap<const BasicBlock *, bool> *Coverage) const {
334*06c3fb27SDimitry Andric   DotFuncBCIInfo Info(this, Coverage);
335*06c3fb27SDimitry Andric   WriteGraph(&Info, "BCI", false,
336*06c3fb27SDimitry Andric              "Block Coverage Inference for " + F.getName());
337*06c3fb27SDimitry Andric }
338*06c3fb27SDimitry Andric 
dump(raw_ostream & OS) const339*06c3fb27SDimitry Andric void BlockCoverageInference::dump(raw_ostream &OS) const {
340*06c3fb27SDimitry Andric   OS << "Minimal block coverage for function \'" << F.getName()
341*06c3fb27SDimitry Andric      << "\' (Instrumented=*)\n";
342*06c3fb27SDimitry Andric   for (auto &BB : F) {
343*06c3fb27SDimitry Andric     OS << (shouldInstrumentBlock(BB) ? "* " : "  ") << BB.getName() << "\n";
344*06c3fb27SDimitry Andric     auto It = PredecessorDependencies.find(&BB);
345*06c3fb27SDimitry Andric     if (It != PredecessorDependencies.end() && It->second.size())
346*06c3fb27SDimitry Andric       OS << "    PredDeps = " << getBlockNames(It->second) << "\n";
347*06c3fb27SDimitry Andric     It = SuccessorDependencies.find(&BB);
348*06c3fb27SDimitry Andric     if (It != SuccessorDependencies.end() && It->second.size())
349*06c3fb27SDimitry Andric       OS << "    SuccDeps = " << getBlockNames(It->second) << "\n";
350*06c3fb27SDimitry Andric   }
351*06c3fb27SDimitry Andric   OS << "  Instrumented Blocks Hash = 0x"
352*06c3fb27SDimitry Andric      << Twine::utohexstr(getInstrumentedBlocksHash()) << "\n";
353*06c3fb27SDimitry Andric }
354*06c3fb27SDimitry Andric 
355*06c3fb27SDimitry Andric std::string
getBlockNames(ArrayRef<const BasicBlock * > BBs)356*06c3fb27SDimitry Andric BlockCoverageInference::getBlockNames(ArrayRef<const BasicBlock *> BBs) {
357*06c3fb27SDimitry Andric   std::string Result;
358*06c3fb27SDimitry Andric   raw_string_ostream OS(Result);
359*06c3fb27SDimitry Andric   OS << "[";
360*06c3fb27SDimitry Andric   if (!BBs.empty()) {
361*06c3fb27SDimitry Andric     OS << BBs.front()->getName();
362*06c3fb27SDimitry Andric     BBs = BBs.drop_front();
363*06c3fb27SDimitry Andric   }
364*06c3fb27SDimitry Andric   for (auto *BB : BBs)
365*06c3fb27SDimitry Andric     OS << ", " << BB->getName();
366*06c3fb27SDimitry Andric   OS << "]";
367*06c3fb27SDimitry Andric   return OS.str();
368*06c3fb27SDimitry Andric }
369