LLVM 22.0.0git
PGOCtxProfReader.cpp
Go to the documentation of this file.
1//===- PGOCtxProfReader.cpp - Contextual Instrumentation profile reader ---===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Read a contextual profile into a datastructure suitable for maintenance
10// throughout IPO
11//
12//===----------------------------------------------------------------------===//
13
19#include "llvm/Support/Error.h"
22#include <utility>
23
24using namespace llvm;
25
26// FIXME(#92054) - these Error handling macros are (re-)invented in a few
27// places.
28#define EXPECT_OR_RET(LHS, RHS) \
29 auto LHS = RHS; \
30 if (!LHS) \
31 return LHS.takeError();
32
33#define RET_ON_ERR(EXPR) \
34 if (auto Err = (EXPR)) \
35 return Err;
36
38PGOCtxProfContext::getOrEmplace(uint32_t Index, GlobalValue::GUID G,
40 auto [Iter, Inserted] =
41 Callsites[Index].insert({G, PGOCtxProfContext(G, std::move(Counters))});
42 if (!Inserted)
43 return make_error<InstrProfError>(instrprof_error::invalid_prof,
44 "Duplicate GUID for same callsite.");
45 return Iter->second;
46}
47
48Expected<BitstreamEntry> PGOCtxProfileReader::advance() {
50}
51
52Error PGOCtxProfileReader::wrongValue(const Twine &Msg) {
53 return make_error<InstrProfError>(instrprof_error::invalid_prof, Msg);
54}
55
56Error PGOCtxProfileReader::unsupported(const Twine &Msg) {
57 return make_error<InstrProfError>(instrprof_error::unsupported_version, Msg);
58}
59
60bool PGOCtxProfileReader::tryGetNextKnownBlockID(PGOCtxProfileBlockIDs &ID) {
61 auto Blk = advance();
62 if (!Blk) {
63 consumeError(Blk.takeError());
64 return false;
65 }
66 if (Blk->Kind != BitstreamEntry::SubBlock)
67 return false;
69 PGOCtxProfileBlockIDs::LAST_VALID < Blk->ID)
70 return false;
71 ID = static_cast<PGOCtxProfileBlockIDs>(Blk->ID);
72 return true;
73}
74
75bool PGOCtxProfileReader::canEnterBlockWithID(PGOCtxProfileBlockIDs ID) {
77 return tryGetNextKnownBlockID(Test) && Test == ID;
78}
79
80Error PGOCtxProfileReader::enterBlockWithID(PGOCtxProfileBlockIDs ID) {
82 return Error::success();
83}
84
85// Note: we use PGOCtxProfContext for flat profiles also, as the latter are
86// structurally similar. Alternative modeling here seems a bit overkill at the
87// moment.
89PGOCtxProfileReader::readProfile(PGOCtxProfileBlockIDs Kind) {
93 "Unexpected profile kind");
94 RET_ON_ERR(enterBlockWithID(Kind));
95
96 std::optional<ctx_profile::GUID> Guid;
97 std::optional<SmallVector<uint64_t, 16>> Counters;
98 std::optional<uint32_t> CallsiteIndex;
99 std::optional<uint64_t> TotalEntryCount;
100 std::optional<CtxProfFlatProfile> Unhandled;
101 SmallVector<uint64_t, 1> RecordValues;
102
103 const bool ExpectIndex = Kind == PGOCtxProfileBlockIDs::ContextNodeBlockID;
105 // We don't prescribe the order in which the records come in, and we are ok
106 // if other unsupported records appear. We seek in the current subblock until
107 // we get all we know.
108 auto GotAllWeNeed = [&]() {
109 return Guid.has_value() && Counters.has_value() &&
110 (!ExpectIndex || CallsiteIndex.has_value()) &&
111 (!IsRoot || TotalEntryCount.has_value()) &&
112 (!IsRoot || Unhandled.has_value());
113 };
114
115 while (!GotAllWeNeed()) {
116 RecordValues.clear();
117 EXPECT_OR_RET(Entry, advance());
118 if (Entry->Kind != BitstreamEntry::Record) {
119 if (IsRoot && Entry->Kind == BitstreamEntry::SubBlock &&
122 Unhandled = CtxProfFlatProfile();
123 RET_ON_ERR(loadFlatProfileList(*Unhandled));
124 continue;
125 }
126 return wrongValue(
127 "Expected records before encountering more subcontexts");
128 }
129 EXPECT_OR_RET(ReadRecord,
130 Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues));
131 switch (*ReadRecord) {
133 if (RecordValues.size() != 1)
134 return wrongValue("The GUID record should have exactly one value");
135 Guid = RecordValues[0];
136 break;
138 Counters = std::move(RecordValues);
139 if (Counters->empty())
140 return wrongValue("Empty counters. At least the entry counter (one "
141 "value) was expected");
142 break;
144 if (!ExpectIndex)
145 return wrongValue("The root context should not have a callee index");
146 if (RecordValues.size() != 1)
147 return wrongValue("The callee index should have exactly one value");
148 CallsiteIndex = RecordValues[0];
149 break;
151 if (!IsRoot)
152 return wrongValue("Non-root has a total entry count record");
153 if (RecordValues.size() != 1)
154 return wrongValue(
155 "The root total entry count record should have exactly one value");
156 TotalEntryCount = RecordValues[0];
157 break;
158 default:
159 // OK if we see records we do not understand, like records (profile
160 // components) introduced later.
161 break;
162 }
163 }
164
165 PGOCtxProfContext Ret(*Guid, std::move(*Counters), TotalEntryCount,
166 std::move(Unhandled));
167
168 while (canEnterBlockWithID(PGOCtxProfileBlockIDs::ContextNodeBlockID)) {
170 auto &Targets = Ret.callsites()[*SC->first];
171 auto [_, Inserted] =
172 Targets.insert({SC->second.guid(), std::move(SC->second)});
173 if (!Inserted)
174 return wrongValue(
175 "Unexpected duplicate target (callee) at the same callsite.");
176 }
177 return std::make_pair(CallsiteIndex, std::move(Ret));
178}
179
180Error PGOCtxProfileReader::readMetadata() {
183 return make_error<InstrProfError>(instrprof_error::invalid_prof,
184 "Invalid magic");
185
187 RET_ON_ERR(Cursor.advance().moveInto(Entry));
188 if (Entry.Kind != BitstreamEntry::SubBlock ||
190 return unsupported("Expected Block ID");
191 // We don't need the blockinfo to read the rest, it's metadata usable for e.g.
192 // llvm-bcanalyzer.
193 RET_ON_ERR(Cursor.SkipBlock());
194
195 EXPECT_OR_RET(Blk, advance());
196 if (Blk->Kind != BitstreamEntry::SubBlock)
197 return unsupported("Expected Version record");
200 EXPECT_OR_RET(MData, advance());
201 if (MData->Kind != BitstreamEntry::Record)
202 return unsupported("Expected Version record");
203
207 return unsupported("Expected Version record");
208 if (Ver.size() != 1 || Ver[0] > PGOCtxProfileWriter::CurrentVersion)
209 return unsupported("Version " + Twine(*Code) +
210 " is higher than supported version " +
212 return Error::success();
213}
214
215Error PGOCtxProfileReader::loadContexts(CtxProfContextualProfiles &P) {
217 while (canEnterBlockWithID(PGOCtxProfileBlockIDs::ContextRootBlockID)) {
219 auto Key = E->second.guid();
220 if (!P.insert({Key, std::move(E->second)}).second)
221 return wrongValue("Duplicate roots");
222 }
223 return Error::success();
224}
225
226Error PGOCtxProfileReader::loadFlatProfileList(CtxProfFlatProfile &P) {
227 while (canEnterBlockWithID(PGOCtxProfileBlockIDs::FlatProfileBlockID)) {
229 auto Guid = E->second.guid();
230 if (!P.insert({Guid, std::move(E->second.counters())}).second)
231 return wrongValue("Duplicate flat profile entries");
232 }
233 return Error::success();
234}
235
236Error PGOCtxProfileReader::loadFlatProfiles(CtxProfFlatProfile &P) {
239 return loadFlatProfileList(P);
240}
241
243 RET_ON_ERR(readMetadata());
244 PGOCtxProfile Ret;
246 for (auto I = 0; I < 2; ++I) {
247 if (!tryGetNextKnownBlockID(Test))
248 break;
250 RET_ON_ERR(loadContexts(Ret.Contexts));
252 RET_ON_ERR(loadFlatProfiles(Ret.FlatProfiles));
253 } else {
254 return wrongValue("Unexpected section");
255 }
256 }
257
258 return std::move(Ret);
259}
260
261namespace {
262// We want to pass `const` values PGOCtxProfContext references to the yaml
263// converter, and the regular yaml mapping APIs are designed to handle both
264// serialization and deserialization, which prevents using const for
265// serialization. Using an intermediate datastructure is overkill, both
266// space-wise and design complexity-wise. Instead, we use the lower-level APIs.
267void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx);
268
269void toYaml(yaml::Output &Out,
270 const PGOCtxProfContext::CallTargetMapTy &CallTargets) {
271 Out.beginSequence();
272 size_t Index = 0;
273 void *SaveData = nullptr;
274 for (const auto &[_, Ctx] : CallTargets) {
275 Out.preflightElement(Index++, SaveData);
276 toYaml(Out, Ctx);
277 Out.postflightElement(nullptr);
278 }
279 Out.endSequence();
280}
281
282void toYaml(yaml::Output &Out,
283 const PGOCtxProfContext::CallsiteMapTy &Callsites) {
284 auto AllCS = ::llvm::make_first_range(Callsites);
285 auto MaxIt = ::llvm::max_element(AllCS);
286 assert(MaxIt != AllCS.end() && "We should have a max value because the "
287 "callsites collection is not empty.");
288 void *SaveData = nullptr;
289 Out.beginSequence();
290 for (auto I = 0U; I <= *MaxIt; ++I) {
291 Out.preflightElement(I, SaveData);
292 auto It = Callsites.find(I);
293 if (It == Callsites.end()) {
294 // This will produce a `[ ]` sequence, which is what we want here.
295 Out.beginFlowSequence();
296 Out.endFlowSequence();
297 } else {
298 toYaml(Out, It->second);
299 }
300 Out.postflightElement(nullptr);
301 }
302 Out.endSequence();
303}
304
305void toYaml(yaml::Output &Out, const CtxProfFlatProfile &Flat);
306
307void toYaml(yaml::Output &Out, GlobalValue::GUID Guid,
309 const PGOCtxProfContext::CallsiteMapTy &Callsites,
310 std::optional<uint64_t> TotalRootEntryCount = std::nullopt,
311 CtxProfFlatProfile Unhandled = {}) {
312 yaml::EmptyContext Empty;
313 Out.beginMapping();
314 void *SaveInfo = nullptr;
315 bool UseDefault = false;
316 {
317 Out.preflightKey("Guid", /*Required=*/true, /*SameAsDefault=*/false,
318 UseDefault, SaveInfo);
319 yaml::yamlize(Out, Guid, true, Empty);
320 Out.postflightKey(nullptr);
321 }
323 Out.preflightKey("TotalRootEntryCount", true, false, UseDefault, SaveInfo);
324 yaml::yamlize(Out, *TotalRootEntryCount, true, Empty);
325 Out.postflightKey(nullptr);
326 }
327 {
328 Out.preflightKey("Counters", true, false, UseDefault, SaveInfo);
329 Out.beginFlowSequence();
330 for (size_t I = 0U, E = Counters.size(); I < E; ++I) {
331 Out.preflightFlowElement(I, SaveInfo);
332 uint64_t V = Counters[I];
333 yaml::yamlize(Out, V, true, Empty);
334 Out.postflightFlowElement(SaveInfo);
335 }
336 Out.endFlowSequence();
337 Out.postflightKey(nullptr);
338 }
339
340 if (!Unhandled.empty()) {
341 assert(TotalRootEntryCount.has_value());
342 Out.preflightKey("Unhandled", false, false, UseDefault, SaveInfo);
343 toYaml(Out, Unhandled);
344 Out.postflightKey(nullptr);
345 }
346
347 if (!Callsites.empty()) {
348 Out.preflightKey("Callsites", true, false, UseDefault, SaveInfo);
349 toYaml(Out, Callsites);
350 Out.postflightKey(nullptr);
351 }
352 Out.endMapping();
353}
354
355void toYaml(yaml::Output &Out, const CtxProfFlatProfile &Flat) {
356 void *SaveInfo = nullptr;
357 Out.beginSequence();
358 size_t ElemID = 0;
359 for (const auto &[Guid, Counters] : Flat) {
360 Out.preflightElement(ElemID++, SaveInfo);
361 toYaml(Out, Guid, Counters, {});
362 Out.postflightElement(nullptr);
363 }
364 Out.endSequence();
365}
366
367void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx) {
368 if (Ctx.isRoot())
369 toYaml(Out, Ctx.guid(), Ctx.counters(), Ctx.callsites(),
371 else
372 toYaml(Out, Ctx.guid(), Ctx.counters(), Ctx.callsites());
373}
374
375} // namespace
376
378 yaml::Output Out(OS);
379 void *SaveInfo = nullptr;
380 bool UseDefault = false;
381 Out.beginMapping();
382 if (!Profile.Contexts.empty()) {
383 Out.preflightKey("Contexts", false, false, UseDefault, SaveInfo);
384 toYaml(Out, Profile.Contexts);
385 Out.postflightKey(nullptr);
386 }
387 if (!Profile.FlatProfiles.empty()) {
388 Out.preflightKey("FlatProfiles", false, false, UseDefault, SaveInfo);
389 toYaml(Out, Profile.FlatProfiles);
390 Out.postflightKey(nullptr);
391 }
392 Out.endMapping();
393}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
#define _
#define I(x, y, z)
Definition: MD5.cpp:58
#define G(x, y, z)
Definition: MD5.cpp:56
Load MIR Sample Profile
#define P(N)
#define RET_ON_ERR(EXPR)
#define EXPECT_OR_RET(LHS, RHS)
Reader for contextual iFDO profile, which comes in bitstream format.
raw_pwrite_stream & OS
@ AF_DontAutoprocessAbbrevs
If this flag is used, abbrev entries are returned just like normal records.
Expected< BitstreamEntry > advance(unsigned Flags=0)
Advance the current bitstream, returning the next entry in the stream.
LLVM_ABI Expected< unsigned > readRecord(unsigned AbbrevID, SmallVectorImpl< uint64_t > &Vals, StringRef *Blob=nullptr)
LLVM_ABI Error EnterSubBlock(unsigned BlockID, unsigned *NumWordsP=nullptr)
Having read the ENTER_SUBBLOCK abbrevid, and enter the block.
Error SkipBlock()
Having read the ENTER_SUBBLOCK abbrevid and a BlockID, skip over the body of this block.
Lightweight error class with error context and mandatory checking.
Definition: Error.h:159
static ErrorSuccess success()
Create a success value.
Definition: Error.h:336
Tagged union holding either a T or a Error.
Definition: Error.h:485
A node (context) in the loaded contextual profile, suitable for mutation during IPO passes.
GlobalValue::GUID guid() const
const SmallVectorImpl< uint64_t > & counters() const
std::map< uint32_t, CallTargetMapTy > CallsiteMapTy
const CtxProfFlatProfile & getUnhandled() const
uint64_t getTotalRootEntryCount() const
std::map< GlobalValue::GUID, PGOCtxProfContext > CallTargetMapTy
const CallsiteMapTy & callsites() const
LLVM_ABI Expected< PGOCtxProfile > loadProfiles()
static constexpr uint32_t CurrentVersion
static constexpr StringRef ContainerMagic
size_t size() const
Definition: SmallVector.h:79
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:574
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1197
constexpr size_t size() const
size - Get the string size.
Definition: StringRef.h:154
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:82
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:53
Key
PAL metadata keys.
@ Entry
Definition: COFF.h:862
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
@ BLOCKINFO_BLOCK_ID
BLOCKINFO_BLOCK is used to define metadata about blocks, for example, standard abbrevs that should be...
Definition: BitCodeEnums.h:69
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
std::map< GlobalValue::GUID, PGOCtxProfContext > CtxProfContextualProfiles
std::map< GlobalValue::GUID, SmallVector< uint64_t, 1 > > CtxProfFlatProfile
auto make_first_range(ContainerTy &&c)
Given a container of pairs, return a range over the first elements.
Definition: STLExtras.h:1444
auto max_element(R &&Range)
Provide wrappers to std::max_element which take ranges instead of having to pass begin/end explicitly...
Definition: STLExtras.h:2049
LLVM_ABI void convertCtxProfToYaml(raw_ostream &OS, const PGOCtxProfile &Profile)
void consumeError(Error Err)
Consume a Error without doing anything.
Definition: Error.h:1083
PGOCtxProfileBlockIDs
@ ContextNodeBlockID
@ ContextsSectionBlockID
@ ContextRootBlockID
@ FlatProfilesSectionBlockID
@ UnhandledBlockID
@ FlatProfileBlockID
@ ProfileMetadataBlockID
@ CallsiteIndex
@ TotalRootEntryCount
When advancing through a bitstream cursor, each advance can discover a few different kinds of entries...