LLVM 20.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 <iterator>
23#include <utility>
24
25using namespace llvm;
26
27// FIXME(#92054) - these Error handling macros are (re-)invented in a few
28// places.
29#define EXPECT_OR_RET(LHS, RHS) \
30 auto LHS = RHS; \
31 if (!LHS) \
32 return LHS.takeError();
33
34#define RET_ON_ERR(EXPR) \
35 if (auto Err = (EXPR)) \
36 return Err;
37
39PGOCtxProfContext::getOrEmplace(uint32_t Index, GlobalValue::GUID G,
41 auto [Iter, Inserted] =
42 Callsites[Index].insert({G, PGOCtxProfContext(G, std::move(Counters))});
43 if (!Inserted)
44 return make_error<InstrProfError>(instrprof_error::invalid_prof,
45 "Duplicate GUID for same callsite.");
46 return Iter->second;
47}
48
49Expected<BitstreamEntry> PGOCtxProfileReader::advance() {
51}
52
53Error PGOCtxProfileReader::wrongValue(const Twine &Msg) {
54 return make_error<InstrProfError>(instrprof_error::invalid_prof, Msg);
55}
56
57Error PGOCtxProfileReader::unsupported(const Twine &Msg) {
58 return make_error<InstrProfError>(instrprof_error::unsupported_version, Msg);
59}
60
61bool PGOCtxProfileReader::canReadContext() {
62 auto Blk = advance();
63 if (!Blk) {
64 consumeError(Blk.takeError());
65 return false;
66 }
67 return Blk->Kind == BitstreamEntry::SubBlock &&
69}
70
72PGOCtxProfileReader::readContext(bool ExpectIndex) {
74
75 std::optional<ctx_profile::GUID> Guid;
76 std::optional<SmallVector<uint64_t, 16>> Counters;
77 std::optional<uint32_t> CallsiteIndex;
78
79 SmallVector<uint64_t, 1> RecordValues;
80
81 // We don't prescribe the order in which the records come in, and we are ok
82 // if other unsupported records appear. We seek in the current subblock until
83 // we get all we know.
84 auto GotAllWeNeed = [&]() {
85 return Guid.has_value() && Counters.has_value() &&
86 (!ExpectIndex || CallsiteIndex.has_value());
87 };
88 while (!GotAllWeNeed()) {
89 RecordValues.clear();
90 EXPECT_OR_RET(Entry, advance());
91 if (Entry->Kind != BitstreamEntry::Record)
92 return wrongValue(
93 "Expected records before encountering more subcontexts");
94 EXPECT_OR_RET(ReadRecord,
95 Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues));
96 switch (*ReadRecord) {
98 if (RecordValues.size() != 1)
99 return wrongValue("The GUID record should have exactly one value");
100 Guid = RecordValues[0];
101 break;
103 Counters = std::move(RecordValues);
104 if (Counters->empty())
105 return wrongValue("Empty counters. At least the entry counter (one "
106 "value) was expected");
107 break;
109 if (!ExpectIndex)
110 return wrongValue("The root context should not have a callee index");
111 if (RecordValues.size() != 1)
112 return wrongValue("The callee index should have exactly one value");
113 CallsiteIndex = RecordValues[0];
114 break;
115 default:
116 // OK if we see records we do not understand, like records (profile
117 // components) introduced later.
118 break;
119 }
120 }
121
122 PGOCtxProfContext Ret(*Guid, std::move(*Counters));
123
124 while (canReadContext()) {
125 EXPECT_OR_RET(SC, readContext(true));
126 auto &Targets = Ret.callsites()[*SC->first];
127 auto [_, Inserted] =
128 Targets.insert({SC->second.guid(), std::move(SC->second)});
129 if (!Inserted)
130 return wrongValue(
131 "Unexpected duplicate target (callee) at the same callsite.");
132 }
133 return std::make_pair(CallsiteIndex, std::move(Ret));
134}
135
136Error PGOCtxProfileReader::readMetadata() {
139 return make_error<InstrProfError>(instrprof_error::invalid_prof,
140 "Invalid magic");
141
143 RET_ON_ERR(Cursor.advance().moveInto(Entry));
144 if (Entry.Kind != BitstreamEntry::SubBlock ||
146 return unsupported("Expected Block ID");
147 // We don't need the blockinfo to read the rest, it's metadata usable for e.g.
148 // llvm-bcanalyzer.
149 RET_ON_ERR(Cursor.SkipBlock());
150
151 EXPECT_OR_RET(Blk, advance());
152 if (Blk->Kind != BitstreamEntry::SubBlock)
153 return unsupported("Expected Version record");
156 EXPECT_OR_RET(MData, advance());
157 if (MData->Kind != BitstreamEntry::Record)
158 return unsupported("Expected Version record");
159
163 return unsupported("Expected Version record");
164 if (Ver.size() != 1 || Ver[0] > PGOCtxProfileWriter::CurrentVersion)
165 return unsupported("Version " + Twine(*Code) +
166 " is higher than supported version " +
168 return Error::success();
169}
170
173 std::map<GlobalValue::GUID, PGOCtxProfContext> Ret;
174 RET_ON_ERR(readMetadata());
175 while (canReadContext()) {
176 EXPECT_OR_RET(E, readContext(false));
177 auto Key = E->second.guid();
178 if (!Ret.insert({Key, std::move(E->second)}).second)
179 return wrongValue("Duplicate roots");
180 }
181 return std::move(Ret);
182}
183
184namespace {
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.
190void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx);
191
192void 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
205void 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
228void 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
263 yaml::Output Out(OS);
264 toYaml(Out, Profiles);
265}
#define _
#define I(x, y, z)
Definition: MD5.cpp:58
#define G(x, y, z)
Definition: MD5.cpp:56
#define RET_ON_ERR(EXPR)
#define EXPECT_OR_RET(LHS, RHS)
Reader for contextual iFDO profile, which comes in bitstream format.
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
raw_pwrite_stream & OS
Expected< BitstreamEntry > advance(unsigned Flags=0)
Advance the current bitstream, returning the next entry in the stream.
Expected< unsigned > readRecord(unsigned AbbrevID, SmallVectorImpl< uint64_t > &Vals, StringRef *Blob=nullptr)
Error EnterSubBlock(unsigned BlockID, unsigned *NumWordsP=nullptr)
Having read the ENTER_SUBBLOCK abbrevid, and enter the block.
@ AF_DontAutoprocessAbbrevs
If this flag is used, abbrev entries are returned just like normal records.
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:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:337
Tagged union holding either a T or a Error.
Definition: Error.h:481
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
std::map< GlobalValue::GUID, PGOCtxProfContext > CallTargetMapTy
const CallsiteMapTy & callsites() const
Expected< std::map< GlobalValue::GUID, PGOCtxProfContext > > loadContexts()
static constexpr uint32_t CurrentVersion
static constexpr StringRef ContainerMagic
size_t size() const
Definition: SmallVector.h:78
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:573
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
constexpr size_t size() const
size - Get the string size.
Definition: StringRef.h:150
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:81
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
@ Entry
Definition: COFF.h:844
@ SC
CHAIN = SC CHAIN, Imm128 - System call.
@ 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
auto make_first_range(ContainerTy &&c)
Given a container of pairs, return a range over the first elements.
Definition: STLExtras.h:1439
void convertCtxProfToYaml(raw_ostream &OS, const PGOCtxProfContext::CallTargetMapTy &)
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:2014
void consumeError(Error Err)
Consume a Error without doing anything.
Definition: Error.h:1069
@ ContextNodeBlockID
@ ProfileMetadataBlockID
When advancing through a bitstream cursor, each advance can discover a few different kinds of entries...