xref: /freebsd/contrib/llvm-project/llvm/include/llvm/Transforms/IPO/ProfiledCallGraph.h (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
1fe6060f1SDimitry Andric //===-- ProfiledCallGraph.h - Profiled Call Graph ----------------- C++ -*-===//
2fe6060f1SDimitry Andric //
3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe6060f1SDimitry Andric //
7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8fe6060f1SDimitry Andric 
9349cc55cSDimitry Andric #ifndef LLVM_TRANSFORMS_IPO_PROFILEDCALLGRAPH_H
10349cc55cSDimitry Andric #define LLVM_TRANSFORMS_IPO_PROFILEDCALLGRAPH_H
11fe6060f1SDimitry Andric 
12fe6060f1SDimitry Andric #include "llvm/ADT/GraphTraits.h"
13fe6060f1SDimitry Andric #include "llvm/ProfileData/SampleProf.h"
14fe6060f1SDimitry Andric #include "llvm/ProfileData/SampleProfReader.h"
15fe6060f1SDimitry Andric #include "llvm/Transforms/IPO/SampleContextTracker.h"
16fe6060f1SDimitry Andric #include <queue>
17fe6060f1SDimitry Andric #include <set>
18fe6060f1SDimitry Andric 
19fe6060f1SDimitry Andric namespace llvm {
20fe6060f1SDimitry Andric namespace sampleprof {
21fe6060f1SDimitry Andric 
224824e7fdSDimitry Andric struct ProfiledCallGraphNode;
23fe6060f1SDimitry Andric 
244824e7fdSDimitry Andric struct ProfiledCallGraphEdge {
254824e7fdSDimitry Andric   ProfiledCallGraphEdge(ProfiledCallGraphNode *Source,
264824e7fdSDimitry Andric                         ProfiledCallGraphNode *Target, uint64_t Weight)
274824e7fdSDimitry Andric       : Source(Source), Target(Target), Weight(Weight) {}
284824e7fdSDimitry Andric   ProfiledCallGraphNode *Source;
294824e7fdSDimitry Andric   ProfiledCallGraphNode *Target;
304824e7fdSDimitry Andric   uint64_t Weight;
314824e7fdSDimitry Andric 
324824e7fdSDimitry Andric   // The call destination is the only important data here,
334824e7fdSDimitry Andric   // allow to transparently unwrap into it.
344824e7fdSDimitry Andric   operator ProfiledCallGraphNode *() const { return Target; }
354824e7fdSDimitry Andric };
364824e7fdSDimitry Andric 
374824e7fdSDimitry Andric struct ProfiledCallGraphNode {
384824e7fdSDimitry Andric 
394824e7fdSDimitry Andric   // Sort edges by callee names only since all edges to be compared are from
404824e7fdSDimitry Andric   // same caller. Edge weights are not considered either because for the same
414824e7fdSDimitry Andric   // callee only the edge with the largest weight is added to the edge set.
424824e7fdSDimitry Andric   struct ProfiledCallGraphEdgeComparer {
434824e7fdSDimitry Andric     bool operator()(const ProfiledCallGraphEdge &L,
444824e7fdSDimitry Andric                     const ProfiledCallGraphEdge &R) const {
454824e7fdSDimitry Andric       return L.Target->Name < R.Target->Name;
46fe6060f1SDimitry Andric     }
47fe6060f1SDimitry Andric   };
484824e7fdSDimitry Andric 
494824e7fdSDimitry Andric   using edge = ProfiledCallGraphEdge;
5081ad6265SDimitry Andric   using edges = std::set<edge, ProfiledCallGraphEdgeComparer>;
5181ad6265SDimitry Andric   using iterator = edges::iterator;
5281ad6265SDimitry Andric   using const_iterator = edges::const_iterator;
534824e7fdSDimitry Andric 
545f757f3fSDimitry Andric   ProfiledCallGraphNode(FunctionId FName = FunctionId()) : Name(FName)
555f757f3fSDimitry Andric   {}
564824e7fdSDimitry Andric 
575f757f3fSDimitry Andric   FunctionId Name;
584824e7fdSDimitry Andric   edges Edges;
59fe6060f1SDimitry Andric };
60fe6060f1SDimitry Andric 
61fe6060f1SDimitry Andric class ProfiledCallGraph {
62fe6060f1SDimitry Andric public:
6381ad6265SDimitry Andric   using iterator = ProfiledCallGraphNode::iterator;
64fe6060f1SDimitry Andric 
65fe6060f1SDimitry Andric   // Constructor for non-CS profile.
6606c3fb27SDimitry Andric   ProfiledCallGraph(SampleProfileMap &ProfileMap,
6706c3fb27SDimitry Andric                     uint64_t IgnoreColdCallThreshold = 0) {
6881ad6265SDimitry Andric     assert(!FunctionSamples::ProfileIsCS &&
690eae32dcSDimitry Andric            "CS flat profile is not handled here");
70fe6060f1SDimitry Andric     for (const auto &Samples : ProfileMap) {
71fe6060f1SDimitry Andric       addProfiledCalls(Samples.second);
72fe6060f1SDimitry Andric     }
7306c3fb27SDimitry Andric 
7406c3fb27SDimitry Andric     // Trim edges with weight up to `IgnoreColdCallThreshold`. This aims
7506c3fb27SDimitry Andric     // for a more stable call graph with "determinstic" edges from run to run.
7606c3fb27SDimitry Andric     trimColdEges(IgnoreColdCallThreshold);
77fe6060f1SDimitry Andric   }
78fe6060f1SDimitry Andric 
79fe6060f1SDimitry Andric   // Constructor for CS profile.
8006c3fb27SDimitry Andric   ProfiledCallGraph(SampleContextTracker &ContextTracker,
8106c3fb27SDimitry Andric                     uint64_t IgnoreColdCallThreshold = 0) {
82fe6060f1SDimitry Andric     // BFS traverse the context profile trie to add call edges for calls shown
83fe6060f1SDimitry Andric     // in context.
84fe6060f1SDimitry Andric     std::queue<ContextTrieNode *> Queue;
85fe6060f1SDimitry Andric     for (auto &Child : ContextTracker.getRootContext().getAllChildContext()) {
86fe6060f1SDimitry Andric       ContextTrieNode *Callee = &Child.second;
875f757f3fSDimitry Andric       addProfiledFunction(Callee->getFuncName());
88fe6060f1SDimitry Andric       Queue.push(Callee);
89fe6060f1SDimitry Andric     }
90fe6060f1SDimitry Andric 
91fe6060f1SDimitry Andric     while (!Queue.empty()) {
92fe6060f1SDimitry Andric       ContextTrieNode *Caller = Queue.front();
93fe6060f1SDimitry Andric       Queue.pop();
944824e7fdSDimitry Andric       FunctionSamples *CallerSamples = Caller->getFunctionSamples();
954824e7fdSDimitry Andric 
964824e7fdSDimitry Andric       // Add calls for context.
97fe6060f1SDimitry Andric       // Note that callsite target samples are completely ignored since they can
98fe6060f1SDimitry Andric       // conflict with the context edges, which are formed by context
99fe6060f1SDimitry Andric       // compression during profile generation, for cyclic SCCs. This may
100fe6060f1SDimitry Andric       // further result in an SCC order incompatible with the purely
101fe6060f1SDimitry Andric       // context-based one, which may in turn block context-based inlining.
102fe6060f1SDimitry Andric       for (auto &Child : Caller->getAllChildContext()) {
103fe6060f1SDimitry Andric         ContextTrieNode *Callee = &Child.second;
1045f757f3fSDimitry Andric         addProfiledFunction(Callee->getFuncName());
105fe6060f1SDimitry Andric         Queue.push(Callee);
1064824e7fdSDimitry Andric 
1074824e7fdSDimitry Andric         // Fetch edge weight from the profile.
1084824e7fdSDimitry Andric         uint64_t Weight;
1094824e7fdSDimitry Andric         FunctionSamples *CalleeSamples = Callee->getFunctionSamples();
1104824e7fdSDimitry Andric         if (!CalleeSamples || !CallerSamples) {
1114824e7fdSDimitry Andric           Weight = 0;
1124824e7fdSDimitry Andric         } else {
113fcaf7f86SDimitry Andric           uint64_t CalleeEntryCount = CalleeSamples->getHeadSamplesEstimate();
1144824e7fdSDimitry Andric           uint64_t CallsiteCount = 0;
1154824e7fdSDimitry Andric           LineLocation Callsite = Callee->getCallSiteLoc();
1164824e7fdSDimitry Andric           if (auto CallTargets = CallerSamples->findCallTargetMapAt(Callsite)) {
117*cb14a3feSDimitry Andric             auto It = CallTargets->find(CalleeSamples->getFunction());
118*cb14a3feSDimitry Andric             if (It != CallTargets->end())
1194824e7fdSDimitry Andric               CallsiteCount = It->second;
1204824e7fdSDimitry Andric           }
1214824e7fdSDimitry Andric           Weight = std::max(CallsiteCount, CalleeEntryCount);
1224824e7fdSDimitry Andric         }
1234824e7fdSDimitry Andric 
1245f757f3fSDimitry Andric         addProfiledCall(Caller->getFuncName(), Callee->getFuncName(), Weight);
125fe6060f1SDimitry Andric       }
126fe6060f1SDimitry Andric     }
12706c3fb27SDimitry Andric 
12806c3fb27SDimitry Andric     // Trim edges with weight up to `IgnoreColdCallThreshold`. This aims
12906c3fb27SDimitry Andric     // for a more stable call graph with "determinstic" edges from run to run.
13006c3fb27SDimitry Andric     trimColdEges(IgnoreColdCallThreshold);
131fe6060f1SDimitry Andric   }
132fe6060f1SDimitry Andric 
1334824e7fdSDimitry Andric   iterator begin() { return Root.Edges.begin(); }
1344824e7fdSDimitry Andric   iterator end() { return Root.Edges.end(); }
135fe6060f1SDimitry Andric   ProfiledCallGraphNode *getEntryNode() { return &Root; }
13606c3fb27SDimitry Andric 
1375f757f3fSDimitry Andric   void addProfiledFunction(FunctionId Name) {
138fe6060f1SDimitry Andric     if (!ProfiledFunctions.count(Name)) {
139fe6060f1SDimitry Andric       // Link to synthetic root to make sure every node is reachable
140fe6060f1SDimitry Andric       // from root. This does not affect SCC order.
1415f757f3fSDimitry Andric       // Store the pointer of the node because the map can be rehashed.
1425f757f3fSDimitry Andric       auto &Node =
1435f757f3fSDimitry Andric           ProfiledCallGraphNodeList.emplace_back(ProfiledCallGraphNode(Name));
1445f757f3fSDimitry Andric       ProfiledFunctions[Name] = &Node;
1455f757f3fSDimitry Andric       Root.Edges.emplace(&Root, ProfiledFunctions[Name], 0);
146fe6060f1SDimitry Andric     }
147fe6060f1SDimitry Andric   }
148fe6060f1SDimitry Andric 
1494824e7fdSDimitry Andric private:
1505f757f3fSDimitry Andric   void addProfiledCall(FunctionId CallerName, FunctionId CalleeName,
1514824e7fdSDimitry Andric                        uint64_t Weight = 0) {
152fe6060f1SDimitry Andric     assert(ProfiledFunctions.count(CallerName));
153fe6060f1SDimitry Andric     auto CalleeIt = ProfiledFunctions.find(CalleeName);
1544824e7fdSDimitry Andric     if (CalleeIt == ProfiledFunctions.end())
155fe6060f1SDimitry Andric       return;
1565f757f3fSDimitry Andric     ProfiledCallGraphEdge Edge(ProfiledFunctions[CallerName],
1575f757f3fSDimitry Andric                                CalleeIt->second, Weight);
1585f757f3fSDimitry Andric     auto &Edges = ProfiledFunctions[CallerName]->Edges;
1594824e7fdSDimitry Andric     auto EdgeIt = Edges.find(Edge);
1604824e7fdSDimitry Andric     if (EdgeIt == Edges.end()) {
1614824e7fdSDimitry Andric       Edges.insert(Edge);
16206c3fb27SDimitry Andric     } else {
16306c3fb27SDimitry Andric       // Accumulate weight to the existing edge.
16406c3fb27SDimitry Andric       Edge.Weight += EdgeIt->Weight;
1654824e7fdSDimitry Andric       Edges.erase(EdgeIt);
1664824e7fdSDimitry Andric       Edges.insert(Edge);
167fe6060f1SDimitry Andric     }
168fe6060f1SDimitry Andric   }
169fe6060f1SDimitry Andric 
170fe6060f1SDimitry Andric   void addProfiledCalls(const FunctionSamples &Samples) {
1715f757f3fSDimitry Andric     addProfiledFunction(Samples.getFunction());
172fe6060f1SDimitry Andric 
173fe6060f1SDimitry Andric     for (const auto &Sample : Samples.getBodySamples()) {
174bdd1243dSDimitry Andric       for (const auto &[Target, Frequency] : Sample.second.getCallTargets()) {
175bdd1243dSDimitry Andric         addProfiledFunction(Target);
1765f757f3fSDimitry Andric         addProfiledCall(Samples.getFunction(), Target, Frequency);
177fe6060f1SDimitry Andric       }
178fe6060f1SDimitry Andric     }
179fe6060f1SDimitry Andric 
180fe6060f1SDimitry Andric     for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
181fe6060f1SDimitry Andric       for (const auto &InlinedSamples : CallsiteSamples.second) {
182fe6060f1SDimitry Andric         addProfiledFunction(InlinedSamples.first);
1835f757f3fSDimitry Andric         addProfiledCall(Samples.getFunction(), InlinedSamples.first,
184fcaf7f86SDimitry Andric                         InlinedSamples.second.getHeadSamplesEstimate());
185fe6060f1SDimitry Andric         addProfiledCalls(InlinedSamples.second);
186fe6060f1SDimitry Andric       }
187fe6060f1SDimitry Andric     }
188fe6060f1SDimitry Andric   }
189fe6060f1SDimitry Andric 
19006c3fb27SDimitry Andric   // Trim edges with weight up to `Threshold`. Do not trim anything if
19106c3fb27SDimitry Andric   // `Threshold` is zero.
19206c3fb27SDimitry Andric   void trimColdEges(uint64_t Threshold = 0) {
19306c3fb27SDimitry Andric     if (!Threshold)
19406c3fb27SDimitry Andric       return;
19506c3fb27SDimitry Andric 
19606c3fb27SDimitry Andric     for (auto &Node : ProfiledFunctions) {
1975f757f3fSDimitry Andric       auto &Edges = Node.second->Edges;
19806c3fb27SDimitry Andric       auto I = Edges.begin();
19906c3fb27SDimitry Andric       while (I != Edges.end()) {
20006c3fb27SDimitry Andric         if (I->Weight <= Threshold)
20106c3fb27SDimitry Andric           I = Edges.erase(I);
20206c3fb27SDimitry Andric         else
20306c3fb27SDimitry Andric           I++;
20406c3fb27SDimitry Andric       }
20506c3fb27SDimitry Andric     }
20606c3fb27SDimitry Andric   }
20706c3fb27SDimitry Andric 
208fe6060f1SDimitry Andric   ProfiledCallGraphNode Root;
2095f757f3fSDimitry Andric   // backing buffer for ProfiledCallGraphNodes.
2105f757f3fSDimitry Andric   std::list<ProfiledCallGraphNode> ProfiledCallGraphNodeList;
2115f757f3fSDimitry Andric   HashKeyMap<llvm::DenseMap, FunctionId, ProfiledCallGraphNode*>
2125f757f3fSDimitry Andric       ProfiledFunctions;
213fe6060f1SDimitry Andric };
214fe6060f1SDimitry Andric 
215fe6060f1SDimitry Andric } // end namespace sampleprof
216fe6060f1SDimitry Andric 
217fe6060f1SDimitry Andric template <> struct GraphTraits<ProfiledCallGraphNode *> {
2184824e7fdSDimitry Andric   using NodeType = ProfiledCallGraphNode;
219fe6060f1SDimitry Andric   using NodeRef = ProfiledCallGraphNode *;
2204824e7fdSDimitry Andric   using EdgeType = NodeType::edge;
2214824e7fdSDimitry Andric   using ChildIteratorType = NodeType::const_iterator;
222fe6060f1SDimitry Andric 
223fe6060f1SDimitry Andric   static NodeRef getEntryNode(NodeRef PCGN) { return PCGN; }
2244824e7fdSDimitry Andric   static ChildIteratorType child_begin(NodeRef N) { return N->Edges.begin(); }
2254824e7fdSDimitry Andric   static ChildIteratorType child_end(NodeRef N) { return N->Edges.end(); }
226fe6060f1SDimitry Andric };
227fe6060f1SDimitry Andric 
228fe6060f1SDimitry Andric template <>
229fe6060f1SDimitry Andric struct GraphTraits<ProfiledCallGraph *>
230fe6060f1SDimitry Andric     : public GraphTraits<ProfiledCallGraphNode *> {
231fe6060f1SDimitry Andric   static NodeRef getEntryNode(ProfiledCallGraph *PCG) {
232fe6060f1SDimitry Andric     return PCG->getEntryNode();
233fe6060f1SDimitry Andric   }
234fe6060f1SDimitry Andric 
235fe6060f1SDimitry Andric   static ChildIteratorType nodes_begin(ProfiledCallGraph *PCG) {
236fe6060f1SDimitry Andric     return PCG->begin();
237fe6060f1SDimitry Andric   }
238fe6060f1SDimitry Andric 
239fe6060f1SDimitry Andric   static ChildIteratorType nodes_end(ProfiledCallGraph *PCG) {
240fe6060f1SDimitry Andric     return PCG->end();
241fe6060f1SDimitry Andric   }
242fe6060f1SDimitry Andric };
243fe6060f1SDimitry Andric 
244fe6060f1SDimitry Andric } // end namespace llvm
245fe6060f1SDimitry Andric 
246fe6060f1SDimitry Andric #endif
247