16b989a17SWenlei He //===- SampleContextTracker.cpp - Context-sensitive Profile Tracker -------===// 26b989a17SWenlei He // 36b989a17SWenlei He // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 46b989a17SWenlei He // See https://llvm.org/LICENSE.txt for license information. 56b989a17SWenlei He // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 66b989a17SWenlei He // 76b989a17SWenlei He //===----------------------------------------------------------------------===// 86b989a17SWenlei He // 96b989a17SWenlei He // This file implements the SampleContextTracker used by CSSPGO. 106b989a17SWenlei He // 116b989a17SWenlei He //===----------------------------------------------------------------------===// 126b989a17SWenlei He 136b989a17SWenlei He #include "llvm/Transforms/IPO/SampleContextTracker.h" 146b989a17SWenlei He #include "llvm/ADT/StringRef.h" 156b989a17SWenlei He #include "llvm/IR/DebugInfoMetadata.h" 1601be9be2Sserge-sans-paille #include "llvm/IR/InstrTypes.h" 1701be9be2Sserge-sans-paille #include "llvm/IR/Instruction.h" 186b989a17SWenlei He #include "llvm/ProfileData/SampleProf.h" 196b989a17SWenlei He #include <map> 206b989a17SWenlei He #include <queue> 216b989a17SWenlei He #include <vector> 226b989a17SWenlei He 236b989a17SWenlei He using namespace llvm; 246b989a17SWenlei He using namespace sampleprof; 256b989a17SWenlei He 266b989a17SWenlei He #define DEBUG_TYPE "sample-context-tracker" 276b989a17SWenlei He 286b989a17SWenlei He namespace llvm { 296b989a17SWenlei He 306b989a17SWenlei He ContextTrieNode *ContextTrieNode::getChildContext(const LineLocation &CallSite, 31ef0e0adcSWilliam Junda Huang FunctionId CalleeName) { 326b989a17SWenlei He if (CalleeName.empty()) 336bae5973SWenlei He return getHottestChildContext(CallSite); 346b989a17SWenlei He 355740bb80SHongtao Yu uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite); 366b989a17SWenlei He auto It = AllChildContext.find(Hash); 376b989a17SWenlei He if (It != AllChildContext.end()) 386b989a17SWenlei He return &It->second; 396b989a17SWenlei He return nullptr; 406b989a17SWenlei He } 416b989a17SWenlei He 426b989a17SWenlei He ContextTrieNode * 436bae5973SWenlei He ContextTrieNode::getHottestChildContext(const LineLocation &CallSite) { 446b989a17SWenlei He // CSFDO-TODO: This could be slow, change AllChildContext so we can 456b989a17SWenlei He // do point look up for child node by call site alone. 466bae5973SWenlei He // Retrieve the child node with max count for indirect call 476b989a17SWenlei He ContextTrieNode *ChildNodeRet = nullptr; 486bae5973SWenlei He uint64_t MaxCalleeSamples = 0; 496b989a17SWenlei He for (auto &It : AllChildContext) { 506b989a17SWenlei He ContextTrieNode &ChildNode = It.second; 516bae5973SWenlei He if (ChildNode.CallSiteLoc != CallSite) 526bae5973SWenlei He continue; 536bae5973SWenlei He FunctionSamples *Samples = ChildNode.getFunctionSamples(); 546bae5973SWenlei He if (!Samples) 556bae5973SWenlei He continue; 566bae5973SWenlei He if (Samples->getTotalSamples() > MaxCalleeSamples) { 576b989a17SWenlei He ChildNodeRet = &ChildNode; 586bae5973SWenlei He MaxCalleeSamples = Samples->getTotalSamples(); 596b989a17SWenlei He } 606b989a17SWenlei He } 616b989a17SWenlei He 626b989a17SWenlei He return ChildNodeRet; 636b989a17SWenlei He } 646b989a17SWenlei He 657e86b13cSwlei ContextTrieNode & 667e86b13cSwlei SampleContextTracker::moveContextSamples(ContextTrieNode &ToNodeParent, 677e86b13cSwlei const LineLocation &CallSite, 687e86b13cSwlei ContextTrieNode &&NodeToMove) { 695740bb80SHongtao Yu uint64_t Hash = 705740bb80SHongtao Yu FunctionSamples::getCallSiteHash(NodeToMove.getFuncName(), CallSite); 717e86b13cSwlei std::map<uint64_t, ContextTrieNode> &AllChildContext = 727e86b13cSwlei ToNodeParent.getAllChildContext(); 736b989a17SWenlei He assert(!AllChildContext.count(Hash) && "Node to remove must exist"); 746b989a17SWenlei He AllChildContext[Hash] = NodeToMove; 756b989a17SWenlei He ContextTrieNode &NewNode = AllChildContext[Hash]; 767e86b13cSwlei NewNode.setCallSiteLoc(CallSite); 776b989a17SWenlei He 786b989a17SWenlei He // Walk through nodes in the moved the subtree, and update 796b989a17SWenlei He // FunctionSamples' context as for the context promotion. 806b989a17SWenlei He // We also need to set new parant link for all children. 816b989a17SWenlei He std::queue<ContextTrieNode *> NodeToUpdate; 827e86b13cSwlei NewNode.setParentContext(&ToNodeParent); 836b989a17SWenlei He NodeToUpdate.push(&NewNode); 846b989a17SWenlei He 856b989a17SWenlei He while (!NodeToUpdate.empty()) { 866b989a17SWenlei He ContextTrieNode *Node = NodeToUpdate.front(); 876b989a17SWenlei He NodeToUpdate.pop(); 886b989a17SWenlei He FunctionSamples *FSamples = Node->getFunctionSamples(); 896b989a17SWenlei He 906b989a17SWenlei He if (FSamples) { 917e86b13cSwlei setContextNode(FSamples, Node); 926b989a17SWenlei He FSamples->getContext().setState(SyntheticContext); 936b989a17SWenlei He } 946b989a17SWenlei He 956b989a17SWenlei He for (auto &It : Node->getAllChildContext()) { 966b989a17SWenlei He ContextTrieNode *ChildNode = &It.second; 976b989a17SWenlei He ChildNode->setParentContext(Node); 986b989a17SWenlei He NodeToUpdate.push(ChildNode); 996b989a17SWenlei He } 1006b989a17SWenlei He } 1016b989a17SWenlei He 1026b989a17SWenlei He return NewNode; 1036b989a17SWenlei He } 1046b989a17SWenlei He 1056b989a17SWenlei He void ContextTrieNode::removeChildContext(const LineLocation &CallSite, 106ef0e0adcSWilliam Junda Huang FunctionId CalleeName) { 1075740bb80SHongtao Yu uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite); 1086b989a17SWenlei He // Note this essentially calls dtor and destroys that child context 1096b989a17SWenlei He AllChildContext.erase(Hash); 1106b989a17SWenlei He } 1116b989a17SWenlei He 112042cefd2SHongtao Yu std::map<uint64_t, ContextTrieNode> &ContextTrieNode::getAllChildContext() { 1136b989a17SWenlei He return AllChildContext; 1146b989a17SWenlei He } 1156b989a17SWenlei He 116ef0e0adcSWilliam Junda Huang FunctionId ContextTrieNode::getFuncName() const { return FuncName; } 1176b989a17SWenlei He 1186b989a17SWenlei He FunctionSamples *ContextTrieNode::getFunctionSamples() const { 1196b989a17SWenlei He return FuncSamples; 1206b989a17SWenlei He } 1216b989a17SWenlei He 1226b989a17SWenlei He void ContextTrieNode::setFunctionSamples(FunctionSamples *FSamples) { 1236b989a17SWenlei He FuncSamples = FSamples; 1246b989a17SWenlei He } 1256b989a17SWenlei He 12675801e3bSFangrui Song std::optional<uint32_t> ContextTrieNode::getFunctionSize() const { 12775801e3bSFangrui Song return FuncSize; 12875801e3bSFangrui Song } 129eca03d27SWenlei He 130a6f15e9aSWenlei He void ContextTrieNode::addFunctionSize(uint32_t FSize) { 131a7938c74SKazu Hirata if (!FuncSize) 132a6f15e9aSWenlei He FuncSize = 0; 133a6f15e9aSWenlei He 13421c4dc79SFangrui Song FuncSize = *FuncSize + FSize; 135a6f15e9aSWenlei He } 136eca03d27SWenlei He 1376b989a17SWenlei He LineLocation ContextTrieNode::getCallSiteLoc() const { return CallSiteLoc; } 1386b989a17SWenlei He 1396b989a17SWenlei He ContextTrieNode *ContextTrieNode::getParentContext() const { 1406b989a17SWenlei He return ParentContext; 1416b989a17SWenlei He } 1426b989a17SWenlei He 1436b989a17SWenlei He void ContextTrieNode::setParentContext(ContextTrieNode *Parent) { 1446b989a17SWenlei He ParentContext = Parent; 1456b989a17SWenlei He } 1466b989a17SWenlei He 1477e86b13cSwlei void ContextTrieNode::setCallSiteLoc(const LineLocation &Loc) { 1487e86b13cSwlei CallSiteLoc = Loc; 1497e86b13cSwlei } 1507e86b13cSwlei 151eca03d27SWenlei He void ContextTrieNode::dumpNode() { 1526b989a17SWenlei He dbgs() << "Node: " << FuncName << "\n" 1536b989a17SWenlei He << " Callsite: " << CallSiteLoc << "\n" 154eca03d27SWenlei He << " Size: " << FuncSize << "\n" 1556b989a17SWenlei He << " Children:\n"; 1566b989a17SWenlei He 1576b989a17SWenlei He for (auto &It : AllChildContext) { 1586b989a17SWenlei He dbgs() << " Node: " << It.second.getFuncName() << "\n"; 1596b989a17SWenlei He } 1606b989a17SWenlei He } 1616b989a17SWenlei He 162eca03d27SWenlei He void ContextTrieNode::dumpTree() { 163eca03d27SWenlei He dbgs() << "Context Profile Tree:\n"; 164eca03d27SWenlei He std::queue<ContextTrieNode *> NodeQueue; 165eca03d27SWenlei He NodeQueue.push(this); 166eca03d27SWenlei He 167eca03d27SWenlei He while (!NodeQueue.empty()) { 168eca03d27SWenlei He ContextTrieNode *Node = NodeQueue.front(); 169eca03d27SWenlei He NodeQueue.pop(); 170eca03d27SWenlei He Node->dumpNode(); 171eca03d27SWenlei He 172eca03d27SWenlei He for (auto &It : Node->getAllChildContext()) { 173eca03d27SWenlei He ContextTrieNode *ChildNode = &It.second; 174eca03d27SWenlei He NodeQueue.push(ChildNode); 175eca03d27SWenlei He } 176eca03d27SWenlei He } 177eca03d27SWenlei He } 178eca03d27SWenlei He 1796b989a17SWenlei He ContextTrieNode *ContextTrieNode::getOrCreateChildContext( 180ef0e0adcSWilliam Junda Huang const LineLocation &CallSite, FunctionId CalleeName, bool AllowCreate) { 1815740bb80SHongtao Yu uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite); 1826b989a17SWenlei He auto It = AllChildContext.find(Hash); 1836b989a17SWenlei He if (It != AllChildContext.end()) { 1846b989a17SWenlei He assert(It->second.getFuncName() == CalleeName && 1856b989a17SWenlei He "Hash collision for child context node"); 1866b989a17SWenlei He return &It->second; 1876b989a17SWenlei He } 1886b989a17SWenlei He 1896b989a17SWenlei He if (!AllowCreate) 1906b989a17SWenlei He return nullptr; 1916b989a17SWenlei He 192a6f15e9aSWenlei He AllChildContext[Hash] = ContextTrieNode(this, CalleeName, nullptr, CallSite); 1936b989a17SWenlei He return &AllChildContext[Hash]; 1946b989a17SWenlei He } 1956b989a17SWenlei He 1966b989a17SWenlei He // Profiler tracker than manages profiles and its associated context 1977ca80300SHongtao Yu SampleContextTracker::SampleContextTracker( 1987ca80300SHongtao Yu SampleProfileMap &Profiles, 1997ca80300SHongtao Yu const DenseMap<uint64_t, StringRef> *GUIDToFuncNameMap) 2007ca80300SHongtao Yu : GUIDToFuncNameMap(GUIDToFuncNameMap) { 2016b989a17SWenlei He for (auto &FuncSample : Profiles) { 2026b989a17SWenlei He FunctionSamples *FSamples = &FuncSample.second; 2037624de5bSWilliam Huang SampleContext Context = FuncSample.second.getContext(); 204b9db7036SHongtao Yu LLVM_DEBUG(dbgs() << "Tracking Context for function: " << Context.toString() 205b9db7036SHongtao Yu << "\n"); 2066b989a17SWenlei He ContextTrieNode *NewNode = getOrCreateContextPath(Context, true); 2076b989a17SWenlei He assert(!NewNode->getFunctionSamples() && 2086b989a17SWenlei He "New node can't have sample profile"); 2096b989a17SWenlei He NewNode->setFunctionSamples(FSamples); 2106b989a17SWenlei He } 2117e86b13cSwlei populateFuncToCtxtMap(); 2127e86b13cSwlei } 2137e86b13cSwlei 2147e86b13cSwlei void SampleContextTracker::populateFuncToCtxtMap() { 2157e86b13cSwlei for (auto *Node : *this) { 2167e86b13cSwlei FunctionSamples *FSamples = Node->getFunctionSamples(); 2177e86b13cSwlei if (FSamples) { 2187e86b13cSwlei FSamples->getContext().setState(RawContext); 2197e86b13cSwlei setContextNode(FSamples, Node); 2207e86b13cSwlei FuncToCtxtProfiles[Node->getFuncName()].push_back(FSamples); 2217e86b13cSwlei } 2227e86b13cSwlei } 2236b989a17SWenlei He } 2246b989a17SWenlei He 2256b989a17SWenlei He FunctionSamples * 2266b989a17SWenlei He SampleContextTracker::getCalleeContextSamplesFor(const CallBase &Inst, 2276b989a17SWenlei He StringRef CalleeName) { 2286b989a17SWenlei He LLVM_DEBUG(dbgs() << "Getting callee context for instr: " << Inst << "\n"); 2296b989a17SWenlei He DILocation *DIL = Inst.getDebugLoc(); 2306b989a17SWenlei He if (!DIL) 2316b989a17SWenlei He return nullptr; 2326b989a17SWenlei He 233ee35784aSWei Mi CalleeName = FunctionSamples::getCanonicalFnName(CalleeName); 234ef0e0adcSWilliam Junda Huang 235ef0e0adcSWilliam Junda Huang FunctionId FName = getRepInFormat(CalleeName); 236ee35784aSWei Mi 2376bae5973SWenlei He // For indirect call, CalleeName will be empty, in which case the context 2386bae5973SWenlei He // profile for callee with largest total samples will be returned. 239ef0e0adcSWilliam Junda Huang ContextTrieNode *CalleeContext = getCalleeContextFor(DIL, FName); 2406b989a17SWenlei He if (CalleeContext) { 2416b989a17SWenlei He FunctionSamples *FSamples = CalleeContext->getFunctionSamples(); 2426b989a17SWenlei He LLVM_DEBUG(if (FSamples) { 2437e86b13cSwlei dbgs() << " Callee context found: " << getContextString(CalleeContext) 244b9db7036SHongtao Yu << "\n"; 2456b989a17SWenlei He }); 2466b989a17SWenlei He return FSamples; 2476b989a17SWenlei He } 2486b989a17SWenlei He 2496b989a17SWenlei He return nullptr; 2506b989a17SWenlei He } 2516b989a17SWenlei He 2526bae5973SWenlei He std::vector<const FunctionSamples *> 2536bae5973SWenlei He SampleContextTracker::getIndirectCalleeContextSamplesFor( 2546bae5973SWenlei He const DILocation *DIL) { 2556bae5973SWenlei He std::vector<const FunctionSamples *> R; 2566bae5973SWenlei He if (!DIL) 2576bae5973SWenlei He return R; 2586bae5973SWenlei He 2596bae5973SWenlei He ContextTrieNode *CallerNode = getContextFor(DIL); 2606bae5973SWenlei He LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL); 2616bae5973SWenlei He for (auto &It : CallerNode->getAllChildContext()) { 2626bae5973SWenlei He ContextTrieNode &ChildNode = It.second; 2636bae5973SWenlei He if (ChildNode.getCallSiteLoc() != CallSite) 2646bae5973SWenlei He continue; 2656bae5973SWenlei He if (FunctionSamples *CalleeSamples = ChildNode.getFunctionSamples()) 2666bae5973SWenlei He R.push_back(CalleeSamples); 2676bae5973SWenlei He } 2686bae5973SWenlei He 2696bae5973SWenlei He return R; 2706bae5973SWenlei He } 2716bae5973SWenlei He 2726b989a17SWenlei He FunctionSamples * 2736b989a17SWenlei He SampleContextTracker::getContextSamplesFor(const DILocation *DIL) { 2746b989a17SWenlei He assert(DIL && "Expect non-null location"); 2756b989a17SWenlei He 2766b989a17SWenlei He ContextTrieNode *ContextNode = getContextFor(DIL); 2776b989a17SWenlei He if (!ContextNode) 2786b989a17SWenlei He return nullptr; 2796b989a17SWenlei He 2806b989a17SWenlei He // We may have inlined callees during pre-LTO compilation, in which case 2816b989a17SWenlei He // we need to rely on the inline stack from !dbg to mark context profile 2826b989a17SWenlei He // as inlined, instead of `MarkContextSamplesInlined` during inlining. 2836b989a17SWenlei He // Sample profile loader walks through all instructions to get profile, 2846b989a17SWenlei He // which calls this function. So once that is done, all previously inlined 2856b989a17SWenlei He // context profile should be marked properly. 2866b989a17SWenlei He FunctionSamples *Samples = ContextNode->getFunctionSamples(); 2876b989a17SWenlei He if (Samples && ContextNode->getParentContext() != &RootContext) 2886b989a17SWenlei He Samples->getContext().setState(InlinedContext); 2896b989a17SWenlei He 2906b989a17SWenlei He return Samples; 2916b989a17SWenlei He } 2926b989a17SWenlei He 2936b989a17SWenlei He FunctionSamples * 2946b989a17SWenlei He SampleContextTracker::getContextSamplesFor(const SampleContext &Context) { 2956b989a17SWenlei He ContextTrieNode *Node = getContextFor(Context); 2966b989a17SWenlei He if (!Node) 2976b989a17SWenlei He return nullptr; 2986b989a17SWenlei He 2996b989a17SWenlei He return Node->getFunctionSamples(); 3006b989a17SWenlei He } 3016b989a17SWenlei He 302de40f6d6SHongtao Yu SampleContextTracker::ContextSamplesTy & 303de40f6d6SHongtao Yu SampleContextTracker::getAllContextSamplesFor(const Function &Func) { 304de40f6d6SHongtao Yu StringRef CanonName = FunctionSamples::getCanonicalFnName(Func); 305ef0e0adcSWilliam Junda Huang return FuncToCtxtProfiles[getRepInFormat(CanonName)]; 306de40f6d6SHongtao Yu } 307de40f6d6SHongtao Yu 308de40f6d6SHongtao Yu SampleContextTracker::ContextSamplesTy & 309de40f6d6SHongtao Yu SampleContextTracker::getAllContextSamplesFor(StringRef Name) { 310ef0e0adcSWilliam Junda Huang return FuncToCtxtProfiles[getRepInFormat(Name)]; 311de40f6d6SHongtao Yu } 312de40f6d6SHongtao Yu 3136b989a17SWenlei He FunctionSamples *SampleContextTracker::getBaseSamplesFor(const Function &Func, 3146b989a17SWenlei He bool MergeContext) { 3156b989a17SWenlei He StringRef CanonName = FunctionSamples::getCanonicalFnName(Func); 316ef0e0adcSWilliam Junda Huang return getBaseSamplesFor(getRepInFormat(CanonName), MergeContext); 3176b989a17SWenlei He } 3186b989a17SWenlei He 319ef0e0adcSWilliam Junda Huang FunctionSamples *SampleContextTracker::getBaseSamplesFor(FunctionId Name, 3206b989a17SWenlei He bool MergeContext) { 3216b989a17SWenlei He LLVM_DEBUG(dbgs() << "Getting base profile for function: " << Name << "\n"); 3227ca80300SHongtao Yu 3236b989a17SWenlei He // Base profile is top-level node (child of root node), so try to retrieve 3246b989a17SWenlei He // existing top-level node for given function first. If it exists, it could be 3256b989a17SWenlei He // that we've merged base profile before, or there's actually context-less 3266b989a17SWenlei He // profile from the input (e.g. due to unreliable stack walking). 3276b989a17SWenlei He ContextTrieNode *Node = getTopLevelContextNode(Name); 3286b989a17SWenlei He if (MergeContext) { 3296b989a17SWenlei He LLVM_DEBUG(dbgs() << " Merging context profile into base profile: " << Name 3306b989a17SWenlei He << "\n"); 3316b989a17SWenlei He 3326b989a17SWenlei He // We have profile for function under different contexts, 3336b989a17SWenlei He // create synthetic base profile and merge context profiles 3346b989a17SWenlei He // into base profile. 335ca721042SHuihui Zhang for (auto *CSamples : FuncToCtxtProfiles[Name]) { 3366b989a17SWenlei He SampleContext &Context = CSamples->getContext(); 3376b989a17SWenlei He // Skip inlined context profile and also don't re-merge any context 3386b989a17SWenlei He if (Context.hasState(InlinedContext) || Context.hasState(MergedContext)) 3396b989a17SWenlei He continue; 3406b989a17SWenlei He 3417e86b13cSwlei ContextTrieNode *FromNode = getContextNodeForProfile(CSamples); 342b9db7036SHongtao Yu if (FromNode == Node) 343b9db7036SHongtao Yu continue; 344b9db7036SHongtao Yu 3456b989a17SWenlei He ContextTrieNode &ToNode = promoteMergeContextSamplesTree(*FromNode); 34650dd1dbaSSimon Pilgrim assert((!Node || Node == &ToNode) && "Expect only one base profile"); 3476b989a17SWenlei He Node = &ToNode; 3486b989a17SWenlei He } 3496b989a17SWenlei He } 3506b989a17SWenlei He 3516b989a17SWenlei He // Still no profile even after merge/promotion (if allowed) 3526b989a17SWenlei He if (!Node) 3536b989a17SWenlei He return nullptr; 3546b989a17SWenlei He 3556b989a17SWenlei He return Node->getFunctionSamples(); 3566b989a17SWenlei He } 3576b989a17SWenlei He 3586b989a17SWenlei He void SampleContextTracker::markContextSamplesInlined( 3596b989a17SWenlei He const FunctionSamples *InlinedSamples) { 3606b989a17SWenlei He assert(InlinedSamples && "Expect non-null inlined samples"); 3616b989a17SWenlei He LLVM_DEBUG(dbgs() << "Marking context profile as inlined: " 3627e86b13cSwlei << getContextString(*InlinedSamples) << "\n"); 3636b989a17SWenlei He InlinedSamples->getContext().setState(InlinedContext); 3646b989a17SWenlei He } 3656b989a17SWenlei He 36630b02323SWenlei He ContextTrieNode &SampleContextTracker::getRootContext() { return RootContext; } 36730b02323SWenlei He 3686b989a17SWenlei He void SampleContextTracker::promoteMergeContextSamplesTree( 369ef0e0adcSWilliam Junda Huang const Instruction &Inst, FunctionId CalleeName) { 3706b989a17SWenlei He LLVM_DEBUG(dbgs() << "Promoting and merging context tree for instr: \n" 3716b989a17SWenlei He << Inst << "\n"); 3726b989a17SWenlei He // Get the caller context for the call instruction, we don't use callee 3736b989a17SWenlei He // name from call because there can be context from indirect calls too. 3746b989a17SWenlei He DILocation *DIL = Inst.getDebugLoc(); 3756b989a17SWenlei He ContextTrieNode *CallerNode = getContextFor(DIL); 3766b989a17SWenlei He if (!CallerNode) 3776b989a17SWenlei He return; 3786b989a17SWenlei He 3796b989a17SWenlei He // Get the context that needs to be promoted 380224fee82SHongtao Yu LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL); 3816bae5973SWenlei He // For indirect call, CalleeName will be empty, in which case we need to 3826bae5973SWenlei He // promote all non-inlined child context profiles. 3836bae5973SWenlei He if (CalleeName.empty()) { 3846bae5973SWenlei He for (auto &It : CallerNode->getAllChildContext()) { 3856bae5973SWenlei He ContextTrieNode *NodeToPromo = &It.second; 3866bae5973SWenlei He if (CallSite != NodeToPromo->getCallSiteLoc()) 3876bae5973SWenlei He continue; 3886bae5973SWenlei He FunctionSamples *FromSamples = NodeToPromo->getFunctionSamples(); 3896bae5973SWenlei He if (FromSamples && FromSamples->getContext().hasState(InlinedContext)) 3906bae5973SWenlei He continue; 3916bae5973SWenlei He promoteMergeContextSamplesTree(*NodeToPromo); 3926bae5973SWenlei He } 3936bae5973SWenlei He return; 3946bae5973SWenlei He } 3956bae5973SWenlei He 3966bae5973SWenlei He // Get the context for the given callee that needs to be promoted 3976b989a17SWenlei He ContextTrieNode *NodeToPromo = 3986b989a17SWenlei He CallerNode->getChildContext(CallSite, CalleeName); 3996b989a17SWenlei He if (!NodeToPromo) 4006b989a17SWenlei He return; 4016b989a17SWenlei He 4026b989a17SWenlei He promoteMergeContextSamplesTree(*NodeToPromo); 4036b989a17SWenlei He } 4046b989a17SWenlei He 4056b989a17SWenlei He ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree( 4066b989a17SWenlei He ContextTrieNode &NodeToPromo) { 4076b989a17SWenlei He // Promote the input node to be directly under root. This can happen 4086b989a17SWenlei He // when we decided to not inline a function under context represented 4096b989a17SWenlei He // by the input node. The promote and merge is then needed to reflect 4106b989a17SWenlei He // the context profile in the base (context-less) profile. 4116b989a17SWenlei He FunctionSamples *FromSamples = NodeToPromo.getFunctionSamples(); 4126b989a17SWenlei He assert(FromSamples && "Shouldn't promote a context without profile"); 413c6c124caSMikhail Goncharov (void)FromSamples; // Unused in release build. 414c6c124caSMikhail Goncharov 4156b989a17SWenlei He LLVM_DEBUG(dbgs() << " Found context tree root to promote: " 4167e86b13cSwlei << getContextString(&NodeToPromo) << "\n"); 4176b989a17SWenlei He 4186bae5973SWenlei He assert(!FromSamples->getContext().hasState(InlinedContext) && 4196bae5973SWenlei He "Shouldn't promote inlined context profile"); 4207e86b13cSwlei return promoteMergeContextSamplesTree(NodeToPromo, RootContext); 4216b989a17SWenlei He } 4226b989a17SWenlei He 4237e86b13cSwlei #ifndef NDEBUG 4247e86b13cSwlei std::string 4257e86b13cSwlei SampleContextTracker::getContextString(const FunctionSamples &FSamples) const { 4267e86b13cSwlei return getContextString(getContextNodeForProfile(&FSamples)); 4277e86b13cSwlei } 4287e86b13cSwlei 4297e86b13cSwlei std::string 4307e86b13cSwlei SampleContextTracker::getContextString(ContextTrieNode *Node) const { 4317e86b13cSwlei SampleContextFrameVector Res; 4327e86b13cSwlei if (Node == &RootContext) 4337e86b13cSwlei return std::string(); 4347e86b13cSwlei Res.emplace_back(Node->getFuncName(), LineLocation(0, 0)); 4357e86b13cSwlei 4367e86b13cSwlei ContextTrieNode *PreNode = Node; 4377e86b13cSwlei Node = Node->getParentContext(); 4387e86b13cSwlei while (Node && Node != &RootContext) { 4397e86b13cSwlei Res.emplace_back(Node->getFuncName(), PreNode->getCallSiteLoc()); 4407e86b13cSwlei PreNode = Node; 4417e86b13cSwlei Node = Node->getParentContext(); 4427e86b13cSwlei } 4437e86b13cSwlei 4447e86b13cSwlei std::reverse(Res.begin(), Res.end()); 4457e86b13cSwlei 4467e86b13cSwlei return SampleContext::getContextString(Res); 4477e86b13cSwlei } 4487e86b13cSwlei #endif 4497e86b13cSwlei 450eca03d27SWenlei He void SampleContextTracker::dump() { RootContext.dumpTree(); } 4516b989a17SWenlei He 4527ca80300SHongtao Yu StringRef SampleContextTracker::getFuncNameFor(ContextTrieNode *Node) const { 4537ca80300SHongtao Yu if (!FunctionSamples::UseMD5) 454ef0e0adcSWilliam Junda Huang return Node->getFuncName().stringRef(); 4557ca80300SHongtao Yu assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be populated first"); 456ef0e0adcSWilliam Junda Huang return GUIDToFuncNameMap->lookup(Node->getFuncName().getHashCode()); 4577ca80300SHongtao Yu } 4587ca80300SHongtao Yu 4596b989a17SWenlei He ContextTrieNode * 4606b989a17SWenlei He SampleContextTracker::getContextFor(const SampleContext &Context) { 4616b989a17SWenlei He return getOrCreateContextPath(Context, false); 4626b989a17SWenlei He } 4636b989a17SWenlei He 4646b989a17SWenlei He ContextTrieNode * 4656b989a17SWenlei He SampleContextTracker::getCalleeContextFor(const DILocation *DIL, 466ef0e0adcSWilliam Junda Huang FunctionId CalleeName) { 4676b989a17SWenlei He assert(DIL && "Expect non-null location"); 4686b989a17SWenlei He 4696b989a17SWenlei He ContextTrieNode *CallContext = getContextFor(DIL); 4706b989a17SWenlei He if (!CallContext) 4716b989a17SWenlei He return nullptr; 4726b989a17SWenlei He 4736bae5973SWenlei He // When CalleeName is empty, the child context profile with max 4746bae5973SWenlei He // total samples will be returned. 4756b989a17SWenlei He return CallContext->getChildContext( 476224fee82SHongtao Yu FunctionSamples::getCallSiteIdentifier(DIL), CalleeName); 4776b989a17SWenlei He } 4786b989a17SWenlei He 4796b989a17SWenlei He ContextTrieNode *SampleContextTracker::getContextFor(const DILocation *DIL) { 4806b989a17SWenlei He assert(DIL && "Expect non-null location"); 481ef0e0adcSWilliam Junda Huang SmallVector<std::pair<LineLocation, FunctionId>, 10> S; 4826b989a17SWenlei He 4836b989a17SWenlei He // Use C++ linkage name if possible. 4846b989a17SWenlei He const DILocation *PrevDIL = DIL; 4856b989a17SWenlei He for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) { 4866b989a17SWenlei He StringRef Name = PrevDIL->getScope()->getSubprogram()->getLinkageName(); 4876b989a17SWenlei He if (Name.empty()) 4886b989a17SWenlei He Name = PrevDIL->getScope()->getSubprogram()->getName(); 4896b989a17SWenlei He S.push_back( 490ef0e0adcSWilliam Junda Huang std::make_pair(FunctionSamples::getCallSiteIdentifier(DIL), 491ef0e0adcSWilliam Junda Huang getRepInFormat(Name))); 4926b989a17SWenlei He PrevDIL = DIL; 4936b989a17SWenlei He } 4946b989a17SWenlei He 4956b989a17SWenlei He // Push root node, note that root node like main may only 4966b989a17SWenlei He // a name, but not linkage name. 4976b989a17SWenlei He StringRef RootName = PrevDIL->getScope()->getSubprogram()->getLinkageName(); 4986b989a17SWenlei He if (RootName.empty()) 4996b989a17SWenlei He RootName = PrevDIL->getScope()->getSubprogram()->getName(); 500ef0e0adcSWilliam Junda Huang S.push_back(std::make_pair(LineLocation(0, 0), 501ef0e0adcSWilliam Junda Huang getRepInFormat(RootName))); 5027ca80300SHongtao Yu 5036b989a17SWenlei He ContextTrieNode *ContextNode = &RootContext; 5046b989a17SWenlei He int I = S.size(); 5056b989a17SWenlei He while (--I >= 0 && ContextNode) { 5066b989a17SWenlei He LineLocation &CallSite = S[I].first; 507ef0e0adcSWilliam Junda Huang FunctionId CalleeName = S[I].second; 5086b989a17SWenlei He ContextNode = ContextNode->getChildContext(CallSite, CalleeName); 5096b989a17SWenlei He } 5106b989a17SWenlei He 5116b989a17SWenlei He if (I < 0) 5126b989a17SWenlei He return ContextNode; 5136b989a17SWenlei He 5146b989a17SWenlei He return nullptr; 5156b989a17SWenlei He } 5166b989a17SWenlei He 5176b989a17SWenlei He ContextTrieNode * 5186b989a17SWenlei He SampleContextTracker::getOrCreateContextPath(const SampleContext &Context, 5196b989a17SWenlei He bool AllowCreate) { 5206b989a17SWenlei He ContextTrieNode *ContextNode = &RootContext; 5216b989a17SWenlei He LineLocation CallSiteLoc(0, 0); 5226b989a17SWenlei He 523e20d210eSKazu Hirata for (const auto &Callsite : Context.getContextFrames()) { 5246b989a17SWenlei He // Create child node at parent line/disc location 5256b989a17SWenlei He if (AllowCreate) { 526fb29d812Swlei ContextNode = 527ef0e0adcSWilliam Junda Huang ContextNode->getOrCreateChildContext(CallSiteLoc, Callsite.Func); 5286b989a17SWenlei He } else { 529b9db7036SHongtao Yu ContextNode = 530ef0e0adcSWilliam Junda Huang ContextNode->getChildContext(CallSiteLoc, Callsite.Func); 5316b989a17SWenlei He } 532fb29d812Swlei CallSiteLoc = Callsite.Location; 5336b989a17SWenlei He } 5346b989a17SWenlei He 5356b989a17SWenlei He assert((!AllowCreate || ContextNode) && 5366b989a17SWenlei He "Node must exist if creation is allowed"); 5376b989a17SWenlei He return ContextNode; 5386b989a17SWenlei He } 5396b989a17SWenlei He 540ef0e0adcSWilliam Junda Huang ContextTrieNode * 541ef0e0adcSWilliam Junda Huang SampleContextTracker::getTopLevelContextNode(FunctionId FName) { 54230b02323SWenlei He assert(!FName.empty() && "Top level node query must provide valid name"); 5436b989a17SWenlei He return RootContext.getChildContext(LineLocation(0, 0), FName); 5446b989a17SWenlei He } 5456b989a17SWenlei He 546ef0e0adcSWilliam Junda Huang ContextTrieNode & 547ef0e0adcSWilliam Junda Huang SampleContextTracker::addTopLevelContextNode(FunctionId FName) { 5486b989a17SWenlei He assert(!getTopLevelContextNode(FName) && "Node to add must not exist"); 5496b989a17SWenlei He return *RootContext.getOrCreateChildContext(LineLocation(0, 0), FName); 5506b989a17SWenlei He } 5516b989a17SWenlei He 5526b989a17SWenlei He void SampleContextTracker::mergeContextNode(ContextTrieNode &FromNode, 5537e86b13cSwlei ContextTrieNode &ToNode) { 5546b989a17SWenlei He FunctionSamples *FromSamples = FromNode.getFunctionSamples(); 5556b989a17SWenlei He FunctionSamples *ToSamples = ToNode.getFunctionSamples(); 5566b989a17SWenlei He if (FromSamples && ToSamples) { 5576b989a17SWenlei He // Merge/duplicate FromSamples into ToSamples 5586b989a17SWenlei He ToSamples->merge(*FromSamples); 5596b989a17SWenlei He ToSamples->getContext().setState(SyntheticContext); 5606b989a17SWenlei He FromSamples->getContext().setState(MergedContext); 561054487c5SWenlei He if (FromSamples->getContext().hasAttribute(ContextShouldBeInlined)) 562054487c5SWenlei He ToSamples->getContext().setAttribute(ContextShouldBeInlined); 5636b989a17SWenlei He } else if (FromSamples) { 5646b989a17SWenlei He // Transfer FromSamples from FromNode to ToNode 5656b989a17SWenlei He ToNode.setFunctionSamples(FromSamples); 5667e86b13cSwlei setContextNode(FromSamples, &ToNode); 5676b989a17SWenlei He FromSamples->getContext().setState(SyntheticContext); 5686b989a17SWenlei He } 5696b989a17SWenlei He } 5706b989a17SWenlei He 5716b989a17SWenlei He ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree( 5727e86b13cSwlei ContextTrieNode &FromNode, ContextTrieNode &ToNodeParent) { 5736b989a17SWenlei He 5746b989a17SWenlei He // Ignore call site location if destination is top level under root 5756b989a17SWenlei He LineLocation NewCallSiteLoc = LineLocation(0, 0); 5766b989a17SWenlei He LineLocation OldCallSiteLoc = FromNode.getCallSiteLoc(); 5776b989a17SWenlei He ContextTrieNode &FromNodeParent = *FromNode.getParentContext(); 5786b989a17SWenlei He ContextTrieNode *ToNode = nullptr; 5796b989a17SWenlei He bool MoveToRoot = (&ToNodeParent == &RootContext); 5806b989a17SWenlei He if (!MoveToRoot) { 5816b989a17SWenlei He NewCallSiteLoc = OldCallSiteLoc; 5826b989a17SWenlei He } 5836b989a17SWenlei He 5846b989a17SWenlei He // Locate destination node, create/move if not existing 5856b989a17SWenlei He ToNode = ToNodeParent.getChildContext(NewCallSiteLoc, FromNode.getFuncName()); 5866b989a17SWenlei He if (!ToNode) { 5876b989a17SWenlei He // Do not delete node to move from its parent here because 5886b989a17SWenlei He // caller is iterating over children of that parent node. 5897e86b13cSwlei ToNode = 5907e86b13cSwlei &moveContextSamples(ToNodeParent, NewCallSiteLoc, std::move(FromNode)); 5917e86b13cSwlei LLVM_DEBUG({ 5927e86b13cSwlei dbgs() << " Context promoted and merged to: " << getContextString(ToNode) 5937e86b13cSwlei << "\n"; 5947e86b13cSwlei }); 5956b989a17SWenlei He } else { 5966b989a17SWenlei He // Destination node exists, merge samples for the context tree 5977e86b13cSwlei mergeContextNode(FromNode, *ToNode); 59812ac0403SHongtao Yu LLVM_DEBUG({ 59912ac0403SHongtao Yu if (ToNode->getFunctionSamples()) 60012ac0403SHongtao Yu dbgs() << " Context promoted and merged to: " 6017e86b13cSwlei << getContextString(ToNode) << "\n"; 60212ac0403SHongtao Yu }); 6036b989a17SWenlei He 6046b989a17SWenlei He // Recursively promote and merge children 6056b989a17SWenlei He for (auto &It : FromNode.getAllChildContext()) { 6066b989a17SWenlei He ContextTrieNode &FromChildNode = It.second; 6077e86b13cSwlei promoteMergeContextSamplesTree(FromChildNode, *ToNode); 6086b989a17SWenlei He } 6096b989a17SWenlei He 6106b989a17SWenlei He // Remove children once they're all merged 6116b989a17SWenlei He FromNode.getAllChildContext().clear(); 6126b989a17SWenlei He } 6136b989a17SWenlei He 6146b989a17SWenlei He // For root of subtree, remove itself from old parent too 6156b989a17SWenlei He if (MoveToRoot) 6166b989a17SWenlei He FromNodeParent.removeChildContext(OldCallSiteLoc, ToNode->getFuncName()); 6176b989a17SWenlei He 6186b989a17SWenlei He return *ToNode; 6196b989a17SWenlei He } 620aa58b7b1Swlei 621aa58b7b1Swlei void SampleContextTracker::createContextLessProfileMap( 622aa58b7b1Swlei SampleProfileMap &ContextLessProfiles) { 6237e86b13cSwlei for (auto *Node : *this) { 624aa58b7b1Swlei FunctionSamples *FProfile = Node->getFunctionSamples(); 625aa58b7b1Swlei // Profile's context can be empty, use ContextNode's func name. 6267e86b13cSwlei if (FProfile) 627*ce03155aSMircea Trofin ContextLessProfiles.create(Node->getFuncName()).merge(*FProfile); 628aa58b7b1Swlei } 629aa58b7b1Swlei } 6306b989a17SWenlei He } // namespace llvm 631