1bf55c4e3SAlexandre Ganea //===- DebugTypes.cpp -----------------------------------------------------===//
2bf55c4e3SAlexandre Ganea //
3bf55c4e3SAlexandre Ganea // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4bf55c4e3SAlexandre Ganea // See https://llvm.org/LICENSE.txt for license information.
5bf55c4e3SAlexandre Ganea // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6bf55c4e3SAlexandre Ganea //
7bf55c4e3SAlexandre Ganea //===----------------------------------------------------------------------===//
8bf55c4e3SAlexandre Ganea
9bf55c4e3SAlexandre Ganea #include "DebugTypes.h"
106f7483b1SAmy Huang #include "COFFLinkerContext.h"
1154a335a2SReid Kleckner #include "Chunks.h"
129c78db60SAlexandre Ganea #include "Driver.h"
13bf55c4e3SAlexandre Ganea #include "InputFiles.h"
145519e4daSReid Kleckner #include "PDB.h"
1554a335a2SReid Kleckner #include "TypeMerger.h"
169c78db60SAlexandre Ganea #include "lld/Common/ErrorHandler.h"
1747feae5dSRui Ueyama #include "lld/Common/Memory.h"
185519e4daSReid Kleckner #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
19bf55c4e3SAlexandre Ganea #include "llvm/DebugInfo/CodeView/TypeRecord.h"
2054a335a2SReid Kleckner #include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
2154a335a2SReid Kleckner #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
229c78db60SAlexandre Ganea #include "llvm/DebugInfo/PDB/GenericError.h"
239c78db60SAlexandre Ganea #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
249c78db60SAlexandre Ganea #include "llvm/DebugInfo/PDB/Native/NativeSession.h"
259c78db60SAlexandre Ganea #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
265519e4daSReid Kleckner #include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
2754a335a2SReid Kleckner #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
285519e4daSReid Kleckner #include "llvm/Support/FormatVariadic.h"
295519e4daSReid Kleckner #include "llvm/Support/Parallel.h"
309c78db60SAlexandre Ganea #include "llvm/Support/Path.h"
31bf55c4e3SAlexandre Ganea
32bf55c4e3SAlexandre Ganea using namespace llvm;
33bf55c4e3SAlexandre Ganea using namespace llvm::codeview;
348a310f40SReid Kleckner using namespace lld;
358a310f40SReid Kleckner using namespace lld::coff;
36d79c3be6SFangrui Song
37bf55c4e3SAlexandre Ganea namespace {
381e5b7e91SReid Kleckner class TypeServerIpiSource;
391e5b7e91SReid Kleckner
409c78db60SAlexandre Ganea // The TypeServerSource class represents a PDB type server, a file referenced by
419c78db60SAlexandre Ganea // OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ
429c78db60SAlexandre Ganea // files, therefore there must be only once instance per OBJ lot. The file path
439c78db60SAlexandre Ganea // is discovered from the dependent OBJ's debug type stream. The
449c78db60SAlexandre Ganea // TypeServerSource object is then queued and loaded by the COFF Driver. The
459c78db60SAlexandre Ganea // debug type stream for such PDB files will be merged first in the final PDB,
469c78db60SAlexandre Ganea // before any dependent OBJ.
47bf55c4e3SAlexandre Ganea class TypeServerSource : public TpiSource {
48bf55c4e3SAlexandre Ganea public:
TypeServerSource(COFFLinkerContext & ctx,PDBInputFile * f)496f7483b1SAmy Huang explicit TypeServerSource(COFFLinkerContext &ctx, PDBInputFile *f)
506f7483b1SAmy Huang : TpiSource(ctx, PDB, nullptr), pdbInputFile(f) {
5154a335a2SReid Kleckner if (f->loadErr && *f->loadErr)
5254a335a2SReid Kleckner return;
5354a335a2SReid Kleckner pdb::PDBFile &file = f->session->getPDBFile();
5454a335a2SReid Kleckner auto expectedInfo = file.getPDBInfoStream();
5554a335a2SReid Kleckner if (!expectedInfo)
5654a335a2SReid Kleckner return;
57e73203a5SReid Kleckner Guid = expectedInfo->getGuid();
586f7483b1SAmy Huang auto it = ctx.typeServerSourceMappings.emplace(Guid, this);
5998bc304eSTobias Hieta if (!it.second) {
6098bc304eSTobias Hieta // If we hit here we have collision on Guid's in two PDB files.
6198bc304eSTobias Hieta // This can happen if the PDB Guid is invalid or if we are really
6298bc304eSTobias Hieta // unlucky. This should fall back on stright file-system lookup.
630dfa8a01STobias Hieta it.first->second = nullptr;
6498bc304eSTobias Hieta }
6554a335a2SReid Kleckner }
669c78db60SAlexandre Ganea
671e5b7e91SReid Kleckner Error mergeDebugT(TypeMerger *m) override;
685519e4daSReid Kleckner
695519e4daSReid Kleckner void loadGHashes() override;
705519e4daSReid Kleckner void remapTpiWithGHashes(GHashState *g) override;
715519e4daSReid Kleckner
isDependency() const7254a335a2SReid Kleckner bool isDependency() const override { return true; }
739c78db60SAlexandre Ganea
7454a335a2SReid Kleckner PDBInputFile *pdbInputFile = nullptr;
759c78db60SAlexandre Ganea
761e5b7e91SReid Kleckner // TpiSource for IPI stream.
771e5b7e91SReid Kleckner TypeServerIpiSource *ipiSrc = nullptr;
789c78db60SAlexandre Ganea
79e73203a5SReid Kleckner // The PDB signature GUID.
80e73203a5SReid Kleckner codeview::GUID Guid;
81bf55c4e3SAlexandre Ganea };
82bf55c4e3SAlexandre Ganea
831e5b7e91SReid Kleckner // Companion to TypeServerSource. Stores the index map for the IPI stream in the
841e5b7e91SReid Kleckner // PDB. Modeling PDBs with two sources for TPI and IPI helps establish the
851e5b7e91SReid Kleckner // invariant of one type index space per source.
861e5b7e91SReid Kleckner class TypeServerIpiSource : public TpiSource {
871e5b7e91SReid Kleckner public:
TypeServerIpiSource(COFFLinkerContext & ctx)886f7483b1SAmy Huang explicit TypeServerIpiSource(COFFLinkerContext &ctx)
896f7483b1SAmy Huang : TpiSource(ctx, PDBIpi, nullptr) {}
901e5b7e91SReid Kleckner
911e5b7e91SReid Kleckner friend class TypeServerSource;
921e5b7e91SReid Kleckner
935519e4daSReid Kleckner // All of the TpiSource methods are no-ops. The parent TypeServerSource
945519e4daSReid Kleckner // handles both TPI and IPI.
mergeDebugT(TypeMerger * m)951e5b7e91SReid Kleckner Error mergeDebugT(TypeMerger *m) override { return Error::success(); }
loadGHashes()965519e4daSReid Kleckner void loadGHashes() override {}
remapTpiWithGHashes(GHashState * g)975519e4daSReid Kleckner void remapTpiWithGHashes(GHashState *g) override {}
isDependency() const981e5b7e91SReid Kleckner bool isDependency() const override { return true; }
991e5b7e91SReid Kleckner };
1001e5b7e91SReid Kleckner
1019c78db60SAlexandre Ganea // This class represents the debug type stream of an OBJ file that depends on a
1029c78db60SAlexandre Ganea // PDB type server (see TypeServerSource).
103bf55c4e3SAlexandre Ganea class UseTypeServerSource : public TpiSource {
1045519e4daSReid Kleckner Expected<TypeServerSource *> getTypeServerSource();
1055519e4daSReid Kleckner
106bf55c4e3SAlexandre Ganea public:
UseTypeServerSource(COFFLinkerContext & ctx,ObjFile * f,TypeServer2Record ts)1076f7483b1SAmy Huang UseTypeServerSource(COFFLinkerContext &ctx, ObjFile *f, TypeServer2Record ts)
1086f7483b1SAmy Huang : TpiSource(ctx, UsingPDB, f), typeServerDependency(ts) {}
10954a335a2SReid Kleckner
1101e5b7e91SReid Kleckner Error mergeDebugT(TypeMerger *m) override;
111bf55c4e3SAlexandre Ganea
1125519e4daSReid Kleckner // No need to load ghashes from /Zi objects.
loadGHashes()1135519e4daSReid Kleckner void loadGHashes() override {}
1145519e4daSReid Kleckner void remapTpiWithGHashes(GHashState *g) override;
1155519e4daSReid Kleckner
116bf55c4e3SAlexandre Ganea // Information about the PDB type server dependency, that needs to be loaded
117bf55c4e3SAlexandre Ganea // in before merging this OBJ.
118136d27abSRui Ueyama TypeServer2Record typeServerDependency;
119bf55c4e3SAlexandre Ganea };
120bf55c4e3SAlexandre Ganea
1219c78db60SAlexandre Ganea // This class represents the debug type stream of a Microsoft precompiled
1229c78db60SAlexandre Ganea // headers OBJ (PCH OBJ). This OBJ kind needs to be merged first in the output
1239c78db60SAlexandre Ganea // PDB, before any other OBJs that depend on this. Note that only MSVC generate
1249c78db60SAlexandre Ganea // such files, clang does not.
125bf55c4e3SAlexandre Ganea class PrecompSource : public TpiSource {
126bf55c4e3SAlexandre Ganea public:
PrecompSource(COFFLinkerContext & ctx,ObjFile * f)1276f7483b1SAmy Huang PrecompSource(COFFLinkerContext &ctx, ObjFile *f) : TpiSource(ctx, PCH, f) {
12854a335a2SReid Kleckner if (!f->pchSignature || !*f->pchSignature)
12954a335a2SReid Kleckner fatal(toString(f) +
13054a335a2SReid Kleckner " claims to be a PCH object, but does not have a valid signature");
1316f7483b1SAmy Huang auto it = ctx.precompSourceMappings.emplace(*f->pchSignature, this);
13254a335a2SReid Kleckner if (!it.second)
13354a335a2SReid Kleckner fatal("a PCH object with the same signature has already been provided (" +
13454a335a2SReid Kleckner toString(it.first->second->file) + " and " + toString(file) + ")");
13554a335a2SReid Kleckner }
13654a335a2SReid Kleckner
1375519e4daSReid Kleckner void loadGHashes() override;
1385519e4daSReid Kleckner
isDependency() const13954a335a2SReid Kleckner bool isDependency() const override { return true; }
140bf55c4e3SAlexandre Ganea };
141bf55c4e3SAlexandre Ganea
1429c78db60SAlexandre Ganea // This class represents the debug type stream of an OBJ file that depends on a
1439c78db60SAlexandre Ganea // Microsoft precompiled headers OBJ (see PrecompSource).
144bf55c4e3SAlexandre Ganea class UsePrecompSource : public TpiSource {
145bf55c4e3SAlexandre Ganea public:
UsePrecompSource(COFFLinkerContext & ctx,ObjFile * f,PrecompRecord precomp)1466f7483b1SAmy Huang UsePrecompSource(COFFLinkerContext &ctx, ObjFile *f, PrecompRecord precomp)
1476f7483b1SAmy Huang : TpiSource(ctx, UsingPCH, f), precompDependency(precomp) {}
14854a335a2SReid Kleckner
1491e5b7e91SReid Kleckner Error mergeDebugT(TypeMerger *m) override;
150bf55c4e3SAlexandre Ganea
1515519e4daSReid Kleckner void loadGHashes() override;
1525519e4daSReid Kleckner void remapTpiWithGHashes(GHashState *g) override;
1535519e4daSReid Kleckner
1545519e4daSReid Kleckner private:
1555519e4daSReid Kleckner Error mergeInPrecompHeaderObj();
1565519e4daSReid Kleckner
1576f7483b1SAmy Huang PrecompSource *findObjByName(StringRef fileNameOnly);
1586f7483b1SAmy Huang PrecompSource *findPrecompSource(ObjFile *file, PrecompRecord &pr);
1596f7483b1SAmy Huang Expected<PrecompSource *> findPrecompMap(ObjFile *file, PrecompRecord &pr);
1606f7483b1SAmy Huang
1615519e4daSReid Kleckner public:
162bf55c4e3SAlexandre Ganea // Information about the Precomp OBJ dependency, that needs to be loaded in
163bf55c4e3SAlexandre Ganea // before merging this OBJ.
164136d27abSRui Ueyama PrecompRecord precompDependency;
165bf55c4e3SAlexandre Ganea };
166bf55c4e3SAlexandre Ganea } // namespace
167bf55c4e3SAlexandre Ganea
TpiSource(COFFLinkerContext & ctx,TpiKind k,ObjFile * f)1686f7483b1SAmy Huang TpiSource::TpiSource(COFFLinkerContext &ctx, TpiKind k, ObjFile *f)
1696f7483b1SAmy Huang : ctx(ctx), kind(k), tpiSrcIdx(ctx.tpiSourceList.size()), file(f) {
1706f7483b1SAmy Huang ctx.addTpiSource(this);
171bf55c4e3SAlexandre Ganea }
172bf55c4e3SAlexandre Ganea
17354a335a2SReid Kleckner // Vtable key method.
~TpiSource()1745519e4daSReid Kleckner TpiSource::~TpiSource() {
1755519e4daSReid Kleckner // Silence any assertions about unchecked errors.
1765519e4daSReid Kleckner consumeError(std::move(typeMergingError));
1775519e4daSReid Kleckner }
1785519e4daSReid Kleckner
makeTpiSource(COFFLinkerContext & ctx,ObjFile * file)1796f7483b1SAmy Huang TpiSource *lld::coff::makeTpiSource(COFFLinkerContext &ctx, ObjFile *file) {
1806f7483b1SAmy Huang return make<TpiSource>(ctx, TpiSource::Regular, file);
1815519e4daSReid Kleckner }
18254a335a2SReid Kleckner
makeTypeServerSource(COFFLinkerContext & ctx,PDBInputFile * pdbInputFile)1836f7483b1SAmy Huang TpiSource *lld::coff::makeTypeServerSource(COFFLinkerContext &ctx,
1846f7483b1SAmy Huang PDBInputFile *pdbInputFile) {
1851e5b7e91SReid Kleckner // Type server sources come in pairs: the TPI stream, and the IPI stream.
1866f7483b1SAmy Huang auto *tpiSource = make<TypeServerSource>(ctx, pdbInputFile);
1871e5b7e91SReid Kleckner if (pdbInputFile->session->getPDBFile().hasPDBIpiStream())
1886f7483b1SAmy Huang tpiSource->ipiSrc = make<TypeServerIpiSource>(ctx);
1891e5b7e91SReid Kleckner return tpiSource;
1909c78db60SAlexandre Ganea }
191bf55c4e3SAlexandre Ganea
makeUseTypeServerSource(COFFLinkerContext & ctx,ObjFile * file,TypeServer2Record ts)1926f7483b1SAmy Huang TpiSource *lld::coff::makeUseTypeServerSource(COFFLinkerContext &ctx,
1936f7483b1SAmy Huang ObjFile *file,
19454a335a2SReid Kleckner TypeServer2Record ts) {
1956f7483b1SAmy Huang return make<UseTypeServerSource>(ctx, file, ts);
196bf55c4e3SAlexandre Ganea }
197bf55c4e3SAlexandre Ganea
makePrecompSource(COFFLinkerContext & ctx,ObjFile * file)1986f7483b1SAmy Huang TpiSource *lld::coff::makePrecompSource(COFFLinkerContext &ctx, ObjFile *file) {
1996f7483b1SAmy Huang return make<PrecompSource>(ctx, file);
200bf55c4e3SAlexandre Ganea }
201bf55c4e3SAlexandre Ganea
makeUsePrecompSource(COFFLinkerContext & ctx,ObjFile * file,PrecompRecord precomp)2026f7483b1SAmy Huang TpiSource *lld::coff::makeUsePrecompSource(COFFLinkerContext &ctx,
2036f7483b1SAmy Huang ObjFile *file,
20454a335a2SReid Kleckner PrecompRecord precomp) {
2056f7483b1SAmy Huang return make<UsePrecompSource>(ctx, file, precomp);
2069c78db60SAlexandre Ganea }
2079c78db60SAlexandre Ganea
remapTypeIndex(TypeIndex & ti,TiRefKind refKind) const2085519e4daSReid Kleckner bool TpiSource::remapTypeIndex(TypeIndex &ti, TiRefKind refKind) const {
2095519e4daSReid Kleckner if (ti.isSimple())
2105519e4daSReid Kleckner return true;
2115519e4daSReid Kleckner
2125519e4daSReid Kleckner // This can be an item index or a type index. Choose the appropriate map.
2135519e4daSReid Kleckner ArrayRef<TypeIndex> tpiOrIpiMap =
2145519e4daSReid Kleckner (refKind == TiRefKind::IndexRef) ? ipiMap : tpiMap;
2155519e4daSReid Kleckner if (ti.toArrayIndex() >= tpiOrIpiMap.size())
2165519e4daSReid Kleckner return false;
2175519e4daSReid Kleckner ti = tpiOrIpiMap[ti.toArrayIndex()];
2185519e4daSReid Kleckner return true;
2195519e4daSReid Kleckner }
2205519e4daSReid Kleckner
remapRecord(MutableArrayRef<uint8_t> rec,ArrayRef<TiReference> typeRefs)2215519e4daSReid Kleckner void TpiSource::remapRecord(MutableArrayRef<uint8_t> rec,
2225519e4daSReid Kleckner ArrayRef<TiReference> typeRefs) {
2235519e4daSReid Kleckner MutableArrayRef<uint8_t> contents = rec.drop_front(sizeof(RecordPrefix));
2245519e4daSReid Kleckner for (const TiReference &ref : typeRefs) {
2255519e4daSReid Kleckner unsigned byteSize = ref.Count * sizeof(TypeIndex);
2265519e4daSReid Kleckner if (contents.size() < ref.Offset + byteSize)
2275519e4daSReid Kleckner fatal("symbol record too short");
2285519e4daSReid Kleckner
2295519e4daSReid Kleckner MutableArrayRef<TypeIndex> indices(
2305519e4daSReid Kleckner reinterpret_cast<TypeIndex *>(contents.data() + ref.Offset), ref.Count);
2315519e4daSReid Kleckner for (TypeIndex &ti : indices) {
2325519e4daSReid Kleckner if (!remapTypeIndex(ti, ref.Kind)) {
2335519e4daSReid Kleckner if (config->verbose) {
2345519e4daSReid Kleckner uint16_t kind =
2355519e4daSReid Kleckner reinterpret_cast<const RecordPrefix *>(rec.data())->RecordKind;
2365519e4daSReid Kleckner StringRef fname = file ? file->getName() : "<unknown PDB>";
2375519e4daSReid Kleckner log("failed to remap type index in record of kind 0x" +
2385519e4daSReid Kleckner utohexstr(kind) + " in " + fname + " with bad " +
2395519e4daSReid Kleckner (ref.Kind == TiRefKind::IndexRef ? "item" : "type") +
2405519e4daSReid Kleckner " index 0x" + utohexstr(ti.getIndex()));
2415519e4daSReid Kleckner }
2425519e4daSReid Kleckner ti = TypeIndex(SimpleTypeKind::NotTranslated);
2435519e4daSReid Kleckner continue;
2445519e4daSReid Kleckner }
2455519e4daSReid Kleckner }
2465519e4daSReid Kleckner }
2475519e4daSReid Kleckner }
2485519e4daSReid Kleckner
remapTypesInTypeRecord(MutableArrayRef<uint8_t> rec)2495519e4daSReid Kleckner void TpiSource::remapTypesInTypeRecord(MutableArrayRef<uint8_t> rec) {
2505519e4daSReid Kleckner // TODO: Handle errors similar to symbols.
2515519e4daSReid Kleckner SmallVector<TiReference, 32> typeRefs;
2525519e4daSReid Kleckner discoverTypeIndices(CVType(rec), typeRefs);
2535519e4daSReid Kleckner remapRecord(rec, typeRefs);
2545519e4daSReid Kleckner }
2555519e4daSReid Kleckner
remapTypesInSymbolRecord(MutableArrayRef<uint8_t> rec)2565519e4daSReid Kleckner bool TpiSource::remapTypesInSymbolRecord(MutableArrayRef<uint8_t> rec) {
2575519e4daSReid Kleckner // Discover type index references in the record. Skip it if we don't
2585519e4daSReid Kleckner // know where they are.
2595519e4daSReid Kleckner SmallVector<TiReference, 32> typeRefs;
2605519e4daSReid Kleckner if (!discoverTypeIndicesInSymbol(rec, typeRefs))
2615519e4daSReid Kleckner return false;
2625519e4daSReid Kleckner remapRecord(rec, typeRefs);
2635519e4daSReid Kleckner return true;
2645519e4daSReid Kleckner }
2655519e4daSReid Kleckner
26654a335a2SReid Kleckner // A COFF .debug$H section is currently a clang extension. This function checks
26754a335a2SReid Kleckner // if a .debug$H section is in a format that we expect / understand, so that we
26854a335a2SReid Kleckner // can ignore any sections which are coincidentally also named .debug$H but do
26954a335a2SReid Kleckner // not contain a format we recognize.
canUseDebugH(ArrayRef<uint8_t> debugH)27054a335a2SReid Kleckner static bool canUseDebugH(ArrayRef<uint8_t> debugH) {
27154a335a2SReid Kleckner if (debugH.size() < sizeof(object::debug_h_header))
27254a335a2SReid Kleckner return false;
27354a335a2SReid Kleckner auto *header =
27454a335a2SReid Kleckner reinterpret_cast<const object::debug_h_header *>(debugH.data());
27554a335a2SReid Kleckner debugH = debugH.drop_front(sizeof(object::debug_h_header));
27654a335a2SReid Kleckner return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
27754a335a2SReid Kleckner header->Version == 0 &&
27854a335a2SReid Kleckner header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) &&
27954a335a2SReid Kleckner (debugH.size() % 8 == 0);
28054a335a2SReid Kleckner }
28154a335a2SReid Kleckner
getDebugH(ObjFile * file)28254a335a2SReid Kleckner static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *file) {
28354a335a2SReid Kleckner SectionChunk *sec =
28454a335a2SReid Kleckner SectionChunk::findByName(file->getDebugChunks(), ".debug$H");
28554a335a2SReid Kleckner if (!sec)
28654a335a2SReid Kleckner return llvm::None;
28754a335a2SReid Kleckner ArrayRef<uint8_t> contents = sec->getContents();
28854a335a2SReid Kleckner if (!canUseDebugH(contents))
28954a335a2SReid Kleckner return None;
29054a335a2SReid Kleckner return contents;
29154a335a2SReid Kleckner }
29254a335a2SReid Kleckner
29354a335a2SReid Kleckner static ArrayRef<GloballyHashedType>
getHashesFromDebugH(ArrayRef<uint8_t> debugH)29454a335a2SReid Kleckner getHashesFromDebugH(ArrayRef<uint8_t> debugH) {
29554a335a2SReid Kleckner assert(canUseDebugH(debugH));
29654a335a2SReid Kleckner debugH = debugH.drop_front(sizeof(object::debug_h_header));
29754a335a2SReid Kleckner uint32_t count = debugH.size() / sizeof(GloballyHashedType);
29854a335a2SReid Kleckner return {reinterpret_cast<const GloballyHashedType *>(debugH.data()), count};
29954a335a2SReid Kleckner }
30054a335a2SReid Kleckner
30154a335a2SReid Kleckner // Merge .debug$T for a generic object file.
mergeDebugT(TypeMerger * m)3021e5b7e91SReid Kleckner Error TpiSource::mergeDebugT(TypeMerger *m) {
3035519e4daSReid Kleckner assert(!config->debugGHashes &&
3045519e4daSReid Kleckner "use remapTpiWithGHashes when ghash is enabled");
3055519e4daSReid Kleckner
30654a335a2SReid Kleckner CVTypeArray types;
30754a335a2SReid Kleckner BinaryStreamReader reader(file->debugTypes, support::little);
30854a335a2SReid Kleckner cantFail(reader.readArray(types, reader.getLength()));
30954a335a2SReid Kleckner
3104140f074SAlexandre Ganea // When dealing with PCH.OBJ, some indices were already merged.
3114140f074SAlexandre Ganea unsigned nbHeadIndices = indexMapStorage.size();
3124140f074SAlexandre Ganea
3135519e4daSReid Kleckner if (auto err = mergeTypeAndIdRecords(
3145519e4daSReid Kleckner m->idTable, m->typeTable, indexMapStorage, types, file->pchSignature))
31554a335a2SReid Kleckner fatal("codeview::mergeTypeAndIdRecords failed: " +
31654a335a2SReid Kleckner toString(std::move(err)));
31754a335a2SReid Kleckner
3181e5b7e91SReid Kleckner // In an object, there is only one mapping for both types and items.
3191e5b7e91SReid Kleckner tpiMap = indexMapStorage;
3201e5b7e91SReid Kleckner ipiMap = indexMapStorage;
3211e5b7e91SReid Kleckner
32254a335a2SReid Kleckner if (config->showSummary) {
32355b97a6dSAlexandre Ganea nbTypeRecords = indexMapStorage.size() - nbHeadIndices;
32455b97a6dSAlexandre Ganea nbTypeRecordsBytes = reader.getLength();
32554a335a2SReid Kleckner // Count how many times we saw each type record in our input. This
32654a335a2SReid Kleckner // calculation requires a second pass over the type records to classify each
32754a335a2SReid Kleckner // record as a type or index. This is slow, but this code executes when
32854a335a2SReid Kleckner // collecting statistics.
32954a335a2SReid Kleckner m->tpiCounts.resize(m->getTypeTable().size());
33054a335a2SReid Kleckner m->ipiCounts.resize(m->getIDTable().size());
3314140f074SAlexandre Ganea uint32_t srcIdx = nbHeadIndices;
3329a2b54afSDuncan P. N. Exon Smith for (const CVType &ty : types) {
3331e5b7e91SReid Kleckner TypeIndex dstIdx = tpiMap[srcIdx++];
33454a335a2SReid Kleckner // Type merging may fail, so a complex source type may become the simple
33554a335a2SReid Kleckner // NotTranslated type, which cannot be used as an array index.
33654a335a2SReid Kleckner if (dstIdx.isSimple())
33754a335a2SReid Kleckner continue;
33854a335a2SReid Kleckner SmallVectorImpl<uint32_t> &counts =
33954a335a2SReid Kleckner isIdRecord(ty.kind()) ? m->ipiCounts : m->tpiCounts;
34054a335a2SReid Kleckner ++counts[dstIdx.toArrayIndex()];
34154a335a2SReid Kleckner }
34254a335a2SReid Kleckner }
34354a335a2SReid Kleckner
3441e5b7e91SReid Kleckner return Error::success();
34554a335a2SReid Kleckner }
34654a335a2SReid Kleckner
34754a335a2SReid Kleckner // Merge types from a type server PDB.
mergeDebugT(TypeMerger * m)3481e5b7e91SReid Kleckner Error TypeServerSource::mergeDebugT(TypeMerger *m) {
3495519e4daSReid Kleckner assert(!config->debugGHashes &&
3505519e4daSReid Kleckner "use remapTpiWithGHashes when ghash is enabled");
3515519e4daSReid Kleckner
35254a335a2SReid Kleckner pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile();
35354a335a2SReid Kleckner Expected<pdb::TpiStream &> expectedTpi = pdbFile.getPDBTpiStream();
35454a335a2SReid Kleckner if (auto e = expectedTpi.takeError())
35554a335a2SReid Kleckner fatal("Type server does not have TPI stream: " + toString(std::move(e)));
35654a335a2SReid Kleckner pdb::TpiStream *maybeIpi = nullptr;
35754a335a2SReid Kleckner if (pdbFile.hasPDBIpiStream()) {
35854a335a2SReid Kleckner Expected<pdb::TpiStream &> expectedIpi = pdbFile.getPDBIpiStream();
35954a335a2SReid Kleckner if (auto e = expectedIpi.takeError())
36054a335a2SReid Kleckner fatal("Error getting type server IPI stream: " + toString(std::move(e)));
36154a335a2SReid Kleckner maybeIpi = &*expectedIpi;
36254a335a2SReid Kleckner }
36354a335a2SReid Kleckner
36454a335a2SReid Kleckner // Merge TPI first, because the IPI stream will reference type indices.
3651e5b7e91SReid Kleckner if (auto err = mergeTypeRecords(m->typeTable, indexMapStorage,
36654a335a2SReid Kleckner expectedTpi->typeArray()))
36754a335a2SReid Kleckner fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
3681e5b7e91SReid Kleckner tpiMap = indexMapStorage;
36954a335a2SReid Kleckner
37054a335a2SReid Kleckner // Merge IPI.
37154a335a2SReid Kleckner if (maybeIpi) {
3721e5b7e91SReid Kleckner if (auto err = mergeIdRecords(m->idTable, tpiMap, ipiSrc->indexMapStorage,
3731e5b7e91SReid Kleckner maybeIpi->typeArray()))
37454a335a2SReid Kleckner fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
3751e5b7e91SReid Kleckner ipiMap = ipiSrc->indexMapStorage;
37654a335a2SReid Kleckner }
37754a335a2SReid Kleckner
37854a335a2SReid Kleckner if (config->showSummary) {
37955b97a6dSAlexandre Ganea nbTypeRecords = tpiMap.size() + ipiMap.size();
38055b97a6dSAlexandre Ganea nbTypeRecordsBytes =
38155b97a6dSAlexandre Ganea expectedTpi->typeArray().getUnderlyingStream().getLength() +
38255b97a6dSAlexandre Ganea (maybeIpi ? maybeIpi->typeArray().getUnderlyingStream().getLength()
38355b97a6dSAlexandre Ganea : 0);
38455b97a6dSAlexandre Ganea
38554a335a2SReid Kleckner // Count how many times we saw each type record in our input. If a
38654a335a2SReid Kleckner // destination type index is present in the source to destination type index
38754a335a2SReid Kleckner // map, that means we saw it once in the input. Add it to our histogram.
38854a335a2SReid Kleckner m->tpiCounts.resize(m->getTypeTable().size());
38954a335a2SReid Kleckner m->ipiCounts.resize(m->getIDTable().size());
3901e5b7e91SReid Kleckner for (TypeIndex ti : tpiMap)
39154a335a2SReid Kleckner if (!ti.isSimple())
39254a335a2SReid Kleckner ++m->tpiCounts[ti.toArrayIndex()];
3931e5b7e91SReid Kleckner for (TypeIndex ti : ipiMap)
39454a335a2SReid Kleckner if (!ti.isSimple())
39554a335a2SReid Kleckner ++m->ipiCounts[ti.toArrayIndex()];
39654a335a2SReid Kleckner }
39754a335a2SReid Kleckner
3981e5b7e91SReid Kleckner return Error::success();
39954a335a2SReid Kleckner }
40054a335a2SReid Kleckner
getTypeServerSource()4015519e4daSReid Kleckner Expected<TypeServerSource *> UseTypeServerSource::getTypeServerSource() {
40254a335a2SReid Kleckner const codeview::GUID &tsId = typeServerDependency.getGuid();
40354a335a2SReid Kleckner StringRef tsPath = typeServerDependency.getName();
40454a335a2SReid Kleckner
4050dfa8a01STobias Hieta TypeServerSource *tsSrc = nullptr;
4066f7483b1SAmy Huang auto it = ctx.typeServerSourceMappings.find(tsId);
4076f7483b1SAmy Huang if (it != ctx.typeServerSourceMappings.end()) {
4086f7483b1SAmy Huang tsSrc = (TypeServerSource *)it->second;
4090dfa8a01STobias Hieta }
4100dfa8a01STobias Hieta if (tsSrc == nullptr) {
41154a335a2SReid Kleckner // The file failed to load, lookup by name
4126f7483b1SAmy Huang PDBInputFile *pdb = PDBInputFile::findFromRecordPath(ctx, tsPath, file);
41354a335a2SReid Kleckner if (!pdb)
41454a335a2SReid Kleckner return createFileError(tsPath, errorCodeToError(std::error_code(
41554a335a2SReid Kleckner ENOENT, std::generic_category())));
41654a335a2SReid Kleckner // If an error occurred during loading, throw it now
41754a335a2SReid Kleckner if (pdb->loadErr && *pdb->loadErr)
41854a335a2SReid Kleckner return createFileError(tsPath, std::move(*pdb->loadErr));
41954a335a2SReid Kleckner
42054a335a2SReid Kleckner tsSrc = (TypeServerSource *)pdb->debugTypesObj;
421e73203a5SReid Kleckner
422e73203a5SReid Kleckner // Just because a file with a matching name was found and it was an actual
423e73203a5SReid Kleckner // PDB file doesn't mean it matches. For it to match the InfoStream's GUID
424e73203a5SReid Kleckner // must match the GUID specified in the TypeServer2 record.
425e73203a5SReid Kleckner if (tsSrc->Guid != tsId) {
426e73203a5SReid Kleckner return createFileError(tsPath,
427e73203a5SReid Kleckner make_error<pdb::PDBError>(
428e73203a5SReid Kleckner pdb::pdb_error_code::signature_out_of_date));
429e73203a5SReid Kleckner }
43054a335a2SReid Kleckner }
4315519e4daSReid Kleckner return tsSrc;
4325519e4daSReid Kleckner }
43354a335a2SReid Kleckner
mergeDebugT(TypeMerger * m)4345519e4daSReid Kleckner Error UseTypeServerSource::mergeDebugT(TypeMerger *m) {
4355519e4daSReid Kleckner Expected<TypeServerSource *> tsSrc = getTypeServerSource();
4365519e4daSReid Kleckner if (!tsSrc)
4375519e4daSReid Kleckner return tsSrc.takeError();
4385519e4daSReid Kleckner
4395519e4daSReid Kleckner pdb::PDBFile &pdbSession = (*tsSrc)->pdbInputFile->session->getPDBFile();
44054a335a2SReid Kleckner auto expectedInfo = pdbSession.getPDBInfoStream();
44154a335a2SReid Kleckner if (!expectedInfo)
4421e5b7e91SReid Kleckner return expectedInfo.takeError();
44354a335a2SReid Kleckner
4441e5b7e91SReid Kleckner // Reuse the type index map of the type server.
4455519e4daSReid Kleckner tpiMap = (*tsSrc)->tpiMap;
4465519e4daSReid Kleckner ipiMap = (*tsSrc)->ipiMap;
4471e5b7e91SReid Kleckner return Error::success();
44854a335a2SReid Kleckner }
44954a335a2SReid Kleckner
equalsPath(StringRef path1,StringRef path2)45054a335a2SReid Kleckner static bool equalsPath(StringRef path1, StringRef path2) {
4519c78db60SAlexandre Ganea #if defined(_WIN32)
4523c6f8ca7SMartin Storsjö return path1.equals_insensitive(path2);
45354a335a2SReid Kleckner #else
45454a335a2SReid Kleckner return path1.equals(path2);
4559c78db60SAlexandre Ganea #endif
4569c78db60SAlexandre Ganea }
4579c78db60SAlexandre Ganea
45854a335a2SReid Kleckner // Find by name an OBJ provided on the command line
findObjByName(StringRef fileNameOnly)4596f7483b1SAmy Huang PrecompSource *UsePrecompSource::findObjByName(StringRef fileNameOnly) {
46054a335a2SReid Kleckner SmallString<128> currentPath;
4616f7483b1SAmy Huang for (auto kv : ctx.precompSourceMappings) {
46254a335a2SReid Kleckner StringRef currentFileName = sys::path::filename(kv.second->file->getName(),
46354a335a2SReid Kleckner sys::path::Style::windows);
46454a335a2SReid Kleckner
46554a335a2SReid Kleckner // Compare based solely on the file name (link.exe behavior)
46654a335a2SReid Kleckner if (equalsPath(currentFileName, fileNameOnly))
4676f7483b1SAmy Huang return (PrecompSource *)kv.second;
46854a335a2SReid Kleckner }
46954a335a2SReid Kleckner return nullptr;
4709c78db60SAlexandre Ganea }
4719c78db60SAlexandre Ganea
findPrecompSource(ObjFile * file,PrecompRecord & pr)4726f7483b1SAmy Huang PrecompSource *UsePrecompSource::findPrecompSource(ObjFile *file,
4736f7483b1SAmy Huang PrecompRecord &pr) {
47454a335a2SReid Kleckner // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP
47554a335a2SReid Kleckner // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly,
47654a335a2SReid Kleckner // the paths embedded in the OBJs are in the Windows format.
47754a335a2SReid Kleckner SmallString<128> prFileName =
47854a335a2SReid Kleckner sys::path::filename(pr.getPrecompFilePath(), sys::path::Style::windows);
4799c78db60SAlexandre Ganea
4806f7483b1SAmy Huang auto it = ctx.precompSourceMappings.find(pr.getSignature());
4816f7483b1SAmy Huang if (it != ctx.precompSourceMappings.end()) {
4826f7483b1SAmy Huang return (PrecompSource *)it->second;
48349b34599SReid Kleckner }
4845519e4daSReid Kleckner // Lookup by name
4855519e4daSReid Kleckner return findObjByName(prFileName);
4865519e4daSReid Kleckner }
4875519e4daSReid Kleckner
findPrecompMap(ObjFile * file,PrecompRecord & pr)4886f7483b1SAmy Huang Expected<PrecompSource *> UsePrecompSource::findPrecompMap(ObjFile *file,
4895519e4daSReid Kleckner PrecompRecord &pr) {
4905519e4daSReid Kleckner PrecompSource *precomp = findPrecompSource(file, pr);
49149b34599SReid Kleckner
49254a335a2SReid Kleckner if (!precomp)
4939c78db60SAlexandre Ganea return createFileError(
4945519e4daSReid Kleckner pr.getPrecompFilePath(),
49554a335a2SReid Kleckner make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
4969c78db60SAlexandre Ganea
49754a335a2SReid Kleckner if (pr.getSignature() != file->pchSignature)
4989c78db60SAlexandre Ganea return createFileError(
49954a335a2SReid Kleckner toString(file),
50054a335a2SReid Kleckner make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
5019c78db60SAlexandre Ganea
50254a335a2SReid Kleckner if (pr.getSignature() != *precomp->file->pchSignature)
50354a335a2SReid Kleckner return createFileError(
50454a335a2SReid Kleckner toString(precomp->file),
50554a335a2SReid Kleckner make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
50654a335a2SReid Kleckner
5071e5b7e91SReid Kleckner return precomp;
5089c78db60SAlexandre Ganea }
5099c78db60SAlexandre Ganea
51054a335a2SReid Kleckner /// Merges a precompiled headers TPI map into the current TPI map. The
51154a335a2SReid Kleckner /// precompiled headers object will also be loaded and remapped in the
51254a335a2SReid Kleckner /// process.
mergeInPrecompHeaderObj()5135519e4daSReid Kleckner Error UsePrecompSource::mergeInPrecompHeaderObj() {
5145519e4daSReid Kleckner auto e = findPrecompMap(file, precompDependency);
51554a335a2SReid Kleckner if (!e)
51654a335a2SReid Kleckner return e.takeError();
51754a335a2SReid Kleckner
5181e5b7e91SReid Kleckner PrecompSource *precompSrc = *e;
5191e5b7e91SReid Kleckner if (precompSrc->tpiMap.empty())
5201e5b7e91SReid Kleckner return Error::success();
52154a335a2SReid Kleckner
5225519e4daSReid Kleckner assert(precompDependency.getStartTypeIndex() ==
5235519e4daSReid Kleckner TypeIndex::FirstNonSimpleIndex);
5245519e4daSReid Kleckner assert(precompDependency.getTypesCount() <= precompSrc->tpiMap.size());
52554a335a2SReid Kleckner // Use the previously remapped index map from the precompiled headers.
526eaadb41dSAlexandre Ganea indexMapStorage.insert(indexMapStorage.begin(), precompSrc->tpiMap.begin(),
5275519e4daSReid Kleckner precompSrc->tpiMap.begin() +
5285519e4daSReid Kleckner precompDependency.getTypesCount());
5295519e4daSReid Kleckner
5301e5b7e91SReid Kleckner return Error::success();
5319c78db60SAlexandre Ganea }
5329c78db60SAlexandre Ganea
mergeDebugT(TypeMerger * m)5331e5b7e91SReid Kleckner Error UsePrecompSource::mergeDebugT(TypeMerger *m) {
53454a335a2SReid Kleckner // This object was compiled with /Yu, so process the corresponding
53554a335a2SReid Kleckner // precompiled headers object (/Yc) first. Some type indices in the current
53654a335a2SReid Kleckner // object are referencing data in the precompiled headers object, so we need
53754a335a2SReid Kleckner // both to be loaded.
5385519e4daSReid Kleckner if (Error e = mergeInPrecompHeaderObj())
5391e5b7e91SReid Kleckner return e;
5409c78db60SAlexandre Ganea
5411e5b7e91SReid Kleckner return TpiSource::mergeDebugT(m);
5429c78db60SAlexandre Ganea }
5439c78db60SAlexandre Ganea
5445519e4daSReid Kleckner //===----------------------------------------------------------------------===//
5455519e4daSReid Kleckner // Parellel GHash type merging implementation.
5465519e4daSReid Kleckner //===----------------------------------------------------------------------===//
5475519e4daSReid Kleckner
loadGHashes()5485519e4daSReid Kleckner void TpiSource::loadGHashes() {
5495519e4daSReid Kleckner if (Optional<ArrayRef<uint8_t>> debugH = getDebugH(file)) {
5505519e4daSReid Kleckner ghashes = getHashesFromDebugH(*debugH);
5515519e4daSReid Kleckner ownedGHashes = false;
5525519e4daSReid Kleckner } else {
5535519e4daSReid Kleckner CVTypeArray types;
5545519e4daSReid Kleckner BinaryStreamReader reader(file->debugTypes, support::little);
5555519e4daSReid Kleckner cantFail(reader.readArray(types, reader.getLength()));
5565519e4daSReid Kleckner assignGHashesFromVector(GloballyHashedType::hashTypes(types));
5575519e4daSReid Kleckner }
5585519e4daSReid Kleckner
5595519e4daSReid Kleckner fillIsItemIndexFromDebugT();
5605519e4daSReid Kleckner }
5615519e4daSReid Kleckner
5625519e4daSReid Kleckner // Copies ghashes from a vector into an array. These are long lived, so it's
5635519e4daSReid Kleckner // worth the time to copy these into an appropriately sized vector to reduce
5645519e4daSReid Kleckner // memory usage.
assignGHashesFromVector(std::vector<GloballyHashedType> && hashVec)5655519e4daSReid Kleckner void TpiSource::assignGHashesFromVector(
5665519e4daSReid Kleckner std::vector<GloballyHashedType> &&hashVec) {
56799f02365SReid Kleckner if (hashVec.empty())
56899f02365SReid Kleckner return;
5695519e4daSReid Kleckner GloballyHashedType *hashes = new GloballyHashedType[hashVec.size()];
5705519e4daSReid Kleckner memcpy(hashes, hashVec.data(), hashVec.size() * sizeof(GloballyHashedType));
5715519e4daSReid Kleckner ghashes = makeArrayRef(hashes, hashVec.size());
5725519e4daSReid Kleckner ownedGHashes = true;
5735519e4daSReid Kleckner }
5745519e4daSReid Kleckner
5755519e4daSReid Kleckner // Faster way to iterate type records. forEachTypeChecked is faster than
5765519e4daSReid Kleckner // iterating CVTypeArray. It avoids virtual readBytes calls in inner loops.
forEachTypeChecked(ArrayRef<uint8_t> types,function_ref<void (const CVType &)> fn)5775519e4daSReid Kleckner static void forEachTypeChecked(ArrayRef<uint8_t> types,
5785519e4daSReid Kleckner function_ref<void(const CVType &)> fn) {
5795519e4daSReid Kleckner checkError(
5805519e4daSReid Kleckner forEachCodeViewRecord<CVType>(types, [fn](const CVType &ty) -> Error {
5815519e4daSReid Kleckner fn(ty);
5825519e4daSReid Kleckner return Error::success();
5835519e4daSReid Kleckner }));
5845519e4daSReid Kleckner }
5855519e4daSReid Kleckner
5865519e4daSReid Kleckner // Walk over file->debugTypes and fill in the isItemIndex bit vector.
5875519e4daSReid Kleckner // TODO: Store this information in .debug$H so that we don't have to recompute
5885519e4daSReid Kleckner // it. This is the main bottleneck slowing down parallel ghashing with one
5895519e4daSReid Kleckner // thread over single-threaded ghashing.
fillIsItemIndexFromDebugT()5905519e4daSReid Kleckner void TpiSource::fillIsItemIndexFromDebugT() {
5915519e4daSReid Kleckner uint32_t index = 0;
5925519e4daSReid Kleckner isItemIndex.resize(ghashes.size());
5935519e4daSReid Kleckner forEachTypeChecked(file->debugTypes, [&](const CVType &ty) {
5945519e4daSReid Kleckner if (isIdRecord(ty.kind()))
5955519e4daSReid Kleckner isItemIndex.set(index);
5965519e4daSReid Kleckner ++index;
5975519e4daSReid Kleckner });
5985519e4daSReid Kleckner }
5995519e4daSReid Kleckner
mergeTypeRecord(TypeIndex curIndex,CVType ty)6005d46d7e8SReid Kleckner void TpiSource::mergeTypeRecord(TypeIndex curIndex, CVType ty) {
6015519e4daSReid Kleckner // Decide if the merged type goes into TPI or IPI.
6025519e4daSReid Kleckner bool isItem = isIdRecord(ty.kind());
6035519e4daSReid Kleckner MergedInfo &merged = isItem ? mergedIpi : mergedTpi;
6045519e4daSReid Kleckner
6055519e4daSReid Kleckner // Copy the type into our mutable buffer.
6065519e4daSReid Kleckner assert(ty.length() <= codeview::MaxRecordLength);
6075519e4daSReid Kleckner size_t offset = merged.recs.size();
6085519e4daSReid Kleckner size_t newSize = alignTo(ty.length(), 4);
6095519e4daSReid Kleckner merged.recs.resize(offset + newSize);
6105519e4daSReid Kleckner auto newRec = makeMutableArrayRef(&merged.recs[offset], newSize);
6115519e4daSReid Kleckner memcpy(newRec.data(), ty.data().data(), newSize);
6125519e4daSReid Kleckner
6135519e4daSReid Kleckner // Fix up the record prefix and padding bytes if it required resizing.
6145519e4daSReid Kleckner if (newSize != ty.length()) {
6155519e4daSReid Kleckner reinterpret_cast<RecordPrefix *>(newRec.data())->RecordLen = newSize - 2;
6165519e4daSReid Kleckner for (size_t i = ty.length(); i < newSize; ++i)
6175519e4daSReid Kleckner newRec[i] = LF_PAD0 + (newSize - i);
6185519e4daSReid Kleckner }
6195519e4daSReid Kleckner
6205519e4daSReid Kleckner // Remap the type indices in the new record.
6215519e4daSReid Kleckner remapTypesInTypeRecord(newRec);
6225519e4daSReid Kleckner uint32_t pdbHash = check(pdb::hashTypeRecord(CVType(newRec)));
6235519e4daSReid Kleckner merged.recSizes.push_back(static_cast<uint16_t>(newSize));
6245519e4daSReid Kleckner merged.recHashes.push_back(pdbHash);
6255d46d7e8SReid Kleckner
6265d46d7e8SReid Kleckner // Retain a mapping from PDB function id to PDB function type. This mapping is
627c8974af1SNico Weber // used during symbol processing to rewrite S_GPROC32_ID symbols to S_GPROC32
6285d46d7e8SReid Kleckner // symbols.
6295d46d7e8SReid Kleckner if (ty.kind() == LF_FUNC_ID || ty.kind() == LF_MFUNC_ID) {
6305d46d7e8SReid Kleckner bool success = ty.length() >= 12;
6315d46d7e8SReid Kleckner TypeIndex funcId = curIndex;
6325d46d7e8SReid Kleckner if (success)
6335d46d7e8SReid Kleckner success &= remapTypeIndex(funcId, TiRefKind::IndexRef);
6345d46d7e8SReid Kleckner TypeIndex funcType =
6355d46d7e8SReid Kleckner *reinterpret_cast<const TypeIndex *>(&newRec.data()[8]);
6365d46d7e8SReid Kleckner if (success) {
6375d46d7e8SReid Kleckner funcIdToType.push_back({funcId, funcType});
6385d46d7e8SReid Kleckner } else {
6395d46d7e8SReid Kleckner StringRef fname = file ? file->getName() : "<unknown PDB>";
6405d46d7e8SReid Kleckner warn("corrupt LF_[M]FUNC_ID record 0x" + utohexstr(curIndex.getIndex()) +
6415d46d7e8SReid Kleckner " in " + fname);
6425d46d7e8SReid Kleckner }
6435d46d7e8SReid Kleckner }
6445519e4daSReid Kleckner }
6455519e4daSReid Kleckner
mergeUniqueTypeRecords(ArrayRef<uint8_t> typeRecords,TypeIndex beginIndex)6465519e4daSReid Kleckner void TpiSource::mergeUniqueTypeRecords(ArrayRef<uint8_t> typeRecords,
6475519e4daSReid Kleckner TypeIndex beginIndex) {
6485519e4daSReid Kleckner // Re-sort the list of unique types by index.
6495519e4daSReid Kleckner if (kind == PDB)
6505519e4daSReid Kleckner assert(std::is_sorted(uniqueTypes.begin(), uniqueTypes.end()));
6515519e4daSReid Kleckner else
6525519e4daSReid Kleckner llvm::sort(uniqueTypes);
6535519e4daSReid Kleckner
6545519e4daSReid Kleckner // Accumulate all the unique types into one buffer in mergedTypes.
6555519e4daSReid Kleckner uint32_t ghashIndex = 0;
6565519e4daSReid Kleckner auto nextUniqueIndex = uniqueTypes.begin();
6575519e4daSReid Kleckner assert(mergedTpi.recs.empty());
6585519e4daSReid Kleckner assert(mergedIpi.recs.empty());
659e7a371f9SAlexandre Ganea
660e7a371f9SAlexandre Ganea // Pre-compute the number of elements in advance to avoid std::vector resizes.
661e7a371f9SAlexandre Ganea unsigned nbTpiRecs = 0;
662e7a371f9SAlexandre Ganea unsigned nbIpiRecs = 0;
663e7a371f9SAlexandre Ganea forEachTypeChecked(typeRecords, [&](const CVType &ty) {
664e7a371f9SAlexandre Ganea if (nextUniqueIndex != uniqueTypes.end() &&
665e7a371f9SAlexandre Ganea *nextUniqueIndex == ghashIndex) {
666e7a371f9SAlexandre Ganea assert(ty.length() <= codeview::MaxRecordLength);
667e7a371f9SAlexandre Ganea size_t newSize = alignTo(ty.length(), 4);
668e7a371f9SAlexandre Ganea (isIdRecord(ty.kind()) ? nbIpiRecs : nbTpiRecs) += newSize;
669e7a371f9SAlexandre Ganea ++nextUniqueIndex;
670e7a371f9SAlexandre Ganea }
671e7a371f9SAlexandre Ganea ++ghashIndex;
672e7a371f9SAlexandre Ganea });
673e7a371f9SAlexandre Ganea mergedTpi.recs.reserve(nbTpiRecs);
674e7a371f9SAlexandre Ganea mergedIpi.recs.reserve(nbIpiRecs);
675e7a371f9SAlexandre Ganea
676e7a371f9SAlexandre Ganea // Do the actual type merge.
677e7a371f9SAlexandre Ganea ghashIndex = 0;
678e7a371f9SAlexandre Ganea nextUniqueIndex = uniqueTypes.begin();
6795519e4daSReid Kleckner forEachTypeChecked(typeRecords, [&](const CVType &ty) {
6805519e4daSReid Kleckner if (nextUniqueIndex != uniqueTypes.end() &&
6815519e4daSReid Kleckner *nextUniqueIndex == ghashIndex) {
6825d46d7e8SReid Kleckner mergeTypeRecord(beginIndex + ghashIndex, ty);
6835519e4daSReid Kleckner ++nextUniqueIndex;
6845519e4daSReid Kleckner }
6855519e4daSReid Kleckner ++ghashIndex;
6865519e4daSReid Kleckner });
6875519e4daSReid Kleckner assert(nextUniqueIndex == uniqueTypes.end() &&
6885519e4daSReid Kleckner "failed to merge all desired records");
6895519e4daSReid Kleckner assert(uniqueTypes.size() ==
6905519e4daSReid Kleckner mergedTpi.recSizes.size() + mergedIpi.recSizes.size() &&
6915519e4daSReid Kleckner "missing desired record");
6925519e4daSReid Kleckner }
6935519e4daSReid Kleckner
remapTpiWithGHashes(GHashState * g)6945519e4daSReid Kleckner void TpiSource::remapTpiWithGHashes(GHashState *g) {
6955519e4daSReid Kleckner assert(config->debugGHashes && "ghashes must be enabled");
696b14ad90bSAlexandre Ganea fillMapFromGHashes(g);
6975519e4daSReid Kleckner tpiMap = indexMapStorage;
6985519e4daSReid Kleckner ipiMap = indexMapStorage;
6995519e4daSReid Kleckner mergeUniqueTypeRecords(file->debugTypes);
7005519e4daSReid Kleckner // TODO: Free all unneeded ghash resources now that we have a full index map.
70155b97a6dSAlexandre Ganea
70255b97a6dSAlexandre Ganea if (config->showSummary) {
70355b97a6dSAlexandre Ganea nbTypeRecords = ghashes.size();
70455b97a6dSAlexandre Ganea nbTypeRecordsBytes = file->debugTypes.size();
70555b97a6dSAlexandre Ganea }
7065519e4daSReid Kleckner }
7075519e4daSReid Kleckner
7085519e4daSReid Kleckner // PDBs do not actually store global hashes, so when merging a type server
7095519e4daSReid Kleckner // PDB we have to synthesize global hashes. To do this, we first synthesize
7105519e4daSReid Kleckner // global hashes for the TPI stream, since it is independent, then we
7115519e4daSReid Kleckner // synthesize hashes for the IPI stream, using the hashes for the TPI stream
7125519e4daSReid Kleckner // as inputs.
loadGHashes()7135519e4daSReid Kleckner void TypeServerSource::loadGHashes() {
7145519e4daSReid Kleckner // Don't hash twice.
7155519e4daSReid Kleckner if (!ghashes.empty())
7165519e4daSReid Kleckner return;
7175519e4daSReid Kleckner pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile();
7185519e4daSReid Kleckner
7195519e4daSReid Kleckner // Hash TPI stream.
7205519e4daSReid Kleckner Expected<pdb::TpiStream &> expectedTpi = pdbFile.getPDBTpiStream();
7215519e4daSReid Kleckner if (auto e = expectedTpi.takeError())
7225519e4daSReid Kleckner fatal("Type server does not have TPI stream: " + toString(std::move(e)));
7235519e4daSReid Kleckner assignGHashesFromVector(
7245519e4daSReid Kleckner GloballyHashedType::hashTypes(expectedTpi->typeArray()));
7255519e4daSReid Kleckner isItemIndex.resize(ghashes.size());
7265519e4daSReid Kleckner
7275519e4daSReid Kleckner // Hash IPI stream, which depends on TPI ghashes.
7285519e4daSReid Kleckner if (!pdbFile.hasPDBIpiStream())
7295519e4daSReid Kleckner return;
7305519e4daSReid Kleckner Expected<pdb::TpiStream &> expectedIpi = pdbFile.getPDBIpiStream();
7315519e4daSReid Kleckner if (auto e = expectedIpi.takeError())
732c8974af1SNico Weber fatal("error retrieving IPI stream: " + toString(std::move(e)));
7335519e4daSReid Kleckner ipiSrc->assignGHashesFromVector(
7345519e4daSReid Kleckner GloballyHashedType::hashIds(expectedIpi->typeArray(), ghashes));
7355519e4daSReid Kleckner
7365519e4daSReid Kleckner // The IPI stream isItemIndex bitvector should be all ones.
7375519e4daSReid Kleckner ipiSrc->isItemIndex.resize(ipiSrc->ghashes.size());
7385519e4daSReid Kleckner ipiSrc->isItemIndex.set(0, ipiSrc->ghashes.size());
7395519e4daSReid Kleckner }
7405519e4daSReid Kleckner
7415519e4daSReid Kleckner // Flatten discontiguous PDB type arrays to bytes so that we can use
7425519e4daSReid Kleckner // forEachTypeChecked instead of CVTypeArray iteration. Copying all types from
7435519e4daSReid Kleckner // type servers is faster than iterating all object files compiled with /Z7 with
7445519e4daSReid Kleckner // CVTypeArray, which has high overheads due to the virtual interface of
7455519e4daSReid Kleckner // BinaryStream::readBytes.
typeArrayToBytes(const CVTypeArray & types)7465519e4daSReid Kleckner static ArrayRef<uint8_t> typeArrayToBytes(const CVTypeArray &types) {
7475519e4daSReid Kleckner BinaryStreamRef stream = types.getUnderlyingStream();
7485519e4daSReid Kleckner ArrayRef<uint8_t> debugTypes;
7495519e4daSReid Kleckner checkError(stream.readBytes(0, stream.getLength(), debugTypes));
7505519e4daSReid Kleckner return debugTypes;
7515519e4daSReid Kleckner }
7525519e4daSReid Kleckner
7535519e4daSReid Kleckner // Merge types from a type server PDB.
remapTpiWithGHashes(GHashState * g)7545519e4daSReid Kleckner void TypeServerSource::remapTpiWithGHashes(GHashState *g) {
7555519e4daSReid Kleckner assert(config->debugGHashes && "ghashes must be enabled");
7565519e4daSReid Kleckner
7575519e4daSReid Kleckner // IPI merging depends on TPI, so do TPI first, then do IPI. No need to
7585519e4daSReid Kleckner // propagate errors, those should've been handled during ghash loading.
7595519e4daSReid Kleckner pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile();
7605519e4daSReid Kleckner pdb::TpiStream &tpi = check(pdbFile.getPDBTpiStream());
761b14ad90bSAlexandre Ganea fillMapFromGHashes(g);
7625519e4daSReid Kleckner tpiMap = indexMapStorage;
7635519e4daSReid Kleckner mergeUniqueTypeRecords(typeArrayToBytes(tpi.typeArray()));
7645519e4daSReid Kleckner if (pdbFile.hasPDBIpiStream()) {
7655519e4daSReid Kleckner pdb::TpiStream &ipi = check(pdbFile.getPDBIpiStream());
7665519e4daSReid Kleckner ipiSrc->indexMapStorage.resize(ipiSrc->ghashes.size());
767b14ad90bSAlexandre Ganea ipiSrc->fillMapFromGHashes(g);
7685519e4daSReid Kleckner ipiMap = ipiSrc->indexMapStorage;
7695519e4daSReid Kleckner ipiSrc->tpiMap = tpiMap;
7705519e4daSReid Kleckner ipiSrc->ipiMap = ipiMap;
7715519e4daSReid Kleckner ipiSrc->mergeUniqueTypeRecords(typeArrayToBytes(ipi.typeArray()));
77255b97a6dSAlexandre Ganea
77355b97a6dSAlexandre Ganea if (config->showSummary) {
77455b97a6dSAlexandre Ganea nbTypeRecords = ipiSrc->ghashes.size();
77555b97a6dSAlexandre Ganea nbTypeRecordsBytes = ipi.typeArray().getUnderlyingStream().getLength();
77655b97a6dSAlexandre Ganea }
77755b97a6dSAlexandre Ganea }
77855b97a6dSAlexandre Ganea
77955b97a6dSAlexandre Ganea if (config->showSummary) {
78055b97a6dSAlexandre Ganea nbTypeRecords += ghashes.size();
78155b97a6dSAlexandre Ganea nbTypeRecordsBytes += tpi.typeArray().getUnderlyingStream().getLength();
7825519e4daSReid Kleckner }
7835519e4daSReid Kleckner }
7845519e4daSReid Kleckner
remapTpiWithGHashes(GHashState * g)7855519e4daSReid Kleckner void UseTypeServerSource::remapTpiWithGHashes(GHashState *g) {
7865519e4daSReid Kleckner // No remapping to do with /Zi objects. Simply use the index map from the type
7875519e4daSReid Kleckner // server. Errors should have been reported earlier. Symbols from this object
7885519e4daSReid Kleckner // will be ignored.
7895519e4daSReid Kleckner Expected<TypeServerSource *> maybeTsSrc = getTypeServerSource();
7905519e4daSReid Kleckner if (!maybeTsSrc) {
7915519e4daSReid Kleckner typeMergingError =
7925519e4daSReid Kleckner joinErrors(std::move(typeMergingError), maybeTsSrc.takeError());
7935519e4daSReid Kleckner return;
7945519e4daSReid Kleckner }
7955519e4daSReid Kleckner TypeServerSource *tsSrc = *maybeTsSrc;
7965519e4daSReid Kleckner tpiMap = tsSrc->tpiMap;
7975519e4daSReid Kleckner ipiMap = tsSrc->ipiMap;
7985519e4daSReid Kleckner }
7995519e4daSReid Kleckner
loadGHashes()8005519e4daSReid Kleckner void PrecompSource::loadGHashes() {
8015519e4daSReid Kleckner if (getDebugH(file)) {
8025519e4daSReid Kleckner warn("ignoring .debug$H section; pch with ghash is not implemented");
8035519e4daSReid Kleckner }
8045519e4daSReid Kleckner
8055519e4daSReid Kleckner uint32_t ghashIdx = 0;
8065519e4daSReid Kleckner std::vector<GloballyHashedType> hashVec;
8075519e4daSReid Kleckner forEachTypeChecked(file->debugTypes, [&](const CVType &ty) {
8085519e4daSReid Kleckner // Remember the index of the LF_ENDPRECOMP record so it can be excluded from
8095519e4daSReid Kleckner // the PDB. There must be an entry in the list of ghashes so that the type
8105519e4daSReid Kleckner // indexes of the following records in the /Yc PCH object line up.
8115519e4daSReid Kleckner if (ty.kind() == LF_ENDPRECOMP)
8125519e4daSReid Kleckner endPrecompGHashIdx = ghashIdx;
8135519e4daSReid Kleckner
8145519e4daSReid Kleckner hashVec.push_back(GloballyHashedType::hashType(ty, hashVec, hashVec));
8155519e4daSReid Kleckner isItemIndex.push_back(isIdRecord(ty.kind()));
8165519e4daSReid Kleckner ++ghashIdx;
8175519e4daSReid Kleckner });
8185519e4daSReid Kleckner assignGHashesFromVector(std::move(hashVec));
8195519e4daSReid Kleckner }
8205519e4daSReid Kleckner
loadGHashes()8215519e4daSReid Kleckner void UsePrecompSource::loadGHashes() {
8225519e4daSReid Kleckner PrecompSource *pchSrc = findPrecompSource(file, precompDependency);
8235519e4daSReid Kleckner if (!pchSrc)
8245519e4daSReid Kleckner return;
8255519e4daSReid Kleckner
8265519e4daSReid Kleckner // To compute ghashes of a /Yu object file, we need to build on the the
8275519e4daSReid Kleckner // ghashes of the /Yc PCH object. After we are done hashing, discard the
8285519e4daSReid Kleckner // ghashes from the PCH source so we don't unnecessarily try to deduplicate
8295519e4daSReid Kleckner // them.
8305519e4daSReid Kleckner std::vector<GloballyHashedType> hashVec =
8315519e4daSReid Kleckner pchSrc->ghashes.take_front(precompDependency.getTypesCount());
8325519e4daSReid Kleckner forEachTypeChecked(file->debugTypes, [&](const CVType &ty) {
8335519e4daSReid Kleckner hashVec.push_back(GloballyHashedType::hashType(ty, hashVec, hashVec));
8345519e4daSReid Kleckner isItemIndex.push_back(isIdRecord(ty.kind()));
8355519e4daSReid Kleckner });
8365519e4daSReid Kleckner hashVec.erase(hashVec.begin(),
8375519e4daSReid Kleckner hashVec.begin() + precompDependency.getTypesCount());
8385519e4daSReid Kleckner assignGHashesFromVector(std::move(hashVec));
8395519e4daSReid Kleckner }
8405519e4daSReid Kleckner
remapTpiWithGHashes(GHashState * g)8415519e4daSReid Kleckner void UsePrecompSource::remapTpiWithGHashes(GHashState *g) {
842b14ad90bSAlexandre Ganea fillMapFromGHashes(g);
8435519e4daSReid Kleckner // This object was compiled with /Yu, so process the corresponding
8445519e4daSReid Kleckner // precompiled headers object (/Yc) first. Some type indices in the current
8455519e4daSReid Kleckner // object are referencing data in the precompiled headers object, so we need
8465519e4daSReid Kleckner // both to be loaded.
8475519e4daSReid Kleckner if (Error e = mergeInPrecompHeaderObj()) {
8485519e4daSReid Kleckner typeMergingError = joinErrors(std::move(typeMergingError), std::move(e));
8495519e4daSReid Kleckner return;
8505519e4daSReid Kleckner }
8515519e4daSReid Kleckner
8525519e4daSReid Kleckner tpiMap = indexMapStorage;
8535519e4daSReid Kleckner ipiMap = indexMapStorage;
8545519e4daSReid Kleckner mergeUniqueTypeRecords(file->debugTypes,
8555519e4daSReid Kleckner TypeIndex(precompDependency.getStartTypeIndex() +
8565519e4daSReid Kleckner precompDependency.getTypesCount()));
85755b97a6dSAlexandre Ganea if (config->showSummary) {
85855b97a6dSAlexandre Ganea nbTypeRecords = ghashes.size();
85955b97a6dSAlexandre Ganea nbTypeRecordsBytes = file->debugTypes.size();
86055b97a6dSAlexandre Ganea }
8615519e4daSReid Kleckner }
8625519e4daSReid Kleckner
8635519e4daSReid Kleckner namespace {
8645519e4daSReid Kleckner /// A concurrent hash table for global type hashing. It is based on this paper:
8655519e4daSReid Kleckner /// Concurrent Hash Tables: Fast and General(?)!
8665519e4daSReid Kleckner /// https://dl.acm.org/doi/10.1145/3309206
8675519e4daSReid Kleckner ///
8685519e4daSReid Kleckner /// This hash table is meant to be used in two phases:
8695519e4daSReid Kleckner /// 1. concurrent insertions
8705519e4daSReid Kleckner /// 2. concurrent reads
8715519e4daSReid Kleckner /// It does not support lookup, deletion, or rehashing. It uses linear probing.
8725519e4daSReid Kleckner ///
8735519e4daSReid Kleckner /// The paper describes storing a key-value pair in two machine words.
8745519e4daSReid Kleckner /// Generally, the values stored in this map are type indices, and we can use
8755519e4daSReid Kleckner /// those values to recover the ghash key from a side table. This allows us to
8765519e4daSReid Kleckner /// shrink the table entries further at the cost of some loads, and sidesteps
8775519e4daSReid Kleckner /// the need for a 128 bit atomic compare-and-swap operation.
8785519e4daSReid Kleckner ///
8795519e4daSReid Kleckner /// During insertion, a priority function is used to decide which insertion
8805519e4daSReid Kleckner /// should be preferred. This ensures that the output is deterministic. For
8815519e4daSReid Kleckner /// ghashing, lower tpiSrcIdx values (earlier inputs) are preferred.
8825519e4daSReid Kleckner ///
8835519e4daSReid Kleckner class GHashCell;
8845519e4daSReid Kleckner struct GHashTable {
8855519e4daSReid Kleckner GHashCell *table = nullptr;
8865519e4daSReid Kleckner uint32_t tableSize = 0;
8875519e4daSReid Kleckner
8885519e4daSReid Kleckner GHashTable() = default;
8895519e4daSReid Kleckner ~GHashTable();
8905519e4daSReid Kleckner
8915519e4daSReid Kleckner /// Initialize the table with the given size. Because the table cannot be
8925519e4daSReid Kleckner /// resized, the initial size of the table must be large enough to contain all
8935519e4daSReid Kleckner /// inputs, or insertion may not be able to find an empty cell.
8945519e4daSReid Kleckner void init(uint32_t newTableSize);
8955519e4daSReid Kleckner
8965519e4daSReid Kleckner /// Insert the cell with the given ghash into the table. Return the insertion
8975519e4daSReid Kleckner /// position in the table. It is safe for the caller to store the insertion
8985519e4daSReid Kleckner /// position because the table cannot be resized.
8996f7483b1SAmy Huang uint32_t insert(COFFLinkerContext &ctx, GloballyHashedType ghash,
9006f7483b1SAmy Huang GHashCell newCell);
9015519e4daSReid Kleckner };
9025519e4daSReid Kleckner
9035519e4daSReid Kleckner /// A ghash table cell for deduplicating types from TpiSources.
9045519e4daSReid Kleckner class GHashCell {
90513fc1781SEli Friedman // Force "data" to be 64-bit aligned; otherwise, some versions of clang
90613fc1781SEli Friedman // will generate calls to libatomic when using some versions of libstdc++
90713fc1781SEli Friedman // on 32-bit targets. (Also, in theory, there could be a target where
90813fc1781SEli Friedman // new[] doesn't always return an 8-byte-aligned allocation.)
90913fc1781SEli Friedman alignas(sizeof(uint64_t)) uint64_t data = 0;
9105519e4daSReid Kleckner
9115519e4daSReid Kleckner public:
9125519e4daSReid Kleckner GHashCell() = default;
9135519e4daSReid Kleckner
9145519e4daSReid Kleckner // Construct data most to least significant so that sorting works well:
9155519e4daSReid Kleckner // - isItem
9165519e4daSReid Kleckner // - tpiSrcIdx
9175519e4daSReid Kleckner // - ghashIdx
9185519e4daSReid Kleckner // Add one to the tpiSrcIdx so that the 0th record from the 0th source has a
9195519e4daSReid Kleckner // non-zero representation.
GHashCell(bool isItem,uint32_t tpiSrcIdx,uint32_t ghashIdx)9205519e4daSReid Kleckner GHashCell(bool isItem, uint32_t tpiSrcIdx, uint32_t ghashIdx)
9215519e4daSReid Kleckner : data((uint64_t(isItem) << 63U) | (uint64_t(tpiSrcIdx + 1) << 32ULL) |
9225519e4daSReid Kleckner ghashIdx) {
9235519e4daSReid Kleckner assert(tpiSrcIdx == getTpiSrcIdx() && "round trip failure");
9245519e4daSReid Kleckner assert(ghashIdx == getGHashIdx() && "round trip failure");
9255519e4daSReid Kleckner }
9265519e4daSReid Kleckner
GHashCell(uint64_t data)9275519e4daSReid Kleckner explicit GHashCell(uint64_t data) : data(data) {}
9285519e4daSReid Kleckner
9295519e4daSReid Kleckner // The empty cell is all zeros.
isEmpty() const9305519e4daSReid Kleckner bool isEmpty() const { return data == 0ULL; }
9315519e4daSReid Kleckner
9325519e4daSReid Kleckner /// Extract the tpiSrcIdx.
getTpiSrcIdx() const9335519e4daSReid Kleckner uint32_t getTpiSrcIdx() const {
9345519e4daSReid Kleckner return ((uint32_t)(data >> 32U) & 0x7FFFFFFF) - 1;
9355519e4daSReid Kleckner }
9365519e4daSReid Kleckner
9375519e4daSReid Kleckner /// Extract the index into the ghash array of the TpiSource.
getGHashIdx() const9385519e4daSReid Kleckner uint32_t getGHashIdx() const { return (uint32_t)data; }
9395519e4daSReid Kleckner
isItem() const9405519e4daSReid Kleckner bool isItem() const { return data & (1ULL << 63U); }
9415519e4daSReid Kleckner
9425519e4daSReid Kleckner /// Get the ghash key for this cell.
getGHash(const COFFLinkerContext & ctx) const9436f7483b1SAmy Huang GloballyHashedType getGHash(const COFFLinkerContext &ctx) const {
9446f7483b1SAmy Huang return ctx.tpiSourceList[getTpiSrcIdx()]->ghashes[getGHashIdx()];
9455519e4daSReid Kleckner }
9465519e4daSReid Kleckner
9475519e4daSReid Kleckner /// The priority function for the cell. The data is stored such that lower
9485519e4daSReid Kleckner /// tpiSrcIdx and ghashIdx values are preferred, which means that type record
9495519e4daSReid Kleckner /// from earlier sources are more likely to prevail.
operator <(const GHashCell & l,const GHashCell & r)9505519e4daSReid Kleckner friend inline bool operator<(const GHashCell &l, const GHashCell &r) {
9515519e4daSReid Kleckner return l.data < r.data;
9525519e4daSReid Kleckner }
9535519e4daSReid Kleckner };
9545519e4daSReid Kleckner } // namespace
9555519e4daSReid Kleckner
9565519e4daSReid Kleckner namespace lld {
9575519e4daSReid Kleckner namespace coff {
9585519e4daSReid Kleckner /// This type is just a wrapper around GHashTable with external linkage so it
9595519e4daSReid Kleckner /// can be used from a header.
9605519e4daSReid Kleckner struct GHashState {
9615519e4daSReid Kleckner GHashTable table;
9625519e4daSReid Kleckner };
9635519e4daSReid Kleckner } // namespace coff
9645519e4daSReid Kleckner } // namespace lld
9655519e4daSReid Kleckner
~GHashTable()9665519e4daSReid Kleckner GHashTable::~GHashTable() { delete[] table; }
9675519e4daSReid Kleckner
init(uint32_t newTableSize)9685519e4daSReid Kleckner void GHashTable::init(uint32_t newTableSize) {
9695519e4daSReid Kleckner table = new GHashCell[newTableSize];
9705519e4daSReid Kleckner memset(table, 0, newTableSize * sizeof(GHashCell));
9715519e4daSReid Kleckner tableSize = newTableSize;
9725519e4daSReid Kleckner }
9735519e4daSReid Kleckner
insert(COFFLinkerContext & ctx,GloballyHashedType ghash,GHashCell newCell)9746f7483b1SAmy Huang uint32_t GHashTable::insert(COFFLinkerContext &ctx, GloballyHashedType ghash,
9756f7483b1SAmy Huang GHashCell newCell) {
9765519e4daSReid Kleckner assert(!newCell.isEmpty() && "cannot insert empty cell value");
9775519e4daSReid Kleckner
9785519e4daSReid Kleckner // FIXME: The low bytes of SHA1 have low entropy for short records, which
9795519e4daSReid Kleckner // type records are. Swap the byte order for better entropy. A better ghash
9805519e4daSReid Kleckner // won't need this.
9815519e4daSReid Kleckner uint32_t startIdx =
9825519e4daSReid Kleckner ByteSwap_64(*reinterpret_cast<uint64_t *>(&ghash)) % tableSize;
9835519e4daSReid Kleckner
9845519e4daSReid Kleckner // Do a linear probe starting at startIdx.
9855519e4daSReid Kleckner uint32_t idx = startIdx;
9865519e4daSReid Kleckner while (true) {
9875519e4daSReid Kleckner // Run a compare and swap loop. There are four cases:
9885519e4daSReid Kleckner // - cell is empty: CAS into place and return
9895519e4daSReid Kleckner // - cell has matching key, earlier priority: do nothing, return
9905519e4daSReid Kleckner // - cell has matching key, later priority: CAS into place and return
9915519e4daSReid Kleckner // - cell has non-matching key: hash collision, probe next cell
9925519e4daSReid Kleckner auto *cellPtr = reinterpret_cast<std::atomic<GHashCell> *>(&table[idx]);
9935519e4daSReid Kleckner GHashCell oldCell(cellPtr->load());
9946f7483b1SAmy Huang while (oldCell.isEmpty() || oldCell.getGHash(ctx) == ghash) {
9955519e4daSReid Kleckner // Check if there is an existing ghash entry with a higher priority
9965519e4daSReid Kleckner // (earlier ordering). If so, this is a duplicate, we are done.
9975519e4daSReid Kleckner if (!oldCell.isEmpty() && oldCell < newCell)
9985519e4daSReid Kleckner return idx;
9995519e4daSReid Kleckner // Either the cell is empty, or our value is higher priority. Try to
10005519e4daSReid Kleckner // compare and swap. If it succeeds, we are done.
10015519e4daSReid Kleckner if (cellPtr->compare_exchange_weak(oldCell, newCell))
10025519e4daSReid Kleckner return idx;
10035519e4daSReid Kleckner // If the CAS failed, check this cell again.
10045519e4daSReid Kleckner }
10055519e4daSReid Kleckner
10065519e4daSReid Kleckner // Advance the probe. Wrap around to the beginning if we run off the end.
10075519e4daSReid Kleckner ++idx;
10085519e4daSReid Kleckner idx = idx == tableSize ? 0 : idx;
10095519e4daSReid Kleckner if (idx == startIdx) {
10105519e4daSReid Kleckner // If this becomes an issue, we could mark failure and rehash from the
10115519e4daSReid Kleckner // beginning with a bigger table. There is no difference between rehashing
10125519e4daSReid Kleckner // internally and starting over.
10135519e4daSReid Kleckner report_fatal_error("ghash table is full");
10145519e4daSReid Kleckner }
10155519e4daSReid Kleckner }
10165519e4daSReid Kleckner llvm_unreachable("left infloop");
10175519e4daSReid Kleckner }
10185519e4daSReid Kleckner
TypeMerger(COFFLinkerContext & c,llvm::BumpPtrAllocator & alloc)10196f7483b1SAmy Huang TypeMerger::TypeMerger(COFFLinkerContext &c, llvm::BumpPtrAllocator &alloc)
10206f7483b1SAmy Huang : typeTable(alloc), idTable(alloc), ctx(c) {}
10215519e4daSReid Kleckner
10225519e4daSReid Kleckner TypeMerger::~TypeMerger() = default;
10235519e4daSReid Kleckner
mergeTypesWithGHash()10245519e4daSReid Kleckner void TypeMerger::mergeTypesWithGHash() {
10255519e4daSReid Kleckner // Load ghashes. Do type servers and PCH objects first.
10265519e4daSReid Kleckner {
10276f7483b1SAmy Huang ScopedTimer t1(ctx.loadGHashTimer);
10286f7483b1SAmy Huang parallelForEach(dependencySources,
10295519e4daSReid Kleckner [&](TpiSource *source) { source->loadGHashes(); });
10306f7483b1SAmy Huang parallelForEach(objectSources,
10315519e4daSReid Kleckner [&](TpiSource *source) { source->loadGHashes(); });
10325519e4daSReid Kleckner }
10335519e4daSReid Kleckner
10346f7483b1SAmy Huang ScopedTimer t2(ctx.mergeGHashTimer);
10355519e4daSReid Kleckner GHashState ghashState;
10365519e4daSReid Kleckner
10375519e4daSReid Kleckner // Estimate the size of hash table needed to deduplicate ghashes. This *must*
10385519e4daSReid Kleckner // be larger than the number of unique types, or hash table insertion may not
10395519e4daSReid Kleckner // be able to find a vacant slot. Summing the input types guarantees this, but
10405519e4daSReid Kleckner // it is a gross overestimate. The table size could be reduced to save memory,
10415519e4daSReid Kleckner // but it would require implementing rehashing, and this table is generally
10425519e4daSReid Kleckner // small compared to total memory usage, at eight bytes per input type record,
10435519e4daSReid Kleckner // and most input type records are larger than eight bytes.
10445519e4daSReid Kleckner size_t tableSize = 0;
10456f7483b1SAmy Huang for (TpiSource *source : ctx.tpiSourceList)
10465519e4daSReid Kleckner tableSize += source->ghashes.size();
10475519e4daSReid Kleckner
10485519e4daSReid Kleckner // Cap the table size so that we can use 32-bit cell indices. Type indices are
10495519e4daSReid Kleckner // also 32-bit, so this is an inherent PDB file format limit anyway.
10502b9b9652SAlexandre Ganea tableSize =
10512b9b9652SAlexandre Ganea std::min(size_t(INT32_MAX) - TypeIndex::FirstNonSimpleIndex, tableSize);
10525519e4daSReid Kleckner ghashState.table.init(static_cast<uint32_t>(tableSize));
10535519e4daSReid Kleckner
10545519e4daSReid Kleckner // Insert ghashes in parallel. During concurrent insertion, we cannot observe
10555519e4daSReid Kleckner // the contents of the hash table cell, but we can remember the insertion
10565519e4daSReid Kleckner // position. Because the table does not rehash, the position will not change
10575519e4daSReid Kleckner // under insertion. After insertion is done, the value of the cell can be read
10589c6a884fSNico Weber // to retrieve the final PDB type index.
10597effcbdaSNico Weber parallelFor(0, ctx.tpiSourceList.size(), [&](size_t tpiSrcIdx) {
10606f7483b1SAmy Huang TpiSource *source = ctx.tpiSourceList[tpiSrcIdx];
10615519e4daSReid Kleckner source->indexMapStorage.resize(source->ghashes.size());
10625519e4daSReid Kleckner for (uint32_t i = 0, e = source->ghashes.size(); i < e; i++) {
10635519e4daSReid Kleckner if (source->shouldOmitFromPdb(i)) {
10645519e4daSReid Kleckner source->indexMapStorage[i] = TypeIndex(SimpleTypeKind::NotTranslated);
10655519e4daSReid Kleckner continue;
10665519e4daSReid Kleckner }
10675519e4daSReid Kleckner GloballyHashedType ghash = source->ghashes[i];
10685519e4daSReid Kleckner bool isItem = source->isItemIndex.test(i);
10695519e4daSReid Kleckner uint32_t cellIdx =
10706f7483b1SAmy Huang ghashState.table.insert(ctx, ghash, GHashCell(isItem, tpiSrcIdx, i));
10715519e4daSReid Kleckner
10725519e4daSReid Kleckner // Store the ghash cell index as a type index in indexMapStorage. Later
10735519e4daSReid Kleckner // we will replace it with the PDB type index.
10745519e4daSReid Kleckner source->indexMapStorage[i] = TypeIndex::fromArrayIndex(cellIdx);
10755519e4daSReid Kleckner }
10765519e4daSReid Kleckner });
10775519e4daSReid Kleckner
10785519e4daSReid Kleckner // Collect all non-empty cells and sort them. This will implicitly assign
10795519e4daSReid Kleckner // destination type indices, and partition the entries into type records and
10805519e4daSReid Kleckner // item records. It arranges types in this order:
10815519e4daSReid Kleckner // - type records
10825519e4daSReid Kleckner // - source 0, type 0...
10835519e4daSReid Kleckner // - source 1, type 1...
10845519e4daSReid Kleckner // - item records
10855519e4daSReid Kleckner // - source 0, type 1...
10865519e4daSReid Kleckner // - source 1, type 0...
10875519e4daSReid Kleckner std::vector<GHashCell> entries;
10885519e4daSReid Kleckner for (const GHashCell &cell :
10895519e4daSReid Kleckner makeArrayRef(ghashState.table.table, tableSize)) {
10905519e4daSReid Kleckner if (!cell.isEmpty())
10915519e4daSReid Kleckner entries.push_back(cell);
10925519e4daSReid Kleckner }
10935519e4daSReid Kleckner parallelSort(entries, std::less<GHashCell>());
10945519e4daSReid Kleckner log(formatv("ghash table load factor: {0:p} (size {1} / capacity {2})\n",
1095d77d7273SMartin Storsjö tableSize ? double(entries.size()) / tableSize : 0,
1096d77d7273SMartin Storsjö entries.size(), tableSize));
10975519e4daSReid Kleckner
10985519e4daSReid Kleckner // Find out how many type and item indices there are.
10995519e4daSReid Kleckner auto mid =
11005519e4daSReid Kleckner std::lower_bound(entries.begin(), entries.end(), GHashCell(true, 0, 0));
11015519e4daSReid Kleckner assert((mid == entries.end() || mid->isItem()) &&
11025519e4daSReid Kleckner (mid == entries.begin() || !std::prev(mid)->isItem()) &&
11035519e4daSReid Kleckner "midpoint is not midpoint");
11045519e4daSReid Kleckner uint32_t numTypes = std::distance(entries.begin(), mid);
11055519e4daSReid Kleckner uint32_t numItems = std::distance(mid, entries.end());
11065519e4daSReid Kleckner log("Tpi record count: " + Twine(numTypes));
11075519e4daSReid Kleckner log("Ipi record count: " + Twine(numItems));
11085519e4daSReid Kleckner
11095519e4daSReid Kleckner // Make a list of the "unique" type records to merge for each tpi source. Type
11105519e4daSReid Kleckner // merging will skip indices not on this list. Store the destination PDB type
11115519e4daSReid Kleckner // index for these unique types in the tpiMap for each source. The entries for
11125519e4daSReid Kleckner // non-unique types will be filled in prior to type merging.
11135519e4daSReid Kleckner for (uint32_t i = 0, e = entries.size(); i < e; ++i) {
11145519e4daSReid Kleckner auto &cell = entries[i];
11155519e4daSReid Kleckner uint32_t tpiSrcIdx = cell.getTpiSrcIdx();
11166f7483b1SAmy Huang TpiSource *source = ctx.tpiSourceList[tpiSrcIdx];
11175519e4daSReid Kleckner source->uniqueTypes.push_back(cell.getGHashIdx());
11185519e4daSReid Kleckner
11195519e4daSReid Kleckner // Update the ghash table to store the destination PDB type index in the
11205519e4daSReid Kleckner // table.
11215519e4daSReid Kleckner uint32_t pdbTypeIndex = i < numTypes ? i : i - numTypes;
11225519e4daSReid Kleckner uint32_t ghashCellIndex =
11235519e4daSReid Kleckner source->indexMapStorage[cell.getGHashIdx()].toArrayIndex();
11245519e4daSReid Kleckner ghashState.table.table[ghashCellIndex] =
11255519e4daSReid Kleckner GHashCell(cell.isItem(), cell.getTpiSrcIdx(), pdbTypeIndex);
11265519e4daSReid Kleckner }
11275519e4daSReid Kleckner
11285519e4daSReid Kleckner // In parallel, remap all types.
1129*c730f9a1SKazu Hirata for (TpiSource *source : dependencySources)
11305519e4daSReid Kleckner source->remapTpiWithGHashes(&ghashState);
11316f7483b1SAmy Huang parallelForEach(objectSources, [&](TpiSource *source) {
11325519e4daSReid Kleckner source->remapTpiWithGHashes(&ghashState);
11335519e4daSReid Kleckner });
11345519e4daSReid Kleckner
11355d46d7e8SReid Kleckner // Build a global map of from function ID to function type.
11366f7483b1SAmy Huang for (TpiSource *source : ctx.tpiSourceList) {
11375d46d7e8SReid Kleckner for (auto idToType : source->funcIdToType)
11385d46d7e8SReid Kleckner funcIdToType.insert(idToType);
11395d46d7e8SReid Kleckner source->funcIdToType.clear();
11405d46d7e8SReid Kleckner }
11415d46d7e8SReid Kleckner
11426f7483b1SAmy Huang clearGHashes();
11436f7483b1SAmy Huang }
11446f7483b1SAmy Huang
sortDependencies()11456f7483b1SAmy Huang void TypeMerger::sortDependencies() {
11466f7483b1SAmy Huang // Order dependencies first, but preserve the existing order.
11476f7483b1SAmy Huang std::vector<TpiSource *> deps;
11486f7483b1SAmy Huang std::vector<TpiSource *> objs;
11496f7483b1SAmy Huang for (TpiSource *s : ctx.tpiSourceList)
11506f7483b1SAmy Huang (s->isDependency() ? deps : objs).push_back(s);
11516f7483b1SAmy Huang uint32_t numDeps = deps.size();
11526f7483b1SAmy Huang uint32_t numObjs = objs.size();
11536f7483b1SAmy Huang ctx.tpiSourceList = std::move(deps);
11546f7483b1SAmy Huang ctx.tpiSourceList.insert(ctx.tpiSourceList.end(), objs.begin(), objs.end());
11556f7483b1SAmy Huang for (uint32_t i = 0, e = ctx.tpiSourceList.size(); i < e; ++i)
11566f7483b1SAmy Huang ctx.tpiSourceList[i]->tpiSrcIdx = i;
11576f7483b1SAmy Huang dependencySources = makeArrayRef(ctx.tpiSourceList.data(), numDeps);
11586f7483b1SAmy Huang objectSources = makeArrayRef(ctx.tpiSourceList.data() + numDeps, numObjs);
11595519e4daSReid Kleckner }
11605519e4daSReid Kleckner
11615519e4daSReid Kleckner /// Given the index into the ghash table for a particular type, return the type
11625519e4daSReid Kleckner /// index for that type in the output PDB.
loadPdbTypeIndexFromCell(GHashState * g,uint32_t ghashCellIdx)11635519e4daSReid Kleckner static TypeIndex loadPdbTypeIndexFromCell(GHashState *g,
11645519e4daSReid Kleckner uint32_t ghashCellIdx) {
11655519e4daSReid Kleckner GHashCell cell = g->table.table[ghashCellIdx];
11665519e4daSReid Kleckner return TypeIndex::fromArrayIndex(cell.getGHashIdx());
11675519e4daSReid Kleckner }
11685519e4daSReid Kleckner
11696f7483b1SAmy Huang /// Free heap allocated ghashes.
clearGHashes()11706f7483b1SAmy Huang void TypeMerger::clearGHashes() {
11716f7483b1SAmy Huang for (TpiSource *src : ctx.tpiSourceList) {
11726f7483b1SAmy Huang if (src->ownedGHashes)
11736f7483b1SAmy Huang delete[] src->ghashes.data();
11746f7483b1SAmy Huang src->ghashes = {};
11756f7483b1SAmy Huang src->isItemIndex.clear();
11766f7483b1SAmy Huang src->uniqueTypes.clear();
11776f7483b1SAmy Huang }
11786f7483b1SAmy Huang }
11796f7483b1SAmy Huang
11805519e4daSReid Kleckner // Fill in a TPI or IPI index map using ghashes. For each source type, use its
11815519e4daSReid Kleckner // ghash to lookup its final type index in the PDB, and store that in the map.
fillMapFromGHashes(GHashState * g)1182b14ad90bSAlexandre Ganea void TpiSource::fillMapFromGHashes(GHashState *g) {
11835519e4daSReid Kleckner for (size_t i = 0, e = ghashes.size(); i < e; ++i) {
11845519e4daSReid Kleckner TypeIndex fakeCellIndex = indexMapStorage[i];
11855519e4daSReid Kleckner if (fakeCellIndex.isSimple())
1186b14ad90bSAlexandre Ganea indexMapStorage[i] = fakeCellIndex;
11875519e4daSReid Kleckner else
1188b14ad90bSAlexandre Ganea indexMapStorage[i] =
1189b14ad90bSAlexandre Ganea loadPdbTypeIndexFromCell(g, fakeCellIndex.toArrayIndex());
11905519e4daSReid Kleckner }
11915519e4daSReid Kleckner }
1192