xref: /freebsd-src/contrib/llvm-project/llvm/lib/Transforms/IPO/SampleContextTracker.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
1e8d8bef9SDimitry Andric //===- SampleContextTracker.cpp - Context-sensitive Profile Tracker -------===//
2e8d8bef9SDimitry Andric //
3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e8d8bef9SDimitry Andric //
7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8e8d8bef9SDimitry Andric //
9e8d8bef9SDimitry Andric // This file implements the SampleContextTracker used by CSSPGO.
10e8d8bef9SDimitry Andric //
11e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
12e8d8bef9SDimitry Andric 
13e8d8bef9SDimitry Andric #include "llvm/Transforms/IPO/SampleContextTracker.h"
14e8d8bef9SDimitry Andric #include "llvm/ADT/StringMap.h"
15e8d8bef9SDimitry Andric #include "llvm/ADT/StringRef.h"
16e8d8bef9SDimitry Andric #include "llvm/IR/DebugInfoMetadata.h"
17e8d8bef9SDimitry Andric #include "llvm/IR/Instructions.h"
18e8d8bef9SDimitry Andric #include "llvm/ProfileData/SampleProf.h"
19e8d8bef9SDimitry Andric #include <map>
20e8d8bef9SDimitry Andric #include <queue>
21e8d8bef9SDimitry Andric #include <vector>
22e8d8bef9SDimitry Andric 
23e8d8bef9SDimitry Andric using namespace llvm;
24e8d8bef9SDimitry Andric using namespace sampleprof;
25e8d8bef9SDimitry Andric 
26e8d8bef9SDimitry Andric #define DEBUG_TYPE "sample-context-tracker"
27e8d8bef9SDimitry Andric 
28e8d8bef9SDimitry Andric namespace llvm {
29e8d8bef9SDimitry Andric 
30e8d8bef9SDimitry Andric ContextTrieNode *ContextTrieNode::getChildContext(const LineLocation &CallSite,
31e8d8bef9SDimitry Andric                                                   StringRef CalleeName) {
32e8d8bef9SDimitry Andric   if (CalleeName.empty())
33d409305fSDimitry Andric     return getHottestChildContext(CallSite);
34e8d8bef9SDimitry Andric 
35e8d8bef9SDimitry Andric   uint32_t Hash = nodeHash(CalleeName, CallSite);
36e8d8bef9SDimitry Andric   auto It = AllChildContext.find(Hash);
37e8d8bef9SDimitry Andric   if (It != AllChildContext.end())
38e8d8bef9SDimitry Andric     return &It->second;
39e8d8bef9SDimitry Andric   return nullptr;
40e8d8bef9SDimitry Andric }
41e8d8bef9SDimitry Andric 
42e8d8bef9SDimitry Andric ContextTrieNode *
43d409305fSDimitry Andric ContextTrieNode::getHottestChildContext(const LineLocation &CallSite) {
44e8d8bef9SDimitry Andric   // CSFDO-TODO: This could be slow, change AllChildContext so we can
45e8d8bef9SDimitry Andric   // do point look up for child node by call site alone.
46d409305fSDimitry Andric   // Retrieve the child node with max count for indirect call
47e8d8bef9SDimitry Andric   ContextTrieNode *ChildNodeRet = nullptr;
48d409305fSDimitry Andric   uint64_t MaxCalleeSamples = 0;
49e8d8bef9SDimitry Andric   for (auto &It : AllChildContext) {
50e8d8bef9SDimitry Andric     ContextTrieNode &ChildNode = It.second;
51d409305fSDimitry Andric     if (ChildNode.CallSiteLoc != CallSite)
52d409305fSDimitry Andric       continue;
53d409305fSDimitry Andric     FunctionSamples *Samples = ChildNode.getFunctionSamples();
54d409305fSDimitry Andric     if (!Samples)
55d409305fSDimitry Andric       continue;
56d409305fSDimitry Andric     if (Samples->getTotalSamples() > MaxCalleeSamples) {
57e8d8bef9SDimitry Andric       ChildNodeRet = &ChildNode;
58d409305fSDimitry Andric       MaxCalleeSamples = Samples->getTotalSamples();
59e8d8bef9SDimitry Andric     }
60e8d8bef9SDimitry Andric   }
61e8d8bef9SDimitry Andric 
62e8d8bef9SDimitry Andric   return ChildNodeRet;
63e8d8bef9SDimitry Andric }
64e8d8bef9SDimitry Andric 
65e8d8bef9SDimitry Andric ContextTrieNode &ContextTrieNode::moveToChildContext(
66e8d8bef9SDimitry Andric     const LineLocation &CallSite, ContextTrieNode &&NodeToMove,
67e8d8bef9SDimitry Andric     StringRef ContextStrToRemove, bool DeleteNode) {
68e8d8bef9SDimitry Andric   uint32_t Hash = nodeHash(NodeToMove.getFuncName(), CallSite);
69e8d8bef9SDimitry Andric   assert(!AllChildContext.count(Hash) && "Node to remove must exist");
70e8d8bef9SDimitry Andric   LineLocation OldCallSite = NodeToMove.CallSiteLoc;
71e8d8bef9SDimitry Andric   ContextTrieNode &OldParentContext = *NodeToMove.getParentContext();
72e8d8bef9SDimitry Andric   AllChildContext[Hash] = NodeToMove;
73e8d8bef9SDimitry Andric   ContextTrieNode &NewNode = AllChildContext[Hash];
74e8d8bef9SDimitry Andric   NewNode.CallSiteLoc = CallSite;
75e8d8bef9SDimitry Andric 
76e8d8bef9SDimitry Andric   // Walk through nodes in the moved the subtree, and update
77e8d8bef9SDimitry Andric   // FunctionSamples' context as for the context promotion.
78e8d8bef9SDimitry Andric   // We also need to set new parant link for all children.
79e8d8bef9SDimitry Andric   std::queue<ContextTrieNode *> NodeToUpdate;
80e8d8bef9SDimitry Andric   NewNode.setParentContext(this);
81e8d8bef9SDimitry Andric   NodeToUpdate.push(&NewNode);
82e8d8bef9SDimitry Andric 
83e8d8bef9SDimitry Andric   while (!NodeToUpdate.empty()) {
84e8d8bef9SDimitry Andric     ContextTrieNode *Node = NodeToUpdate.front();
85e8d8bef9SDimitry Andric     NodeToUpdate.pop();
86e8d8bef9SDimitry Andric     FunctionSamples *FSamples = Node->getFunctionSamples();
87e8d8bef9SDimitry Andric 
88e8d8bef9SDimitry Andric     if (FSamples) {
89e8d8bef9SDimitry Andric       FSamples->getContext().promoteOnPath(ContextStrToRemove);
90e8d8bef9SDimitry Andric       FSamples->getContext().setState(SyntheticContext);
91e8d8bef9SDimitry Andric       LLVM_DEBUG(dbgs() << "  Context promoted to: " << FSamples->getContext()
92e8d8bef9SDimitry Andric                         << "\n");
93e8d8bef9SDimitry Andric     }
94e8d8bef9SDimitry Andric 
95e8d8bef9SDimitry Andric     for (auto &It : Node->getAllChildContext()) {
96e8d8bef9SDimitry Andric       ContextTrieNode *ChildNode = &It.second;
97e8d8bef9SDimitry Andric       ChildNode->setParentContext(Node);
98e8d8bef9SDimitry Andric       NodeToUpdate.push(ChildNode);
99e8d8bef9SDimitry Andric     }
100e8d8bef9SDimitry Andric   }
101e8d8bef9SDimitry Andric 
102e8d8bef9SDimitry Andric   // Original context no longer needed, destroy if requested.
103e8d8bef9SDimitry Andric   if (DeleteNode)
104e8d8bef9SDimitry Andric     OldParentContext.removeChildContext(OldCallSite, NewNode.getFuncName());
105e8d8bef9SDimitry Andric 
106e8d8bef9SDimitry Andric   return NewNode;
107e8d8bef9SDimitry Andric }
108e8d8bef9SDimitry Andric 
109e8d8bef9SDimitry Andric void ContextTrieNode::removeChildContext(const LineLocation &CallSite,
110e8d8bef9SDimitry Andric                                          StringRef CalleeName) {
111e8d8bef9SDimitry Andric   uint32_t Hash = nodeHash(CalleeName, CallSite);
112e8d8bef9SDimitry Andric   // Note this essentially calls dtor and destroys that child context
113e8d8bef9SDimitry Andric   AllChildContext.erase(Hash);
114e8d8bef9SDimitry Andric }
115e8d8bef9SDimitry Andric 
116e8d8bef9SDimitry Andric std::map<uint32_t, ContextTrieNode> &ContextTrieNode::getAllChildContext() {
117e8d8bef9SDimitry Andric   return AllChildContext;
118e8d8bef9SDimitry Andric }
119e8d8bef9SDimitry Andric 
120*fe6060f1SDimitry Andric StringRef ContextTrieNode::getFuncName() const { return FuncName; }
121e8d8bef9SDimitry Andric 
122e8d8bef9SDimitry Andric FunctionSamples *ContextTrieNode::getFunctionSamples() const {
123e8d8bef9SDimitry Andric   return FuncSamples;
124e8d8bef9SDimitry Andric }
125e8d8bef9SDimitry Andric 
126e8d8bef9SDimitry Andric void ContextTrieNode::setFunctionSamples(FunctionSamples *FSamples) {
127e8d8bef9SDimitry Andric   FuncSamples = FSamples;
128e8d8bef9SDimitry Andric }
129e8d8bef9SDimitry Andric 
130e8d8bef9SDimitry Andric LineLocation ContextTrieNode::getCallSiteLoc() const { return CallSiteLoc; }
131e8d8bef9SDimitry Andric 
132e8d8bef9SDimitry Andric ContextTrieNode *ContextTrieNode::getParentContext() const {
133e8d8bef9SDimitry Andric   return ParentContext;
134e8d8bef9SDimitry Andric }
135e8d8bef9SDimitry Andric 
136e8d8bef9SDimitry Andric void ContextTrieNode::setParentContext(ContextTrieNode *Parent) {
137e8d8bef9SDimitry Andric   ParentContext = Parent;
138e8d8bef9SDimitry Andric }
139e8d8bef9SDimitry Andric 
140e8d8bef9SDimitry Andric void ContextTrieNode::dump() {
141e8d8bef9SDimitry Andric   dbgs() << "Node: " << FuncName << "\n"
142e8d8bef9SDimitry Andric          << "  Callsite: " << CallSiteLoc << "\n"
143e8d8bef9SDimitry Andric          << "  Children:\n";
144e8d8bef9SDimitry Andric 
145e8d8bef9SDimitry Andric   for (auto &It : AllChildContext) {
146e8d8bef9SDimitry Andric     dbgs() << "    Node: " << It.second.getFuncName() << "\n";
147e8d8bef9SDimitry Andric   }
148e8d8bef9SDimitry Andric }
149e8d8bef9SDimitry Andric 
150e8d8bef9SDimitry Andric uint32_t ContextTrieNode::nodeHash(StringRef ChildName,
151e8d8bef9SDimitry Andric                                    const LineLocation &Callsite) {
152e8d8bef9SDimitry Andric   // We still use child's name for child hash, this is
153e8d8bef9SDimitry Andric   // because for children of root node, we don't have
154e8d8bef9SDimitry Andric   // different line/discriminator, and we'll rely on name
155e8d8bef9SDimitry Andric   // to differentiate children.
156e8d8bef9SDimitry Andric   uint32_t NameHash = std::hash<std::string>{}(ChildName.str());
157e8d8bef9SDimitry Andric   uint32_t LocId = (Callsite.LineOffset << 16) | Callsite.Discriminator;
158e8d8bef9SDimitry Andric   return NameHash + (LocId << 5) + LocId;
159e8d8bef9SDimitry Andric }
160e8d8bef9SDimitry Andric 
161e8d8bef9SDimitry Andric ContextTrieNode *ContextTrieNode::getOrCreateChildContext(
162e8d8bef9SDimitry Andric     const LineLocation &CallSite, StringRef CalleeName, bool AllowCreate) {
163e8d8bef9SDimitry Andric   uint32_t Hash = nodeHash(CalleeName, CallSite);
164e8d8bef9SDimitry Andric   auto It = AllChildContext.find(Hash);
165e8d8bef9SDimitry Andric   if (It != AllChildContext.end()) {
166e8d8bef9SDimitry Andric     assert(It->second.getFuncName() == CalleeName &&
167e8d8bef9SDimitry Andric            "Hash collision for child context node");
168e8d8bef9SDimitry Andric     return &It->second;
169e8d8bef9SDimitry Andric   }
170e8d8bef9SDimitry Andric 
171e8d8bef9SDimitry Andric   if (!AllowCreate)
172e8d8bef9SDimitry Andric     return nullptr;
173e8d8bef9SDimitry Andric 
174e8d8bef9SDimitry Andric   AllChildContext[Hash] = ContextTrieNode(this, CalleeName, nullptr, CallSite);
175e8d8bef9SDimitry Andric   return &AllChildContext[Hash];
176e8d8bef9SDimitry Andric }
177e8d8bef9SDimitry Andric 
178e8d8bef9SDimitry Andric // Profiler tracker than manages profiles and its associated context
179e8d8bef9SDimitry Andric SampleContextTracker::SampleContextTracker(
180e8d8bef9SDimitry Andric     StringMap<FunctionSamples> &Profiles) {
181e8d8bef9SDimitry Andric   for (auto &FuncSample : Profiles) {
182e8d8bef9SDimitry Andric     FunctionSamples *FSamples = &FuncSample.second;
183e8d8bef9SDimitry Andric     SampleContext Context(FuncSample.first(), RawContext);
184e8d8bef9SDimitry Andric     LLVM_DEBUG(dbgs() << "Tracking Context for function: " << Context << "\n");
185e8d8bef9SDimitry Andric     if (!Context.isBaseContext())
186*fe6060f1SDimitry Andric       FuncToCtxtProfiles[Context.getNameWithoutContext()].push_back(FSamples);
187e8d8bef9SDimitry Andric     ContextTrieNode *NewNode = getOrCreateContextPath(Context, true);
188e8d8bef9SDimitry Andric     assert(!NewNode->getFunctionSamples() &&
189e8d8bef9SDimitry Andric            "New node can't have sample profile");
190e8d8bef9SDimitry Andric     NewNode->setFunctionSamples(FSamples);
191e8d8bef9SDimitry Andric   }
192e8d8bef9SDimitry Andric }
193e8d8bef9SDimitry Andric 
194e8d8bef9SDimitry Andric FunctionSamples *
195e8d8bef9SDimitry Andric SampleContextTracker::getCalleeContextSamplesFor(const CallBase &Inst,
196e8d8bef9SDimitry Andric                                                  StringRef CalleeName) {
197e8d8bef9SDimitry Andric   LLVM_DEBUG(dbgs() << "Getting callee context for instr: " << Inst << "\n");
198e8d8bef9SDimitry Andric   DILocation *DIL = Inst.getDebugLoc();
199e8d8bef9SDimitry Andric   if (!DIL)
200e8d8bef9SDimitry Andric     return nullptr;
201e8d8bef9SDimitry Andric 
202*fe6060f1SDimitry Andric   CalleeName = FunctionSamples::getCanonicalFnName(CalleeName);
203*fe6060f1SDimitry Andric 
204d409305fSDimitry Andric   // For indirect call, CalleeName will be empty, in which case the context
205d409305fSDimitry Andric   // profile for callee with largest total samples will be returned.
206e8d8bef9SDimitry Andric   ContextTrieNode *CalleeContext = getCalleeContextFor(DIL, CalleeName);
207e8d8bef9SDimitry Andric   if (CalleeContext) {
208e8d8bef9SDimitry Andric     FunctionSamples *FSamples = CalleeContext->getFunctionSamples();
209e8d8bef9SDimitry Andric     LLVM_DEBUG(if (FSamples) {
210e8d8bef9SDimitry Andric       dbgs() << "  Callee context found: " << FSamples->getContext() << "\n";
211e8d8bef9SDimitry Andric     });
212e8d8bef9SDimitry Andric     return FSamples;
213e8d8bef9SDimitry Andric   }
214e8d8bef9SDimitry Andric 
215e8d8bef9SDimitry Andric   return nullptr;
216e8d8bef9SDimitry Andric }
217e8d8bef9SDimitry Andric 
218d409305fSDimitry Andric std::vector<const FunctionSamples *>
219d409305fSDimitry Andric SampleContextTracker::getIndirectCalleeContextSamplesFor(
220d409305fSDimitry Andric     const DILocation *DIL) {
221d409305fSDimitry Andric   std::vector<const FunctionSamples *> R;
222d409305fSDimitry Andric   if (!DIL)
223d409305fSDimitry Andric     return R;
224d409305fSDimitry Andric 
225d409305fSDimitry Andric   ContextTrieNode *CallerNode = getContextFor(DIL);
226d409305fSDimitry Andric   LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL);
227d409305fSDimitry Andric   for (auto &It : CallerNode->getAllChildContext()) {
228d409305fSDimitry Andric     ContextTrieNode &ChildNode = It.second;
229d409305fSDimitry Andric     if (ChildNode.getCallSiteLoc() != CallSite)
230d409305fSDimitry Andric       continue;
231d409305fSDimitry Andric     if (FunctionSamples *CalleeSamples = ChildNode.getFunctionSamples())
232d409305fSDimitry Andric       R.push_back(CalleeSamples);
233d409305fSDimitry Andric   }
234d409305fSDimitry Andric 
235d409305fSDimitry Andric   return R;
236d409305fSDimitry Andric }
237d409305fSDimitry Andric 
238e8d8bef9SDimitry Andric FunctionSamples *
239e8d8bef9SDimitry Andric SampleContextTracker::getContextSamplesFor(const DILocation *DIL) {
240e8d8bef9SDimitry Andric   assert(DIL && "Expect non-null location");
241e8d8bef9SDimitry Andric 
242e8d8bef9SDimitry Andric   ContextTrieNode *ContextNode = getContextFor(DIL);
243e8d8bef9SDimitry Andric   if (!ContextNode)
244e8d8bef9SDimitry Andric     return nullptr;
245e8d8bef9SDimitry Andric 
246e8d8bef9SDimitry Andric   // We may have inlined callees during pre-LTO compilation, in which case
247e8d8bef9SDimitry Andric   // we need to rely on the inline stack from !dbg to mark context profile
248e8d8bef9SDimitry Andric   // as inlined, instead of `MarkContextSamplesInlined` during inlining.
249e8d8bef9SDimitry Andric   // Sample profile loader walks through all instructions to get profile,
250e8d8bef9SDimitry Andric   // which calls this function. So once that is done, all previously inlined
251e8d8bef9SDimitry Andric   // context profile should be marked properly.
252e8d8bef9SDimitry Andric   FunctionSamples *Samples = ContextNode->getFunctionSamples();
253e8d8bef9SDimitry Andric   if (Samples && ContextNode->getParentContext() != &RootContext)
254e8d8bef9SDimitry Andric     Samples->getContext().setState(InlinedContext);
255e8d8bef9SDimitry Andric 
256e8d8bef9SDimitry Andric   return Samples;
257e8d8bef9SDimitry Andric }
258e8d8bef9SDimitry Andric 
259e8d8bef9SDimitry Andric FunctionSamples *
260e8d8bef9SDimitry Andric SampleContextTracker::getContextSamplesFor(const SampleContext &Context) {
261e8d8bef9SDimitry Andric   ContextTrieNode *Node = getContextFor(Context);
262e8d8bef9SDimitry Andric   if (!Node)
263e8d8bef9SDimitry Andric     return nullptr;
264e8d8bef9SDimitry Andric 
265e8d8bef9SDimitry Andric   return Node->getFunctionSamples();
266e8d8bef9SDimitry Andric }
267e8d8bef9SDimitry Andric 
268d409305fSDimitry Andric SampleContextTracker::ContextSamplesTy &
269d409305fSDimitry Andric SampleContextTracker::getAllContextSamplesFor(const Function &Func) {
270d409305fSDimitry Andric   StringRef CanonName = FunctionSamples::getCanonicalFnName(Func);
271*fe6060f1SDimitry Andric   return FuncToCtxtProfiles[CanonName];
272d409305fSDimitry Andric }
273d409305fSDimitry Andric 
274d409305fSDimitry Andric SampleContextTracker::ContextSamplesTy &
275d409305fSDimitry Andric SampleContextTracker::getAllContextSamplesFor(StringRef Name) {
276*fe6060f1SDimitry Andric   return FuncToCtxtProfiles[Name];
277d409305fSDimitry Andric }
278d409305fSDimitry Andric 
279e8d8bef9SDimitry Andric FunctionSamples *SampleContextTracker::getBaseSamplesFor(const Function &Func,
280e8d8bef9SDimitry Andric                                                          bool MergeContext) {
281e8d8bef9SDimitry Andric   StringRef CanonName = FunctionSamples::getCanonicalFnName(Func);
282e8d8bef9SDimitry Andric   return getBaseSamplesFor(CanonName, MergeContext);
283e8d8bef9SDimitry Andric }
284e8d8bef9SDimitry Andric 
285e8d8bef9SDimitry Andric FunctionSamples *SampleContextTracker::getBaseSamplesFor(StringRef Name,
286e8d8bef9SDimitry Andric                                                          bool MergeContext) {
287e8d8bef9SDimitry Andric   LLVM_DEBUG(dbgs() << "Getting base profile for function: " << Name << "\n");
288e8d8bef9SDimitry Andric   // Base profile is top-level node (child of root node), so try to retrieve
289e8d8bef9SDimitry Andric   // existing top-level node for given function first. If it exists, it could be
290e8d8bef9SDimitry Andric   // that we've merged base profile before, or there's actually context-less
291e8d8bef9SDimitry Andric   // profile from the input (e.g. due to unreliable stack walking).
292e8d8bef9SDimitry Andric   ContextTrieNode *Node = getTopLevelContextNode(Name);
293e8d8bef9SDimitry Andric   if (MergeContext) {
294e8d8bef9SDimitry Andric     LLVM_DEBUG(dbgs() << "  Merging context profile into base profile: " << Name
295e8d8bef9SDimitry Andric                       << "\n");
296e8d8bef9SDimitry Andric 
297e8d8bef9SDimitry Andric     // We have profile for function under different contexts,
298e8d8bef9SDimitry Andric     // create synthetic base profile and merge context profiles
299e8d8bef9SDimitry Andric     // into base profile.
300*fe6060f1SDimitry Andric     for (auto *CSamples : FuncToCtxtProfiles[Name]) {
301e8d8bef9SDimitry Andric       SampleContext &Context = CSamples->getContext();
302e8d8bef9SDimitry Andric       ContextTrieNode *FromNode = getContextFor(Context);
303e8d8bef9SDimitry Andric       if (FromNode == Node)
304e8d8bef9SDimitry Andric         continue;
305e8d8bef9SDimitry Andric 
306e8d8bef9SDimitry Andric       // Skip inlined context profile and also don't re-merge any context
307e8d8bef9SDimitry Andric       if (Context.hasState(InlinedContext) || Context.hasState(MergedContext))
308e8d8bef9SDimitry Andric         continue;
309e8d8bef9SDimitry Andric 
310e8d8bef9SDimitry Andric       ContextTrieNode &ToNode = promoteMergeContextSamplesTree(*FromNode);
311e8d8bef9SDimitry Andric       assert((!Node || Node == &ToNode) && "Expect only one base profile");
312e8d8bef9SDimitry Andric       Node = &ToNode;
313e8d8bef9SDimitry Andric     }
314e8d8bef9SDimitry Andric   }
315e8d8bef9SDimitry Andric 
316e8d8bef9SDimitry Andric   // Still no profile even after merge/promotion (if allowed)
317e8d8bef9SDimitry Andric   if (!Node)
318e8d8bef9SDimitry Andric     return nullptr;
319e8d8bef9SDimitry Andric 
320e8d8bef9SDimitry Andric   return Node->getFunctionSamples();
321e8d8bef9SDimitry Andric }
322e8d8bef9SDimitry Andric 
323e8d8bef9SDimitry Andric void SampleContextTracker::markContextSamplesInlined(
324e8d8bef9SDimitry Andric     const FunctionSamples *InlinedSamples) {
325e8d8bef9SDimitry Andric   assert(InlinedSamples && "Expect non-null inlined samples");
326e8d8bef9SDimitry Andric   LLVM_DEBUG(dbgs() << "Marking context profile as inlined: "
327e8d8bef9SDimitry Andric                     << InlinedSamples->getContext() << "\n");
328e8d8bef9SDimitry Andric   InlinedSamples->getContext().setState(InlinedContext);
329e8d8bef9SDimitry Andric }
330e8d8bef9SDimitry Andric 
331*fe6060f1SDimitry Andric ContextTrieNode &SampleContextTracker::getRootContext() { return RootContext; }
332*fe6060f1SDimitry Andric 
333e8d8bef9SDimitry Andric void SampleContextTracker::promoteMergeContextSamplesTree(
334e8d8bef9SDimitry Andric     const Instruction &Inst, StringRef CalleeName) {
335e8d8bef9SDimitry Andric   LLVM_DEBUG(dbgs() << "Promoting and merging context tree for instr: \n"
336e8d8bef9SDimitry Andric                     << Inst << "\n");
337e8d8bef9SDimitry Andric   // Get the caller context for the call instruction, we don't use callee
338e8d8bef9SDimitry Andric   // name from call because there can be context from indirect calls too.
339e8d8bef9SDimitry Andric   DILocation *DIL = Inst.getDebugLoc();
340e8d8bef9SDimitry Andric   ContextTrieNode *CallerNode = getContextFor(DIL);
341e8d8bef9SDimitry Andric   if (!CallerNode)
342e8d8bef9SDimitry Andric     return;
343e8d8bef9SDimitry Andric 
344e8d8bef9SDimitry Andric   // Get the context that needs to be promoted
345d409305fSDimitry Andric   LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL);
346d409305fSDimitry Andric   // For indirect call, CalleeName will be empty, in which case we need to
347d409305fSDimitry Andric   // promote all non-inlined child context profiles.
348d409305fSDimitry Andric   if (CalleeName.empty()) {
349d409305fSDimitry Andric     for (auto &It : CallerNode->getAllChildContext()) {
350d409305fSDimitry Andric       ContextTrieNode *NodeToPromo = &It.second;
351d409305fSDimitry Andric       if (CallSite != NodeToPromo->getCallSiteLoc())
352d409305fSDimitry Andric         continue;
353d409305fSDimitry Andric       FunctionSamples *FromSamples = NodeToPromo->getFunctionSamples();
354d409305fSDimitry Andric       if (FromSamples && FromSamples->getContext().hasState(InlinedContext))
355d409305fSDimitry Andric         continue;
356d409305fSDimitry Andric       promoteMergeContextSamplesTree(*NodeToPromo);
357d409305fSDimitry Andric     }
358d409305fSDimitry Andric     return;
359d409305fSDimitry Andric   }
360d409305fSDimitry Andric 
361d409305fSDimitry Andric   // Get the context for the given callee that needs to be promoted
362e8d8bef9SDimitry Andric   ContextTrieNode *NodeToPromo =
363e8d8bef9SDimitry Andric       CallerNode->getChildContext(CallSite, CalleeName);
364e8d8bef9SDimitry Andric   if (!NodeToPromo)
365e8d8bef9SDimitry Andric     return;
366e8d8bef9SDimitry Andric 
367e8d8bef9SDimitry Andric   promoteMergeContextSamplesTree(*NodeToPromo);
368e8d8bef9SDimitry Andric }
369e8d8bef9SDimitry Andric 
370e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree(
371e8d8bef9SDimitry Andric     ContextTrieNode &NodeToPromo) {
372e8d8bef9SDimitry Andric   // Promote the input node to be directly under root. This can happen
373e8d8bef9SDimitry Andric   // when we decided to not inline a function under context represented
374e8d8bef9SDimitry Andric   // by the input node. The promote and merge is then needed to reflect
375e8d8bef9SDimitry Andric   // the context profile in the base (context-less) profile.
376e8d8bef9SDimitry Andric   FunctionSamples *FromSamples = NodeToPromo.getFunctionSamples();
377e8d8bef9SDimitry Andric   assert(FromSamples && "Shouldn't promote a context without profile");
378e8d8bef9SDimitry Andric   LLVM_DEBUG(dbgs() << "  Found context tree root to promote: "
379e8d8bef9SDimitry Andric                     << FromSamples->getContext() << "\n");
380e8d8bef9SDimitry Andric 
381d409305fSDimitry Andric   assert(!FromSamples->getContext().hasState(InlinedContext) &&
382d409305fSDimitry Andric          "Shouldn't promote inlined context profile");
383e8d8bef9SDimitry Andric   StringRef ContextStrToRemove = FromSamples->getContext().getCallingContext();
384e8d8bef9SDimitry Andric   return promoteMergeContextSamplesTree(NodeToPromo, RootContext,
385e8d8bef9SDimitry Andric                                         ContextStrToRemove);
386e8d8bef9SDimitry Andric }
387e8d8bef9SDimitry Andric 
388e8d8bef9SDimitry Andric void SampleContextTracker::dump() {
389e8d8bef9SDimitry Andric   dbgs() << "Context Profile Tree:\n";
390e8d8bef9SDimitry Andric   std::queue<ContextTrieNode *> NodeQueue;
391e8d8bef9SDimitry Andric   NodeQueue.push(&RootContext);
392e8d8bef9SDimitry Andric 
393e8d8bef9SDimitry Andric   while (!NodeQueue.empty()) {
394e8d8bef9SDimitry Andric     ContextTrieNode *Node = NodeQueue.front();
395e8d8bef9SDimitry Andric     NodeQueue.pop();
396e8d8bef9SDimitry Andric     Node->dump();
397e8d8bef9SDimitry Andric 
398e8d8bef9SDimitry Andric     for (auto &It : Node->getAllChildContext()) {
399e8d8bef9SDimitry Andric       ContextTrieNode *ChildNode = &It.second;
400e8d8bef9SDimitry Andric       NodeQueue.push(ChildNode);
401e8d8bef9SDimitry Andric     }
402e8d8bef9SDimitry Andric   }
403e8d8bef9SDimitry Andric }
404e8d8bef9SDimitry Andric 
405e8d8bef9SDimitry Andric ContextTrieNode *
406e8d8bef9SDimitry Andric SampleContextTracker::getContextFor(const SampleContext &Context) {
407e8d8bef9SDimitry Andric   return getOrCreateContextPath(Context, false);
408e8d8bef9SDimitry Andric }
409e8d8bef9SDimitry Andric 
410e8d8bef9SDimitry Andric ContextTrieNode *
411e8d8bef9SDimitry Andric SampleContextTracker::getCalleeContextFor(const DILocation *DIL,
412e8d8bef9SDimitry Andric                                           StringRef CalleeName) {
413e8d8bef9SDimitry Andric   assert(DIL && "Expect non-null location");
414e8d8bef9SDimitry Andric 
415e8d8bef9SDimitry Andric   ContextTrieNode *CallContext = getContextFor(DIL);
416e8d8bef9SDimitry Andric   if (!CallContext)
417e8d8bef9SDimitry Andric     return nullptr;
418e8d8bef9SDimitry Andric 
419d409305fSDimitry Andric   // When CalleeName is empty, the child context profile with max
420d409305fSDimitry Andric   // total samples will be returned.
421e8d8bef9SDimitry Andric   return CallContext->getChildContext(
422d409305fSDimitry Andric       FunctionSamples::getCallSiteIdentifier(DIL), CalleeName);
423e8d8bef9SDimitry Andric }
424e8d8bef9SDimitry Andric 
425e8d8bef9SDimitry Andric ContextTrieNode *SampleContextTracker::getContextFor(const DILocation *DIL) {
426e8d8bef9SDimitry Andric   assert(DIL && "Expect non-null location");
427e8d8bef9SDimitry Andric   SmallVector<std::pair<LineLocation, StringRef>, 10> S;
428e8d8bef9SDimitry Andric 
429e8d8bef9SDimitry Andric   // Use C++ linkage name if possible.
430e8d8bef9SDimitry Andric   const DILocation *PrevDIL = DIL;
431e8d8bef9SDimitry Andric   for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) {
432e8d8bef9SDimitry Andric     StringRef Name = PrevDIL->getScope()->getSubprogram()->getLinkageName();
433e8d8bef9SDimitry Andric     if (Name.empty())
434e8d8bef9SDimitry Andric       Name = PrevDIL->getScope()->getSubprogram()->getName();
435e8d8bef9SDimitry Andric     S.push_back(
436*fe6060f1SDimitry Andric         std::make_pair(FunctionSamples::getCallSiteIdentifier(DIL), Name));
437e8d8bef9SDimitry Andric     PrevDIL = DIL;
438e8d8bef9SDimitry Andric   }
439e8d8bef9SDimitry Andric 
440e8d8bef9SDimitry Andric   // Push root node, note that root node like main may only
441e8d8bef9SDimitry Andric   // a name, but not linkage name.
442e8d8bef9SDimitry Andric   StringRef RootName = PrevDIL->getScope()->getSubprogram()->getLinkageName();
443e8d8bef9SDimitry Andric   if (RootName.empty())
444e8d8bef9SDimitry Andric     RootName = PrevDIL->getScope()->getSubprogram()->getName();
445e8d8bef9SDimitry Andric   S.push_back(std::make_pair(LineLocation(0, 0), RootName));
446e8d8bef9SDimitry Andric 
447e8d8bef9SDimitry Andric   ContextTrieNode *ContextNode = &RootContext;
448e8d8bef9SDimitry Andric   int I = S.size();
449e8d8bef9SDimitry Andric   while (--I >= 0 && ContextNode) {
450e8d8bef9SDimitry Andric     LineLocation &CallSite = S[I].first;
451e8d8bef9SDimitry Andric     StringRef &CalleeName = S[I].second;
452e8d8bef9SDimitry Andric     ContextNode = ContextNode->getChildContext(CallSite, CalleeName);
453e8d8bef9SDimitry Andric   }
454e8d8bef9SDimitry Andric 
455e8d8bef9SDimitry Andric   if (I < 0)
456e8d8bef9SDimitry Andric     return ContextNode;
457e8d8bef9SDimitry Andric 
458e8d8bef9SDimitry Andric   return nullptr;
459e8d8bef9SDimitry Andric }
460e8d8bef9SDimitry Andric 
461e8d8bef9SDimitry Andric ContextTrieNode *
462e8d8bef9SDimitry Andric SampleContextTracker::getOrCreateContextPath(const SampleContext &Context,
463e8d8bef9SDimitry Andric                                              bool AllowCreate) {
464e8d8bef9SDimitry Andric   ContextTrieNode *ContextNode = &RootContext;
465e8d8bef9SDimitry Andric   StringRef ContextRemain = Context;
466e8d8bef9SDimitry Andric   StringRef ChildContext;
467e8d8bef9SDimitry Andric   StringRef CalleeName;
468e8d8bef9SDimitry Andric   LineLocation CallSiteLoc(0, 0);
469e8d8bef9SDimitry Andric 
470e8d8bef9SDimitry Andric   while (ContextNode && !ContextRemain.empty()) {
471e8d8bef9SDimitry Andric     auto ContextSplit = SampleContext::splitContextString(ContextRemain);
472e8d8bef9SDimitry Andric     ChildContext = ContextSplit.first;
473e8d8bef9SDimitry Andric     ContextRemain = ContextSplit.second;
474e8d8bef9SDimitry Andric     LineLocation NextCallSiteLoc(0, 0);
475e8d8bef9SDimitry Andric     SampleContext::decodeContextString(ChildContext, CalleeName,
476e8d8bef9SDimitry Andric                                        NextCallSiteLoc);
477e8d8bef9SDimitry Andric 
478e8d8bef9SDimitry Andric     // Create child node at parent line/disc location
479e8d8bef9SDimitry Andric     if (AllowCreate) {
480e8d8bef9SDimitry Andric       ContextNode =
481e8d8bef9SDimitry Andric           ContextNode->getOrCreateChildContext(CallSiteLoc, CalleeName);
482e8d8bef9SDimitry Andric     } else {
483e8d8bef9SDimitry Andric       ContextNode = ContextNode->getChildContext(CallSiteLoc, CalleeName);
484e8d8bef9SDimitry Andric     }
485e8d8bef9SDimitry Andric     CallSiteLoc = NextCallSiteLoc;
486e8d8bef9SDimitry Andric   }
487e8d8bef9SDimitry Andric 
488e8d8bef9SDimitry Andric   assert((!AllowCreate || ContextNode) &&
489e8d8bef9SDimitry Andric          "Node must exist if creation is allowed");
490e8d8bef9SDimitry Andric   return ContextNode;
491e8d8bef9SDimitry Andric }
492e8d8bef9SDimitry Andric 
493e8d8bef9SDimitry Andric ContextTrieNode *SampleContextTracker::getTopLevelContextNode(StringRef FName) {
494*fe6060f1SDimitry Andric   assert(!FName.empty() && "Top level node query must provide valid name");
495e8d8bef9SDimitry Andric   return RootContext.getChildContext(LineLocation(0, 0), FName);
496e8d8bef9SDimitry Andric }
497e8d8bef9SDimitry Andric 
498e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::addTopLevelContextNode(StringRef FName) {
499e8d8bef9SDimitry Andric   assert(!getTopLevelContextNode(FName) && "Node to add must not exist");
500e8d8bef9SDimitry Andric   return *RootContext.getOrCreateChildContext(LineLocation(0, 0), FName);
501e8d8bef9SDimitry Andric }
502e8d8bef9SDimitry Andric 
503e8d8bef9SDimitry Andric void SampleContextTracker::mergeContextNode(ContextTrieNode &FromNode,
504e8d8bef9SDimitry Andric                                             ContextTrieNode &ToNode,
505e8d8bef9SDimitry Andric                                             StringRef ContextStrToRemove) {
506e8d8bef9SDimitry Andric   FunctionSamples *FromSamples = FromNode.getFunctionSamples();
507e8d8bef9SDimitry Andric   FunctionSamples *ToSamples = ToNode.getFunctionSamples();
508e8d8bef9SDimitry Andric   if (FromSamples && ToSamples) {
509e8d8bef9SDimitry Andric     // Merge/duplicate FromSamples into ToSamples
510e8d8bef9SDimitry Andric     ToSamples->merge(*FromSamples);
511e8d8bef9SDimitry Andric     ToSamples->getContext().setState(SyntheticContext);
512e8d8bef9SDimitry Andric     FromSamples->getContext().setState(MergedContext);
513e8d8bef9SDimitry Andric   } else if (FromSamples) {
514e8d8bef9SDimitry Andric     // Transfer FromSamples from FromNode to ToNode
515e8d8bef9SDimitry Andric     ToNode.setFunctionSamples(FromSamples);
516e8d8bef9SDimitry Andric     FromSamples->getContext().setState(SyntheticContext);
517e8d8bef9SDimitry Andric     FromSamples->getContext().promoteOnPath(ContextStrToRemove);
518e8d8bef9SDimitry Andric     FromNode.setFunctionSamples(nullptr);
519e8d8bef9SDimitry Andric   }
520e8d8bef9SDimitry Andric }
521e8d8bef9SDimitry Andric 
522e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree(
523e8d8bef9SDimitry Andric     ContextTrieNode &FromNode, ContextTrieNode &ToNodeParent,
524e8d8bef9SDimitry Andric     StringRef ContextStrToRemove) {
525e8d8bef9SDimitry Andric   assert(!ContextStrToRemove.empty() && "Context to remove can't be empty");
526e8d8bef9SDimitry Andric 
527e8d8bef9SDimitry Andric   // Ignore call site location if destination is top level under root
528e8d8bef9SDimitry Andric   LineLocation NewCallSiteLoc = LineLocation(0, 0);
529e8d8bef9SDimitry Andric   LineLocation OldCallSiteLoc = FromNode.getCallSiteLoc();
530e8d8bef9SDimitry Andric   ContextTrieNode &FromNodeParent = *FromNode.getParentContext();
531e8d8bef9SDimitry Andric   ContextTrieNode *ToNode = nullptr;
532e8d8bef9SDimitry Andric   bool MoveToRoot = (&ToNodeParent == &RootContext);
533e8d8bef9SDimitry Andric   if (!MoveToRoot) {
534e8d8bef9SDimitry Andric     NewCallSiteLoc = OldCallSiteLoc;
535e8d8bef9SDimitry Andric   }
536e8d8bef9SDimitry Andric 
537e8d8bef9SDimitry Andric   // Locate destination node, create/move if not existing
538e8d8bef9SDimitry Andric   ToNode = ToNodeParent.getChildContext(NewCallSiteLoc, FromNode.getFuncName());
539e8d8bef9SDimitry Andric   if (!ToNode) {
540e8d8bef9SDimitry Andric     // Do not delete node to move from its parent here because
541e8d8bef9SDimitry Andric     // caller is iterating over children of that parent node.
542e8d8bef9SDimitry Andric     ToNode = &ToNodeParent.moveToChildContext(
543e8d8bef9SDimitry Andric         NewCallSiteLoc, std::move(FromNode), ContextStrToRemove, false);
544e8d8bef9SDimitry Andric   } else {
545e8d8bef9SDimitry Andric     // Destination node exists, merge samples for the context tree
546e8d8bef9SDimitry Andric     mergeContextNode(FromNode, *ToNode, ContextStrToRemove);
547*fe6060f1SDimitry Andric     LLVM_DEBUG({
548*fe6060f1SDimitry Andric       if (ToNode->getFunctionSamples())
549*fe6060f1SDimitry Andric         dbgs() << "  Context promoted and merged to: "
550*fe6060f1SDimitry Andric                << ToNode->getFunctionSamples()->getContext() << "\n";
551*fe6060f1SDimitry Andric     });
552e8d8bef9SDimitry Andric 
553e8d8bef9SDimitry Andric     // Recursively promote and merge children
554e8d8bef9SDimitry Andric     for (auto &It : FromNode.getAllChildContext()) {
555e8d8bef9SDimitry Andric       ContextTrieNode &FromChildNode = It.second;
556e8d8bef9SDimitry Andric       promoteMergeContextSamplesTree(FromChildNode, *ToNode,
557e8d8bef9SDimitry Andric                                      ContextStrToRemove);
558e8d8bef9SDimitry Andric     }
559e8d8bef9SDimitry Andric 
560e8d8bef9SDimitry Andric     // Remove children once they're all merged
561e8d8bef9SDimitry Andric     FromNode.getAllChildContext().clear();
562e8d8bef9SDimitry Andric   }
563e8d8bef9SDimitry Andric 
564e8d8bef9SDimitry Andric   // For root of subtree, remove itself from old parent too
565e8d8bef9SDimitry Andric   if (MoveToRoot)
566e8d8bef9SDimitry Andric     FromNodeParent.removeChildContext(OldCallSiteLoc, ToNode->getFuncName());
567e8d8bef9SDimitry Andric 
568e8d8bef9SDimitry Andric   return *ToNode;
569e8d8bef9SDimitry Andric }
570e8d8bef9SDimitry Andric } // namespace llvm
571