1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
#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
|