| //===- 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 <cstdint> | |
| #include <optional> | |
| #include <variant> | |
| 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<SourceLocationRef, 0> 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<StringRef, uint64_t>; | |
| // The senamtic is the same as `SymbolHandleRef` above. The strings are owned. | |
| using SymbolHandle = std::variant<std::string, uint64_t>; | |
| /// The data access profiles for a symbol. | |
| struct DataAccessProfRecord { | |
| public: | |
| DataAccessProfRecord(SymbolHandleRef SymHandleRef, uint64_t AccessCount, | |
| ArrayRef<internal::SourceLocationRef> LocRefs) | |
| : AccessCount(AccessCount) { | |
| if (std::holds_alternative<StringRef>(SymHandleRef)) { | |
| SymHandle = std::get<StringRef>(SymHandleRef).str(); | |
| } else | |
| SymHandle = std::get<uint64_t>(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<SourceLocation> 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<StringRef, uint64_t>; | |
| 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<DataAccessProfRecord> | |
| 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<SourceLocation> 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<StringToIndexMap::value_type> getStrToIndexMapRef() const { | |
| return StrToIndexMap.getArrayRef(); | |
| } | |
| ArrayRef< | |
| MapVector<SymbolHandleRef, internal::DataAccessProfRecordRef>::value_type> | |
| getRecords() const { | |
| return Records.getArrayRef(); | |
| } | |
| ArrayRef<StringRef> getKnownColdSymbols() const { | |
| return KnownColdSymbols.getArrayRef(); | |
| } | |
| ArrayRef<uint64_t> 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<SymbolHandleRef, internal::DataAccessProfRecordRef> Records; | |
| StringToIndexMap StrToIndexMap; | |
| llvm::SetVector<uint64_t> KnownColdHashes; | |
| llvm::SetVector<StringRef> KnownColdSymbols; | |
| }; | |
| } // namespace memprof | |
| } // namespace llvm | |
| #endif // LLVM_PROFILEDATA_DATAACCESSPROF_H_ |