diff --git a/llvm/include/llvm/Analysis/CtxProfAnalysis.h b/llvm/include/llvm/Analysis/CtxProfAnalysis.h new file mode 100644 index 00000000000000..d77c81d03582e1 --- /dev/null +++ b/llvm/include/llvm/Analysis/CtxProfAnalysis.h @@ -0,0 +1,70 @@ +//===- CtxProfAnalysis.h - maintain contextual profile info -*- C++ ---*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +#ifndef LLVM_ANALYSIS_CTXPROFANALYSIS_H +#define LLVM_ANALYSIS_CTXPROFANALYSIS_H + +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/PassManager.h" +#include "llvm/ProfileData/PGOCtxProfReader.h" +#include + +namespace llvm { + +class CtxProfAnalysis; + +/// The instrumented contextual profile, produced by the CtxProfAnalysis. +class PGOContextualProfile { + std::optional Profiles; + +public: + explicit PGOContextualProfile(PGOCtxProfContext::CallTargetMapTy &&Profiles) + : Profiles(std::move(Profiles)) {} + PGOContextualProfile() = default; + PGOContextualProfile(const PGOContextualProfile &) = delete; + PGOContextualProfile(PGOContextualProfile &&) = default; + + operator bool() const { return Profiles.has_value(); } + + const PGOCtxProfContext::CallTargetMapTy &profiles() const { + return *Profiles; + } + + bool invalidate(Module &, const PreservedAnalyses &PA, + ModuleAnalysisManager::Invalidator &) { + // Check whether the analysis has been explicitly invalidated. Otherwise, + // it's stateless and remains preserved. + auto PAC = PA.getChecker(); + return !PAC.preservedWhenStateless(); + } +}; + +class CtxProfAnalysis : public AnalysisInfoMixin { + StringRef Profile; + +public: + static AnalysisKey Key; + explicit CtxProfAnalysis(StringRef Profile) : Profile(Profile) {}; + + using Result = PGOContextualProfile; + + PGOContextualProfile run(Module &M, ModuleAnalysisManager &MAM); +}; + +class CtxProfAnalysisPrinterPass + : public PassInfoMixin { + raw_ostream &OS; + +public: + explicit CtxProfAnalysisPrinterPass(raw_ostream &OS) : OS(OS) {} + + PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); + static bool isRequired() { return true; } +}; +} // namespace llvm +#endif // LLVM_ANALYSIS_CTXPROFANALYSIS_H diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt index 997bb7a0bb1785..2cb3547ec40473 100644 --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -46,6 +46,7 @@ add_llvm_component_library(LLVMAnalysis CostModel.cpp CodeMetrics.cpp ConstantFolding.cpp + CtxProfAnalysis.cpp CycleAnalysis.cpp DDG.cpp DDGPrinter.cpp diff --git a/llvm/lib/Analysis/CtxProfAnalysis.cpp b/llvm/lib/Analysis/CtxProfAnalysis.cpp new file mode 100644 index 00000000000000..f56f910bd77844 --- /dev/null +++ b/llvm/lib/Analysis/CtxProfAnalysis.cpp @@ -0,0 +1,95 @@ +//===- CtxProfAnalysis.cpp - contextual profile analysis ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implementation of the contextual profile analysis, which maintains contextual +// profiling info through IPO passes. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/CtxProfAnalysis.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/IR/Analysis.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/ProfileData/PGOCtxProfReader.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/MemoryBuffer.h" + +#define DEBUG_TYPE "ctx_prof" + +namespace llvm { +namespace json { +Value toJSON(const PGOCtxProfContext &P) { + Object Ret; + Ret["Guid"] = P.guid(); + Ret["Counters"] = Array(P.counters()); + if (P.callsites().empty()) + return Ret; + auto AllCS = + ::llvm::map_range(P.callsites(), [](const auto &P) { return P.first; }); + auto MaxIt = ::llvm::max_element(AllCS); + assert(MaxIt != AllCS.end() && "We should have a max value because the " + "callsites collection is not empty."); + Array CSites; + // Iterate to, and including, the maximum index. + for (auto I = 0U, Max = *MaxIt; I <= Max; ++I) { + CSites.push_back(Array()); + Array &Targets = *CSites.back().getAsArray(); + if (P.hasCallsite(I)) + for (const auto &[_, Ctx] : P.callsite(I)) + Targets.push_back(toJSON(Ctx)); + } + Ret["Callsites"] = std::move(CSites); + + return Ret; +} + +Value toJSON(const PGOCtxProfContext::CallTargetMapTy &P) { + Array Ret; + for (const auto &[_, Ctx] : P) + Ret.push_back(toJSON(Ctx)); + return Ret; +} +} // namespace json +} // namespace llvm + +using namespace llvm; + +AnalysisKey CtxProfAnalysis::Key; + +CtxProfAnalysis::Result CtxProfAnalysis::run(Module &M, + ModuleAnalysisManager &MAM) { + ErrorOr> MB = MemoryBuffer::getFile(Profile); + if (auto EC = MB.getError()) { + M.getContext().emitError("could not open contextual profile file: " + + EC.message()); + return {}; + } + PGOCtxProfileReader Reader(MB.get()->getBuffer()); + auto MaybeCtx = Reader.loadContexts(); + if (!MaybeCtx) { + M.getContext().emitError("contextual profile file is invalid: " + + toString(MaybeCtx.takeError())); + return {}; + } + return Result(std::move(*MaybeCtx)); +} + +PreservedAnalyses CtxProfAnalysisPrinterPass::run(Module &M, + ModuleAnalysisManager &MAM) { + CtxProfAnalysis::Result &C = MAM.getResult(M); + if (!C) { + M.getContext().emitError("Invalid CtxProfAnalysis"); + return PreservedAnalyses::all(); + } + const auto JSONed = ::llvm::json::toJSON(C.profiles()); + + OS << formatv("{0:2}", JSONed); + OS << "\n"; + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 5dbb1e2f498716..bcc69d5ac3db67 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -28,6 +28,7 @@ #include "llvm/Analysis/CallGraph.h" #include "llvm/Analysis/CallPrinter.h" #include "llvm/Analysis/CostModel.h" +#include "llvm/Analysis/CtxProfAnalysis.h" #include "llvm/Analysis/CycleAnalysis.h" #include "llvm/Analysis/DDG.h" #include "llvm/Analysis/DDGPrinter.h" @@ -330,6 +331,8 @@ cl::opt PrintPipelinePasses( "(best-effort only).")); } // namespace llvm +extern cl::opt UseCtxProfile; + AnalysisKey NoOpModuleAnalysis::Key; AnalysisKey NoOpCGSCCAnalysis::Key; AnalysisKey NoOpFunctionAnalysis::Key; diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index adebbb5eeba32b..c175ee89809849 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -304,7 +304,7 @@ static cl::opt UseLoopVersioningLICM( "enable-loop-versioning-licm", cl::init(false), cl::Hidden, cl::desc("Enable the experimental Loop Versioning LICM pass")); -static cl::opt +cl::opt UseCtxProfile("use-ctx-profile", cl::init(""), cl::Hidden, cl::desc("Use the specified contextual profile file")); diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 03c7d0c3c469a8..61a5bab92927fe 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -20,6 +20,7 @@ #endif MODULE_ANALYSIS("callgraph", CallGraphAnalysis()) MODULE_ANALYSIS("collector-metadata", CollectorMetadataAnalysis()) +MODULE_ANALYSIS("ctx-prof-analysis", CtxProfAnalysis(UseCtxProfile)) MODULE_ANALYSIS("inline-advisor", InlineAdvisorAnalysis()) MODULE_ANALYSIS("ir-similarity", IRSimilarityAnalysis()) MODULE_ANALYSIS("lcg", LazyCallGraphAnalysis()) @@ -79,6 +80,7 @@ MODULE_PASS("insert-gcov-profiling", GCOVProfilerPass()) MODULE_PASS("instrorderfile", InstrOrderFilePass()) MODULE_PASS("instrprof", InstrProfilingLoweringPass()) MODULE_PASS("ctx-instr-lower", PGOCtxProfLoweringPass()) +MODULE_PASS("print", CtxProfAnalysisPrinterPass(dbgs())) MODULE_PASS("invalidate", InvalidateAllAnalysesPass()) MODULE_PASS("iroutliner", IROutlinerPass()) MODULE_PASS("jmc-instrumenter", JMCInstrumenterPass()) diff --git a/llvm/test/Analysis/CtxProfAnalysis/load.ll b/llvm/test/Analysis/CtxProfAnalysis/load.ll new file mode 100644 index 00000000000000..fdf40d1b7f136f --- /dev/null +++ b/llvm/test/Analysis/CtxProfAnalysis/load.ll @@ -0,0 +1,56 @@ +; RUN: split-file %s %t +; RUN: llvm-ctxprof-util fromJSON --input=%t/profile.json --output=%t/profile.ctxprofdata +; RUN: not opt -passes='require,print' \ +; RUN: %t/empty.ll -S 2>&1 | FileCheck %s --check-prefix=NO-FILE + +; RUN: not opt -passes='require,print' \ +; RUN: -use-ctx-profile=does_not_exist.ctxprofdata %t/empty.ll -S 2>&1 | FileCheck %s --check-prefix=NO-FILE + +; RUN: opt -passes='require,print' \ +; RUN: -use-ctx-profile=%t/profile.ctxprofdata %t/empty.ll -S 2> %t/output.json +; RUN: diff %t/profile.json %t/output.json + +; NO-FILE: error: could not open contextual profile file +; +; This is the reference profile, laid out in the format the json formatter will +; output it from opt. +;--- profile.json +[ + { + "Callsites": [ + [], + [ + { + "Counters": [ + 4, + 5 + ], + "Guid": 2000 + }, + { + "Counters": [ + 6, + 7, + 8 + ], + "Guid": 18446744073709551613 + } + ] + ], + "Counters": [ + 1, + 2, + 3 + ], + "Guid": 1000 + }, + { + "Counters": [ + 5, + 9, + 10 + ], + "Guid": 18446744073709551612 + } +] +;--- empty.ll