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