Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:44:40

0001 //===-- ProfiledCallGraph.h - Profiled Call Graph ----------------- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 
0009 #ifndef LLVM_TRANSFORMS_IPO_PROFILEDCALLGRAPH_H
0010 #define LLVM_TRANSFORMS_IPO_PROFILEDCALLGRAPH_H
0011 
0012 #include "llvm/ADT/GraphTraits.h"
0013 #include "llvm/ProfileData/SampleProf.h"
0014 #include "llvm/ProfileData/SampleProfReader.h"
0015 #include "llvm/Transforms/IPO/SampleContextTracker.h"
0016 #include <queue>
0017 #include <set>
0018 
0019 namespace llvm {
0020 namespace sampleprof {
0021 
0022 struct ProfiledCallGraphNode;
0023 
0024 struct ProfiledCallGraphEdge {
0025   ProfiledCallGraphEdge(ProfiledCallGraphNode *Source,
0026                         ProfiledCallGraphNode *Target, uint64_t Weight)
0027       : Source(Source), Target(Target), Weight(Weight) {}
0028   ProfiledCallGraphNode *Source;
0029   ProfiledCallGraphNode *Target;
0030   uint64_t Weight;
0031 
0032   // The call destination is the only important data here,
0033   // allow to transparently unwrap into it.
0034   operator ProfiledCallGraphNode *() const { return Target; }
0035 };
0036 
0037 struct ProfiledCallGraphNode {
0038 
0039   // Sort edges by callee names only since all edges to be compared are from
0040   // same caller. Edge weights are not considered either because for the same
0041   // callee only the edge with the largest weight is added to the edge set.
0042   struct ProfiledCallGraphEdgeComparer {
0043     bool operator()(const ProfiledCallGraphEdge &L,
0044                     const ProfiledCallGraphEdge &R) const {
0045       return L.Target->Name < R.Target->Name;
0046     }
0047   };
0048 
0049   using edge = ProfiledCallGraphEdge;
0050   using edges = std::set<edge, ProfiledCallGraphEdgeComparer>;
0051   using iterator = edges::iterator;
0052   using const_iterator = edges::const_iterator;
0053   
0054   ProfiledCallGraphNode(FunctionId FName = FunctionId()) : Name(FName)
0055   {}
0056   
0057   FunctionId Name;
0058   edges Edges;
0059 };
0060 
0061 class ProfiledCallGraph {
0062 public:
0063   using iterator = ProfiledCallGraphNode::iterator;
0064 
0065   // Constructor for non-CS profile.
0066   ProfiledCallGraph(SampleProfileMap &ProfileMap,
0067                     uint64_t IgnoreColdCallThreshold = 0) {
0068     assert(!FunctionSamples::ProfileIsCS &&
0069            "CS flat profile is not handled here");
0070     for (const auto &Samples : ProfileMap) {
0071       addProfiledCalls(Samples.second);
0072     }
0073 
0074     // Trim edges with weight up to `IgnoreColdCallThreshold`. This aims
0075     // for a more stable call graph with "determinstic" edges from run to run.
0076     trimColdEges(IgnoreColdCallThreshold);
0077   }
0078 
0079   // Constructor for CS profile.
0080   ProfiledCallGraph(SampleContextTracker &ContextTracker,
0081                     uint64_t IgnoreColdCallThreshold = 0) {
0082     // BFS traverse the context profile trie to add call edges for calls shown
0083     // in context.
0084     std::queue<ContextTrieNode *> Queue;
0085     for (auto &Child : ContextTracker.getRootContext().getAllChildContext()) {
0086       ContextTrieNode *Callee = &Child.second;
0087       addProfiledFunction(Callee->getFuncName());
0088       Queue.push(Callee);
0089     }
0090 
0091     while (!Queue.empty()) {
0092       ContextTrieNode *Caller = Queue.front();
0093       Queue.pop();
0094       FunctionSamples *CallerSamples = Caller->getFunctionSamples();
0095 
0096       // Add calls for context.
0097       // Note that callsite target samples are completely ignored since they can
0098       // conflict with the context edges, which are formed by context
0099       // compression during profile generation, for cyclic SCCs. This may
0100       // further result in an SCC order incompatible with the purely
0101       // context-based one, which may in turn block context-based inlining.
0102       for (auto &Child : Caller->getAllChildContext()) {
0103         ContextTrieNode *Callee = &Child.second;
0104         addProfiledFunction(Callee->getFuncName());
0105         Queue.push(Callee);
0106 
0107         // Fetch edge weight from the profile.
0108         uint64_t Weight;
0109         FunctionSamples *CalleeSamples = Callee->getFunctionSamples();
0110         if (!CalleeSamples || !CallerSamples) {
0111           Weight = 0;
0112         } else {
0113           uint64_t CalleeEntryCount = CalleeSamples->getHeadSamplesEstimate();
0114           uint64_t CallsiteCount = 0;
0115           LineLocation Callsite = Callee->getCallSiteLoc();
0116           if (auto CallTargets = CallerSamples->findCallTargetMapAt(Callsite)) {
0117             auto It = CallTargets->find(CalleeSamples->getFunction());
0118             if (It != CallTargets->end())
0119               CallsiteCount = It->second;
0120           }
0121           Weight = std::max(CallsiteCount, CalleeEntryCount);
0122         }
0123 
0124         addProfiledCall(Caller->getFuncName(), Callee->getFuncName(), Weight);
0125       }
0126     }
0127 
0128     // Trim edges with weight up to `IgnoreColdCallThreshold`. This aims
0129     // for a more stable call graph with "determinstic" edges from run to run.
0130     trimColdEges(IgnoreColdCallThreshold);
0131   }
0132 
0133   iterator begin() { return Root.Edges.begin(); }
0134   iterator end() { return Root.Edges.end(); }
0135   ProfiledCallGraphNode *getEntryNode() { return &Root; }
0136   
0137   void addProfiledFunction(FunctionId Name) {
0138     if (!ProfiledFunctions.count(Name)) {
0139       // Link to synthetic root to make sure every node is reachable
0140       // from root. This does not affect SCC order.
0141       // Store the pointer of the node because the map can be rehashed.
0142       auto &Node =
0143           ProfiledCallGraphNodeList.emplace_back(ProfiledCallGraphNode(Name));
0144       ProfiledFunctions[Name] = &Node;
0145       Root.Edges.emplace(&Root, ProfiledFunctions[Name], 0);
0146     }
0147   }
0148 
0149 private:
0150   void addProfiledCall(FunctionId CallerName, FunctionId CalleeName,
0151                        uint64_t Weight = 0) {
0152     assert(ProfiledFunctions.count(CallerName));
0153     auto CalleeIt = ProfiledFunctions.find(CalleeName);
0154     if (CalleeIt == ProfiledFunctions.end())
0155       return;
0156     ProfiledCallGraphEdge Edge(ProfiledFunctions[CallerName],
0157                                CalleeIt->second, Weight);
0158     auto &Edges = ProfiledFunctions[CallerName]->Edges;
0159     auto [EdgeIt, Inserted] = Edges.insert(Edge);
0160     if (!Inserted) {
0161       // Accumulate weight to the existing edge.
0162       Edge.Weight += EdgeIt->Weight;
0163       Edges.erase(EdgeIt);
0164       Edges.insert(Edge);
0165     }
0166   }
0167 
0168   void addProfiledCalls(const FunctionSamples &Samples) {
0169     addProfiledFunction(Samples.getFunction());
0170 
0171     for (const auto &Sample : Samples.getBodySamples()) {
0172       for (const auto &[Target, Frequency] : Sample.second.getCallTargets()) {
0173         addProfiledFunction(Target);
0174         addProfiledCall(Samples.getFunction(), Target, Frequency);
0175       }
0176     }
0177 
0178     for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
0179       for (const auto &InlinedSamples : CallsiteSamples.second) {
0180         addProfiledFunction(InlinedSamples.first);
0181         addProfiledCall(Samples.getFunction(), InlinedSamples.first,
0182                         InlinedSamples.second.getHeadSamplesEstimate());
0183         addProfiledCalls(InlinedSamples.second);
0184       }
0185     }
0186   }
0187 
0188   // Trim edges with weight up to `Threshold`. Do not trim anything if
0189   // `Threshold` is zero.
0190   void trimColdEges(uint64_t Threshold = 0) {
0191     if (!Threshold)
0192       return;
0193 
0194     for (auto &Node : ProfiledFunctions) {
0195       auto &Edges = Node.second->Edges;
0196       auto I = Edges.begin();
0197       while (I != Edges.end()) {
0198         if (I->Weight <= Threshold)
0199           I = Edges.erase(I);
0200         else
0201           I++;
0202       }
0203     }
0204   }
0205 
0206   ProfiledCallGraphNode Root;
0207   // backing buffer for ProfiledCallGraphNodes.
0208   std::list<ProfiledCallGraphNode> ProfiledCallGraphNodeList;
0209   HashKeyMap<llvm::DenseMap, FunctionId, ProfiledCallGraphNode*>
0210       ProfiledFunctions;
0211 };
0212 
0213 } // end namespace sampleprof
0214 
0215 template <> struct GraphTraits<ProfiledCallGraphNode *> {
0216   using NodeType = ProfiledCallGraphNode;
0217   using NodeRef = ProfiledCallGraphNode *;
0218   using EdgeType = NodeType::edge;
0219   using ChildIteratorType = NodeType::const_iterator;
0220 
0221   static NodeRef getEntryNode(NodeRef PCGN) { return PCGN; }
0222   static ChildIteratorType child_begin(NodeRef N) { return N->Edges.begin(); }
0223   static ChildIteratorType child_end(NodeRef N) { return N->Edges.end(); }
0224 };
0225 
0226 template <>
0227 struct GraphTraits<ProfiledCallGraph *>
0228     : public GraphTraits<ProfiledCallGraphNode *> {
0229   static NodeRef getEntryNode(ProfiledCallGraph *PCG) {
0230     return PCG->getEntryNode();
0231   }
0232 
0233   static ChildIteratorType nodes_begin(ProfiledCallGraph *PCG) {
0234     return PCG->begin();
0235   }
0236 
0237   static ChildIteratorType nodes_end(ProfiledCallGraph *PCG) {
0238     return PCG->end();
0239   }
0240 };
0241 
0242 } // end namespace llvm
0243 
0244 #endif