//===- TpiHashing.cpp -----------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/PDB/Native/Hash.h" #include "llvm/Support/JamCRC.h" using namespace llvm; using namespace llvm::codeview; using namespace llvm::pdb; // Corresponds to `fUDTAnon`. static bool isAnonymous(StringRef Name) { return Name == "" || Name == "__unnamed" || Name.endswith("::") || Name.endswith("::__unnamed"); } // Computes the hash for a user-defined type record. This could be a struct, // class, union, or enum. static uint32_t getHashForUdt(const TagRecord &Rec, ArrayRef FullRecord) { ClassOptions Opts = Rec.getOptions(); bool ForwardRef = bool(Opts & ClassOptions::ForwardReference); bool Scoped = bool(Opts & ClassOptions::Scoped); bool HasUniqueName = bool(Opts & ClassOptions::HasUniqueName); bool IsAnon = HasUniqueName && isAnonymous(Rec.getName()); if (!ForwardRef && !Scoped && !IsAnon) return hashStringV1(Rec.getName()); if (!ForwardRef && HasUniqueName && !IsAnon) return hashStringV1(Rec.getUniqueName()); return hashBufferV8(FullRecord); } template static Expected getHashForUdt(const CVType &Rec) { T Deserialized; if (auto E = TypeDeserializer::deserializeAs(const_cast(Rec), Deserialized)) return std::move(E); return getHashForUdt(Deserialized, Rec.data()); } template static Expected getTagRecordHashForUdt(const CVType &Rec) { T Deserialized; if (auto E = TypeDeserializer::deserializeAs(const_cast(Rec), Deserialized)) return std::move(E); ClassOptions Opts = Deserialized.getOptions(); bool ForwardRef = bool(Opts & ClassOptions::ForwardReference); uint32_t ThisRecordHash = getHashForUdt(Deserialized, Rec.data()); // If we don't have a forward ref we can't compute the hash of it from the // full record because it requires hashing the entire buffer. if (!ForwardRef) return TagRecordHash{std::move(Deserialized), ThisRecordHash, 0}; bool Scoped = bool(Opts & ClassOptions::Scoped); StringRef NameToHash = Scoped ? Deserialized.getUniqueName() : Deserialized.getName(); uint32_t FullHash = hashStringV1(NameToHash); return TagRecordHash{std::move(Deserialized), FullHash, ThisRecordHash}; } template static Expected getSourceLineHash(const CVType &Rec) { T Deserialized; if (auto E = TypeDeserializer::deserializeAs(const_cast(Rec), Deserialized)) return std::move(E); char Buf[4]; support::endian::write32le(Buf, Deserialized.getUDT().getIndex()); return hashStringV1(StringRef(Buf, 4)); } Expected llvm::pdb::hashTagRecord(const codeview::CVType &Type) { switch (Type.kind()) { case LF_CLASS: case LF_STRUCTURE: case LF_INTERFACE: return getTagRecordHashForUdt(Type); case LF_UNION: return getTagRecordHashForUdt(Type); case LF_ENUM: return getTagRecordHashForUdt(Type); default: assert(false && "Type is not a tag record!"); } return make_error("Invalid record type", inconvertibleErrorCode()); } Expected llvm::pdb::hashTypeRecord(const CVType &Rec) { switch (Rec.kind()) { case LF_CLASS: case LF_STRUCTURE: case LF_INTERFACE: return getHashForUdt(Rec); case LF_UNION: return getHashForUdt(Rec); case LF_ENUM: return getHashForUdt(Rec); case LF_UDT_SRC_LINE: return getSourceLineHash(Rec); case LF_UDT_MOD_SRC_LINE: return getSourceLineHash(Rec); default: break; } // Run CRC32 over the bytes. This corresponds to `hashBufv8`. JamCRC JC(/*Init=*/0U); ArrayRef Bytes(reinterpret_cast(Rec.data().data()), Rec.data().size()); JC.update(Bytes); return JC.getCRC(); }