1e8d8bef9SDimitry Andric //===- SampleContextTracker.cpp - Context-sensitive Profile Tracker -------===// 2e8d8bef9SDimitry Andric // 3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e8d8bef9SDimitry Andric // 7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 8e8d8bef9SDimitry Andric // 9e8d8bef9SDimitry Andric // This file implements the SampleContextTracker used by CSSPGO. 10e8d8bef9SDimitry Andric // 11e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 12e8d8bef9SDimitry Andric 13e8d8bef9SDimitry Andric #include "llvm/Transforms/IPO/SampleContextTracker.h" 14e8d8bef9SDimitry Andric #include "llvm/ADT/StringMap.h" 15e8d8bef9SDimitry Andric #include "llvm/ADT/StringRef.h" 16e8d8bef9SDimitry Andric #include "llvm/IR/DebugInfoMetadata.h" 17e8d8bef9SDimitry Andric #include "llvm/IR/Instructions.h" 18e8d8bef9SDimitry Andric #include "llvm/ProfileData/SampleProf.h" 19e8d8bef9SDimitry Andric #include <map> 20e8d8bef9SDimitry Andric #include <queue> 21e8d8bef9SDimitry Andric #include <vector> 22e8d8bef9SDimitry Andric 23e8d8bef9SDimitry Andric using namespace llvm; 24e8d8bef9SDimitry Andric using namespace sampleprof; 25e8d8bef9SDimitry Andric 26e8d8bef9SDimitry Andric #define DEBUG_TYPE "sample-context-tracker" 27e8d8bef9SDimitry Andric 28e8d8bef9SDimitry Andric namespace llvm { 29e8d8bef9SDimitry Andric 30e8d8bef9SDimitry Andric ContextTrieNode *ContextTrieNode::getChildContext(const LineLocation &CallSite, 31e8d8bef9SDimitry Andric StringRef CalleeName) { 32e8d8bef9SDimitry Andric if (CalleeName.empty()) 33d409305fSDimitry Andric return getHottestChildContext(CallSite); 34e8d8bef9SDimitry Andric 35*0eae32dcSDimitry Andric uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite); 36e8d8bef9SDimitry Andric auto It = AllChildContext.find(Hash); 37e8d8bef9SDimitry Andric if (It != AllChildContext.end()) 38e8d8bef9SDimitry Andric return &It->second; 39e8d8bef9SDimitry Andric return nullptr; 40e8d8bef9SDimitry Andric } 41e8d8bef9SDimitry Andric 42e8d8bef9SDimitry Andric ContextTrieNode * 43d409305fSDimitry Andric ContextTrieNode::getHottestChildContext(const LineLocation &CallSite) { 44e8d8bef9SDimitry Andric // CSFDO-TODO: This could be slow, change AllChildContext so we can 45e8d8bef9SDimitry Andric // do point look up for child node by call site alone. 46d409305fSDimitry Andric // Retrieve the child node with max count for indirect call 47e8d8bef9SDimitry Andric ContextTrieNode *ChildNodeRet = nullptr; 48d409305fSDimitry Andric uint64_t MaxCalleeSamples = 0; 49e8d8bef9SDimitry Andric for (auto &It : AllChildContext) { 50e8d8bef9SDimitry Andric ContextTrieNode &ChildNode = It.second; 51d409305fSDimitry Andric if (ChildNode.CallSiteLoc != CallSite) 52d409305fSDimitry Andric continue; 53d409305fSDimitry Andric FunctionSamples *Samples = ChildNode.getFunctionSamples(); 54d409305fSDimitry Andric if (!Samples) 55d409305fSDimitry Andric continue; 56d409305fSDimitry Andric if (Samples->getTotalSamples() > MaxCalleeSamples) { 57e8d8bef9SDimitry Andric ChildNodeRet = &ChildNode; 58d409305fSDimitry Andric MaxCalleeSamples = Samples->getTotalSamples(); 59e8d8bef9SDimitry Andric } 60e8d8bef9SDimitry Andric } 61e8d8bef9SDimitry Andric 62e8d8bef9SDimitry Andric return ChildNodeRet; 63e8d8bef9SDimitry Andric } 64e8d8bef9SDimitry Andric 65e8d8bef9SDimitry Andric ContextTrieNode &ContextTrieNode::moveToChildContext( 66e8d8bef9SDimitry Andric const LineLocation &CallSite, ContextTrieNode &&NodeToMove, 67349cc55cSDimitry Andric uint32_t ContextFramesToRemove, bool DeleteNode) { 68*0eae32dcSDimitry Andric uint64_t Hash = 69*0eae32dcSDimitry Andric FunctionSamples::getCallSiteHash(NodeToMove.getFuncName(), CallSite); 70e8d8bef9SDimitry Andric assert(!AllChildContext.count(Hash) && "Node to remove must exist"); 71e8d8bef9SDimitry Andric LineLocation OldCallSite = NodeToMove.CallSiteLoc; 72e8d8bef9SDimitry Andric ContextTrieNode &OldParentContext = *NodeToMove.getParentContext(); 73e8d8bef9SDimitry Andric AllChildContext[Hash] = NodeToMove; 74e8d8bef9SDimitry Andric ContextTrieNode &NewNode = AllChildContext[Hash]; 75e8d8bef9SDimitry Andric NewNode.CallSiteLoc = CallSite; 76e8d8bef9SDimitry Andric 77e8d8bef9SDimitry Andric // Walk through nodes in the moved the subtree, and update 78e8d8bef9SDimitry Andric // FunctionSamples' context as for the context promotion. 79e8d8bef9SDimitry Andric // We also need to set new parant link for all children. 80e8d8bef9SDimitry Andric std::queue<ContextTrieNode *> NodeToUpdate; 81e8d8bef9SDimitry Andric NewNode.setParentContext(this); 82e8d8bef9SDimitry Andric NodeToUpdate.push(&NewNode); 83e8d8bef9SDimitry Andric 84e8d8bef9SDimitry Andric while (!NodeToUpdate.empty()) { 85e8d8bef9SDimitry Andric ContextTrieNode *Node = NodeToUpdate.front(); 86e8d8bef9SDimitry Andric NodeToUpdate.pop(); 87e8d8bef9SDimitry Andric FunctionSamples *FSamples = Node->getFunctionSamples(); 88e8d8bef9SDimitry Andric 89e8d8bef9SDimitry Andric if (FSamples) { 90349cc55cSDimitry Andric FSamples->getContext().promoteOnPath(ContextFramesToRemove); 91e8d8bef9SDimitry Andric FSamples->getContext().setState(SyntheticContext); 92349cc55cSDimitry Andric LLVM_DEBUG(dbgs() << " Context promoted to: " 93349cc55cSDimitry Andric << FSamples->getContext().toString() << "\n"); 94e8d8bef9SDimitry Andric } 95e8d8bef9SDimitry Andric 96e8d8bef9SDimitry Andric for (auto &It : Node->getAllChildContext()) { 97e8d8bef9SDimitry Andric ContextTrieNode *ChildNode = &It.second; 98e8d8bef9SDimitry Andric ChildNode->setParentContext(Node); 99e8d8bef9SDimitry Andric NodeToUpdate.push(ChildNode); 100e8d8bef9SDimitry Andric } 101e8d8bef9SDimitry Andric } 102e8d8bef9SDimitry Andric 103e8d8bef9SDimitry Andric // Original context no longer needed, destroy if requested. 104e8d8bef9SDimitry Andric if (DeleteNode) 105e8d8bef9SDimitry Andric OldParentContext.removeChildContext(OldCallSite, NewNode.getFuncName()); 106e8d8bef9SDimitry Andric 107e8d8bef9SDimitry Andric return NewNode; 108e8d8bef9SDimitry Andric } 109e8d8bef9SDimitry Andric 110e8d8bef9SDimitry Andric void ContextTrieNode::removeChildContext(const LineLocation &CallSite, 111e8d8bef9SDimitry Andric StringRef CalleeName) { 112*0eae32dcSDimitry Andric uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite); 113e8d8bef9SDimitry Andric // Note this essentially calls dtor and destroys that child context 114e8d8bef9SDimitry Andric AllChildContext.erase(Hash); 115e8d8bef9SDimitry Andric } 116e8d8bef9SDimitry Andric 117349cc55cSDimitry Andric std::map<uint64_t, ContextTrieNode> &ContextTrieNode::getAllChildContext() { 118e8d8bef9SDimitry Andric return AllChildContext; 119e8d8bef9SDimitry Andric } 120e8d8bef9SDimitry Andric 121fe6060f1SDimitry Andric StringRef ContextTrieNode::getFuncName() const { return FuncName; } 122e8d8bef9SDimitry Andric 123e8d8bef9SDimitry Andric FunctionSamples *ContextTrieNode::getFunctionSamples() const { 124e8d8bef9SDimitry Andric return FuncSamples; 125e8d8bef9SDimitry Andric } 126e8d8bef9SDimitry Andric 127e8d8bef9SDimitry Andric void ContextTrieNode::setFunctionSamples(FunctionSamples *FSamples) { 128e8d8bef9SDimitry Andric FuncSamples = FSamples; 129e8d8bef9SDimitry Andric } 130e8d8bef9SDimitry Andric 131349cc55cSDimitry Andric Optional<uint32_t> ContextTrieNode::getFunctionSize() const { return FuncSize; } 132349cc55cSDimitry Andric 133349cc55cSDimitry Andric void ContextTrieNode::addFunctionSize(uint32_t FSize) { 134349cc55cSDimitry Andric if (!FuncSize.hasValue()) 135349cc55cSDimitry Andric FuncSize = 0; 136349cc55cSDimitry Andric 137349cc55cSDimitry Andric FuncSize = FuncSize.getValue() + FSize; 138349cc55cSDimitry Andric } 139349cc55cSDimitry Andric 140e8d8bef9SDimitry Andric LineLocation ContextTrieNode::getCallSiteLoc() const { return CallSiteLoc; } 141e8d8bef9SDimitry Andric 142e8d8bef9SDimitry Andric ContextTrieNode *ContextTrieNode::getParentContext() const { 143e8d8bef9SDimitry Andric return ParentContext; 144e8d8bef9SDimitry Andric } 145e8d8bef9SDimitry Andric 146e8d8bef9SDimitry Andric void ContextTrieNode::setParentContext(ContextTrieNode *Parent) { 147e8d8bef9SDimitry Andric ParentContext = Parent; 148e8d8bef9SDimitry Andric } 149e8d8bef9SDimitry Andric 150349cc55cSDimitry Andric void ContextTrieNode::dumpNode() { 151e8d8bef9SDimitry Andric dbgs() << "Node: " << FuncName << "\n" 152e8d8bef9SDimitry Andric << " Callsite: " << CallSiteLoc << "\n" 153349cc55cSDimitry Andric << " Size: " << FuncSize << "\n" 154e8d8bef9SDimitry Andric << " Children:\n"; 155e8d8bef9SDimitry Andric 156e8d8bef9SDimitry Andric for (auto &It : AllChildContext) { 157e8d8bef9SDimitry Andric dbgs() << " Node: " << It.second.getFuncName() << "\n"; 158e8d8bef9SDimitry Andric } 159e8d8bef9SDimitry Andric } 160e8d8bef9SDimitry Andric 161349cc55cSDimitry Andric void ContextTrieNode::dumpTree() { 162349cc55cSDimitry Andric dbgs() << "Context Profile Tree:\n"; 163349cc55cSDimitry Andric std::queue<ContextTrieNode *> NodeQueue; 164349cc55cSDimitry Andric NodeQueue.push(this); 165349cc55cSDimitry Andric 166349cc55cSDimitry Andric while (!NodeQueue.empty()) { 167349cc55cSDimitry Andric ContextTrieNode *Node = NodeQueue.front(); 168349cc55cSDimitry Andric NodeQueue.pop(); 169349cc55cSDimitry Andric Node->dumpNode(); 170349cc55cSDimitry Andric 171349cc55cSDimitry Andric for (auto &It : Node->getAllChildContext()) { 172349cc55cSDimitry Andric ContextTrieNode *ChildNode = &It.second; 173349cc55cSDimitry Andric NodeQueue.push(ChildNode); 174349cc55cSDimitry Andric } 175349cc55cSDimitry Andric } 176349cc55cSDimitry Andric } 177349cc55cSDimitry Andric 178e8d8bef9SDimitry Andric ContextTrieNode *ContextTrieNode::getOrCreateChildContext( 179e8d8bef9SDimitry Andric const LineLocation &CallSite, StringRef CalleeName, bool AllowCreate) { 180*0eae32dcSDimitry Andric uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite); 181e8d8bef9SDimitry Andric auto It = AllChildContext.find(Hash); 182e8d8bef9SDimitry Andric if (It != AllChildContext.end()) { 183e8d8bef9SDimitry Andric assert(It->second.getFuncName() == CalleeName && 184e8d8bef9SDimitry Andric "Hash collision for child context node"); 185e8d8bef9SDimitry Andric return &It->second; 186e8d8bef9SDimitry Andric } 187e8d8bef9SDimitry Andric 188e8d8bef9SDimitry Andric if (!AllowCreate) 189e8d8bef9SDimitry Andric return nullptr; 190e8d8bef9SDimitry Andric 191e8d8bef9SDimitry Andric AllChildContext[Hash] = ContextTrieNode(this, CalleeName, nullptr, CallSite); 192e8d8bef9SDimitry Andric return &AllChildContext[Hash]; 193e8d8bef9SDimitry Andric } 194e8d8bef9SDimitry Andric 195e8d8bef9SDimitry Andric // Profiler tracker than manages profiles and its associated context 196e8d8bef9SDimitry Andric SampleContextTracker::SampleContextTracker( 197349cc55cSDimitry Andric SampleProfileMap &Profiles, 198349cc55cSDimitry Andric const DenseMap<uint64_t, StringRef> *GUIDToFuncNameMap) 199349cc55cSDimitry Andric : GUIDToFuncNameMap(GUIDToFuncNameMap) { 200e8d8bef9SDimitry Andric for (auto &FuncSample : Profiles) { 201e8d8bef9SDimitry Andric FunctionSamples *FSamples = &FuncSample.second; 202349cc55cSDimitry Andric SampleContext Context = FuncSample.first; 203349cc55cSDimitry Andric LLVM_DEBUG(dbgs() << "Tracking Context for function: " << Context.toString() 204349cc55cSDimitry Andric << "\n"); 205e8d8bef9SDimitry Andric if (!Context.isBaseContext()) 206349cc55cSDimitry Andric FuncToCtxtProfiles[Context.getName()].insert(FSamples); 207e8d8bef9SDimitry Andric ContextTrieNode *NewNode = getOrCreateContextPath(Context, true); 208e8d8bef9SDimitry Andric assert(!NewNode->getFunctionSamples() && 209e8d8bef9SDimitry Andric "New node can't have sample profile"); 210e8d8bef9SDimitry Andric NewNode->setFunctionSamples(FSamples); 211e8d8bef9SDimitry Andric } 212e8d8bef9SDimitry Andric } 213e8d8bef9SDimitry Andric 214e8d8bef9SDimitry Andric FunctionSamples * 215e8d8bef9SDimitry Andric SampleContextTracker::getCalleeContextSamplesFor(const CallBase &Inst, 216e8d8bef9SDimitry Andric StringRef CalleeName) { 217e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Getting callee context for instr: " << Inst << "\n"); 218e8d8bef9SDimitry Andric DILocation *DIL = Inst.getDebugLoc(); 219e8d8bef9SDimitry Andric if (!DIL) 220e8d8bef9SDimitry Andric return nullptr; 221e8d8bef9SDimitry Andric 222fe6060f1SDimitry Andric CalleeName = FunctionSamples::getCanonicalFnName(CalleeName); 223349cc55cSDimitry Andric // Convert real function names to MD5 names, if the input profile is 224349cc55cSDimitry Andric // MD5-based. 225349cc55cSDimitry Andric std::string FGUID; 226349cc55cSDimitry Andric CalleeName = getRepInFormat(CalleeName, FunctionSamples::UseMD5, FGUID); 227fe6060f1SDimitry Andric 228d409305fSDimitry Andric // For indirect call, CalleeName will be empty, in which case the context 229d409305fSDimitry Andric // profile for callee with largest total samples will be returned. 230e8d8bef9SDimitry Andric ContextTrieNode *CalleeContext = getCalleeContextFor(DIL, CalleeName); 231e8d8bef9SDimitry Andric if (CalleeContext) { 232e8d8bef9SDimitry Andric FunctionSamples *FSamples = CalleeContext->getFunctionSamples(); 233e8d8bef9SDimitry Andric LLVM_DEBUG(if (FSamples) { 234349cc55cSDimitry Andric dbgs() << " Callee context found: " << FSamples->getContext().toString() 235349cc55cSDimitry Andric << "\n"; 236e8d8bef9SDimitry Andric }); 237e8d8bef9SDimitry Andric return FSamples; 238e8d8bef9SDimitry Andric } 239e8d8bef9SDimitry Andric 240e8d8bef9SDimitry Andric return nullptr; 241e8d8bef9SDimitry Andric } 242e8d8bef9SDimitry Andric 243d409305fSDimitry Andric std::vector<const FunctionSamples *> 244d409305fSDimitry Andric SampleContextTracker::getIndirectCalleeContextSamplesFor( 245d409305fSDimitry Andric const DILocation *DIL) { 246d409305fSDimitry Andric std::vector<const FunctionSamples *> R; 247d409305fSDimitry Andric if (!DIL) 248d409305fSDimitry Andric return R; 249d409305fSDimitry Andric 250d409305fSDimitry Andric ContextTrieNode *CallerNode = getContextFor(DIL); 251d409305fSDimitry Andric LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL); 252d409305fSDimitry Andric for (auto &It : CallerNode->getAllChildContext()) { 253d409305fSDimitry Andric ContextTrieNode &ChildNode = It.second; 254d409305fSDimitry Andric if (ChildNode.getCallSiteLoc() != CallSite) 255d409305fSDimitry Andric continue; 256d409305fSDimitry Andric if (FunctionSamples *CalleeSamples = ChildNode.getFunctionSamples()) 257d409305fSDimitry Andric R.push_back(CalleeSamples); 258d409305fSDimitry Andric } 259d409305fSDimitry Andric 260d409305fSDimitry Andric return R; 261d409305fSDimitry Andric } 262d409305fSDimitry Andric 263e8d8bef9SDimitry Andric FunctionSamples * 264e8d8bef9SDimitry Andric SampleContextTracker::getContextSamplesFor(const DILocation *DIL) { 265e8d8bef9SDimitry Andric assert(DIL && "Expect non-null location"); 266e8d8bef9SDimitry Andric 267e8d8bef9SDimitry Andric ContextTrieNode *ContextNode = getContextFor(DIL); 268e8d8bef9SDimitry Andric if (!ContextNode) 269e8d8bef9SDimitry Andric return nullptr; 270e8d8bef9SDimitry Andric 271e8d8bef9SDimitry Andric // We may have inlined callees during pre-LTO compilation, in which case 272e8d8bef9SDimitry Andric // we need to rely on the inline stack from !dbg to mark context profile 273e8d8bef9SDimitry Andric // as inlined, instead of `MarkContextSamplesInlined` during inlining. 274e8d8bef9SDimitry Andric // Sample profile loader walks through all instructions to get profile, 275e8d8bef9SDimitry Andric // which calls this function. So once that is done, all previously inlined 276e8d8bef9SDimitry Andric // context profile should be marked properly. 277e8d8bef9SDimitry Andric FunctionSamples *Samples = ContextNode->getFunctionSamples(); 278e8d8bef9SDimitry Andric if (Samples && ContextNode->getParentContext() != &RootContext) 279e8d8bef9SDimitry Andric Samples->getContext().setState(InlinedContext); 280e8d8bef9SDimitry Andric 281e8d8bef9SDimitry Andric return Samples; 282e8d8bef9SDimitry Andric } 283e8d8bef9SDimitry Andric 284e8d8bef9SDimitry Andric FunctionSamples * 285e8d8bef9SDimitry Andric SampleContextTracker::getContextSamplesFor(const SampleContext &Context) { 286e8d8bef9SDimitry Andric ContextTrieNode *Node = getContextFor(Context); 287e8d8bef9SDimitry Andric if (!Node) 288e8d8bef9SDimitry Andric return nullptr; 289e8d8bef9SDimitry Andric 290e8d8bef9SDimitry Andric return Node->getFunctionSamples(); 291e8d8bef9SDimitry Andric } 292e8d8bef9SDimitry Andric 293d409305fSDimitry Andric SampleContextTracker::ContextSamplesTy & 294d409305fSDimitry Andric SampleContextTracker::getAllContextSamplesFor(const Function &Func) { 295d409305fSDimitry Andric StringRef CanonName = FunctionSamples::getCanonicalFnName(Func); 296fe6060f1SDimitry Andric return FuncToCtxtProfiles[CanonName]; 297d409305fSDimitry Andric } 298d409305fSDimitry Andric 299d409305fSDimitry Andric SampleContextTracker::ContextSamplesTy & 300d409305fSDimitry Andric SampleContextTracker::getAllContextSamplesFor(StringRef Name) { 301fe6060f1SDimitry Andric return FuncToCtxtProfiles[Name]; 302d409305fSDimitry Andric } 303d409305fSDimitry Andric 304e8d8bef9SDimitry Andric FunctionSamples *SampleContextTracker::getBaseSamplesFor(const Function &Func, 305e8d8bef9SDimitry Andric bool MergeContext) { 306e8d8bef9SDimitry Andric StringRef CanonName = FunctionSamples::getCanonicalFnName(Func); 307e8d8bef9SDimitry Andric return getBaseSamplesFor(CanonName, MergeContext); 308e8d8bef9SDimitry Andric } 309e8d8bef9SDimitry Andric 310e8d8bef9SDimitry Andric FunctionSamples *SampleContextTracker::getBaseSamplesFor(StringRef Name, 311e8d8bef9SDimitry Andric bool MergeContext) { 312e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Getting base profile for function: " << Name << "\n"); 313349cc55cSDimitry Andric // Convert real function names to MD5 names, if the input profile is 314349cc55cSDimitry Andric // MD5-based. 315349cc55cSDimitry Andric std::string FGUID; 316349cc55cSDimitry Andric Name = getRepInFormat(Name, FunctionSamples::UseMD5, FGUID); 317349cc55cSDimitry Andric 318e8d8bef9SDimitry Andric // Base profile is top-level node (child of root node), so try to retrieve 319e8d8bef9SDimitry Andric // existing top-level node for given function first. If it exists, it could be 320e8d8bef9SDimitry Andric // that we've merged base profile before, or there's actually context-less 321e8d8bef9SDimitry Andric // profile from the input (e.g. due to unreliable stack walking). 322e8d8bef9SDimitry Andric ContextTrieNode *Node = getTopLevelContextNode(Name); 323e8d8bef9SDimitry Andric if (MergeContext) { 324e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << " Merging context profile into base profile: " << Name 325e8d8bef9SDimitry Andric << "\n"); 326e8d8bef9SDimitry Andric 327e8d8bef9SDimitry Andric // We have profile for function under different contexts, 328e8d8bef9SDimitry Andric // create synthetic base profile and merge context profiles 329e8d8bef9SDimitry Andric // into base profile. 330fe6060f1SDimitry Andric for (auto *CSamples : FuncToCtxtProfiles[Name]) { 331e8d8bef9SDimitry Andric SampleContext &Context = CSamples->getContext(); 332e8d8bef9SDimitry Andric // Skip inlined context profile and also don't re-merge any context 333e8d8bef9SDimitry Andric if (Context.hasState(InlinedContext) || Context.hasState(MergedContext)) 334e8d8bef9SDimitry Andric continue; 335e8d8bef9SDimitry Andric 336349cc55cSDimitry Andric ContextTrieNode *FromNode = getContextFor(Context); 337349cc55cSDimitry Andric if (FromNode == Node) 338349cc55cSDimitry Andric continue; 339349cc55cSDimitry Andric 340e8d8bef9SDimitry Andric ContextTrieNode &ToNode = promoteMergeContextSamplesTree(*FromNode); 341e8d8bef9SDimitry Andric assert((!Node || Node == &ToNode) && "Expect only one base profile"); 342e8d8bef9SDimitry Andric Node = &ToNode; 343e8d8bef9SDimitry Andric } 344e8d8bef9SDimitry Andric } 345e8d8bef9SDimitry Andric 346e8d8bef9SDimitry Andric // Still no profile even after merge/promotion (if allowed) 347e8d8bef9SDimitry Andric if (!Node) 348e8d8bef9SDimitry Andric return nullptr; 349e8d8bef9SDimitry Andric 350e8d8bef9SDimitry Andric return Node->getFunctionSamples(); 351e8d8bef9SDimitry Andric } 352e8d8bef9SDimitry Andric 353e8d8bef9SDimitry Andric void SampleContextTracker::markContextSamplesInlined( 354e8d8bef9SDimitry Andric const FunctionSamples *InlinedSamples) { 355e8d8bef9SDimitry Andric assert(InlinedSamples && "Expect non-null inlined samples"); 356e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Marking context profile as inlined: " 357349cc55cSDimitry Andric << InlinedSamples->getContext().toString() << "\n"); 358e8d8bef9SDimitry Andric InlinedSamples->getContext().setState(InlinedContext); 359e8d8bef9SDimitry Andric } 360e8d8bef9SDimitry Andric 361fe6060f1SDimitry Andric ContextTrieNode &SampleContextTracker::getRootContext() { return RootContext; } 362fe6060f1SDimitry Andric 363e8d8bef9SDimitry Andric void SampleContextTracker::promoteMergeContextSamplesTree( 364e8d8bef9SDimitry Andric const Instruction &Inst, StringRef CalleeName) { 365e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Promoting and merging context tree for instr: \n" 366e8d8bef9SDimitry Andric << Inst << "\n"); 367e8d8bef9SDimitry Andric // Get the caller context for the call instruction, we don't use callee 368e8d8bef9SDimitry Andric // name from call because there can be context from indirect calls too. 369e8d8bef9SDimitry Andric DILocation *DIL = Inst.getDebugLoc(); 370e8d8bef9SDimitry Andric ContextTrieNode *CallerNode = getContextFor(DIL); 371e8d8bef9SDimitry Andric if (!CallerNode) 372e8d8bef9SDimitry Andric return; 373e8d8bef9SDimitry Andric 374e8d8bef9SDimitry Andric // Get the context that needs to be promoted 375d409305fSDimitry Andric LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL); 376d409305fSDimitry Andric // For indirect call, CalleeName will be empty, in which case we need to 377d409305fSDimitry Andric // promote all non-inlined child context profiles. 378d409305fSDimitry Andric if (CalleeName.empty()) { 379d409305fSDimitry Andric for (auto &It : CallerNode->getAllChildContext()) { 380d409305fSDimitry Andric ContextTrieNode *NodeToPromo = &It.second; 381d409305fSDimitry Andric if (CallSite != NodeToPromo->getCallSiteLoc()) 382d409305fSDimitry Andric continue; 383d409305fSDimitry Andric FunctionSamples *FromSamples = NodeToPromo->getFunctionSamples(); 384d409305fSDimitry Andric if (FromSamples && FromSamples->getContext().hasState(InlinedContext)) 385d409305fSDimitry Andric continue; 386d409305fSDimitry Andric promoteMergeContextSamplesTree(*NodeToPromo); 387d409305fSDimitry Andric } 388d409305fSDimitry Andric return; 389d409305fSDimitry Andric } 390d409305fSDimitry Andric 391d409305fSDimitry Andric // Get the context for the given callee that needs to be promoted 392e8d8bef9SDimitry Andric ContextTrieNode *NodeToPromo = 393e8d8bef9SDimitry Andric CallerNode->getChildContext(CallSite, CalleeName); 394e8d8bef9SDimitry Andric if (!NodeToPromo) 395e8d8bef9SDimitry Andric return; 396e8d8bef9SDimitry Andric 397e8d8bef9SDimitry Andric promoteMergeContextSamplesTree(*NodeToPromo); 398e8d8bef9SDimitry Andric } 399e8d8bef9SDimitry Andric 400e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree( 401e8d8bef9SDimitry Andric ContextTrieNode &NodeToPromo) { 402e8d8bef9SDimitry Andric // Promote the input node to be directly under root. This can happen 403e8d8bef9SDimitry Andric // when we decided to not inline a function under context represented 404e8d8bef9SDimitry Andric // by the input node. The promote and merge is then needed to reflect 405e8d8bef9SDimitry Andric // the context profile in the base (context-less) profile. 406e8d8bef9SDimitry Andric FunctionSamples *FromSamples = NodeToPromo.getFunctionSamples(); 407e8d8bef9SDimitry Andric assert(FromSamples && "Shouldn't promote a context without profile"); 408e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << " Found context tree root to promote: " 409349cc55cSDimitry Andric << FromSamples->getContext().toString() << "\n"); 410e8d8bef9SDimitry Andric 411d409305fSDimitry Andric assert(!FromSamples->getContext().hasState(InlinedContext) && 412d409305fSDimitry Andric "Shouldn't promote inlined context profile"); 413349cc55cSDimitry Andric uint32_t ContextFramesToRemove = 414349cc55cSDimitry Andric FromSamples->getContext().getContextFrames().size() - 1; 415e8d8bef9SDimitry Andric return promoteMergeContextSamplesTree(NodeToPromo, RootContext, 416349cc55cSDimitry Andric ContextFramesToRemove); 417e8d8bef9SDimitry Andric } 418e8d8bef9SDimitry Andric 419349cc55cSDimitry Andric void SampleContextTracker::dump() { RootContext.dumpTree(); } 420e8d8bef9SDimitry Andric 421349cc55cSDimitry Andric StringRef SampleContextTracker::getFuncNameFor(ContextTrieNode *Node) const { 422349cc55cSDimitry Andric if (!FunctionSamples::UseMD5) 423349cc55cSDimitry Andric return Node->getFuncName(); 424349cc55cSDimitry Andric assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be populated first"); 425349cc55cSDimitry Andric return GUIDToFuncNameMap->lookup(std::stoull(Node->getFuncName().data())); 426e8d8bef9SDimitry Andric } 427e8d8bef9SDimitry Andric 428e8d8bef9SDimitry Andric ContextTrieNode * 429e8d8bef9SDimitry Andric SampleContextTracker::getContextFor(const SampleContext &Context) { 430e8d8bef9SDimitry Andric return getOrCreateContextPath(Context, false); 431e8d8bef9SDimitry Andric } 432e8d8bef9SDimitry Andric 433e8d8bef9SDimitry Andric ContextTrieNode * 434e8d8bef9SDimitry Andric SampleContextTracker::getCalleeContextFor(const DILocation *DIL, 435e8d8bef9SDimitry Andric StringRef CalleeName) { 436e8d8bef9SDimitry Andric assert(DIL && "Expect non-null location"); 437e8d8bef9SDimitry Andric 438e8d8bef9SDimitry Andric ContextTrieNode *CallContext = getContextFor(DIL); 439e8d8bef9SDimitry Andric if (!CallContext) 440e8d8bef9SDimitry Andric return nullptr; 441e8d8bef9SDimitry Andric 442d409305fSDimitry Andric // When CalleeName is empty, the child context profile with max 443d409305fSDimitry Andric // total samples will be returned. 444e8d8bef9SDimitry Andric return CallContext->getChildContext( 445d409305fSDimitry Andric FunctionSamples::getCallSiteIdentifier(DIL), CalleeName); 446e8d8bef9SDimitry Andric } 447e8d8bef9SDimitry Andric 448e8d8bef9SDimitry Andric ContextTrieNode *SampleContextTracker::getContextFor(const DILocation *DIL) { 449e8d8bef9SDimitry Andric assert(DIL && "Expect non-null location"); 450e8d8bef9SDimitry Andric SmallVector<std::pair<LineLocation, StringRef>, 10> S; 451e8d8bef9SDimitry Andric 452e8d8bef9SDimitry Andric // Use C++ linkage name if possible. 453e8d8bef9SDimitry Andric const DILocation *PrevDIL = DIL; 454e8d8bef9SDimitry Andric for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) { 455e8d8bef9SDimitry Andric StringRef Name = PrevDIL->getScope()->getSubprogram()->getLinkageName(); 456e8d8bef9SDimitry Andric if (Name.empty()) 457e8d8bef9SDimitry Andric Name = PrevDIL->getScope()->getSubprogram()->getName(); 458e8d8bef9SDimitry Andric S.push_back( 459fe6060f1SDimitry Andric std::make_pair(FunctionSamples::getCallSiteIdentifier(DIL), Name)); 460e8d8bef9SDimitry Andric PrevDIL = DIL; 461e8d8bef9SDimitry Andric } 462e8d8bef9SDimitry Andric 463e8d8bef9SDimitry Andric // Push root node, note that root node like main may only 464e8d8bef9SDimitry Andric // a name, but not linkage name. 465e8d8bef9SDimitry Andric StringRef RootName = PrevDIL->getScope()->getSubprogram()->getLinkageName(); 466e8d8bef9SDimitry Andric if (RootName.empty()) 467e8d8bef9SDimitry Andric RootName = PrevDIL->getScope()->getSubprogram()->getName(); 468e8d8bef9SDimitry Andric S.push_back(std::make_pair(LineLocation(0, 0), RootName)); 469e8d8bef9SDimitry Andric 470349cc55cSDimitry Andric // Convert real function names to MD5 names, if the input profile is 471349cc55cSDimitry Andric // MD5-based. 472349cc55cSDimitry Andric std::list<std::string> MD5Names; 473349cc55cSDimitry Andric if (FunctionSamples::UseMD5) { 474349cc55cSDimitry Andric for (auto &Location : S) { 475349cc55cSDimitry Andric MD5Names.emplace_back(); 476349cc55cSDimitry Andric getRepInFormat(Location.second, FunctionSamples::UseMD5, MD5Names.back()); 477349cc55cSDimitry Andric Location.second = MD5Names.back(); 478349cc55cSDimitry Andric } 479349cc55cSDimitry Andric } 480349cc55cSDimitry Andric 481e8d8bef9SDimitry Andric ContextTrieNode *ContextNode = &RootContext; 482e8d8bef9SDimitry Andric int I = S.size(); 483e8d8bef9SDimitry Andric while (--I >= 0 && ContextNode) { 484e8d8bef9SDimitry Andric LineLocation &CallSite = S[I].first; 485349cc55cSDimitry Andric StringRef CalleeName = S[I].second; 486e8d8bef9SDimitry Andric ContextNode = ContextNode->getChildContext(CallSite, CalleeName); 487e8d8bef9SDimitry Andric } 488e8d8bef9SDimitry Andric 489e8d8bef9SDimitry Andric if (I < 0) 490e8d8bef9SDimitry Andric return ContextNode; 491e8d8bef9SDimitry Andric 492e8d8bef9SDimitry Andric return nullptr; 493e8d8bef9SDimitry Andric } 494e8d8bef9SDimitry Andric 495e8d8bef9SDimitry Andric ContextTrieNode * 496e8d8bef9SDimitry Andric SampleContextTracker::getOrCreateContextPath(const SampleContext &Context, 497e8d8bef9SDimitry Andric bool AllowCreate) { 498e8d8bef9SDimitry Andric ContextTrieNode *ContextNode = &RootContext; 499e8d8bef9SDimitry Andric LineLocation CallSiteLoc(0, 0); 500e8d8bef9SDimitry Andric 501349cc55cSDimitry Andric for (auto &Callsite : Context.getContextFrames()) { 502e8d8bef9SDimitry Andric // Create child node at parent line/disc location 503e8d8bef9SDimitry Andric if (AllowCreate) { 504e8d8bef9SDimitry Andric ContextNode = 505349cc55cSDimitry Andric ContextNode->getOrCreateChildContext(CallSiteLoc, Callsite.FuncName); 506e8d8bef9SDimitry Andric } else { 507349cc55cSDimitry Andric ContextNode = 508349cc55cSDimitry Andric ContextNode->getChildContext(CallSiteLoc, Callsite.FuncName); 509e8d8bef9SDimitry Andric } 510349cc55cSDimitry Andric CallSiteLoc = Callsite.Location; 511e8d8bef9SDimitry Andric } 512e8d8bef9SDimitry Andric 513e8d8bef9SDimitry Andric assert((!AllowCreate || ContextNode) && 514e8d8bef9SDimitry Andric "Node must exist if creation is allowed"); 515e8d8bef9SDimitry Andric return ContextNode; 516e8d8bef9SDimitry Andric } 517e8d8bef9SDimitry Andric 518e8d8bef9SDimitry Andric ContextTrieNode *SampleContextTracker::getTopLevelContextNode(StringRef FName) { 519fe6060f1SDimitry Andric assert(!FName.empty() && "Top level node query must provide valid name"); 520e8d8bef9SDimitry Andric return RootContext.getChildContext(LineLocation(0, 0), FName); 521e8d8bef9SDimitry Andric } 522e8d8bef9SDimitry Andric 523e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::addTopLevelContextNode(StringRef FName) { 524e8d8bef9SDimitry Andric assert(!getTopLevelContextNode(FName) && "Node to add must not exist"); 525e8d8bef9SDimitry Andric return *RootContext.getOrCreateChildContext(LineLocation(0, 0), FName); 526e8d8bef9SDimitry Andric } 527e8d8bef9SDimitry Andric 528e8d8bef9SDimitry Andric void SampleContextTracker::mergeContextNode(ContextTrieNode &FromNode, 529e8d8bef9SDimitry Andric ContextTrieNode &ToNode, 530349cc55cSDimitry Andric uint32_t ContextFramesToRemove) { 531e8d8bef9SDimitry Andric FunctionSamples *FromSamples = FromNode.getFunctionSamples(); 532e8d8bef9SDimitry Andric FunctionSamples *ToSamples = ToNode.getFunctionSamples(); 533e8d8bef9SDimitry Andric if (FromSamples && ToSamples) { 534e8d8bef9SDimitry Andric // Merge/duplicate FromSamples into ToSamples 535e8d8bef9SDimitry Andric ToSamples->merge(*FromSamples); 536e8d8bef9SDimitry Andric ToSamples->getContext().setState(SyntheticContext); 537e8d8bef9SDimitry Andric FromSamples->getContext().setState(MergedContext); 538349cc55cSDimitry Andric if (FromSamples->getContext().hasAttribute(ContextShouldBeInlined)) 539349cc55cSDimitry Andric ToSamples->getContext().setAttribute(ContextShouldBeInlined); 540e8d8bef9SDimitry Andric } else if (FromSamples) { 541e8d8bef9SDimitry Andric // Transfer FromSamples from FromNode to ToNode 542e8d8bef9SDimitry Andric ToNode.setFunctionSamples(FromSamples); 543e8d8bef9SDimitry Andric FromSamples->getContext().setState(SyntheticContext); 544349cc55cSDimitry Andric FromSamples->getContext().promoteOnPath(ContextFramesToRemove); 545e8d8bef9SDimitry Andric FromNode.setFunctionSamples(nullptr); 546e8d8bef9SDimitry Andric } 547e8d8bef9SDimitry Andric } 548e8d8bef9SDimitry Andric 549e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree( 550e8d8bef9SDimitry Andric ContextTrieNode &FromNode, ContextTrieNode &ToNodeParent, 551349cc55cSDimitry Andric uint32_t ContextFramesToRemove) { 552349cc55cSDimitry Andric assert(ContextFramesToRemove && "Context to remove can't be empty"); 553e8d8bef9SDimitry Andric 554e8d8bef9SDimitry Andric // Ignore call site location if destination is top level under root 555e8d8bef9SDimitry Andric LineLocation NewCallSiteLoc = LineLocation(0, 0); 556e8d8bef9SDimitry Andric LineLocation OldCallSiteLoc = FromNode.getCallSiteLoc(); 557e8d8bef9SDimitry Andric ContextTrieNode &FromNodeParent = *FromNode.getParentContext(); 558e8d8bef9SDimitry Andric ContextTrieNode *ToNode = nullptr; 559e8d8bef9SDimitry Andric bool MoveToRoot = (&ToNodeParent == &RootContext); 560e8d8bef9SDimitry Andric if (!MoveToRoot) { 561e8d8bef9SDimitry Andric NewCallSiteLoc = OldCallSiteLoc; 562e8d8bef9SDimitry Andric } 563e8d8bef9SDimitry Andric 564e8d8bef9SDimitry Andric // Locate destination node, create/move if not existing 565e8d8bef9SDimitry Andric ToNode = ToNodeParent.getChildContext(NewCallSiteLoc, FromNode.getFuncName()); 566e8d8bef9SDimitry Andric if (!ToNode) { 567e8d8bef9SDimitry Andric // Do not delete node to move from its parent here because 568e8d8bef9SDimitry Andric // caller is iterating over children of that parent node. 569e8d8bef9SDimitry Andric ToNode = &ToNodeParent.moveToChildContext( 570349cc55cSDimitry Andric NewCallSiteLoc, std::move(FromNode), ContextFramesToRemove, false); 571e8d8bef9SDimitry Andric } else { 572e8d8bef9SDimitry Andric // Destination node exists, merge samples for the context tree 573349cc55cSDimitry Andric mergeContextNode(FromNode, *ToNode, ContextFramesToRemove); 574fe6060f1SDimitry Andric LLVM_DEBUG({ 575fe6060f1SDimitry Andric if (ToNode->getFunctionSamples()) 576fe6060f1SDimitry Andric dbgs() << " Context promoted and merged to: " 577349cc55cSDimitry Andric << ToNode->getFunctionSamples()->getContext().toString() << "\n"; 578fe6060f1SDimitry Andric }); 579e8d8bef9SDimitry Andric 580e8d8bef9SDimitry Andric // Recursively promote and merge children 581e8d8bef9SDimitry Andric for (auto &It : FromNode.getAllChildContext()) { 582e8d8bef9SDimitry Andric ContextTrieNode &FromChildNode = It.second; 583e8d8bef9SDimitry Andric promoteMergeContextSamplesTree(FromChildNode, *ToNode, 584349cc55cSDimitry Andric ContextFramesToRemove); 585e8d8bef9SDimitry Andric } 586e8d8bef9SDimitry Andric 587e8d8bef9SDimitry Andric // Remove children once they're all merged 588e8d8bef9SDimitry Andric FromNode.getAllChildContext().clear(); 589e8d8bef9SDimitry Andric } 590e8d8bef9SDimitry Andric 591e8d8bef9SDimitry Andric // For root of subtree, remove itself from old parent too 592e8d8bef9SDimitry Andric if (MoveToRoot) 593e8d8bef9SDimitry Andric FromNodeParent.removeChildContext(OldCallSiteLoc, ToNode->getFuncName()); 594e8d8bef9SDimitry Andric 595e8d8bef9SDimitry Andric return *ToNode; 596e8d8bef9SDimitry Andric } 597e8d8bef9SDimitry Andric } // namespace llvm 598