Skip to content

Commit b15845c

Browse files
authored
[ctxprof] dump profiles using yaml (for testing) (#123108)
This is a follow-up from PR #122545, which enabled converting yaml to contextual profiles. This change uses the lower level yaml APIs because: - the mapping APIs `llvm::yaml` offers don't work with `const` values, because they (the APIs) want to enable both serialization and deserialization - building a helper data structure would be an alternative, but it'd be either memory-consuming or overly-complex design, given the recursive nature of the contextual profiles.
1 parent acf6072 commit b15845c

File tree

11 files changed

+175
-200
lines changed

11 files changed

+175
-200
lines changed

llvm/include/llvm/Analysis/CtxProfAnalysis.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class CtxProfAnalysis : public AnalysisInfoMixin<CtxProfAnalysis> {
140140
class CtxProfAnalysisPrinterPass
141141
: public PassInfoMixin<CtxProfAnalysisPrinterPass> {
142142
public:
143-
enum class PrintMode { Everything, JSON };
143+
enum class PrintMode { Everything, YAML };
144144
explicit CtxProfAnalysisPrinterPass(raw_ostream &OS);
145145

146146
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);

llvm/include/llvm/ProfileData/PGOCtxProfReader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,5 +183,8 @@ class PGOCtxProfileReader final {
183183

184184
Expected<std::map<GlobalValue::GUID, PGOCtxProfContext>> loadContexts();
185185
};
186+
187+
void convertCtxProfToYaml(raw_ostream &OS,
188+
const PGOCtxProfContext::CallTargetMapTy &);
186189
} // namespace llvm
187190
#endif

llvm/include/llvm/ProfileData/PGOCtxProfWriter.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,6 @@ class PGOCtxProfileWriter final {
8181
static constexpr StringRef ContainerMagic = "CTXP";
8282
};
8383

84-
/// Representation of the context node suitable for yaml / json serialization /
85-
/// deserialization.
86-
struct SerializableCtxRepresentation {
87-
ctx_profile::GUID Guid = 0;
88-
std::vector<uint64_t> Counters;
89-
std::vector<std::vector<SerializableCtxRepresentation>> Callsites;
90-
};
91-
9284
Error createCtxProfFromYAML(StringRef Profile, raw_ostream &Out);
9385
} // namespace llvm
9486
#endif

llvm/lib/Analysis/CtxProfAnalysis.cpp

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include "llvm/IR/PassManager.h"
2020
#include "llvm/ProfileData/PGOCtxProfReader.h"
2121
#include "llvm/Support/CommandLine.h"
22-
#include "llvm/Support/JSON.h"
2322
#include "llvm/Support/MemoryBuffer.h"
2423

2524
#define DEBUG_TYPE "ctx_prof"
@@ -31,49 +30,13 @@ cl::opt<std::string>
3130

3231
static cl::opt<CtxProfAnalysisPrinterPass::PrintMode> PrintLevel(
3332
"ctx-profile-printer-level",
34-
cl::init(CtxProfAnalysisPrinterPass::PrintMode::JSON), cl::Hidden,
33+
cl::init(CtxProfAnalysisPrinterPass::PrintMode::YAML), cl::Hidden,
3534
cl::values(clEnumValN(CtxProfAnalysisPrinterPass::PrintMode::Everything,
3635
"everything", "print everything - most verbose"),
37-
clEnumValN(CtxProfAnalysisPrinterPass::PrintMode::JSON, "json",
38-
"just the json representation of the profile")),
36+
clEnumValN(CtxProfAnalysisPrinterPass::PrintMode::YAML, "yaml",
37+
"just the yaml representation of the profile")),
3938
cl::desc("Verbosity level of the contextual profile printer pass."));
4039

