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("::<unnamed-tag>") || Name.ends_with("::__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(Rec.getName()); 34 35 if (!ForwardRef && !Scoped && !IsAnon) 36 return hashStringV1(Rec.getName()); 37 if (!ForwardRef && HasUniqueName && !IsAnon) 38 return hashStringV1(Rec.getUniqueName()); 39 return hashBufferV8(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(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(Buf, Deserialized.getUDT().getIndex()); 85 return hashStringV1(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>(Type); 94 case LF_UNION: 95 return getTagRecordHashForUdt<UnionRecord>(Type); 96 case LF_ENUM: 97 return getTagRecordHashForUdt<EnumRecord>(Type); 98 default: 99 assert(false && "Type is not a tag record!"); 100 } 101 return make_error<StringError>("Invalid record type", 102 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(Rec.data()); 128 return JC.getCRC(); 129 } 130