1 //===- PGOCtxProfReader.cpp - Contextual Instrumentation profile reader ---===//
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
7 //===----------------------------------------------------------------------===//
9 // Read a contextual profile into a datastructure suitable for maintenance
12 //===----------------------------------------------------------------------===//
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"
24 // FIXME(#92054) - these Error handling macros are (re-)invented in a few
26 #define EXPECT_OR_RET(LHS, RHS) \
29 return LHS.takeError();
31 #define RET_ON_ERR(EXPR) \
32 if (auto Err = (EXPR)) \
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 {G
, PGOContextualProfile(G
, std::move(Counters
))});
41 return make_error
<InstrProfError
>(instrprof_error::invalid_prof
,
42 "Duplicate GUID for same callsite.");
46 void PGOContextualProfile::getContainedGuids(
47 DenseSet
<GlobalValue::GUID
> &Guids
) const {
49 for (const auto &[_
, Callsite
] : Callsites
)
50 for (const auto &[_
, Callee
] : Callsite
)
51 Callee
.getContainedGuids(Guids
);
54 Expected
<BitstreamEntry
> PGOCtxProfileReader::advance() {
55 return Cursor
.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs
);
58 Error
PGOCtxProfileReader::wrongValue(const Twine
&Msg
) {
59 return make_error
<InstrProfError
>(instrprof_error::invalid_prof
, Msg
);
62 Error
PGOCtxProfileReader::unsupported(const Twine
&Msg
) {
63 return make_error
<InstrProfError
>(instrprof_error::unsupported_version
, Msg
);
66 bool PGOCtxProfileReader::canReadContext() {
69 consumeError(Blk
.takeError());
72 return Blk
->Kind
== BitstreamEntry::SubBlock
&&
73 Blk
->ID
== PGOCtxProfileBlockIDs::ContextNodeBlockID
;
76 Expected
<std::pair
<std::optional
<uint32_t>, PGOContextualProfile
>>
77 PGOCtxProfileReader::readContext(bool ExpectIndex
) {
78 RET_ON_ERR(Cursor
.EnterSubBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID
));
80 std::optional
<ctx_profile::GUID
> Guid
;
81 std::optional
<SmallVector
<uint64_t, 16>> Counters
;
82 std::optional
<uint32_t> CallsiteIndex
;
84 SmallVector
<uint64_t, 1> RecordValues
;
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());
93 while (!GotAllWeNeed()) {
95 EXPECT_OR_RET(Entry
, advance());
96 if (Entry
->Kind
!= BitstreamEntry::Record
)
98 "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("The GUID record should have exactly one value");
105 Guid
= RecordValues
[0];
107 case PGOCtxProfileRecords::Counters
:
108 Counters
= std::move(RecordValues
);
109 if (Counters
->empty())
110 return wrongValue("Empty counters. At least the entry counter (one "
111 "value) was expected");
113 case PGOCtxProfileRecords::CalleeIndex
:
115 return wrongValue("The root context should not have a callee index");
116 if (RecordValues
.size() != 1)
117 return wrongValue("The callee index should have exactly one value");
118 CallsiteIndex
= RecordValues
[0];
121 // OK if we see records we do not understand, like records (profile
122 // components) introduced later.
127 PGOContextualProfile
Ret(*Guid
, std::move(*Counters
));
129 while (canReadContext()) {
130 EXPECT_OR_RET(SC
, readContext(true));
131 auto &Targets
= Ret
.callsites()[*SC
->first
];
133 Targets
.insert({SC
->second
.guid(), std::move(SC
->second
)});
136 "Unexpected duplicate target (callee) at the same callsite.");
138 return std::make_pair(CallsiteIndex
, std::move(Ret
));
141 Error
PGOCtxProfileReader::readMetadata() {
142 EXPECT_OR_RET(Blk
, advance());
143 if (Blk
->Kind
!= BitstreamEntry::SubBlock
)
144 return unsupported("Expected Version record");
146 Cursor
.EnterSubBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID
));
147 EXPECT_OR_RET(MData
, advance());
148 if (MData
->Kind
!= BitstreamEntry::Record
)
149 return unsupported("Expected Version record");
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("Expected Version record");
155 if (Ver
.size() != 1 || Ver
[0] > PGOCtxProfileWriter::CurrentVersion
)
156 return unsupported("Version " + Twine(*Code
) +
157 " is higher than supported version " +
158 Twine(PGOCtxProfileWriter::CurrentVersion
));
159 return Error::success();
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({Key
, std::move(E
->second
)}).second
)
170 return wrongValue("Duplicate roots");
172 return std::move(Ret
);