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