1 | //===- TpiHashing.cpp -----------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" |
10 | |
11 | #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" |
12 | #include "llvm/DebugInfo/PDB/Native/Hash.h" |
13 | #include "llvm/Support/CRC.h" |
14 | |
15 | using namespace llvm; |
16 | using namespace llvm::codeview; |
17 | using namespace llvm::pdb; |
18 | |
19 | // Corresponds to `fUDTAnon`. |
20 | static bool isAnonymous(StringRef Name) { |
21 | return Name == "<unnamed-tag>" || Name == "__unnamed" || |
22 | Name.ends_with(Suffix: "::<unnamed-tag>" ) || Name.ends_with(Suffix: "::__unnamed" ); |
23 | } |
24 | |
25 | // Computes the hash for a user-defined type record. This could be a struct, |
26 | // class, union, or enum. |
27 | static uint32_t getHashForUdt(const TagRecord &Rec, |
28 | ArrayRef<uint8_t> FullRecord) { |
29 | ClassOptions Opts = Rec.getOptions(); |
30 | bool ForwardRef = bool(Opts & ClassOptions::ForwardReference); |
31 | bool Scoped = bool(Opts & ClassOptions::Scoped); |
32 | bool HasUniqueName = bool(Opts & ClassOptions::HasUniqueName); |
33 | bool IsAnon = HasUniqueName && isAnonymous(Name: Rec.getName()); |
34 | |
35 | if (!ForwardRef && !Scoped && !IsAnon) |
36 | return hashStringV1(Str: Rec.getName()); |
37 | if (!ForwardRef && HasUniqueName && !IsAnon) |
38 | return hashStringV1(Str: Rec.getUniqueName()); |
39 | return hashBufferV8(Data: FullRecord); |
40 | } |
41 | |
42 | template <typename T> |
43 | static Expected<uint32_t> getHashForUdt(const CVType &Rec) { |
44 | T Deserialized; |
45 | if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec), |
46 | Deserialized)) |
47 | return std::move(E); |
48 | return getHashForUdt(Deserialized, Rec.data()); |
49 | } |
50 | |
51 | template <typename T> |
52 | static Expected<TagRecordHash> getTagRecordHashForUdt(const CVType &Rec) { |
53 | T Deserialized; |
54 | if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec), |
55 | Deserialized)) |
56 | return std::move(E); |
57 | |
58 | ClassOptions Opts = Deserialized.getOptions(); |
59 | |
60 | bool ForwardRef = bool(Opts & ClassOptions::ForwardReference); |
61 | |
62 | uint32_t ThisRecordHash = getHashForUdt(Deserialized, Rec.data()); |
63 | |
64 | // If we don't have a forward ref we can't compute the hash of it from the |
65 | // full record because it requires hashing the entire buffer. |
66 | if (!ForwardRef) |
67 | return TagRecordHash{std::move(Deserialized), ThisRecordHash, 0}; |
68 | |
69 | bool Scoped = bool(Opts & ClassOptions::Scoped); |
70 | |
71 | StringRef NameToHash = |
72 | Scoped ? Deserialized.getUniqueName() : Deserialized.getName(); |
73 | uint32_t FullHash = hashStringV1(Str: NameToHash); |
74 | return TagRecordHash{std::move(Deserialized), FullHash, ThisRecordHash}; |
75 | } |
76 | |
77 | template <typename T> |
78 | static Expected<uint32_t> getSourceLineHash(const CVType &Rec) { |
79 | T Deserialized; |
80 | if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec), |
81 | Deserialized)) |
82 | return std::move(E); |
83 | char Buf[4]; |
84 | support::endian::write32le(P: Buf, V: Deserialized.getUDT().getIndex()); |
85 | return hashStringV1(Str: StringRef(Buf, 4)); |
86 | } |
87 | |
88 | Expected<TagRecordHash> llvm::pdb::hashTagRecord(const codeview::CVType &Type) { |
89 | switch (Type.kind()) { |
90 | case LF_CLASS: |
91 | case LF_STRUCTURE: |
92 | case LF_INTERFACE: |
93 | return getTagRecordHashForUdt<ClassRecord>(Rec: Type); |
94 | case LF_UNION: |
95 | return getTagRecordHashForUdt<UnionRecord>(Rec: Type); |
96 | case LF_ENUM: |
97 | return getTagRecordHashForUdt<EnumRecord>(Rec: Type); |
98 | default: |
99 | assert(false && "Type is not a tag record!" ); |
100 | } |
101 | return make_error<StringError>(Args: "Invalid record type" , |
102 | Args: inconvertibleErrorCode()); |
103 | } |
104 | |
105 | Expected<uint32_t> llvm::pdb::hashTypeRecord(const CVType &Rec) { |
106 | switch (Rec.kind()) { |
107 | case LF_CLASS: |
108 | case LF_STRUCTURE: |
109 | case LF_INTERFACE: |
110 | return getHashForUdt<ClassRecord>(Rec); |
111 | case LF_UNION: |
112 | return getHashForUdt<UnionRecord>(Rec); |
113 | case LF_ENUM: |
114 | return getHashForUdt<EnumRecord>(Rec); |
115 | |
116 | case LF_UDT_SRC_LINE: |
117 | return getSourceLineHash<UdtSourceLineRecord>(Rec); |
118 | case LF_UDT_MOD_SRC_LINE: |
119 | return getSourceLineHash<UdtModSourceLineRecord>(Rec); |
120 | |
121 | default: |
122 | break; |
123 | } |
124 | |
125 | // Run CRC32 over the bytes. This corresponds to `hashBufv8`. |
126 | JamCRC JC(/*Init=*/0U); |
127 | JC.update(Data: Rec.data()); |
128 | return JC.getCRC(); |
129 | } |
130 | |