41-
namespace llvm {
42-
namespace json {
43-
Value toJSON(const PGOCtxProfContext &P) {
44-
Object Ret;
45-
Ret["Guid"] = P.guid();
46-
Ret["Counters"] = Array(P.counters());
47-
if (P.callsites().empty())
48-
return Ret;
49-
auto AllCS =
50-
::llvm::map_range(P.callsites(), [](const auto &P) { return P.first; });
51-
auto MaxIt = ::llvm::max_element(AllCS);
52-
assert(MaxIt != AllCS.end() && "We should have a max value because the "
53-
"callsites collection is not empty.");
54-
Array CSites;
55-
// Iterate to, and including, the maximum index.
56-
for (auto I = 0U, Max = *MaxIt; I <= Max; ++I) {
57-
CSites.push_back(Array());
58-
Array &Targets = *CSites.back().getAsArray();
59-
if (P.hasCallsite(I))
60-
for (const auto &[_, Ctx] : P.callsite(I))
61-
Targets.push_back(toJSON(Ctx));
62-
}
63-
Ret["Callsites"] = std::move(CSites);
64-
65-
return Ret;
66-
}
67-
68-
Value toJSON(const PGOCtxProfContext::CallTargetMapTy &P) {
69-
Array Ret;
70-
for (const auto &[_, Ctx] : P)
71-
Ret.push_back(toJSON(Ctx));
72-
return Ret;
73-
}
74-
} // namespace json
75-
} // namespace llvm
76-
7740
const char *AssignGUIDPass::GUIDMetadataName = "guid";
7841

