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" 17*81ad6265SDimitry Andric #include "llvm/IR/InstrTypes.h" 18*81ad6265SDimitry Andric #include "llvm/IR/Instruction.h" 19e8d8bef9SDimitry Andric #include "llvm/ProfileData/SampleProf.h" 20e8d8bef9SDimitry Andric #include <map> 21e8d8bef9SDimitry Andric #include <queue> 22e8d8bef9SDimitry Andric #include <vector> 23e8d8bef9SDimitry Andric 24e8d8bef9SDimitry Andric using namespace llvm; 25e8d8bef9SDimitry Andric using namespace sampleprof; 26e8d8bef9SDimitry Andric 27e8d8bef9SDimitry Andric #define DEBUG_TYPE "sample-context-tracker" 28e8d8bef9SDimitry Andric 29e8d8bef9SDimitry Andric namespace llvm { 30e8d8bef9SDimitry Andric 31e8d8bef9SDimitry Andric ContextTrieNode *ContextTrieNode::getChildContext(const LineLocation &CallSite, 32e8d8bef9SDimitry Andric StringRef CalleeName) { 33e8d8bef9SDimitry Andric if (CalleeName.empty()) 34d409305fSDimitry Andric return getHottestChildContext(CallSite); 35e8d8bef9SDimitry Andric 360eae32dcSDimitry Andric uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite); 37e8d8bef9SDimitry Andric auto It = AllChildContext.find(Hash); 38e8d8bef9SDimitry Andric if (It != AllChildContext.end()) 39e8d8bef9SDimitry Andric return &It->second; 40e8d8bef9SDimitry Andric return nullptr; 41e8d8bef9SDimitry Andric } 42e8d8bef9SDimitry Andric 43e8d8bef9SDimitry Andric ContextTrieNode * 44d409305fSDimitry Andric ContextTrieNode::getHottestChildContext(const LineLocation &CallSite) { 45e8d8bef9SDimitry Andric // CSFDO-TODO: This could be slow, change AllChildContext so we can 46e8d8bef9SDimitry Andric // do point look up for child node by call site alone. 47d409305fSDimitry Andric // Retrieve the child node with max count for indirect call 48e8d8bef9SDimitry Andric ContextTrieNode *ChildNodeRet = nullptr; 49d409305fSDimitry Andric uint64_t MaxCalleeSamples = 0; 50e8d8bef9SDimitry Andric for (auto &It : AllChildContext) { 51e8d8bef9SDimitry Andric ContextTrieNode &ChildNode = It.second; 52d409305fSDimitry Andric if (ChildNode.CallSiteLoc != CallSite) 53d409305fSDimitry Andric continue; 54d409305fSDimitry Andric FunctionSamples *Samples = ChildNode.getFunctionSamples(); 55d409305fSDimitry Andric if (!Samples) 56d409305fSDimitry Andric continue; 57d409305fSDimitry Andric if (Samples->getTotalSamples() > MaxCalleeSamples) { 58e8d8bef9SDimitry Andric ChildNodeRet = &ChildNode; 59d409305fSDimitry Andric MaxCalleeSamples = Samples->getTotalSamples(); 60e8d8bef9SDimitry Andric } 61e8d8bef9SDimitry Andric } 62e8d8bef9SDimitry Andric 63e8d8bef9SDimitry Andric return ChildNodeRet; 64e8d8bef9SDimitry Andric } 65e8d8bef9SDimitry Andric 66*81ad6265SDimitry Andric ContextTrieNode & 67*81ad6265SDimitry Andric SampleContextTracker::moveContextSamples(ContextTrieNode &ToNodeParent, 68*81ad6265SDimitry Andric const LineLocation &CallSite, 69*81ad6265SDimitry Andric ContextTrieNode &&NodeToMove) { 700eae32dcSDimitry Andric uint64_t Hash = 710eae32dcSDimitry Andric FunctionSamples::getCallSiteHash(NodeToMove.getFuncName(), CallSite); 72*81ad6265SDimitry Andric std::map<uint64_t, ContextTrieNode> &AllChildContext = 73*81ad6265SDimitry Andric ToNodeParent.getAllChildContext(); 74e8d8bef9SDimitry Andric assert(!AllChildContext.count(Hash) && "Node to remove must exist"); 75e8d8bef9SDimitry Andric AllChildContext[Hash] = NodeToMove; 76e8d8bef9SDimitry Andric ContextTrieNode &NewNode = AllChildContext[Hash]; 77*81ad6265SDimitry Andric NewNode.setCallSiteLoc(CallSite); 78e8d8bef9SDimitry Andric 79e8d8bef9SDimitry Andric // Walk through nodes in the moved the subtree, and update 80e8d8bef9SDimitry Andric // FunctionSamples' context as for the context promotion. 81e8d8bef9SDimitry Andric // We also need to set new parant link for all children. 82e8d8bef9SDimitry Andric std::queue<ContextTrieNode *> NodeToUpdate; 83*81ad6265SDimitry Andric NewNode.setParentContext(&ToNodeParent); 84e8d8bef9SDimitry Andric NodeToUpdate.push(&NewNode); 85e8d8bef9SDimitry Andric 86e8d8bef9SDimitry Andric while (!NodeToUpdate.empty()) { 87e8d8bef9SDimitry Andric ContextTrieNode *Node = NodeToUpdate.front(); 88e8d8bef9SDimitry Andric NodeToUpdate.pop(); 89e8d8bef9SDimitry Andric FunctionSamples *FSamples = Node->getFunctionSamples(); 90e8d8bef9SDimitry Andric 91e8d8bef9SDimitry Andric if (FSamples) { 92*81ad6265SDimitry Andric setContextNode(FSamples, Node); 93e8d8bef9SDimitry Andric FSamples->getContext().setState(SyntheticContext); 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 return NewNode; 104e8d8bef9SDimitry Andric } 105e8d8bef9SDimitry Andric 106e8d8bef9SDimitry Andric void ContextTrieNode::removeChildContext(const LineLocation &CallSite, 107e8d8bef9SDimitry Andric StringRef CalleeName) { 1080eae32dcSDimitry Andric uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite); 109e8d8bef9SDimitry Andric // Note this essentially calls dtor and destroys that child context 110e8d8bef9SDimitry Andric AllChildContext.erase(Hash); 111e8d8bef9SDimitry Andric } 112e8d8bef9SDimitry Andric 113349cc55cSDimitry Andric std::map<uint64_t, ContextTrieNode> &ContextTrieNode::getAllChildContext() { 114e8d8bef9SDimitry Andric return AllChildContext; 115e8d8bef9SDimitry Andric } 116e8d8bef9SDimitry Andric 117fe6060f1SDimitry Andric StringRef ContextTrieNode::getFuncName() const { return FuncName; } 118e8d8bef9SDimitry Andric 119e8d8bef9SDimitry Andric FunctionSamples *ContextTrieNode::getFunctionSamples() const { 120e8d8bef9SDimitry Andric return FuncSamples; 121e8d8bef9SDimitry Andric } 122e8d8bef9SDimitry Andric 123e8d8bef9SDimitry Andric void ContextTrieNode::setFunctionSamples(FunctionSamples *FSamples) { 124e8d8bef9SDimitry Andric FuncSamples = FSamples; 125e8d8bef9SDimitry Andric } 126e8d8bef9SDimitry Andric 127349cc55cSDimitry Andric Optional<uint32_t> ContextTrieNode::getFunctionSize() const { return FuncSize; } 128349cc55cSDimitry Andric 129349cc55cSDimitry Andric void ContextTrieNode::addFunctionSize(uint32_t FSize) { 130*81ad6265SDimitry Andric if (!FuncSize) 131349cc55cSDimitry Andric FuncSize = 0; 132349cc55cSDimitry Andric 133349cc55cSDimitry Andric FuncSize = FuncSize.getValue() + FSize; 134349cc55cSDimitry Andric } 135349cc55cSDimitry Andric 136e8d8bef9SDimitry Andric LineLocation ContextTrieNode::getCallSiteLoc() const { return CallSiteLoc; } 137e8d8bef9SDimitry Andric 138e8d8bef9SDimitry Andric ContextTrieNode *ContextTrieNode::getParentContext() const { 139e8d8bef9SDimitry Andric return ParentContext; 140e8d8bef9SDimitry Andric } 141e8d8bef9SDimitry Andric 142e8d8bef9SDimitry Andric void ContextTrieNode::setParentContext(ContextTrieNode *Parent) { 143e8d8bef9SDimitry Andric ParentContext = Parent; 144e8d8bef9SDimitry Andric } 145e8d8bef9SDimitry Andric 146*81ad6265SDimitry Andric void ContextTrieNode::setCallSiteLoc(const LineLocation &Loc) { 147*81ad6265SDimitry Andric CallSiteLoc = Loc; 148*81ad6265SDimitry Andric } 149*81ad6265SDimitry 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) { 1800eae32dcSDimitry 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 ContextTrieNode *NewNode = getOrCreateContextPath(Context, true); 206e8d8bef9SDimitry Andric assert(!NewNode->getFunctionSamples() && 207e8d8bef9SDimitry Andric "New node can't have sample profile"); 208e8d8bef9SDimitry Andric NewNode->setFunctionSamples(FSamples); 209e8d8bef9SDimitry Andric } 210*81ad6265SDimitry Andric populateFuncToCtxtMap(); 211*81ad6265SDimitry Andric } 212*81ad6265SDimitry Andric 213*81ad6265SDimitry Andric void SampleContextTracker::populateFuncToCtxtMap() { 214*81ad6265SDimitry Andric for (auto *Node : *this) { 215*81ad6265SDimitry Andric FunctionSamples *FSamples = Node->getFunctionSamples(); 216*81ad6265SDimitry Andric if (FSamples) { 217*81ad6265SDimitry Andric FSamples->getContext().setState(RawContext); 218*81ad6265SDimitry Andric setContextNode(FSamples, Node); 219*81ad6265SDimitry Andric FuncToCtxtProfiles[Node->getFuncName()].push_back(FSamples); 220*81ad6265SDimitry Andric } 221*81ad6265SDimitry Andric } 222e8d8bef9SDimitry Andric } 223e8d8bef9SDimitry Andric 224e8d8bef9SDimitry Andric FunctionSamples * 225e8d8bef9SDimitry Andric SampleContextTracker::getCalleeContextSamplesFor(const CallBase &Inst, 226e8d8bef9SDimitry Andric StringRef CalleeName) { 227e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Getting callee context for instr: " << Inst << "\n"); 228e8d8bef9SDimitry Andric DILocation *DIL = Inst.getDebugLoc(); 229e8d8bef9SDimitry Andric if (!DIL) 230e8d8bef9SDimitry Andric return nullptr; 231e8d8bef9SDimitry Andric 232fe6060f1SDimitry Andric CalleeName = FunctionSamples::getCanonicalFnName(CalleeName); 233349cc55cSDimitry Andric // Convert real function names to MD5 names, if the input profile is 234349cc55cSDimitry Andric // MD5-based. 235349cc55cSDimitry Andric std::string FGUID; 236349cc55cSDimitry Andric CalleeName = getRepInFormat(CalleeName, FunctionSamples::UseMD5, FGUID); 237fe6060f1SDimitry Andric 238d409305fSDimitry Andric // For indirect call, CalleeName will be empty, in which case the context 239d409305fSDimitry Andric // profile for callee with largest total samples will be returned. 240e8d8bef9SDimitry Andric ContextTrieNode *CalleeContext = getCalleeContextFor(DIL, CalleeName); 241e8d8bef9SDimitry Andric if (CalleeContext) { 242e8d8bef9SDimitry Andric FunctionSamples *FSamples = CalleeContext->getFunctionSamples(); 243e8d8bef9SDimitry Andric LLVM_DEBUG(if (FSamples) { 244*81ad6265SDimitry Andric dbgs() << " Callee context found: " << getContextString(CalleeContext) 245349cc55cSDimitry Andric << "\n"; 246e8d8bef9SDimitry Andric }); 247e8d8bef9SDimitry Andric return FSamples; 248e8d8bef9SDimitry Andric } 249e8d8bef9SDimitry Andric 250e8d8bef9SDimitry Andric return nullptr; 251e8d8bef9SDimitry Andric } 252e8d8bef9SDimitry Andric 253d409305fSDimitry Andric std::vector<const FunctionSamples *> 254d409305fSDimitry Andric SampleContextTracker::getIndirectCalleeContextSamplesFor( 255d409305fSDimitry Andric const DILocation *DIL) { 256d409305fSDimitry Andric std::vector<const FunctionSamples *> R; 257d409305fSDimitry Andric if (!DIL) 258d409305fSDimitry Andric return R; 259d409305fSDimitry Andric 260d409305fSDimitry Andric ContextTrieNode *CallerNode = getContextFor(DIL); 261d409305fSDimitry Andric LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL); 262d409305fSDimitry Andric for (auto &It : CallerNode->getAllChildContext()) { 263d409305fSDimitry Andric ContextTrieNode &ChildNode = It.second; 264d409305fSDimitry Andric if (ChildNode.getCallSiteLoc() != CallSite) 265d409305fSDimitry Andric continue; 266d409305fSDimitry Andric if (FunctionSamples *CalleeSamples = ChildNode.getFunctionSamples()) 267d409305fSDimitry Andric R.push_back(CalleeSamples); 268d409305fSDimitry Andric } 269d409305fSDimitry Andric 270d409305fSDimitry Andric return R; 271d409305fSDimitry Andric } 272d409305fSDimitry Andric 273e8d8bef9SDimitry Andric FunctionSamples * 274e8d8bef9SDimitry Andric SampleContextTracker::getContextSamplesFor(const DILocation *DIL) { 275e8d8bef9SDimitry Andric assert(DIL && "Expect non-null location"); 276e8d8bef9SDimitry Andric 277e8d8bef9SDimitry Andric ContextTrieNode *ContextNode = getContextFor(DIL); 278e8d8bef9SDimitry Andric if (!ContextNode) 279e8d8bef9SDimitry Andric return nullptr; 280e8d8bef9SDimitry Andric 281e8d8bef9SDimitry Andric // We may have inlined callees during pre-LTO compilation, in which case 282e8d8bef9SDimitry Andric // we need to rely on the inline stack from !dbg to mark context profile 283e8d8bef9SDimitry Andric // as inlined, instead of `MarkContextSamplesInlined` during inlining. 284e8d8bef9SDimitry Andric // Sample profile loader walks through all instructions to get profile, 285e8d8bef9SDimitry Andric // which calls this function. So once that is done, all previously inlined 286e8d8bef9SDimitry Andric // context profile should be marked properly. 287e8d8bef9SDimitry Andric FunctionSamples *Samples = ContextNode->getFunctionSamples(); 288e8d8bef9SDimitry Andric if (Samples && ContextNode->getParentContext() != &RootContext) 289e8d8bef9SDimitry Andric Samples->getContext().setState(InlinedContext); 290e8d8bef9SDimitry Andric 291e8d8bef9SDimitry Andric return Samples; 292e8d8bef9SDimitry Andric } 293e8d8bef9SDimitry Andric 294e8d8bef9SDimitry Andric FunctionSamples * 295e8d8bef9SDimitry Andric SampleContextTracker::getContextSamplesFor(const SampleContext &Context) { 296e8d8bef9SDimitry Andric ContextTrieNode *Node = getContextFor(Context); 297e8d8bef9SDimitry Andric if (!Node) 298e8d8bef9SDimitry Andric return nullptr; 299e8d8bef9SDimitry Andric 300e8d8bef9SDimitry Andric return Node->getFunctionSamples(); 301e8d8bef9SDimitry Andric } 302e8d8bef9SDimitry Andric 303d409305fSDimitry Andric SampleContextTracker::ContextSamplesTy & 304d409305fSDimitry Andric SampleContextTracker::getAllContextSamplesFor(const Function &Func) { 305d409305fSDimitry Andric StringRef CanonName = FunctionSamples::getCanonicalFnName(Func); 306fe6060f1SDimitry Andric return FuncToCtxtProfiles[CanonName]; 307d409305fSDimitry Andric } 308d409305fSDimitry Andric 309d409305fSDimitry Andric SampleContextTracker::ContextSamplesTy & 310d409305fSDimitry Andric SampleContextTracker::getAllContextSamplesFor(StringRef Name) { 311fe6060f1SDimitry Andric return FuncToCtxtProfiles[Name]; 312d409305fSDimitry Andric } 313d409305fSDimitry Andric 314e8d8bef9SDimitry Andric FunctionSamples *SampleContextTracker::getBaseSamplesFor(const Function &Func, 315e8d8bef9SDimitry Andric bool MergeContext) { 316e8d8bef9SDimitry Andric StringRef CanonName = FunctionSamples::getCanonicalFnName(Func); 317e8d8bef9SDimitry Andric return getBaseSamplesFor(CanonName, MergeContext); 318e8d8bef9SDimitry Andric } 319e8d8bef9SDimitry Andric 320e8d8bef9SDimitry Andric FunctionSamples *SampleContextTracker::getBaseSamplesFor(StringRef Name, 321e8d8bef9SDimitry Andric bool MergeContext) { 322e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Getting base profile for function: " << Name << "\n"); 323349cc55cSDimitry Andric // Convert real function names to MD5 names, if the input profile is 324349cc55cSDimitry Andric // MD5-based. 325349cc55cSDimitry Andric std::string FGUID; 326349cc55cSDimitry Andric Name = getRepInFormat(Name, FunctionSamples::UseMD5, FGUID); 327349cc55cSDimitry Andric 328e8d8bef9SDimitry Andric // Base profile is top-level node (child of root node), so try to retrieve 329e8d8bef9SDimitry Andric // existing top-level node for given function first. If it exists, it could be 330e8d8bef9SDimitry Andric // that we've merged base profile before, or there's actually context-less 331e8d8bef9SDimitry Andric // profile from the input (e.g. due to unreliable stack walking). 332e8d8bef9SDimitry Andric ContextTrieNode *Node = getTopLevelContextNode(Name); 333e8d8bef9SDimitry Andric if (MergeContext) { 334e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << " Merging context profile into base profile: " << Name 335e8d8bef9SDimitry Andric << "\n"); 336e8d8bef9SDimitry Andric 337e8d8bef9SDimitry Andric // We have profile for function under different contexts, 338e8d8bef9SDimitry Andric // create synthetic base profile and merge context profiles 339e8d8bef9SDimitry Andric // into base profile. 340fe6060f1SDimitry Andric for (auto *CSamples : FuncToCtxtProfiles[Name]) { 341e8d8bef9SDimitry Andric SampleContext &Context = CSamples->getContext(); 342e8d8bef9SDimitry Andric // Skip inlined context profile and also don't re-merge any context 343e8d8bef9SDimitry Andric if (Context.hasState(InlinedContext) || Context.hasState(MergedContext)) 344e8d8bef9SDimitry Andric continue; 345e8d8bef9SDimitry Andric 346*81ad6265SDimitry Andric ContextTrieNode *FromNode = getContextNodeForProfile(CSamples); 347349cc55cSDimitry Andric if (FromNode == Node) 348349cc55cSDimitry Andric continue; 349349cc55cSDimitry Andric 350e8d8bef9SDimitry Andric ContextTrieNode &ToNode = promoteMergeContextSamplesTree(*FromNode); 351e8d8bef9SDimitry Andric assert((!Node || Node == &ToNode) && "Expect only one base profile"); 352e8d8bef9SDimitry Andric Node = &ToNode; 353e8d8bef9SDimitry Andric } 354e8d8bef9SDimitry Andric } 355e8d8bef9SDimitry Andric 356e8d8bef9SDimitry Andric // Still no profile even after merge/promotion (if allowed) 357e8d8bef9SDimitry Andric if (!Node) 358e8d8bef9SDimitry Andric return nullptr; 359e8d8bef9SDimitry Andric 360e8d8bef9SDimitry Andric return Node->getFunctionSamples(); 361e8d8bef9SDimitry Andric } 362e8d8bef9SDimitry Andric 363e8d8bef9SDimitry Andric void SampleContextTracker::markContextSamplesInlined( 364e8d8bef9SDimitry Andric const FunctionSamples *InlinedSamples) { 365e8d8bef9SDimitry Andric assert(InlinedSamples && "Expect non-null inlined samples"); 366e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Marking context profile as inlined: " 367*81ad6265SDimitry Andric << getContextString(*InlinedSamples) << "\n"); 368e8d8bef9SDimitry Andric InlinedSamples->getContext().setState(InlinedContext); 369e8d8bef9SDimitry Andric } 370e8d8bef9SDimitry Andric 371fe6060f1SDimitry Andric ContextTrieNode &SampleContextTracker::getRootContext() { return RootContext; } 372fe6060f1SDimitry Andric 373e8d8bef9SDimitry Andric void SampleContextTracker::promoteMergeContextSamplesTree( 374e8d8bef9SDimitry Andric const Instruction &Inst, StringRef CalleeName) { 375e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "Promoting and merging context tree for instr: \n" 376e8d8bef9SDimitry Andric << Inst << "\n"); 377e8d8bef9SDimitry Andric // Get the caller context for the call instruction, we don't use callee 378e8d8bef9SDimitry Andric // name from call because there can be context from indirect calls too. 379e8d8bef9SDimitry Andric DILocation *DIL = Inst.getDebugLoc(); 380e8d8bef9SDimitry Andric ContextTrieNode *CallerNode = getContextFor(DIL); 381e8d8bef9SDimitry Andric if (!CallerNode) 382e8d8bef9SDimitry Andric return; 383e8d8bef9SDimitry Andric 384e8d8bef9SDimitry Andric // Get the context that needs to be promoted 385d409305fSDimitry Andric LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL); 386d409305fSDimitry Andric // For indirect call, CalleeName will be empty, in which case we need to 387d409305fSDimitry Andric // promote all non-inlined child context profiles. 388d409305fSDimitry Andric if (CalleeName.empty()) { 389d409305fSDimitry Andric for (auto &It : CallerNode->getAllChildContext()) { 390d409305fSDimitry Andric ContextTrieNode *NodeToPromo = &It.second; 391d409305fSDimitry Andric if (CallSite != NodeToPromo->getCallSiteLoc()) 392d409305fSDimitry Andric continue; 393d409305fSDimitry Andric FunctionSamples *FromSamples = NodeToPromo->getFunctionSamples(); 394d409305fSDimitry Andric if (FromSamples && FromSamples->getContext().hasState(InlinedContext)) 395d409305fSDimitry Andric continue; 396d409305fSDimitry Andric promoteMergeContextSamplesTree(*NodeToPromo); 397d409305fSDimitry Andric } 398d409305fSDimitry Andric return; 399d409305fSDimitry Andric } 400d409305fSDimitry Andric 401d409305fSDimitry Andric // Get the context for the given callee that needs to be promoted 402e8d8bef9SDimitry Andric ContextTrieNode *NodeToPromo = 403e8d8bef9SDimitry Andric CallerNode->getChildContext(CallSite, CalleeName); 404e8d8bef9SDimitry Andric if (!NodeToPromo) 405e8d8bef9SDimitry Andric return; 406e8d8bef9SDimitry Andric 407e8d8bef9SDimitry Andric promoteMergeContextSamplesTree(*NodeToPromo); 408e8d8bef9SDimitry Andric } 409e8d8bef9SDimitry Andric 410e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree( 411e8d8bef9SDimitry Andric ContextTrieNode &NodeToPromo) { 412e8d8bef9SDimitry Andric // Promote the input node to be directly under root. This can happen 413e8d8bef9SDimitry Andric // when we decided to not inline a function under context represented 414e8d8bef9SDimitry Andric // by the input node. The promote and merge is then needed to reflect 415e8d8bef9SDimitry Andric // the context profile in the base (context-less) profile. 416e8d8bef9SDimitry Andric FunctionSamples *FromSamples = NodeToPromo.getFunctionSamples(); 417e8d8bef9SDimitry Andric assert(FromSamples && "Shouldn't promote a context without profile"); 418*81ad6265SDimitry Andric (void)FromSamples; // Unused in release build. 419*81ad6265SDimitry Andric 420e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << " Found context tree root to promote: " 421*81ad6265SDimitry Andric << getContextString(&NodeToPromo) << "\n"); 422e8d8bef9SDimitry Andric 423d409305fSDimitry Andric assert(!FromSamples->getContext().hasState(InlinedContext) && 424d409305fSDimitry Andric "Shouldn't promote inlined context profile"); 425*81ad6265SDimitry Andric return promoteMergeContextSamplesTree(NodeToPromo, RootContext); 426e8d8bef9SDimitry Andric } 427e8d8bef9SDimitry Andric 428*81ad6265SDimitry Andric #ifndef NDEBUG 429*81ad6265SDimitry Andric std::string 430*81ad6265SDimitry Andric SampleContextTracker::getContextString(const FunctionSamples &FSamples) const { 431*81ad6265SDimitry Andric return getContextString(getContextNodeForProfile(&FSamples)); 432*81ad6265SDimitry Andric } 433*81ad6265SDimitry Andric 434*81ad6265SDimitry Andric std::string 435*81ad6265SDimitry Andric SampleContextTracker::getContextString(ContextTrieNode *Node) const { 436*81ad6265SDimitry Andric SampleContextFrameVector Res; 437*81ad6265SDimitry Andric if (Node == &RootContext) 438*81ad6265SDimitry Andric return std::string(); 439*81ad6265SDimitry Andric Res.emplace_back(Node->getFuncName(), LineLocation(0, 0)); 440*81ad6265SDimitry Andric 441*81ad6265SDimitry Andric ContextTrieNode *PreNode = Node; 442*81ad6265SDimitry Andric Node = Node->getParentContext(); 443*81ad6265SDimitry Andric while (Node && Node != &RootContext) { 444*81ad6265SDimitry Andric Res.emplace_back(Node->getFuncName(), PreNode->getCallSiteLoc()); 445*81ad6265SDimitry Andric PreNode = Node; 446*81ad6265SDimitry Andric Node = Node->getParentContext(); 447*81ad6265SDimitry Andric } 448*81ad6265SDimitry Andric 449*81ad6265SDimitry Andric std::reverse(Res.begin(), Res.end()); 450*81ad6265SDimitry Andric 451*81ad6265SDimitry Andric return SampleContext::getContextString(Res); 452*81ad6265SDimitry Andric } 453*81ad6265SDimitry Andric #endif 454*81ad6265SDimitry Andric 455349cc55cSDimitry Andric void SampleContextTracker::dump() { RootContext.dumpTree(); } 456e8d8bef9SDimitry Andric 457349cc55cSDimitry Andric StringRef SampleContextTracker::getFuncNameFor(ContextTrieNode *Node) const { 458349cc55cSDimitry Andric if (!FunctionSamples::UseMD5) 459349cc55cSDimitry Andric return Node->getFuncName(); 460349cc55cSDimitry Andric assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be populated first"); 461349cc55cSDimitry Andric return GUIDToFuncNameMap->lookup(std::stoull(Node->getFuncName().data())); 462e8d8bef9SDimitry Andric } 463e8d8bef9SDimitry Andric 464e8d8bef9SDimitry Andric ContextTrieNode * 465e8d8bef9SDimitry Andric SampleContextTracker::getContextFor(const SampleContext &Context) { 466e8d8bef9SDimitry Andric return getOrCreateContextPath(Context, false); 467e8d8bef9SDimitry Andric } 468e8d8bef9SDimitry Andric 469e8d8bef9SDimitry Andric ContextTrieNode * 470e8d8bef9SDimitry Andric SampleContextTracker::getCalleeContextFor(const DILocation *DIL, 471e8d8bef9SDimitry Andric StringRef CalleeName) { 472e8d8bef9SDimitry Andric assert(DIL && "Expect non-null location"); 473e8d8bef9SDimitry Andric 474e8d8bef9SDimitry Andric ContextTrieNode *CallContext = getContextFor(DIL); 475e8d8bef9SDimitry Andric if (!CallContext) 476e8d8bef9SDimitry Andric return nullptr; 477e8d8bef9SDimitry Andric 478d409305fSDimitry Andric // When CalleeName is empty, the child context profile with max 479d409305fSDimitry Andric // total samples will be returned. 480e8d8bef9SDimitry Andric return CallContext->getChildContext( 481d409305fSDimitry Andric FunctionSamples::getCallSiteIdentifier(DIL), CalleeName); 482e8d8bef9SDimitry Andric } 483e8d8bef9SDimitry Andric 484e8d8bef9SDimitry Andric ContextTrieNode *SampleContextTracker::getContextFor(const DILocation *DIL) { 485e8d8bef9SDimitry Andric assert(DIL && "Expect non-null location"); 486e8d8bef9SDimitry Andric SmallVector<std::pair<LineLocation, StringRef>, 10> S; 487e8d8bef9SDimitry Andric 488e8d8bef9SDimitry Andric // Use C++ linkage name if possible. 489e8d8bef9SDimitry Andric const DILocation *PrevDIL = DIL; 490e8d8bef9SDimitry Andric for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) { 491e8d8bef9SDimitry Andric StringRef Name = PrevDIL->getScope()->getSubprogram()->getLinkageName(); 492e8d8bef9SDimitry Andric if (Name.empty()) 493e8d8bef9SDimitry Andric Name = PrevDIL->getScope()->getSubprogram()->getName(); 494e8d8bef9SDimitry Andric S.push_back( 495fe6060f1SDimitry Andric std::make_pair(FunctionSamples::getCallSiteIdentifier(DIL), Name)); 496e8d8bef9SDimitry Andric PrevDIL = DIL; 497e8d8bef9SDimitry Andric } 498e8d8bef9SDimitry Andric 499e8d8bef9SDimitry Andric // Push root node, note that root node like main may only 500e8d8bef9SDimitry Andric // a name, but not linkage name. 501e8d8bef9SDimitry Andric StringRef RootName = PrevDIL->getScope()->getSubprogram()->getLinkageName(); 502e8d8bef9SDimitry Andric if (RootName.empty()) 503e8d8bef9SDimitry Andric RootName = PrevDIL->getScope()->getSubprogram()->getName(); 504e8d8bef9SDimitry Andric S.push_back(std::make_pair(LineLocation(0, 0), RootName)); 505e8d8bef9SDimitry Andric 506349cc55cSDimitry Andric // Convert real function names to MD5 names, if the input profile is 507349cc55cSDimitry Andric // MD5-based. 508349cc55cSDimitry Andric std::list<std::string> MD5Names; 509349cc55cSDimitry Andric if (FunctionSamples::UseMD5) { 510349cc55cSDimitry Andric for (auto &Location : S) { 511349cc55cSDimitry Andric MD5Names.emplace_back(); 512349cc55cSDimitry Andric getRepInFormat(Location.second, FunctionSamples::UseMD5, MD5Names.back()); 513349cc55cSDimitry Andric Location.second = MD5Names.back(); 514349cc55cSDimitry Andric } 515349cc55cSDimitry Andric } 516349cc55cSDimitry Andric 517e8d8bef9SDimitry Andric ContextTrieNode *ContextNode = &RootContext; 518e8d8bef9SDimitry Andric int I = S.size(); 519e8d8bef9SDimitry Andric while (--I >= 0 && ContextNode) { 520e8d8bef9SDimitry Andric LineLocation &CallSite = S[I].first; 521349cc55cSDimitry Andric StringRef CalleeName = S[I].second; 522e8d8bef9SDimitry Andric ContextNode = ContextNode->getChildContext(CallSite, CalleeName); 523e8d8bef9SDimitry Andric } 524e8d8bef9SDimitry Andric 525e8d8bef9SDimitry Andric if (I < 0) 526e8d8bef9SDimitry Andric return ContextNode; 527e8d8bef9SDimitry Andric 528e8d8bef9SDimitry Andric return nullptr; 529e8d8bef9SDimitry Andric } 530e8d8bef9SDimitry Andric 531e8d8bef9SDimitry Andric ContextTrieNode * 532e8d8bef9SDimitry Andric SampleContextTracker::getOrCreateContextPath(const SampleContext &Context, 533e8d8bef9SDimitry Andric bool AllowCreate) { 534e8d8bef9SDimitry Andric ContextTrieNode *ContextNode = &RootContext; 535e8d8bef9SDimitry Andric LineLocation CallSiteLoc(0, 0); 536e8d8bef9SDimitry Andric 537349cc55cSDimitry Andric for (auto &Callsite : Context.getContextFrames()) { 538e8d8bef9SDimitry Andric // Create child node at parent line/disc location 539e8d8bef9SDimitry Andric if (AllowCreate) { 540e8d8bef9SDimitry Andric ContextNode = 541349cc55cSDimitry Andric ContextNode->getOrCreateChildContext(CallSiteLoc, Callsite.FuncName); 542e8d8bef9SDimitry Andric } else { 543349cc55cSDimitry Andric ContextNode = 544349cc55cSDimitry Andric ContextNode->getChildContext(CallSiteLoc, Callsite.FuncName); 545e8d8bef9SDimitry Andric } 546349cc55cSDimitry Andric CallSiteLoc = Callsite.Location; 547e8d8bef9SDimitry Andric } 548e8d8bef9SDimitry Andric 549e8d8bef9SDimitry Andric assert((!AllowCreate || ContextNode) && 550e8d8bef9SDimitry Andric "Node must exist if creation is allowed"); 551e8d8bef9SDimitry Andric return ContextNode; 552e8d8bef9SDimitry Andric } 553e8d8bef9SDimitry Andric 554e8d8bef9SDimitry Andric ContextTrieNode *SampleContextTracker::getTopLevelContextNode(StringRef FName) { 555fe6060f1SDimitry Andric assert(!FName.empty() && "Top level node query must provide valid name"); 556e8d8bef9SDimitry Andric return RootContext.getChildContext(LineLocation(0, 0), FName); 557e8d8bef9SDimitry Andric } 558e8d8bef9SDimitry Andric 559e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::addTopLevelContextNode(StringRef FName) { 560e8d8bef9SDimitry Andric assert(!getTopLevelContextNode(FName) && "Node to add must not exist"); 561e8d8bef9SDimitry Andric return *RootContext.getOrCreateChildContext(LineLocation(0, 0), FName); 562e8d8bef9SDimitry Andric } 563e8d8bef9SDimitry Andric 564e8d8bef9SDimitry Andric void SampleContextTracker::mergeContextNode(ContextTrieNode &FromNode, 565*81ad6265SDimitry Andric ContextTrieNode &ToNode) { 566e8d8bef9SDimitry Andric FunctionSamples *FromSamples = FromNode.getFunctionSamples(); 567e8d8bef9SDimitry Andric FunctionSamples *ToSamples = ToNode.getFunctionSamples(); 568e8d8bef9SDimitry Andric if (FromSamples && ToSamples) { 569e8d8bef9SDimitry Andric // Merge/duplicate FromSamples into ToSamples 570e8d8bef9SDimitry Andric ToSamples->merge(*FromSamples); 571e8d8bef9SDimitry Andric ToSamples->getContext().setState(SyntheticContext); 572e8d8bef9SDimitry Andric FromSamples->getContext().setState(MergedContext); 573349cc55cSDimitry Andric if (FromSamples->getContext().hasAttribute(ContextShouldBeInlined)) 574349cc55cSDimitry Andric ToSamples->getContext().setAttribute(ContextShouldBeInlined); 575e8d8bef9SDimitry Andric } else if (FromSamples) { 576e8d8bef9SDimitry Andric // Transfer FromSamples from FromNode to ToNode 577e8d8bef9SDimitry Andric ToNode.setFunctionSamples(FromSamples); 578*81ad6265SDimitry Andric setContextNode(FromSamples, &ToNode); 579e8d8bef9SDimitry Andric FromSamples->getContext().setState(SyntheticContext); 580e8d8bef9SDimitry Andric } 581e8d8bef9SDimitry Andric } 582e8d8bef9SDimitry Andric 583e8d8bef9SDimitry Andric ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree( 584*81ad6265SDimitry Andric ContextTrieNode &FromNode, ContextTrieNode &ToNodeParent) { 585e8d8bef9SDimitry Andric 586e8d8bef9SDimitry Andric // Ignore call site location if destination is top level under root 587e8d8bef9SDimitry Andric LineLocation NewCallSiteLoc = LineLocation(0, 0); 588e8d8bef9SDimitry Andric LineLocation OldCallSiteLoc = FromNode.getCallSiteLoc(); 589e8d8bef9SDimitry Andric ContextTrieNode &FromNodeParent = *FromNode.getParentContext(); 590e8d8bef9SDimitry Andric ContextTrieNode *ToNode = nullptr; 591e8d8bef9SDimitry Andric bool MoveToRoot = (&ToNodeParent == &RootContext); 592e8d8bef9SDimitry Andric if (!MoveToRoot) { 593e8d8bef9SDimitry Andric NewCallSiteLoc = OldCallSiteLoc; 594e8d8bef9SDimitry Andric } 595e8d8bef9SDimitry Andric 596e8d8bef9SDimitry Andric // Locate destination node, create/move if not existing 597e8d8bef9SDimitry Andric ToNode = ToNodeParent.getChildContext(NewCallSiteLoc, FromNode.getFuncName()); 598e8d8bef9SDimitry Andric if (!ToNode) { 599e8d8bef9SDimitry Andric // Do not delete node to move from its parent here because 600e8d8bef9SDimitry Andric // caller is iterating over children of that parent node. 601*81ad6265SDimitry Andric ToNode = 602*81ad6265SDimitry Andric &moveContextSamples(ToNodeParent, NewCallSiteLoc, std::move(FromNode)); 603*81ad6265SDimitry Andric LLVM_DEBUG({ 604*81ad6265SDimitry Andric dbgs() << " Context promoted and merged to: " << getContextString(ToNode) 605*81ad6265SDimitry Andric << "\n"; 606*81ad6265SDimitry Andric }); 607e8d8bef9SDimitry Andric } else { 608e8d8bef9SDimitry Andric // Destination node exists, merge samples for the context tree 609*81ad6265SDimitry Andric mergeContextNode(FromNode, *ToNode); 610fe6060f1SDimitry Andric LLVM_DEBUG({ 611fe6060f1SDimitry Andric if (ToNode->getFunctionSamples()) 612fe6060f1SDimitry Andric dbgs() << " Context promoted and merged to: " 613*81ad6265SDimitry Andric << getContextString(ToNode) << "\n"; 614fe6060f1SDimitry Andric }); 615e8d8bef9SDimitry Andric 616e8d8bef9SDimitry Andric // Recursively promote and merge children 617e8d8bef9SDimitry Andric for (auto &It : FromNode.getAllChildContext()) { 618e8d8bef9SDimitry Andric ContextTrieNode &FromChildNode = It.second; 619*81ad6265SDimitry Andric promoteMergeContextSamplesTree(FromChildNode, *ToNode); 620e8d8bef9SDimitry Andric } 621e8d8bef9SDimitry Andric 622e8d8bef9SDimitry Andric // Remove children once they're all merged 623e8d8bef9SDimitry Andric FromNode.getAllChildContext().clear(); 624e8d8bef9SDimitry Andric } 625e8d8bef9SDimitry Andric 626e8d8bef9SDimitry Andric // For root of subtree, remove itself from old parent too 627e8d8bef9SDimitry Andric if (MoveToRoot) 628e8d8bef9SDimitry Andric FromNodeParent.removeChildContext(OldCallSiteLoc, ToNode->getFuncName()); 629e8d8bef9SDimitry Andric 630e8d8bef9SDimitry Andric return *ToNode; 631e8d8bef9SDimitry Andric } 632*81ad6265SDimitry Andric 633*81ad6265SDimitry Andric void SampleContextTracker::createContextLessProfileMap( 634*81ad6265SDimitry Andric SampleProfileMap &ContextLessProfiles) { 635*81ad6265SDimitry Andric for (auto *Node : *this) { 636*81ad6265SDimitry Andric FunctionSamples *FProfile = Node->getFunctionSamples(); 637*81ad6265SDimitry Andric // Profile's context can be empty, use ContextNode's func name. 638*81ad6265SDimitry Andric if (FProfile) 639*81ad6265SDimitry Andric ContextLessProfiles[Node->getFuncName()].merge(*FProfile); 640*81ad6265SDimitry Andric } 641*81ad6265SDimitry Andric } 642e8d8bef9SDimitry Andric } // namespace llvm 643