xref: /freebsd/contrib/llvm-project/llvm/lib/Transforms/IPO/SampleContextTracker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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