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