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 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 * 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 & 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 ContextTrieNode &NewNode = AllChildContext[Hash]; 75 NewNode = NodeToMove; 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 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 112 std::map<uint64_t, ContextTrieNode> &ContextTrieNode::getAllChildContext() { 113 return AllChildContext; 114 } 115 116 FunctionId ContextTrieNode::getFuncName() const { return FuncName; } 117 118 FunctionSamples *ContextTrieNode::getFunctionSamples() const { 119 return FuncSamples; 120 } 121 122 void ContextTrieNode::setFunctionSamples(FunctionSamples *FSamples) { 123 FuncSamples = FSamples; 124 } 125 126 std::optional<uint32_t> ContextTrieNode::getFunctionSize() const { 127 return FuncSize; 128 } 129 130 void ContextTrieNode::addFunctionSize(uint32_t FSize) { 131 if (!FuncSize) 132 FuncSize = 0; 133 134 FuncSize = *FuncSize + FSize; 135 } 136 137 LineLocation ContextTrieNode::getCallSiteLoc() const { return CallSiteLoc; } 138 139 ContextTrieNode *ContextTrieNode::getParentContext() const { 140 return ParentContext; 141 } 142 143 void ContextTrieNode::setParentContext(ContextTrieNode *Parent) { 144 ParentContext = Parent; 145 } 146 147 void ContextTrieNode::setCallSiteLoc(const LineLocation &Loc) { 148 CallSiteLoc = Loc; 149 } 150 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 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 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 ContextTrieNode &ACC = AllChildContext[Hash]; 193 ACC = ContextTrieNode(this, CalleeName, nullptr, CallSite); 194 return &ACC; 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.second.getContext(); 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 236 FunctionId FName = getRepInFormat(CalleeName); 237 238 // For indirect call, CalleeName will be empty, in which case the context 239 // profile for callee with largest total samples will be returned. 240 ContextTrieNode *CalleeContext = getCalleeContextFor(DIL, FName); 241 if (CalleeContext) { 242 FunctionSamples *FSamples = CalleeContext->getFunctionSamples(); 243 LLVM_DEBUG(if (FSamples) { 244 dbgs() << " Callee context found: " << getContextString(CalleeContext) 245 << "\n"; 246 }); 247 return FSamples; 248 } 249 250 return nullptr; 251 } 252 253 std::vector<const FunctionSamples *> 254 SampleContextTracker::getIndirectCalleeContextSamplesFor( 255 const DILocation *DIL) { 256 std::vector<const FunctionSamples *> R; 257 if (!DIL) 258 return R; 259 260 ContextTrieNode *CallerNode = getContextFor(DIL); 261 LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL); 262 for (auto &It : CallerNode->getAllChildContext()) { 263 ContextTrieNode &ChildNode = It.second; 264 if (ChildNode.getCallSiteLoc() != CallSite) 265 continue; 266 if (FunctionSamples *CalleeSamples = ChildNode.getFunctionSamples()) 267 R.push_back(CalleeSamples); 268 } 269 270 return R; 271 } 272 273 FunctionSamples * 274 SampleContextTracker::getContextSamplesFor(const DILocation *DIL) { 275 assert(DIL && "Expect non-null location"); 276 277 ContextTrieNode *ContextNode = getContextFor(DIL); 278 if (!ContextNode) 279 return nullptr; 280 281 // We may have inlined callees during pre-LTO compilation, in which case 282 // we need to rely on the inline stack from !dbg to mark context profile 283 // as inlined, instead of `MarkContextSamplesInlined` during inlining. 284 // Sample profile loader walks through all instructions to get profile, 285 // which calls this function. So once that is done, all previously inlined 286 // context profile should be marked properly. 287 FunctionSamples *Samples = ContextNode->getFunctionSamples(); 288 if (Samples && ContextNode->getParentContext() != &RootContext) 289 Samples->getContext().setState(InlinedContext); 290 291 return Samples; 292 } 293 294 FunctionSamples * 295 SampleContextTracker::getContextSamplesFor(const SampleContext &Context) { 296 ContextTrieNode *Node = getContextFor(Context); 297 if (!Node) 298 return nullptr; 299 300 return Node->getFunctionSamples(); 301 } 302 303 SampleContextTracker::ContextSamplesTy & 304 SampleContextTracker::getAllContextSamplesFor(const Function &Func) { 305 StringRef CanonName = FunctionSamples::getCanonicalFnName(Func); 306 return FuncToCtxtProfiles[getRepInFormat(CanonName)]; 307 } 308 309 SampleContextTracker::ContextSamplesTy & 310 SampleContextTracker::getAllContextSamplesFor(StringRef Name) { 311 return FuncToCtxtProfiles[getRepInFormat(Name)]; 312 } 313 314 FunctionSamples *SampleContextTracker::getBaseSamplesFor(const Function &Func, 315 bool MergeContext) { 316 StringRef CanonName = FunctionSamples::getCanonicalFnName(Func); 317 return getBaseSamplesFor(getRepInFormat(CanonName), MergeContext); 318 } 319 320 FunctionSamples *SampleContextTracker::getBaseSamplesFor(FunctionId Name, 321 bool MergeContext) { 322 LLVM_DEBUG(dbgs() << "Getting base profile for function: " << Name << "\n"); 323 324 // Base profile is top-level node (child of root node), so try to retrieve 325 // existing top-level node for given function first. If it exists, it could be 326 // that we've merged base profile before, or there's actually context-less 327 // profile from the input (e.g. due to unreliable stack walking). 328 ContextTrieNode *Node = getTopLevelContextNode(Name); 329 if (MergeContext) { 330 LLVM_DEBUG(dbgs() << " Merging context profile into base profile: " << Name 331 << "\n"); 332 333 // We have profile for function under different contexts, 334 // create synthetic base profile and merge context profiles 335 // into base profile. 336 for (auto *CSamples : FuncToCtxtProfiles[Name]) { 337 SampleContext &Context = CSamples->getContext(); 338 // Skip inlined context profile and also don't re-merge any context 339 if (Context.hasState(InlinedContext) || Context.hasState(MergedContext)) 340 continue; 341 342 ContextTrieNode *FromNode = getContextNodeForProfile(CSamples); 343 if (FromNode == Node) 344 continue; 345 346 ContextTrieNode &ToNode = promoteMergeContextSamplesTree(*FromNode); 347 assert((!Node || Node == &ToNode) && "Expect only one base profile"); 348 Node = &ToNode; 349 } 350 } 351 352 // Still no profile even after merge/promotion (if allowed) 353 if (!Node) 354 return nullptr; 355 356 return Node->getFunctionSamples(); 357 } 358 359 void SampleContextTracker::markContextSamplesInlined( 360 const FunctionSamples *InlinedSamples) { 361 assert(InlinedSamples && "Expect non-null inlined samples"); 362 LLVM_DEBUG(dbgs() << "Marking context profile as inlined: " 363 << getContextString(*InlinedSamples) << "\n"); 364 InlinedSamples->getContext().setState(InlinedContext); 365 } 366 367 ContextTrieNode &SampleContextTracker::getRootContext() { return RootContext; } 368 369 void SampleContextTracker::promoteMergeContextSamplesTree( 370 const Instruction &Inst, FunctionId CalleeName) { 371 LLVM_DEBUG(dbgs() << "Promoting and merging context tree for instr: \n" 372 << Inst << "\n"); 373 // Get the caller context for the call instruction, we don't use callee 374 // name from call because there can be context from indirect calls too. 375 DILocation *DIL = Inst.getDebugLoc(); 376 ContextTrieNode *CallerNode = getContextFor(DIL); 377 if (!CallerNode) 378 return; 379 380 // Get the context that needs to be promoted 381 LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL); 382 // For indirect call, CalleeName will be empty, in which case we need to 383 // promote all non-inlined child context profiles. 384 if (CalleeName.empty()) { 385 for (auto &It : CallerNode->getAllChildContext()) { 386 ContextTrieNode *NodeToPromo = &It.second; 387 if (CallSite != NodeToPromo->getCallSiteLoc()) 388 continue; 389 FunctionSamples *FromSamples = NodeToPromo->getFunctionSamples(); 390 if (FromSamples && FromSamples->getContext().hasState(InlinedContext)) 391 continue; 392 promoteMergeContextSamplesTree(*NodeToPromo); 393 } 394 return; 395 } 396 397 // Get the context for the given callee that needs to be promoted 398 ContextTrieNode *NodeToPromo = 399 CallerNode->getChildContext(CallSite, CalleeName); 400 if (!NodeToPromo) 401 return; 402 403 promoteMergeContextSamplesTree(*NodeToPromo); 404 } 405 406 ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree( 407 ContextTrieNode &NodeToPromo) { 408 // Promote the input node to be directly under root. This can happen 409 // when we decided to not inline a function under context represented 410 // by the input node. The promote and merge is then needed to reflect 411 // the context profile in the base (context-less) profile. 412 FunctionSamples *FromSamples = NodeToPromo.getFunctionSamples(); 413 assert(FromSamples && "Shouldn't promote a context without profile"); 414 (void)FromSamples; // Unused in release build. 415 416 LLVM_DEBUG(dbgs() << " Found context tree root to promote: " 417 << getContextString(&NodeToPromo) << "\n"); 418 419 assert(!FromSamples->getContext().hasState(InlinedContext) && 420 "Shouldn't promote inlined context profile"); 421 return promoteMergeContextSamplesTree(NodeToPromo, RootContext); 422 } 423 424 #ifndef NDEBUG 425 std::string 426 SampleContextTracker::getContextString(const FunctionSamples &FSamples) const { 427 return getContextString(getContextNodeForProfile(&FSamples)); 428 } 429 430 std::string 431 SampleContextTracker::getContextString(ContextTrieNode *Node) const { 432 SampleContextFrameVector Res; 433 if (Node == &RootContext) 434 return std::string(); 435 Res.emplace_back(Node->getFuncName(), LineLocation(0, 0)); 436 437 ContextTrieNode *PreNode = Node; 438 Node = Node->getParentContext(); 439 while (Node && Node != &RootContext) { 440 Res.emplace_back(Node->getFuncName(), PreNode->getCallSiteLoc()); 441 PreNode = Node; 442 Node = Node->getParentContext(); 443 } 444 445 std::reverse(Res.begin(), Res.end()); 446 447 return SampleContext::getContextString(Res); 448 } 449 #endif 450 451 void SampleContextTracker::dump() { RootContext.dumpTree(); } 452 453 StringRef SampleContextTracker::getFuncNameFor(ContextTrieNode *Node) const { 454 if (!FunctionSamples::UseMD5) 455 return Node->getFuncName().stringRef(); 456 assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be populated first"); 457 return GUIDToFuncNameMap->lookup(Node->getFuncName().getHashCode()); 458 } 459 460 ContextTrieNode * 461 SampleContextTracker::getContextFor(const SampleContext &Context) { 462 return getOrCreateContextPath(Context, false); 463 } 464 465 ContextTrieNode * 466 SampleContextTracker::getCalleeContextFor(const DILocation *DIL, 467 FunctionId CalleeName) { 468 assert(DIL && "Expect non-null location"); 469 470 ContextTrieNode *CallContext = getContextFor(DIL); 471 if (!CallContext) 472 return nullptr; 473 474 // When CalleeName is empty, the child context profile with max 475 // total samples will be returned. 476 return CallContext->getChildContext( 477 FunctionSamples::getCallSiteIdentifier(DIL), CalleeName); 478 } 479 480 ContextTrieNode *SampleContextTracker::getContextFor(const DILocation *DIL) { 481 assert(DIL && "Expect non-null location"); 482 SmallVector<std::pair<LineLocation, FunctionId>, 10> S; 483 484 // Use C++ linkage name if possible. 485 const DILocation *PrevDIL = DIL; 486 for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) { 487 StringRef Name = PrevDIL->getScope()->getSubprogram()->getLinkageName(); 488 if (Name.empty()) 489 Name = PrevDIL->getScope()->getSubprogram()->getName(); 490 S.push_back( 491 std::make_pair(FunctionSamples::getCallSiteIdentifier(DIL), 492 getRepInFormat(Name))); 493 PrevDIL = DIL; 494 } 495 496 // Push root node, note that root node like main may only 497 // a name, but not linkage name. 498 StringRef RootName = PrevDIL->getScope()->getSubprogram()->getLinkageName(); 499 if (RootName.empty()) 500 RootName = PrevDIL->getScope()->getSubprogram()->getName(); 501 S.push_back(std::make_pair(LineLocation(0, 0), 502 getRepInFormat(RootName))); 503 504 ContextTrieNode *ContextNode = &RootContext; 505 int I = S.size(); 506 while (--I >= 0 && ContextNode) { 507 LineLocation &CallSite = S[I].first; 508 FunctionId CalleeName = S[I].second; 509 ContextNode = ContextNode->getChildContext(CallSite, CalleeName); 510 } 511 512 if (I < 0) 513 return ContextNode; 514 515 return nullptr; 516 } 517 518 ContextTrieNode * 519 SampleContextTracker::getOrCreateContextPath(const SampleContext &Context, 520 bool AllowCreate) { 521 ContextTrieNode *ContextNode = &RootContext; 522 LineLocation CallSiteLoc(0, 0); 523 524 for (const auto &Callsite : Context.getContextFrames()) { 525 // Create child node at parent line/disc location 526 if (AllowCreate) { 527 ContextNode = 528 ContextNode->getOrCreateChildContext(CallSiteLoc, Callsite.Func); 529 } else { 530 ContextNode = 531 ContextNode->getChildContext(CallSiteLoc, Callsite.Func); 532 } 533 CallSiteLoc = Callsite.Location; 534 } 535 536 assert((!AllowCreate || ContextNode) && 537 "Node must exist if creation is allowed"); 538 return ContextNode; 539 } 540 541 ContextTrieNode * 542 SampleContextTracker::getTopLevelContextNode(FunctionId FName) { 543 assert(!FName.empty() && "Top level node query must provide valid name"); 544 return RootContext.getChildContext(LineLocation(0, 0), FName); 545 } 546 547 ContextTrieNode & 548 SampleContextTracker::addTopLevelContextNode(FunctionId FName) { 549 assert(!getTopLevelContextNode(FName) && "Node to add must not exist"); 550 return *RootContext.getOrCreateChildContext(LineLocation(0, 0), FName); 551 } 552 553 void SampleContextTracker::mergeContextNode(ContextTrieNode &FromNode, 554 ContextTrieNode &ToNode) { 555 FunctionSamples *FromSamples = FromNode.getFunctionSamples(); 556 FunctionSamples *ToSamples = ToNode.getFunctionSamples(); 557 if (FromSamples && ToSamples) { 558 // Merge/duplicate FromSamples into ToSamples 559 ToSamples->merge(*FromSamples); 560 ToSamples->getContext().setState(SyntheticContext); 561 FromSamples->getContext().setState(MergedContext); 562 if (FromSamples->getContext().hasAttribute(ContextShouldBeInlined)) 563 ToSamples->getContext().setAttribute(ContextShouldBeInlined); 564 } else if (FromSamples) { 565 // Transfer FromSamples from FromNode to ToNode 566 ToNode.setFunctionSamples(FromSamples); 567 setContextNode(FromSamples, &ToNode); 568 FromSamples->getContext().setState(SyntheticContext); 569 } 570 } 571 572 ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree( 573 ContextTrieNode &FromNode, ContextTrieNode &ToNodeParent) { 574 575 // Ignore call site location if destination is top level under root 576 LineLocation NewCallSiteLoc = LineLocation(0, 0); 577 LineLocation OldCallSiteLoc = FromNode.getCallSiteLoc(); 578 ContextTrieNode &FromNodeParent = *FromNode.getParentContext(); 579 ContextTrieNode *ToNode = nullptr; 580 bool MoveToRoot = (&ToNodeParent == &RootContext); 581 if (!MoveToRoot) { 582 NewCallSiteLoc = OldCallSiteLoc; 583 } 584 585 // Locate destination node, create/move if not existing 586 ToNode = ToNodeParent.getChildContext(NewCallSiteLoc, FromNode.getFuncName()); 587 if (!ToNode) { 588 // Do not delete node to move from its parent here because 589 // caller is iterating over children of that parent node. 590 ToNode = 591 &moveContextSamples(ToNodeParent, NewCallSiteLoc, std::move(FromNode)); 592 LLVM_DEBUG({ 593 dbgs() << " Context promoted and merged to: " << getContextString(ToNode) 594 << "\n"; 595 }); 596 } else { 597 // Destination node exists, merge samples for the context tree 598 mergeContextNode(FromNode, *ToNode); 599 LLVM_DEBUG({ 600 if (ToNode->getFunctionSamples()) 601 dbgs() << " Context promoted and merged to: " 602 << getContextString(ToNode) << "\n"; 603 }); 604 605 // Recursively promote and merge children 606 for (auto &It : FromNode.getAllChildContext()) { 607 ContextTrieNode &FromChildNode = It.second; 608 promoteMergeContextSamplesTree(FromChildNode, *ToNode); 609 } 610 611 // Remove children once they're all merged 612 FromNode.getAllChildContext().clear(); 613 } 614 615 // For root of subtree, remove itself from old parent too 616 if (MoveToRoot) 617 FromNodeParent.removeChildContext(OldCallSiteLoc, ToNode->getFuncName()); 618 619 return *ToNode; 620 } 621 622 void SampleContextTracker::createContextLessProfileMap( 623 SampleProfileMap &ContextLessProfiles) { 624 for (auto *Node : *this) { 625 FunctionSamples *FProfile = Node->getFunctionSamples(); 626 // Profile's context can be empty, use ContextNode's func name. 627 if (FProfile) 628 ContextLessProfiles.create(Node->getFuncName()).merge(*FProfile); 629 } 630 } 631 } // namespace llvm 632