7942
PreservedAnalyses AssignGUIDPass::run(Module &M, ModuleAnalysisManager &MAM) {
@@ -214,15 +177,13 @@ PreservedAnalyses CtxProfAnalysisPrinterPass::run(Module &M,
214177
<< ". MaxCallsiteID: " << FuncInfo.NextCallsiteIndex << "\n";
215178
}
216179

217-
const auto JSONed = ::llvm::json::toJSON(C.profiles());
218-
219180
if (Mode == PrintMode::Everything)
220181
OS << "\nCurrent Profile:\n";
221-
OS << formatv("{0:2}", JSONed);
222-
if (Mode == PrintMode::JSON)
182+
convertCtxProfToYaml(OS, C.profiles());
183+
OS << "\n";
184+
if (Mode == PrintMode::YAML)
223185
return PreservedAnalyses::all();
224186

225-
OS << "\n";
226187
OS << "\nFlat Profile:\n";
227188
auto Flat = C.flatten();
228189
for (const auto &[Guid, Counters] : Flat) {

llvm/lib/ProfileData/PGOCtxProfReader.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
#include "llvm/ProfileData/InstrProf.h"
1818
#include "llvm/ProfileData/PGOCtxProfWriter.h"
1919
#include "llvm/Support/Error.h"
20+
#include "llvm/Support/ErrorHandling.h"
21+
#include "llvm/Support/YAMLTraits.h"
22+
#include <iterator>
23+
#include <utility>
2024

2125
using namespace llvm;
2226

@@ -176,3 +180,86 @@ PGOCtxProfileReader::loadContexts() {
176180
}
177181
return std::move(Ret);
178182
}
183+
184+
namespace {
185+
// We want to pass `const` values PGOCtxProfContext references to the yaml
186+
// converter, and the regular yaml mapping APIs are designed to handle both
187+
// serialization and deserialization, which prevents using const for
188+
// serialization. Using an intermediate datastructure is overkill, both
189+
// space-wise and design complexity-wise. Instead, we use the lower-level APIs.
190+
void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx);
191+
192+
void toYaml(yaml::Output &Out,
193+
const PGOCtxProfContext::CallTargetMapTy &CallTargets) {
194+
Out.beginSequence();
195+
size_t Index = 0;
196+
void *SaveData = nullptr;
197+
for (const auto &[_, Ctx] : CallTargets) {
198+
Out.preflightElement(Index++, SaveData);
199+
toYaml(Out, Ctx);
200+
Out.postflightElement(nullptr);
201+
}
202+
Out.endSequence();
203+
}
204+
205+
void toYaml(yaml::Output &Out,
206+
const PGOCtxProfContext::CallsiteMapTy &Callsites) {
207+
auto AllCS = ::llvm::make_first_range(Callsites);
208+
auto MaxIt = ::llvm::max_element(AllCS);
209+
assert(MaxIt != AllCS.end() && "We should have a max value because the "
210+
"callsites collection is not empty.");
211+
void *SaveData = nullptr;
212+
Out.beginSequence();
213+
for (auto I = 0U; I <= *MaxIt; ++I) {
214+
Out.preflightElement(I, SaveData);
215+
auto It = Callsites.find(I);
216+
if (It == Callsites.end()) {
217+
// This will produce a `[ ]` sequence, which is what we want here.
218+
Out.beginFlowSequence();
219+
Out.endFlowSequence();
220+
} else {
221+
toYaml(Out, It->second);
222+
}
223+
Out.postflightElement(nullptr);
224+
}
225+
Out.endSequence();
226+
}
227+
228+
void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx) {
229+
yaml::EmptyContext Empty;
230+
Out.beginMapping();
231+
void *SaveInfo = nullptr;
232+
bool UseDefault = false;
233+
{
234+
Out.preflightKey("Guid", /*Required=*/true, /*SameAsDefault=*/false,
235+
UseDefault, SaveInfo);
236+
auto Guid = Ctx.guid();
237+
yaml::yamlize(Out, Guid, true, Empty);
238+
Out.postflightKey(nullptr);
239+
}
240+
{
241+
Out.preflightKey("Counters", true, false, UseDefault, SaveInfo);
242+
Out.beginFlowSequence();
243+
for (size_t I = 0U, E = Ctx.counters().size(); I < E; ++I) {
244+
Out.preflightFlowElement(I, SaveInfo);
245+
uint64_t V = Ctx.counters()[I];
246+
yaml::yamlize(Out, V, true, Empty);
247+
Out.postflightFlowElement(SaveInfo);
248+
}
249+
Out.endFlowSequence();
250+
Out.postflightKey(nullptr);
251+
}
252+
if (!Ctx.callsites().empty()) {
253+
Out.preflightKey("Callsites", true, false, UseDefault, SaveInfo);
254+
toYaml(Out, Ctx.callsites());
255+
Out.postflightKey(nullptr);
256+
}
257+
Out.endMapping();
258+
}
259+
} // namespace
260+
261+
void llvm::convertCtxProfToYaml(
262+
raw_ostream &OS, const PGOCtxProfContext::CallTargetMapTy &Profiles) {
263+
yaml::Output Out(OS);
264+
toYaml(Out, Profiles);
265+
}

llvm/lib/ProfileData/PGOCtxProfWriter.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
#include "llvm/Bitstream/BitCodeEnums.h"
1515
#include "llvm/ProfileData/CtxInstrContextNode.h"
1616
#include "llvm/Support/Error.h"
17-
#include "llvm/Support/JSON.h"
1817
#include "llvm/Support/MemoryBuffer.h"
1918
#include "llvm/Support/YAMLTraits.h"
2019
#include "llvm/Support/raw_ostream.h"
@@ -89,6 +88,15 @@ void PGOCtxProfileWriter::write(const ContextNode &RootNode) {
8988
}
9089

