10b57cec5SDimitry Andric //===- TpiHashing.cpp -----------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
100b57cec5SDimitry Andric 
110b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
120b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/Hash.h"
138bcb0991SDimitry Andric #include "llvm/Support/CRC.h"
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric using namespace llvm;
160b57cec5SDimitry Andric using namespace llvm::codeview;
170b57cec5SDimitry Andric using namespace llvm::pdb;
180b57cec5SDimitry Andric 
190b57cec5SDimitry Andric // Corresponds to `fUDTAnon`.
isAnonymous(StringRef Name)200b57cec5SDimitry Andric static bool isAnonymous(StringRef Name) {
210b57cec5SDimitry Andric   return Name == "<unnamed-tag>" || Name == "__unnamed" ||
225f757f3fSDimitry Andric          Name.ends_with("::<unnamed-tag>") || Name.ends_with("::__unnamed");
230b57cec5SDimitry Andric }
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric // Computes the hash for a user-defined type record. This could be a struct,
260b57cec5SDimitry Andric // class, union, or enum.
getHashForUdt(const TagRecord & Rec,ArrayRef<uint8_t> FullRecord)270b57cec5SDimitry Andric static uint32_t getHashForUdt(const TagRecord &Rec,
280b57cec5SDimitry Andric                               ArrayRef<uint8_t> FullRecord) {
290b57cec5SDimitry Andric   ClassOptions Opts = Rec.getOptions();
300b57cec5SDimitry Andric   bool ForwardRef = bool(Opts & ClassOptions::ForwardReference);
310b57cec5SDimitry Andric   bool Scoped = bool(Opts & ClassOptions::Scoped);
320b57cec5SDimitry Andric   bool HasUniqueName = bool(Opts & ClassOptions::HasUniqueName);
330b57cec5SDimitry Andric   bool IsAnon = HasUniqueName && isAnonymous(Rec.getName());
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric   if (!ForwardRef && !Scoped && !IsAnon)
360b57cec5SDimitry Andric     return hashStringV1(Rec.getName());
370b57cec5SDimitry Andric   if (!ForwardRef && HasUniqueName && !IsAnon)
380b57cec5SDimitry Andric     return hashStringV1(Rec.getUniqueName());
390b57cec5SDimitry Andric   return hashBufferV8(FullRecord);
400b57cec5SDimitry Andric }
410b57cec5SDimitry Andric 
420b57cec5SDimitry Andric template <typename T>
getHashForUdt(const CVType & Rec)430b57cec5SDimitry Andric static Expected<uint32_t> getHashForUdt(const CVType &Rec) {
440b57cec5SDimitry Andric   T Deserialized;
450b57cec5SDimitry Andric   if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec),
460b57cec5SDimitry Andric                                                Deserialized))
470b57cec5SDimitry Andric     return std::move(E);
480b57cec5SDimitry Andric   return getHashForUdt(Deserialized, Rec.data());
490b57cec5SDimitry Andric }
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric template <typename T>
getTagRecordHashForUdt(const CVType & Rec)520b57cec5SDimitry Andric static Expected<TagRecordHash> getTagRecordHashForUdt(const CVType &Rec) {
530b57cec5SDimitry Andric   T Deserialized;
540b57cec5SDimitry Andric   if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec),
550b57cec5SDimitry Andric                                                Deserialized))
560b57cec5SDimitry Andric     return std::move(E);
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric   ClassOptions Opts = Deserialized.getOptions();
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric   bool ForwardRef = bool(Opts & ClassOptions::ForwardReference);
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric   uint32_t ThisRecordHash = getHashForUdt(Deserialized, Rec.data());
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric   // If we don't have a forward ref we can't compute the hash of it from the
650b57cec5SDimitry Andric   // full record because it requires hashing the entire buffer.
660b57cec5SDimitry Andric   if (!ForwardRef)
670b57cec5SDimitry Andric     return TagRecordHash{std::move(Deserialized), ThisRecordHash, 0};
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric   bool Scoped = bool(Opts & ClassOptions::Scoped);
700b57cec5SDimitry Andric 
710b57cec5SDimitry Andric   StringRef NameToHash =
720b57cec5SDimitry Andric       Scoped ? Deserialized.getUniqueName() : Deserialized.getName();
730b57cec5SDimitry Andric   uint32_t FullHash = hashStringV1(NameToHash);
740b57cec5SDimitry Andric   return TagRecordHash{std::move(Deserialized), FullHash, ThisRecordHash};
750b57cec5SDimitry Andric }
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric template <typename T>
getSourceLineHash(const CVType & Rec)780b57cec5SDimitry Andric static Expected<uint32_t> getSourceLineHash(const CVType &Rec) {
790b57cec5SDimitry Andric   T Deserialized;
800b57cec5SDimitry Andric   if (auto E = TypeDeserializer::deserializeAs(const_cast<CVType &>(Rec),
810b57cec5SDimitry Andric                                                Deserialized))
820b57cec5SDimitry Andric     return std::move(E);
830b57cec5SDimitry Andric   char Buf[4];
840b57cec5SDimitry Andric   support::endian::write32le(Buf, Deserialized.getUDT().getIndex());
850b57cec5SDimitry Andric   return hashStringV1(StringRef(Buf, 4));
860b57cec5SDimitry Andric }
870b57cec5SDimitry Andric 
hashTagRecord(const codeview::CVType & Type)880b57cec5SDimitry Andric Expected<TagRecordHash> llvm::pdb::hashTagRecord(const codeview::CVType &Type) {
890b57cec5SDimitry Andric   switch (Type.kind()) {
900b57cec5SDimitry Andric   case LF_CLASS:
910b57cec5SDimitry Andric   case LF_STRUCTURE:
920b57cec5SDimitry Andric   case LF_INTERFACE:
930b57cec5SDimitry Andric     return getTagRecordHashForUdt<ClassRecord>(Type);
940b57cec5SDimitry Andric   case LF_UNION:
950b57cec5SDimitry Andric     return getTagRecordHashForUdt<UnionRecord>(Type);
960b57cec5SDimitry Andric   case LF_ENUM:
970b57cec5SDimitry Andric     return getTagRecordHashForUdt<EnumRecord>(Type);
980b57cec5SDimitry Andric   default:
990b57cec5SDimitry Andric     assert(false && "Type is not a tag record!");
1000b57cec5SDimitry Andric   }
1010b57cec5SDimitry Andric   return make_error<StringError>("Invalid record type",
1020b57cec5SDimitry Andric                                  inconvertibleErrorCode());
1030b57cec5SDimitry Andric }
1040b57cec5SDimitry Andric 
hashTypeRecord(const CVType & Rec)1050b57cec5SDimitry Andric Expected<uint32_t> llvm::pdb::hashTypeRecord(const CVType &Rec) {
1060b57cec5SDimitry Andric   switch (Rec.kind()) {
1070b57cec5SDimitry Andric   case LF_CLASS:
1080b57cec5SDimitry Andric   case LF_STRUCTURE:
1090b57cec5SDimitry Andric   case LF_INTERFACE:
1100b57cec5SDimitry Andric     return getHashForUdt<ClassRecord>(Rec);
1110b57cec5SDimitry Andric   case LF_UNION:
1120b57cec5SDimitry Andric     return getHashForUdt<UnionRecord>(Rec);
1130b57cec5SDimitry Andric   case LF_ENUM:
1140b57cec5SDimitry Andric     return getHashForUdt<EnumRecord>(Rec);
1150b57cec5SDimitry Andric 
1160b57cec5SDimitry Andric   case LF_UDT_SRC_LINE:
1170b57cec5SDimitry Andric     return getSourceLineHash<UdtSourceLineRecord>(Rec);
1180b57cec5SDimitry Andric   case LF_UDT_MOD_SRC_LINE:
1190b57cec5SDimitry Andric     return getSourceLineHash<UdtModSourceLineRecord>(Rec);
1200b57cec5SDimitry Andric 
1210b57cec5SDimitry Andric   default:
1220b57cec5SDimitry Andric     break;
1230b57cec5SDimitry Andric   }
1240b57cec5SDimitry Andric 
1250b57cec5SDimitry Andric   // Run CRC32 over the bytes. This corresponds to `hashBufv8`.
1260b57cec5SDimitry Andric   JamCRC JC(/*Init=*/0U);
1278bcb0991SDimitry Andric   JC.update(Rec.data());
1280b57cec5SDimitry Andric   return JC.getCRC();
1290b57cec5SDimitry Andric }
130