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