SampleContextTracker.cpp 22 KB

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