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