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 | |
14 | #include "llvm/ProfileData/PGOCtxProfReader.h" |
15 | #include "llvm/Bitstream/BitCodeEnums.h" |
16 | #include "llvm/Bitstream/BitstreamReader.h" |
17 | #include "llvm/ProfileData/InstrProf.h" |
18 | #include "llvm/ProfileData/PGOCtxProfWriter.h" |
19 | #include "llvm/Support/Errc.h" |
20 | #include "llvm/Support/Error.h" |
21 | |
22 | using namespace llvm; |
23 | |
24 | // FIXME(#92054) - these Error handling macros are (re-)invented in a few |
25 | // places. |
26 | #define EXPECT_OR_RET(LHS, RHS) \ |
27 | auto LHS = RHS; \ |
28 | if (!LHS) \ |
29 | return LHS.takeError(); |
30 | |
31 | #define RET_ON_ERR(EXPR) \ |
32 | if (auto Err = (EXPR)) \ |
33 | return Err; |
34 | |
35 | Expected<PGOContextualProfile &> |
36 | PGOContextualProfile::getOrEmplace(uint32_t Index, GlobalValue::GUID G, |
37 | SmallVectorImpl<uint64_t> &&Counters) { |
38 | auto [Iter, Inserted] = Callsites[Index].insert( |
39 | x: {G, PGOContextualProfile(G, std::move(Counters))}); |
40 | if (!Inserted) |
41 | return make_error<InstrProfError>(Args: instrprof_error::invalid_prof, |
42 | Args: "Duplicate GUID for same callsite." ); |
43 | return Iter->second; |
44 | } |
45 | |
46 | void PGOContextualProfile::getContainedGuids( |
47 | DenseSet<GlobalValue::GUID> &Guids) const { |
48 | Guids.insert(V: GUID); |
49 | for (const auto &[_, Callsite] : Callsites) |
50 | for (const auto &[_, Callee] : Callsite) |
51 | Callee.getContainedGuids(Guids); |
52 | } |
53 | |
54 | Expected<BitstreamEntry> PGOCtxProfileReader::advance() { |
55 | return Cursor.advance(Flags: BitstreamCursor::AF_DontAutoprocessAbbrevs); |
56 | } |
57 | |
58 | Error PGOCtxProfileReader::wrongValue(const Twine &Msg) { |
59 | return make_error<InstrProfError>(Args: instrprof_error::invalid_prof, Args: Msg); |
60 | } |
61 | |
62 | Error PGOCtxProfileReader::unsupported(const Twine &Msg) { |
63 | return make_error<InstrProfError>(Args: instrprof_error::unsupported_version, Args: Msg); |
64 | } |
65 | |
66 | bool PGOCtxProfileReader::canReadContext() { |
67 | auto Blk = advance(); |
68 | if (!Blk) { |
69 | consumeError(Err: Blk.takeError()); |
70 | return false; |
71 | } |
72 | return Blk->Kind == BitstreamEntry::SubBlock && |
73 | Blk->ID == PGOCtxProfileBlockIDs::ContextNodeBlockID; |
74 | } |
75 | |
76 | Expected<std::pair<std::optional<uint32_t>, PGOContextualProfile>> |
77 | PGOCtxProfileReader::readContext(bool ExpectIndex) { |
78 | RET_ON_ERR(Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID)); |
79 | |
80 | std::optional<ctx_profile::GUID> Guid; |
81 | std::optional<SmallVector<uint64_t, 16>> Counters; |
82 | std::optional<uint32_t> CallsiteIndex; |
83 | |
84 | SmallVector<uint64_t, 1> RecordValues; |
85 | |
86 | // We don't prescribe the order in which the records come in, and we are ok |
87 | // if other unsupported records appear. We seek in the current subblock until |
88 | // we get all we know. |
89 | auto GotAllWeNeed = [&]() { |
90 | return Guid.has_value() && Counters.has_value() && |
91 | (!ExpectIndex || CallsiteIndex.has_value()); |
92 | }; |
93 | while (!GotAllWeNeed()) { |
94 | RecordValues.clear(); |
95 | EXPECT_OR_RET(Entry, advance()); |
96 | if (Entry->Kind != BitstreamEntry::Record) |
97 | return wrongValue( |
98 | Msg: "Expected records before encountering more subcontexts" ); |
99 | EXPECT_OR_RET(ReadRecord, |
100 | Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues)); |
101 | switch (*ReadRecord) { |
102 | case PGOCtxProfileRecords::Guid: |
103 | if (RecordValues.size() != 1) |
104 | return wrongValue(Msg: "The GUID record should have exactly one value" ); |
105 | Guid = RecordValues[0]; |
106 | break; |
107 | case PGOCtxProfileRecords::Counters: |
108 | Counters = std::move(RecordValues); |
109 | if (Counters->empty()) |
110 | return wrongValue(Msg: "Empty counters. At least the entry counter (one " |
111 | "value) was expected" ); |
112 | break; |
113 | case PGOCtxProfileRecords::CalleeIndex: |
114 | if (!ExpectIndex) |
115 | return wrongValue(Msg: "The root context should not have a callee index" ); |
116 | if (RecordValues.size() != 1) |
117 | return wrongValue(Msg: "The callee index should have exactly one value" ); |
118 | CallsiteIndex = RecordValues[0]; |
119 | break; |
120 | default: |
121 | // OK if we see records we do not understand, like records (profile |
122 | // components) introduced later. |
123 | break; |
124 | } |
125 | } |
126 | |
127 | PGOContextualProfile Ret(*Guid, std::move(*Counters)); |
128 | |
129 | while (canReadContext()) { |
130 | EXPECT_OR_RET(SC, readContext(true)); |
131 | auto &Targets = Ret.callsites()[*SC->first]; |
132 | auto [_, Inserted] = |
133 | Targets.insert(x: {SC->second.guid(), std::move(SC->second)}); |
134 | if (!Inserted) |
135 | return wrongValue( |
136 | Msg: "Unexpected duplicate target (callee) at the same callsite." ); |
137 | } |
138 | return std::make_pair(x&: CallsiteIndex, y: std::move(Ret)); |
139 | } |
140 | |
141 | Error PGOCtxProfileReader::readMetadata() { |
142 | EXPECT_OR_RET(Blk, advance()); |
143 | if (Blk->Kind != BitstreamEntry::SubBlock) |
144 | return unsupported(Msg: "Expected Version record" ); |
145 | RET_ON_ERR( |
146 | Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID)); |
147 | EXPECT_OR_RET(MData, advance()); |
148 | if (MData->Kind != BitstreamEntry::Record) |
149 | return unsupported(Msg: "Expected Version record" ); |
150 | |
151 | SmallVector<uint64_t, 1> Ver; |
152 | EXPECT_OR_RET(Code, Cursor.readRecord(bitc::UNABBREV_RECORD, Ver)); |
153 | if (*Code != PGOCtxProfileRecords::Version) |
154 | return unsupported(Msg: "Expected Version record" ); |
155 | if (Ver.size() != 1 || Ver[0] > PGOCtxProfileWriter::CurrentVersion) |
156 | return unsupported(Msg: "Version " + Twine(*Code) + |
157 | " is higher than supported version " + |
158 | Twine(PGOCtxProfileWriter::CurrentVersion)); |
159 | return Error::success(); |
160 | } |
161 | |
162 | Expected<std::map<GlobalValue::GUID, PGOContextualProfile>> |
163 | PGOCtxProfileReader::loadContexts() { |
164 | std::map<GlobalValue::GUID, PGOContextualProfile> Ret; |
165 | RET_ON_ERR(readMetadata()); |
166 | while (canReadContext()) { |
167 | EXPECT_OR_RET(E, readContext(false)); |
168 | auto Key = E->second.guid(); |
169 | if (!Ret.insert(x: {Key, std::move(E->second)}).second) |
170 | return wrongValue(Msg: "Duplicate roots" ); |
171 | } |
172 | return std::move(Ret); |
173 | } |
174 | |