Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14 pmbaty 1
#ifndef LLVM_PROFILEDATA_MEMPROF_H_
2
#define LLVM_PROFILEDATA_MEMPROF_H_
3
 
4
#include "llvm/ADT/DenseMap.h"
5
#include "llvm/ADT/STLFunctionalExtras.h"
6
#include "llvm/ADT/SmallVector.h"
7
#include "llvm/IR/GlobalValue.h"
8
#include "llvm/ProfileData/MemProfData.inc"
9
#include "llvm/Support/Endian.h"
10
#include "llvm/Support/EndianStream.h"
11
#include "llvm/Support/raw_ostream.h"
12
 
13
#include <cstdint>
14
#include <optional>
15
 
16
namespace llvm {
17
namespace memprof {
18
 
19
enum class Meta : uint64_t {
20
  Start = 0,
21
#define MIBEntryDef(NameTag, Name, Type) NameTag,
22
#include "llvm/ProfileData/MIBEntryDef.inc"
23
#undef MIBEntryDef
24
  Size
25
};
26
 
27
using MemProfSchema = llvm::SmallVector<Meta, static_cast<int>(Meta::Size)>;
28
 
29
// Holds the actual MemInfoBlock data with all fields. Contents may be read or
30
// written partially by providing an appropriate schema to the serialize and
31
// deserialize methods.
32
struct PortableMemInfoBlock {
33
  PortableMemInfoBlock() = default;
34
  explicit PortableMemInfoBlock(const MemInfoBlock &Block) {
35
#define MIBEntryDef(NameTag, Name, Type) Name = Block.Name;
36
#include "llvm/ProfileData/MIBEntryDef.inc"
37
#undef MIBEntryDef
38
  }
39
 
40
  PortableMemInfoBlock(const MemProfSchema &Schema, const unsigned char *Ptr) {
41
    deserialize(Schema, Ptr);
42
  }
43
 
44
  // Read the contents of \p Ptr based on the \p Schema to populate the
45
  // MemInfoBlock member.
46
  void deserialize(const MemProfSchema &Schema, const unsigned char *Ptr) {
47
    using namespace support;
48
 
49
    for (const Meta Id : Schema) {
50
      switch (Id) {
51
#define MIBEntryDef(NameTag, Name, Type)                                       \
52
  case Meta::Name: {                                                           \
53
    Name = endian::readNext<Type, little, unaligned>(Ptr);                     \
54
  } break;
55
#include "llvm/ProfileData/MIBEntryDef.inc"
56
#undef MIBEntryDef
57
      default:
58
        llvm_unreachable("Unknown meta type id, is the profile collected from "
59
                         "a newer version of the runtime?");
60
      }
61
    }
62
  }
63
 
64
  // Write the contents of the MemInfoBlock based on the \p Schema provided to
65
  // the raw_ostream \p OS.
66
  void serialize(const MemProfSchema &Schema, raw_ostream &OS) const {
67
    using namespace support;
68
 
69
    endian::Writer LE(OS, little);
70
    for (const Meta Id : Schema) {
71
      switch (Id) {
72
#define MIBEntryDef(NameTag, Name, Type)                                       \
73
  case Meta::Name: {                                                           \
74
    LE.write<Type>(Name);                                                      \
75
  } break;
76
#include "llvm/ProfileData/MIBEntryDef.inc"
77
#undef MIBEntryDef
78
      default:
79
        llvm_unreachable("Unknown meta type id, invalid input?");
80
      }
81
    }
82
  }
83
 
84
  // Print out the contents of the MemInfoBlock in YAML format.
85
  void printYAML(raw_ostream &OS) const {
86
    OS << "      MemInfoBlock:\n";
87
#define MIBEntryDef(NameTag, Name, Type)                                       \
88
  OS << "        " << #Name << ": " << Name << "\n";
89
#include "llvm/ProfileData/MIBEntryDef.inc"
90
#undef MIBEntryDef
91
  }
92
 
93
  // Define getters for each type which can be called by analyses.
94
#define MIBEntryDef(NameTag, Name, Type)                                       \
95
  Type get##Name() const { return Name; }
96
#include "llvm/ProfileData/MIBEntryDef.inc"
97
#undef MIBEntryDef
98
 
99
  void clear() { *this = PortableMemInfoBlock(); }
100
 
101
  // Returns the full schema currently in use.
102
  static MemProfSchema getSchema() {
103
    MemProfSchema List;
104
#define MIBEntryDef(NameTag, Name, Type) List.push_back(Meta::Name);
105
#include "llvm/ProfileData/MIBEntryDef.inc"
106
#undef MIBEntryDef
107
    return List;
108
  }
109
 
110
  bool operator==(const PortableMemInfoBlock &Other) const {
111
#define MIBEntryDef(NameTag, Name, Type)                                       \
112
  if (Other.get##Name() != get##Name())                                        \
113
    return false;
114
#include "llvm/ProfileData/MIBEntryDef.inc"
115
#undef MIBEntryDef
116
    return true;
117
  }
118
 
119
  bool operator!=(const PortableMemInfoBlock &Other) const {
120
    return !operator==(Other);
121
  }
122
 
123
  static constexpr size_t serializedSize() {
124
    size_t Result = 0;
125
#define MIBEntryDef(NameTag, Name, Type) Result += sizeof(Type);
126
#include "llvm/ProfileData/MIBEntryDef.inc"
127
#undef MIBEntryDef
128
    return Result;
129
  }
130
 
131
private:
132
#define MIBEntryDef(NameTag, Name, Type) Type Name = Type();
133
#include "llvm/ProfileData/MIBEntryDef.inc"
134
#undef MIBEntryDef
135
};
136
 
137
// A type representing the id generated by hashing the contents of the Frame.
138
using FrameId = uint64_t;
139
// Describes a call frame for a dynamic allocation context. The contents of
140
// the frame are populated by symbolizing the stack depot call frame from the
141
// compiler runtime.
142
struct Frame {
143
  // A uuid (uint64_t) identifying the function. It is obtained by
144
  // llvm::md5(FunctionName) which returns the lower 64 bits.
145
  GlobalValue::GUID Function;
146
  // The symbol name for the function. Only populated in the Frame by the reader
147
  // if requested during initialization. This field should not be serialized.
148
  std::optional<std::string> SymbolName;
149
  // The source line offset of the call from the beginning of parent function.
150
  uint32_t LineOffset;
151
  // The source column number of the call to help distinguish multiple calls
152
  // on the same line.
153
  uint32_t Column;
154
  // Whether the current frame is inlined.
155
  bool IsInlineFrame;
156
 
157
  Frame(const Frame &Other) {
158
    Function = Other.Function;
159
    SymbolName = Other.SymbolName;
160
    LineOffset = Other.LineOffset;
161
    Column = Other.Column;
162
    IsInlineFrame = Other.IsInlineFrame;
163
  }
164
 
165
  Frame(uint64_t Hash, uint32_t Off, uint32_t Col, bool Inline)
166
      : Function(Hash), LineOffset(Off), Column(Col), IsInlineFrame(Inline) {}
167
 
168
  bool operator==(const Frame &Other) const {
169
    // Ignore the SymbolName field to avoid a string compare. Comparing the
170
    // function hash serves the same purpose.
171
    return Other.Function == Function && Other.LineOffset == LineOffset &&
172
           Other.Column == Column && Other.IsInlineFrame == IsInlineFrame;
173
  }
174
 
175
  Frame &operator=(const Frame &Other) {
176
    Function = Other.Function;
177
    SymbolName = Other.SymbolName;
178
    LineOffset = Other.LineOffset;
179
    Column = Other.Column;
180
    IsInlineFrame = Other.IsInlineFrame;
181
    return *this;
182
  }
183
 
184
  bool operator!=(const Frame &Other) const { return !operator==(Other); }
185
 
186
  // Write the contents of the frame to the ostream \p OS.
187
  void serialize(raw_ostream &OS) const {
188
    using namespace support;
189
 
190
    endian::Writer LE(OS, little);
191
 
192
    // If the type of the GlobalValue::GUID changes, then we need to update
193
    // the reader and the writer.
194
    static_assert(std::is_same<GlobalValue::GUID, uint64_t>::value,
195
                  "Expect GUID to be uint64_t.");
196
    LE.write<uint64_t>(Function);
197
 
198
    LE.write<uint32_t>(LineOffset);
199
    LE.write<uint32_t>(Column);
200
    LE.write<bool>(IsInlineFrame);
201
  }
202
 
203
  // Read a frame from char data which has been serialized as little endian.
204
  static Frame deserialize(const unsigned char *Ptr) {
205
    using namespace support;
206
 
207
    const uint64_t F = endian::readNext<uint64_t, little, unaligned>(Ptr);
208
    const uint32_t L = endian::readNext<uint32_t, little, unaligned>(Ptr);
209
    const uint32_t C = endian::readNext<uint32_t, little, unaligned>(Ptr);
210
    const bool I = endian::readNext<bool, little, unaligned>(Ptr);
211
    return Frame(/*Function=*/F, /*LineOffset=*/L, /*Column=*/C,
212
                 /*IsInlineFrame=*/I);
213
  }
214
 
215
  // Returns the size of the frame information.
216
  static constexpr size_t serializedSize() {
217
    return sizeof(Frame::Function) + sizeof(Frame::LineOffset) +
218
           sizeof(Frame::Column) + sizeof(Frame::IsInlineFrame);
219
  }
220
 
221
  // Print the frame information in YAML format.
222
  void printYAML(raw_ostream &OS) const {
223
    OS << "      -\n"
224
       << "        Function: " << Function << "\n"
225
       << "        SymbolName: " << SymbolName.value_or("<None>") << "\n"
226
       << "        LineOffset: " << LineOffset << "\n"
227
       << "        Column: " << Column << "\n"
228
       << "        Inline: " << IsInlineFrame << "\n";
229
  }
230
 
231
  // Return a hash value based on the contents of the frame. Here we don't use
232
  // hashing from llvm ADT since we are going to persist the hash id, the hash
233
  // combine algorithm in ADT uses a new randomized seed each time.
234
  inline FrameId hash() const {
235
    auto HashCombine = [](auto Value, size_t Seed) {
236
      std::hash<decltype(Value)> Hasher;
237
      // The constant used below is the 64 bit representation of the fractional
238
      // part of the golden ratio. Used here for the randomness in their bit
239
      // pattern.
240
      return Hasher(Value) + 0x9e3779b97f4a7c15 + (Seed << 6) + (Seed >> 2);
241
    };
242
 
243
    size_t Result = 0;
244
    Result ^= HashCombine(Function, Result);
245
    Result ^= HashCombine(LineOffset, Result);
246
    Result ^= HashCombine(Column, Result);
247
    Result ^= HashCombine(IsInlineFrame, Result);
248
    return static_cast<FrameId>(Result);
249
  }
250
};
251
 
252
// Holds allocation information in a space efficient format where frames are
253
// represented using unique identifiers.
254
struct IndexedAllocationInfo {
255
  // The dynamic calling context for the allocation in bottom-up (leaf-to-root)
256
  // order. Frame contents are stored out-of-line.
257
  llvm::SmallVector<FrameId> CallStack;
258
  // The statistics obtained from the runtime for the allocation.
259
  PortableMemInfoBlock Info;
260
 
261
  IndexedAllocationInfo() = default;
262
  IndexedAllocationInfo(ArrayRef<FrameId> CS, const MemInfoBlock &MB)
263
      : CallStack(CS.begin(), CS.end()), Info(MB) {}
264
 
265
  // Returns the size in bytes when this allocation info struct is serialized.
266
  size_t serializedSize() const {
267
    return sizeof(uint64_t) + // The number of frames to serialize.
268
           sizeof(FrameId) * CallStack.size() +    // The callstack frame ids.
269
           PortableMemInfoBlock::serializedSize(); // The size of the payload.
270
  }
271
 
272
  bool operator==(const IndexedAllocationInfo &Other) const {
273
    if (Other.Info != Info)
274
      return false;
275
 
276
    if (Other.CallStack.size() != CallStack.size())
277
      return false;
278
 
279
    for (size_t J = 0; J < Other.CallStack.size(); J++) {
280
      if (Other.CallStack[J] != CallStack[J])
281
        return false;
282
    }
283
    return true;
284
  }
285
 
286
  bool operator!=(const IndexedAllocationInfo &Other) const {
287
    return !operator==(Other);
288
  }
289
};
290
 
291
// Holds allocation information with frame contents inline. The type should
292
// be used for temporary in-memory instances.
293
struct AllocationInfo {
294
  // Same as IndexedAllocationInfo::CallStack with the frame contents inline.
295
  llvm::SmallVector<Frame> CallStack;
296
  // Same as IndexedAllocationInfo::Info;
297
  PortableMemInfoBlock Info;
298
 
299
  AllocationInfo() = default;
300
  AllocationInfo(
301
      const IndexedAllocationInfo &IndexedAI,
302
      llvm::function_ref<const Frame(const FrameId)> IdToFrameCallback) {
303
    for (const FrameId &Id : IndexedAI.CallStack) {
304
      CallStack.push_back(IdToFrameCallback(Id));
305
    }
306
    Info = IndexedAI.Info;
307
  }
308
 
309
  void printYAML(raw_ostream &OS) const {
310
    OS << "    -\n";
311
    OS << "      Callstack:\n";
312
    // TODO: Print out the frame on one line with to make it easier for deep
313
    // callstacks once we have a test to check valid YAML is generated.
314
    for (const Frame &F : CallStack) {
315
      F.printYAML(OS);
316
    }
317
    Info.printYAML(OS);
318
  }
319
};
320
 
321
// Holds the memprof profile information for a function. The internal
322
// representation stores frame ids for efficiency. This representation should
323
// be used in the profile conversion and manipulation tools.
324
struct IndexedMemProfRecord {
325
  // Memory allocation sites in this function for which we have memory
326
  // profiling data.
327
  llvm::SmallVector<IndexedAllocationInfo> AllocSites;
328
  // Holds call sites in this function which are part of some memory
329
  // allocation context. We store this as a list of locations, each with its
330
  // list of inline locations in bottom-up order i.e. from leaf to root. The
331
  // inline location list may include additional entries, users should pick
332
  // the last entry in the list with the same function GUID.
333
  llvm::SmallVector<llvm::SmallVector<FrameId>> CallSites;
334
 
335
  void clear() {
336
    AllocSites.clear();
337
    CallSites.clear();
338
  }
339
 
340
  void merge(const IndexedMemProfRecord &Other) {
341
    // TODO: Filter out duplicates which may occur if multiple memprof
342
    // profiles are merged together using llvm-profdata.
343
    AllocSites.append(Other.AllocSites);
344
    CallSites.append(Other.CallSites);
345
  }
346
 
347
  size_t serializedSize() const {
348
    size_t Result = sizeof(GlobalValue::GUID);
349
    for (const IndexedAllocationInfo &N : AllocSites)
350
      Result += N.serializedSize();
351
 
352
    // The number of callsites we have information for.
353
    Result += sizeof(uint64_t);
354
    for (const auto &Frames : CallSites) {
355
      // The number of frame ids to serialize.
356
      Result += sizeof(uint64_t);
357
      Result += Frames.size() * sizeof(FrameId);
358
    }
359
    return Result;
360
  }
361
 
362
  bool operator==(const IndexedMemProfRecord &Other) const {
363
    if (Other.AllocSites.size() != AllocSites.size())
364
      return false;
365
 
366
    if (Other.CallSites.size() != CallSites.size())
367
      return false;
368
 
369
    for (size_t I = 0; I < AllocSites.size(); I++) {
370
      if (AllocSites[I] != Other.AllocSites[I])
371
        return false;
372
    }
373
 
374
    for (size_t I = 0; I < CallSites.size(); I++) {
375
      if (CallSites[I] != Other.CallSites[I])
376
        return false;
377
    }
378
    return true;
379
  }
380
 
381
  // Serializes the memprof records in \p Records to the ostream \p OS based
382
  // on the schema provided in \p Schema.
383
  void serialize(const MemProfSchema &Schema, raw_ostream &OS);
384
 
385
  // Deserializes memprof records from the Buffer.
386
  static IndexedMemProfRecord deserialize(const MemProfSchema &Schema,
387
                                          const unsigned char *Buffer);
388
 
389
  // Returns the GUID for the function name after canonicalization. For
390
  // memprof, we remove any .llvm suffix added by LTO. MemProfRecords are
391
  // mapped to functions using this GUID.
392
  static GlobalValue::GUID getGUID(const StringRef FunctionName);
393
};
394
 
395
// Holds the memprof profile information for a function. The internal
396
// representation stores frame contents inline. This representation should
397
// be used for small amount of temporary, in memory instances.
398
struct MemProfRecord {
399
  // Same as IndexedMemProfRecord::AllocSites with frame contents inline.
400
  llvm::SmallVector<AllocationInfo> AllocSites;
401
  // Same as IndexedMemProfRecord::CallSites with frame contents inline.
402
  llvm::SmallVector<llvm::SmallVector<Frame>> CallSites;
403
 
404
  MemProfRecord() = default;
405
  MemProfRecord(
406
      const IndexedMemProfRecord &Record,
407
      llvm::function_ref<const Frame(const FrameId Id)> IdToFrameCallback) {
408
    for (const IndexedAllocationInfo &IndexedAI : Record.AllocSites) {
409
      AllocSites.emplace_back(IndexedAI, IdToFrameCallback);
410
    }
411
    for (const ArrayRef<FrameId> Site : Record.CallSites) {
412
      llvm::SmallVector<Frame> Frames;
413
      for (const FrameId Id : Site) {
414
        Frames.push_back(IdToFrameCallback(Id));
415
      }
416
      CallSites.push_back(Frames);
417
    }
418
  }
419
 
420
  // Prints out the contents of the memprof record in YAML.
421
  void print(llvm::raw_ostream &OS) const {
422
    if (!AllocSites.empty()) {
423
      OS << "    AllocSites:\n";
424
      for (const AllocationInfo &N : AllocSites)
425
        N.printYAML(OS);
426
    }
427
 
428
    if (!CallSites.empty()) {
429
      OS << "    CallSites:\n";
430
      for (const llvm::SmallVector<Frame> &Frames : CallSites) {
431
        for (const Frame &F : Frames) {
432
          OS << "    -\n";
433
          F.printYAML(OS);
434
        }
435
      }
436
    }
437
  }
438
};
439
 
440
// Reads a memprof schema from a buffer. All entries in the buffer are
441
// interpreted as uint64_t. The first entry in the buffer denotes the number of
442
// ids in the schema. Subsequent entries are integers which map to memprof::Meta
443
// enum class entries. After successfully reading the schema, the pointer is one
444
// byte past the schema contents.
445
Expected<MemProfSchema> readMemProfSchema(const unsigned char *&Buffer);
446
 
447
// Trait for reading IndexedMemProfRecord data from the on-disk hash table.
448
class RecordLookupTrait {
449
public:
450
  using data_type = const IndexedMemProfRecord &;
451
  using internal_key_type = uint64_t;
452
  using external_key_type = uint64_t;
453
  using hash_value_type = uint64_t;
454
  using offset_type = uint64_t;
455
 
456
  RecordLookupTrait() = delete;
457
  RecordLookupTrait(const MemProfSchema &S) : Schema(S) {}
458
 
459
  static bool EqualKey(uint64_t A, uint64_t B) { return A == B; }
460
  static uint64_t GetInternalKey(uint64_t K) { return K; }
461
  static uint64_t GetExternalKey(uint64_t K) { return K; }
462
 
463
  hash_value_type ComputeHash(uint64_t K) { return K; }
464
 
465
  static std::pair<offset_type, offset_type>
466
  ReadKeyDataLength(const unsigned char *&D) {
467
    using namespace support;
468
 
469
    offset_type KeyLen = endian::readNext<offset_type, little, unaligned>(D);
470
    offset_type DataLen = endian::readNext<offset_type, little, unaligned>(D);
471
    return std::make_pair(KeyLen, DataLen);
472
  }
473
 
474
  uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) {
475
    using namespace support;
476
    return endian::readNext<external_key_type, little, unaligned>(D);
477
  }
478
 
479
  data_type ReadData(uint64_t K, const unsigned char *D,
480
                     offset_type /*Unused*/) {
481
    Record = IndexedMemProfRecord::deserialize(Schema, D);
482
    return Record;
483
  }
484
 
485
private:
486
  // Holds the memprof schema used to deserialize records.
487
  MemProfSchema Schema;
488
  // Holds the records from one function deserialized from the indexed format.
489
  IndexedMemProfRecord Record;
490
};
491
 
492
// Trait for writing IndexedMemProfRecord data to the on-disk hash table.
493
class RecordWriterTrait {
494
public:
495
  using key_type = uint64_t;
496
  using key_type_ref = uint64_t;
497
 
498
  using data_type = IndexedMemProfRecord;
499
  using data_type_ref = IndexedMemProfRecord &;
500
 
501
  using hash_value_type = uint64_t;
502
  using offset_type = uint64_t;
503
 
504
  // Pointer to the memprof schema to use for the generator. Unlike the reader
505
  // we must use a default constructor with no params for the writer trait so we
506
  // have a public member which must be initialized by the user.
507
  MemProfSchema *Schema = nullptr;
508
 
509
  RecordWriterTrait() = default;
510
 
511
  static hash_value_type ComputeHash(key_type_ref K) { return K; }
512
 
513
  static std::pair<offset_type, offset_type>
514
  EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
515
    using namespace support;
516
 
517
    endian::Writer LE(Out, little);
518
    offset_type N = sizeof(K);
519
    LE.write<offset_type>(N);
520
    offset_type M = V.serializedSize();
521
    LE.write<offset_type>(M);
522
    return std::make_pair(N, M);
523
  }
524
 
525
  void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) {
526
    using namespace support;
527
    endian::Writer LE(Out, little);
528
    LE.write<uint64_t>(K);
529
  }
530
 
531
  void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
532
                offset_type /*Unused*/) {
533
    assert(Schema != nullptr && "MemProf schema is not initialized!");
534
    V.serialize(*Schema, Out);
535
  }
