xref: /llvm-project/llvm/lib/Analysis/CtxProfAnalysis.cpp (revision b15845c0059b06f406e33f278127d7eb41ff5ab6)
1dbbf0762SMircea Trofin //===- CtxProfAnalysis.cpp - contextual profile analysis ------------------===//
2dbbf0762SMircea Trofin //
3dbbf0762SMircea Trofin // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4dbbf0762SMircea Trofin // See https://llvm.org/LICENSE.txt for license information.
5dbbf0762SMircea Trofin // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6dbbf0762SMircea Trofin //
7dbbf0762SMircea Trofin //===----------------------------------------------------------------------===//
8dbbf0762SMircea Trofin //
9dbbf0762SMircea Trofin // Implementation of the contextual profile analysis, which maintains contextual
10dbbf0762SMircea Trofin // profiling info through IPO passes.
11dbbf0762SMircea Trofin //
12dbbf0762SMircea Trofin //===----------------------------------------------------------------------===//
13dbbf0762SMircea Trofin 
14dbbf0762SMircea Trofin #include "llvm/Analysis/CtxProfAnalysis.h"
15dbbf0762SMircea Trofin #include "llvm/ADT/STLExtras.h"
16dbbf0762SMircea Trofin #include "llvm/IR/Analysis.h"
17aca01bffSMircea Trofin #include "llvm/IR/IntrinsicInst.h"
18dbbf0762SMircea Trofin #include "llvm/IR/Module.h"
19dbbf0762SMircea Trofin #include "llvm/IR/PassManager.h"
20dbbf0762SMircea Trofin #include "llvm/ProfileData/PGOCtxProfReader.h"
214a2bf059SMircea Trofin #include "llvm/Support/CommandLine.h"
22dbbf0762SMircea Trofin #include "llvm/Support/MemoryBuffer.h"
23dbbf0762SMircea Trofin 
24dbbf0762SMircea Trofin #define DEBUG_TYPE "ctx_prof"
25dbbf0762SMircea Trofin 
264a2bf059SMircea Trofin using namespace llvm;
274a2bf059SMircea Trofin cl::opt<std::string>
284a2bf059SMircea Trofin     UseCtxProfile("use-ctx-profile", cl::init(""), cl::Hidden,
294a2bf059SMircea Trofin                   cl::desc("Use the specified contextual profile file"));
304a2bf059SMircea Trofin 
3132097666SMircea Trofin static cl::opt<CtxProfAnalysisPrinterPass::PrintMode> PrintLevel(
3232097666SMircea Trofin     "ctx-profile-printer-level",
33*b15845c0SMircea Trofin     cl::init(CtxProfAnalysisPrinterPass::PrintMode::YAML), cl::Hidden,
3432097666SMircea Trofin     cl::values(clEnumValN(CtxProfAnalysisPrinterPass::PrintMode::Everything,
3532097666SMircea Trofin                           "everything", "print everything - most verbose"),
36*b15845c0SMircea Trofin                clEnumValN(CtxProfAnalysisPrinterPass::PrintMode::YAML, "yaml",
37*b15845c0SMircea Trofin                           "just the yaml representation of the profile")),
3832097666SMircea Trofin     cl::desc("Verbosity level of the contextual profile printer pass."));
3932097666SMircea Trofin 
40aca01bffSMircea Trofin const char *AssignGUIDPass::GUIDMetadataName = "guid";
41aca01bffSMircea Trofin 
42aca01bffSMircea Trofin PreservedAnalyses AssignGUIDPass::run(Module &M, ModuleAnalysisManager &MAM) {
43aca01bffSMircea Trofin   for (auto &F : M.functions()) {
44aca01bffSMircea Trofin     if (F.isDeclaration())
45aca01bffSMircea Trofin       continue;
46aca01bffSMircea Trofin     if (F.getMetadata(GUIDMetadataName))
47aca01bffSMircea Trofin       continue;
48aca01bffSMircea Trofin     const GlobalValue::GUID GUID = F.getGUID();
49aca01bffSMircea Trofin     F.setMetadata(GUIDMetadataName,
50aca01bffSMircea Trofin                   MDNode::get(M.getContext(),
51aca01bffSMircea Trofin                               {ConstantAsMetadata::get(ConstantInt::get(
52aca01bffSMircea Trofin                                   Type::getInt64Ty(M.getContext()), GUID))}));
53aca01bffSMircea Trofin   }
54aca01bffSMircea Trofin   return PreservedAnalyses::none();
55aca01bffSMircea Trofin }
56aca01bffSMircea Trofin 
57aca01bffSMircea Trofin GlobalValue::GUID AssignGUIDPass::getGUID(const Function &F) {
58aca01bffSMircea Trofin   if (F.isDeclaration()) {
59aca01bffSMircea Trofin     assert(GlobalValue::isExternalLinkage(F.getLinkage()));
60aca01bffSMircea Trofin     return GlobalValue::getGUID(F.getGlobalIdentifier());
61aca01bffSMircea Trofin   }
62aca01bffSMircea Trofin   auto *MD = F.getMetadata(GUIDMetadataName);
63aca01bffSMircea Trofin   assert(MD && "guid not found for defined function");
64aca01bffSMircea Trofin   return cast<ConstantInt>(cast<ConstantAsMetadata>(MD->getOperand(0))
65aca01bffSMircea Trofin                                ->getValue()
66aca01bffSMircea Trofin                                ->stripPointerCasts())
67aca01bffSMircea Trofin       ->getZExtValue();
68aca01bffSMircea Trofin }
69dbbf0762SMircea Trofin AnalysisKey CtxProfAnalysis::Key;
70dbbf0762SMircea Trofin 
7132097666SMircea Trofin CtxProfAnalysis::CtxProfAnalysis(std::optional<StringRef> Profile)
7232097666SMircea Trofin     : Profile([&]() -> std::optional<StringRef> {
7332097666SMircea Trofin         if (Profile)
7432097666SMircea Trofin           return *Profile;
7532097666SMircea Trofin         if (UseCtxProfile.getNumOccurrences())
7632097666SMircea Trofin           return UseCtxProfile;
7732097666SMircea Trofin         return std::nullopt;
7832097666SMircea Trofin       }()) {}
7950c876a4SMircea Trofin 
80aca01bffSMircea Trofin PGOContextualProfile CtxProfAnalysis::run(Module &M,
81dbbf0762SMircea Trofin                                           ModuleAnalysisManager &MAM) {
8232097666SMircea Trofin   if (!Profile)
8332097666SMircea Trofin     return {};
8432097666SMircea Trofin   ErrorOr<std::unique_ptr<MemoryBuffer>> MB = MemoryBuffer::getFile(*Profile);
85dbbf0762SMircea Trofin   if (auto EC = MB.getError()) {
86dbbf0762SMircea Trofin     M.getContext().emitError("could not open contextual profile file: " +
87dbbf0762SMircea Trofin                              EC.message());
88dbbf0762SMircea Trofin     return {};
89dbbf0762SMircea Trofin   }
90dbbf0762SMircea Trofin   PGOCtxProfileReader Reader(MB.get()->getBuffer());
91dbbf0762SMircea Trofin   auto MaybeCtx = Reader.loadContexts();
92dbbf0762SMircea Trofin   if (!MaybeCtx) {
93dbbf0762SMircea Trofin     M.getContext().emitError("contextual profile file is invalid: " +
94dbbf0762SMircea Trofin                              toString(MaybeCtx.takeError()));
95dbbf0762SMircea Trofin     return {};
96dbbf0762SMircea Trofin   }
97aca01bffSMircea Trofin 
986cb2d403SMircea Trofin   DenseSet<GlobalValue::GUID> ProfileRootsInModule;
996cb2d403SMircea Trofin   for (const auto &F : M)
1006cb2d403SMircea Trofin     if (!F.isDeclaration())
1016cb2d403SMircea Trofin       if (auto GUID = AssignGUIDPass::getGUID(F);
1026cb2d403SMircea Trofin           MaybeCtx->find(GUID) != MaybeCtx->end())
1036cb2d403SMircea Trofin         ProfileRootsInModule.insert(GUID);
1046cb2d403SMircea Trofin 
1056cb2d403SMircea Trofin   // Trim first the roots that aren't in this module.
1066cb2d403SMircea Trofin   for (auto &[RootGuid, _] : llvm::make_early_inc_range(*MaybeCtx))
1076cb2d403SMircea Trofin     if (!ProfileRootsInModule.contains(RootGuid))
1086cb2d403SMircea Trofin       MaybeCtx->erase(RootGuid);
1096cb2d403SMircea Trofin   // If none of the roots are in the module, we have no profile (for this
1106cb2d403SMircea Trofin   // module)
1116cb2d403SMircea Trofin   if (MaybeCtx->empty())
1126cb2d403SMircea Trofin     return {};
1136cb2d403SMircea Trofin 
1146cb2d403SMircea Trofin   // OK, so we have a valid profile and it's applicable to roots in this module.
115aca01bffSMircea Trofin   PGOContextualProfile Result;
116aca01bffSMircea Trofin 
117aca01bffSMircea Trofin   for (const auto &F : M) {
118aca01bffSMircea Trofin     if (F.isDeclaration())
119aca01bffSMircea Trofin       continue;
120aca01bffSMircea Trofin     auto GUID = AssignGUIDPass::getGUID(F);
121aca01bffSMircea Trofin     assert(GUID && "guid not found for defined function");
122aca01bffSMircea Trofin     const auto &Entry = F.begin();
123aca01bffSMircea Trofin     uint32_t MaxCounters = 0; // we expect at least a counter.
124aca01bffSMircea Trofin     for (const auto &I : *Entry)
125aca01bffSMircea Trofin       if (auto *C = dyn_cast<InstrProfIncrementInst>(&I)) {
126aca01bffSMircea Trofin         MaxCounters =
127aca01bffSMircea Trofin             static_cast<uint32_t>(C->getNumCounters()->getZExtValue());
128aca01bffSMircea Trofin         break;
129aca01bffSMircea Trofin       }
130aca01bffSMircea Trofin     if (!MaxCounters)
131aca01bffSMircea Trofin       continue;
132aca01bffSMircea Trofin     uint32_t MaxCallsites = 0;
133aca01bffSMircea Trofin     for (const auto &BB : F)
134aca01bffSMircea Trofin       for (const auto &I : BB)
135aca01bffSMircea Trofin         if (auto *C = dyn_cast<InstrProfCallsite>(&I)) {
136aca01bffSMircea Trofin           MaxCallsites =
137aca01bffSMircea Trofin               static_cast<uint32_t>(C->getNumCounters()->getZExtValue());
138aca01bffSMircea Trofin           break;
139aca01bffSMircea Trofin         }
140aca01bffSMircea Trofin     auto [It, Ins] = Result.FuncInfo.insert(
141aca01bffSMircea Trofin         {GUID, PGOContextualProfile::FunctionInfo(F.getName())});
142aca01bffSMircea Trofin     (void)Ins;
143aca01bffSMircea Trofin     assert(Ins);
144aca01bffSMircea Trofin     It->second.NextCallsiteIndex = MaxCallsites;
145aca01bffSMircea Trofin     It->second.NextCounterIndex = MaxCounters;
146aca01bffSMircea Trofin   }
147aca01bffSMircea Trofin   // If we made it this far, the Result is valid - which we mark by setting
148aca01bffSMircea Trofin   // .Profiles.
149aca01bffSMircea Trofin   Result.Profiles = std::move(*MaybeCtx);
150c4952e51SMircea Trofin   Result.initIndex();
151aca01bffSMircea Trofin   return Result;
152aca01bffSMircea Trofin }
153aca01bffSMircea Trofin 
154aca01bffSMircea Trofin GlobalValue::GUID
155aca01bffSMircea Trofin PGOContextualProfile::getDefinedFunctionGUID(const Function &F) const {
156aca01bffSMircea Trofin   if (auto It = FuncInfo.find(AssignGUIDPass::getGUID(F)); It != FuncInfo.end())
157aca01bffSMircea Trofin     return It->first;
158aca01bffSMircea Trofin   return 0;
159dbbf0762SMircea Trofin }
160dbbf0762SMircea Trofin 
16132097666SMircea Trofin CtxProfAnalysisPrinterPass::CtxProfAnalysisPrinterPass(raw_ostream &OS)
16232097666SMircea Trofin     : OS(OS), Mode(PrintLevel) {}
16332097666SMircea Trofin 
164dbbf0762SMircea Trofin PreservedAnalyses CtxProfAnalysisPrinterPass::run(Module &M,
165dbbf0762SMircea Trofin                                                   ModuleAnalysisManager &MAM) {
166dbbf0762SMircea Trofin   CtxProfAnalysis::Result &C = MAM.getResult<CtxProfAnalysis>(M);
167dbbf0762SMircea Trofin   if (!C) {
16832097666SMircea Trofin     OS << "No contextual profile was provided.\n";
169dbbf0762SMircea Trofin     return PreservedAnalyses::all();
170dbbf0762SMircea Trofin   }
171aca01bffSMircea Trofin 
17273c3b733SMircea Trofin   if (Mode == PrintMode::Everything) {
173aca01bffSMircea Trofin     OS << "Function Info:\n";
174aca01bffSMircea Trofin     for (const auto &[Guid, FuncInfo] : C.FuncInfo)
175aca01bffSMircea Trofin       OS << Guid << " : " << FuncInfo.Name
176aca01bffSMircea Trofin          << ". MaxCounterID: " << FuncInfo.NextCounterIndex
177aca01bffSMircea Trofin          << ". MaxCallsiteID: " << FuncInfo.NextCallsiteIndex << "\n";
17873c3b733SMircea Trofin   }
179aca01bffSMircea Trofin 
18073c3b733SMircea Trofin   if (Mode == PrintMode::Everything)
181aca01bffSMircea Trofin     OS << "\nCurrent Profile:\n";
182*b15845c0SMircea Trofin   convertCtxProfToYaml(OS, C.profiles());
183*b15845c0SMircea Trofin   OS << "\n";
184*b15845c0SMircea Trofin   if (Mode == PrintMode::YAML)
18573c3b733SMircea Trofin     return PreservedAnalyses::all();
18673c3b733SMircea Trofin 
18722d3fb18SMircea Trofin   OS << "\nFlat Profile:\n";
18822d3fb18SMircea Trofin   auto Flat = C.flatten();
18922d3fb18SMircea Trofin   for (const auto &[Guid, Counters] : Flat) {
19022d3fb18SMircea Trofin     OS << Guid << " : ";
19122d3fb18SMircea Trofin     for (auto V : Counters)
19222d3fb18SMircea Trofin       OS << V << " ";
19322d3fb18SMircea Trofin     OS << "\n";
19422d3fb18SMircea Trofin   }
195dbbf0762SMircea Trofin   return PreservedAnalyses::all();
196dbbf0762SMircea Trofin }
197c8a678b1SMircea Trofin 
198c8a678b1SMircea Trofin InstrProfCallsite *CtxProfAnalysis::getCallsiteInstrumentation(CallBase &CB) {
199ee5709b3SMircea Trofin   if (!InstrProfCallsite::canInstrumentCallsite(CB))
200ee5709b3SMircea Trofin     return nullptr;
201ee5709b3SMircea Trofin   for (auto *Prev = CB.getPrevNode(); Prev; Prev = Prev->getPrevNode()) {
202c8a678b1SMircea Trofin     if (auto *IPC = dyn_cast<InstrProfCallsite>(Prev))
203c8a678b1SMircea Trofin       return IPC;
204ee5709b3SMircea Trofin     assert(!isa<CallBase>(Prev) &&
205ee5709b3SMircea Trofin            "didn't expect to find another call, that's not the callsite "
206ee5709b3SMircea Trofin            "instrumentation, before an instrumentable callsite");
207ee5709b3SMircea Trofin   }
208c8a678b1SMircea Trofin   return nullptr;
209c8a678b1SMircea Trofin }
21022d3fb18SMircea Trofin 
2111e70122cSMircea Trofin InstrProfIncrementInst *CtxProfAnalysis::getBBInstrumentation(BasicBlock &BB) {
2121e70122cSMircea Trofin   for (auto &I : BB)
2131e70122cSMircea Trofin     if (auto *Incr = dyn_cast<InstrProfIncrementInst>(&I))
214ee5709b3SMircea Trofin       if (!isa<InstrProfIncrementInstStep>(&I))
2151e70122cSMircea Trofin         return Incr;
2161e70122cSMircea Trofin   return nullptr;
2171e70122cSMircea Trofin }
2181e70122cSMircea Trofin 
219783bac7fSMircea Trofin InstrProfIncrementInstStep *
220783bac7fSMircea Trofin CtxProfAnalysis::getSelectInstrumentation(SelectInst &SI) {
221783bac7fSMircea Trofin   Instruction *Prev = &SI;
222783bac7fSMircea Trofin   while ((Prev = Prev->getPrevNode()))
223783bac7fSMircea Trofin     if (auto *Step = dyn_cast<InstrProfIncrementInstStep>(Prev))
224783bac7fSMircea Trofin       return Step;
225783bac7fSMircea Trofin   return nullptr;
226783bac7fSMircea Trofin }
227783bac7fSMircea Trofin 
22873c3b733SMircea Trofin template <class ProfilesTy, class ProfTy>
22973c3b733SMircea Trofin static void preorderVisit(ProfilesTy &Profiles,
230c4952e51SMircea Trofin                           function_ref<void(ProfTy &)> Visitor) {
23173c3b733SMircea Trofin   std::function<void(ProfTy &)> Traverser = [&](auto &Ctx) {
23222d3fb18SMircea Trofin     Visitor(Ctx);
23373c3b733SMircea Trofin     for (auto &[_, SubCtxSet] : Ctx.callsites())
23473c3b733SMircea Trofin       for (auto &[__, Subctx] : SubCtxSet)
23522d3fb18SMircea Trofin         Traverser(Subctx);
23622d3fb18SMircea Trofin   };
23773c3b733SMircea Trofin   for (auto &[_, P] : Profiles)
23822d3fb18SMircea Trofin     Traverser(P);
23922d3fb18SMircea Trofin }
24022d3fb18SMircea Trofin 
241c4952e51SMircea Trofin void PGOContextualProfile::initIndex() {
242c4952e51SMircea Trofin   // Initialize the head of the index list for each function. We don't need it
243c4952e51SMircea Trofin   // after this point.
244c4952e51SMircea Trofin   DenseMap<GlobalValue::GUID, PGOCtxProfContext *> InsertionPoints;
245c4952e51SMircea Trofin   for (auto &[Guid, FI] : FuncInfo)
246c4952e51SMircea Trofin     InsertionPoints[Guid] = &FI.Index;
24773c3b733SMircea Trofin   preorderVisit<PGOCtxProfContext::CallTargetMapTy, PGOCtxProfContext>(
248c4952e51SMircea Trofin       *Profiles, [&](PGOCtxProfContext &Ctx) {
249c4952e51SMircea Trofin         auto InsertIt = InsertionPoints.find(Ctx.guid());
250c4952e51SMircea Trofin         if (InsertIt == InsertionPoints.end())
251c4952e51SMircea Trofin           return;
252c4952e51SMircea Trofin         // Insert at the end of the list. Since we traverse in preorder, it
253c4952e51SMircea Trofin         // means that when we iterate the list from the beginning, we'd
254c4952e51SMircea Trofin         // encounter the contexts in the order we would have, should we have
255c4952e51SMircea Trofin         // performed a full preorder traversal.
256c4952e51SMircea Trofin         InsertIt->second->Next = &Ctx;
257c4952e51SMircea Trofin         Ctx.Previous = InsertIt->second;
258c4952e51SMircea Trofin         InsertIt->second = &Ctx;
259c4952e51SMircea Trofin       });
260c4952e51SMircea Trofin }
261c4952e51SMircea Trofin 
262c4952e51SMircea Trofin void PGOContextualProfile::update(Visitor V, const Function &F) {
263c4952e51SMircea Trofin   assert(isFunctionKnown(F));
264c4952e51SMircea Trofin   GlobalValue::GUID G = getDefinedFunctionGUID(F);
265c4952e51SMircea Trofin   for (auto *Node = FuncInfo.find(G)->second.Index.Next; Node;
266c4952e51SMircea Trofin        Node = Node->Next)
267c4952e51SMircea Trofin     V(*reinterpret_cast<PGOCtxProfContext *>(Node));
26873c3b733SMircea Trofin }
26973c3b733SMircea Trofin 
27073c3b733SMircea Trofin void PGOContextualProfile::visit(ConstVisitor V, const Function *F) const {
271c4952e51SMircea Trofin   if (!F)
272c4952e51SMircea Trofin     return preorderVisit<const PGOCtxProfContext::CallTargetMapTy,
273c4952e51SMircea Trofin                          const PGOCtxProfContext>(*Profiles, V);
274c4952e51SMircea Trofin   assert(isFunctionKnown(*F));
275c4952e51SMircea Trofin   GlobalValue::GUID G = getDefinedFunctionGUID(*F);
276c4952e51SMircea Trofin   for (const auto *Node = FuncInfo.find(G)->second.Index.Next; Node;
277c4952e51SMircea Trofin        Node = Node->Next)
278c4952e51SMircea Trofin     V(*reinterpret_cast<const PGOCtxProfContext *>(Node));
27973c3b733SMircea Trofin }
28073c3b733SMircea Trofin 
28122d3fb18SMircea Trofin const CtxProfFlatProfile PGOContextualProfile::flatten() const {
28222d3fb18SMircea Trofin   assert(Profiles.has_value());
28322d3fb18SMircea Trofin   CtxProfFlatProfile Flat;
28473c3b733SMircea Trofin   preorderVisit<const PGOCtxProfContext::CallTargetMapTy,
28573c3b733SMircea Trofin                 const PGOCtxProfContext>(
28673c3b733SMircea Trofin       *Profiles, [&](const PGOCtxProfContext &Ctx) {
28722d3fb18SMircea Trofin         auto [It, Ins] = Flat.insert({Ctx.guid(), {}});
28822d3fb18SMircea Trofin         if (Ins) {
28922d3fb18SMircea Trofin           llvm::append_range(It->second, Ctx.counters());
29022d3fb18SMircea Trofin           return;
29122d3fb18SMircea Trofin         }
29222d3fb18SMircea Trofin         assert(It->second.size() == Ctx.counters().size() &&
29322d3fb18SMircea Trofin                "All contexts corresponding to a function should have the exact "
29422d3fb18SMircea Trofin                "same number of counters.");
29522d3fb18SMircea Trofin         for (size_t I = 0, E = It->second.size(); I < E; ++I)
29622d3fb18SMircea Trofin           It->second[I] += Ctx.counters()[I];
29722d3fb18SMircea Trofin       });
29822d3fb18SMircea Trofin   return Flat;
29922d3fb18SMircea Trofin }
300c8365feeSMircea Trofin 
301c8365feeSMircea Trofin void CtxProfAnalysis::collectIndirectCallPromotionList(
302c8365feeSMircea Trofin     CallBase &IC, Result &Profile,
303c8365feeSMircea Trofin     SetVector<std::pair<CallBase *, Function *>> &Candidates) {
304c8365feeSMircea Trofin   const auto *Instr = CtxProfAnalysis::getCallsiteInstrumentation(IC);
305c8365feeSMircea Trofin   if (!Instr)
306c8365feeSMircea Trofin     return;
307c8365feeSMircea Trofin   Module &M = *IC.getParent()->getModule();
308c8365feeSMircea Trofin   const uint32_t CallID = Instr->getIndex()->getZExtValue();
309c8365feeSMircea Trofin   Profile.visit(
310c8365feeSMircea Trofin       [&](const PGOCtxProfContext &Ctx) {
311c8365feeSMircea Trofin         const auto &Targets = Ctx.callsites().find(CallID);
312c8365feeSMircea Trofin         if (Targets == Ctx.callsites().end())
313c8365feeSMircea Trofin           return;
314c8365feeSMircea Trofin         for (const auto &[Guid, _] : Targets->second)
315c8365feeSMircea Trofin           if (auto Name = Profile.getFunctionName(Guid); !Name.empty())
316c8365feeSMircea Trofin             if (auto *Target = M.getFunction(Name))
317c8365feeSMircea Trofin               if (Target->hasFnAttribute(Attribute::AlwaysInline))
318c8365feeSMircea Trofin                 Candidates.insert({&IC, Target});
319c8365feeSMircea Trofin       },
320c8365feeSMircea Trofin       IC.getCaller());
321c8365feeSMircea Trofin }
322