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 &> 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 46*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 54*0fca6ea1SDimitry Andric Expected<BitstreamEntry> PGOCtxProfileReader::advance() { 55*0fca6ea1SDimitry Andric return Cursor.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs); 56*0fca6ea1SDimitry Andric } 57*0fca6ea1SDimitry Andric 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 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 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>> 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 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>> 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