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