diff --git a/lld/COFF/DebugTypes.h b/lld/COFF/DebugTypes.h --- a/lld/COFF/DebugTypes.h +++ b/lld/COFF/DebugTypes.h @@ -9,6 +9,8 @@ #ifndef LLD_COFF_DEBUGTYPES_H #define LLD_COFF_DEBUGTYPES_H +#include "lld/Common/LLVM.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" @@ -25,14 +27,25 @@ namespace lld { namespace coff { +using llvm::codeview::TypeIndex; + class ObjFile; class PDBInputFile; -struct CVIndexMap; class TypeMerger; +/// Map from type index and item index in a type server PDB to the +/// corresponding index in the destination PDB. +struct CVIndexMap { + llvm::ArrayRef tpiMap; + llvm::ArrayRef ipiMap; + + bool isTypeServerMap = false; + bool isPrecompiledTypeMap = false; +}; + class TpiSource { public: - enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB }; + enum TpiKind { Regular, PCH, UsingPCH, PDB, PDBIpi, UsingPDB }; TpiSource(TpiKind k, ObjFile *f); virtual ~TpiSource(); @@ -48,8 +61,8 @@ /// If the object does not use a type server PDB (compiled with /Z7), we merge /// all the type and item records from the .debug$S stream and fill in the /// caller-provided ObjectIndexMap. - virtual llvm::Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap); + virtual Error mergeDebugT(TypeMerger *m); + /// Is this a dependent file that needs to be processed first, before other /// OBJs? virtual bool isDependency() const { return false; } @@ -64,6 +77,11 @@ const TpiKind kind; ObjFile *file; + + // Storage for indexMap.tpiMap or ipiMap, depending on the kind of source. + llvm::SmallVector indexMapStorage; + + CVIndexMap indexMap; }; TpiSource *makeTpiSource(ObjFile *file); diff --git a/lld/COFF/DebugTypes.cpp b/lld/COFF/DebugTypes.cpp --- a/lld/COFF/DebugTypes.cpp +++ b/lld/COFF/DebugTypes.cpp @@ -29,6 +29,8 @@ using namespace lld::coff; namespace { +class TypeServerIpiSource; + // The TypeServerSource class represents a PDB type server, a file referenced by // OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ // files, therefore there must be only once instance per OBJ lot. The file path @@ -49,20 +51,36 @@ auto it = mappings.emplace(expectedInfo->getGuid(), this); assert(it.second); (void)it; - tsIndexMap.isTypeServerMap = true; + indexMap.isTypeServerMap = true; } - Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; + Error mergeDebugT(TypeMerger *m) override; bool isDependency() const override { return true; } PDBInputFile *pdbInputFile = nullptr; - CVIndexMap tsIndexMap; + // TpiSource for IPI stream. + TypeServerIpiSource *ipiSrc = nullptr; static std::map mappings; }; +// Companion to TypeServerSource. Stores the index map for the IPI stream in the +// PDB. Modeling PDBs with two sources for TPI and IPI helps establish the +// invariant of one type index space per source. +class TypeServerIpiSource : public TpiSource { +public: + explicit TypeServerIpiSource() : TpiSource(PDBIpi, nullptr) {} + + friend class TypeServerSource; + + // IPI merging is handled in TypeServerSource::mergeDebugT, since it depends + // directly on type merging. + Error mergeDebugT(TypeMerger *m) override { return Error::success(); } + + bool isDependency() const override { return true; } +}; + // This class represents the debug type stream of an OBJ file that depends on a // PDB type server (see TypeServerSource). class UseTypeServerSource : public TpiSource { @@ -70,8 +88,7 @@ UseTypeServerSource(ObjFile *f, TypeServer2Record ts) : TpiSource(UsingPDB, f), typeServerDependency(ts) {} - Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; + Error mergeDebugT(TypeMerger *m) override; // Information about the PDB type server dependency, that needs to be loaded // in before merging this OBJ. @@ -92,15 +109,11 @@ if (!it.second) fatal("a PCH object with the same signature has already been provided (" + toString(it.first->second->file) + " and " + toString(file) + ")"); - precompIndexMap.isPrecompiledTypeMap = true; + indexMap.isPrecompiledTypeMap = true; } - Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; bool isDependency() const override { return true; } - CVIndexMap precompIndexMap; - static std::map mappings; }; @@ -111,8 +124,7 @@ UsePrecompSource(ObjFile *f, PrecompRecord precomp) : TpiSource(UsingPCH, f), precompDependency(precomp) {} - Expected mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) override; + Error mergeDebugT(TypeMerger *m) override; // Information about the Precomp OBJ dependency, that needs to be loaded in // before merging this OBJ. @@ -134,7 +146,11 @@ } TpiSource *lld::coff::makeTypeServerSource(PDBInputFile *pdbInputFile) { - return make(pdbInputFile); + // Type server sources come in pairs: the TPI stream, and the IPI stream. + auto *tpiSource = make(pdbInputFile); + if (pdbInputFile->session->getPDBFile().hasPDBIpiStream()) + tpiSource->ipiSrc = make(); + return tpiSource; } TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file, @@ -196,8 +212,7 @@ } // Merge .debug$T for a generic object file. -Expected TpiSource::mergeDebugT(TypeMerger *m, - CVIndexMap *indexMap) { +Error TpiSource::mergeDebugT(TypeMerger *m) { CVTypeArray types; BinaryStreamReader reader(file->debugTypes, support::little); cantFail(reader.readArray(types, reader.getLength())); @@ -213,17 +228,18 @@ } if (auto err = mergeTypeAndIdRecords(m->globalIDTable, m->globalTypeTable, - indexMap->tpiMap, types, hashes, + indexMapStorage, types, hashes, file->pchSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(err))); } else { if (auto err = - mergeTypeAndIdRecords(m->idTable, m->typeTable, indexMap->tpiMap, + mergeTypeAndIdRecords(m->idTable, m->typeTable, indexMapStorage, types, file->pchSignature)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(err))); } + indexMap.tpiMap = indexMapStorage; if (config->showSummary) { // Count how many times we saw each type record in our input. This @@ -234,7 +250,7 @@ m->ipiCounts.resize(m->getIDTable().size()); uint32_t srcIdx = 0; for (CVType &ty : types) { - TypeIndex dstIdx = indexMap->tpiMap[srcIdx++]; + TypeIndex dstIdx = indexMap.tpiMap[srcIdx++]; // Type merging may fail, so a complex source type may become the simple // NotTranslated type, which cannot be used as an array index. if (dstIdx.isSimple()) @@ -245,12 +261,11 @@ } } - return indexMap; + return Error::success(); } // Merge types from a type server PDB. -Expected TypeServerSource::mergeDebugT(TypeMerger *m, - CVIndexMap *) { +Error TypeServerSource::mergeDebugT(TypeMerger *m) { pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile(); Expected expectedTpi = pdbFile.getPDBTpiStream(); if (auto e = expectedTpi.takeError()) @@ -273,30 +288,35 @@ Optional endPrecomp; // Merge TPI first, because the IPI stream will reference type indices. if (auto err = - mergeTypeRecords(m->globalTypeTable, tsIndexMap.tpiMap, + mergeTypeRecords(m->globalTypeTable, indexMapStorage, expectedTpi->typeArray(), tpiHashes, endPrecomp)) fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); + indexMap.tpiMap = indexMapStorage; // Merge IPI. if (maybeIpi) { auto ipiHashes = GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes); - if (auto err = mergeIdRecords(m->globalIDTable, tsIndexMap.tpiMap, - tsIndexMap.ipiMap, maybeIpi->typeArray(), - ipiHashes)) + if (auto err = mergeIdRecords(m->globalIDTable, indexMap.tpiMap, + ipiSrc->indexMapStorage, + maybeIpi->typeArray(), ipiHashes)) fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); + indexMap.ipiMap = ipiSrc->indexMapStorage; } } else { // Merge TPI first, because the IPI stream will reference type indices. - if (auto err = mergeTypeRecords(m->typeTable, tsIndexMap.tpiMap, + if (auto err = mergeTypeRecords(m->typeTable, indexMapStorage, expectedTpi->typeArray())) fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err))); + indexMap.tpiMap = indexMapStorage; // Merge IPI. if (maybeIpi) { - if (auto err = mergeIdRecords(m->idTable, tsIndexMap.tpiMap, - tsIndexMap.ipiMap, maybeIpi->typeArray())) + if (auto err = + mergeIdRecords(m->idTable, indexMap.tpiMap, + ipiSrc->indexMapStorage, maybeIpi->typeArray())) fatal("codeview::mergeIdRecords failed: " + toString(std::move(err))); + indexMap.ipiMap = ipiSrc->indexMapStorage; } } @@ -306,19 +326,18 @@ // map, that means we saw it once in the input. Add it to our histogram. m->tpiCounts.resize(m->getTypeTable().size()); m->ipiCounts.resize(m->getIDTable().size()); - for (TypeIndex ti : tsIndexMap.tpiMap) + for (TypeIndex ti : indexMap.tpiMap) if (!ti.isSimple()) ++m->tpiCounts[ti.toArrayIndex()]; - for (TypeIndex ti : tsIndexMap.ipiMap) + for (TypeIndex ti : indexMap.ipiMap) if (!ti.isSimple()) ++m->ipiCounts[ti.toArrayIndex()]; } - return &tsIndexMap; + return Error::success(); } -Expected -UseTypeServerSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) { +Error UseTypeServerSource::mergeDebugT(TypeMerger *m) { const codeview::GUID &tsId = typeServerDependency.getGuid(); StringRef tsPath = typeServerDependency.getName(); @@ -342,7 +361,7 @@ pdb::PDBFile &pdbSession = tsSrc->pdbInputFile->session->getPDBFile(); auto expectedInfo = pdbSession.getPDBInfoStream(); if (!expectedInfo) - return &tsSrc->tsIndexMap; + return expectedInfo.takeError(); // Just because a file with a matching name was found and it was an actual // PDB file doesn't mean it matches. For it to match the InfoStream's GUID @@ -352,7 +371,9 @@ tsPath, make_error(pdb::pdb_error_code::signature_out_of_date)); - return &tsSrc->tsIndexMap; + // Reuse the type index map of the type server. + indexMap = tsSrc->indexMap; + return Error::success(); } static bool equalsPath(StringRef path1, StringRef path2) { @@ -409,14 +430,15 @@ toString(precomp->file), make_error(pdb::pdb_error_code::no_matching_pch)); - return &precomp->precompIndexMap; + return &precomp->indexMap; } /// Merges a precompiled headers TPI map into the current TPI map. The /// precompiled headers object will also be loaded and remapped in the /// process. -static Expected -mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *indexMap, +static Error +mergeInPrecompHeaderObj(ObjFile *file, + SmallVectorImpl &indexMapStorage, PrecompRecord &precomp) { auto e = findPrecompMap(file, precomp); if (!e) @@ -426,36 +448,27 @@ assert(precompIndexMap->isPrecompiledTypeMap); if (precompIndexMap->tpiMap.empty()) - return precompIndexMap; + return Error::success(); assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex); assert(precomp.getTypesCount() <= precompIndexMap->tpiMap.size()); // Use the previously remapped index map from the precompiled headers. - indexMap->tpiMap.append(precompIndexMap->tpiMap.begin(), - precompIndexMap->tpiMap.begin() + - precomp.getTypesCount()); - return indexMap; + indexMapStorage.append(precompIndexMap->tpiMap.begin(), + precompIndexMap->tpiMap.begin() + + precomp.getTypesCount()); + return Error::success(); } -Expected -UsePrecompSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) { +Error UsePrecompSource::mergeDebugT(TypeMerger *m) { // This object was compiled with /Yu, so process the corresponding // precompiled headers object (/Yc) first. Some type indices in the current // object are referencing data in the precompiled headers object, so we need // both to be loaded. - auto e = mergeInPrecompHeaderObj(file, indexMap, precompDependency); - if (!e) - return e.takeError(); - - return TpiSource::mergeDebugT(m, indexMap); -} + if (Error e = + mergeInPrecompHeaderObj(file, indexMapStorage, precompDependency)) + return e; -Expected PrecompSource::mergeDebugT(TypeMerger *m, - CVIndexMap *) { - // Note that we're not using the provided CVIndexMap. Instead, we use our - // local one. Precompiled headers objects need to save the index map for - // further reference by other objects which use the precompiled headers. - return TpiSource::mergeDebugT(m, &precompIndexMap); + return TpiSource::mergeDebugT(m); } uint32_t TpiSource::countTypeServerPDBs() { diff --git a/lld/COFF/PDB.cpp b/lld/COFF/PDB.cpp --- a/lld/COFF/PDB.cpp +++ b/lld/COFF/PDB.cpp @@ -112,7 +112,7 @@ /// externally. void addDebug(TpiSource *source); - const CVIndexMap *mergeTypeRecords(TpiSource *source, CVIndexMap *localMap); + const CVIndexMap *mergeTypeRecords(TpiSource *source); void addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap); @@ -834,21 +834,18 @@ warn(msg); } -const CVIndexMap *PDBLinker::mergeTypeRecords(TpiSource *source, - CVIndexMap *localMap) { +const CVIndexMap *PDBLinker::mergeTypeRecords(TpiSource *source) { ScopedTimer t(typeMergingTimer); // Before we can process symbol substreams from .debug$S, we need to process // type information, file checksums, and the string table. Add type info to // the PDB first, so that we can get the map from object file type and item // indices to PDB type and item indices. - Expected r = source->mergeDebugT(&tMerger, localMap); - - // If the .debug$T sections fail to merge, assume there is no debug info. - if (!r) { - warnUnusable(source->file, r.takeError()); + if (Error e = source->mergeDebugT(&tMerger)) { + // If the .debug$T sections fail to merge, assume there is no debug info. + warnUnusable(source->file, std::move(e)); return nullptr; } - return *r; + return &source->indexMap; } // Allocate memory for a .debug$S / .debug$F section and relocate it. @@ -925,13 +922,11 @@ } void PDBLinker::addDebug(TpiSource *source) { - CVIndexMap localMap; - const CVIndexMap *indexMap = mergeTypeRecords(source, &localMap); - - if (source->kind == TpiSource::PDB) - return; // No symbols in TypeServer PDBs + const CVIndexMap *indexMap = mergeTypeRecords(source); - addDebugSymbols(source->file, indexMap); + // If the source has an object file, add its symbol records. + if (source->file) + addDebugSymbols(source->file, indexMap); } static pdb::BulkPublic createPublic(Defined *def) { diff --git a/lld/COFF/TypeMerger.h b/lld/COFF/TypeMerger.h --- a/lld/COFF/TypeMerger.h +++ b/lld/COFF/TypeMerger.h @@ -55,15 +55,6 @@ SmallVector ipiCounts; }; -/// Map from type index and item index in a type server PDB to the -/// corresponding index in the destination PDB. -struct CVIndexMap { - llvm::SmallVector tpiMap; - llvm::SmallVector ipiMap; - bool isTypeServerMap = false; - bool isPrecompiledTypeMap = false; -}; - } // namespace coff } // namespace lld