[COFF] Teach LLD to use the COFF .debug$H section.

This adds the /DEBUG:GHASH option to LLD which will look for
the existence of .debug$H sections in linker inputs and use them
to accelerate type merging.  The clang-cl side has already been
added, so this completes the work necessary to begin experimenting
with this feature.

Differential Revision: https://reviews.llvm.org/D40980

llvm-svn: 320719
This commit is contained in:
Zachary Turner 2017-12-14 18:07:04 +00:00
parent e010be38fd
commit 3e534be355
5 changed files with 167 additions and 14 deletions

View File

@ -88,18 +88,39 @@ struct GloballyHashedType {
ArrayRef<GloballyHashedType> PreviousTypes,
ArrayRef<GloballyHashedType> PreviousIds);
/// Given a sequence of bytes representing a record, compute a global hash for
/// this record. Due to the nature of global hashes incorporating the hashes
/// of referenced records, this function requires a list of types and ids
/// that RecordData might reference, indexable by TypeIndex.
static GloballyHashedType hashType(CVType Type,
ArrayRef<GloballyHashedType> PreviousTypes,
ArrayRef<GloballyHashedType> PreviousIds) {
return hashType(Type.RecordData, PreviousTypes, PreviousIds);
}
/// Given a sequence of combined type and ID records, compute global hashes
/// for each of them, returning the results in a vector of hashed types.
template <typename Range>
static std::vector<GloballyHashedType> hashTypes(Range &&Records) {
std::vector<GloballyHashedType> Hashes;
Hashes.reserve(std::distance(std::begin(Records), std::end(Records)));
for (const auto &R : Records)
Hashes.push_back(hashType(R, Hashes, Hashes));
return Hashes;
}
/// Given a sequence of combined type and ID records, compute global hashes
/// for each of them, returning the results in a vector of hashed types.
template <typename Range>
static std::vector<GloballyHashedType>
hashIds(Range &&Records, ArrayRef<GloballyHashedType> TypeHashes) {
std::vector<GloballyHashedType> IdHashes;
for (const auto &R : Records)
IdHashes.push_back(hashType(R, TypeHashes, IdHashes));
return IdHashes;
}
static std::vector<GloballyHashedType>
hashTypeCollection(TypeCollection &Types) {
std::vector<GloballyHashedType> Hashes;
@ -109,6 +130,11 @@ struct GloballyHashedType {
return Hashes;
}
};
static_assert(std::is_trivially_copyable<GloballyHashedType>::value,
"GloballyHashedType must be trivially copyable so that we can "
"reinterpret_cast arrays of hash data to arrays of "
"GloballyHashedType");
} // namespace codeview
template <> struct DenseMapInfo<codeview::LocallyHashedType> {

View File

@ -19,6 +19,8 @@ namespace llvm {
namespace codeview {
class TypeIndex;
struct GloballyHashedType;
class GlobalTypeTableBuilder;
class MergingTypeTableBuilder;
/// \brief Merge one set of type records into another. This method assumes
@ -83,6 +85,22 @@ Error mergeTypeAndIdRecords(MergingTypeTableBuilder &DestIds,
SmallVectorImpl<TypeIndex> &SourceToDest,
const CVTypeArray &IdsAndTypes);
Error mergeTypeAndIdRecords(GlobalTypeTableBuilder &DestIds,
GlobalTypeTableBuilder &DestTypes,
SmallVectorImpl<TypeIndex> &SourceToDest,
const CVTypeArray &IdsAndTypes,
ArrayRef<GloballyHashedType> Hashes);
Error mergeTypeRecords(GlobalTypeTableBuilder &Dest,
SmallVectorImpl<TypeIndex> &SourceToDest,
const CVTypeArray &Types,
ArrayRef<GloballyHashedType> Hashes);
Error mergeIdRecords(GlobalTypeTableBuilder &Dest, ArrayRef<TypeIndex> Types,
SmallVectorImpl<TypeIndex> &SourceToDest,
const CVTypeArray &Ids,
ArrayRef<GloballyHashedType> Hashes);
} // end namespace codeview
} // end namespace llvm

View File

@ -743,6 +743,12 @@ struct coff_resource_dir_table {
support::ulittle16_t NumberOfIDEntries;
};
struct debug_h_header {
support::ulittle32_t Magic;
support::ulittle16_t Version;
support::ulittle16_t HashAlgorithm;
};
class COFFObjectFile : public ObjectFile {
private:
friend class ImportDirectoryEntryRef;

View File

@ -54,7 +54,7 @@ GloballyHashedType::hashType(ArrayRef<uint8_t> RecordData,
reinterpret_cast<const TypeIndex *>(RefData.data()), Ref.Count);
for (TypeIndex TI : Indices) {
ArrayRef<uint8_t> BytesToHash;
if (TI.isSimple() || TI.isNoneType()) {
if (TI.isSimple() || TI.isNoneType() || TI.toArrayIndex() >= Prev.size()) {
const uint8_t *IndexBytes = reinterpret_cast<const uint8_t *>(&TI);
BytesToHash = makeArrayRef(IndexBytes, sizeof(TypeIndex));
} else {

View File

@ -10,6 +10,7 @@
#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
@ -62,6 +63,7 @@ public:
static const TypeIndex Untranslated;
// Local hashing entry points
Error mergeTypesAndIds(MergingTypeTableBuilder &DestIds,
MergingTypeTableBuilder &DestTypes,
const CVTypeArray &IdsAndTypes);
@ -71,6 +73,18 @@ public:
Error mergeTypeRecords(MergingTypeTableBuilder &Dest,
const CVTypeArray &Types);
// Global hashing entry points
Error mergeTypesAndIds(GlobalTypeTableBuilder &DestIds,
GlobalTypeTableBuilder &DestTypes,
const CVTypeArray &IdsAndTypes,
ArrayRef<GloballyHashedType> Hashes);
Error mergeIdRecords(GlobalTypeTableBuilder &Dest,
ArrayRef<TypeIndex> TypeSourceToDest,
const CVTypeArray &Ids,
ArrayRef<GloballyHashedType> Hashes);
Error mergeTypeRecords(GlobalTypeTableBuilder &Dest, const CVTypeArray &Types,
ArrayRef<GloballyHashedType> Hashes);
private:
Error doit(const CVTypeArray &Types);
@ -83,6 +97,14 @@ private:
bool remapTypeIndex(TypeIndex &Idx);
bool remapItemIndex(TypeIndex &Idx);
bool hasTypeStream() const {
return (UseGlobalHashes) ? (!!DestGlobalTypeStream) : (!!DestTypeStream);
}
bool hasIdStream() const {
return (UseGlobalHashes) ? (!!DestGlobalIdStream) : (!!DestIdStream);
}
ArrayRef<uint8_t> serializeRemapped(const RemappedType &Record);
bool remapIndices(RemappedType &Record, ArrayRef<TiReference> Refs);
@ -100,6 +122,8 @@ private:
Optional<Error> LastError;
bool UseGlobalHashes = false;
bool IsSecondPass = false;
unsigned NumBadIndices = 0;
@ -109,6 +133,11 @@ private:
MergingTypeTableBuilder *DestIdStream = nullptr;
MergingTypeTableBuilder *DestTypeStream = nullptr;
GlobalTypeTableBuilder *DestGlobalIdStream = nullptr;
GlobalTypeTableBuilder *DestGlobalTypeStream = nullptr;
ArrayRef<GloballyHashedType> GlobalHashes;
// If we're only mapping id records, this array contains the mapping for
// type records.
ArrayRef<TypeIndex> TypeLookup;
@ -209,7 +238,7 @@ bool TypeStreamMerger::remapTypeIndex(TypeIndex &Idx) {
// special mapping from OldTypeStream -> NewTypeStream which was computed
// externally. Regardless, we use this special map if and only if we are
// doing an id-only mapping.
if (DestTypeStream == nullptr)
if (!hasTypeStream())
return remapIndex(Idx, TypeLookup);
assert(TypeLookup.empty());
@ -217,13 +246,15 @@ bool TypeStreamMerger::remapTypeIndex(TypeIndex &Idx) {
}
bool TypeStreamMerger::remapItemIndex(TypeIndex &Idx) {
assert(DestIdStream);
assert(hasIdStream());
return remapIndex(Idx, IndexMap);
}
// Local hashing entry points
Error TypeStreamMerger::mergeTypeRecords(MergingTypeTableBuilder &Dest,
const CVTypeArray &Types) {
DestTypeStream = &Dest;
UseGlobalHashes = false;
return doit(Types);
}
@ -233,6 +264,7 @@ Error TypeStreamMerger::mergeIdRecords(MergingTypeTableBuilder &Dest,
const CVTypeArray &Ids) {
DestIdStream = &Dest;
TypeLookup = TypeSourceToDest;
UseGlobalHashes = false;
return doit(Ids);
}
@ -242,6 +274,41 @@ Error TypeStreamMerger::mergeTypesAndIds(MergingTypeTableBuilder &DestIds,
const CVTypeArray &IdsAndTypes) {
DestIdStream = &DestIds;
DestTypeStream = &DestTypes;
UseGlobalHashes = false;
return doit(IdsAndTypes);
}
// Global hashing entry points
Error TypeStreamMerger::mergeTypeRecords(GlobalTypeTableBuilder &Dest,
const CVTypeArray &Types,
ArrayRef<GloballyHashedType> Hashes) {
DestGlobalTypeStream = &Dest;
UseGlobalHashes = true;
GlobalHashes = Hashes;
return doit(Types);
}
Error TypeStreamMerger::mergeIdRecords(GlobalTypeTableBuilder &Dest,
ArrayRef<TypeIndex> TypeSourceToDest,
const CVTypeArray &Ids,
ArrayRef<GloballyHashedType> Hashes) {
DestGlobalIdStream = &Dest;
TypeLookup = TypeSourceToDest;
UseGlobalHashes = true;
GlobalHashes = Hashes;
return doit(Ids);
}
Error TypeStreamMerger::mergeTypesAndIds(GlobalTypeTableBuilder &DestIds,
GlobalTypeTableBuilder &DestTypes,
const CVTypeArray &IdsAndTypes,
ArrayRef<GloballyHashedType> Hashes) {
DestGlobalIdStream = &DestIds;
DestGlobalTypeStream = &DestTypes;
UseGlobalHashes = true;
GlobalHashes = Hashes;
return doit(IdsAndTypes);
}
@ -286,18 +353,29 @@ Error TypeStreamMerger::remapAllTypes(const CVTypeArray &Types) {
}
Error TypeStreamMerger::remapType(const CVType &Type) {
MergingTypeTableBuilder &Dest =
isIdRecord(Type.kind()) ? *DestIdStream : *DestTypeStream;
RemappedType R(Type);
SmallVector<TiReference, 32> Refs;
discoverTypeIndices(Type.RecordData, Refs);
bool MappedAllIndices = remapIndices(R, Refs);
ArrayRef<uint8_t> Data = serializeRemapped(R);
auto DoSerialize = [this, Type]() -> ArrayRef<uint8_t> {
RemappedType R(Type);
SmallVector<TiReference, 32> Refs;
discoverTypeIndices(Type.RecordData, Refs);
if (!remapIndices(R, Refs))
return {};
return serializeRemapped(R);
};
TypeIndex DestIdx = Untranslated;
if (MappedAllIndices)
DestIdx = Dest.insertRecordBytes(Data);
if (UseGlobalHashes) {
GlobalTypeTableBuilder &Dest =
isIdRecord(Type.kind()) ? *DestGlobalIdStream : *DestGlobalTypeStream;
GloballyHashedType H = GlobalHashes[CurIndex.toArrayIndex()];
DestIdx = Dest.insertRecordAs(H, DoSerialize);
} else {
MergingTypeTableBuilder &Dest =
isIdRecord(Type.kind()) ? *DestIdStream : *DestTypeStream;
auto Data = DoSerialize();
if (!Data.empty())
DestIdx = Dest.insertRecordBytes(Data);
}
addMapping(DestIdx);
++CurIndex;
@ -350,3 +428,28 @@ Error llvm::codeview::mergeTypeAndIdRecords(
TypeStreamMerger M(SourceToDest);
return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes);
}
Error llvm::codeview::mergeTypeAndIdRecords(
GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes,
SmallVectorImpl<TypeIndex> &SourceToDest, const CVTypeArray &IdsAndTypes,
ArrayRef<GloballyHashedType> Hashes) {
TypeStreamMerger M(SourceToDest);
return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, Hashes);
}
Error llvm::codeview::mergeTypeRecords(GlobalTypeTableBuilder &Dest,
SmallVectorImpl<TypeIndex> &SourceToDest,
const CVTypeArray &Types,
ArrayRef<GloballyHashedType> Hashes) {
TypeStreamMerger M(SourceToDest);
return M.mergeTypeRecords(Dest, Types, Hashes);
}
Error llvm::codeview::mergeIdRecords(GlobalTypeTableBuilder &Dest,
ArrayRef<TypeIndex> Types,
SmallVectorImpl<TypeIndex> &SourceToDest,
const CVTypeArray &Ids,
ArrayRef<GloballyHashedType> Hashes) {
TypeStreamMerger M(SourceToDest);
return M.mergeIdRecords(Dest, Types, Ids, Hashes);
}