//===- DataAccessProf.h - Data access profile format support ---------*- C++ //-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains support to construct and use data access profiles. // // For the original RFC of this pass please see // https://discourse.llvm.org/t/rfc-profile-guided-static-data-partitioning/83744 // //===----------------------------------------------------------------------===// #ifndef LLVM_PROFILEDATA_DATAACCESSPROF_H_ #define LLVM_PROFILEDATA_DATAACCESSPROF_H_ #include "llvm/ADT/DenseMapInfoVariant.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" #include "llvm/Support/StringSaver.h" #include #include #include namespace llvm { namespace memprof { /// The location of data in the source code. Used by profile lookup API. struct SourceLocation { SourceLocation(StringRef FileNameRef, uint32_t Line) : FileName(FileNameRef.str()), Line(Line) {} // Empty constructor is used in yaml conversion. SourceLocation() = default; /// The filename where the data is located. std::string FileName; /// The line number in the source code. uint32_t Line; }; namespace internal { // Conceptually similar to SourceLocation except that FileNames are StringRef of // which strings are owned by `DataAccessProfData`. Used by `DataAccessProfData` // to represent data locations internally. struct SourceLocationRef { SourceLocationRef(StringRef FileNameRef, uint32_t Line) : FileName(FileNameRef), Line(Line) {} // The filename where the data is located. StringRef FileName; // The line number in the source code. uint32_t Line; }; // The data access profiles for a symbol. Used by `DataAccessProfData` // to represent records internally. struct DataAccessProfRecordRef { DataAccessProfRecordRef(uint64_t SymbolID, uint64_t AccessCount, bool IsStringLiteral) : SymbolID(SymbolID), AccessCount(AccessCount), IsStringLiteral(IsStringLiteral) {} // Represents a data symbol. The semantic comes in two forms: a symbol index // for symbol name if `IsStringLiteral` is false, or the hash of a string // content if `IsStringLiteral` is true. For most of the symbolizable static // data, the mangled symbol names remain stable relative to the source code // and therefore used to identify symbols across binary releases. String // literals have unstable name patterns like `.str.N[.llvm.hash]`, so we use // the content hash instead. This is a required field. uint64_t SymbolID; // The access count of symbol. Required. uint64_t AccessCount; // True iff this is a record for string literal (symbols with name pattern // `.str.*` in the symbol table). Required. bool IsStringLiteral; // The locations of data in the source code. Optional. llvm::SmallVector Locations; }; } // namespace internal // SymbolID is either a string representing symbol name if the symbol has // stable mangled name relative to source code, or a uint64_t representing the // content hash of a string literal (with unstable name patterns like // `.str.N[.llvm.hash]`). The StringRef is owned by the class's saver object. using SymbolHandleRef = std::variant; // The senamtic is the same as `SymbolHandleRef` above. The strings are owned. using SymbolHandle = std::variant; /// The data access profiles for a symbol. struct DataAccessProfRecord { public: DataAccessProfRecord(SymbolHandleRef SymHandleRef, uint64_t AccessCount, ArrayRef LocRefs) : AccessCount(AccessCount) { if (std::holds_alternative(SymHandleRef)) { SymHandle = std::get(SymHandleRef).str(); } else SymHandle = std::get(SymHandleRef); for (auto Loc : LocRefs) Locations.emplace_back(Loc.FileName, Loc.Line); } // Empty constructor is used in yaml conversion. DataAccessProfRecord() = default; SymbolHandle SymHandle; uint64_t AccessCount = 0; // The locations of data in the source code. Optional. SmallVector Locations; }; /// Encapsulates the data access profile data and the methods to operate on /// it. This class provides profile look-up, serialization and /// deserialization. class DataAccessProfData { public: // Use MapVector to keep input order of strings for serialization and // deserialization. using StringToIndexMap = llvm::MapVector; DataAccessProfData() : Saver(Allocator) {} /// Serialize profile data to the output stream. /// Storage layout: /// - Serialized strings. /// - The encoded hashes. /// - Records. LLVM_ABI Error serialize(ProfOStream &OS) const; /// Deserialize this class from the given buffer. LLVM_ABI Error deserialize(const unsigned char *&Ptr); /// Returns a profile record for \p SymbolID, or std::nullopt if there /// isn't a record. Internally, this function will canonicalize the symbol /// name before the lookup. LLVM_ABI std::optional getProfileRecord(const SymbolHandleRef SymID) const; /// Returns true if \p SymID is seen in profiled binaries and cold. LLVM_ABI bool isKnownColdSymbol(const SymbolHandleRef SymID) const; /// Methods to set symbolized data access profile. Returns error if /// duplicated symbol names or content hashes are seen. The user of this /// class should aggregate counters that correspond to the same symbol name /// or with the same string literal hash before calling 'set*' methods. LLVM_ABI Error setDataAccessProfile(SymbolHandleRef SymbolID, uint64_t AccessCount); /// Similar to the method above, for records with \p Locations representing /// the `filename:line` where this symbol shows up. Note because of linker's /// merge of identical symbols (e.g., unnamed_addr string literals), one /// symbol is likely to have multiple locations. LLVM_ABI Error setDataAccessProfile(SymbolHandleRef SymbolID, uint64_t AccessCount, ArrayRef Locations); /// Add a symbol that's seen in the profiled binary without samples. LLVM_ABI Error addKnownSymbolWithoutSamples(SymbolHandleRef SymbolID); /// The following methods return array reference for various internal data /// structures. ArrayRef getStrToIndexMapRef() const { return StrToIndexMap.getArrayRef(); } ArrayRef< MapVector::value_type> getRecords() const { return Records.getArrayRef(); } ArrayRef getKnownColdSymbols() const { return KnownColdSymbols.getArrayRef(); } ArrayRef getKnownColdHashes() const { return KnownColdHashes.getArrayRef(); } [[nodiscard]] bool empty() const { return Records.empty() && KnownColdSymbols.empty() && KnownColdHashes.empty(); } private: /// Serialize the symbol strings into the output stream. Error serializeSymbolsAndFilenames(ProfOStream &OS) const; /// Deserialize the symbol strings from \p Ptr and increment \p Ptr to the /// start of the next payload. Error deserializeSymbolsAndFilenames(const unsigned char *&Ptr, const uint64_t NumSampledSymbols, const uint64_t NumColdKnownSymbols); /// Decode the records and increment \p Ptr to the start of the next /// payload. Error deserializeRecords(const unsigned char *&Ptr); /// A helper function to compute a storage index for \p SymbolID. uint64_t getEncodedIndex(const SymbolHandleRef SymbolID) const; // Keeps owned copies of the input strings. // NOTE: Keep `Saver` initialized before other class members that reference // its string copies and destructed after they are destructed. llvm::BumpPtrAllocator Allocator; llvm::UniqueStringSaver Saver; // `Records` stores the records. MapVector Records; StringToIndexMap StrToIndexMap; llvm::SetVector KnownColdHashes; llvm::SetVector KnownColdSymbols; }; } // namespace memprof } // namespace llvm #endif // LLVM_PROFILEDATA_DATAACCESSPROF_H_