9190
namespace {
91+
92+
/// Representation of the context node suitable for yaml serialization /
93+
/// deserialization.
94+
struct SerializableCtxRepresentation {
95+
ctx_profile::GUID Guid = 0;
96+
std::vector<uint64_t> Counters;
97+
std::vector<std::vector<SerializableCtxRepresentation>> Callsites;
98+
};
99+
92100
ctx_profile::ContextNode *
93101
createNode(std::vector<std::unique_ptr<char[]>> &Nodes,
94102
const std::vector<SerializableCtxRepresentation> &DCList);

llvm/test/Analysis/CtxProfAnalysis/full-cycle.ll

Lines changed: 14 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -88,54 +88,20 @@ Function Info:
8888
10507721908651011566 : entrypoint. MaxCounterID: 1. MaxCallsiteID: 2
8989

9090
Current Profile:
91-
[
92-
{
93-
"Callsites": [
94-
[
95-
{
96-
"Callsites": [
97-
[
98-
{
99-
"Counters": [
100-
10,
101-
7
102-
],
103-
"Guid": 3087265239403591524
104-
}
105-
]
106-
],
107-
"Counters": [
108-
7
109-
],
110-
"Guid": 2072045998141807037
111-
}
112-
],
113-
[
114-
{
115-
"Callsites": [
116-
[
117-
{
118-
"Counters": [
119-
1,
120-
2
121-
],
122-
"Guid": 3087265239403591524
123-
}
124-
]
125-
],
126-
"Counters": [
127-
2
128-
],
129-
"Guid": 4197650231481825559
130-
}
131-
]
132-
],
133-
"Counters": [
134-
1
135-
],
136-
"Guid": 10507721908651011566
137-
}
138-
]
91+
92+
- Guid: 10507721908651011566
93+
Counters: [ 1 ]
94+
Callsites:
95+
- - Guid: 2072045998141807037
96+
Counters: [ 7 ]
97+
Callsites:
98+
- - Guid: 3087265239403591524
99+
Counters: [ 10, 7 ]
100+
- - Guid: 4197650231481825559
101+
Counters: [ 2 ]
102+
Callsites:
103+
- - Guid: 3087265239403591524
104+
Counters: [ 1, 2 ]
139105

140106
Flat Profile:
141107
2072045998141807037 : 7

llvm/test/Analysis/CtxProfAnalysis/inline.ll

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
; REQUIRES: x86_64-linux
12
; RUN: rm -rf %t
23
; RUN: split-file %s %t
34
; RUN: llvm-ctxprof-util fromYAML --input=%t/profile.yaml --output=%t/profile.ctxprofdata
45

56
; RUN: opt -passes='module-inline,print<ctx-prof-analysis>' -ctx-profile-printer-level=everything %t/module.ll -S \
6-
; RUN: -use-ctx-profile=%t/profile.ctxprofdata -ctx-profile-printer-level=json \
7-
; RUN: -o - 2> %t/profile-final.txt | FileCheck %s
8-
; RUN: %python %S/json_equals.py %t/profile-final.txt %t/expected.json
7+
; RUN: -use-ctx-profile=%t/profile.ctxprofdata -ctx-profile-printer-level=yaml \
8+
; RUN: -o - 2> %t/profile-final.yaml | FileCheck %s
9+
; RUN: diff %t/profile-final.yaml %t/expected.yaml
910

1011
; There are 2 calls to @a from @entrypoint. We only inline the one callsite
1112
; marked as alwaysinline, the rest are blocked (marked noinline). After the inline,
@@ -109,17 +110,16 @@ define i32 @b() !guid !2 {
109110
Callsites: -
110111
- Guid: 1002
111112
Counters: [500]
112-
;--- expected.json
113-
[
114-
{ "Guid": 1000,
115-
"Counters": [10, 2, 8, 100],
116-
"Callsites": [
117-
[],
118-
[ { "Guid": 1001,
119-
"Counters": [8, 500],
120-
"Callsites": [[{"Guid": 1002, "Counters": [500]}]]}
121-
],
122-
[{ "Guid": 1002, "Counters": [100]}]
123-
]
124-
}
125-
]
113+
;--- expected.yaml
114+
115+
- Guid: 1000
116+
Counters: [ 10, 2, 8, 100 ]
117+
Callsites:
118+
- [ ]
119+
- - Guid: 1001
120+
Counters: [ 8, 500 ]
121+
Callsites:
122+
- - Guid: 1002
123+
Counters: [ 500 ]
124+
- - Guid: 1002
125+
Counters: [ 100 ]

llvm/test/Analysis/CtxProfAnalysis/json_equals.py

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)