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