xref: /freebsd/contrib/llvm-project/llvm/lib/ProfileData/PGOCtxProfReader.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 &>
getOrEmplace(uint32_t Index,GlobalValue::GUID G,SmallVectorImpl<uint64_t> && Counters)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))});
40   if (!Inserted)
41     return make_error<InstrProfError>(instrprof_error::invalid_prof,
42                                       "Duplicate GUID for same callsite.");
43   return Iter->second;
44 }
45 
getContainedGuids(DenseSet<GlobalValue::GUID> & Guids) const46 void PGOContextualProfile::getContainedGuids(
47     DenseSet<GlobalValue::GUID> &Guids) const {
48   Guids.insert(GUID);
49   for (const auto &[_, Callsite] : Callsites)
50     for (const auto &[_, Callee] : Callsite)
51       Callee.getContainedGuids(Guids);
52 }
53 
advance()54 Expected<BitstreamEntry> PGOCtxProfileReader::advance() {
55   return Cursor.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs);
56 }
57 
wrongValue(const Twine & Msg)58 Error PGOCtxProfileReader::wrongValue(const Twine &Msg) {
59   return make_error<InstrProfError>(instrprof_error::invalid_prof, Msg);
60 }
61 
unsupported(const Twine & Msg)62 Error PGOCtxProfileReader::unsupported(const Twine &Msg) {
63   return make_error<InstrProfError>(instrprof_error::unsupported_version, Msg);
64 }
65 
canReadContext()66 bool PGOCtxProfileReader::canReadContext() {
67   auto Blk = advance();
68   if (!Blk) {
69     consumeError(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>>
readContext(bool ExpectIndex)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           "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];
106       break;
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");
112       break;
113     case PGOCtxProfileRecords::CalleeIndex:
114       if (!ExpectIndex)
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];
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({SC->second.guid(), std::move(SC->second)});
134     if (!Inserted)
135       return wrongValue(
136           "Unexpected duplicate target (callee) at the same callsite.");
137   }
138   return std::make_pair(CallsiteIndex, std::move(Ret));
139 }
140 
readMetadata()141 Error PGOCtxProfileReader::readMetadata() {
142   EXPECT_OR_RET(Blk, advance());
143   if (Blk->Kind != BitstreamEntry::SubBlock)
144     return unsupported("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("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("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();
160 }
161 
162 Expected<std::map<GlobalValue::GUID, PGOContextualProfile>>
loadContexts()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");
171   }
172   return std::move(Ret);
173 }
174