Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:44:24

0001 //===--- PGOCtxProfReader.h - Contextual profile reader ---------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 ///
0009 /// \file
0010 ///
0011 /// Reader for contextual iFDO profile, which comes in bitstream format.
0012 ///
0013 //===----------------------------------------------------------------------===//
0014 
0015 #ifndef LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H
0016 #define LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H
0017 
0018 #include "llvm/Bitstream/BitstreamReader.h"
0019 #include "llvm/IR/GlobalValue.h"
0020 #include "llvm/ProfileData/PGOCtxProfWriter.h"
0021 #include "llvm/Support/Error.h"
0022 #include <map>
0023 
0024 namespace llvm {
0025 class PGOContextualProfile;
0026 class PGOCtxProfContext;
0027 
0028 namespace internal {
0029 // When we traverse the contextual profile, we typically want to visit contexts
0030 // pertaining to a specific function. To avoid traversing the whole tree, we
0031 // want to keep a per-function list - which will be in preorder - of that
0032 // function's contexts. This happens in PGOContextualProfile. For memory use
0033 // efficiency, we want to make PGOCtxProfContext an intrusive double-linked list
0034 // node. We need to handle the cases where PGOCtxProfContext nodes are moved and
0035 // deleted: in both cases, we need to update the index (==list). We can do that
0036 // directly from the node in the list, without knowing who the "parent" of the
0037 // list is. That makes the ADT ilist overkill here. Finally, IndexNode is meant
0038 // to be an implementation detail of PGOCtxProfContext, and the only reason it's
0039 // factored out is to avoid implementing move semantics for all its members.
0040 class IndexNode {
0041   // This class' members are intentionally private - it's a convenience
0042   // implementation detail.
0043   friend class ::llvm::PGOCtxProfContext;
0044   friend class ::llvm::PGOContextualProfile;
0045 
0046   IndexNode *Previous = nullptr;
0047   IndexNode *Next = nullptr;
0048 
0049   ~IndexNode() {
0050     if (Next)
0051       Next->Previous = Previous;
0052     if (Previous)
0053       Previous->Next = Next;
0054   }
0055 
0056   IndexNode(const IndexNode &Other) = delete;
0057 
0058   IndexNode(IndexNode &&Other) {
0059     // Copy the neighbor info
0060     Next = Other.Next;
0061     Previous = Other.Previous;
0062 
0063     // Update the neighbors to point to this object
0064     if (Other.Next)
0065       Other.Next->Previous = this;
0066     if (Other.Previous)
0067       Other.Previous->Next = this;
0068 
0069     // Make sure the dtor is a noop
0070     Other.Next = nullptr;
0071     Other.Previous = nullptr;
0072   }
0073   IndexNode() = default;
0074 };
0075 } // namespace internal
0076 
0077 /// A node (context) in the loaded contextual profile, suitable for mutation
0078 /// during IPO passes. We generally expect a fraction of counters and
0079 /// callsites to be populated. We continue to model counters as vectors, but
0080 /// callsites are modeled as a map of a map. The expectation is that, typically,
0081 /// there is a small number of indirect targets (usually, 1 for direct calls);
0082 /// but potentially a large number of callsites, and, as inlining progresses,
0083 /// the callsite count of a caller will grow.
0084 class PGOCtxProfContext final : public internal::IndexNode {
0085 public:
0086   using CallTargetMapTy = std::map<GlobalValue::GUID, PGOCtxProfContext>;
0087   using CallsiteMapTy = std::map<uint32_t, CallTargetMapTy>;
0088 
0089 private:
0090   friend class PGOCtxProfileReader;
0091   friend class PGOContextualProfile;
0092 
0093   GlobalValue::GUID GUID = 0;
0094   SmallVector<uint64_t, 16> Counters;
0095   CallsiteMapTy Callsites;
0096 
0097   PGOCtxProfContext(GlobalValue::GUID G, SmallVectorImpl<uint64_t> &&Counters)
0098       : GUID(G), Counters(std::move(Counters)) {}
0099 
0100   Expected<PGOCtxProfContext &>
0101   getOrEmplace(uint32_t Index, GlobalValue::GUID G,
0102                SmallVectorImpl<uint64_t> &&Counters);
0103 
0104   // Create a bogus context object, used for anchoring the index double linked
0105   // list - see IndexNode
0106   PGOCtxProfContext() = default;
0107 
0108 public:
0109   PGOCtxProfContext(const PGOCtxProfContext &) = delete;
0110   PGOCtxProfContext &operator=(const PGOCtxProfContext &) = delete;
0111   PGOCtxProfContext(PGOCtxProfContext &&) = default;
0112   PGOCtxProfContext &operator=(PGOCtxProfContext &&) = delete;
0113 
0114   GlobalValue::GUID guid() const { return GUID; }
0115   const SmallVectorImpl<uint64_t> &counters() const { return Counters; }
0116   SmallVectorImpl<uint64_t> &counters() { return Counters; }
0117 
0118   uint64_t getEntrycount() const {
0119     assert(!Counters.empty() &&
0120            "Functions are expected to have at their entry BB instrumented, so "
0121            "there should always be at least 1 counter.");
0122     return Counters[0];
0123   }
0124 
0125   const CallsiteMapTy &callsites() const { return Callsites; }
0126   CallsiteMapTy &callsites() { return Callsites; }
0127 
0128   void ingestContext(uint32_t CSId, PGOCtxProfContext &&Other) {
0129     callsites()[CSId].emplace(Other.guid(), std::move(Other));
0130   }
0131 
0132   void ingestAllContexts(uint32_t CSId, CallTargetMapTy &&Other) {
0133     auto [_, Inserted] = callsites().try_emplace(CSId, std::move(Other));
0134     (void)Inserted;
0135     assert(Inserted &&
0136            "CSId was expected to be newly created as result of e.g. inlining");
0137   }
0138 
0139   void resizeCounters(uint32_t Size) { Counters.resize(Size); }
0140 
0141   bool hasCallsite(uint32_t I) const {
0142     return Callsites.find(I) != Callsites.end();
0143   }
0144 
0145   const CallTargetMapTy &callsite(uint32_t I) const {
0146     assert(hasCallsite(I) && "Callsite not found");
0147     return Callsites.find(I)->second;
0148   }
0149 
0150   CallTargetMapTy &callsite(uint32_t I) {
0151     assert(hasCallsite(I) && "Callsite not found");
0152     return Callsites.find(I)->second;
0153   }
0154 
0155   /// Insert this node's GUID as well as the GUIDs of the transitive closure of
0156   /// child nodes, into the provided set (technically, all that is required of
0157   /// `TSetOfGUIDs` is to have an `insert(GUID)` member)
0158   template <class TSetOfGUIDs>
0159   void getContainedGuids(TSetOfGUIDs &Guids) const {
0160     Guids.insert(GUID);
0161     for (const auto &[_, Callsite] : Callsites)
0162       for (const auto &[_, Callee] : Callsite)
0163         Callee.getContainedGuids(Guids);
0164   }
0165 };
0166 
0167 class PGOCtxProfileReader final {
0168   StringRef Magic;
0169   BitstreamCursor Cursor;
0170   Expected<BitstreamEntry> advance();
0171   Error readMetadata();
0172   Error wrongValue(const Twine &);
0173   Error unsupported(const Twine &);
0174 
0175   Expected<std::pair<std::optional<uint32_t>, PGOCtxProfContext>>
0176   readContext(bool ExpectIndex);
0177   bool canReadContext();
0178 
0179 public:
0180   PGOCtxProfileReader(StringRef Buffer)
0181       : Magic(Buffer.substr(0, PGOCtxProfileWriter::ContainerMagic.size())),
0182         Cursor(Buffer.substr(PGOCtxProfileWriter::ContainerMagic.size())) {}
0183 
0184   Expected<std::map<GlobalValue::GUID, PGOCtxProfContext>> loadContexts();
0185 };
0186 
0187 void convertCtxProfToYaml(raw_ostream &OS,
0188                           const PGOCtxProfContext::CallTargetMapTy &);
0189 } // namespace llvm
0190 #endif