//===- PGOCtxProfReader.cpp - Contextual Instrumentation profile reader ---===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Read a contextual profile into a datastructure suitable for maintenance // throughout IPO // //===----------------------------------------------------------------------===// #include "llvm/ProfileData/PGOCtxProfReader.h" #include "llvm/Bitstream/BitCodeEnums.h" #include "llvm/Bitstream/BitstreamReader.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/ProfileData/PGOCtxProfWriter.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" using namespace llvm; // FIXME(#92054) - these Error handling macros are (re-)invented in a few // places. #define EXPECT_OR_RET(LHS, RHS) \ auto LHS = RHS; \ if (!LHS) \ return LHS.takeError(); #define RET_ON_ERR(EXPR) \ if (auto Err = (EXPR)) \ return Err; Expected PGOContextualProfile::getOrEmplace(uint32_t Index, GlobalValue::GUID G, SmallVectorImpl &&Counters) { auto [Iter, Inserted] = Callsites[Index].insert( {G, PGOContextualProfile(G, std::move(Counters))}); if (!Inserted) return make_error(instrprof_error::invalid_prof, "Duplicate GUID for same callsite."); return Iter->second; } void PGOContextualProfile::getContainedGuids( DenseSet &Guids) const { Guids.insert(GUID); for (const auto &[_, Callsite] : Callsites) for (const auto &[_, Callee] : Callsite) Callee.getContainedGuids(Guids); } Expected PGOCtxProfileReader::advance() { return Cursor.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs); } Error PGOCtxProfileReader::wrongValue(const Twine &Msg) { return make_error(instrprof_error::invalid_prof, Msg); } Error PGOCtxProfileReader::unsupported(const Twine &Msg) { return make_error(instrprof_error::unsupported_version, Msg); } bool PGOCtxProfileReader::canReadContext() { auto Blk = advance(); if (!Blk) { consumeError(Blk.takeError()); return false; } return Blk->Kind == BitstreamEntry::SubBlock && Blk->ID == PGOCtxProfileBlockIDs::ContextNodeBlockID; } Expected, PGOContextualProfile>> PGOCtxProfileReader::readContext(bool ExpectIndex) { RET_ON_ERR(Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID)); std::optional Guid; std::optional> Counters; std::optional CallsiteIndex; SmallVector RecordValues; // We don't prescribe the order in which the records come in, and we are ok // if other unsupported records appear. We seek in the current subblock until // we get all we know. auto GotAllWeNeed = [&]() { return Guid.has_value() && Counters.has_value() && (!ExpectIndex || CallsiteIndex.has_value()); }; while (!GotAllWeNeed()) { RecordValues.clear(); EXPECT_OR_RET(Entry, advance()); if (Entry->Kind != BitstreamEntry::Record) return wrongValue( "Expected records before encountering more subcontexts"); EXPECT_OR_RET(ReadRecord, Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues)); switch (*ReadRecord) { case PGOCtxProfileRecords::Guid: if (RecordValues.size() != 1) return wrongValue("The GUID record should have exactly one value"); Guid = RecordValues[0]; break; case PGOCtxProfileRecords::Counters: Counters = std::move(RecordValues); if (Counters->empty()) return wrongValue("Empty counters. At least the entry counter (one " "value) was expected"); break; case PGOCtxProfileRecords::CalleeIndex: if (!ExpectIndex) return wrongValue("The root context should not have a callee index"); if (RecordValues.size() != 1) return wrongValue("The callee index should have exactly one value"); CallsiteIndex = RecordValues[0]; break; default: // OK if we see records we do not understand, like records (profile // components) introduced later. break; } } PGOContextualProfile Ret(*Guid, std::move(*Counters)); while (canReadContext()) { EXPECT_OR_RET(SC, readContext(true)); auto &Targets = Ret.callsites()[*SC->first]; auto [_, Inserted] = Targets.insert({SC->second.guid(), std::move(SC->second)}); if (!Inserted) return wrongValue( "Unexpected duplicate target (callee) at the same callsite."); } return std::make_pair(CallsiteIndex, std::move(Ret)); } Error PGOCtxProfileReader::readMetadata() { EXPECT_OR_RET(Blk, advance()); if (Blk->Kind != BitstreamEntry::SubBlock) return unsupported("Expected Version record"); RET_ON_ERR( Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID)); EXPECT_OR_RET(MData, advance()); if (MData->Kind != BitstreamEntry::Record) return unsupported("Expected Version record"); SmallVector Ver; EXPECT_OR_RET(Code, Cursor.readRecord(bitc::UNABBREV_RECORD, Ver)); if (*Code != PGOCtxProfileRecords::Version) return unsupported("Expected Version record"); if (Ver.size() != 1 || Ver[0] > PGOCtxProfileWriter::CurrentVersion) return unsupported("Version " + Twine(*Code) + " is higher than supported version " + Twine(PGOCtxProfileWriter::CurrentVersion)); return Error::success(); } Expected> PGOCtxProfileReader::loadContexts() { std::map Ret; RET_ON_ERR(readMetadata()); while (canReadContext()) { EXPECT_OR_RET(E, readContext(false)); auto Key = E->second.guid(); if (!Ret.insert({Key, std::move(E->second)}).second) return wrongValue("Duplicate roots"); } return std::move(Ret); }