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