1b60736ecSDimitry Andric //===- SampleContextTracker.cpp - Context-sensitive Profile Tracker -------===//
2b60736ecSDimitry Andric //
3b60736ecSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b60736ecSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5b60736ecSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b60736ecSDimitry Andric //
7b60736ecSDimitry Andric //===----------------------------------------------------------------------===//
8b60736ecSDimitry Andric //
9b60736ecSDimitry Andric // This file implements the SampleContextTracker used by CSSPGO.
10b60736ecSDimitry Andric //
11b60736ecSDimitry Andric //===----------------------------------------------------------------------===//
12b60736ecSDimitry Andric
13b60736ecSDimitry Andric #include "llvm/Transforms/IPO/SampleContextTracker.h"
14b60736ecSDimitry Andric #include "llvm/ADT/StringRef.h"
15b60736ecSDimitry Andric #include "llvm/IR/DebugInfoMetadata.h"
16145449b1SDimitry Andric #include "llvm/IR/InstrTypes.h"
17145449b1SDimitry Andric #include "llvm/IR/Instruction.h"
18b60736ecSDimitry Andric #include "llvm/ProfileData/SampleProf.h"
19b60736ecSDimitry Andric #include <map>
20b60736ecSDimitry Andric #include <queue>
21b60736ecSDimitry Andric #include <vector>
22b60736ecSDimitry Andric
23b60736ecSDimitry Andric using namespace llvm;
24b60736ecSDimitry Andric using namespace sampleprof;
25b60736ecSDimitry Andric
26b60736ecSDimitry Andric #define DEBUG_TYPE "sample-context-tracker"
27b60736ecSDimitry Andric
28b60736ecSDimitry Andric namespace llvm {
29b60736ecSDimitry Andric
getChildContext(const LineLocation & CallSite,FunctionId CalleeName)30b60736ecSDimitry Andric ContextTrieNode *ContextTrieNode::getChildContext(const LineLocation &CallSite,
31b1c73532SDimitry Andric FunctionId CalleeName) {
32b60736ecSDimitry Andric if (CalleeName.empty())
33344a3780SDimitry Andric return getHottestChildContext(CallSite);
34b60736ecSDimitry Andric
3577fc4c14SDimitry Andric uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite);
36b60736ecSDimitry Andric auto It = AllChildContext.find(Hash);
37b60736ecSDimitry Andric if (It != AllChildContext.end())
38b60736ecSDimitry Andric return &It->second;
39b60736ecSDimitry Andric return nullptr;
40b60736ecSDimitry Andric }
41b60736ecSDimitry Andric
42b60736ecSDimitry Andric ContextTrieNode *
getHottestChildContext(const LineLocation & CallSite)43344a3780SDimitry Andric ContextTrieNode::getHottestChildContext(const LineLocation &CallSite) {
44b60736ecSDimitry Andric // CSFDO-TODO: This could be slow, change AllChildContext so we can
45b60736ecSDimitry Andric // do point look up for child node by call site alone.
46344a3780SDimitry Andric // Retrieve the child node with max count for indirect call
47b60736ecSDimitry Andric ContextTrieNode *ChildNodeRet = nullptr;
48344a3780SDimitry Andric uint64_t MaxCalleeSamples = 0;
49b60736ecSDimitry Andric for (auto &It : AllChildContext) {
50b60736ecSDimitry Andric ContextTrieNode &ChildNode = It.second;
51344a3780SDimitry Andric if (ChildNode.CallSiteLoc != CallSite)
52344a3780SDimitry Andric continue;
53344a3780SDimitry Andric FunctionSamples *Samples = ChildNode.getFunctionSamples();
54344a3780SDimitry Andric if (!Samples)
55344a3780SDimitry Andric continue;
56344a3780SDimitry Andric if (Samples->getTotalSamples() > MaxCalleeSamples) {
57b60736ecSDimitry Andric ChildNodeRet = &ChildNode;
58344a3780SDimitry Andric MaxCalleeSamples = Samples->getTotalSamples();
59b60736ecSDimitry Andric }
60b60736ecSDimitry Andric }
61b60736ecSDimitry Andric
62b60736ecSDimitry Andric return ChildNodeRet;
63b60736ecSDimitry Andric }
64b60736ecSDimitry Andric
65145449b1SDimitry Andric ContextTrieNode &
moveContextSamples(ContextTrieNode & ToNodeParent,const LineLocation & CallSite,ContextTrieNode && NodeToMove)66145449b1SDimitry Andric SampleContextTracker::moveContextSamples(ContextTrieNode &ToNodeParent,
67145449b1SDimitry Andric const LineLocation &CallSite,
68145449b1SDimitry Andric ContextTrieNode &&NodeToMove) {
6977fc4c14SDimitry Andric uint64_t Hash =
7077fc4c14SDimitry Andric FunctionSamples::getCallSiteHash(NodeToMove.getFuncName(), CallSite);
71145449b1SDimitry Andric std::map<uint64_t, ContextTrieNode> &AllChildContext =
72145449b1SDimitry Andric ToNodeParent.getAllChildContext();
73b60736ecSDimitry Andric assert(!AllChildContext.count(Hash) && "Node to remove must exist");
74b60736ecSDimitry Andric AllChildContext[Hash] = NodeToMove;
75b60736ecSDimitry Andric ContextTrieNode &NewNode = AllChildContext[Hash];
76145449b1SDimitry Andric NewNode.setCallSiteLoc(CallSite);
77b60736ecSDimitry Andric
78b60736ecSDimitry Andric // Walk through nodes in the moved the subtree, and update
79b60736ecSDimitry Andric // FunctionSamples' context as for the context promotion.
80b60736ecSDimitry Andric // We also need to set new parant link for all children.
81b60736ecSDimitry Andric std::queue<ContextTrieNode *> NodeToUpdate;
82145449b1SDimitry Andric NewNode.setParentContext(&ToNodeParent);
83b60736ecSDimitry Andric NodeToUpdate.push(&NewNode);
84b60736ecSDimitry Andric
85b60736ecSDimitry Andric while (!NodeToUpdate.empty()) {
86b60736ecSDimitry Andric ContextTrieNode *Node = NodeToUpdate.front();
87b60736ecSDimitry Andric NodeToUpdate.pop();
88b60736ecSDimitry Andric FunctionSamples *FSamples = Node->getFunctionSamples();
89b60736ecSDimitry Andric
90b60736ecSDimitry Andric if (FSamples) {
91145449b1SDimitry Andric setContextNode(FSamples, Node);
92b60736ecSDimitry Andric FSamples->getContext().setState(SyntheticContext);
93b60736ecSDimitry Andric }
94b60736ecSDimitry Andric
95b60736ecSDimitry Andric for (auto &It : Node->getAllChildContext()) {
96b60736ecSDimitry Andric ContextTrieNode *ChildNode = &It.second;
97b60736ecSDimitry Andric ChildNode->setParentContext(Node);
98b60736ecSDimitry Andric NodeToUpdate.push(ChildNode);
99b60736ecSDimitry Andric }
100b60736ecSDimitry Andric }
101b60736ecSDimitry Andric
102b60736ecSDimitry Andric return NewNode;
103b60736ecSDimitry Andric }
104b60736ecSDimitry Andric
removeChildContext(const LineLocation & CallSite,FunctionId CalleeName)105b60736ecSDimitry Andric void ContextTrieNode::removeChildContext(const LineLocation &CallSite,
106b1c73532SDimitry Andric FunctionId CalleeName) {
10777fc4c14SDimitry Andric uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite);
108b60736ecSDimitry Andric // Note this essentially calls dtor and destroys that child context
109b60736ecSDimitry Andric AllChildContext.erase(Hash);
110b60736ecSDimitry Andric }
111b60736ecSDimitry Andric
getAllChildContext()112c0981da4SDimitry Andric std::map<uint64_t, ContextTrieNode> &ContextTrieNode::getAllChildContext() {
113b60736ecSDimitry Andric return AllChildContext;
114b60736ecSDimitry Andric }
115b60736ecSDimitry Andric
getFuncName() const116b1c73532SDimitry Andric FunctionId ContextTrieNode::getFuncName() const { return FuncName; }
117b60736ecSDimitry Andric
getFunctionSamples() const118b60736ecSDimitry Andric FunctionSamples *ContextTrieNode::getFunctionSamples() const {
119b60736ecSDimitry Andric return FuncSamples;
120b60736ecSDimitry Andric }
121b60736ecSDimitry Andric
setFunctionSamples(FunctionSamples * FSamples)122b60736ecSDimitry Andric void ContextTrieNode::setFunctionSamples(FunctionSamples *FSamples) {
123b60736ecSDimitry Andric FuncSamples = FSamples;
124b60736ecSDimitry Andric }
125b60736ecSDimitry Andric
getFunctionSize() const126e3b55780SDimitry Andric std::optional<uint32_t> ContextTrieNode::getFunctionSize() const {
127e3b55780SDimitry Andric return FuncSize;
128e3b55780SDimitry Andric }
129c0981da4SDimitry Andric
addFunctionSize(uint32_t FSize)130c0981da4SDimitry Andric void ContextTrieNode::addFunctionSize(uint32_t FSize) {
131145449b1SDimitry Andric if (!FuncSize)
132c0981da4SDimitry Andric FuncSize = 0;
133c0981da4SDimitry Andric
134e3b55780SDimitry Andric FuncSize = *FuncSize + FSize;
135c0981da4SDimitry Andric }
136c0981da4SDimitry Andric
getCallSiteLoc() const137b60736ecSDimitry Andric LineLocation ContextTrieNode::getCallSiteLoc() const { return CallSiteLoc; }
138b60736ecSDimitry Andric
getParentContext() const139b60736ecSDimitry Andric ContextTrieNode *ContextTrieNode::getParentContext() const {
140b60736ecSDimitry Andric return ParentContext;
141b60736ecSDimitry Andric }
142b60736ecSDimitry Andric
setParentContext(ContextTrieNode * Parent)143b60736ecSDimitry Andric void ContextTrieNode::setParentContext(ContextTrieNode *Parent) {
144b60736ecSDimitry Andric ParentContext = Parent;
145b60736ecSDimitry Andric }
146b60736ecSDimitry Andric
setCallSiteLoc(const LineLocation & Loc)147145449b1SDimitry Andric void ContextTrieNode::setCallSiteLoc(const LineLocation &Loc) {
148145449b1SDimitry Andric CallSiteLoc = Loc;
149145449b1SDimitry Andric }
150145449b1SDimitry Andric
dumpNode()151c0981da4SDimitry Andric void ContextTrieNode::dumpNode() {
152b60736ecSDimitry Andric dbgs() << "Node: " << FuncName << "\n"
153b60736ecSDimitry Andric << " Callsite: " << CallSiteLoc << "\n"
154c0981da4SDimitry Andric << " Size: " << FuncSize << "\n"
155b60736ecSDimitry Andric << " Children:\n";
156b60736ecSDimitry Andric
157b60736ecSDimitry Andric for (auto &It : AllChildContext) {
158b60736ecSDimitry Andric dbgs() << " Node: " << It.second.getFuncName() << "\n";
159b60736ecSDimitry Andric }
160b60736ecSDimitry Andric }
161b60736ecSDimitry Andric
dumpTree()162c0981da4SDimitry Andric void ContextTrieNode::dumpTree() {
163c0981da4SDimitry Andric dbgs() << "Context Profile Tree:\n";
164c0981da4SDimitry Andric std::queue<ContextTrieNode *> NodeQueue;
165c0981da4SDimitry Andric NodeQueue.push(this);
166c0981da4SDimitry Andric
167c0981da4SDimitry Andric while (!NodeQueue.empty()) {
168c0981da4SDimitry Andric ContextTrieNode *Node = NodeQueue.front();
169c0981da4SDimitry Andric NodeQueue.pop();
170c0981da4SDimitry Andric Node->dumpNode();
171c0981da4SDimitry Andric
172c0981da4SDimitry Andric for (auto &It : Node->getAllChildContext()) {
173c0981da4SDimitry Andric ContextTrieNode *ChildNode = &It.second;
174c0981da4SDimitry Andric NodeQueue.push(ChildNode);
175c0981da4SDimitry Andric }
176c0981da4SDimitry Andric }
177c0981da4SDimitry Andric }
178c0981da4SDimitry Andric
getOrCreateChildContext(const LineLocation & CallSite,FunctionId CalleeName,bool AllowCreate)179b60736ecSDimitry Andric ContextTrieNode *ContextTrieNode::getOrCreateChildContext(
180b1c73532SDimitry Andric const LineLocation &CallSite, FunctionId CalleeName, bool AllowCreate) {
18177fc4c14SDimitry Andric uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite);
182b60736ecSDimitry Andric auto It = AllChildContext.find(Hash);
183b60736ecSDimitry Andric if (It != AllChildContext.end()) {
184b60736ecSDimitry Andric assert(It->second.getFuncName() == CalleeName &&
185b60736ecSDimitry Andric "Hash collision for child context node");
186b60736ecSDimitry Andric return &It->second;
187b60736ecSDimitry Andric }
188b60736ecSDimitry Andric
189b60736ecSDimitry Andric if (!AllowCreate)
190b60736ecSDimitry Andric return nullptr;
191b60736ecSDimitry Andric
192b60736ecSDimitry Andric AllChildContext[Hash] = ContextTrieNode(this, CalleeName, nullptr, CallSite);
193b60736ecSDimitry Andric return &AllChildContext[Hash];
194b60736ecSDimitry Andric }
195b60736ecSDimitry Andric
196b60736ecSDimitry Andric // Profiler tracker than manages profiles and its associated context
SampleContextTracker(SampleProfileMap & Profiles,const DenseMap<uint64_t,StringRef> * GUIDToFuncNameMap)197b60736ecSDimitry Andric SampleContextTracker::SampleContextTracker(
198c0981da4SDimitry Andric SampleProfileMap &Profiles,
199c0981da4SDimitry Andric const DenseMap<uint64_t, StringRef> *GUIDToFuncNameMap)
200c0981da4SDimitry Andric : GUIDToFuncNameMap(GUIDToFuncNameMap) {
201b60736ecSDimitry Andric for (auto &FuncSample : Profiles) {
202b60736ecSDimitry Andric FunctionSamples *FSamples = &FuncSample.second;
203b1c73532SDimitry Andric SampleContext Context = FuncSample.second.getContext();
204c0981da4SDimitry Andric LLVM_DEBUG(dbgs() << "Tracking Context for function: " << Context.toString()
205c0981da4SDimitry Andric << "\n");
206b60736ecSDimitry Andric ContextTrieNode *NewNode = getOrCreateContextPath(Context, true);
207b60736ecSDimitry Andric assert(!NewNode->getFunctionSamples() &&
208b60736ecSDimitry Andric "New node can't have sample profile");
209b60736ecSDimitry Andric NewNode->setFunctionSamples(FSamples);
210b60736ecSDimitry Andric }
211145449b1SDimitry Andric populateFuncToCtxtMap();
212145449b1SDimitry Andric }
213145449b1SDimitry Andric
populateFuncToCtxtMap()214145449b1SDimitry Andric void SampleContextTracker::populateFuncToCtxtMap() {
215145449b1SDimitry Andric for (auto *Node : *this) {
216145449b1SDimitry Andric FunctionSamples *FSamples = Node->getFunctionSamples();
217145449b1SDimitry Andric if (FSamples) {
218145449b1SDimitry Andric FSamples->getContext().setState(RawContext);
219145449b1SDimitry Andric setContextNode(FSamples, Node);
220145449b1SDimitry Andric FuncToCtxtProfiles[Node->getFuncName()].push_back(FSamples);
221145449b1SDimitry Andric }
222145449b1SDimitry Andric }
223b60736ecSDimitry Andric }
224b60736ecSDimitry Andric
225b60736ecSDimitry Andric FunctionSamples *
getCalleeContextSamplesFor(const CallBase & Inst,StringRef CalleeName)226b60736ecSDimitry Andric SampleContextTracker::getCalleeContextSamplesFor(const CallBase &Inst,
227b60736ecSDimitry Andric StringRef CalleeName) {
228b60736ecSDimitry Andric LLVM_DEBUG(dbgs() << "Getting callee context for instr: " << Inst << "\n");
229b60736ecSDimitry Andric DILocation *DIL = Inst.getDebugLoc();
230b60736ecSDimitry Andric if (!DIL)
231b60736ecSDimitry Andric return nullptr;
232b60736ecSDimitry Andric
233344a3780SDimitry Andric CalleeName = FunctionSamples::getCanonicalFnName(CalleeName);
234b1c73532SDimitry Andric
235b1c73532SDimitry Andric FunctionId FName = getRepInFormat(CalleeName);
236344a3780SDimitry Andric
237344a3780SDimitry Andric // For indirect call, CalleeName will be empty, in which case the context
238344a3780SDimitry Andric // profile for callee with largest total samples will be returned.
239b1c73532SDimitry Andric ContextTrieNode *CalleeContext = getCalleeContextFor(DIL, FName);
240b60736ecSDimitry Andric if (CalleeContext) {
241b60736ecSDimitry Andric FunctionSamples *FSamples = CalleeContext->getFunctionSamples();
242b60736ecSDimitry Andric LLVM_DEBUG(if (FSamples) {
243145449b1SDimitry Andric dbgs() << " Callee context found: " << getContextString(CalleeContext)
244c0981da4SDimitry Andric << "\n";
245b60736ecSDimitry Andric });
246b60736ecSDimitry Andric return FSamples;
247b60736ecSDimitry Andric }
248b60736ecSDimitry Andric
249b60736ecSDimitry Andric return nullptr;
250b60736ecSDimitry Andric }
251b60736ecSDimitry Andric
252344a3780SDimitry Andric std::vector<const FunctionSamples *>
getIndirectCalleeContextSamplesFor(const DILocation * DIL)253344a3780SDimitry Andric SampleContextTracker::getIndirectCalleeContextSamplesFor(
254344a3780SDimitry Andric const DILocation *DIL) {
255344a3780SDimitry Andric std::vector<const FunctionSamples *> R;
256344a3780SDimitry Andric if (!DIL)
257344a3780SDimitry Andric return R;
258344a3780SDimitry Andric
259344a3780SDimitry Andric ContextTrieNode *CallerNode = getContextFor(DIL);
260344a3780SDimitry Andric LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL);
261344a3780SDimitry Andric for (auto &It : CallerNode->getAllChildContext()) {
262344a3780SDimitry Andric ContextTrieNode &ChildNode = It.second;
263344a3780SDimitry Andric if (ChildNode.getCallSiteLoc() != CallSite)
264344a3780SDimitry Andric continue;
265344a3780SDimitry Andric if (FunctionSamples *CalleeSamples = ChildNode.getFunctionSamples())
266344a3780SDimitry Andric R.push_back(CalleeSamples);
267344a3780SDimitry Andric }
268344a3780SDimitry Andric
269344a3780SDimitry Andric return R;
270344a3780SDimitry Andric }
271344a3780SDimitry Andric
272b60736ecSDimitry Andric FunctionSamples *
getContextSamplesFor(const DILocation * DIL)273b60736ecSDimitry Andric SampleContextTracker::getContextSamplesFor(const DILocation *DIL) {
274b60736ecSDimitry Andric assert(DIL && "Expect non-null location");
275b60736ecSDimitry Andric
276b60736ecSDimitry Andric ContextTrieNode *ContextNode = getContextFor(DIL);
277b60736ecSDimitry Andric if (!ContextNode)
278b60736ecSDimitry Andric return nullptr;
279b60736ecSDimitry Andric
280b60736ecSDimitry Andric // We may have inlined callees during pre-LTO compilation, in which case
281b60736ecSDimitry Andric // we need to rely on the inline stack from !dbg to mark context profile
282b60736ecSDimitry Andric // as inlined, instead of `MarkContextSamplesInlined` during inlining.
283b60736ecSDimitry Andric // Sample profile loader walks through all instructions to get profile,
284b60736ecSDimitry Andric // which calls this function. So once that is done, all previously inlined
285b60736ecSDimitry Andric // context profile should be marked properly.
286b60736ecSDimitry Andric FunctionSamples *Samples = ContextNode->getFunctionSamples();
287b60736ecSDimitry Andric if (Samples && ContextNode->getParentContext() != &RootContext)
288b60736ecSDimitry Andric Samples->getContext().setState(InlinedContext);
289b60736ecSDimitry Andric
290b60736ecSDimitry Andric return Samples;
291b60736ecSDimitry Andric }
292b60736ecSDimitry Andric
293b60736ecSDimitry Andric FunctionSamples *
getContextSamplesFor(const SampleContext & Context)294b60736ecSDimitry Andric SampleContextTracker::getContextSamplesFor(const SampleContext &Context) {
295b60736ecSDimitry Andric ContextTrieNode *Node = getContextFor(Context);
296b60736ecSDimitry Andric if (!Node)
297b60736ecSDimitry Andric return nullptr;
298b60736ecSDimitry Andric
299b60736ecSDimitry Andric return Node->getFunctionSamples();
300b60736ecSDimitry Andric }
301b60736ecSDimitry Andric
302344a3780SDimitry Andric SampleContextTracker::ContextSamplesTy &
getAllContextSamplesFor(const Function & Func)303344a3780SDimitry Andric SampleContextTracker::getAllContextSamplesFor(const Function &Func) {
304344a3780SDimitry Andric StringRef CanonName = FunctionSamples::getCanonicalFnName(Func);
305b1c73532SDimitry Andric return FuncToCtxtProfiles[getRepInFormat(CanonName)];
306344a3780SDimitry Andric }
307344a3780SDimitry Andric
308344a3780SDimitry Andric SampleContextTracker::ContextSamplesTy &
getAllContextSamplesFor(StringRef Name)309344a3780SDimitry Andric SampleContextTracker::getAllContextSamplesFor(StringRef Name) {
310b1c73532SDimitry Andric return FuncToCtxtProfiles[getRepInFormat(Name)];
311344a3780SDimitry Andric }
312344a3780SDimitry Andric
getBaseSamplesFor(const Function & Func,bool MergeContext)313b60736ecSDimitry Andric FunctionSamples *SampleContextTracker::getBaseSamplesFor(const Function &Func,
314b60736ecSDimitry Andric bool MergeContext) {
315b60736ecSDimitry Andric StringRef CanonName = FunctionSamples::getCanonicalFnName(Func);
316b1c73532SDimitry Andric return getBaseSamplesFor(getRepInFormat(CanonName), MergeContext);
317b60736ecSDimitry Andric }
318b60736ecSDimitry Andric
getBaseSamplesFor(FunctionId Name,bool MergeContext)319b1c73532SDimitry Andric FunctionSamples *SampleContextTracker::getBaseSamplesFor(FunctionId Name,
320b60736ecSDimitry Andric bool MergeContext) {
321b60736ecSDimitry Andric LLVM_DEBUG(dbgs() << "Getting base profile for function: " << Name << "\n");
322c0981da4SDimitry Andric
323b60736ecSDimitry Andric // Base profile is top-level node (child of root node), so try to retrieve
324b60736ecSDimitry Andric // existing top-level node for given function first. If it exists, it could be
325b60736ecSDimitry Andric // that we've merged base profile before, or there's actually context-less
326b60736ecSDimitry Andric // profile from the input (e.g. due to unreliable stack walking).
327b60736ecSDimitry Andric ContextTrieNode *Node = getTopLevelContextNode(Name);
328b60736ecSDimitry Andric if (MergeContext) {
329b60736ecSDimitry Andric LLVM_DEBUG(dbgs() << " Merging context profile into base profile: " << Name
330b60736ecSDimitry Andric << "\n");
331b60736ecSDimitry Andric
332b60736ecSDimitry Andric // We have profile for function under different contexts,
333b60736ecSDimitry Andric // create synthetic base profile and merge context profiles
334b60736ecSDimitry Andric // into base profile.
335344a3780SDimitry Andric for (auto *CSamples : FuncToCtxtProfiles[Name]) {
336b60736ecSDimitry Andric SampleContext &Context = CSamples->getContext();
337b60736ecSDimitry Andric // Skip inlined context profile and also don't re-merge any context
338b60736ecSDimitry Andric if (Context.hasState(InlinedContext) || Context.hasState(MergedContext))
339b60736ecSDimitry Andric continue;
340b60736ecSDimitry Andric
341145449b1SDimitry Andric ContextTrieNode *FromNode = getContextNodeForProfile(CSamples);
342c0981da4SDimitry Andric if (FromNode == Node)
343c0981da4SDimitry Andric continue;
344c0981da4SDimitry Andric
345b60736ecSDimitry Andric ContextTrieNode &ToNode = promoteMergeContextSamplesTree(*FromNode);
346b60736ecSDimitry Andric assert((!Node || Node == &ToNode) && "Expect only one base profile");
347b60736ecSDimitry Andric Node = &ToNode;
348b60736ecSDimitry Andric }
349b60736ecSDimitry Andric }
350b60736ecSDimitry Andric
351b60736ecSDimitry Andric // Still no profile even after merge/promotion (if allowed)
352b60736ecSDimitry Andric if (!Node)
353b60736ecSDimitry Andric return nullptr;
354b60736ecSDimitry Andric
355b60736ecSDimitry Andric return Node->getFunctionSamples();
356b60736ecSDimitry Andric }
357b60736ecSDimitry Andric
markContextSamplesInlined(const FunctionSamples * InlinedSamples)358b60736ecSDimitry Andric void SampleContextTracker::markContextSamplesInlined(
359b60736ecSDimitry Andric const FunctionSamples *InlinedSamples) {
360b60736ecSDimitry Andric assert(InlinedSamples && "Expect non-null inlined samples");
361b60736ecSDimitry Andric LLVM_DEBUG(dbgs() << "Marking context profile as inlined: "
362145449b1SDimitry Andric << getContextString(*InlinedSamples) << "\n");
363b60736ecSDimitry Andric InlinedSamples->getContext().setState(InlinedContext);
364b60736ecSDimitry Andric }
365b60736ecSDimitry Andric
getRootContext()366344a3780SDimitry Andric ContextTrieNode &SampleContextTracker::getRootContext() { return RootContext; }
367344a3780SDimitry Andric
promoteMergeContextSamplesTree(const Instruction & Inst,FunctionId CalleeName)368b60736ecSDimitry Andric void SampleContextTracker::promoteMergeContextSamplesTree(
369b1c73532SDimitry Andric const Instruction &Inst, FunctionId CalleeName) {
370b60736ecSDimitry Andric LLVM_DEBUG(dbgs() << "Promoting and merging context tree for instr: \n"
371b60736ecSDimitry Andric << Inst << "\n");
372b60736ecSDimitry Andric // Get the caller context for the call instruction, we don't use callee
373b60736ecSDimitry Andric // name from call because there can be context from indirect calls too.
374b60736ecSDimitry Andric DILocation *DIL = Inst.getDebugLoc();
375b60736ecSDimitry Andric ContextTrieNode *CallerNode = getContextFor(DIL);
376b60736ecSDimitry Andric if (!CallerNode)
377b60736ecSDimitry Andric return;
378b60736ecSDimitry Andric
379b60736ecSDimitry Andric // Get the context that needs to be promoted
380344a3780SDimitry Andric LineLocation CallSite = FunctionSamples::getCallSiteIdentifier(DIL);
381344a3780SDimitry Andric // For indirect call, CalleeName will be empty, in which case we need to
382344a3780SDimitry Andric // promote all non-inlined child context profiles.
383344a3780SDimitry Andric if (CalleeName.empty()) {
384344a3780SDimitry Andric for (auto &It : CallerNode->getAllChildContext()) {
385344a3780SDimitry Andric ContextTrieNode *NodeToPromo = &It.second;
386344a3780SDimitry Andric if (CallSite != NodeToPromo->getCallSiteLoc())
387344a3780SDimitry Andric continue;
388344a3780SDimitry Andric FunctionSamples *FromSamples = NodeToPromo->getFunctionSamples();
389344a3780SDimitry Andric if (FromSamples && FromSamples->getContext().hasState(InlinedContext))
390344a3780SDimitry Andric continue;
391344a3780SDimitry Andric promoteMergeContextSamplesTree(*NodeToPromo);
392344a3780SDimitry Andric }
393344a3780SDimitry Andric return;
394344a3780SDimitry Andric }
395344a3780SDimitry Andric
396344a3780SDimitry Andric // Get the context for the given callee that needs to be promoted
397b60736ecSDimitry Andric ContextTrieNode *NodeToPromo =
398b60736ecSDimitry Andric CallerNode->getChildContext(CallSite, CalleeName);
399b60736ecSDimitry Andric if (!NodeToPromo)
400b60736ecSDimitry Andric return;
401b60736ecSDimitry Andric
402b60736ecSDimitry Andric promoteMergeContextSamplesTree(*NodeToPromo);
403b60736ecSDimitry Andric }
404b60736ecSDimitry Andric
promoteMergeContextSamplesTree(ContextTrieNode & NodeToPromo)405b60736ecSDimitry Andric ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree(
406b60736ecSDimitry Andric ContextTrieNode &NodeToPromo) {
407b60736ecSDimitry Andric // Promote the input node to be directly under root. This can happen
408b60736ecSDimitry Andric // when we decided to not inline a function under context represented
409b60736ecSDimitry Andric // by the input node. The promote and merge is then needed to reflect
410b60736ecSDimitry Andric // the context profile in the base (context-less) profile.
411b60736ecSDimitry Andric FunctionSamples *FromSamples = NodeToPromo.getFunctionSamples();
412b60736ecSDimitry Andric assert(FromSamples && "Shouldn't promote a context without profile");
413145449b1SDimitry Andric (void)FromSamples; // Unused in release build.
414145449b1SDimitry Andric
415b60736ecSDimitry Andric LLVM_DEBUG(dbgs() << " Found context tree root to promote: "
416145449b1SDimitry Andric << getContextString(&NodeToPromo) << "\n");
417b60736ecSDimitry Andric
418344a3780SDimitry Andric assert(!FromSamples->getContext().hasState(InlinedContext) &&
419344a3780SDimitry Andric "Shouldn't promote inlined context profile");
420145449b1SDimitry Andric return promoteMergeContextSamplesTree(NodeToPromo, RootContext);
421b60736ecSDimitry Andric }
422b60736ecSDimitry Andric
423145449b1SDimitry Andric #ifndef NDEBUG
424145449b1SDimitry Andric std::string
getContextString(const FunctionSamples & FSamples) const425145449b1SDimitry Andric SampleContextTracker::getContextString(const FunctionSamples &FSamples) const {
426145449b1SDimitry Andric return getContextString(getContextNodeForProfile(&FSamples));
427145449b1SDimitry Andric }
428145449b1SDimitry Andric
429145449b1SDimitry Andric std::string
getContextString(ContextTrieNode * Node) const430145449b1SDimitry Andric SampleContextTracker::getContextString(ContextTrieNode *Node) const {
431145449b1SDimitry Andric SampleContextFrameVector Res;
432145449b1SDimitry Andric if (Node == &RootContext)
433145449b1SDimitry Andric return std::string();
434145449b1SDimitry Andric Res.emplace_back(Node->getFuncName(), LineLocation(0, 0));
435145449b1SDimitry Andric
436145449b1SDimitry Andric ContextTrieNode *PreNode = Node;
437145449b1SDimitry Andric Node = Node->getParentContext();
438145449b1SDimitry Andric while (Node && Node != &RootContext) {
439145449b1SDimitry Andric Res.emplace_back(Node->getFuncName(), PreNode->getCallSiteLoc());
440145449b1SDimitry Andric PreNode = Node;
441145449b1SDimitry Andric Node = Node->getParentContext();
442145449b1SDimitry Andric }
443145449b1SDimitry Andric
444145449b1SDimitry Andric std::reverse(Res.begin(), Res.end());
445145449b1SDimitry Andric
446145449b1SDimitry Andric return SampleContext::getContextString(Res);
447145449b1SDimitry Andric }
448145449b1SDimitry Andric #endif
449145449b1SDimitry Andric
dump()450c0981da4SDimitry Andric void SampleContextTracker::dump() { RootContext.dumpTree(); }
451b60736ecSDimitry Andric
getFuncNameFor(ContextTrieNode * Node) const452c0981da4SDimitry Andric StringRef SampleContextTracker::getFuncNameFor(ContextTrieNode *Node) const {
453c0981da4SDimitry Andric if (!FunctionSamples::UseMD5)
454b1c73532SDimitry Andric return Node->getFuncName().stringRef();
455c0981da4SDimitry Andric assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be populated first");
456b1c73532SDimitry Andric return GUIDToFuncNameMap->lookup(Node->getFuncName().getHashCode());
457b60736ecSDimitry Andric }
458b60736ecSDimitry Andric
459b60736ecSDimitry Andric ContextTrieNode *
getContextFor(const SampleContext & Context)460b60736ecSDimitry Andric SampleContextTracker::getContextFor(const SampleContext &Context) {
461b60736ecSDimitry Andric return getOrCreateContextPath(Context, false);
462b60736ecSDimitry Andric }
463b60736ecSDimitry Andric
464b60736ecSDimitry Andric ContextTrieNode *
getCalleeContextFor(const DILocation * DIL,FunctionId CalleeName)465b60736ecSDimitry Andric SampleContextTracker::getCalleeContextFor(const DILocation *DIL,
466b1c73532SDimitry Andric FunctionId CalleeName) {
467b60736ecSDimitry Andric assert(DIL && "Expect non-null location");
468b60736ecSDimitry Andric
469b60736ecSDimitry Andric ContextTrieNode *CallContext = getContextFor(DIL);
470b60736ecSDimitry Andric if (!CallContext)
471b60736ecSDimitry Andric return nullptr;
472b60736ecSDimitry Andric
473344a3780SDimitry Andric // When CalleeName is empty, the child context profile with max
474344a3780SDimitry Andric // total samples will be returned.
475b60736ecSDimitry Andric return CallContext->getChildContext(
476344a3780SDimitry Andric FunctionSamples::getCallSiteIdentifier(DIL), CalleeName);
477b60736ecSDimitry Andric }
478b60736ecSDimitry Andric
getContextFor(const DILocation * DIL)479b60736ecSDimitry Andric ContextTrieNode *SampleContextTracker::getContextFor(const DILocation *DIL) {
480b60736ecSDimitry Andric assert(DIL && "Expect non-null location");
481b1c73532SDimitry Andric SmallVector<std::pair<LineLocation, FunctionId>, 10> S;
482b60736ecSDimitry Andric
483b60736ecSDimitry Andric // Use C++ linkage name if possible.
484b60736ecSDimitry Andric const DILocation *PrevDIL = DIL;
485b60736ecSDimitry Andric for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) {
486b60736ecSDimitry Andric StringRef Name = PrevDIL->getScope()->getSubprogram()->getLinkageName();
487b60736ecSDimitry Andric if (Name.empty())
488b60736ecSDimitry Andric Name = PrevDIL->getScope()->getSubprogram()->getName();
489b60736ecSDimitry Andric S.push_back(
490b1c73532SDimitry Andric std::make_pair(FunctionSamples::getCallSiteIdentifier(DIL),
491b1c73532SDimitry Andric getRepInFormat(Name)));
492b60736ecSDimitry Andric PrevDIL = DIL;
493b60736ecSDimitry Andric }
494b60736ecSDimitry Andric
495b60736ecSDimitry Andric // Push root node, note that root node like main may only
496b60736ecSDimitry Andric // a name, but not linkage name.
497b60736ecSDimitry Andric StringRef RootName = PrevDIL->getScope()->getSubprogram()->getLinkageName();
498b60736ecSDimitry Andric if (RootName.empty())
499b60736ecSDimitry Andric RootName = PrevDIL->getScope()->getSubprogram()->getName();
500b1c73532SDimitry Andric S.push_back(std::make_pair(LineLocation(0, 0),
501b1c73532SDimitry Andric getRepInFormat(RootName)));
502c0981da4SDimitry Andric
503b60736ecSDimitry Andric ContextTrieNode *ContextNode = &RootContext;
504b60736ecSDimitry Andric int I = S.size();
505b60736ecSDimitry Andric while (--I >= 0 && ContextNode) {
506b60736ecSDimitry Andric LineLocation &CallSite = S[I].first;
507b1c73532SDimitry Andric FunctionId CalleeName = S[I].second;
508b60736ecSDimitry Andric ContextNode = ContextNode->getChildContext(CallSite, CalleeName);
509b60736ecSDimitry Andric }
510b60736ecSDimitry Andric
511b60736ecSDimitry Andric if (I < 0)
512b60736ecSDimitry Andric return ContextNode;
513b60736ecSDimitry Andric
514b60736ecSDimitry Andric return nullptr;
515b60736ecSDimitry Andric }
516b60736ecSDimitry Andric
517b60736ecSDimitry Andric ContextTrieNode *
getOrCreateContextPath(const SampleContext & Context,bool AllowCreate)518b60736ecSDimitry Andric SampleContextTracker::getOrCreateContextPath(const SampleContext &Context,
519b60736ecSDimitry Andric bool AllowCreate) {
520b60736ecSDimitry Andric ContextTrieNode *ContextNode = &RootContext;
521b60736ecSDimitry Andric LineLocation CallSiteLoc(0, 0);
522b60736ecSDimitry Andric
523e3b55780SDimitry Andric for (const auto &Callsite : Context.getContextFrames()) {
524b60736ecSDimitry Andric // Create child node at parent line/disc location
525b60736ecSDimitry Andric if (AllowCreate) {
526b60736ecSDimitry Andric ContextNode =
527b1c73532SDimitry Andric ContextNode->getOrCreateChildContext(CallSiteLoc, Callsite.Func);
528b60736ecSDimitry Andric } else {
529c0981da4SDimitry Andric ContextNode =
530b1c73532SDimitry Andric ContextNode->getChildContext(CallSiteLoc, Callsite.Func);
531b60736ecSDimitry Andric }
532c0981da4SDimitry Andric CallSiteLoc = Callsite.Location;
533b60736ecSDimitry Andric }
534b60736ecSDimitry Andric
535b60736ecSDimitry Andric assert((!AllowCreate || ContextNode) &&
536b60736ecSDimitry Andric "Node must exist if creation is allowed");
537b60736ecSDimitry Andric return ContextNode;
538b60736ecSDimitry Andric }
539b60736ecSDimitry Andric
540b1c73532SDimitry Andric ContextTrieNode *
getTopLevelContextNode(FunctionId FName)541b1c73532SDimitry Andric SampleContextTracker::getTopLevelContextNode(FunctionId FName) {
542344a3780SDimitry Andric assert(!FName.empty() && "Top level node query must provide valid name");
543b60736ecSDimitry Andric return RootContext.getChildContext(LineLocation(0, 0), FName);
544b60736ecSDimitry Andric }
545b60736ecSDimitry Andric
546b1c73532SDimitry Andric ContextTrieNode &
addTopLevelContextNode(FunctionId FName)547b1c73532SDimitry Andric SampleContextTracker::addTopLevelContextNode(FunctionId FName) {
548b60736ecSDimitry Andric assert(!getTopLevelContextNode(FName) && "Node to add must not exist");
549b60736ecSDimitry Andric return *RootContext.getOrCreateChildContext(LineLocation(0, 0), FName);
550b60736ecSDimitry Andric }
551b60736ecSDimitry Andric
mergeContextNode(ContextTrieNode & FromNode,ContextTrieNode & ToNode)552b60736ecSDimitry Andric void SampleContextTracker::mergeContextNode(ContextTrieNode &FromNode,
553145449b1SDimitry Andric ContextTrieNode &ToNode) {
554b60736ecSDimitry Andric FunctionSamples *FromSamples = FromNode.getFunctionSamples();
555b60736ecSDimitry Andric FunctionSamples *ToSamples = ToNode.getFunctionSamples();
556b60736ecSDimitry Andric if (FromSamples && ToSamples) {
557b60736ecSDimitry Andric // Merge/duplicate FromSamples into ToSamples
558b60736ecSDimitry Andric ToSamples->merge(*FromSamples);
559b60736ecSDimitry Andric ToSamples->getContext().setState(SyntheticContext);
560b60736ecSDimitry Andric FromSamples->getContext().setState(MergedContext);
561c0981da4SDimitry Andric if (FromSamples->getContext().hasAttribute(ContextShouldBeInlined))
562c0981da4SDimitry Andric ToSamples->getContext().setAttribute(ContextShouldBeInlined);
563b60736ecSDimitry Andric } else if (FromSamples) {
564b60736ecSDimitry Andric // Transfer FromSamples from FromNode to ToNode
565b60736ecSDimitry Andric ToNode.setFunctionSamples(FromSamples);
566145449b1SDimitry Andric setContextNode(FromSamples, &ToNode);
567b60736ecSDimitry Andric FromSamples->getContext().setState(SyntheticContext);
568b60736ecSDimitry Andric }
569b60736ecSDimitry Andric }
570b60736ecSDimitry Andric
promoteMergeContextSamplesTree(ContextTrieNode & FromNode,ContextTrieNode & ToNodeParent)571b60736ecSDimitry Andric ContextTrieNode &SampleContextTracker::promoteMergeContextSamplesTree(
572145449b1SDimitry Andric ContextTrieNode &FromNode, ContextTrieNode &ToNodeParent) {
573b60736ecSDimitry Andric
574b60736ecSDimitry Andric // Ignore call site location if destination is top level under root
575b60736ecSDimitry Andric LineLocation NewCallSiteLoc = LineLocation(0, 0);
576b60736ecSDimitry Andric LineLocation OldCallSiteLoc = FromNode.getCallSiteLoc();
577b60736ecSDimitry Andric ContextTrieNode &FromNodeParent = *FromNode.getParentContext();
578b60736ecSDimitry Andric ContextTrieNode *ToNode = nullptr;
579b60736ecSDimitry Andric bool MoveToRoot = (&ToNodeParent == &RootContext);
580b60736ecSDimitry Andric if (!MoveToRoot) {
581b60736ecSDimitry Andric NewCallSiteLoc = OldCallSiteLoc;
582b60736ecSDimitry Andric }
583b60736ecSDimitry Andric
584b60736ecSDimitry Andric // Locate destination node, create/move if not existing
585b60736ecSDimitry Andric ToNode = ToNodeParent.getChildContext(NewCallSiteLoc, FromNode.getFuncName());
586b60736ecSDimitry Andric if (!ToNode) {
587b60736ecSDimitry Andric // Do not delete node to move from its parent here because
588b60736ecSDimitry Andric // caller is iterating over children of that parent node.
589145449b1SDimitry Andric ToNode =
590145449b1SDimitry Andric &moveContextSamples(ToNodeParent, NewCallSiteLoc, std::move(FromNode));
591145449b1SDimitry Andric LLVM_DEBUG({
592145449b1SDimitry Andric dbgs() << " Context promoted and merged to: " << getContextString(ToNode)
593145449b1SDimitry Andric << "\n";
594145449b1SDimitry Andric });
595b60736ecSDimitry Andric } else {
596b60736ecSDimitry Andric // Destination node exists, merge samples for the context tree
597145449b1SDimitry Andric mergeContextNode(FromNode, *ToNode);
598344a3780SDimitry Andric LLVM_DEBUG({
599344a3780SDimitry Andric if (ToNode->getFunctionSamples())
600344a3780SDimitry Andric dbgs() << " Context promoted and merged to: "
601145449b1SDimitry Andric << getContextString(ToNode) << "\n";
602344a3780SDimitry Andric });
603b60736ecSDimitry Andric
604b60736ecSDimitry Andric // Recursively promote and merge children
605b60736ecSDimitry Andric for (auto &It : FromNode.getAllChildContext()) {
606b60736ecSDimitry Andric ContextTrieNode &FromChildNode = It.second;
607145449b1SDimitry Andric promoteMergeContextSamplesTree(FromChildNode, *ToNode);
608b60736ecSDimitry Andric }
609b60736ecSDimitry Andric
610b60736ecSDimitry Andric // Remove children once they're all merged
611b60736ecSDimitry Andric FromNode.getAllChildContext().clear();
612b60736ecSDimitry Andric }
613b60736ecSDimitry Andric
614b60736ecSDimitry Andric // For root of subtree, remove itself from old parent too
615b60736ecSDimitry Andric if (MoveToRoot)
616b60736ecSDimitry Andric FromNodeParent.removeChildContext(OldCallSiteLoc, ToNode->getFuncName());
617b60736ecSDimitry Andric
618b60736ecSDimitry Andric return *ToNode;
619b60736ecSDimitry Andric }
620145449b1SDimitry Andric
createContextLessProfileMap(SampleProfileMap & ContextLessProfiles)621145449b1SDimitry Andric void SampleContextTracker::createContextLessProfileMap(
622145449b1SDimitry Andric SampleProfileMap &ContextLessProfiles) {
623145449b1SDimitry Andric for (auto *Node : *this) {
624145449b1SDimitry Andric FunctionSamples *FProfile = Node->getFunctionSamples();
625145449b1SDimitry Andric // Profile's context can be empty, use ContextNode's func name.
626145449b1SDimitry Andric if (FProfile)
627ac9a064cSDimitry Andric ContextLessProfiles.create(Node->getFuncName()).merge(*FProfile);
628145449b1SDimitry Andric }
629145449b1SDimitry Andric }
630b60736ecSDimitry Andric } // namespace llvm
631