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