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