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 &> 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 46 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 54 Expected<BitstreamEntry> PGOCtxProfileReader::advance() { 55 return Cursor.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs); 56 } 57 58 Error PGOCtxProfileReader::wrongValue(const Twine &Msg) { 59 return make_error<InstrProfError>(instrprof_error::invalid_prof, Msg); 60 } 61 62 Error PGOCtxProfileReader::unsupported(const Twine &Msg) { 63 return make_error<InstrProfError>(instrprof_error::unsupported_version, Msg); 64 } 65 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>> 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 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>> 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