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