1 //===- PGOCtxProfWriter.cpp - Contextual Instrumentation profile writer ---===// 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 // Write a contextual profile to bitstream. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/ProfileData/PGOCtxProfWriter.h" 14 #include "llvm/Bitstream/BitCodeEnums.h" 15 #include "llvm/ProfileData/CtxInstrContextNode.h" 16 #include "llvm/Support/Error.h" 17 #include "llvm/Support/MemoryBuffer.h" 18 #include "llvm/Support/YAMLTraits.h" 19 #include "llvm/Support/raw_ostream.h" 20 21 using namespace llvm; 22 using namespace llvm::ctx_profile; 23 24 PGOCtxProfileWriter::PGOCtxProfileWriter( 25 raw_ostream &Out, std::optional<unsigned> VersionOverride) 26 : Writer(Out, 0) { 27 static_assert(ContainerMagic.size() == 4); 28 Out.write(ContainerMagic.data(), ContainerMagic.size()); 29 Writer.EnterBlockInfoBlock(); 30 { 31 auto DescribeBlock = [&](unsigned ID, StringRef Name) { 32 Writer.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, 33 SmallVector<unsigned, 1>{ID}); 34 Writer.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, 35 llvm::arrayRefFromStringRef(Name)); 36 }; 37 SmallVector<uint64_t, 16> Data; 38 auto DescribeRecord = [&](unsigned RecordID, StringRef Name) { 39 Data.clear(); 40 Data.push_back(RecordID); 41 llvm::append_range(Data, Name); 42 Writer.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, Data); 43 }; 44 DescribeBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID, "Metadata"); 45 DescribeRecord(PGOCtxProfileRecords::Version, "Version"); 46 DescribeBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID, "Context"); 47 DescribeRecord(PGOCtxProfileRecords::Guid, "GUID"); 48 DescribeRecord(PGOCtxProfileRecords::CalleeIndex, "CalleeIndex"); 49 DescribeRecord(PGOCtxProfileRecords::Counters, "Counters"); 50 } 51 Writer.ExitBlock(); 52 Writer.EnterSubblock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID, CodeLen); 53 const auto Version = VersionOverride.value_or(CurrentVersion); 54 Writer.EmitRecord(PGOCtxProfileRecords::Version, 55 SmallVector<unsigned, 1>({Version})); 56 } 57 58 void PGOCtxProfileWriter::writeCounters(const ContextNode &Node) { 59 Writer.EmitCode(bitc::UNABBREV_RECORD); 60 Writer.EmitVBR(PGOCtxProfileRecords::Counters, VBREncodingBits); 61 Writer.EmitVBR(Node.counters_size(), VBREncodingBits); 62 for (uint32_t I = 0U; I < Node.counters_size(); ++I) 63 Writer.EmitVBR64(Node.counters()[I], VBREncodingBits); 64 } 65 66 // recursively write all the subcontexts. We do need to traverse depth first to 67 // model the context->subcontext implicitly, and since this captures call 68 // stacks, we don't really need to be worried about stack overflow and we can 69 // keep the implementation simple. 70 void PGOCtxProfileWriter::writeImpl(std::optional<uint32_t> CallerIndex, 71 const ContextNode &Node) { 72 Writer.EnterSubblock(PGOCtxProfileBlockIDs::ContextNodeBlockID, CodeLen); 73 Writer.EmitRecord(PGOCtxProfileRecords::Guid, 74 SmallVector<uint64_t, 1>{Node.guid()}); 75 if (CallerIndex) 76 Writer.EmitRecord(PGOCtxProfileRecords::CalleeIndex, 77 SmallVector<uint64_t, 1>{*CallerIndex}); 78 writeCounters(Node); 79 for (uint32_t I = 0U; I < Node.callsites_size(); ++I) 80 for (const auto *Subcontext = Node.subContexts()[I]; Subcontext; 81 Subcontext = Subcontext->next()) 82 writeImpl(I, *Subcontext); 83 Writer.ExitBlock(); 84 } 85 86 void PGOCtxProfileWriter::write(const ContextNode &RootNode) { 87 writeImpl(std::nullopt, RootNode); 88 } 89 90 namespace { 91 92 /// Representation of the context node suitable for yaml serialization / 93 /// deserialization. 94 struct SerializableCtxRepresentation { 95 ctx_profile::GUID Guid = 0; 96 std::vector<uint64_t> Counters; 97 std::vector<std::vector<SerializableCtxRepresentation>> Callsites; 98 }; 99 100 ctx_profile::ContextNode * 101 createNode(std::vector<std::unique_ptr<char[]>> &Nodes, 102 const std::vector<SerializableCtxRepresentation> &DCList); 103 104 // Convert a DeserializableCtx into a ContextNode, potentially linking it to 105 // its sibling (e.g. callee at same callsite) "Next". 106 ctx_profile::ContextNode * 107 createNode(std::vector<std::unique_ptr<char[]>> &Nodes, 108 const SerializableCtxRepresentation &DC, 109 ctx_profile::ContextNode *Next = nullptr) { 110 auto AllocSize = ctx_profile::ContextNode::getAllocSize(DC.Counters.size(), 111 DC.Callsites.size()); 112 auto *Mem = Nodes.emplace_back(std::make_unique<char[]>(AllocSize)).get(); 113 std::memset(Mem, 0, AllocSize); 114 auto *Ret = new (Mem) ctx_profile::ContextNode(DC.Guid, DC.Counters.size(), 115 DC.Callsites.size(), Next); 116 std::memcpy(Ret->counters(), DC.Counters.data(), 117 sizeof(uint64_t) * DC.Counters.size()); 118 for (const auto &[I, DCList] : llvm::enumerate(DC.Callsites)) 119 Ret->subContexts()[I] = createNode(Nodes, DCList); 120 return Ret; 121 } 122 123 // Convert a list of SerializableCtxRepresentation into a linked list of 124 // ContextNodes. 125 ctx_profile::ContextNode * 126 createNode(std::vector<std::unique_ptr<char[]>> &Nodes, 127 const std::vector<SerializableCtxRepresentation> &DCList) { 128 ctx_profile::ContextNode *List = nullptr; 129 for (const auto &DC : DCList) 130 List = createNode(Nodes, DC, List); 131 return List; 132 } 133 } // namespace 134 135 LLVM_YAML_IS_SEQUENCE_VECTOR(SerializableCtxRepresentation) 136 LLVM_YAML_IS_SEQUENCE_VECTOR(std::vector<SerializableCtxRepresentation>) 137 template <> struct yaml::MappingTraits<SerializableCtxRepresentation> { 138 static void mapping(yaml::IO &IO, SerializableCtxRepresentation &SCR) { 139 IO.mapRequired("Guid", SCR.Guid); 140 IO.mapRequired("Counters", SCR.Counters); 141 IO.mapOptional("Callsites", SCR.Callsites); 142 } 143 }; 144 145 Error llvm::createCtxProfFromYAML(StringRef Profile, raw_ostream &Out) { 146 yaml::Input In(Profile); 147 std::vector<SerializableCtxRepresentation> DCList; 148 In >> DCList; 149 if (In.error()) 150 return createStringError(In.error(), "incorrect yaml content"); 151 std::vector<std::unique_ptr<char[]>> Nodes; 152 std::error_code EC; 153 if (EC) 154 return createStringError(EC, "failed to open output"); 155 PGOCtxProfileWriter Writer(Out); 156 for (const auto &DC : DCList) { 157 auto *TopList = createNode(Nodes, DC); 158 if (!TopList) 159 return createStringError( 160 "Unexpected error converting internal structure to ctx profile"); 161 Writer.write(*TopList); 162 } 163 if (EC) 164 return createStringError(EC, "failed to write output"); 165 return Error::success(); 166 } 167