//===- TypeReferenceTracker.cpp ------------------------------- *- C++ --*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "TypeReferenceTracker.h"
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/Object/COFF.h"
using namespace llvm;
using namespace llvm::pdb;
using namespace llvm::codeview;
// LazyRandomTypeCollection doesn't appear to expose the number of records, so
// just iterate up front to find out.
static uint32_t getNumRecordsInCollection(LazyRandomTypeCollection &Types) {
uint32_t NumTypes = 0;
for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI))
++NumTypes;
return NumTypes;
}
TypeReferenceTracker::TypeReferenceTracker(InputFile &File)
: File(File), Types(File.types()),
Ids(File.isPdb() ? &File.ids() : nullptr) {
NumTypeRecords = getNumRecordsInCollection(Types);
TypeReferenced.resize(NumTypeRecords, false);
// If this is a PDB, ids are stored separately, so make a separate bit vector.
if (Ids) {
NumIdRecords = getNumRecordsInCollection(*Ids);
IdReferenced.resize(NumIdRecords, false);
}
// Get the TpiStream pointer for forward decl resolution if this is a pdb.
// Build the hash map to enable resolving forward decls.
if (File.isPdb()) {
Tpi = &cantFail(File.pdb().getPDBTpiStream());
Tpi->buildHashMap();
}
}
void TypeReferenceTracker::mark() {
// Walk type roots:
// - globals
// - modi symbols
// - LF_UDT_MOD_SRC_LINE? VC always links these in.
for (SymbolGroup SG : File.symbol_groups()) {
if (File.isObj()) {
for (const auto &SS : SG.getDebugSubsections()) {
// FIXME: Are there other type-referencing subsections? Inlinees?
// Probably for IDs.
if (SS.kind() != DebugSubsectionKind::Symbols)
continue;
CVSymbolArray Symbols;
BinaryStreamReader Reader(SS.getRecordData());
cantFail(Reader.readArray(Symbols, Reader.getLength()));
for (const CVSymbol &S : Symbols)
addTypeRefsFromSymbol(S);
}
} else if (SG.hasDebugStream()) {
for (const CVSymbol &S : SG.getPdbModuleStream().getSymbolArray())
addTypeRefsFromSymbol(S);
}
}
// Walk globals and mark types referenced from globals.
if (File.isPdb() && File.pdb().hasPDBGlobalsStream()) {
SymbolStream &SymStream = cantFail(File.pdb().getPDBSymbolStream());
GlobalsStream &GS = cantFail(File.pdb().getPDBGlobalsStream());
for (uint32_t PubSymOff : GS.getGlobalsTable()) {
CVSymbol Sym = SymStream.readRecord(PubSymOff);
addTypeRefsFromSymbol(Sym);
}
}
// FIXME: Should we walk Ids?
}
void TypeReferenceTracker::addOneTypeRef(TiRefKind RefKind, TypeIndex RefTI) {
// If it's simple or already seen, no need to add to work list.
BitVector &TypeOrIdReferenced =
(Ids && RefKind == TiRefKind::IndexRef) ? IdReferenced : TypeReferenced;
if (RefTI.isSimple() || TypeOrIdReferenced.test(RefTI.toArrayIndex()))
return;
// Otherwise, mark it seen and add it to the work list.
TypeOrIdReferenced.set(RefTI.toArrayIndex());
RefWorklist.push_back({RefKind, RefTI});
}
void TypeReferenceTracker::addTypeRefsFromSymbol(const CVSymbol &Sym) {
SmallVector<TiReference, 4> DepList;
// FIXME: Check for failure.
discoverTypeIndicesInSymbol(Sym, DepList);
addReferencedTypes(Sym.content(), DepList);
markReferencedTypes();
}
void TypeReferenceTracker::addReferencedTypes(ArrayRef<uint8_t> RecData,
ArrayRef<TiReference> DepList) {
for (const auto &Ref : DepList) {
// FIXME: Report OOB slice instead of truncating.
ArrayRef<uint8_t> ByteSlice =
RecData.drop_front(Ref.Offset).take_front(4 * Ref.Count);
ArrayRef<TypeIndex> TIs(
reinterpret_cast<const TypeIndex *>(ByteSlice.data()),
ByteSlice.size() / 4);
// If this is a PDB and this is an item reference, track it in the IPI
// bitvector. Otherwise, it's a type ref, or there is only one stream.
for (TypeIndex RefTI : TIs)
addOneTypeRef(Ref.Kind, RefTI);
}
}
void TypeReferenceTracker::markReferencedTypes() {
while (!RefWorklist.empty()) {
TiRefKind RefKind;
TypeIndex RefTI;
std::tie(RefKind, RefTI) = RefWorklist.pop_back_val();
Optional<CVType> Rec = (Ids && RefKind == TiRefKind::IndexRef)
? Ids->tryGetType(RefTI)
: Types.tryGetType(RefTI);
if (!Rec)
continue; // FIXME: Report a reference to a non-existant type.
SmallVector<TiReference, 4> DepList;
// FIXME: Check for failure.
discoverTypeIndices(*Rec, DepList);
addReferencedTypes(Rec->content(), DepList);
// If this is a tag kind and this is a PDB input, mark the complete type as
// referenced.
// FIXME: This limitation makes this feature somewhat useless on object file
// inputs.
if (Tpi) {
switch (Rec->kind()) {
default:
break;
case LF_CLASS:
case LF_INTERFACE:
case LF_STRUCTURE:
case LF_UNION:
case LF_ENUM:
addOneTypeRef(TiRefKind::TypeRef,
cantFail(Tpi->findFullDeclForForwardRef(RefTI)));
break;
}
}
}
}