16b6b8c4fSAdrian McCarthy //===- TpiHashing.cpp -----------------------------------------------------===//
26b6b8c4fSAdrian McCarthy //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66b6b8c4fSAdrian McCarthy //
76b6b8c4fSAdrian McCarthy //===----------------------------------------------------------------------===//
86b6b8c4fSAdrian McCarthy
96b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
106b6b8c4fSAdrian McCarthy
11f8a2e048SZachary Turner #include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
126b6b8c4fSAdrian McCarthy #include "llvm/DebugInfo/PDB/Native/Hash.h"
131e1e3ba2SHans Wennborg #include "llvm/Support/CRC.h"
146b6b8c4fSAdrian McCarthy
156b6b8c4fSAdrian McCarthy using namespace llvm;
166b6b8c4fSAdrian McCarthy using namespace llvm::codeview;
176b6b8c4fSAdrian McCarthy using namespace llvm::pdb;
186b6b8c4fSAdrian McCarthy
196b6b8c4fSAdrian McCarthy // Corresponds to `fUDTAnon`.
isAnonymous(StringRef Name)20c50349d4SReid Kleckner static bool isAnonymous(StringRef Name) {
216b6b8c4fSAdrian McCarthy return Name == "<unnamed-tag>" || Name == "__unnamed" ||
226b6b8c4fSAdrian McCarthy Name.endswith("::<unnamed-tag>") || Name.endswith("::__unnamed");
236b6b8c4fSAdrian McCarthy }
246b6b8c4fSAdrian McCarthy
25c50349d4SReid Kleckner // Computes the hash for a user-defined type record. This could be a struct,
26c50349d4SReid Kleckner // class, union, or enum.
getHashForUdt(const TagRecord & Rec,ArrayRef<uint8_t> FullRecord)27c50349d4SReid Kleckner static uint32_t getHashForUdt(const TagRecord &Rec,
28c50349d4SReid Kleckner ArrayRef<uint8_t> FullRecord) {
29c50349d4SReid Kleckner ClassOptions Opts = Rec.getOptions();
30c50349d4SReid Kleckner bool ForwardRef = bool(Opts & ClassOptions::ForwardReference);
31c50349d4SReid Kleckner bool Scoped = bool(Opts & ClassOptions::Scoped);
32c50349d4SReid Kleckner bool HasUniqueName = bool(Opts & ClassOptions::HasUniqueName);
33c50349d4SReid Kleckner bool IsAnon = HasUniqueName && isAnonymous(Rec.getName());
346b6b8c4fSAdrian McCarthy
356b6b8c4fSAdrian McCarthy if (!ForwardRef && !Scoped && !IsAnon)
366b6b8c4fSAdrian McCarthy return hashStringV1(Rec.getName());
37c50349d4SReid Kleckner if (!ForwardRef && HasUniqueName && !IsAnon)
386b6b8c4fSAdrian McCarthy return hashStringV1(Rec.getUniqueName());
396b6b8c4fSAdrian McCarthy return hashBufferV8(FullRecord);
406b6b8c4fSAdrian McCarthy }
416b6b8c4fSAdrian McCarthy
42c50349d4SReid Kleckner template <typename T>
getHashForUdt(const CVType & Rec)43c50349d4SReid Kleckner static Expected<uint32_t> getHashForUdt(const CVType &Rec) {
44c50349d4SReid Kleckner T Deserialized;
45c50349d4SReid Kleckner if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec),
46c50349d4SReid Kleckner Deserialized))
47*c55cf4afSBill Wendling return std::move(E);
48c50349d4SReid Kleckner return getHashForUdt(Deserialized, Rec.data());
49c50349d4SReid Kleckner }
50c50349d4SReid Kleckner
51c50349d4SReid Kleckner template <typename T>
getTagRecordHashForUdt(const CVType & Rec)52cfa1d499SZachary Turner static Expected<TagRecordHash> getTagRecordHashForUdt(const CVType &Rec) {
53cfa1d499SZachary Turner T Deserialized;
54cfa1d499SZachary Turner if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec),
55cfa1d499SZachary Turner Deserialized))
56*c55cf4afSBill Wendling return std::move(E);
57cfa1d499SZachary Turner
58cfa1d499SZachary Turner ClassOptions Opts = Deserialized.getOptions();
59cfa1d499SZachary Turner
60cfa1d499SZachary Turner bool ForwardRef = bool(Opts & ClassOptions::ForwardReference);
61cfa1d499SZachary Turner
62cfa1d499SZachary Turner uint32_t ThisRecordHash = getHashForUdt(Deserialized, Rec.data());
63cfa1d499SZachary Turner
64cfa1d499SZachary Turner // If we don't have a forward ref we can't compute the hash of it from the
65cfa1d499SZachary Turner // full record because it requires hashing the entire buffer.
66cfa1d499SZachary Turner if (!ForwardRef)
67cfa1d499SZachary Turner return TagRecordHash{std::move(Deserialized), ThisRecordHash, 0};
68cfa1d499SZachary Turner
69cfa1d499SZachary Turner bool Scoped = bool(Opts & ClassOptions::Scoped);
70cfa1d499SZachary Turner
71cfa1d499SZachary Turner StringRef NameToHash =
72cfa1d499SZachary Turner Scoped ? Deserialized.getUniqueName() : Deserialized.getName();
73cfa1d499SZachary Turner uint32_t FullHash = hashStringV1(NameToHash);
74cfa1d499SZachary Turner return TagRecordHash{std::move(Deserialized), FullHash, ThisRecordHash};
75cfa1d499SZachary Turner }
76cfa1d499SZachary Turner
77cfa1d499SZachary Turner template <typename T>
getSourceLineHash(const CVType & Rec)78c50349d4SReid Kleckner static Expected<uint32_t> getSourceLineHash(const CVType &Rec) {
79c50349d4SReid Kleckner T Deserialized;
80c50349d4SReid Kleckner if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec),
81c50349d4SReid Kleckner Deserialized))
82*c55cf4afSBill Wendling return std::move(E);
836b6b8c4fSAdrian McCarthy char Buf[4];
84c50349d4SReid Kleckner support::endian::write32le(Buf, Deserialized.getUDT().getIndex());
856b6b8c4fSAdrian McCarthy return hashStringV1(StringRef(Buf, 4));
866b6b8c4fSAdrian McCarthy }
876b6b8c4fSAdrian McCarthy
hashTagRecord(const codeview::CVType & Type)88cfa1d499SZachary Turner Expected<TagRecordHash> llvm::pdb::hashTagRecord(const codeview::CVType &Type) {
89cfa1d499SZachary Turner switch (Type.kind()) {
90cfa1d499SZachary Turner case LF_CLASS:
91cfa1d499SZachary Turner case LF_STRUCTURE:
92cfa1d499SZachary Turner case LF_INTERFACE:
93cfa1d499SZachary Turner return getTagRecordHashForUdt<ClassRecord>(Type);
94cfa1d499SZachary Turner case LF_UNION:
95cfa1d499SZachary Turner return getTagRecordHashForUdt<UnionRecord>(Type);
96cfa1d499SZachary Turner case LF_ENUM:
97cfa1d499SZachary Turner return getTagRecordHashForUdt<EnumRecord>(Type);
98cfa1d499SZachary Turner default:
99cfa1d499SZachary Turner assert(false && "Type is not a tag record!");
100cfa1d499SZachary Turner }
101cfa1d499SZachary Turner return make_error<StringError>("Invalid record type",
102cfa1d499SZachary Turner inconvertibleErrorCode());
103cfa1d499SZachary Turner }
104cfa1d499SZachary Turner
hashTypeRecord(const CVType & Rec)105c50349d4SReid Kleckner Expected<uint32_t> llvm::pdb::hashTypeRecord(const CVType &Rec) {
106c50349d4SReid Kleckner switch (Rec.kind()) {
107c50349d4SReid Kleckner case LF_CLASS:
108c50349d4SReid Kleckner case LF_STRUCTURE:
109c50349d4SReid Kleckner case LF_INTERFACE:
110c50349d4SReid Kleckner return getHashForUdt<ClassRecord>(Rec);
111c50349d4SReid Kleckner case LF_UNION:
112c50349d4SReid Kleckner return getHashForUdt<UnionRecord>(Rec);
113c50349d4SReid Kleckner case LF_ENUM:
114c50349d4SReid Kleckner return getHashForUdt<EnumRecord>(Rec);
115c50349d4SReid Kleckner
116c50349d4SReid Kleckner case LF_UDT_SRC_LINE:
117c50349d4SReid Kleckner return getSourceLineHash<UdtSourceLineRecord>(Rec);
118c50349d4SReid Kleckner case LF_UDT_MOD_SRC_LINE:
119c50349d4SReid Kleckner return getSourceLineHash<UdtModSourceLineRecord>(Rec);
120c50349d4SReid Kleckner
121c50349d4SReid Kleckner default:
122c50349d4SReid Kleckner break;
1236b6b8c4fSAdrian McCarthy }
1246b6b8c4fSAdrian McCarthy
125c50349d4SReid Kleckner // Run CRC32 over the bytes. This corresponds to `hashBufv8`.
126c50349d4SReid Kleckner JamCRC JC(/*Init=*/0U);
1271e1e3ba2SHans Wennborg JC.update(Rec.data());
128c50349d4SReid Kleckner return JC.getCRC();
1296b6b8c4fSAdrian McCarthy }
130