1fc8775e2SMircea Trofin //===- PGOCtxProfWriter.cpp - Contextual Instrumentation profile writer ---===// 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 // Write a contextual profile to bitstream. 10fc8775e2SMircea Trofin // 11fc8775e2SMircea Trofin //===----------------------------------------------------------------------===// 12fc8775e2SMircea Trofin 13fc8775e2SMircea Trofin #include "llvm/ProfileData/PGOCtxProfWriter.h" 14fc8775e2SMircea Trofin #include "llvm/Bitstream/BitCodeEnums.h" 151022323cSMircea Trofin #include "llvm/ProfileData/CtxInstrContextNode.h" 1663293558SMircea Trofin #include "llvm/Support/Error.h" 1763293558SMircea Trofin #include "llvm/Support/MemoryBuffer.h" 1863293558SMircea Trofin #include "llvm/Support/YAMLTraits.h" 1963293558SMircea Trofin #include "llvm/Support/raw_ostream.h" 20fc8775e2SMircea Trofin 21fc8775e2SMircea Trofin using namespace llvm; 22fc8775e2SMircea Trofin using namespace llvm::ctx_profile; 23fc8775e2SMircea Trofin 24cc7308a1SMircea Trofin PGOCtxProfileWriter::PGOCtxProfileWriter( 25cc7308a1SMircea Trofin raw_ostream &Out, std::optional<unsigned> VersionOverride) 26cc7308a1SMircea Trofin : Writer(Out, 0) { 27cc7308a1SMircea Trofin static_assert(ContainerMagic.size() == 4); 28cc7308a1SMircea Trofin Out.write(ContainerMagic.data(), ContainerMagic.size()); 29cc7308a1SMircea Trofin Writer.EnterBlockInfoBlock(); 30cc7308a1SMircea Trofin { 31cc7308a1SMircea Trofin auto DescribeBlock = [&](unsigned ID, StringRef Name) { 32cc7308a1SMircea Trofin Writer.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, 33cc7308a1SMircea Trofin SmallVector<unsigned, 1>{ID}); 34cc7308a1SMircea Trofin Writer.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, 35cc7308a1SMircea Trofin llvm::arrayRefFromStringRef(Name)); 36cc7308a1SMircea Trofin }; 37cc7308a1SMircea Trofin SmallVector<uint64_t, 16> Data; 38cc7308a1SMircea Trofin auto DescribeRecord = [&](unsigned RecordID, StringRef Name) { 39cc7308a1SMircea Trofin Data.clear(); 40cc7308a1SMircea Trofin Data.push_back(RecordID); 41cc7308a1SMircea Trofin llvm::append_range(Data, Name); 42cc7308a1SMircea Trofin Writer.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, Data); 43cc7308a1SMircea Trofin }; 44cc7308a1SMircea Trofin DescribeBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID, "Metadata"); 45cc7308a1SMircea Trofin DescribeRecord(PGOCtxProfileRecords::Version, "Version"); 46cc7308a1SMircea Trofin DescribeBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID, "Context"); 47cc7308a1SMircea Trofin DescribeRecord(PGOCtxProfileRecords::Guid, "GUID"); 48cc7308a1SMircea Trofin DescribeRecord(PGOCtxProfileRecords::CalleeIndex, "CalleeIndex"); 49cc7308a1SMircea Trofin DescribeRecord(PGOCtxProfileRecords::Counters, "Counters"); 50cc7308a1SMircea Trofin } 51cc7308a1SMircea Trofin Writer.ExitBlock(); 52cc7308a1SMircea Trofin Writer.EnterSubblock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID, CodeLen); 53e4e3ff5aSKazu Hirata const auto Version = VersionOverride.value_or(CurrentVersion); 54cc7308a1SMircea Trofin Writer.EmitRecord(PGOCtxProfileRecords::Version, 55cc7308a1SMircea Trofin SmallVector<unsigned, 1>({Version})); 56cc7308a1SMircea Trofin } 57cc7308a1SMircea Trofin 58fc8775e2SMircea Trofin void PGOCtxProfileWriter::writeCounters(const ContextNode &Node) { 59fc8775e2SMircea Trofin Writer.EmitCode(bitc::UNABBREV_RECORD); 60fc8775e2SMircea Trofin Writer.EmitVBR(PGOCtxProfileRecords::Counters, VBREncodingBits); 61fc8775e2SMircea Trofin Writer.EmitVBR(Node.counters_size(), VBREncodingBits); 62fc8775e2SMircea Trofin for (uint32_t I = 0U; I < Node.counters_size(); ++I) 63fc8775e2SMircea Trofin Writer.EmitVBR64(Node.counters()[I], VBREncodingBits); 64fc8775e2SMircea Trofin } 65fc8775e2SMircea Trofin 66fc8775e2SMircea Trofin // recursively write all the subcontexts. We do need to traverse depth first to 67fc8775e2SMircea Trofin // model the context->subcontext implicitly, and since this captures call 68fc8775e2SMircea Trofin // stacks, we don't really need to be worried about stack overflow and we can 69fc8775e2SMircea Trofin // keep the implementation simple. 70fc8775e2SMircea Trofin void PGOCtxProfileWriter::writeImpl(std::optional<uint32_t> CallerIndex, 71fc8775e2SMircea Trofin const ContextNode &Node) { 72fc8775e2SMircea Trofin Writer.EnterSubblock(PGOCtxProfileBlockIDs::ContextNodeBlockID, CodeLen); 73fc8775e2SMircea Trofin Writer.EmitRecord(PGOCtxProfileRecords::Guid, 74fc8775e2SMircea Trofin SmallVector<uint64_t, 1>{Node.guid()}); 75fc8775e2SMircea Trofin if (CallerIndex) 76fc8775e2SMircea Trofin Writer.EmitRecord(PGOCtxProfileRecords::CalleeIndex, 77fc8775e2SMircea Trofin SmallVector<uint64_t, 1>{*CallerIndex}); 78fc8775e2SMircea Trofin writeCounters(Node); 79fc8775e2SMircea Trofin for (uint32_t I = 0U; I < Node.callsites_size(); ++I) 80fc8775e2SMircea Trofin for (const auto *Subcontext = Node.subContexts()[I]; Subcontext; 81fc8775e2SMircea Trofin Subcontext = Subcontext->next()) 82fc8775e2SMircea Trofin writeImpl(I, *Subcontext); 83fc8775e2SMircea Trofin Writer.ExitBlock(); 84fc8775e2SMircea Trofin } 85fc8775e2SMircea Trofin 86fc8775e2SMircea Trofin void PGOCtxProfileWriter::write(const ContextNode &RootNode) { 87fc8775e2SMircea Trofin writeImpl(std::nullopt, RootNode); 88fc8775e2SMircea Trofin } 891022323cSMircea Trofin 901022323cSMircea Trofin namespace { 91*b15845c0SMircea Trofin 92*b15845c0SMircea Trofin /// Representation of the context node suitable for yaml serialization / 93*b15845c0SMircea Trofin /// deserialization. 94*b15845c0SMircea Trofin struct SerializableCtxRepresentation { 95*b15845c0SMircea Trofin ctx_profile::GUID Guid = 0; 96*b15845c0SMircea Trofin std::vector<uint64_t> Counters; 97*b15845c0SMircea Trofin std::vector<std::vector<SerializableCtxRepresentation>> Callsites; 98*b15845c0SMircea Trofin }; 99*b15845c0SMircea Trofin 1001022323cSMircea Trofin ctx_profile::ContextNode * 1011022323cSMircea Trofin createNode(std::vector<std::unique_ptr<char[]>> &Nodes, 10263293558SMircea Trofin const std::vector<SerializableCtxRepresentation> &DCList); 1031022323cSMircea Trofin 1041022323cSMircea Trofin // Convert a DeserializableCtx into a ContextNode, potentially linking it to 1051022323cSMircea Trofin // its sibling (e.g. callee at same callsite) "Next". 1061022323cSMircea Trofin ctx_profile::ContextNode * 1071022323cSMircea Trofin createNode(std::vector<std::unique_ptr<char[]>> &Nodes, 10863293558SMircea Trofin const SerializableCtxRepresentation &DC, 1091022323cSMircea Trofin ctx_profile::ContextNode *Next = nullptr) { 1101022323cSMircea Trofin auto AllocSize = ctx_profile::ContextNode::getAllocSize(DC.Counters.size(), 1111022323cSMircea Trofin DC.Callsites.size()); 1121022323cSMircea Trofin auto *Mem = Nodes.emplace_back(std::make_unique<char[]>(AllocSize)).get(); 1131022323cSMircea Trofin std::memset(Mem, 0, AllocSize); 1141022323cSMircea Trofin auto *Ret = new (Mem) ctx_profile::ContextNode(DC.Guid, DC.Counters.size(), 1151022323cSMircea Trofin DC.Callsites.size(), Next); 1161022323cSMircea Trofin std::memcpy(Ret->counters(), DC.Counters.data(), 1171022323cSMircea Trofin sizeof(uint64_t) * DC.Counters.size()); 1181022323cSMircea Trofin for (const auto &[I, DCList] : llvm::enumerate(DC.Callsites)) 1191022323cSMircea Trofin Ret->subContexts()[I] = createNode(Nodes, DCList); 1201022323cSMircea Trofin return Ret; 1211022323cSMircea Trofin } 1221022323cSMircea Trofin 12363293558SMircea Trofin // Convert a list of SerializableCtxRepresentation into a linked list of 12463293558SMircea Trofin // ContextNodes. 1251022323cSMircea Trofin ctx_profile::ContextNode * 1261022323cSMircea Trofin createNode(std::vector<std::unique_ptr<char[]>> &Nodes, 12763293558SMircea Trofin const std::vector<SerializableCtxRepresentation> &DCList) { 1281022323cSMircea Trofin ctx_profile::ContextNode *List = nullptr; 1291022323cSMircea Trofin for (const auto &DC : DCList) 1301022323cSMircea Trofin List = createNode(Nodes, DC, List); 1311022323cSMircea Trofin return List; 1321022323cSMircea Trofin } 1331022323cSMircea Trofin } // namespace 1341022323cSMircea Trofin 13563293558SMircea Trofin LLVM_YAML_IS_SEQUENCE_VECTOR(SerializableCtxRepresentation) 13663293558SMircea Trofin LLVM_YAML_IS_SEQUENCE_VECTOR(std::vector<SerializableCtxRepresentation>) 13763293558SMircea Trofin template <> struct yaml::MappingTraits<SerializableCtxRepresentation> { 13863293558SMircea Trofin static void mapping(yaml::IO &IO, SerializableCtxRepresentation &SCR) { 13963293558SMircea Trofin IO.mapRequired("Guid", SCR.Guid); 14063293558SMircea Trofin IO.mapRequired("Counters", SCR.Counters); 14163293558SMircea Trofin IO.mapOptional("Callsites", SCR.Callsites); 1421022323cSMircea Trofin } 14363293558SMircea Trofin }; 1441022323cSMircea Trofin 14563293558SMircea Trofin Error llvm::createCtxProfFromYAML(StringRef Profile, raw_ostream &Out) { 14663293558SMircea Trofin yaml::Input In(Profile); 14763293558SMircea Trofin std::vector<SerializableCtxRepresentation> DCList; 14863293558SMircea Trofin In >> DCList; 14963293558SMircea Trofin if (In.error()) 15063293558SMircea Trofin return createStringError(In.error(), "incorrect yaml content"); 1511022323cSMircea Trofin std::vector<std::unique_ptr<char[]>> Nodes; 1521022323cSMircea Trofin std::error_code EC; 1531022323cSMircea Trofin if (EC) 1541022323cSMircea Trofin return createStringError(EC, "failed to open output"); 1551022323cSMircea Trofin PGOCtxProfileWriter Writer(Out); 1561022323cSMircea Trofin for (const auto &DC : DCList) { 1571022323cSMircea Trofin auto *TopList = createNode(Nodes, DC); 1581022323cSMircea Trofin if (!TopList) 1591022323cSMircea Trofin return createStringError( 1601022323cSMircea Trofin "Unexpected error converting internal structure to ctx profile"); 1611022323cSMircea Trofin Writer.write(*TopList); 1621022323cSMircea Trofin } 1631022323cSMircea Trofin if (EC) 1641022323cSMircea Trofin return createStringError(EC, "failed to write output"); 1651022323cSMircea Trofin return Error::success(); 1661022323cSMircea Trofin } 167