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