Skip to content

Commit

Permalink
[ctx_prof] CtxProfAnalysis (llvm#102084)
Browse files Browse the repository at this point in the history
This is an immutable analysis that loads and makes the contextual profile available to other passes. This patch introduces the analysis and an analysis printer pass. Subsequent patches will introduce the APIs that IPO passes will call to modify the profile as result of their changes.
  • Loading branch information
mtrofin committed Aug 7, 2024
1 parent 5855237 commit dbbf076
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 1 deletion.
70 changes: 70 additions & 0 deletions llvm/include/llvm/Analysis/CtxProfAnalysis.h
Original file line number Diff line number Diff line change
@@ -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 <map>

namespace llvm {

class CtxProfAnalysis;

/// The instrumented contextual profile, produced by the CtxProfAnalysis.
class PGOContextualProfile {
std::optional<PGOCtxProfContext::CallTargetMapTy> 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<CtxProfAnalysis>();
return !PAC.preservedWhenStateless();
}
};

class CtxProfAnalysis : public AnalysisInfoMixin<CtxProfAnalysis> {
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<CtxProfAnalysisPrinterPass> {
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
1 change: 1 addition & 0 deletions llvm/lib/Analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ add_llvm_component_library(LLVMAnalysis
CostModel.cpp
CodeMetrics.cpp
ConstantFolding.cpp
CtxProfAnalysis.cpp
CycleAnalysis.cpp
DDG.cpp
DDGPrinter.cpp
Expand Down
95 changes: 95 additions & 0 deletions llvm/lib/Analysis/CtxProfAnalysis.cpp
Original file line number Diff line number Diff line change
@@ -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<std::unique_ptr<MemoryBuffer>> 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<CtxProfAnalysis>(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();
}
3 changes: 3 additions & 0 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -330,6 +331,8 @@ cl::opt<bool> PrintPipelinePasses(
"(best-effort only)."));
} // namespace llvm

extern cl::opt<std::string> UseCtxProfile;

AnalysisKey NoOpModuleAnalysis::Key;
AnalysisKey NoOpCGSCCAnalysis::Key;
AnalysisKey NoOpFunctionAnalysis::Key;
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Passes/PassBuilderPipelines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ static cl::opt<bool> UseLoopVersioningLICM(
"enable-loop-versioning-licm", cl::init(false), cl::Hidden,
cl::desc("Enable the experimental Loop Versioning LICM pass"));

static cl::opt<std::string>
cl::opt<std::string>
UseCtxProfile("use-ctx-profile", cl::init(""), cl::Hidden,
cl::desc("Use the specified contextual profile file"));

Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Passes/PassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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<ctx-prof-analysis>", CtxProfAnalysisPrinterPass(dbgs()))
MODULE_PASS("invalidate<all>", InvalidateAllAnalysesPass())
MODULE_PASS("iroutliner", IROutlinerPass())
MODULE_PASS("jmc-instrumenter", JMCInstrumenterPass())
Expand Down
56 changes: 56 additions & 0 deletions llvm/test/Analysis/CtxProfAnalysis/load.ll
Original file line number Diff line number Diff line change
@@ -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<ctx-prof-analysis>,print<ctx-prof-analysis>' \
; RUN: %t/empty.ll -S 2>&1 | FileCheck %s --check-prefix=NO-FILE

; RUN: not opt -passes='require<ctx-prof-analysis>,print<ctx-prof-analysis>' \
; RUN: -use-ctx-profile=does_not_exist.ctxprofdata %t/empty.ll -S 2>&1 | FileCheck %s --check-prefix=NO-FILE

; RUN: opt -passes='require<ctx-prof-analysis>,print<ctx-prof-analysis>' \
; 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

0 comments on commit dbbf076

Please sign in to comment.