summaryrefslogtreecommitdiffstats
path: root/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp
blob: 18708826ffc786e47449a6829d5838c738b00037 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//===- TpiHashing.cpp -----------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"

#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/PDB/Native/Hash.h"
#include "llvm/Support/JamCRC.h"

using namespace llvm;
using namespace llvm::codeview;
using namespace llvm::pdb;

// Corresponds to `fUDTAnon`.
static bool isAnonymous(StringRef Name) {
  return Name == "<unnamed-tag>" || Name == "__unnamed" ||
         Name.endswith("::<unnamed-tag>") || Name.endswith("::__unnamed");
}

// Computes the hash for a user-defined type record. This could be a struct,
// class, union, or enum.
static uint32_t getHashForUdt(const TagRecord &Rec,
                              ArrayRef<uint8_t> FullRecord) {
  ClassOptions Opts = Rec.getOptions();
  bool ForwardRef = bool(Opts & ClassOptions::ForwardReference);
  bool Scoped = bool(Opts & ClassOptions::Scoped);
  bool HasUniqueName = bool(Opts & ClassOptions::HasUniqueName);
  bool IsAnon = HasUniqueName && isAnonymous(Rec.getName());

  if (!ForwardRef && !Scoped && !IsAnon)
    return hashStringV1(Rec.getName());
  if (!ForwardRef && HasUniqueName && !IsAnon)
    return hashStringV1(Rec.getUniqueName());
  return hashBufferV8(FullRecord);
}

template <typename T>
static Expected<uint32_t> getHashForUdt(const CVType &Rec) {
  T Deserialized;
  if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec),
                                               Deserialized))
    return std::move(E);
  return getHashForUdt(Deserialized, Rec.data());
}

template <typename T>
static Expected<TagRecordHash> getTagRecordHashForUdt(const CVType &Rec) {
  T Deserialized;
  if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec),
                                               Deserialized))
    return std::move(E);

  ClassOptions Opts = Deserialized.getOptions();

  bool ForwardRef = bool(Opts & ClassOptions::ForwardReference);

  uint32_t ThisRecordHash = getHashForUdt(Deserialized, Rec.data());

  // If we don't have a forward ref we can't compute the hash of it from the
  // full record because it requires hashing the entire buffer.
  if (!ForwardRef)
    return TagRecordHash{std::move(Deserialized), ThisRecordHash, 0};

  bool Scoped = bool(Opts & ClassOptions::Scoped);

  StringRef NameToHash =
      Scoped ? Deserialized.getUniqueName() : Deserialized.getName();
  uint32_t FullHash = hashStringV1(NameToHash);
  return TagRecordHash{std::move(Deserialized), FullHash, ThisRecordHash};
}

template <typename T>
static Expected<uint32_t> getSourceLineHash(const CVType &Rec) {
  T Deserialized;
  if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec),
                                               Deserialized))
    return std::move(E);
  char Buf[4];
  support::endian::write32le(Buf, Deserialized.getUDT().getIndex());
  return hashStringV1(StringRef(Buf, 4));
}

Expected<TagRecordHash> llvm::pdb::hashTagRecord(const codeview::CVType &Type) {
  switch (Type.kind()) {
  case LF_CLASS:
  case LF_STRUCTURE:
  case LF_INTERFACE:
    return getTagRecordHashForUdt<ClassRecord>(Type);
  case LF_UNION:
    return getTagRecordHashForUdt<UnionRecord>(Type);
  case LF_ENUM:
    return getTagRecordHashForUdt<EnumRecord>(Type);
  default:
    assert(false && "Type is not a tag record!");
  }
  return make_error<StringError>("Invalid record type",
                                 inconvertibleErrorCode());
}

Expected<uint32_t> llvm::pdb::hashTypeRecord(const CVType &Rec) {
  switch (Rec.kind()) {
  case LF_CLASS:
  case LF_STRUCTURE:
  case LF_INTERFACE:
    return getHashForUdt<ClassRecord>(Rec);
  case LF_UNION:
    return getHashForUdt<UnionRecord>(Rec);
  case LF_ENUM:
    return getHashForUdt<EnumRecord>(Rec);

  case LF_UDT_SRC_LINE:
    return getSourceLineHash<UdtSourceLineRecord>(Rec);
  case LF_UDT_MOD_SRC_LINE:
    return getSourceLineHash<UdtModSourceLineRecord>(Rec);

  default:
    break;
  }

  // Run CRC32 over the bytes. This corresponds to `hashBufv8`.
  JamCRC JC(/*Init=*/0U);
  ArrayRef<char> Bytes(reinterpret_cast<const char *>(Rec.data().data()),
                       Rec.data().size());
  JC.update(Bytes);
  return JC.getCRC();
}
OpenPOWER on IntegriCloud