1 //===--- PGOCtxProfReader.h - Contextual profile reader ---------*- C++ -*-===// 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 /// \file 10 /// 11 /// Reader for contextual iFDO profile, which comes in bitstream format. 12 /// 13 //===----------------------------------------------------------------------===// 14 15 #ifndef LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H 16 #define LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H 17 18 #include "llvm/Bitstream/BitstreamReader.h" 19 #include "llvm/IR/GlobalValue.h" 20 #include "llvm/ProfileData/PGOCtxProfWriter.h" 21 #include "llvm/Support/Compiler.h" 22 #include "llvm/Support/Error.h" 23 #include <map> 24 25 namespace llvm { 26 class PGOContextualProfile; 27 class PGOCtxProfContext; 28 29 namespace internal { 30 // When we traverse the contextual profile, we typically want to visit contexts 31 // pertaining to a specific function. To avoid traversing the whole tree, we 32 // want to keep a per-function list - which will be in preorder - of that 33 // function's contexts. This happens in PGOContextualProfile. For memory use 34 // efficiency, we want to make PGOCtxProfContext an intrusive double-linked list 35 // node. We need to handle the cases where PGOCtxProfContext nodes are moved and 36 // deleted: in both cases, we need to update the index (==list). We can do that 37 // directly from the node in the list, without knowing who the "parent" of the 38 // list is. That makes the ADT ilist overkill here. Finally, IndexNode is meant 39 // to be an implementation detail of PGOCtxProfContext, and the only reason it's 40 // factored out is to avoid implementing move semantics for all its members. 41 class IndexNode { 42 // This class' members are intentionally private - it's a convenience 43 // implementation detail. 44 friend class ::llvm::PGOCtxProfContext; 45 friend class ::llvm::PGOContextualProfile; 46 47 IndexNode *Previous = nullptr; 48 IndexNode *Next = nullptr; 49 ~IndexNode()50 ~IndexNode() { 51 if (Next) 52 Next->Previous = Previous; 53 if (Previous) 54 Previous->Next = Next; 55 } 56 57 IndexNode(const IndexNode &Other) = delete; 58 IndexNode(IndexNode && Other)59 IndexNode(IndexNode &&Other) { 60 // Copy the neighbor info 61 Next = Other.Next; 62 Previous = Other.Previous; 63 64 // Update the neighbors to point to this object 65 if (Other.Next) 66 Other.Next->Previous = this; 67 if (Other.Previous) 68 Other.Previous->Next = this; 69 70 // Make sure the dtor is a noop 71 Other.Next = nullptr; 72 Other.Previous = nullptr; 73 } 74 IndexNode() = default; 75 }; 76 } // namespace internal 77 78 // Setting initial capacity to 1 because all contexts must have at least 1 79 // counter, and then, because all contexts belonging to a function have the same 80 // size, there'll be at most one other heap allocation. 81 using CtxProfFlatProfile = 82 std::map<GlobalValue::GUID, SmallVector<uint64_t, 1>>; 83 84 /// A node (context) in the loaded contextual profile, suitable for mutation 85 /// during IPO passes. We generally expect a fraction of counters and 86 /// callsites to be populated. We continue to model counters as vectors, but 87 /// callsites are modeled as a map of a map. The expectation is that, typically, 88 /// there is a small number of indirect targets (usually, 1 for direct calls); 89 /// but potentially a large number of callsites, and, as inlining progresses, 90 /// the callsite count of a caller will grow. 91 class PGOCtxProfContext final : public internal::IndexNode { 92 public: 93 using CallTargetMapTy = std::map<GlobalValue::GUID, PGOCtxProfContext>; 94 using CallsiteMapTy = std::map<uint32_t, CallTargetMapTy>; 95 96 private: 97 friend class PGOCtxProfileReader; 98 friend class PGOContextualProfile; 99 100 GlobalValue::GUID GUID = 0; 101 SmallVector<uint64_t, 16> Counters; 102 const std::optional<uint64_t> RootEntryCount{}; 103 std::optional<CtxProfFlatProfile> Unhandled{}; 104 CallsiteMapTy Callsites; 105 106 PGOCtxProfContext( 107 GlobalValue::GUID G, SmallVectorImpl<uint64_t> &&Counters, 108 std::optional<uint64_t> RootEntryCount = std::nullopt, 109 std::optional<CtxProfFlatProfile> &&Unhandled = std::nullopt) GUID(G)110 : GUID(G), Counters(std::move(Counters)), RootEntryCount(RootEntryCount), 111 Unhandled(std::move(Unhandled)) { 112 assert(RootEntryCount.has_value() == Unhandled.has_value()); 113 } 114 115 Expected<PGOCtxProfContext &> 116 getOrEmplace(uint32_t Index, GlobalValue::GUID G, 117 SmallVectorImpl<uint64_t> &&Counters); 118 119 // Create a bogus context object, used for anchoring the index double linked 120 // list - see IndexNode 121 PGOCtxProfContext() = default; 122 123 public: 124 PGOCtxProfContext(const PGOCtxProfContext &) = delete; 125 PGOCtxProfContext &operator=(const PGOCtxProfContext &) = delete; 126 PGOCtxProfContext(PGOCtxProfContext &&) = default; 127 PGOCtxProfContext &operator=(PGOCtxProfContext &&) = delete; 128 guid()129 GlobalValue::GUID guid() const { return GUID; } counters()130 const SmallVectorImpl<uint64_t> &counters() const { return Counters; } counters()131 SmallVectorImpl<uint64_t> &counters() { return Counters; } 132 isRoot()133 bool isRoot() const { return RootEntryCount.has_value(); } getTotalRootEntryCount()134 uint64_t getTotalRootEntryCount() const { return RootEntryCount.value(); } 135 getUnhandled()136 const CtxProfFlatProfile &getUnhandled() const { return Unhandled.value(); } 137 getEntrycount()138 uint64_t getEntrycount() const { 139 assert(!Counters.empty() && 140 "Functions are expected to have at their entry BB instrumented, so " 141 "there should always be at least 1 counter."); 142 return Counters[0]; 143 } 144 callsites()145 const CallsiteMapTy &callsites() const { return Callsites; } callsites()146 CallsiteMapTy &callsites() { return Callsites; } 147 ingestContext(uint32_t CSId,PGOCtxProfContext && Other)148 void ingestContext(uint32_t CSId, PGOCtxProfContext &&Other) { 149 callsites()[CSId].emplace(Other.guid(), std::move(Other)); 150 } 151 ingestAllContexts(uint32_t CSId,CallTargetMapTy && Other)152 void ingestAllContexts(uint32_t CSId, CallTargetMapTy &&Other) { 153 auto [_, Inserted] = callsites().try_emplace(CSId, std::move(Other)); 154 (void)Inserted; 155 assert(Inserted && 156 "CSId was expected to be newly created as result of e.g. inlining"); 157 } 158 resizeCounters(uint32_t Size)159 void resizeCounters(uint32_t Size) { Counters.resize(Size); } 160 hasCallsite(uint32_t I)161 bool hasCallsite(uint32_t I) const { 162 return Callsites.find(I) != Callsites.end(); 163 } 164 callsite(uint32_t I)165 const CallTargetMapTy &callsite(uint32_t I) const { 166 assert(hasCallsite(I) && "Callsite not found"); 167 return Callsites.find(I)->second; 168 } 169 callsite(uint32_t I)170 CallTargetMapTy &callsite(uint32_t I) { 171 assert(hasCallsite(I) && "Callsite not found"); 172 return Callsites.find(I)->second; 173 } 174 175 /// Insert this node's GUID as well as the GUIDs of the transitive closure of 176 /// child nodes, into the provided set (technically, all that is required of 177 /// `TSetOfGUIDs` is to have an `insert(GUID)` member) 178 template <class TSetOfGUIDs> getContainedGuids(TSetOfGUIDs & Guids)179 void getContainedGuids(TSetOfGUIDs &Guids) const { 180 Guids.insert(GUID); 181 for (const auto &[_, Callsite] : Callsites) 182 for (const auto &[_, Callee] : Callsite) 183 Callee.getContainedGuids(Guids); 184 } 185 }; 186 187 using CtxProfContextualProfiles = 188 std::map<GlobalValue::GUID, PGOCtxProfContext>; 189 struct PGOCtxProfile { 190 CtxProfContextualProfiles Contexts; 191 CtxProfFlatProfile FlatProfiles; 192 193 PGOCtxProfile() = default; 194 PGOCtxProfile(const PGOCtxProfile &) = delete; 195 PGOCtxProfile(PGOCtxProfile &&) = default; 196 PGOCtxProfile &operator=(PGOCtxProfile &&) = default; 197 }; 198 199 class PGOCtxProfileReader final { 200 StringRef Magic; 201 BitstreamCursor Cursor; 202 Expected<BitstreamEntry> advance(); 203 Error readMetadata(); 204 Error wrongValue(const Twine &Msg); 205 Error unsupported(const Twine &Msg); 206 207 Expected<std::pair<std::optional<uint32_t>, PGOCtxProfContext>> 208 readProfile(PGOCtxProfileBlockIDs Kind); 209 210 bool tryGetNextKnownBlockID(PGOCtxProfileBlockIDs &ID); 211 bool canEnterBlockWithID(PGOCtxProfileBlockIDs ID); 212 Error enterBlockWithID(PGOCtxProfileBlockIDs ID); 213 214 Error loadContexts(CtxProfContextualProfiles &P); 215 Error loadFlatProfiles(CtxProfFlatProfile &P); 216 Error loadFlatProfileList(CtxProfFlatProfile &P); 217 218 public: PGOCtxProfileReader(StringRef Buffer)219 PGOCtxProfileReader(StringRef Buffer) 220 : Magic(Buffer.substr(0, PGOCtxProfileWriter::ContainerMagic.size())), 221 Cursor(Buffer.substr(PGOCtxProfileWriter::ContainerMagic.size())) {} 222 223 LLVM_ABI Expected<PGOCtxProfile> loadProfiles(); 224 }; 225 226 LLVM_ABI void convertCtxProfToYaml(raw_ostream &OS, 227 const PGOCtxProfile &Profile); 228 } // namespace llvm 229 #endif 230