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