536
};
537
 
538
// Trait for writing frame mappings to the on-disk hash table.
539
class FrameWriterTrait {
540
public:
541
  using key_type = FrameId;
542
  using key_type_ref = FrameId;
543
 
544
  using data_type = Frame;
545
  using data_type_ref = Frame &;
546
 
547
  using hash_value_type = FrameId;
548
  using offset_type = uint64_t;
549
 
550
  static hash_value_type ComputeHash(key_type_ref K) { return K; }
551
 
552
  static std::pair<offset_type, offset_type>
553
  EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
554
    using namespace support;
555
    endian::Writer LE(Out, little);
556
    offset_type N = sizeof(K);
557
    LE.write<offset_type>(N);
558
    offset_type M = V.serializedSize();
559
    LE.write<offset_type>(M);
560
    return std::make_pair(N, M);
561
  }
562
 
563
  void EmitKey(raw_ostream &Out, key_type_ref K, offset_type /*Unused*/) {
564
    using namespace support;
565
    endian::Writer LE(Out, little);
566
    LE.write<key_type>(K);
567
  }
568
 
569
  void EmitData(raw_ostream &Out, key_type_ref /*Unused*/, data_type_ref V,
570
                offset_type /*Unused*/) {
571
    V.serialize(Out);
572
  }
