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 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 * 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, 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 120*fe6060f1SDimitry Andric 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*fe6060f1SDimitry Andric FuncToCtxtProfiles[Context.getNameWithoutContext()].push_back(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*fe6060f1SDimitry Andric CalleeName = FunctionSamples::getCanonicalFnName(CalleeName); 203*fe6060f1SDimitry Andric 204d409305fSDimitry Andric // For indirect call, CalleeName will be empty, in which case the context 205d409305fSDimitry Andric // profile for callee with largest total samples will be returned. 206e8d8bef9SDimitry Andric ContextTrieNode *CalleeContext = getCalleeContextFor(DIL, CalleeName); 207e8d8bef9SDimitry Andric if (CalleeContext) { 208e8d8bef9SDimitry Andric FunctionSamples *FSamples = CalleeContext->getFunctionSamples(); 209e8d8bef9SDimitry Andric LLVM_DEBUG(if (FSamples) { 210e8d8bef9SDimitry Andric dbgs() << " Callee context found: " << FSamples->getContext() << "\n"; 211e8d8bef9SDimitry Andric }); 212e8d8bef9SDimitry Andric return FSamples; 213e8d8bef9SDimitry Andric } 214e8d8bef9SDimitry Andric 215e8d8bef9SDimitry Andric return nullptr; 216e8d8bef9SDimitry Andric } 217e8d8bef9SDimitry Andric 218d409305fSDimitry Andric std::vector<const FunctionSamples *> 219d409305fSDimitry Andric SampleContextTracker::getIndirectCalleeContextSamplesFor( 220d409305fSDimitry Andric const DILocation *DIL) { 221d409305fSDimitry Andric std::vector<const FunctionSamples *> R; 222d409305fSDimitry Andric if (!DIL) 223d409305fSDimitry Andric return R; 224d409305fSDimitry Andric 225d409305fSDimitry Andric ContextTrieNode *CallerNode = getContextFor(DIL); 226d409305fSDimitry Andric LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL); 227d409305fSDimitry Andric for (auto &It : CallerNode->getAllChildContext()) { 228d409305fSDimitry Andric ContextTrieNode &ChildNode = It.second; 229d409305fSDimitry Andric if (ChildNode.getCallSiteLoc() != CallSite) 230d409305fSDimitry Andric continue; 231d409305fSDimitry Andric if (FunctionSamples *CalleeSamples = ChildNode.getFunctionSamples()) 232d409305fSDimitry Andric R.push_back(CalleeSamples); 233d409305fSDimitry Andric } 234d409305fSDimitry Andric 235d409305fSDimitry Andric return R; 236d409305fSDimitry Andric } 237d409305fSDimitry Andric 238e8d8bef9SDimitry Andric FunctionSamples * 239e8d8bef9SDimitry Andric SampleContextTracker::getContextSamplesFor(const DILocation *DIL) { 240e8d8bef9SDimitry Andric assert(DIL && "Expect non-null location"); 241e8d8bef9SDimitry Andric 242e8d8bef9SDimitry Andric ContextTrieNode *ContextNode = getContextFor(DIL); 243e8d8bef9SDimitry Andric if (!ContextNode) 244e8d8bef9SDimitry Andric return nullptr; 245e8d8bef9SDimitry Andric 246e8d8bef9SDimitry Andric // We may have inlined callees during pre-LTO compilation, in which case 247e8d8bef9SDimitry Andric // we need to rely on the inline stack from !dbg to mark context profile 248e8d8bef9SDimitry Andric // as inlined, instead of `MarkContextSamplesInlined` during inlining. 249e8d8bef9SDimitry Andric // Sample profile loader walks through all instructions to get profile, 250e8d8bef9SDimitry Andric // which calls this function. So once that is done, all previously inlined 251e8d8bef9SDimitry Andric // context profile should be marked properly. 252e8d8bef9SDimitry Andric FunctionSamples *Samples = ContextNode->getFunctionSamples(); 253e8d8bef9SDimitry Andric if (Samples && ContextNode->getParentContext() != &RootContext) 254e8d8bef9SDimitry Andric Samples->getContext().setState(InlinedContext); 255e8d8bef9SDimitry Andric 256e8d8bef9SDimitry Andric return Samples; 257e8d8bef9SDimitry Andric } 258e8d8bef9SDimitry Andric 259e8d8bef9SDimitry Andric FunctionSamples * 260e8d8bef9SDimitry Andric SampleContextTracker::getContextSamplesFor(const SampleContext &Context) { 261e8d8bef9SDimitry Andric ContextTrieNode *Node = getContextFor(Context); 262e8d8bef9SDimitry Andric if (!Node) 263e8d8bef9SDimitry Andric return nullptr; 264e8d8bef9SDimitry Andric 265e8d8bef9SDimitry Andric return Node->getFunctionSamples(); 266e8d8bef9SDimitry Andric } 267e8d8bef9SDimitry Andric 268d409305fSDimitry Andric SampleContextTracker::ContextSamplesTy & 269d409305fSDimitry Andric SampleContextTracker::getAllContextSamplesFor(const Function &Func) { 270d409305fSDimitry Andric StringRef CanonName = FunctionSamples::getCanonicalFnName(Func); 271*fe6060f1SDimitry Andric return FuncToCtxtProfiles[CanonName]; 272d409305fSDimitry Andric } 273d409305fSDimitry Andric 274d409305fSDimitry Andric SampleContextTracker::ContextSamplesTy & 275d409305fSDimitry Andric SampleContextTracker::getAllContextSamplesFor(StringRef Name) { 276*fe6060f1SDimitry Andric return FuncToCtxtProfiles[Name]; 277d409305fSDimitry Andric } 278d409305fSDimitry Andric 279e8d8bef9SDimitry Andric FunctionSamples *SampleContextTracker::getBaseSamplesFor(const Function &Func, 280e8d8bef9SDimitry Andric bool MergeContext) { 281e8d8bef9SDimitry Andric StringRef CanonName = FunctionSamples::getCanonicalFnName(Func); 282e8d8bef9SDimitry Andric return getBaseSamplesFor(CanonName, MergeContext); 283e8d8bef9SDimitry Andric } 284e8d8bef9SDimitry Andric 285e8d8bef9SDimitry Andric FunctionSamples *SampleContextTracker::getBaseSamplesFor(StringRef Name, 286e8d8bef9SDimitry Andric bool MergeContext) { 287e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Getting base profile for function: " << Name << "\n"); 288e8d8bef9SDimitry Andric // Base profile is top-level node (child of root node), so try to retrieve 289e8d8bef9SDimitry Andric // existing top-level node for given function first. If it exists, it could be 290e8d8bef9SDimitry Andric // that we've merged base profile before, or there's actually context-less 291e8d8bef9SDimitry Andric // profile from the input (e.g. due to unreliable stack walking). 292e8d8bef9SDimitry Andric ContextTrieNode *Node = getTopLevelContextNode(Name); 293e8d8bef9SDimitry Andric if (MergeContext) { 294e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << " Merging context profile into base profile: " << Name 295e8d8bef9SDimitry Andric << "\n"); 296e8d8bef9SDimitry Andric 297e8d8bef9SDimitry Andric // We have profile for function under different contexts, 298e8d8bef9SDimitry Andric // create synthetic base profile and merge context profiles 299e8d8bef9SDimitry Andric // into base profile. 300*fe6060f1SDimitry Andric for (auto *CSamples : FuncToCtxtProfiles[Name]) { 301e8d8bef9SDimitry Andric SampleContext &Context = CSamples->getContext(); 302e8d8bef9SDimitry Andric ContextTrieNode *FromNode = getContextFor(Context); 303e8d8bef9SDimitry Andric if (FromNode == Node) 304e8d8bef9SDimitry Andric continue; 305e8d8bef9SDimitry Andric 306e8d8bef9SDimitry Andric // Skip inlined context profile and also don't re-merge any context 307e8d8bef9SDimitry Andric if (Context.hasState(InlinedContext) || Context.hasState(MergedContext)) 308e8d8bef9SDimitry Andric continue; 309e8d8bef9SDimitry Andric 310e8d8bef9SDimitry Andric ContextTrieNode &ToNode = promoteMergeContextSamplesTree(*FromNode); 311e8d8bef9SDimitry Andric assert((!Node || Node == &ToNode) && "Expect only one base profile"); 312e8d8bef9SDimitry Andric Node = &ToNode; 313e8d8bef9SDimitry Andric } 314e8d8bef9SDimitry Andric } 315e8d8bef9SDimitry Andric 316e8d8bef9SDimitry Andric // Still no profile even after merge/promotion (if allowed) 317e8d8bef9SDimitry Andric if (!Node) 318e8d8bef9SDimitry Andric return nullptr; 319e8d8bef9SDimitry Andric 320e8d8bef9SDimitry Andric return Node->getFunctionSamples(); 321e8d8bef9SDimitry Andric } 322e8d8bef9SDimitry Andric 323e8d8bef9SDimitry Andric void SampleContextTracker::markContextSamplesInlined( 324e8d8bef9SDimitry Andric const FunctionSamples *InlinedSamples) { 325e8d8bef9SDimitry Andric assert(InlinedSamples && "Expect non-null inlined samples"); 326e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Marking context profile as inlined: " 327e8d8bef9SDimitry Andric << InlinedSamples->getContext() << "\n"); 328e8d8bef9SDimitry Andric InlinedSamples->getContext().setState(InlinedContext); 329e8d8bef9SDimitry Andric } 330e8d8bef9SDimitry Andric 331*fe6060f1SDimitry Andric ContextTrieNode &SampleContextTracker::getRootContext() { return RootContext; } 332*fe6060f1SDimitry Andric 333e8d8bef9SDimitry Andric void SampleContextTracker::promoteMergeContextSamplesTree( 334e8d8bef9SDimitry Andric const Instruction &Inst, StringRef CalleeName) { 335e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Promoting and merging context tree for instr: \n" 336e8d8bef9SDimitry Andric << Inst << "\n"); 337e8d8bef9SDimitry Andric // Get the caller context for the call instruction, we don't use callee 338e8d8bef9SDimitry Andric // name from call because there can be context from indirect calls too. 339e8d8bef9SDimitry Andric DILocation *DIL = Inst.getDebugLoc(); 340e8d8bef9SDimitry Andric ContextTrieNode *CallerNode = getContextFor(DIL); 341e8d8bef9SDimitry Andric if (!CallerNode) 342e8d8bef9SDimitry Andric return; 343e8d8bef9SDimitry Andric 344e8d8bef9SDimitry Andric // Get the context that needs to be promoted 345d409305fSDimitry Andric LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL); 346d409305fSDimitry Andric // For indirect call, CalleeName will be empty, in which case we need to 347d409305fSDimitry Andric // promote all non-inlined child context profiles. 348d409305fSDimitry Andric if (CalleeName.empty()) { 349d409305fSDimitry Andric for (auto &It : CallerNode->getAllChildContext()) { 350d409305fSDimitry Andric ContextTrieNode *NodeToPromo = &It.second; 351d409305fSDimitry Andric if (CallSite != NodeToPromo->getCallSiteLoc()) 352d409305fSDimitry Andric continue; 353d409305fSDimitry Andric FunctionSamples *FromSamples = NodeToPromo->getFunctionSamples(); 354d409305fSDimitry Andric if (FromSamples && FromSamples->getContext().hasState(InlinedContext)) 355d409305fSDimitry Andric continue; 356d409305fSDimitry Andric promoteMergeContextSamplesTree(*NodeToPromo); 357d409305fSDimitry Andric } 358d409305fSDimitry Andric return; 359d409305fSDimitry Andric } 360d409305fSDimitry Andric 361d409305fSDimitry Andric // Get the context for the given callee that needs to be promoted 362e8d8bef9SDimitry Andric ContextTrieNode *NodeToPromo = 363e8d8bef9SDimitry Andric CallerNode->getChildContext(CallSite, CalleeName); 364e8d8bef9SDimitry Andric if (!NodeToPromo) 365e8d8bef9SDimitry Andric return; 366e8d8bef9SDimitry Andric 367e8d8bef9SDimitry Andric promoteMergeContextSamplesTree(*NodeToPromo); 368e8d8bef9SDimitry Andric } 369e8d8bef9SDimitry Andric 370e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree( 371e8d8bef9SDimitry Andric ContextTrieNode &NodeToPromo) { 372e8d8bef9SDimitry Andric // Promote the input node to be directly under root. This can happen 373e8d8bef9SDimitry Andric // when we decided to not inline a function under context represented 374e8d8bef9SDimitry Andric // by the input node. The promote and merge is then needed to reflect 375e8d8bef9SDimitry Andric // the context profile in the base (context-less) profile. 376e8d8bef9SDimitry Andric FunctionSamples *FromSamples = NodeToPromo.getFunctionSamples(); 377e8d8bef9SDimitry Andric assert(FromSamples && "Shouldn't promote a context without profile"); 378e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << " Found context tree root to promote: " 379e8d8bef9SDimitry Andric << FromSamples->getContext() << "\n"); 380e8d8bef9SDimitry Andric 381d409305fSDimitry Andric assert(!FromSamples->getContext().hasState(InlinedContext) && 382d409305fSDimitry Andric "Shouldn't promote inlined context profile"); 383e8d8bef9SDimitry Andric StringRef ContextStrToRemove = FromSamples->getContext().getCallingContext(); 384e8d8bef9SDimitry Andric return promoteMergeContextSamplesTree(NodeToPromo, RootContext, 385e8d8bef9SDimitry Andric ContextStrToRemove); 386e8d8bef9SDimitry Andric } 387e8d8bef9SDimitry Andric 388e8d8bef9SDimitry Andric void SampleContextTracker::dump() { 389e8d8bef9SDimitry Andric dbgs() << "Context Profile Tree:\n"; 390e8d8bef9SDimitry Andric std::queue<ContextTrieNode *> NodeQueue; 391e8d8bef9SDimitry Andric NodeQueue.push(&RootContext); 392e8d8bef9SDimitry Andric 393e8d8bef9SDimitry Andric while (!NodeQueue.empty()) { 394e8d8bef9SDimitry Andric ContextTrieNode *Node = NodeQueue.front(); 395e8d8bef9SDimitry Andric NodeQueue.pop(); 396e8d8bef9SDimitry Andric Node->dump(); 397e8d8bef9SDimitry Andric 398e8d8bef9SDimitry Andric for (auto &It : Node->getAllChildContext()) { 399e8d8bef9SDimitry Andric ContextTrieNode *ChildNode = &It.second; 400e8d8bef9SDimitry Andric NodeQueue.push(ChildNode); 401e8d8bef9SDimitry Andric } 402e8d8bef9SDimitry Andric } 403e8d8bef9SDimitry Andric } 404e8d8bef9SDimitry Andric 405e8d8bef9SDimitry Andric ContextTrieNode * 406e8d8bef9SDimitry Andric SampleContextTracker::getContextFor(const SampleContext &Context) { 407e8d8bef9SDimitry Andric return getOrCreateContextPath(Context, false); 408e8d8bef9SDimitry Andric } 409e8d8bef9SDimitry Andric 410e8d8bef9SDimitry Andric ContextTrieNode * 411e8d8bef9SDimitry Andric SampleContextTracker::getCalleeContextFor(const DILocation *DIL, 412e8d8bef9SDimitry Andric StringRef CalleeName) { 413e8d8bef9SDimitry Andric assert(DIL && "Expect non-null location"); 414e8d8bef9SDimitry Andric 415e8d8bef9SDimitry Andric ContextTrieNode *CallContext = getContextFor(DIL); 416e8d8bef9SDimitry Andric if (!CallContext) 417e8d8bef9SDimitry Andric return nullptr; 418e8d8bef9SDimitry Andric 419d409305fSDimitry Andric // When CalleeName is empty, the child context profile with max 420d409305fSDimitry Andric // total samples will be returned. 421e8d8bef9SDimitry Andric return CallContext->getChildContext( 422d409305fSDimitry Andric FunctionSamples::getCallSiteIdentifier(DIL), CalleeName); 423e8d8bef9SDimitry Andric } 424e8d8bef9SDimitry Andric 425e8d8bef9SDimitry Andric ContextTrieNode *SampleContextTracker::getContextFor(const DILocation *DIL) { 426e8d8bef9SDimitry Andric assert(DIL && "Expect non-null location"); 427e8d8bef9SDimitry Andric SmallVector<std::pair<LineLocation, StringRef>, 10> S; 428e8d8bef9SDimitry Andric 429e8d8bef9SDimitry Andric // Use C++ linkage name if possible. 430e8d8bef9SDimitry Andric const DILocation *PrevDIL = DIL; 431e8d8bef9SDimitry Andric for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) { 432e8d8bef9SDimitry Andric StringRef Name = PrevDIL->getScope()->getSubprogram()->getLinkageName(); 433e8d8bef9SDimitry Andric if (Name.empty()) 434e8d8bef9SDimitry Andric Name = PrevDIL->getScope()->getSubprogram()->getName(); 435e8d8bef9SDimitry Andric S.push_back( 436*fe6060f1SDimitry Andric std::make_pair(FunctionSamples::getCallSiteIdentifier(DIL), Name)); 437e8d8bef9SDimitry Andric PrevDIL = DIL; 438e8d8bef9SDimitry Andric } 439e8d8bef9SDimitry Andric 440e8d8bef9SDimitry Andric // Push root node, note that root node like main may only 441e8d8bef9SDimitry Andric // a name, but not linkage name. 442e8d8bef9SDimitry Andric StringRef RootName = PrevDIL->getScope()->getSubprogram()->getLinkageName(); 443e8d8bef9SDimitry Andric if (RootName.empty()) 444e8d8bef9SDimitry Andric RootName = PrevDIL->getScope()->getSubprogram()->getName(); 445e8d8bef9SDimitry Andric S.push_back(std::make_pair(LineLocation(0, 0), RootName)); 446e8d8bef9SDimitry Andric 447e8d8bef9SDimitry Andric ContextTrieNode *ContextNode = &RootContext; 448e8d8bef9SDimitry Andric int I = S.size(); 449e8d8bef9SDimitry Andric while (--I >= 0 && ContextNode) { 450e8d8bef9SDimitry Andric LineLocation &CallSite = S[I].first; 451e8d8bef9SDimitry Andric StringRef &CalleeName = S[I].second; 452e8d8bef9SDimitry Andric ContextNode = ContextNode->getChildContext(CallSite, CalleeName); 453e8d8bef9SDimitry Andric } 454e8d8bef9SDimitry Andric 455e8d8bef9SDimitry Andric if (I < 0) 456e8d8bef9SDimitry Andric return ContextNode; 457e8d8bef9SDimitry Andric 458e8d8bef9SDimitry Andric return nullptr; 459e8d8bef9SDimitry Andric } 460e8d8bef9SDimitry Andric 461e8d8bef9SDimitry Andric ContextTrieNode * 462e8d8bef9SDimitry Andric SampleContextTracker::getOrCreateContextPath(const SampleContext &Context, 463e8d8bef9SDimitry Andric bool AllowCreate) { 464e8d8bef9SDimitry Andric ContextTrieNode *ContextNode = &RootContext; 465e8d8bef9SDimitry Andric StringRef ContextRemain = Context; 466e8d8bef9SDimitry Andric StringRef ChildContext; 467e8d8bef9SDimitry Andric StringRef CalleeName; 468e8d8bef9SDimitry Andric LineLocation CallSiteLoc(0, 0); 469e8d8bef9SDimitry Andric 470e8d8bef9SDimitry Andric while (ContextNode && !ContextRemain.empty()) { 471e8d8bef9SDimitry Andric auto ContextSplit = SampleContext::splitContextString(ContextRemain); 472e8d8bef9SDimitry Andric ChildContext = ContextSplit.first; 473e8d8bef9SDimitry Andric ContextRemain = ContextSplit.second; 474e8d8bef9SDimitry Andric LineLocation NextCallSiteLoc(0, 0); 475e8d8bef9SDimitry Andric SampleContext::decodeContextString(ChildContext, CalleeName, 476e8d8bef9SDimitry Andric NextCallSiteLoc); 477e8d8bef9SDimitry Andric 478e8d8bef9SDimitry Andric // Create child node at parent line/disc location 479e8d8bef9SDimitry Andric if (AllowCreate) { 480e8d8bef9SDimitry Andric ContextNode = 481e8d8bef9SDimitry Andric ContextNode->getOrCreateChildContext(CallSiteLoc, CalleeName); 482e8d8bef9SDimitry Andric } else { 483e8d8bef9SDimitry Andric ContextNode = ContextNode->getChildContext(CallSiteLoc, CalleeName); 484e8d8bef9SDimitry Andric } 485e8d8bef9SDimitry Andric CallSiteLoc = NextCallSiteLoc; 486e8d8bef9SDimitry Andric } 487e8d8bef9SDimitry Andric 488e8d8bef9SDimitry Andric assert((!AllowCreate || ContextNode) && 489e8d8bef9SDimitry Andric "Node must exist if creation is allowed"); 490e8d8bef9SDimitry Andric return ContextNode; 491e8d8bef9SDimitry Andric } 492e8d8bef9SDimitry Andric 493e8d8bef9SDimitry Andric ContextTrieNode *SampleContextTracker::getTopLevelContextNode(StringRef FName) { 494*fe6060f1SDimitry Andric assert(!FName.empty() && "Top level node query must provide valid name"); 495e8d8bef9SDimitry Andric return RootContext.getChildContext(LineLocation(0, 0), FName); 496e8d8bef9SDimitry Andric } 497e8d8bef9SDimitry Andric 498e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::addTopLevelContextNode(StringRef FName) { 499e8d8bef9SDimitry Andric assert(!getTopLevelContextNode(FName) && "Node to add must not exist"); 500e8d8bef9SDimitry Andric return *RootContext.getOrCreateChildContext(LineLocation(0, 0), FName); 501e8d8bef9SDimitry Andric } 502e8d8bef9SDimitry Andric 503e8d8bef9SDimitry Andric void SampleContextTracker::mergeContextNode(ContextTrieNode &FromNode, 504e8d8bef9SDimitry Andric ContextTrieNode &ToNode, 505e8d8bef9SDimitry Andric StringRef ContextStrToRemove) { 506e8d8bef9SDimitry Andric FunctionSamples *FromSamples = FromNode.getFunctionSamples(); 507e8d8bef9SDimitry Andric FunctionSamples *ToSamples = ToNode.getFunctionSamples(); 508e8d8bef9SDimitry Andric if (FromSamples && ToSamples) { 509e8d8bef9SDimitry Andric // Merge/duplicate FromSamples into ToSamples 510e8d8bef9SDimitry Andric ToSamples->merge(*FromSamples); 511e8d8bef9SDimitry Andric ToSamples->getContext().setState(SyntheticContext); 512e8d8bef9SDimitry Andric FromSamples->getContext().setState(MergedContext); 513e8d8bef9SDimitry Andric } else if (FromSamples) { 514e8d8bef9SDimitry Andric // Transfer FromSamples from FromNode to ToNode 515e8d8bef9SDimitry Andric ToNode.setFunctionSamples(FromSamples); 516e8d8bef9SDimitry Andric FromSamples->getContext().setState(SyntheticContext); 517e8d8bef9SDimitry Andric FromSamples->getContext().promoteOnPath(ContextStrToRemove); 518e8d8bef9SDimitry Andric FromNode.setFunctionSamples(nullptr); 519e8d8bef9SDimitry Andric } 520e8d8bef9SDimitry Andric } 521e8d8bef9SDimitry Andric 522e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree( 523e8d8bef9SDimitry Andric ContextTrieNode &FromNode, ContextTrieNode &ToNodeParent, 524e8d8bef9SDimitry Andric StringRef ContextStrToRemove) { 525e8d8bef9SDimitry Andric assert(!ContextStrToRemove.empty() && "Context to remove can't be empty"); 526e8d8bef9SDimitry Andric 527e8d8bef9SDimitry Andric // Ignore call site location if destination is top level under root 528e8d8bef9SDimitry Andric LineLocation NewCallSiteLoc = LineLocation(0, 0); 529e8d8bef9SDimitry Andric LineLocation OldCallSiteLoc = FromNode.getCallSiteLoc(); 530e8d8bef9SDimitry Andric ContextTrieNode &FromNodeParent = *FromNode.getParentContext(); 531e8d8bef9SDimitry Andric ContextTrieNode *ToNode = nullptr; 532e8d8bef9SDimitry Andric bool MoveToRoot = (&ToNodeParent == &RootContext); 533e8d8bef9SDimitry Andric if (!MoveToRoot) { 534e8d8bef9SDimitry Andric NewCallSiteLoc = OldCallSiteLoc; 535e8d8bef9SDimitry Andric } 536e8d8bef9SDimitry Andric 537e8d8bef9SDimitry Andric // Locate destination node, create/move if not existing 538e8d8bef9SDimitry Andric ToNode = ToNodeParent.getChildContext(NewCallSiteLoc, FromNode.getFuncName()); 539e8d8bef9SDimitry Andric if (!ToNode) { 540e8d8bef9SDimitry Andric // Do not delete node to move from its parent here because 541e8d8bef9SDimitry Andric // caller is iterating over children of that parent node. 542e8d8bef9SDimitry Andric ToNode = &ToNodeParent.moveToChildContext( 543e8d8bef9SDimitry Andric NewCallSiteLoc, std::move(FromNode), ContextStrToRemove, false); 544e8d8bef9SDimitry Andric } else { 545e8d8bef9SDimitry Andric // Destination node exists, merge samples for the context tree 546e8d8bef9SDimitry Andric mergeContextNode(FromNode, *ToNode, ContextStrToRemove); 547*fe6060f1SDimitry Andric LLVM_DEBUG({ 548*fe6060f1SDimitry Andric if (ToNode->getFunctionSamples()) 549*fe6060f1SDimitry Andric dbgs() << " Context promoted and merged to: " 550*fe6060f1SDimitry Andric << ToNode->getFunctionSamples()->getContext() << "\n"; 551*fe6060f1SDimitry Andric }); 552e8d8bef9SDimitry Andric 553e8d8bef9SDimitry Andric // Recursively promote and merge children 554e8d8bef9SDimitry Andric for (auto &It : FromNode.getAllChildContext()) { 555e8d8bef9SDimitry Andric ContextTrieNode &FromChildNode = It.second; 556e8d8bef9SDimitry Andric promoteMergeContextSamplesTree(FromChildNode, *ToNode, 557e8d8bef9SDimitry Andric ContextStrToRemove); 558e8d8bef9SDimitry Andric } 559e8d8bef9SDimitry Andric 560e8d8bef9SDimitry Andric // Remove children once they're all merged 561e8d8bef9SDimitry Andric FromNode.getAllChildContext().clear(); 562e8d8bef9SDimitry Andric } 563e8d8bef9SDimitry Andric 564e8d8bef9SDimitry Andric // For root of subtree, remove itself from old parent too 565e8d8bef9SDimitry Andric if (MoveToRoot) 566e8d8bef9SDimitry Andric FromNodeParent.removeChildContext(OldCallSiteLoc, ToNode->getFuncName()); 567e8d8bef9SDimitry Andric 568e8d8bef9SDimitry Andric return *ToNode; 569e8d8bef9SDimitry Andric } 570e8d8bef9SDimitry Andric } // namespace llvm 571