SampleContextTracker.cpp 21 KB

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