573
};
574
 
575
// Trait for reading frame mappings from the on-disk hash table.
576
class FrameLookupTrait {
577
public:
578
  using data_type = const Frame;
579
  using internal_key_type = FrameId;
580
  using external_key_type = FrameId;
581
  using hash_value_type = FrameId;
582
  using offset_type = uint64_t;
583
 
584
  static bool EqualKey(internal_key_type A, internal_key_type B) {
585
    return A == B;
586
  }
587
  static uint64_t GetInternalKey(internal_key_type K) { return K; }
588
  static uint64_t GetExternalKey(external_key_type K) { return K; }
589
 
590
  hash_value_type ComputeHash(internal_key_type K) { return K; }
591
 
592
  static std::pair<offset_type, offset_type>
593
  ReadKeyDataLength(const unsigned char *&D) {
594
    using namespace support;
595
 
596
    offset_type KeyLen = endian::readNext<offset_type, little, unaligned>(D);
597
    offset_type DataLen = endian::readNext<offset_type, little, unaligned>(D);
598
    return std::make_pair(KeyLen, DataLen);
599
  }
600
 
601
  uint64_t ReadKey(const unsigned char *D, offset_type /*Unused*/) {
602
    using namespace support;
603
    return endian::readNext<external_key_type, little, unaligned>(D);
604
  }
605
 
606
  data_type ReadData(uint64_t K, const unsigned char *D,
607
                     offset_type /*Unused*/) {
608
    return Frame::deserialize(D);
609
  }
610
};
611
} // namespace memprof
612
} // namespace llvm
613
 
614
#endif // LLVM_PROFILEDATA_MEMPROF_H_