123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===-- ProfiledCallGraph.h - Profiled Call Graph ----------------- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_TRANSFORMS_IPO_PROFILEDCALLGRAPH_H
- #define LLVM_TRANSFORMS_IPO_PROFILEDCALLGRAPH_H
- #include "llvm/ADT/GraphTraits.h"
- #include "llvm/ADT/StringMap.h"
- #include "llvm/ADT/StringRef.h"
- #include "llvm/ProfileData/SampleProf.h"
- #include "llvm/ProfileData/SampleProfReader.h"
- #include "llvm/Transforms/IPO/SampleContextTracker.h"
- #include <queue>
- #include <set>
- namespace llvm {
- namespace sampleprof {
- struct ProfiledCallGraphNode;
- struct ProfiledCallGraphEdge {
- ProfiledCallGraphEdge(ProfiledCallGraphNode *Source,
- ProfiledCallGraphNode *Target, uint64_t Weight)
- : Source(Source), Target(Target), Weight(Weight) {}
- ProfiledCallGraphNode *Source;
- ProfiledCallGraphNode *Target;
- uint64_t Weight;
- // The call destination is the only important data here,
- // allow to transparently unwrap into it.
- operator ProfiledCallGraphNode *() const { return Target; }
- };
- struct ProfiledCallGraphNode {
- // Sort edges by callee names only since all edges to be compared are from
- // same caller. Edge weights are not considered either because for the same
- // callee only the edge with the largest weight is added to the edge set.
- struct ProfiledCallGraphEdgeComparer {
- bool operator()(const ProfiledCallGraphEdge &L,
- const ProfiledCallGraphEdge &R) const {
- return L.Target->Name < R.Target->Name;
- }
- };
- using edge = ProfiledCallGraphEdge;
- using edges = std::set<edge, ProfiledCallGraphEdgeComparer>;
- using iterator = edges::iterator;
- using const_iterator = edges::const_iterator;
- ProfiledCallGraphNode(StringRef FName = StringRef()) : Name(FName) {}
- StringRef Name;
- edges Edges;
- };
- class ProfiledCallGraph {
- public:
- using iterator = ProfiledCallGraphNode::iterator;
- // Constructor for non-CS profile.
- ProfiledCallGraph(SampleProfileMap &ProfileMap) {
- assert(!FunctionSamples::ProfileIsCS &&
- "CS flat profile is not handled here");
- for (const auto &Samples : ProfileMap) {
- addProfiledCalls(Samples.second);
- }
- }
- // Constructor for CS profile.
- ProfiledCallGraph(SampleContextTracker &ContextTracker) {
- // BFS traverse the context profile trie to add call edges for calls shown
- // in context.
- std::queue<ContextTrieNode *> Queue;
- for (auto &Child : ContextTracker.getRootContext().getAllChildContext()) {
- ContextTrieNode *Callee = &Child.second;
- addProfiledFunction(ContextTracker.getFuncNameFor(Callee));
- Queue.push(Callee);
- }
- while (!Queue.empty()) {
- ContextTrieNode *Caller = Queue.front();
- Queue.pop();
- FunctionSamples *CallerSamples = Caller->getFunctionSamples();
- // Add calls for context.
- // Note that callsite target samples are completely ignored since they can
- // conflict with the context edges, which are formed by context
- // compression during profile generation, for cyclic SCCs. This may
- // further result in an SCC order incompatible with the purely
- // context-based one, which may in turn block context-based inlining.
- for (auto &Child : Caller->getAllChildContext()) {
- ContextTrieNode *Callee = &Child.second;
- addProfiledFunction(ContextTracker.getFuncNameFor(Callee));
- Queue.push(Callee);
- // Fetch edge weight from the profile.
- uint64_t Weight;
- FunctionSamples *CalleeSamples = Callee->getFunctionSamples();
- if (!CalleeSamples || !CallerSamples) {
- Weight = 0;
- } else {
- uint64_t CalleeEntryCount = CalleeSamples->getHeadSamplesEstimate();
- uint64_t CallsiteCount = 0;
- LineLocation Callsite = Callee->getCallSiteLoc();
- if (auto CallTargets = CallerSamples->findCallTargetMapAt(Callsite)) {
- SampleRecord::CallTargetMap &TargetCounts = CallTargets.get();
- auto It = TargetCounts.find(CalleeSamples->getName());
- if (It != TargetCounts.end())
- CallsiteCount = It->second;
- }
- Weight = std::max(CallsiteCount, CalleeEntryCount);
- }
- addProfiledCall(ContextTracker.getFuncNameFor(Caller),
- ContextTracker.getFuncNameFor(Callee), Weight);
- }
- }
- }
- iterator begin() { return Root.Edges.begin(); }
- iterator end() { return Root.Edges.end(); }
- ProfiledCallGraphNode *getEntryNode() { return &Root; }
- void addProfiledFunction(StringRef Name) {
- if (!ProfiledFunctions.count(Name)) {
- // Link to synthetic root to make sure every node is reachable
- // from root. This does not affect SCC order.
- ProfiledFunctions[Name] = ProfiledCallGraphNode(Name);
- Root.Edges.emplace(&Root, &ProfiledFunctions[Name], 0);
- }
- }
- private:
- void addProfiledCall(StringRef CallerName, StringRef CalleeName,
- uint64_t Weight = 0) {
- assert(ProfiledFunctions.count(CallerName));
- auto CalleeIt = ProfiledFunctions.find(CalleeName);
- if (CalleeIt == ProfiledFunctions.end())
- return;
- ProfiledCallGraphEdge Edge(&ProfiledFunctions[CallerName],
- &CalleeIt->second, Weight);
- auto &Edges = ProfiledFunctions[CallerName].Edges;
- auto EdgeIt = Edges.find(Edge);
- if (EdgeIt == Edges.end()) {
- Edges.insert(Edge);
- } else if (EdgeIt->Weight < Edge.Weight) {
- // Replace existing call edges with same target but smaller weight.
- Edges.erase(EdgeIt);
- Edges.insert(Edge);
- }
- }
- void addProfiledCalls(const FunctionSamples &Samples) {
- addProfiledFunction(Samples.getFuncName());
- for (const auto &Sample : Samples.getBodySamples()) {
- for (const auto &[Target, Frequency] : Sample.second.getCallTargets()) {
- addProfiledFunction(Target);
- addProfiledCall(Samples.getFuncName(), Target, Frequency);
- }
- }
- for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
- for (const auto &InlinedSamples : CallsiteSamples.second) {
- addProfiledFunction(InlinedSamples.first);
- addProfiledCall(Samples.getFuncName(), InlinedSamples.first,
- InlinedSamples.second.getHeadSamplesEstimate());
- addProfiledCalls(InlinedSamples.second);
- }
- }
- }
- ProfiledCallGraphNode Root;
- StringMap<ProfiledCallGraphNode> ProfiledFunctions;
- };
- } // end namespace sampleprof
- template <> struct GraphTraits<ProfiledCallGraphNode *> {
- using NodeType = ProfiledCallGraphNode;
- using NodeRef = ProfiledCallGraphNode *;
- using EdgeType = NodeType::edge;
- using ChildIteratorType = NodeType::const_iterator;
- static NodeRef getEntryNode(NodeRef PCGN) { return PCGN; }
- static ChildIteratorType child_begin(NodeRef N) { return N->Edges.begin(); }
- static ChildIteratorType child_end(NodeRef N) { return N->Edges.end(); }
- };
- template <>
- struct GraphTraits<ProfiledCallGraph *>
- : public GraphTraits<ProfiledCallGraphNode *> {
- static NodeRef getEntryNode(ProfiledCallGraph *PCG) {
- return PCG->getEntryNode();
- }
- static ChildIteratorType nodes_begin(ProfiledCallGraph *PCG) {
- return PCG->begin();
- }
- static ChildIteratorType nodes_end(ProfiledCallGraph *PCG) {
- return PCG->end();
- }
- };
- } // end namespace llvm
- #endif
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|