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 15using namespace llvm; 16using namespace llvm::codeview; 17using namespace llvm::pdb; 18 19// Corresponds to `fUDTAnon`. 20static bool isAnonymous(StringRef Name) { 21 return Name == "<unnamed-tag>" || Name == "__unnamed" || 22 Name.endswith("::<unnamed-tag>") || Name.endswith("::__unnamed"); 23} 24 25// Computes the hash for a user-defined type record. This could be a struct, 26// class, union, or enum. 27static 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 42template <typename T> 43static 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 51template <typename T> 52static 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 77template <typename T> 78static 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 88Expected<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 105Expected<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