Back to home page

EIC code displayed by LXR

 
 

    


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

0001 #ifndef LLVM_PROFILEDATA_MEMPROF_H_
0002 #define LLVM_PROFILEDATA_MEMPROF_H_
0003 
0004 #include "llvm/ADT/BitVector.h"
0005 #include "llvm/ADT/MapVector.h"
0006 #include "llvm/ADT/STLForwardCompat.h"
0007 #include "llvm/ADT/STLFunctionalExtras.h"
0008 #include "llvm/ADT/SmallVector.h"
0009 #include "llvm/IR/GlobalValue.h"
0010 #include "llvm/ProfileData/MemProfData.inc"
0011 #include "llvm/Support/BLAKE3.h"
0012 #include "llvm/Support/Endian.h"
0013 #include "llvm/Support/EndianStream.h"
0014 #include "llvm/Support/HashBuilder.h"
0015 #include "llvm/Support/raw_ostream.h"
0016 
0017 #include <bitset>
0018 #include <cstdint>
0019 #include <optional>
0020 
0021 namespace llvm {
0022 namespace yaml {
0023 template <typename T> struct CustomMappingTraits;
0024 } // namespace yaml
0025 
0026 namespace memprof {
0027 
0028 struct MemProfRecord;
0029 
0030 // The versions of the indexed MemProf format
0031 enum IndexedVersion : uint64_t {
0032   // Version 2: Added a call stack table.
0033   Version2 = 2,
0034   // Version 3: Added a radix tree for call stacks.  Switched to linear IDs for
0035   // frames and call stacks.
0036   Version3 = 3,
0037 };
0038 
0039 constexpr uint64_t MinimumSupportedVersion = Version2;
0040 constexpr uint64_t MaximumSupportedVersion = Version3;
0041 
0042 // Verify that the minimum and maximum satisfy the obvious constraint.
0043 static_assert(MinimumSupportedVersion <= MaximumSupportedVersion);
0044 
0045 enum class Meta : uint64_t {
0046   Start = 0,
0047 #define MIBEntryDef(NameTag, Name, Type) NameTag,
0048 #include "llvm/ProfileData/MIBEntryDef.inc"
0049 #undef MIBEntryDef
0050   Size
0051 };
0052 
0053 using MemProfSchema = llvm::SmallVector<Meta, static_cast<int>(Meta::Size)>;
0054 
0055 // Returns the full schema currently in use.
0056 MemProfSchema getFullSchema();
0057 
0058 // Returns the schema consisting of the fields used for hot cold memory hinting.
0059 MemProfSchema getHotColdSchema();
0060 
0061 // Holds the actual MemInfoBlock data with all fields. Contents may be read or
0062 // written partially by providing an appropriate schema to the serialize and
0063 // deserialize methods.
0064 struct PortableMemInfoBlock {
0065   PortableMemInfoBlock() = default;
0066   explicit PortableMemInfoBlock(const MemInfoBlock &Block,
0067                                 const MemProfSchema &IncomingSchema) {
0068     for (const Meta Id : IncomingSchema)
0069       Schema.set(llvm::to_underlying(Id));
0070 #define MIBEntryDef(NameTag, Name, Type) Name = Block.Name;
0071 #include "llvm/ProfileData/MIBEntryDef.inc"
0072 #undef MIBEntryDef
0073   }
0074 
0075   PortableMemInfoBlock(const MemProfSchema &Schema, const unsigned char *Ptr) {
0076     deserialize(Schema, Ptr);
0077   }
0078 
0079   // Read the contents of \p Ptr based on the \p Schema to populate the
0080   // MemInfoBlock member.
0081   void deserialize(const MemProfSchema &IncomingSchema,
0082                    const unsigned char *Ptr) {
0083     using namespace support;
0084 
0085     Schema.reset();
0086     for (const Meta Id : IncomingSchema) {
0087       switch (Id) {
0088 #define MIBEntryDef(NameTag, Name, Type)                                       \
0089   case Meta::Name: {                                                           \
0090     Name = endian::readNext<Type, llvm::endianness::little>(Ptr);              \
0091   } break;
0092 #include "llvm/ProfileData/MIBEntryDef.inc"
0093 #undef MIBEntryDef
0094       default:
0095         llvm_unreachable("Unknown meta type id, is the profile collected from "
0096                          "a newer version of the runtime?");
0097       }
0098 
0099       Schema.set(llvm::to_underlying(Id));
0100     }
0101   }
0102 
0103   // Write the contents of the MemInfoBlock based on the \p Schema provided to
0104   // the raw_ostream \p OS.
0105   void serialize(const MemProfSchema &Schema, raw_ostream &OS) const {
0106     using namespace support;
0107 
0108     endian::Writer LE(OS, llvm::endianness::little);
0109     for (const Meta Id : Schema) {
0110       switch (Id) {
0111 #define MIBEntryDef(NameTag, Name, Type)                                       \
0112   case Meta::Name: {                                                           \
0113     LE.write<Type>(Name);                                                      \
0114   } break;
0115 #include "llvm/ProfileData/MIBEntryDef.inc"
0116 #undef MIBEntryDef
0117       default:
0118         llvm_unreachable("Unknown meta type id, invalid input?");
0119       }
0120     }
0121   }
0122 
0123   // Print out the contents of the MemInfoBlock in YAML format.
0124   void printYAML(raw_ostream &OS) const {
0125     OS << "      MemInfoBlock:\n";
0126 #define MIBEntryDef(NameTag, Name, Type)                                       \
0127   OS << "        " << #Name << ": " << Name << "\n";
0128 #include "llvm/ProfileData/MIBEntryDef.inc"
0129 #undef MIBEntryDef
0130     if (AccessHistogramSize > 0) {
0131       OS << "        " << "AccessHistogramValues" << ":";
0132       for (uint32_t I = 0; I < AccessHistogramSize; ++I) {
0133         OS << " " << ((uint64_t *)AccessHistogram)[I];
0134       }
0135       OS << "\n";
0136     }
0137   }
0138 
0139   // Return the schema, only for unit tests.
0140   std::bitset<llvm::to_underlying(Meta::Size)> getSchema() const {
0141     return Schema;
0142   }
0143 
0144   // Define getters for each type which can be called by analyses.
0145 #define MIBEntryDef(NameTag, Name, Type)                                       \
0146   Type get##Name() const {                                                     \
0147     assert(Schema[llvm::to_underlying(Meta::Name)]);                           \
0148     return Name;                                                               \
0149   }
0150 #include "llvm/ProfileData/MIBEntryDef.inc"
0151 #undef MIBEntryDef
0152 
0153   // Define setters for each type which can be called by the writer.
0154 #define MIBEntryDef(NameTag, Name, Type)                                       \
0155   void set##Name(Type NewVal) {                                                \
0156     assert(Schema[llvm::to_underlying(Meta::Name)]);                           \
0157     Name = NewVal;                                                             \
0158   }
0159 #include "llvm/ProfileData/MIBEntryDef.inc"
0160 #undef MIBEntryDef
0161 
0162   void clear() { *this = PortableMemInfoBlock(); }
0163 
0164   bool operator==(const PortableMemInfoBlock &Other) const {
0165     if (Other.Schema != Schema)
0166       return false;
0167 
0168 #define MIBEntryDef(NameTag, Name, Type)                                       \
0169   if (Schema[llvm::to_underlying(Meta::Name)] &&                               \
0170       Other.get##Name() != get##Name())                                        \
0171     return false;
0172 #include "llvm/ProfileData/MIBEntryDef.inc"
0173 #undef MIBEntryDef
0174     return true;
0175   }
0176 
0177   bool operator!=(const PortableMemInfoBlock &Other) const {
0178     return !operator==(Other);
0179   }
0180 
0181   static size_t serializedSize(const MemProfSchema &Schema) {
0182     size_t Result = 0;
0183 
0184     for (const Meta Id : Schema) {
0185       switch (Id) {
0186 #define MIBEntryDef(NameTag, Name, Type)                                       \
0187   case Meta::Name: {                                                           \
0188     Result += sizeof(Type);                                                    \
0189   } break;
0190 #include "llvm/ProfileData/MIBEntryDef.inc"
0191 #undef MIBEntryDef
0192       default:
0193         llvm_unreachable("Unknown meta type id, invalid input?");
0194       }
0195     }
0196 
0197     return Result;
0198   }
0199 
0200   // Give YAML access to the individual MIB fields.
0201   friend struct yaml::CustomMappingTraits<memprof::PortableMemInfoBlock>;
0202 
0203 private:
0204   // The set of available fields, indexed by Meta::Name.
0205   std::bitset<llvm::to_underlying(Meta::Size)> Schema;
0206 
0207 #define MIBEntryDef(NameTag, Name, Type) Type Name = Type();
0208 #include "llvm/ProfileData/MIBEntryDef.inc"
0209 #undef MIBEntryDef
0210 };
0211 
0212 // A type representing the id generated by hashing the contents of the Frame.
0213 using FrameId = uint64_t;
0214 // A type representing the id to index into the frame array.
0215 using LinearFrameId = uint32_t;
0216 // Describes a call frame for a dynamic allocation context. The contents of
0217 // the frame are populated by symbolizing the stack depot call frame from the
0218 // compiler runtime.
0219 struct Frame {
0220   // A uuid (uint64_t) identifying the function. It is obtained by
0221   // llvm::md5(FunctionName) which returns the lower 64 bits.
0222   GlobalValue::GUID Function = 0;
0223   // The symbol name for the function. Only populated in the Frame by the reader
0224   // if requested during initialization. This field should not be serialized.
0225   std::unique_ptr<std::string> SymbolName;
0226   // The source line offset of the call from the beginning of parent function.
0227   uint32_t LineOffset = 0;
0228   // The source column number of the call to help distinguish multiple calls
0229   // on the same line.
0230   uint32_t Column = 0;
0231   // Whether the current frame is inlined.
0232   bool IsInlineFrame = false;
0233 
0234   Frame() = default;
0235   Frame(const Frame &Other) {
0236     Function = Other.Function;
0237     SymbolName = Other.SymbolName
0238                      ? std::make_unique<std::string>(*Other.SymbolName)
0239                      : nullptr;
0240     LineOffset = Other.LineOffset;
0241     Column = Other.Column;
0242     IsInlineFrame = Other.IsInlineFrame;
0243   }
0244 
0245   Frame(GlobalValue::GUID Hash, uint32_t Off, uint32_t Col, bool Inline)
0246       : Function(Hash), LineOffset(Off), Column(Col), IsInlineFrame(Inline) {}
0247 
0248   bool operator==(const Frame &Other) const {
0249     // Ignore the SymbolName field to avoid a string compare. Comparing the
0250     // function hash serves the same purpose.
0251     return Other.Function == Function && Other.LineOffset == LineOffset &&
0252            Other.Column == Column && Other.IsInlineFrame == IsInlineFrame;
0253   }
0254 
0255   Frame &operator=(const Frame &Other) {
0256     Function = Other.Function;
0257     SymbolName = Other.SymbolName
0258                      ? std::make_unique<std::string>(*Other.SymbolName)
0259                      : nullptr;
0260     LineOffset = Other.LineOffset;
0261     Column = Other.Column;
0262     IsInlineFrame = Other.IsInlineFrame;
0263     return *this;
0264   }
0265 
0266   bool operator!=(const Frame &Other) const { return !operator==(Other); }
0267 
0268   bool hasSymbolName() const { return !!SymbolName; }
0269 
0270   StringRef getSymbolName() const {
0271     assert(hasSymbolName());
0272     return *SymbolName;
0273   }
0274 
0275   std::string getSymbolNameOr(StringRef Alt) const {
0276     return std::string(hasSymbolName() ? getSymbolName() : Alt);
0277   }
0278 
0279   // Write the contents of the frame to the ostream \p OS.
0280   void serialize(raw_ostream &OS) const {
0281     using namespace support;
0282 
0283     endian::Writer LE(OS, llvm::endianness::little);
0284 
0285     // If the type of the GlobalValue::GUID changes, then we need to update
0286     // the reader and the writer.
0287     static_assert(std::is_same<GlobalValue::GUID, uint64_t>::value,
0288                   "Expect GUID to be uint64_t.");
0289     LE.write<uint64_t>(Function);
0290 
0291     LE.write<uint32_t>(LineOffset);
0292     LE.write<uint32_t>(Column);
0293     LE.write<bool>(IsInlineFrame);
0294   }
0295 
0296   // Read a frame from char data which has been serialized as little endian.
0297   static Frame deserialize(const unsigned char *Ptr) {
0298     using namespace support;
0299 
0300     const uint64_t F =
0301         endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
0302     const uint32_t L =
0303         endian::readNext<uint32_t, llvm::endianness::little>(Ptr);
0304     const uint32_t C =
0305         endian::readNext<uint32_t, llvm::endianness::little>(Ptr);
0306     const bool I = endian::readNext<bool, llvm::endianness::little>(Ptr);
0307     return Frame(/*Function=*/F, /*LineOffset=*/L, /*Column=*/C,
0308                  /*IsInlineFrame=*/I);
0309   }
0310 
0311   // Returns the size of the frame information.
0312   static constexpr size_t serializedSize() {
0313     return sizeof(Frame::Function) + sizeof(Frame::LineOffset) +
0314            sizeof(Frame::Column) + sizeof(Frame::IsInlineFrame);
0315   }
0316 
0317   // Print the frame information in YAML format.
0318   void printYAML(raw_ostream &OS) const {
0319     OS << "      -\n"
0320        << "        Function: " << Function << "\n"
0321        << "        SymbolName: " << getSymbolNameOr("<None>") << "\n"
0322        << "        LineOffset: " << LineOffset << "\n"
0323        << "        Column: " << Column << "\n"
0324        << "        Inline: " << IsInlineFrame << "\n";
0325   }
0326 };
0327 
0328 // A type representing the index into the table of call stacks.
0329 using CallStackId = uint64_t;
0330 
0331 // A type representing the index into the call stack array.
0332 using LinearCallStackId = uint32_t;
0333 
0334 // Holds allocation information in a space efficient format where frames are
0335 // represented using unique identifiers.
0336 struct IndexedAllocationInfo {
0337   // The dynamic calling context for the allocation in bottom-up (leaf-to-root)
0338   // order. Frame contents are stored out-of-line.
0339   CallStackId CSId = 0;
0340   // The statistics obtained from the runtime for the allocation.
0341   PortableMemInfoBlock Info;
0342 
0343   IndexedAllocationInfo() = default;
0344   IndexedAllocationInfo(CallStackId CSId, const MemInfoBlock &MB,
0345                         const MemProfSchema &Schema = getFullSchema())
0346       : CSId(CSId), Info(MB, Schema) {}
0347   IndexedAllocationInfo(CallStackId CSId, const PortableMemInfoBlock &MB)
0348       : CSId(CSId), Info(MB) {}
0349 
0350   // Returns the size in bytes when this allocation info struct is serialized.
0351   size_t serializedSize(const MemProfSchema &Schema,
0352                         IndexedVersion Version) const;
0353 
0354   bool operator==(const IndexedAllocationInfo &Other) const {
0355     if (Other.Info != Info)
0356       return false;
0357 
0358     if (Other.CSId != CSId)
0359       return false;
0360     return true;
0361   }
0362 
0363   bool operator!=(const IndexedAllocationInfo &Other) const {
0364     return !operator==(Other);
0365   }
0366 };
0367 
0368 // Holds allocation information with frame contents inline. The type should
0369 // be used for temporary in-memory instances.
0370 struct AllocationInfo {
0371   // Same as IndexedAllocationInfo::CallStack with the frame contents inline.
0372   std::vector<Frame> CallStack;
0373   // Same as IndexedAllocationInfo::Info;
0374   PortableMemInfoBlock Info;
0375 
0376   AllocationInfo() = default;
0377 
0378   void printYAML(raw_ostream &OS) const {
0379     OS << "    -\n";
0380     OS << "      Callstack:\n";
0381     // TODO: Print out the frame on one line with to make it easier for deep
0382     // callstacks once we have a test to check valid YAML is generated.
0383     for (const Frame &F : CallStack) {
0384       F.printYAML(OS);
0385     }
0386     Info.printYAML(OS);
0387   }
0388 };
0389 
0390 // Holds the memprof profile information for a function. The internal
0391 // representation stores frame ids for efficiency. This representation should
0392 // be used in the profile conversion and manipulation tools.
0393 struct IndexedMemProfRecord {
0394   // Memory allocation sites in this function for which we have memory
0395   // profiling data.
0396   llvm::SmallVector<IndexedAllocationInfo> AllocSites;
0397   // Holds call sites in this function which are part of some memory
0398   // allocation context. We store this as a list of locations, each with its
0399   // list of inline locations in bottom-up order i.e. from leaf to root. The
0400   // inline location list may include additional entries, users should pick
0401   // the last entry in the list with the same function GUID.
0402   llvm::SmallVector<CallStackId> CallSiteIds;
0403 
0404   void clear() { *this = IndexedMemProfRecord(); }
0405 
0406   void merge(const IndexedMemProfRecord &Other) {
0407     // TODO: Filter out duplicates which may occur if multiple memprof
0408     // profiles are merged together using llvm-profdata.
0409     AllocSites.append(Other.AllocSites);
0410   }
0411 
0412   size_t serializedSize(const MemProfSchema &Schema,
0413                         IndexedVersion Version) const;
0414 
0415   bool operator==(const IndexedMemProfRecord &Other) const {
0416     if (Other.AllocSites != AllocSites)
0417       return false;
0418 
0419     if (Other.CallSiteIds != CallSiteIds)
0420       return false;
0421     return true;
0422   }
0423 
0424   // Serializes the memprof records in \p Records to the ostream \p OS based
0425   // on the schema provided in \p Schema.
0426   void serialize(const MemProfSchema &Schema, raw_ostream &OS,
0427                  IndexedVersion Version,
0428                  llvm::DenseMap<CallStackId, LinearCallStackId>
0429                      *MemProfCallStackIndexes = nullptr) const;
0430 
0431   // Deserializes memprof records from the Buffer.
0432   static IndexedMemProfRecord deserialize(const MemProfSchema &Schema,
0433                                           const unsigned char *Buffer,
0434                                           IndexedVersion Version);
0435 
0436   // Convert IndexedMemProfRecord to MemProfRecord.  Callback is used to
0437   // translate CallStackId to call stacks with frames inline.
0438   MemProfRecord toMemProfRecord(
0439       llvm::function_ref<std::vector<Frame>(const CallStackId)> Callback) const;
0440 
0441   // Returns the GUID for the function name after canonicalization. For
0442   // memprof, we remove any .llvm suffix added by LTO. MemProfRecords are
0443   // mapped to functions using this GUID.
0444   static GlobalValue::GUID getGUID(const StringRef FunctionName);
0445 };
0446 
0447 // Holds the memprof profile information for a function. The internal
0448 // representation stores frame contents inline. This representation should
0449 // be used for small amount of temporary, in memory instances.
0450 struct MemProfRecord {
0451   // Same as IndexedMemProfRecord::AllocSites with frame contents inline.
0452   llvm::SmallVector<AllocationInfo> AllocSites;
0453   // Same as IndexedMemProfRecord::CallSites with frame contents inline.
0454   llvm::SmallVector<std::vector<Frame>> CallSites;
0455 
0456   MemProfRecord() = default;
0457 
0458   // Prints out the contents of the memprof record in YAML.
0459   void print(llvm::raw_ostream &OS) const {
0460     if (!AllocSites.empty()) {
0461       OS << "    AllocSites:\n";
0462       for (const AllocationInfo &N : AllocSites)
0463         N.printYAML(OS);
0464     }
0465 
0466     if (!CallSites.empty()) {
0467       OS << "    CallSites:\n";
0468       for (const std::vector<Frame> &Frames : CallSites) {
0469         for (const Frame &F : Frames) {
0470           OS << "    -\n";
0471           F.printYAML(OS);
0472         }
0473       }
0474     }
0475   }
0476 };
0477 
0478 // Reads a memprof schema from a buffer. All entries in the buffer are
0479 // interpreted as uint64_t. The first entry in the buffer denotes the number of
0480 // ids in the schema. Subsequent entries are integers which map to memprof::Meta
0481 // enum class entries. After successfully reading the schema, the pointer is one
0482 // byte past the schema contents.
0483 Expected<MemProfSchema> readMemProfSchema(const unsigned char *&Buffer);
0484 
0485 // Trait for reading IndexedMemProfRecord data from the on-disk hash table.
0486 class RecordLookupTrait {
0487 public:
0488   using data_type = const IndexedMemProfRecord &;
0489   using internal_key_type = uint64_t;
0490   using external_key_type = uint64_t;
0491   using hash_value_type = uint64_t;
0492   using offset_type = uint64_t;
0493 
0494   RecordLookupTrait() = delete;
0495   RecordLookupTrait(IndexedVersion V, const MemProfSchema &S)
0496       : Version(V), Schema(S) {}
0497 
0498   static bool EqualKey(uint64_t A, uint64_t B) { return A == B; }
0499   static uint64_t GetInternalKey(uint64_t K) { return K; }
0500   static uint64_t GetExternalKey(uint64_t K) { return K; }
0501 
0502   hash_value_type ComputeHash(uint64_t K) { return K; }
0503 
0504   static std::pair<offset_type, offset_type>
0505   ReadKeyDataLength(const unsigned char *&D) {
0506     using namespace support;
0507 
0508     offset_type KeyLen =
0509         endian::readNext<offset_type, llvm::endianness::little>(D);
0510     offset_type DataLen =
0511         endian::readNext<offset_type, llvm::endianness::little>(D);
0512     return std::make_pair(KeyLen, DataLen);
0513   }
0514 
0515   uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) {
0516     using namespace support;
0517     return endian::readNext<external_key_type, llvm::endianness::little>(D);
0518   }
0519 
0520   data_type ReadData(uint64_t K, const unsigned char *D,
0521                      offset_type /*Unused*/) {
0522     Record = IndexedMemProfRecord::deserialize(Schema, D, Version);
0523     return Record;
0524   }
0525 
0526 private:
0527   // Holds the MemProf version.
0528   IndexedVersion Version;
0529   // Holds the memprof schema used to deserialize records.
0530   MemProfSchema Schema;
0531   // Holds the records from one function deserialized from the indexed format.
0532   IndexedMemProfRecord Record;
0533 };
0534 
0535 // Trait for writing IndexedMemProfRecord data to the on-disk hash table.
0536 class RecordWriterTrait {
0537 public:
0538   using key_type = uint64_t;
0539   using key_type_ref = uint64_t;
0540 
0541   using data_type = IndexedMemProfRecord;
0542   using data_type_ref = IndexedMemProfRecord &;
0543 
0544   using hash_value_type = uint64_t;
0545   using offset_type = uint64_t;
0546 
0547 private:
0548   // Pointer to the memprof schema to use for the generator.
0549   const MemProfSchema *Schema;
0550   // The MemProf version to use for the serialization.
0551   IndexedVersion Version;
0552 
0553   // Mappings from CallStackId to the indexes into the call stack array.
0554   llvm::DenseMap<CallStackId, LinearCallStackId> *MemProfCallStackIndexes;
0555 
0556 public:
0557   // We do not support the default constructor, which does not set Version.
0558   RecordWriterTrait() = delete;
0559   RecordWriterTrait(
0560       const MemProfSchema *Schema, IndexedVersion V,
0561       llvm::DenseMap<CallStackId, LinearCallStackId> *MemProfCallStackIndexes)
0562       : Schema(Schema), Version(V),
0563         MemProfCallStackIndexes(MemProfCallStackIndexes) {}
0564 
0565   static hash_value_type ComputeHash(key_type_ref K) { return K; }
0566 
0567   std::pair<offset_type, offset_type>
0568   EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
0569     using namespace support;
0570 
0571     endian::Writer LE(Out, llvm::endianness::little);
0572     offset_type N = sizeof(K);
0573     LE.write<offset_type>(N);
0574     offset_type M = V.serializedSize(*Schema, Version);
0575     LE.write<offset_type>(M);
0576     return std::make_pair(N, M);
0577   }
0578 
0579   void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) {
0580     using namespace support;
0581     endian::Writer LE(Out, llvm::endianness::little);
0582     LE.write<uint64_t>(K);
0583   }
0584 
0585   void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
0586                 offset_type /*Unused*/) {
0587     assert(Schema != nullptr && "MemProf schema is not initialized!");
0588     V.serialize(*Schema, Out, Version, MemProfCallStackIndexes);
0589     // Clear the IndexedMemProfRecord which results in clearing/freeing its
0590     // vectors of allocs and callsites. This is owned by the associated on-disk
0591     // hash table, but unused after this point. See also the comment added to
0592     // the client which constructs the on-disk hash table for this trait.
0593     V.clear();
0594   }
0595 };
0596 
0597 // Trait for writing frame mappings to the on-disk hash table.
0598 class FrameWriterTrait {
0599 public:
0600   using key_type = FrameId;
0601   using key_type_ref = FrameId;
0602 
0603   using data_type = Frame;
0604   using data_type_ref = Frame &;
0605 
0606   using hash_value_type = FrameId;
0607   using offset_type = uint64_t;
0608 
0609   static hash_value_type ComputeHash(key_type_ref K) { return K; }
0610 
0611   static std::pair<offset_type, offset_type>
0612   EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
0613     using namespace support;
0614     endian::Writer LE(Out, llvm::endianness::little);
0615     offset_type N = sizeof(K);
0616     LE.write<offset_type>(N);
0617     offset_type M = V.serializedSize();
0618     LE.write<offset_type>(M);
0619     return std::make_pair(N, M);
0620   }
0621 
0622   void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) {
0623     using namespace support;
0624     endian::Writer LE(Out, llvm::endianness::little);
0625     LE.write<key_type>(K);
0626   }
0627 
0628   void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
0629                 offset_type /*Unused*/) {
0630     V.serialize(Out);
0631   }
0632 };
0633 
0634 // Trait for reading frame mappings from the on-disk hash table.
0635 class FrameLookupTrait {
0636 public:
0637   using data_type = const Frame;
0638   using internal_key_type = FrameId;
0639   using external_key_type = FrameId;
0640   using hash_value_type = FrameId;
0641   using offset_type = uint64_t;
0642 
0643   static bool EqualKey(internal_key_type A, internal_key_type B) {
0644     return A == B;
0645   }
0646   static uint64_t GetInternalKey(internal_key_type K) { return K; }
0647   static uint64_t GetExternalKey(external_key_type K) { return K; }
0648 
0649   hash_value_type ComputeHash(internal_key_type K) { return K; }
0650 
0651   static std::pair<offset_type, offset_type>
0652   ReadKeyDataLength(const unsigned char *&D) {
0653     using namespace support;
0654 
0655     offset_type KeyLen =
0656         endian::readNext<offset_type, llvm::endianness::little>(D);
0657     offset_type DataLen =
0658         endian::readNext<offset_type, llvm::endianness::little>(D);
0659     return std::make_pair(KeyLen, DataLen);
0660   }
0661 
0662   uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) {
0663     using namespace support;
0664     return endian::readNext<external_key_type, llvm::endianness::little>(D);
0665   }
0666 
0667   data_type ReadData(uint64_t K, const unsigned char *D,
0668                      offset_type /*Unused*/) {
0669     return Frame::deserialize(D);
0670   }
0671 };
0672 
0673 // Trait for writing call stacks to the on-disk hash table.
0674 class CallStackWriterTrait {
0675 public:
0676   using key_type = CallStackId;
0677   using key_type_ref = CallStackId;
0678 
0679   using data_type = llvm::SmallVector<FrameId>;
0680   using data_type_ref = llvm::SmallVector<FrameId> &;
0681 
0682   using hash_value_type = CallStackId;
0683   using offset_type = uint64_t;
0684 
0685   static hash_value_type ComputeHash(key_type_ref K) { return K; }
0686 
0687   static std::pair<offset_type, offset_type>
0688   EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
0689     using namespace support;
0690     endian::Writer LE(Out, llvm::endianness::little);
0691     // We do not explicitly emit the key length because it is a constant.
0692     offset_type N = sizeof(K);
0693     offset_type M = sizeof(FrameId) * V.size();
0694     LE.write<offset_type>(M);
0695     return std::make_pair(N, M);
0696   }
0697 
0698   void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) {
0699     using namespace support;
0700     endian::Writer LE(Out, llvm::endianness::little);
0701     LE.write<key_type>(K);
0702   }
0703 
0704   void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
0705                 offset_type /*Unused*/) {
0706     using namespace support;
0707     endian::Writer LE(Out, llvm::endianness::little);
0708     // Emit the frames.  We do not explicitly emit the length of the vector
0709     // because it can be inferred from the data length.
0710     for (FrameId F : V)
0711       LE.write<FrameId>(F);
0712   }
0713 };
0714 
0715 // Trait for reading call stack mappings from the on-disk hash table.
0716 class CallStackLookupTrait {
0717 public:
0718   using data_type = const llvm::SmallVector<FrameId>;
0719   using internal_key_type = CallStackId;
0720   using external_key_type = CallStackId;
0721   using hash_value_type = CallStackId;
0722   using offset_type = uint64_t;
0723 
0724   static bool EqualKey(internal_key_type A, internal_key_type B) {
0725     return A == B;
0726   }
0727   static uint64_t GetInternalKey(internal_key_type K) { return K; }
0728   static uint64_t GetExternalKey(external_key_type K) { return K; }
0729 
0730   hash_value_type ComputeHash(internal_key_type K) { return K; }
0731 
0732   static std::pair<offset_type, offset_type>
0733   ReadKeyDataLength(const unsigned char *&D) {
0734     using namespace support;
0735 
0736     // We do not explicitly read the key length because it is a constant.
0737     offset_type KeyLen = sizeof(external_key_type);
0738     offset_type DataLen =
0739         endian::readNext<offset_type, llvm::endianness::little>(D);
0740     return std::make_pair(KeyLen, DataLen);
0741   }
0742 
0743   uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) {
0744     using namespace support;
0745     return endian::readNext<external_key_type, llvm::endianness::little>(D);
0746   }
0747 
0748   data_type ReadData(uint64_t K, const unsigned char *D, offset_type Length) {
0749     using namespace support;
0750     llvm::SmallVector<FrameId> CS;
0751     // Derive the number of frames from the data length.
0752     uint64_t NumFrames = Length / sizeof(FrameId);
0753     assert(Length % sizeof(FrameId) == 0);
0754     CS.reserve(NumFrames);
0755     for (size_t I = 0; I != NumFrames; ++I) {
0756       FrameId F = endian::readNext<FrameId, llvm::endianness::little>(D);
0757       CS.push_back(F);
0758     }
0759     return CS;
0760   }
0761 };
0762 
0763 namespace detail {
0764 // "Dereference" the iterator from DenseMap or OnDiskChainedHashTable.  We have
0765 // to do so in one of two different ways depending on the type of the hash
0766 // table.
0767 template <typename value_type, typename IterTy>
0768 value_type DerefIterator(IterTy Iter) {
0769   using deref_type = llvm::remove_cvref_t<decltype(*Iter)>;
0770   if constexpr (std::is_same_v<deref_type, value_type>)
0771     return *Iter;
0772   else
0773     return Iter->second;
0774 }
0775 } // namespace detail
0776 
0777 // A function object that returns a frame for a given FrameId.
0778 template <typename MapTy> struct FrameIdConverter {
0779   std::optional<FrameId> LastUnmappedId;
0780   MapTy &Map;
0781 
0782   FrameIdConverter() = delete;
0783   FrameIdConverter(MapTy &Map) : Map(Map) {}
0784 
0785   // Delete the copy constructor and copy assignment operator to avoid a
0786   // situation where a copy of FrameIdConverter gets an error in LastUnmappedId
0787   // while the original instance doesn't.
0788   FrameIdConverter(const FrameIdConverter &) = delete;
0789   FrameIdConverter &operator=(const FrameIdConverter &) = delete;
0790 
0791   Frame operator()(FrameId Id) {
0792     auto Iter = Map.find(Id);
0793     if (Iter == Map.end()) {
0794       LastUnmappedId = Id;
0795       return Frame();
0796     }
0797     return detail::DerefIterator<Frame>(Iter);
0798   }
0799 };
0800 
0801 // A function object that returns a call stack for a given CallStackId.
0802 template <typename MapTy> struct CallStackIdConverter {
0803   std::optional<CallStackId> LastUnmappedId;
0804   MapTy &Map;
0805   llvm::function_ref<Frame(FrameId)> FrameIdToFrame;
0806 
0807   CallStackIdConverter() = delete;
0808   CallStackIdConverter(MapTy &Map,
0809                        llvm::function_ref<Frame(FrameId)> FrameIdToFrame)
0810       : Map(Map), FrameIdToFrame(FrameIdToFrame) {}
0811 
0812   // Delete the copy constructor and copy assignment operator to avoid a
0813   // situation where a copy of CallStackIdConverter gets an error in
0814   // LastUnmappedId while the original instance doesn't.
0815   CallStackIdConverter(const CallStackIdConverter &) = delete;
0816   CallStackIdConverter &operator=(const CallStackIdConverter &) = delete;
0817 
0818   std::vector<Frame> operator()(CallStackId CSId) {
0819     std::vector<Frame> Frames;
0820     auto CSIter = Map.find(CSId);
0821     if (CSIter == Map.end()) {
0822       LastUnmappedId = CSId;
0823     } else {
0824       llvm::SmallVector<FrameId> CS =
0825           detail::DerefIterator<llvm::SmallVector<FrameId>>(CSIter);
0826       Frames.reserve(CS.size());
0827       for (FrameId Id : CS)
0828         Frames.push_back(FrameIdToFrame(Id));
0829     }
0830     return Frames;
0831   }
0832 };
0833 
0834 // A function object that returns a Frame stored at a given index into the Frame
0835 // array in the profile.
0836 struct LinearFrameIdConverter {
0837   const unsigned char *FrameBase;
0838 
0839   LinearFrameIdConverter() = delete;
0840   LinearFrameIdConverter(const unsigned char *FrameBase)
0841       : FrameBase(FrameBase) {}
0842 
0843   Frame operator()(LinearFrameId LinearId) {
0844     uint64_t Offset = static_cast<uint64_t>(LinearId) * Frame::serializedSize();
0845     return Frame::deserialize(FrameBase + Offset);
0846   }
0847 };
0848 
0849 // A function object that returns a call stack stored at a given index into the
0850 // call stack array in the profile.
0851 struct LinearCallStackIdConverter {
0852   const unsigned char *CallStackBase;
0853   llvm::function_ref<Frame(LinearFrameId)> FrameIdToFrame;
0854 
0855   LinearCallStackIdConverter() = delete;
0856   LinearCallStackIdConverter(
0857       const unsigned char *CallStackBase,
0858       llvm::function_ref<Frame(LinearFrameId)> FrameIdToFrame)
0859       : CallStackBase(CallStackBase), FrameIdToFrame(FrameIdToFrame) {}
0860 
0861   std::vector<Frame> operator()(LinearCallStackId LinearCSId) {
0862     std::vector<Frame> Frames;
0863 
0864     const unsigned char *Ptr =
0865         CallStackBase +
0866         static_cast<uint64_t>(LinearCSId) * sizeof(LinearFrameId);
0867     uint32_t NumFrames =
0868         support::endian::readNext<uint32_t, llvm::endianness::little>(Ptr);
0869     Frames.reserve(NumFrames);
0870     for (; NumFrames; --NumFrames) {
0871       LinearFrameId Elem =
0872           support::endian::read<LinearFrameId, llvm::endianness::little>(Ptr);
0873       // Follow a pointer to the parent, if any.  See comments below on
0874       // CallStackRadixTreeBuilder for the description of the radix tree format.
0875       if (static_cast<std::make_signed_t<LinearFrameId>>(Elem) < 0) {
0876         Ptr += (-Elem) * sizeof(LinearFrameId);
0877         Elem =
0878             support::endian::read<LinearFrameId, llvm::endianness::little>(Ptr);
0879       }
0880       // We shouldn't encounter another pointer.
0881       assert(static_cast<std::make_signed_t<LinearFrameId>>(Elem) >= 0);
0882       Frames.push_back(FrameIdToFrame(Elem));
0883       Ptr += sizeof(LinearFrameId);
0884     }
0885 
0886     return Frames;
0887   }
0888 };
0889 
0890 struct LineLocation {
0891   LineLocation(uint32_t L, uint32_t D) : LineOffset(L), Column(D) {}
0892 
0893   bool operator<(const LineLocation &O) const {
0894     return LineOffset < O.LineOffset ||
0895            (LineOffset == O.LineOffset && Column < O.Column);
0896   }
0897 
0898   bool operator==(const LineLocation &O) const {
0899     return LineOffset == O.LineOffset && Column == O.Column;
0900   }
0901 
0902   bool operator!=(const LineLocation &O) const {
0903     return LineOffset != O.LineOffset || Column != O.Column;
0904   }
0905 
0906   uint64_t getHashCode() const { return ((uint64_t)Column << 32) | LineOffset; }
0907 
0908   uint32_t LineOffset;
0909   uint32_t Column;
0910 };
0911 
0912 // A pair of a call site location and its corresponding callee GUID.
0913 using CallEdgeTy = std::pair<LineLocation, uint64_t>;
0914 
0915 // Used to extract caller-callee pairs from the call stack array.  The leaf
0916 // frame is assumed to call a heap allocation function with GUID 0.  The
0917 // resulting pairs are accumulated in CallerCalleePairs.  Users can take it
0918 // with:
0919 //
0920 //   auto Pairs = std::move(Extractor.CallerCalleePairs);
0921 struct CallerCalleePairExtractor {
0922   // The base address of the radix tree array.
0923   const unsigned char *CallStackBase;
0924   // A functor to convert a linear FrameId to a Frame.
0925   llvm::function_ref<Frame(LinearFrameId)> FrameIdToFrame;
0926   // A map from caller GUIDs to lists of call sites in respective callers.
0927   DenseMap<uint64_t, SmallVector<CallEdgeTy, 0>> CallerCalleePairs;
0928 
0929   // The set of linear call stack IDs that we've visited.
0930   BitVector Visited;
0931 
0932   CallerCalleePairExtractor() = delete;
0933   CallerCalleePairExtractor(
0934       const unsigned char *CallStackBase,
0935       llvm::function_ref<Frame(LinearFrameId)> FrameIdToFrame,
0936       unsigned RadixTreeSize)
0937       : CallStackBase(CallStackBase), FrameIdToFrame(FrameIdToFrame),
0938         Visited(RadixTreeSize) {}
0939 
0940   void operator()(LinearCallStackId LinearCSId) {
0941     const unsigned char *Ptr =
0942         CallStackBase +
0943         static_cast<uint64_t>(LinearCSId) * sizeof(LinearFrameId);
0944     uint32_t NumFrames =
0945         support::endian::readNext<uint32_t, llvm::endianness::little>(Ptr);
0946     // The leaf frame calls a function with GUID 0.
0947     uint64_t CalleeGUID = 0;
0948     for (; NumFrames; --NumFrames) {
0949       LinearFrameId Elem =
0950           support::endian::read<LinearFrameId, llvm::endianness::little>(Ptr);
0951       // Follow a pointer to the parent, if any.  See comments below on
0952       // CallStackRadixTreeBuilder for the description of the radix tree format.
0953       if (static_cast<std::make_signed_t<LinearFrameId>>(Elem) < 0) {
0954         Ptr += (-Elem) * sizeof(LinearFrameId);
0955         Elem =
0956             support::endian::read<LinearFrameId, llvm::endianness::little>(Ptr);
0957       }
0958       // We shouldn't encounter another pointer.
0959       assert(static_cast<std::make_signed_t<LinearFrameId>>(Elem) >= 0);
0960 
0961       // Add a new caller-callee pair.
0962       Frame F = FrameIdToFrame(Elem);
0963       uint64_t CallerGUID = F.Function;
0964       LineLocation Loc(F.LineOffset, F.Column);
0965       CallerCalleePairs[CallerGUID].emplace_back(Loc, CalleeGUID);
0966 
0967       // Keep track of the indices we've visited.  If we've already visited the
0968       // current one, terminate the traversal.  We will not discover any new
0969       // caller-callee pair by continuing the traversal.
0970       unsigned Offset =
0971           std::distance(CallStackBase, Ptr) / sizeof(LinearFrameId);
0972       if (Visited.test(Offset))
0973         break;
0974       Visited.set(Offset);
0975 
0976       Ptr += sizeof(LinearFrameId);
0977       CalleeGUID = CallerGUID;
0978     }
0979   }
0980 };
0981 
0982 struct IndexedMemProfData {
0983   // A map to hold memprof data per function. The lower 64 bits obtained from
0984   // the md5 hash of the function name is used to index into the map.
0985   llvm::MapVector<GlobalValue::GUID, IndexedMemProfRecord> Records;
0986 
0987   // A map to hold frame id to frame mappings. The mappings are used to
0988   // convert IndexedMemProfRecord to MemProfRecords with frame information
0989   // inline.
0990   llvm::MapVector<FrameId, Frame> Frames;
0991 
0992   // A map to hold call stack id to call stacks.
0993   llvm::MapVector<CallStackId, llvm::SmallVector<FrameId>> CallStacks;
0994 
0995   FrameId addFrame(const Frame &F) {
0996     const FrameId Id = hashFrame(F);
0997     Frames.try_emplace(Id, F);
0998     return Id;
0999   }
1000 
1001   CallStackId addCallStack(ArrayRef<FrameId> CS) {
1002     CallStackId CSId = hashCallStack(CS);
1003     CallStacks.try_emplace(CSId, CS);
1004     return CSId;
1005   }
1006 
1007   CallStackId addCallStack(SmallVector<FrameId> &&CS) {
1008     CallStackId CSId = hashCallStack(CS);
1009     CallStacks.try_emplace(CSId, std::move(CS));
1010     return CSId;
1011   }
1012 
1013 private:
1014   // Return a hash value based on the contents of the frame. Here we use a
1015   // cryptographic hash function to minimize the chance of hash collisions.  We
1016   // do persist FrameIds as part of memprof formats up to Version 2, inclusive.
1017   // However, the deserializer never calls this function; it uses FrameIds
1018   // merely as keys to look up Frames proper.
1019   FrameId hashFrame(const Frame &F) const {
1020     llvm::HashBuilder<llvm::TruncatedBLAKE3<8>, llvm::endianness::little>
1021         HashBuilder;
1022     HashBuilder.add(F.Function, F.LineOffset, F.Column, F.IsInlineFrame);
1023     llvm::BLAKE3Result<8> Hash = HashBuilder.final();
1024     FrameId Id;
1025     std::memcpy(&Id, Hash.data(), sizeof(Hash));
1026     return Id;
1027   }
1028 
1029   // Compute a CallStackId for a given call stack.
1030   CallStackId hashCallStack(ArrayRef<FrameId> CS) const;
1031 };
1032 
1033 // A convenience wrapper around FrameIdConverter and CallStackIdConverter for
1034 // tests.
1035 struct IndexedCallstackIdConveter {
1036   IndexedCallstackIdConveter() = delete;
1037   IndexedCallstackIdConveter(IndexedMemProfData &MemProfData)
1038       : FrameIdConv(MemProfData.Frames),
1039         CSIdConv(MemProfData.CallStacks, FrameIdConv) {}
1040 
1041   // Delete the copy constructor and copy assignment operator to avoid a
1042   // situation where a copy of IndexedCallStackIdConverter gets an error in
1043   // LastUnmappedId while the original instance doesn't.
1044   IndexedCallstackIdConveter(const IndexedCallstackIdConveter &) = delete;
1045   IndexedCallstackIdConveter &
1046   operator=(const IndexedCallstackIdConveter &) = delete;
1047 
1048   std::vector<Frame> operator()(CallStackId CSId) { return CSIdConv(CSId); }
1049 
1050   FrameIdConverter<decltype(IndexedMemProfData::Frames)> FrameIdConv;
1051   CallStackIdConverter<decltype(IndexedMemProfData::CallStacks)> CSIdConv;
1052 };
1053 
1054 struct FrameStat {
1055   // The number of occurrences of a given FrameId.
1056   uint64_t Count = 0;
1057   // The sum of indexes where a given FrameId shows up.
1058   uint64_t PositionSum = 0;
1059 };
1060 
1061 // Compute a histogram of Frames in call stacks.
1062 template <typename FrameIdTy>
1063 llvm::DenseMap<FrameIdTy, FrameStat>
1064 computeFrameHistogram(llvm::MapVector<CallStackId, llvm::SmallVector<FrameIdTy>>
1065                           &MemProfCallStackData);
1066 
1067 // Construct a radix tree of call stacks.
1068 //
1069 // A set of call stacks might look like:
1070 //
1071 // CallStackId 1:  f1 -> f2 -> f3
1072 // CallStackId 2:  f1 -> f2 -> f4 -> f5
1073 // CallStackId 3:  f1 -> f2 -> f4 -> f6
1074 // CallStackId 4:  f7 -> f8 -> f9
1075 //
1076 // where each fn refers to a stack frame.
1077 //
1078 // Since we expect a lot of common prefixes, we can compress the call stacks
1079 // into a radix tree like:
1080 //
1081 // CallStackId 1:  f1 -> f2 -> f3
1082 //                       |
1083 // CallStackId 2:        +---> f4 -> f5
1084 //                             |
1085 // CallStackId 3:              +---> f6
1086 //
1087 // CallStackId 4:  f7 -> f8 -> f9
1088 //
1089 // Now, we are interested in retrieving call stacks for a given CallStackId, so
1090 // we just need a pointer from a given call stack to its parent.  For example,
1091 // CallStackId 2 would point to CallStackId 1 as a parent.
1092 //
1093 // We serialize the radix tree above into a single array along with the length
1094 // of each call stack and pointers to the parent call stacks.
1095 //
1096 // Index:              0  1  2  3  4  5  6  7  8  9 10 11 12 13 14
1097 // Array:             L3 f9 f8 f7 L4 f6 J3 L4 f5 f4 J3 L3 f3 f2 f1
1098 //                     ^           ^        ^           ^
1099 //                     |           |        |           |
1100 // CallStackId 4:  0 --+           |        |           |
1101 // CallStackId 3:  4 --------------+        |           |
1102 // CallStackId 2:  7 -----------------------+           |
1103 // CallStackId 1: 11 -----------------------------------+
1104 //
1105 // - LN indicates the length of a call stack, encoded as ordinary integer N.
1106 //
1107 // - JN indicates a pointer to the parent, encoded as -N.
1108 //
1109 // The radix tree allows us to reconstruct call stacks in the leaf-to-root
1110 // order as we scan the array from left ro right while following pointers to
1111 // parents along the way.
1112 //
1113 // For example, if we are decoding CallStackId 2, we start a forward traversal
1114 // at Index 7, noting the call stack length of 4 and obtaining f5 and f4.  When
1115 // we see J3 at Index 10, we resume a forward traversal at Index 13 = 10 + 3,
1116 // picking up f2 and f1.  We are done after collecting 4 frames as indicated at
1117 // the beginning of the traversal.
1118 //
1119 // On-disk IndexedMemProfRecord will refer to call stacks by their indexes into
1120 // the radix tree array, so we do not explicitly encode mappings like:
1121 // "CallStackId 1 -> 11".
1122 template <typename FrameIdTy> class CallStackRadixTreeBuilder {
1123   // The radix tree array.
1124   std::vector<LinearFrameId> RadixArray;
1125 
1126   // Mapping from CallStackIds to indexes into RadixArray.
1127   llvm::DenseMap<CallStackId, LinearCallStackId> CallStackPos;
1128 
1129   // In build, we partition a given call stack into two parts -- the prefix
1130   // that's common with the previously encoded call stack and the frames beyond
1131   // the common prefix -- the unique portion.  Then we want to find out where
1132   // the common prefix is stored in RadixArray so that we can link the unique
1133   // portion to the common prefix.  Indexes, declared below, helps with our
1134   // needs.  Intuitively, Indexes tells us where each of the previously encoded
1135   // call stack is stored in RadixArray.  More formally, Indexes satisfies:
1136   //
1137   //   RadixArray[Indexes[I]] == Prev[I]
1138   //
1139   // for every I, where Prev is the the call stack in the root-to-leaf order
1140   // previously encoded by build.  (Note that Prev, as passed to
1141   // encodeCallStack, is in the leaf-to-root order.)
1142   //
1143   // For example, if the call stack being encoded shares 5 frames at the root of
1144   // the call stack with the previously encoded call stack,
1145   // RadixArray[Indexes[0]] is the root frame of the common prefix.
1146   // RadixArray[Indexes[5 - 1]] is the last frame of the common prefix.
1147   std::vector<LinearCallStackId> Indexes;
1148 
1149   using CSIdPair = std::pair<CallStackId, llvm::SmallVector<FrameIdTy>>;
1150 
1151   // Encode a call stack into RadixArray.  Return the starting index within
1152   // RadixArray.
1153   LinearCallStackId encodeCallStack(
1154       const llvm::SmallVector<FrameIdTy> *CallStack,
1155       const llvm::SmallVector<FrameIdTy> *Prev,
1156       const llvm::DenseMap<FrameIdTy, LinearFrameId> *MemProfFrameIndexes);
1157 
1158 public:
1159   CallStackRadixTreeBuilder() = default;
1160 
1161   // Build a radix tree array.
1162   void
1163   build(llvm::MapVector<CallStackId, llvm::SmallVector<FrameIdTy>>
1164             &&MemProfCallStackData,
1165         const llvm::DenseMap<FrameIdTy, LinearFrameId> *MemProfFrameIndexes,
1166         llvm::DenseMap<FrameIdTy, FrameStat> &FrameHistogram);
1167 
1168   ArrayRef<LinearFrameId> getRadixArray() const { return RadixArray; }
1169 
1170   llvm::DenseMap<CallStackId, LinearCallStackId> takeCallStackPos() {
1171     return std::move(CallStackPos);
1172   }
1173 };
1174 } // namespace memprof
1175 } // namespace llvm
1176 
1177 #endif // LLVM_PROFILEDATA_MEMPROF_H_