1*2f09f445SMaksim Panchenko //===- bolt/Passes/BinaryFunctionCallGraph.cpp ----------------------------===//
2a34c753fSRafael Auler //
3a34c753fSRafael Auler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a34c753fSRafael Auler // See https://llvm.org/LICENSE.txt for license information.
5a34c753fSRafael Auler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a34c753fSRafael Auler //
7a34c753fSRafael Auler //===----------------------------------------------------------------------===//
8a34c753fSRafael Auler //
9*2f09f445SMaksim Panchenko // This file implements the BinaryFunctionCallGraph class.
10*2f09f445SMaksim Panchenko //
11a34c753fSRafael Auler //===----------------------------------------------------------------------===//
12a34c753fSRafael Auler 
13a34c753fSRafael Auler #include "bolt/Passes/BinaryFunctionCallGraph.h"
14a34c753fSRafael Auler #include "bolt/Core/BinaryContext.h"
15a34c753fSRafael Auler #include "bolt/Core/BinaryFunction.h"
16a34c753fSRafael Auler #include "llvm/Support/CommandLine.h"
17a34c753fSRafael Auler #include "llvm/Support/Timer.h"
18a34c753fSRafael Auler #include <stack>
19a34c753fSRafael Auler 
20a34c753fSRafael Auler #define DEBUG_TYPE "callgraph"
21a34c753fSRafael Auler 
22a34c753fSRafael Auler namespace opts {
23a34c753fSRafael Auler extern llvm::cl::opt<bool> TimeOpts;
24a34c753fSRafael Auler extern llvm::cl::opt<unsigned> Verbosity;
2540c2e0faSMaksim Panchenko } // namespace opts
26a34c753fSRafael Auler 
27a34c753fSRafael Auler namespace llvm {
28a34c753fSRafael Auler namespace bolt {
29a34c753fSRafael Auler 
30a34c753fSRafael Auler CallGraph::NodeId BinaryFunctionCallGraph::addNode(BinaryFunction *BF,
31a34c753fSRafael Auler                                                    uint32_t Size,
32a34c753fSRafael Auler                                                    uint64_t Samples) {
33a34c753fSRafael Auler   NodeId Id = CallGraph::addNode(Size, Samples);
34a34c753fSRafael Auler   assert(size_t(Id) == Funcs.size());
35a34c753fSRafael Auler   Funcs.push_back(BF);
36a34c753fSRafael Auler   FuncToNodeId[BF] = Id;
37a34c753fSRafael Auler   assert(Funcs[Id] == BF);
38a34c753fSRafael Auler   return Id;
39a34c753fSRafael Auler }
40a34c753fSRafael Auler 
41a34c753fSRafael Auler std::deque<BinaryFunction *> BinaryFunctionCallGraph::buildTraversalOrder() {
42a34c753fSRafael Auler   NamedRegionTimer T1("buildcgorder", "Build cg traversal order",
43a34c753fSRafael Auler                       "CG breakdown", "CG breakdown", opts::TimeOpts);
44a34c753fSRafael Auler   std::deque<BinaryFunction *> TopologicalOrder;
45a34c753fSRafael Auler   enum NodeStatus { NEW, VISITING, VISITED };
46a34c753fSRafael Auler   std::vector<NodeStatus> NodeStatus(Funcs.size());
47a34c753fSRafael Auler   std::stack<NodeId> Worklist;
48a34c753fSRafael Auler 
49a34c753fSRafael Auler   for (BinaryFunction *Func : Funcs) {
50a34c753fSRafael Auler     const NodeId Id = FuncToNodeId.at(Func);
51a34c753fSRafael Auler     Worklist.push(Id);
52a34c753fSRafael Auler     NodeStatus[Id] = NEW;
53a34c753fSRafael Auler   }
54a34c753fSRafael Auler 
55a34c753fSRafael Auler   while (!Worklist.empty()) {
56a34c753fSRafael Auler     const NodeId FuncId = Worklist.top();
57a34c753fSRafael Auler     Worklist.pop();
58a34c753fSRafael Auler 
59a34c753fSRafael Auler     if (NodeStatus[FuncId] == VISITED)
60a34c753fSRafael Auler       continue;
61a34c753fSRafael Auler 
62a34c753fSRafael Auler     if (NodeStatus[FuncId] == VISITING) {
63a34c753fSRafael Auler       TopologicalOrder.push_back(Funcs[FuncId]);
64a34c753fSRafael Auler       NodeStatus[FuncId] = VISITED;
65a34c753fSRafael Auler       continue;
66a34c753fSRafael Auler     }
67a34c753fSRafael Auler 
68a34c753fSRafael Auler     assert(NodeStatus[FuncId] == NEW);
69a34c753fSRafael Auler     NodeStatus[FuncId] = VISITING;
70a34c753fSRafael Auler     Worklist.push(FuncId);
71a34c753fSRafael Auler     for (const NodeId Callee : successors(FuncId)) {
72a34c753fSRafael Auler       if (NodeStatus[Callee] == VISITING || NodeStatus[Callee] == VISITED)
73a34c753fSRafael Auler         continue;
74a34c753fSRafael Auler       Worklist.push(Callee);
75a34c753fSRafael Auler     }
76a34c753fSRafael Auler   }
77a34c753fSRafael Auler 
78a34c753fSRafael Auler   return TopologicalOrder;
79a34c753fSRafael Auler }
80a34c753fSRafael Auler 
8140c2e0faSMaksim Panchenko BinaryFunctionCallGraph
8240c2e0faSMaksim Panchenko buildCallGraph(BinaryContext &BC, CgFilterFunction Filter, bool CgFromPerfData,
8340c2e0faSMaksim Panchenko                bool IncludeColdCalls, bool UseFunctionHotSize,
8440c2e0faSMaksim Panchenko                bool UseSplitHotSize, bool UseEdgeCounts,
85a34c753fSRafael Auler                bool IgnoreRecursiveCalls) {
86a34c753fSRafael Auler   NamedRegionTimer T1("buildcg", "Callgraph construction", "CG breakdown",
87a34c753fSRafael Auler                       "CG breakdown", opts::TimeOpts);
88a34c753fSRafael Auler   BinaryFunctionCallGraph Cg;
89a34c753fSRafael Auler   static constexpr uint64_t COUNT_NO_PROFILE =
90a34c753fSRafael Auler       BinaryBasicBlock::COUNT_NO_PROFILE;
91a34c753fSRafael Auler 
92a34c753fSRafael Auler   // Compute function size
93a34c753fSRafael Auler   auto functionSize = [&](const BinaryFunction *Function) {
94a34c753fSRafael Auler     return UseFunctionHotSize && Function->isSplit()
95a34c753fSRafael Auler                ? Function->estimateHotSize(UseSplitHotSize)
96a34c753fSRafael Auler                : Function->estimateSize();
97a34c753fSRafael Auler   };
98a34c753fSRafael Auler 
99a34c753fSRafael Auler   // Add call graph nodes.
100a34c753fSRafael Auler   auto lookupNode = [&](BinaryFunction *Function) {
101a34c753fSRafael Auler     const CallGraph::NodeId Id = Cg.maybeGetNodeId(Function);
102a34c753fSRafael Auler     if (Id == CallGraph::InvalidId) {
103a34c753fSRafael Auler       // It's ok to use the hot size here when the function is split.  This is
104a34c753fSRafael Auler       // because emitFunctions will emit the hot part first in the order that is
105a34c753fSRafael Auler       // computed by ReorderFunctions.  The cold part will be emitted with the
106a34c753fSRafael Auler       // rest of the cold functions and code.
107a34c753fSRafael Auler       const size_t Size = functionSize(Function);
108a34c753fSRafael Auler       // NOTE: for functions without a profile, we set the number of samples
109a34c753fSRafael Auler       // to zero.  This will keep these functions from appearing in the hot
110a34c753fSRafael Auler       // section.  This is a little weird because we wouldn't be trying to
111a34c753fSRafael Auler       // create a node for a function unless it was the target of a call from
112a34c753fSRafael Auler       // a hot block.  The alternative would be to set the count to one or
113a34c753fSRafael Auler       // accumulate the number of calls from the callsite into the function
114a34c753fSRafael Auler       // samples.  Results from perfomance testing seem to favor the zero
115a34c753fSRafael Auler       // count though, so I'm leaving it this way for now.
116a34c753fSRafael Auler       return Cg.addNode(Function, Size, Function->getKnownExecutionCount());
117a34c753fSRafael Auler     } else {
118a34c753fSRafael Auler       return Id;
119a34c753fSRafael Auler     }
120a34c753fSRafael Auler   };
121a34c753fSRafael Auler 
122a34c753fSRafael Auler   // Add call graph edges.
123a34c753fSRafael Auler   uint64_t NotProcessed = 0;
124a34c753fSRafael Auler   uint64_t TotalCallsites = 0;
125a34c753fSRafael Auler   uint64_t NoProfileCallsites = 0;
126a34c753fSRafael Auler   uint64_t NumFallbacks = 0;
127a34c753fSRafael Auler   uint64_t RecursiveCallsites = 0;
128a34c753fSRafael Auler   for (auto &It : BC.getBinaryFunctions()) {
129a34c753fSRafael Auler     BinaryFunction *Function = &It.second;
130a34c753fSRafael Auler 
131a34c753fSRafael Auler     if (Filter(*Function)) {
132a34c753fSRafael Auler       continue;
133a34c753fSRafael Auler     }
134a34c753fSRafael Auler 
135a34c753fSRafael Auler     const CallGraph::NodeId SrcId = lookupNode(Function);
136a34c753fSRafael Auler     // Offset of the current basic block from the beginning of the function
137a34c753fSRafael Auler     uint64_t Offset = 0;
138a34c753fSRafael Auler 
139a34c753fSRafael Auler     auto recordCall = [&](const MCSymbol *DestSymbol, const uint64_t Count) {
140a34c753fSRafael Auler       if (BinaryFunction *DstFunc =
141a34c753fSRafael Auler               DestSymbol ? BC.getFunctionForSymbol(DestSymbol) : nullptr) {
142a34c753fSRafael Auler         if (DstFunc == Function) {
143a34c753fSRafael Auler           LLVM_DEBUG(dbgs() << "BOLT-INFO: recursive call detected in "
144a34c753fSRafael Auler                             << *DstFunc << "\n");
145a34c753fSRafael Auler           ++RecursiveCallsites;
146a34c753fSRafael Auler           if (IgnoreRecursiveCalls)
147a34c753fSRafael Auler             return false;
148a34c753fSRafael Auler         }
149a34c753fSRafael Auler         if (Filter(*DstFunc)) {
150a34c753fSRafael Auler           return false;
151a34c753fSRafael Auler         }
152a34c753fSRafael Auler         const CallGraph::NodeId DstId = lookupNode(DstFunc);
153a34c753fSRafael Auler         const bool IsValidCount = Count != COUNT_NO_PROFILE;
154a34c753fSRafael Auler         const uint64_t AdjCount = UseEdgeCounts && IsValidCount ? Count : 1;
155a34c753fSRafael Auler         if (!IsValidCount)
156a34c753fSRafael Auler           ++NoProfileCallsites;
157a34c753fSRafael Auler         Cg.incArcWeight(SrcId, DstId, AdjCount, Offset);
15840c2e0faSMaksim Panchenko         LLVM_DEBUG(if (opts::Verbosity > 1) {
15940c2e0faSMaksim Panchenko           dbgs() << "BOLT-DEBUG: buildCallGraph: call " << *Function << " -> "
16040c2e0faSMaksim Panchenko                  << *DstFunc << " @ " << Offset << "\n";
161a34c753fSRafael Auler         });
162a34c753fSRafael Auler         return true;
163a34c753fSRafael Auler       }
164a34c753fSRafael Auler 
165a34c753fSRafael Auler       return false;
166a34c753fSRafael Auler     };
167a34c753fSRafael Auler 
168a34c753fSRafael Auler     // Pairs of (symbol, count) for each target at this callsite.
169a34c753fSRafael Auler     using TargetDesc = std::pair<const MCSymbol *, uint64_t>;
170a34c753fSRafael Auler     using CallInfoTy = std::vector<TargetDesc>;
171a34c753fSRafael Auler 
172a34c753fSRafael Auler     // Get pairs of (symbol, count) for each target at this callsite.
173a34c753fSRafael Auler     // If the call is to an unknown function the symbol will be nullptr.
174a34c753fSRafael Auler     // If there is no profiling data the count will be COUNT_NO_PROFILE.
175a34c753fSRafael Auler     auto getCallInfo = [&](const BinaryBasicBlock *BB, const MCInst &Inst) {
176a34c753fSRafael Auler       CallInfoTy Counts;
177a34c753fSRafael Auler       const MCSymbol *DstSym = BC.MIB->getTargetSymbol(Inst);
178a34c753fSRafael Auler 
179a34c753fSRafael Auler       // If this is an indirect call use perf data directly.
180a34c753fSRafael Auler       if (!DstSym && BC.MIB->hasAnnotation(Inst, "CallProfile")) {
18140c2e0faSMaksim Panchenko         const auto &ICSP = BC.MIB->getAnnotationAs<IndirectCallSiteProfile>(
18240c2e0faSMaksim Panchenko             Inst, "CallProfile");
183a34c753fSRafael Auler         for (const IndirectCallProfile &CSI : ICSP) {
184a34c753fSRafael Auler           if (CSI.Symbol)
185a34c753fSRafael Auler             Counts.emplace_back(CSI.Symbol, CSI.Count);
186a34c753fSRafael Auler         }
187a34c753fSRafael Auler       } else {
188a34c753fSRafael Auler         const uint64_t Count = BB->getExecutionCount();
189a34c753fSRafael Auler         Counts.emplace_back(DstSym, Count);
190a34c753fSRafael Auler       }
191a34c753fSRafael Auler 
192a34c753fSRafael Auler       return Counts;
193a34c753fSRafael Auler     };
194a34c753fSRafael Auler 
195a34c753fSRafael Auler     // If the function has an invalid profile, try to use the perf data
196a34c753fSRafael Auler     // directly (if requested).  If there is no perf data for this function,
197a34c753fSRafael Auler     // fall back to the CFG walker which attempts to handle missing data.
198a34c753fSRafael Auler     if (!Function->hasValidProfile() && CgFromPerfData &&
199a34c753fSRafael Auler         !Function->getAllCallSites().empty()) {
200a34c753fSRafael Auler       LLVM_DEBUG(
201a34c753fSRafael Auler           dbgs() << "BOLT-DEBUG: buildCallGraph: Falling back to perf data"
202a34c753fSRafael Auler                  << " for " << *Function << "\n");
203a34c753fSRafael Auler       ++NumFallbacks;
204a34c753fSRafael Auler       const size_t Size = functionSize(Function);
205a34c753fSRafael Auler       for (const IndirectCallProfile &CSI : Function->getAllCallSites()) {
206a34c753fSRafael Auler         ++TotalCallsites;
207a34c753fSRafael Auler 
208a34c753fSRafael Auler         if (!CSI.Symbol)
209a34c753fSRafael Auler           continue;
210a34c753fSRafael Auler 
211a34c753fSRafael Auler         // The computed offset may exceed the hot part of the function; hence,
212a34c753fSRafael Auler         // bound it by the size.
213a34c753fSRafael Auler         Offset = CSI.Offset;
214a34c753fSRafael Auler         if (Offset > Size)
215a34c753fSRafael Auler           Offset = Size;
216a34c753fSRafael Auler 
217a34c753fSRafael Auler         if (!recordCall(CSI.Symbol, CSI.Count)) {
218a34c753fSRafael Auler           ++NotProcessed;
219a34c753fSRafael Auler         }
220a34c753fSRafael Auler       }
221a34c753fSRafael Auler     } else {
222a34c753fSRafael Auler       for (BinaryBasicBlock *BB : Function->layout()) {
223a34c753fSRafael Auler         // Don't count calls from cold blocks unless requested.
224a34c753fSRafael Auler         if (BB->isCold() && !IncludeColdCalls)
225a34c753fSRafael Auler           continue;
226a34c753fSRafael Auler 
227a34c753fSRafael Auler         // Determine whether the block is included in Function's (hot) size
228a34c753fSRafael Auler         // See BinaryFunction::estimateHotSize
229a34c753fSRafael Auler         bool BBIncludedInFunctionSize = false;
230a34c753fSRafael Auler         if (UseFunctionHotSize && Function->isSplit()) {
231a34c753fSRafael Auler           if (UseSplitHotSize)
232a34c753fSRafael Auler             BBIncludedInFunctionSize = !BB->isCold();
233a34c753fSRafael Auler           else
234a34c753fSRafael Auler             BBIncludedInFunctionSize = BB->getKnownExecutionCount() != 0;
235a34c753fSRafael Auler         } else {
236a34c753fSRafael Auler           BBIncludedInFunctionSize = true;
237a34c753fSRafael Auler         }
238a34c753fSRafael Auler 
239a34c753fSRafael Auler         for (MCInst &Inst : *BB) {
240a34c753fSRafael Auler           // Find call instructions and extract target symbols from each one.
241a34c753fSRafael Auler           if (BC.MIB->isCall(Inst)) {
242a34c753fSRafael Auler             const CallInfoTy CallInfo = getCallInfo(BB, Inst);
243a34c753fSRafael Auler 
244a34c753fSRafael Auler             if (!CallInfo.empty()) {
245a34c753fSRafael Auler               for (const TargetDesc &CI : CallInfo) {
246a34c753fSRafael Auler                 ++TotalCallsites;
247a34c753fSRafael Auler                 if (!recordCall(CI.first, CI.second))
248a34c753fSRafael Auler                   ++NotProcessed;
249a34c753fSRafael Auler               }
250a34c753fSRafael Auler             } else {
251a34c753fSRafael Auler               ++TotalCallsites;
252a34c753fSRafael Auler               ++NotProcessed;
253a34c753fSRafael Auler             }
254a34c753fSRafael Auler           }
255a34c753fSRafael Auler           // Increase Offset if needed
256a34c753fSRafael Auler           if (BBIncludedInFunctionSize) {
257a34c753fSRafael Auler             Offset += BC.computeCodeSize(&Inst, &Inst + 1);
258a34c753fSRafael Auler           }
259a34c753fSRafael Auler         }
260a34c753fSRafael Auler       }
261a34c753fSRafael Auler     }
262a34c753fSRafael Auler   }
263a34c753fSRafael Auler 
264a34c753fSRafael Auler #ifndef NDEBUG
265a34c753fSRafael Auler   bool PrintInfo = DebugFlag && isCurrentDebugType("callgraph");
266a34c753fSRafael Auler #else
267a34c753fSRafael Auler   bool PrintInfo = false;
268a34c753fSRafael Auler #endif
269a34c753fSRafael Auler   if (PrintInfo || opts::Verbosity > 0) {
270a34c753fSRafael Auler     outs() << format("BOLT-INFO: buildCallGraph: %u nodes, %u callsites "
271a34c753fSRafael Auler                      "(%u recursive), density = %.6lf, %u callsites not "
272a34c753fSRafael Auler                      "processed, %u callsites with invalid profile, "
273a34c753fSRafael Auler                      "used perf data for %u stale functions.\n",
274a34c753fSRafael Auler                      Cg.numNodes(), TotalCallsites, RecursiveCallsites,
275a34c753fSRafael Auler                      Cg.density(), NotProcessed, NoProfileCallsites,
276a34c753fSRafael Auler                      NumFallbacks);
277a34c753fSRafael Auler   }
278a34c753fSRafael Auler 
279a34c753fSRafael Auler   return Cg;
280a34c753fSRafael Auler }
281a34c753fSRafael Auler 
28240c2e0faSMaksim Panchenko } // namespace bolt
28340c2e0faSMaksim Panchenko } // namespace llvm
284