1fc8775e2SMircea Trofin //===- PGOCtxProfReader.cpp - Contextual Instrumentation profile reader ---===// 2fc8775e2SMircea Trofin // 3fc8775e2SMircea Trofin // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4fc8775e2SMircea Trofin // See https://llvm.org/LICENSE.txt for license information. 5fc8775e2SMircea Trofin // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6fc8775e2SMircea Trofin // 7fc8775e2SMircea Trofin //===----------------------------------------------------------------------===// 8fc8775e2SMircea Trofin // 9fc8775e2SMircea Trofin // Read a contextual profile into a datastructure suitable for maintenance 10fc8775e2SMircea Trofin // throughout IPO 11fc8775e2SMircea Trofin // 12fc8775e2SMircea Trofin //===----------------------------------------------------------------------===// 13fc8775e2SMircea Trofin 14fc8775e2SMircea Trofin #include "llvm/ProfileData/PGOCtxProfReader.h" 15fc8775e2SMircea Trofin #include "llvm/Bitstream/BitCodeEnums.h" 16fc8775e2SMircea Trofin #include "llvm/Bitstream/BitstreamReader.h" 17fc8775e2SMircea Trofin #include "llvm/ProfileData/InstrProf.h" 18fc8775e2SMircea Trofin #include "llvm/ProfileData/PGOCtxProfWriter.h" 19fc8775e2SMircea Trofin #include "llvm/Support/Error.h" 20*b15845c0SMircea Trofin #include "llvm/Support/ErrorHandling.h" 21*b15845c0SMircea Trofin #include "llvm/Support/YAMLTraits.h" 22*b15845c0SMircea Trofin #include <iterator> 23*b15845c0SMircea Trofin #include <utility> 24fc8775e2SMircea Trofin 25fc8775e2SMircea Trofin using namespace llvm; 26fc8775e2SMircea Trofin 27fc8775e2SMircea Trofin // FIXME(#92054) - these Error handling macros are (re-)invented in a few 28fc8775e2SMircea Trofin // places. 29fc8775e2SMircea Trofin #define EXPECT_OR_RET(LHS, RHS) \ 30fc8775e2SMircea Trofin auto LHS = RHS; \ 31fc8775e2SMircea Trofin if (!LHS) \ 32fc8775e2SMircea Trofin return LHS.takeError(); 33fc8775e2SMircea Trofin 34fc8775e2SMircea Trofin #define RET_ON_ERR(EXPR) \ 35fc8775e2SMircea Trofin if (auto Err = (EXPR)) \ 36fc8775e2SMircea Trofin return Err; 37fc8775e2SMircea Trofin 386b47772aSMircea Trofin Expected<PGOCtxProfContext &> 396b47772aSMircea Trofin PGOCtxProfContext::getOrEmplace(uint32_t Index, GlobalValue::GUID G, 40fc8775e2SMircea Trofin SmallVectorImpl<uint64_t> &&Counters) { 416b47772aSMircea Trofin auto [Iter, Inserted] = 426b47772aSMircea Trofin Callsites[Index].insert({G, PGOCtxProfContext(G, std::move(Counters))}); 43fc8775e2SMircea Trofin if (!Inserted) 44fc8775e2SMircea Trofin return make_error<InstrProfError>(instrprof_error::invalid_prof, 45fc8775e2SMircea Trofin "Duplicate GUID for same callsite."); 46fc8775e2SMircea Trofin return Iter->second; 47fc8775e2SMircea Trofin } 48fc8775e2SMircea Trofin 49fc8775e2SMircea Trofin Expected<BitstreamEntry> PGOCtxProfileReader::advance() { 50fc8775e2SMircea Trofin return Cursor.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs); 51fc8775e2SMircea Trofin } 52fc8775e2SMircea Trofin 53fc8775e2SMircea Trofin Error PGOCtxProfileReader::wrongValue(const Twine &Msg) { 54fc8775e2SMircea Trofin return make_error<InstrProfError>(instrprof_error::invalid_prof, Msg); 55fc8775e2SMircea Trofin } 56fc8775e2SMircea Trofin 57fc8775e2SMircea Trofin Error PGOCtxProfileReader::unsupported(const Twine &Msg) { 58fc8775e2SMircea Trofin return make_error<InstrProfError>(instrprof_error::unsupported_version, Msg); 59fc8775e2SMircea Trofin } 60fc8775e2SMircea Trofin 61fc8775e2SMircea Trofin bool PGOCtxProfileReader::canReadContext() { 62fc8775e2SMircea Trofin auto Blk = advance(); 63fc8775e2SMircea Trofin if (!Blk) { 64fc8775e2SMircea Trofin consumeError(Blk.takeError()); 65fc8775e2SMircea Trofin return false; 66fc8775e2SMircea Trofin } 67fc8775e2SMircea Trofin return Blk->Kind == BitstreamEntry::SubBlock && 68fc8775e2SMircea Trofin Blk->ID == PGOCtxProfileBlockIDs::ContextNodeBlockID; 69fc8775e2SMircea Trofin } 70fc8775e2SMircea Trofin 716b47772aSMircea Trofin Expected<std::pair<std::optional<uint32_t>, PGOCtxProfContext>> 72fc8775e2SMircea Trofin PGOCtxProfileReader::readContext(bool ExpectIndex) { 73fc8775e2SMircea Trofin RET_ON_ERR(Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID)); 74fc8775e2SMircea Trofin 75fc8775e2SMircea Trofin std::optional<ctx_profile::GUID> Guid; 76fc8775e2SMircea Trofin std::optional<SmallVector<uint64_t, 16>> Counters; 77fc8775e2SMircea Trofin std::optional<uint32_t> CallsiteIndex; 78fc8775e2SMircea Trofin 79fc8775e2SMircea Trofin SmallVector<uint64_t, 1> RecordValues; 80fc8775e2SMircea Trofin 81fc8775e2SMircea Trofin // We don't prescribe the order in which the records come in, and we are ok 82fc8775e2SMircea Trofin // if other unsupported records appear. We seek in the current subblock until 83fc8775e2SMircea Trofin // we get all we know. 84fc8775e2SMircea Trofin auto GotAllWeNeed = [&]() { 85fc8775e2SMircea Trofin return Guid.has_value() && Counters.has_value() && 86fc8775e2SMircea Trofin (!ExpectIndex || CallsiteIndex.has_value()); 87fc8775e2SMircea Trofin }; 88fc8775e2SMircea Trofin while (!GotAllWeNeed()) { 89fc8775e2SMircea Trofin RecordValues.clear(); 90fc8775e2SMircea Trofin EXPECT_OR_RET(Entry, advance()); 91fc8775e2SMircea Trofin if (Entry->Kind != BitstreamEntry::Record) 92fc8775e2SMircea Trofin return wrongValue( 93fc8775e2SMircea Trofin "Expected records before encountering more subcontexts"); 94fc8775e2SMircea Trofin EXPECT_OR_RET(ReadRecord, 95fc8775e2SMircea Trofin Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues)); 96fc8775e2SMircea Trofin switch (*ReadRecord) { 97fc8775e2SMircea Trofin case PGOCtxProfileRecords::Guid: 98fc8775e2SMircea Trofin if (RecordValues.size() != 1) 99fc8775e2SMircea Trofin return wrongValue("The GUID record should have exactly one value"); 100fc8775e2SMircea Trofin Guid = RecordValues[0]; 101fc8775e2SMircea Trofin break; 102fc8775e2SMircea Trofin case PGOCtxProfileRecords::Counters: 103fc8775e2SMircea Trofin Counters = std::move(RecordValues); 104fc8775e2SMircea Trofin if (Counters->empty()) 105fc8775e2SMircea Trofin return wrongValue("Empty counters. At least the entry counter (one " 106fc8775e2SMircea Trofin "value) was expected"); 107fc8775e2SMircea Trofin break; 108fc8775e2SMircea Trofin case PGOCtxProfileRecords::CalleeIndex: 109fc8775e2SMircea Trofin if (!ExpectIndex) 110fc8775e2SMircea Trofin return wrongValue("The root context should not have a callee index"); 111fc8775e2SMircea Trofin if (RecordValues.size() != 1) 112fc8775e2SMircea Trofin return wrongValue("The callee index should have exactly one value"); 113fc8775e2SMircea Trofin CallsiteIndex = RecordValues[0]; 114fc8775e2SMircea Trofin break; 115fc8775e2SMircea Trofin default: 116fc8775e2SMircea Trofin // OK if we see records we do not understand, like records (profile 117fc8775e2SMircea Trofin // components) introduced later. 118fc8775e2SMircea Trofin break; 119fc8775e2SMircea Trofin } 120fc8775e2SMircea Trofin } 121fc8775e2SMircea Trofin 1226b47772aSMircea Trofin PGOCtxProfContext Ret(*Guid, std::move(*Counters)); 123fc8775e2SMircea Trofin 124fc8775e2SMircea Trofin while (canReadContext()) { 125fc8775e2SMircea Trofin EXPECT_OR_RET(SC, readContext(true)); 126fc8775e2SMircea Trofin auto &Targets = Ret.callsites()[*SC->first]; 127fc8775e2SMircea Trofin auto [_, Inserted] = 128fc8775e2SMircea Trofin Targets.insert({SC->second.guid(), std::move(SC->second)}); 129fc8775e2SMircea Trofin if (!Inserted) 130fc8775e2SMircea Trofin return wrongValue( 131fc8775e2SMircea Trofin "Unexpected duplicate target (callee) at the same callsite."); 132fc8775e2SMircea Trofin } 133fc8775e2SMircea Trofin return std::make_pair(CallsiteIndex, std::move(Ret)); 134fc8775e2SMircea Trofin } 135fc8775e2SMircea Trofin 136fc8775e2SMircea Trofin Error PGOCtxProfileReader::readMetadata() { 137cc7308a1SMircea Trofin if (Magic.size() < PGOCtxProfileWriter::ContainerMagic.size() || 138cc7308a1SMircea Trofin Magic != PGOCtxProfileWriter::ContainerMagic) 139cc7308a1SMircea Trofin return make_error<InstrProfError>(instrprof_error::invalid_prof, 140cc7308a1SMircea Trofin "Invalid magic"); 141cc7308a1SMircea Trofin 142cc7308a1SMircea Trofin BitstreamEntry Entry; 143cc7308a1SMircea Trofin RET_ON_ERR(Cursor.advance().moveInto(Entry)); 144cc7308a1SMircea Trofin if (Entry.Kind != BitstreamEntry::SubBlock || 145cc7308a1SMircea Trofin Entry.ID != bitc::BLOCKINFO_BLOCK_ID) 146cc7308a1SMircea Trofin return unsupported("Expected Block ID"); 147cc7308a1SMircea Trofin // We don't need the blockinfo to read the rest, it's metadata usable for e.g. 148cc7308a1SMircea Trofin // llvm-bcanalyzer. 149cc7308a1SMircea Trofin RET_ON_ERR(Cursor.SkipBlock()); 150cc7308a1SMircea Trofin 151fc8775e2SMircea Trofin EXPECT_OR_RET(Blk, advance()); 152fc8775e2SMircea Trofin if (Blk->Kind != BitstreamEntry::SubBlock) 153fc8775e2SMircea Trofin return unsupported("Expected Version record"); 154fc8775e2SMircea Trofin RET_ON_ERR( 155fc8775e2SMircea Trofin Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID)); 156fc8775e2SMircea Trofin EXPECT_OR_RET(MData, advance()); 157fc8775e2SMircea Trofin if (MData->Kind != BitstreamEntry::Record) 158fc8775e2SMircea Trofin return unsupported("Expected Version record"); 159fc8775e2SMircea Trofin 160fc8775e2SMircea Trofin SmallVector<uint64_t, 1> Ver; 161fc8775e2SMircea Trofin EXPECT_OR_RET(Code, Cursor.readRecord(bitc::UNABBREV_RECORD, Ver)); 162fc8775e2SMircea Trofin if (*Code != PGOCtxProfileRecords::Version) 163fc8775e2SMircea Trofin return unsupported("Expected Version record"); 164fc8775e2SMircea Trofin if (Ver.size() != 1 || Ver[0] > PGOCtxProfileWriter::CurrentVersion) 165fc8775e2SMircea Trofin return unsupported("Version " + Twine(*Code) + 166fc8775e2SMircea Trofin " is higher than supported version " + 167fc8775e2SMircea Trofin Twine(PGOCtxProfileWriter::CurrentVersion)); 168fc8775e2SMircea Trofin return Error::success(); 169fc8775e2SMircea Trofin } 170fc8775e2SMircea Trofin 1716b47772aSMircea Trofin Expected<std::map<GlobalValue::GUID, PGOCtxProfContext>> 172fc8775e2SMircea Trofin PGOCtxProfileReader::loadContexts() { 1736b47772aSMircea Trofin std::map<GlobalValue::GUID, PGOCtxProfContext> Ret; 174fc8775e2SMircea Trofin RET_ON_ERR(readMetadata()); 175fc8775e2SMircea Trofin while (canReadContext()) { 176fc8775e2SMircea Trofin EXPECT_OR_RET(E, readContext(false)); 177fc8775e2SMircea Trofin auto Key = E->second.guid(); 178fc8775e2SMircea Trofin if (!Ret.insert({Key, std::move(E->second)}).second) 179fc8775e2SMircea Trofin return wrongValue("Duplicate roots"); 180fc8775e2SMircea Trofin } 181fc8775e2SMircea Trofin return std::move(Ret); 182fc8775e2SMircea Trofin } 183*b15845c0SMircea Trofin 184*b15845c0SMircea Trofin namespace { 185*b15845c0SMircea Trofin // We want to pass `const` values PGOCtxProfContext references to the yaml 186*b15845c0SMircea Trofin // converter, and the regular yaml mapping APIs are designed to handle both 187*b15845c0SMircea Trofin // serialization and deserialization, which prevents using const for 188*b15845c0SMircea Trofin // serialization. Using an intermediate datastructure is overkill, both 189*b15845c0SMircea Trofin // space-wise and design complexity-wise. Instead, we use the lower-level APIs. 190*b15845c0SMircea Trofin void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx); 191*b15845c0SMircea Trofin 192*b15845c0SMircea Trofin void toYaml(yaml::Output &Out, 193*b15845c0SMircea Trofin const PGOCtxProfContext::CallTargetMapTy &CallTargets) { 194*b15845c0SMircea Trofin Out.beginSequence(); 195*b15845c0SMircea Trofin size_t Index = 0; 196*b15845c0SMircea Trofin void *SaveData = nullptr; 197*b15845c0SMircea Trofin for (const auto &[_, Ctx] : CallTargets) { 198*b15845c0SMircea Trofin Out.preflightElement(Index++, SaveData); 199*b15845c0SMircea Trofin toYaml(Out, Ctx); 200*b15845c0SMircea Trofin Out.postflightElement(nullptr); 201*b15845c0SMircea Trofin } 202*b15845c0SMircea Trofin Out.endSequence(); 203*b15845c0SMircea Trofin } 204*b15845c0SMircea Trofin 205*b15845c0SMircea Trofin void toYaml(yaml::Output &Out, 206*b15845c0SMircea Trofin const PGOCtxProfContext::CallsiteMapTy &Callsites) { 207*b15845c0SMircea Trofin auto AllCS = ::llvm::make_first_range(Callsites); 208*b15845c0SMircea Trofin auto MaxIt = ::llvm::max_element(AllCS); 209*b15845c0SMircea Trofin assert(MaxIt != AllCS.end() && "We should have a max value because the " 210*b15845c0SMircea Trofin "callsites collection is not empty."); 211*b15845c0SMircea Trofin void *SaveData = nullptr; 212*b15845c0SMircea Trofin Out.beginSequence(); 213*b15845c0SMircea Trofin for (auto I = 0U; I <= *MaxIt; ++I) { 214*b15845c0SMircea Trofin Out.preflightElement(I, SaveData); 215*b15845c0SMircea Trofin auto It = Callsites.find(I); 216*b15845c0SMircea Trofin if (It == Callsites.end()) { 217*b15845c0SMircea Trofin // This will produce a `[ ]` sequence, which is what we want here. 218*b15845c0SMircea Trofin Out.beginFlowSequence(); 219*b15845c0SMircea Trofin Out.endFlowSequence(); 220*b15845c0SMircea Trofin } else { 221*b15845c0SMircea Trofin toYaml(Out, It->second); 222*b15845c0SMircea Trofin } 223*b15845c0SMircea Trofin Out.postflightElement(nullptr); 224*b15845c0SMircea Trofin } 225*b15845c0SMircea Trofin Out.endSequence(); 226*b15845c0SMircea Trofin } 227*b15845c0SMircea Trofin 228*b15845c0SMircea Trofin void toYaml(yaml::Output &Out, const PGOCtxProfContext &Ctx) { 229*b15845c0SMircea Trofin yaml::EmptyContext Empty; 230*b15845c0SMircea Trofin Out.beginMapping(); 231*b15845c0SMircea Trofin void *SaveInfo = nullptr; 232*b15845c0SMircea Trofin bool UseDefault = false; 233*b15845c0SMircea Trofin { 234*b15845c0SMircea Trofin Out.preflightKey("Guid", /*Required=*/true, /*SameAsDefault=*/false, 235*b15845c0SMircea Trofin UseDefault, SaveInfo); 236*b15845c0SMircea Trofin auto Guid = Ctx.guid(); 237*b15845c0SMircea Trofin yaml::yamlize(Out, Guid, true, Empty); 238*b15845c0SMircea Trofin Out.postflightKey(nullptr); 239*b15845c0SMircea Trofin } 240*b15845c0SMircea Trofin { 241*b15845c0SMircea Trofin Out.preflightKey("Counters", true, false, UseDefault, SaveInfo); 242*b15845c0SMircea Trofin Out.beginFlowSequence(); 243*b15845c0SMircea Trofin for (size_t I = 0U, E = Ctx.counters().size(); I < E; ++I) { 244*b15845c0SMircea Trofin Out.preflightFlowElement(I, SaveInfo); 245*b15845c0SMircea Trofin uint64_t V = Ctx.counters()[I]; 246*b15845c0SMircea Trofin yaml::yamlize(Out, V, true, Empty); 247*b15845c0SMircea Trofin Out.postflightFlowElement(SaveInfo); 248*b15845c0SMircea Trofin } 249*b15845c0SMircea Trofin Out.endFlowSequence(); 250*b15845c0SMircea Trofin Out.postflightKey(nullptr); 251*b15845c0SMircea Trofin } 252*b15845c0SMircea Trofin if (!Ctx.callsites().empty()) { 253*b15845c0SMircea Trofin Out.preflightKey("Callsites", true, false, UseDefault, SaveInfo); 254*b15845c0SMircea Trofin toYaml(Out, Ctx.callsites()); 255*b15845c0SMircea Trofin Out.postflightKey(nullptr); 256*b15845c0SMircea Trofin } 257*b15845c0SMircea Trofin Out.endMapping(); 258*b15845c0SMircea Trofin } 259*b15845c0SMircea Trofin } // namespace 260*b15845c0SMircea Trofin 261*b15845c0SMircea Trofin void llvm::convertCtxProfToYaml( 262*b15845c0SMircea Trofin raw_ostream &OS, const PGOCtxProfContext::CallTargetMapTy &Profiles) { 263*b15845c0SMircea Trofin yaml::Output Out(OS); 264*b15845c0SMircea Trofin toYaml(Out, Profiles); 265*b15845c0SMircea Trofin }