16b989a17SWenlei He //===- SampleContextTracker.cpp - Context-sensitive Profile Tracker -------===//
26b989a17SWenlei He //
36b989a17SWenlei He // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
46b989a17SWenlei He // See https://llvm.org/LICENSE.txt for license information.
56b989a17SWenlei He // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66b989a17SWenlei He //
76b989a17SWenlei He //===----------------------------------------------------------------------===//
86b989a17SWenlei He //
96b989a17SWenlei He // This file implements the SampleContextTracker used by CSSPGO.
106b989a17SWenlei He //
116b989a17SWenlei He //===----------------------------------------------------------------------===//
126b989a17SWenlei He
136b989a17SWenlei He #include "llvm/Transforms/IPO/SampleContextTracker.h"
146b989a17SWenlei He #include "llvm/ADT/StringMap.h"
156b989a17SWenlei He #include "llvm/ADT/StringRef.h"
166b989a17SWenlei He #include "llvm/IR/DebugInfoMetadata.h"
1701be9be2Sserge-sans-paille #include "llvm/IR/InstrTypes.h"
1801be9be2Sserge-sans-paille #include "llvm/IR/Instruction.h"
196b989a17SWenlei He #include "llvm/ProfileData/SampleProf.h"
206b989a17SWenlei He #include <map>
216b989a17SWenlei He #include <queue>
226b989a17SWenlei He #include <vector>
236b989a17SWenlei He
246b989a17SWenlei He using namespace llvm;
256b989a17SWenlei He using namespace sampleprof;
266b989a17SWenlei He
276b989a17SWenlei He #define DEBUG_TYPE "sample-context-tracker"
286b989a17SWenlei He
296b989a17SWenlei He namespace llvm {
306b989a17SWenlei He
getChildContext(const LineLocation & CallSite,StringRef CalleeName)316b989a17SWenlei He ContextTrieNode *ContextTrieNode::getChildContext(const LineLocation &CallSite,
326b989a17SWenlei He StringRef CalleeName) {
336b989a17SWenlei He if (CalleeName.empty())
346bae5973SWenlei He return getHottestChildContext(CallSite);
356b989a17SWenlei He
365740bb80SHongtao Yu uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite);
376b989a17SWenlei He auto It = AllChildContext.find(Hash);
386b989a17SWenlei He if (It != AllChildContext.end())
396b989a17SWenlei He return &It->second;
406b989a17SWenlei He return nullptr;
416b989a17SWenlei He }
426b989a17SWenlei He
436b989a17SWenlei He ContextTrieNode *
getHottestChildContext(const LineLocation & CallSite)446bae5973SWenlei He ContextTrieNode::getHottestChildContext(const LineLocation &CallSite) {
456b989a17SWenlei He // CSFDO-TODO: This could be slow, change AllChildContext so we can
466b989a17SWenlei He // do point look up for child node by call site alone.
476bae5973SWenlei He // Retrieve the child node with max count for indirect call
486b989a17SWenlei He ContextTrieNode *ChildNodeRet = nullptr;
496bae5973SWenlei He uint64_t MaxCalleeSamples = 0;
506b989a17SWenlei He for (auto &It : AllChildContext) {
516b989a17SWenlei He ContextTrieNode &ChildNode = It.second;
526bae5973SWenlei He if (ChildNode.CallSiteLoc != CallSite)
536bae5973SWenlei He continue;
546bae5973SWenlei He FunctionSamples *Samples = ChildNode.getFunctionSamples();
556bae5973SWenlei He if (!Samples)
566bae5973SWenlei He continue;
576bae5973SWenlei He if (Samples->getTotalSamples() > MaxCalleeSamples) {
586b989a17SWenlei He ChildNodeRet = &ChildNode;
596bae5973SWenlei He MaxCalleeSamples = Samples->getTotalSamples();
606b989a17SWenlei He }
616b989a17SWenlei He }
626b989a17SWenlei He
636b989a17SWenlei He return ChildNodeRet;
646b989a17SWenlei He }
656b989a17SWenlei He
667e86b13cSwlei ContextTrieNode &
moveContextSamples(ContextTrieNode & ToNodeParent,const LineLocation & CallSite,ContextTrieNode && NodeToMove)677e86b13cSwlei SampleContextTracker::moveContextSamples(ContextTrieNode &ToNodeParent,
687e86b13cSwlei const LineLocation &CallSite,
697e86b13cSwlei ContextTrieNode &&NodeToMove) {
705740bb80SHongtao Yu uint64_t Hash =
715740bb80SHongtao Yu FunctionSamples::getCallSiteHash(NodeToMove.getFuncName(), CallSite);
727e86b13cSwlei std::map<uint64_t, ContextTrieNode> &AllChildContext =
737e86b13cSwlei ToNodeParent.getAllChildContext();
746b989a17SWenlei He assert(!AllChildContext.count(Hash) && "Node to remove must exist");
756b989a17SWenlei He AllChildContext[Hash] = NodeToMove;
766b989a17SWenlei He ContextTrieNode &NewNode = AllChildContext[Hash];
777e86b13cSwlei NewNode.setCallSiteLoc(CallSite);
786b989a17SWenlei He
796b989a17SWenlei He // Walk through nodes in the moved the subtree, and update
806b989a17SWenlei He // FunctionSamples' context as for the context promotion.
816b989a17SWenlei He // We also need to set new parant link for all children.
826b989a17SWenlei He std::queue<ContextTrieNode *> NodeToUpdate;
837e86b13cSwlei NewNode.setParentContext(&ToNodeParent);
846b989a17SWenlei He NodeToUpdate.push(&NewNode);
856b989a17SWenlei He
866b989a17SWenlei He while (!NodeToUpdate.empty()) {
876b989a17SWenlei He ContextTrieNode *Node = NodeToUpdate.front();
886b989a17SWenlei He NodeToUpdate.pop();
896b989a17SWenlei He FunctionSamples *FSamples = Node->getFunctionSamples();
906b989a17SWenlei He
916b989a17SWenlei He if (FSamples) {
927e86b13cSwlei setContextNode(FSamples, Node);
936b989a17SWenlei He FSamples->getContext().setState(SyntheticContext);
946b989a17SWenlei He }
956b989a17SWenlei He
966b989a17SWenlei He for (auto &It : Node->getAllChildContext()) {
976b989a17SWenlei He ContextTrieNode *ChildNode = &It.second;
986b989a17SWenlei He ChildNode->setParentContext(Node);
996b989a17SWenlei He NodeToUpdate.push(ChildNode);
1006b989a17SWenlei He }
1016b989a17SWenlei He }
1026b989a17SWenlei He
1036b989a17SWenlei He return NewNode;
1046b989a17SWenlei He }
1056b989a17SWenlei He
removeChildContext(const LineLocation & CallSite,StringRef CalleeName)1066b989a17SWenlei He void ContextTrieNode::removeChildContext(const LineLocation &CallSite,
1076b989a17SWenlei He StringRef CalleeName) {
1085740bb80SHongtao Yu uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite);
1096b989a17SWenlei He // Note this essentially calls dtor and destroys that child context
1106b989a17SWenlei He AllChildContext.erase(Hash);
1116b989a17SWenlei He }
1126b989a17SWenlei He
getAllChildContext()113042cefd2SHongtao Yu std::map<uint64_t, ContextTrieNode> &ContextTrieNode::getAllChildContext() {
1146b989a17SWenlei He return AllChildContext;
1156b989a17SWenlei He }
1166b989a17SWenlei He
getFuncName() const1173d1200b9SKazu Hirata StringRef ContextTrieNode::getFuncName() const { return FuncName; }
1186b989a17SWenlei He
getFunctionSamples() const1196b989a17SWenlei He FunctionSamples *ContextTrieNode::getFunctionSamples() const {
1206b989a17SWenlei He return FuncSamples;
1216b989a17SWenlei He }
1226b989a17SWenlei He
setFunctionSamples(FunctionSamples * FSamples)1236b989a17SWenlei He void ContextTrieNode::setFunctionSamples(FunctionSamples *FSamples) {
1246b989a17SWenlei He FuncSamples = FSamples;
1256b989a17SWenlei He }
1266b989a17SWenlei He
getFunctionSize() const127a6f15e9aSWenlei He Optional<uint32_t> ContextTrieNode::getFunctionSize() const { return FuncSize; }
128eca03d27SWenlei He
addFunctionSize(uint32_t FSize)129a6f15e9aSWenlei He void ContextTrieNode::addFunctionSize(uint32_t FSize) {
130a7938c74SKazu Hirata if (!FuncSize)
131a6f15e9aSWenlei He FuncSize = 0;
132a6f15e9aSWenlei He
133*611ffcf4SKazu Hirata FuncSize = FuncSize.value() + FSize;
134a6f15e9aSWenlei He }
135eca03d27SWenlei He
getCallSiteLoc() const1366b989a17SWenlei He LineLocation ContextTrieNode::getCallSiteLoc() const { return CallSiteLoc; }
1376b989a17SWenlei He
getParentContext() const1386b989a17SWenlei He ContextTrieNode *ContextTrieNode::getParentContext() const {
1396b989a17SWenlei He return ParentContext;
1406b989a17SWenlei He }
1416b989a17SWenlei He
setParentContext(ContextTrieNode * Parent)1426b989a17SWenlei He void ContextTrieNode::setParentContext(ContextTrieNode *Parent) {
1436b989a17SWenlei He ParentContext = Parent;
1446b989a17SWenlei He }
1456b989a17SWenlei He
setCallSiteLoc(const LineLocation & Loc)1467e86b13cSwlei void ContextTrieNode::setCallSiteLoc(const LineLocation &Loc) {
1477e86b13cSwlei CallSiteLoc = Loc;
1487e86b13cSwlei }
1497e86b13cSwlei
dumpNode()150eca03d27SWenlei He void ContextTrieNode::dumpNode() {
1516b989a17SWenlei He dbgs() << "Node: " << FuncName << "\n"
1526b989a17SWenlei He << " Callsite: " << CallSiteLoc << "\n"
153eca03d27SWenlei He << " Size: " << FuncSize << "\n"
1546b989a17SWenlei He << " Children:\n";
1556b989a17SWenlei He
1566b989a17SWenlei He for (auto &It : AllChildContext) {
1576b989a17SWenlei He dbgs() << " Node: " << It.second.getFuncName() << "\n";
1586b989a17SWenlei He }
1596b989a17SWenlei He }
1606b989a17SWenlei He
dumpTree()161eca03d27SWenlei He void ContextTrieNode::dumpTree() {
162eca03d27SWenlei He dbgs() << "Context Profile Tree:\n";
163eca03d27SWenlei He std::queue<ContextTrieNode *> NodeQueue;
164eca03d27SWenlei He NodeQueue.push(this);
165eca03d27SWenlei He
166eca03d27SWenlei He while (!NodeQueue.empty()) {
167eca03d27SWenlei He ContextTrieNode *Node = NodeQueue.front();
168eca03d27SWenlei He NodeQueue.pop();
169eca03d27SWenlei He Node->dumpNode();
170eca03d27SWenlei He
171eca03d27SWenlei He for (auto &It : Node->getAllChildContext()) {
172eca03d27SWenlei He ContextTrieNode *ChildNode = &It.second;
173eca03d27SWenlei He NodeQueue.push(ChildNode);
174eca03d27SWenlei He }
175eca03d27SWenlei He }
176eca03d27SWenlei He }
177eca03d27SWenlei He
getOrCreateChildContext(const LineLocation & CallSite,StringRef CalleeName,bool AllowCreate)1786b989a17SWenlei He ContextTrieNode *ContextTrieNode::getOrCreateChildContext(
1796b989a17SWenlei He const LineLocation &CallSite, StringRef CalleeName, bool AllowCreate) {
1805740bb80SHongtao Yu uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite);
1816b989a17SWenlei He auto It = AllChildContext.find(Hash);
1826b989a17SWenlei He if (It != AllChildContext.end()) {
1836b989a17SWenlei He assert(It->second.getFuncName() == CalleeName &&
1846b989a17SWenlei He "Hash collision for child context node");
1856b989a17SWenlei He return &It->second;
1866b989a17SWenlei He }
1876b989a17SWenlei He
1886b989a17SWenlei He if (!AllowCreate)
1896b989a17SWenlei He return nullptr;
1906b989a17SWenlei He
191a6f15e9aSWenlei He AllChildContext[Hash] = ContextTrieNode(this, CalleeName, nullptr, CallSite);
1926b989a17SWenlei He return &AllChildContext[Hash];
1936b989a17SWenlei He }
1946b989a17SWenlei He
1956b989a17SWenlei He // Profiler tracker than manages profiles and its associated context
SampleContextTracker(SampleProfileMap & Profiles,const DenseMap<uint64_t,StringRef> * GUIDToFuncNameMap)1967ca80300SHongtao Yu SampleContextTracker::SampleContextTracker(
1977ca80300SHongtao Yu SampleProfileMap &Profiles,
1987ca80300SHongtao Yu const DenseMap<uint64_t, StringRef> *GUIDToFuncNameMap)
1997ca80300SHongtao Yu : GUIDToFuncNameMap(GUIDToFuncNameMap) {
2006b989a17SWenlei He for (auto &FuncSample : Profiles) {
2016b989a17SWenlei He FunctionSamples *FSamples = &FuncSample.second;
202b9db7036SHongtao Yu SampleContext Context = FuncSample.first;
203b9db7036SHongtao Yu LLVM_DEBUG(dbgs() << "Tracking Context for function: " << Context.toString()
204b9db7036SHongtao Yu << "\n");
2056b989a17SWenlei He ContextTrieNode *NewNode = getOrCreateContextPath(Context, true);
2066b989a17SWenlei He assert(!NewNode->getFunctionSamples() &&
2076b989a17SWenlei He "New node can't have sample profile");
2086b989a17SWenlei He NewNode->setFunctionSamples(FSamples);
2096b989a17SWenlei He }
2107e86b13cSwlei populateFuncToCtxtMap();
2117e86b13cSwlei }
2127e86b13cSwlei
populateFuncToCtxtMap()2137e86b13cSwlei void SampleContextTracker::populateFuncToCtxtMap() {
2147e86b13cSwlei for (auto *Node : *this) {
2157e86b13cSwlei FunctionSamples *FSamples = Node->getFunctionSamples();
2167e86b13cSwlei if (FSamples) {
2177e86b13cSwlei FSamples->getContext().setState(RawContext);
2187e86b13cSwlei setContextNode(FSamples, Node);
2197e86b13cSwlei FuncToCtxtProfiles[Node->getFuncName()].push_back(FSamples);
2207e86b13cSwlei }
2217e86b13cSwlei }
2226b989a17SWenlei He }
2236b989a17SWenlei He
2246b989a17SWenlei He FunctionSamples *
getCalleeContextSamplesFor(const CallBase & Inst,StringRef CalleeName)2256b989a17SWenlei He SampleContextTracker::getCalleeContextSamplesFor(const CallBase &Inst,
2266b989a17SWenlei He StringRef CalleeName) {
2276b989a17SWenlei He LLVM_DEBUG(dbgs() << "Getting callee context for instr: " << Inst << "\n");
2286b989a17SWenlei He DILocation *DIL = Inst.getDebugLoc();
2296b989a17SWenlei He if (!DIL)
2306b989a17SWenlei He return nullptr;
2316b989a17SWenlei He
232ee35784aSWei Mi CalleeName = FunctionSamples::getCanonicalFnName(CalleeName);
2337ca80300SHongtao Yu // Convert real function names to MD5 names, if the input profile is
2347ca80300SHongtao Yu // MD5-based.
2357ca80300SHongtao Yu std::string FGUID;
2367ca80300SHongtao Yu CalleeName = getRepInFormat(CalleeName, FunctionSamples::UseMD5, FGUID);
237ee35784aSWei Mi
2386bae5973SWenlei He // For indirect call, CalleeName will be empty, in which case the context
2396bae5973SWenlei He // profile for callee with largest total samples will be returned.
2406b989a17SWenlei He ContextTrieNode *CalleeContext = getCalleeContextFor(DIL, CalleeName);
2416b989a17SWenlei He if (CalleeContext) {
2426b989a17SWenlei He FunctionSamples *FSamples = CalleeContext->getFunctionSamples();
2436b989a17SWenlei He LLVM_DEBUG(if (FSamples) {
2447e86b13cSwlei dbgs() << " Callee context found: " << getContextString(CalleeContext)
245b9db7036SHongtao Yu << "\n";
2466b989a17SWenlei He });
2476b989a17SWenlei He return FSamples;
2486b989a17SWenlei He }
2496b989a17SWenlei He
2506b989a17SWenlei He return nullptr;
2516b989a17SWenlei He }
2526b989a17SWenlei He
2536bae5973SWenlei He std::vector<const FunctionSamples *>
getIndirectCalleeContextSamplesFor(const DILocation * DIL)2546bae5973SWenlei He SampleContextTracker::getIndirectCalleeContextSamplesFor(
2556bae5973SWenlei He const DILocation *DIL) {
2566bae5973SWenlei He std::vector<const FunctionSamples *> R;
2576bae5973SWenlei He if (!DIL)
2586bae5973SWenlei He return R;
2596bae5973SWenlei He
2606bae5973SWenlei He ContextTrieNode *CallerNode = getContextFor(DIL);
2616bae5973SWenlei He LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL);
2626bae5973SWenlei He for (auto &It : CallerNode->getAllChildContext()) {
2636bae5973SWenlei He ContextTrieNode &ChildNode = It.second;
2646bae5973SWenlei He if (ChildNode.getCallSiteLoc() != CallSite)
2656bae5973SWenlei He continue;
2666bae5973SWenlei He if (FunctionSamples *CalleeSamples = ChildNode.getFunctionSamples())
2676bae5973SWenlei He R.push_back(CalleeSamples);
2686bae5973SWenlei He }
2696bae5973SWenlei He
2706bae5973SWenlei He return R;
2716bae5973SWenlei He }
2726bae5973SWenlei He
2736b989a17SWenlei He FunctionSamples *
getContextSamplesFor(const DILocation * DIL)2746b989a17SWenlei He SampleContextTracker::getContextSamplesFor(const DILocation *DIL) {
2756b989a17SWenlei He assert(DIL && "Expect non-null location");
2766b989a17SWenlei He
2776b989a17SWenlei He ContextTrieNode *ContextNode = getContextFor(DIL);
2786b989a17SWenlei He if (!ContextNode)
2796b989a17SWenlei He return nullptr;
2806b989a17SWenlei He
2816b989a17SWenlei He // We may have inlined callees during pre-LTO compilation, in which case
2826b989a17SWenlei He // we need to rely on the inline stack from !dbg to mark context profile
2836b989a17SWenlei He // as inlined, instead of `MarkContextSamplesInlined` during inlining.
2846b989a17SWenlei He // Sample profile loader walks through all instructions to get profile,
2856b989a17SWenlei He // which calls this function. So once that is done, all previously inlined
2866b989a17SWenlei He // context profile should be marked properly.
2876b989a17SWenlei He FunctionSamples *Samples = ContextNode->getFunctionSamples();
2886b989a17SWenlei He if (Samples && ContextNode->getParentContext() != &RootContext)
2896b989a17SWenlei He Samples->getContext().setState(InlinedContext);
2906b989a17SWenlei He
2916b989a17SWenlei He return Samples;
2926b989a17SWenlei He }
2936b989a17SWenlei He
2946b989a17SWenlei He FunctionSamples *
getContextSamplesFor(const SampleContext & Context)2956b989a17SWenlei He SampleContextTracker::getContextSamplesFor(const SampleContext &Context) {
2966b989a17SWenlei He ContextTrieNode *Node = getContextFor(Context);
2976b989a17SWenlei He if (!Node)
2986b989a17SWenlei He return nullptr;
2996b989a17SWenlei He
3006b989a17SWenlei He return Node->getFunctionSamples();
3016b989a17SWenlei He }
3026b989a17SWenlei He
303de40f6d6SHongtao Yu SampleContextTracker::ContextSamplesTy &
getAllContextSamplesFor(const Function & Func)304de40f6d6SHongtao Yu SampleContextTracker::getAllContextSamplesFor(const Function &Func) {
305de40f6d6SHongtao Yu StringRef CanonName = FunctionSamples::getCanonicalFnName(Func);
306ca721042SHuihui Zhang return FuncToCtxtProfiles[CanonName];
307de40f6d6SHongtao Yu }
308de40f6d6SHongtao Yu
309de40f6d6SHongtao Yu SampleContextTracker::ContextSamplesTy &
getAllContextSamplesFor(StringRef Name)310de40f6d6SHongtao Yu SampleContextTracker::getAllContextSamplesFor(StringRef Name) {
311ca721042SHuihui Zhang return FuncToCtxtProfiles[Name];
312de40f6d6SHongtao Yu }
313de40f6d6SHongtao Yu
getBaseSamplesFor(const Function & Func,bool MergeContext)3146b989a17SWenlei He FunctionSamples *SampleContextTracker::getBaseSamplesFor(const Function &Func,
3156b989a17SWenlei He bool MergeContext) {
3166b989a17SWenlei He StringRef CanonName = FunctionSamples::getCanonicalFnName(Func);
3176b989a17SWenlei He return getBaseSamplesFor(CanonName, MergeContext);
3186b989a17SWenlei He }
3196b989a17SWenlei He
getBaseSamplesFor(StringRef Name,bool MergeContext)3206b989a17SWenlei He FunctionSamples *SampleContextTracker::getBaseSamplesFor(StringRef Name,
3216b989a17SWenlei He bool MergeContext) {
3226b989a17SWenlei He LLVM_DEBUG(dbgs() << "Getting base profile for function: " << Name << "\n");
3237ca80300SHongtao Yu // Convert real function names to MD5 names, if the input profile is
3247ca80300SHongtao Yu // MD5-based.
3257ca80300SHongtao Yu std::string FGUID;
3267ca80300SHongtao Yu Name = getRepInFormat(Name, FunctionSamples::UseMD5, FGUID);
3277ca80300SHongtao Yu
3286b989a17SWenlei He // Base profile is top-level node (child of root node), so try to retrieve
3296b989a17SWenlei He // existing top-level node for given function first. If it exists, it could be
3306b989a17SWenlei He // that we've merged base profile before, or there's actually context-less
3316b989a17SWenlei He // profile from the input (e.g. due to unreliable stack walking).
3326b989a17SWenlei He ContextTrieNode *Node = getTopLevelContextNode(Name);
3336b989a17SWenlei He if (MergeContext) {
3346b989a17SWenlei He LLVM_DEBUG(dbgs() << " Merging context profile into base profile: " << Name
3356b989a17SWenlei He << "\n");
3366b989a17SWenlei He
3376b989a17SWenlei He // We have profile for function under different contexts,
3386b989a17SWenlei He // create synthetic base profile and merge context profiles
3396b989a17SWenlei He // into base profile.
340ca721042SHuihui Zhang for (auto *CSamples : FuncToCtxtProfiles[Name]) {
3416b989a17SWenlei He SampleContext &Context = CSamples->getContext();
3426b989a17SWenlei He // Skip inlined context profile and also don't re-merge any context
3436b989a17SWenlei He if (Context.hasState(InlinedContext) || Context.hasState(MergedContext))
3446b989a17SWenlei He continue;
3456b989a17SWenlei He
3467e86b13cSwlei ContextTrieNode *FromNode = getContextNodeForProfile(CSamples);
347b9db7036SHongtao Yu if (FromNode == Node)
348b9db7036SHongtao Yu continue;
349b9db7036SHongtao Yu
3506b989a17SWenlei He ContextTrieNode &ToNode = promoteMergeContextSamplesTree(*FromNode);
35150dd1dbaSSimon Pilgrim assert((!Node || Node == &ToNode) && "Expect only one base profile");
3526b989a17SWenlei He Node = &ToNode;
3536b989a17SWenlei He }
3546b989a17SWenlei He }
3556b989a17SWenlei He
3566b989a17SWenlei He // Still no profile even after merge/promotion (if allowed)
3576b989a17SWenlei He if (!Node)
3586b989a17SWenlei He return nullptr;
3596b989a17SWenlei He
3606b989a17SWenlei He return Node->getFunctionSamples();
3616b989a17SWenlei He }
3626b989a17SWenlei He
markContextSamplesInlined(const FunctionSamples * InlinedSamples)3636b989a17SWenlei He void SampleContextTracker::markContextSamplesInlined(
3646b989a17SWenlei He const FunctionSamples *InlinedSamples) {
3656b989a17SWenlei He assert(InlinedSamples && "Expect non-null inlined samples");
3666b989a17SWenlei He LLVM_DEBUG(dbgs() << "Marking context profile as inlined: "
3677e86b13cSwlei << getContextString(*InlinedSamples) << "\n");
3686b989a17SWenlei He InlinedSamples->getContext().setState(InlinedContext);
3696b989a17SWenlei He }
3706b989a17SWenlei He
getRootContext()37130b02323SWenlei He ContextTrieNode &SampleContextTracker::getRootContext() { return RootContext; }
37230b02323SWenlei He
promoteMergeContextSamplesTree(const Instruction & Inst,StringRef CalleeName)3736b989a17SWenlei He void SampleContextTracker::promoteMergeContextSamplesTree(
3746b989a17SWenlei He const Instruction &Inst, StringRef CalleeName) {
3756b989a17SWenlei He LLVM_DEBUG(dbgs() << "Promoting and merging context tree for instr: \n"
3766b989a17SWenlei He << Inst << "\n");
3776b989a17SWenlei He // Get the caller context for the call instruction, we don't use callee
3786b989a17SWenlei He // name from call because there can be context from indirect calls too.
3796b989a17SWenlei He DILocation *DIL = Inst.getDebugLoc();
3806b989a17SWenlei He ContextTrieNode *CallerNode = getContextFor(DIL);
3816b989a17SWenlei He if (!CallerNode)
3826b989a17SWenlei He return;
3836b989a17SWenlei He
3846b989a17SWenlei He // Get the context that needs to be promoted
385224fee82SHongtao Yu LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL);
3866bae5973SWenlei He // For indirect call, CalleeName will be empty, in which case we need to
3876bae5973SWenlei He // promote all non-inlined child context profiles.
3886bae5973SWenlei He if (CalleeName.empty()) {
3896bae5973SWenlei He for (auto &It : CallerNode->getAllChildContext()) {
3906bae5973SWenlei He ContextTrieNode *NodeToPromo = &It.second;
3916bae5973SWenlei He if (CallSite != NodeToPromo->getCallSiteLoc())
3926bae5973SWenlei He continue;
3936bae5973SWenlei He FunctionSamples *FromSamples = NodeToPromo->getFunctionSamples();
3946bae5973SWenlei He if (FromSamples && FromSamples->getContext().hasState(InlinedContext))
3956bae5973SWenlei He continue;
3966bae5973SWenlei He promoteMergeContextSamplesTree(*NodeToPromo);
3976bae5973SWenlei He }
3986bae5973SWenlei He return;
3996bae5973SWenlei He }
4006bae5973SWenlei He
4016bae5973SWenlei He // Get the context for the given callee that needs to be promoted
4026b989a17SWenlei He ContextTrieNode *NodeToPromo =
4036b989a17SWenlei He CallerNode->getChildContext(CallSite, CalleeName);
4046b989a17SWenlei He if (!NodeToPromo)
4056b989a17SWenlei He return;
4066b989a17SWenlei He
4076b989a17SWenlei He promoteMergeContextSamplesTree(*NodeToPromo);
4086b989a17SWenlei He }
4096b989a17SWenlei He
promoteMergeContextSamplesTree(ContextTrieNode & NodeToPromo)4106b989a17SWenlei He ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree(
4116b989a17SWenlei He ContextTrieNode &NodeToPromo) {
4126b989a17SWenlei He // Promote the input node to be directly under root. This can happen
4136b989a17SWenlei He // when we decided to not inline a function under context represented
4146b989a17SWenlei He // by the input node. The promote and merge is then needed to reflect
4156b989a17SWenlei He // the context profile in the base (context-less) profile.
4166b989a17SWenlei He FunctionSamples *FromSamples = NodeToPromo.getFunctionSamples();
4176b989a17SWenlei He assert(FromSamples && "Shouldn't promote a context without profile");
418c6c124caSMikhail Goncharov (void)FromSamples; // Unused in release build.
419c6c124caSMikhail Goncharov
4206b989a17SWenlei He LLVM_DEBUG(dbgs() << " Found context tree root to promote: "
4217e86b13cSwlei << getContextString(&NodeToPromo) << "\n");
4226b989a17SWenlei He
4236bae5973SWenlei He assert(!FromSamples->getContext().hasState(InlinedContext) &&
4246bae5973SWenlei He "Shouldn't promote inlined context profile");
4257e86b13cSwlei return promoteMergeContextSamplesTree(NodeToPromo, RootContext);
4266b989a17SWenlei He }
4276b989a17SWenlei He
4287e86b13cSwlei #ifndef NDEBUG
4297e86b13cSwlei std::string
getContextString(const FunctionSamples & FSamples) const4307e86b13cSwlei SampleContextTracker::getContextString(const FunctionSamples &FSamples) const {
4317e86b13cSwlei return getContextString(getContextNodeForProfile(&FSamples));
4327e86b13cSwlei }
4337e86b13cSwlei
4347e86b13cSwlei std::string
getContextString(ContextTrieNode * Node) const4357e86b13cSwlei SampleContextTracker::getContextString(ContextTrieNode *Node) const {
4367e86b13cSwlei SampleContextFrameVector Res;
4377e86b13cSwlei if (Node == &RootContext)
4387e86b13cSwlei return std::string();
4397e86b13cSwlei Res.emplace_back(Node->getFuncName(), LineLocation(0, 0));
4407e86b13cSwlei
4417e86b13cSwlei ContextTrieNode *PreNode = Node;
4427e86b13cSwlei Node = Node->getParentContext();
4437e86b13cSwlei while (Node && Node != &RootContext) {
4447e86b13cSwlei Res.emplace_back(Node->getFuncName(), PreNode->getCallSiteLoc());
4457e86b13cSwlei PreNode = Node;
4467e86b13cSwlei Node = Node->getParentContext();
4477e86b13cSwlei }
4487e86b13cSwlei
4497e86b13cSwlei std::reverse(Res.begin(), Res.end());
4507e86b13cSwlei
4517e86b13cSwlei return SampleContext::getContextString(Res);
4527e86b13cSwlei }
4537e86b13cSwlei #endif
4547e86b13cSwlei
dump()455eca03d27SWenlei He void SampleContextTracker::dump() { RootContext.dumpTree(); }
4566b989a17SWenlei He
getFuncNameFor(ContextTrieNode * Node) const4577ca80300SHongtao Yu StringRef SampleContextTracker::getFuncNameFor(ContextTrieNode *Node) const {
4587ca80300SHongtao Yu if (!FunctionSamples::UseMD5)
4597ca80300SHongtao Yu return Node->getFuncName();
4607ca80300SHongtao Yu assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be populated first");
4617ca80300SHongtao Yu return GUIDToFuncNameMap->lookup(std::stoull(Node->getFuncName().data()));
4627ca80300SHongtao Yu }
4637ca80300SHongtao Yu
4646b989a17SWenlei He ContextTrieNode *
getContextFor(const SampleContext & Context)4656b989a17SWenlei He SampleContextTracker::getContextFor(const SampleContext &Context) {
4666b989a17SWenlei He return getOrCreateContextPath(Context, false);
4676b989a17SWenlei He }
4686b989a17SWenlei He
4696b989a17SWenlei He ContextTrieNode *
getCalleeContextFor(const DILocation * DIL,StringRef CalleeName)4706b989a17SWenlei He SampleContextTracker::getCalleeContextFor(const DILocation *DIL,
4716b989a17SWenlei He StringRef CalleeName) {
4726b989a17SWenlei He assert(DIL && "Expect non-null location");
4736b989a17SWenlei He
4746b989a17SWenlei He ContextTrieNode *CallContext = getContextFor(DIL);
4756b989a17SWenlei He if (!CallContext)
4766b989a17SWenlei He return nullptr;
4776b989a17SWenlei He
4786bae5973SWenlei He // When CalleeName is empty, the child context profile with max
4796bae5973SWenlei He // total samples will be returned.
4806b989a17SWenlei He return CallContext->getChildContext(
481224fee82SHongtao Yu FunctionSamples::getCallSiteIdentifier(DIL), CalleeName);
4826b989a17SWenlei He }
4836b989a17SWenlei He
getContextFor(const DILocation * DIL)4846b989a17SWenlei He ContextTrieNode *SampleContextTracker::getContextFor(const DILocation *DIL) {
4856b989a17SWenlei He assert(DIL && "Expect non-null location");
4866b989a17SWenlei He SmallVector<std::pair<LineLocation, StringRef>, 10> S;
4876b989a17SWenlei He
4886b989a17SWenlei He // Use C++ linkage name if possible.
4896b989a17SWenlei He const DILocation *PrevDIL = DIL;
4906b989a17SWenlei He for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) {
4916b989a17SWenlei He StringRef Name = PrevDIL->getScope()->getSubprogram()->getLinkageName();
4926b989a17SWenlei He if (Name.empty())
4936b989a17SWenlei He Name = PrevDIL->getScope()->getSubprogram()->getName();
4946b989a17SWenlei He S.push_back(
495ab5ac659SHongtao Yu std::make_pair(FunctionSamples::getCallSiteIdentifier(DIL), Name));
4966b989a17SWenlei He PrevDIL = DIL;
4976b989a17SWenlei He }
4986b989a17SWenlei He
4996b989a17SWenlei He // Push root node, note that root node like main may only
5006b989a17SWenlei He // a name, but not linkage name.
5016b989a17SWenlei He StringRef RootName = PrevDIL->getScope()->getSubprogram()->getLinkageName();
5026b989a17SWenlei He if (RootName.empty())
5036b989a17SWenlei He RootName = PrevDIL->getScope()->getSubprogram()->getName();
5046b989a17SWenlei He S.push_back(std::make_pair(LineLocation(0, 0), RootName));
5056b989a17SWenlei He
5067ca80300SHongtao Yu // Convert real function names to MD5 names, if the input profile is
5077ca80300SHongtao Yu // MD5-based.
508dde162d8SHongtao Yu std::list<std::string> MD5Names;
5097ca80300SHongtao Yu if (FunctionSamples::UseMD5) {
5107ca80300SHongtao Yu for (auto &Location : S) {
5117ca80300SHongtao Yu MD5Names.emplace_back();
5127ca80300SHongtao Yu getRepInFormat(Location.second, FunctionSamples::UseMD5, MD5Names.back());
5137ca80300SHongtao Yu Location.second = MD5Names.back();
5147ca80300SHongtao Yu }
5157ca80300SHongtao Yu }
5167ca80300SHongtao Yu
5176b989a17SWenlei He ContextTrieNode *ContextNode = &RootContext;
5186b989a17SWenlei He int I = S.size();
5196b989a17SWenlei He while (--I >= 0 && ContextNode) {
5206b989a17SWenlei He LineLocation &CallSite = S[I].first;
521dde162d8SHongtao Yu StringRef CalleeName = S[I].second;
5226b989a17SWenlei He ContextNode = ContextNode->getChildContext(CallSite, CalleeName);
5236b989a17SWenlei He }
5246b989a17SWenlei He
5256b989a17SWenlei He if (I < 0)
5266b989a17SWenlei He return ContextNode;
5276b989a17SWenlei He
5286b989a17SWenlei He return nullptr;
5296b989a17SWenlei He }
5306b989a17SWenlei He
5316b989a17SWenlei He ContextTrieNode *
getOrCreateContextPath(const SampleContext & Context,bool AllowCreate)5326b989a17SWenlei He SampleContextTracker::getOrCreateContextPath(const SampleContext &Context,
5336b989a17SWenlei He bool AllowCreate) {
5346b989a17SWenlei He ContextTrieNode *ContextNode = &RootContext;
5356b989a17SWenlei He LineLocation CallSiteLoc(0, 0);
5366b989a17SWenlei He
537b9db7036SHongtao Yu for (auto &Callsite : Context.getContextFrames()) {
5386b989a17SWenlei He // Create child node at parent line/disc location
5396b989a17SWenlei He if (AllowCreate) {
540fb29d812Swlei ContextNode =
541fb29d812Swlei ContextNode->getOrCreateChildContext(CallSiteLoc, Callsite.FuncName);
5426b989a17SWenlei He } else {
543b9db7036SHongtao Yu ContextNode =
544fb29d812Swlei ContextNode->getChildContext(CallSiteLoc, Callsite.FuncName);
5456b989a17SWenlei He }
546fb29d812Swlei CallSiteLoc = Callsite.Location;
5476b989a17SWenlei He }
5486b989a17SWenlei He
5496b989a17SWenlei He assert((!AllowCreate || ContextNode) &&
5506b989a17SWenlei He "Node must exist if creation is allowed");
5516b989a17SWenlei He return ContextNode;
5526b989a17SWenlei He }
5536b989a17SWenlei He
getTopLevelContextNode(StringRef FName)5546b989a17SWenlei He ContextTrieNode *SampleContextTracker::getTopLevelContextNode(StringRef FName) {
55530b02323SWenlei He assert(!FName.empty() && "Top level node query must provide valid name");
5566b989a17SWenlei He return RootContext.getChildContext(LineLocation(0, 0), FName);
5576b989a17SWenlei He }
5586b989a17SWenlei He
addTopLevelContextNode(StringRef FName)5596b989a17SWenlei He ContextTrieNode &SampleContextTracker::addTopLevelContextNode(StringRef FName) {
5606b989a17SWenlei He assert(!getTopLevelContextNode(FName) && "Node to add must not exist");
5616b989a17SWenlei He return *RootContext.getOrCreateChildContext(LineLocation(0, 0), FName);
5626b989a17SWenlei He }
5636b989a17SWenlei He
mergeContextNode(ContextTrieNode & FromNode,ContextTrieNode & ToNode)5646b989a17SWenlei He void SampleContextTracker::mergeContextNode(ContextTrieNode &FromNode,
5657e86b13cSwlei ContextTrieNode &ToNode) {
5666b989a17SWenlei He FunctionSamples *FromSamples = FromNode.getFunctionSamples();
5676b989a17SWenlei He FunctionSamples *ToSamples = ToNode.getFunctionSamples();
5686b989a17SWenlei He if (FromSamples && ToSamples) {
5696b989a17SWenlei He // Merge/duplicate FromSamples into ToSamples
5706b989a17SWenlei He ToSamples->merge(*FromSamples);
5716b989a17SWenlei He ToSamples->getContext().setState(SyntheticContext);
5726b989a17SWenlei He FromSamples->getContext().setState(MergedContext);
573054487c5SWenlei He if (FromSamples->getContext().hasAttribute(ContextShouldBeInlined))
574054487c5SWenlei He ToSamples->getContext().setAttribute(ContextShouldBeInlined);
5756b989a17SWenlei He } else if (FromSamples) {
5766b989a17SWenlei He // Transfer FromSamples from FromNode to ToNode
5776b989a17SWenlei He ToNode.setFunctionSamples(FromSamples);
5787e86b13cSwlei setContextNode(FromSamples, &ToNode);
5796b989a17SWenlei He FromSamples->getContext().setState(SyntheticContext);
5806b989a17SWenlei He }
5816b989a17SWenlei He }
5826b989a17SWenlei He
promoteMergeContextSamplesTree(ContextTrieNode & FromNode,ContextTrieNode & ToNodeParent)5836b989a17SWenlei He ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree(
5847e86b13cSwlei ContextTrieNode &FromNode, ContextTrieNode &ToNodeParent) {
5856b989a17SWenlei He
5866b989a17SWenlei He // Ignore call site location if destination is top level under root
5876b989a17SWenlei He LineLocation NewCallSiteLoc = LineLocation(0, 0);
5886b989a17SWenlei He LineLocation OldCallSiteLoc = FromNode.getCallSiteLoc();
5896b989a17SWenlei He ContextTrieNode &FromNodeParent = *FromNode.getParentContext();
5906b989a17SWenlei He ContextTrieNode *ToNode = nullptr;
5916b989a17SWenlei He bool MoveToRoot = (&ToNodeParent == &RootContext);
5926b989a17SWenlei He if (!MoveToRoot) {
5936b989a17SWenlei He NewCallSiteLoc = OldCallSiteLoc;
5946b989a17SWenlei He }
5956b989a17SWenlei He
5966b989a17SWenlei He // Locate destination node, create/move if not existing
5976b989a17SWenlei He ToNode = ToNodeParent.getChildContext(NewCallSiteLoc, FromNode.getFuncName());
5986b989a17SWenlei He if (!ToNode) {
5996b989a17SWenlei He // Do not delete node to move from its parent here because
6006b989a17SWenlei He // caller is iterating over children of that parent node.
6017e86b13cSwlei ToNode =
6027e86b13cSwlei &moveContextSamples(ToNodeParent, NewCallSiteLoc, std::move(FromNode));
6037e86b13cSwlei LLVM_DEBUG({
6047e86b13cSwlei dbgs() << " Context promoted and merged to: " << getContextString(ToNode)
6057e86b13cSwlei << "\n";
6067e86b13cSwlei });
6076b989a17SWenlei He } else {
6086b989a17SWenlei He // Destination node exists, merge samples for the context tree
6097e86b13cSwlei mergeContextNode(FromNode, *ToNode);
61012ac0403SHongtao Yu LLVM_DEBUG({
61112ac0403SHongtao Yu if (ToNode->getFunctionSamples())
61212ac0403SHongtao Yu dbgs() << " Context promoted and merged to: "
6137e86b13cSwlei << getContextString(ToNode) << "\n";
61412ac0403SHongtao Yu });
6156b989a17SWenlei He
6166b989a17SWenlei He // Recursively promote and merge children
6176b989a17SWenlei He for (auto &It : FromNode.getAllChildContext()) {
6186b989a17SWenlei He ContextTrieNode &FromChildNode = It.second;
6197e86b13cSwlei promoteMergeContextSamplesTree(FromChildNode, *ToNode);
6206b989a17SWenlei He }
6216b989a17SWenlei He
6226b989a17SWenlei He // Remove children once they're all merged
6236b989a17SWenlei He FromNode.getAllChildContext().clear();
6246b989a17SWenlei He }
6256b989a17SWenlei He
6266b989a17SWenlei He // For root of subtree, remove itself from old parent too
6276b989a17SWenlei He if (MoveToRoot)
6286b989a17SWenlei He FromNodeParent.removeChildContext(OldCallSiteLoc, ToNode->getFuncName());
6296b989a17SWenlei He
6306b989a17SWenlei He return *ToNode;
6316b989a17SWenlei He }
632aa58b7b1Swlei
createContextLessProfileMap(SampleProfileMap & ContextLessProfiles)633aa58b7b1Swlei void SampleContextTracker::createContextLessProfileMap(
634aa58b7b1Swlei SampleProfileMap &ContextLessProfiles) {
6357e86b13cSwlei for (auto *Node : *this) {
636aa58b7b1Swlei FunctionSamples *FProfile = Node->getFunctionSamples();
637aa58b7b1Swlei // Profile's context can be empty, use ContextNode's func name.
6387e86b13cSwlei if (FProfile)
639aa58b7b1Swlei ContextLessProfiles[Node->getFuncName()].merge(*FProfile);
640aa58b7b1Swlei }
641aa58b7b1Swlei }
6426b989a17SWenlei He } // namespace llvm
643