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