1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 02:52:53 +02:00

[pdb] Write PDB TPI Stream from Yaml.

This writes the full sequence of type records described in
Yaml to the TPI stream of the PDB file.

Reviewed By: rnk
Differential Revision: https://reviews.llvm.org/D24316

llvm-svn: 281063
This commit is contained in:
Zachary Turner 2016-09-09 17:46:17 +00:00
parent 7259fc9dc4
commit c2876ae1eb
30 changed files with 630 additions and 143 deletions

View File

@ -20,7 +20,8 @@ namespace codeview {
class MemoryTypeTableBuilder : public TypeTableBuilder {
public:
MemoryTypeTableBuilder() {}
explicit MemoryTypeTableBuilder(BumpPtrAllocator &Allocator)
: RecordStorage(Allocator) {}
bool empty() const { return Records.empty(); }
@ -33,12 +34,13 @@ public:
}
}
protected:
TypeIndex writeRecord(llvm::StringRef Data) override;
ArrayRef<StringRef> getRecords() const { return Records; }
private:
std::vector<StringRef> Records;
BumpPtrAllocator RecordStorage;
BumpPtrAllocator &RecordStorage;
DenseMap<StringRef, TypeIndex> HashedRecords;
};

View File

@ -47,6 +47,7 @@ public:
llvm::StringRef str();
uint64_t size() const { return Stream.tell(); }
TypeRecordKind kind() const { return Kind; }
void truncate(uint64_t Size) {
// This works because raw_svector_ostream is not buffered.
@ -56,10 +57,12 @@ public:
void reset(TypeRecordKind K) {
Buffer.clear();
Kind = K;
writeTypeRecordKind(K);
}
private:
TypeRecordKind Kind;
llvm::SmallVector<char, 256> Buffer;
llvm::raw_svector_ostream Stream;
llvm::support::endian::Writer<llvm::support::endianness::little> Writer;

View File

@ -0,0 +1,74 @@
//===- TypeSerializationVisitor.h -------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZATIONVISITOR_H
#define LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZATIONVISITOR_H
#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h"
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
#include "llvm/ADT/StringRef.h"
namespace llvm {
namespace codeview {
class TypeSerializationVisitor : public TypeVisitorCallbacks {
public:
TypeSerializationVisitor(FieldListRecordBuilder &FieldListBuilder,
MemoryTypeTableBuilder &TypeTableBuilder)
: FieldListBuilder(FieldListBuilder), TypeTableBuilder(TypeTableBuilder) {
}
virtual Expected<TypeLeafKind> visitTypeBegin(const CVType &Record) override {
if (Record.Type == TypeLeafKind::LF_FIELDLIST)
FieldListBuilder.reset();
return Record.Type;
}
virtual Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) override {
if (Record.Type == TypeLeafKind::LF_FIELDLIST)
TypeTableBuilder.writeFieldList(FieldListBuilder);
return Error::success();
}
#define TYPE_RECORD(EnumName, EnumVal, Name) \
virtual Error visitKnownRecord(const CVRecord<TypeLeafKind> &CVR, \
Name##Record &Record) override { \
visitKnownRecordImpl(Record); \
return Error::success(); \
}
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
virtual Error visitKnownRecord(const CVRecord<TypeLeafKind> &CVR, \
Name##Record &Record) override { \
visitMemberRecordImpl(Record); \
return Error::success(); \
}
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
private:
template <typename RecordKind> void visitKnownRecordImpl(RecordKind &Record) {
TypeTableBuilder.writeKnownType(Record);
}
template <typename RecordKind>
void visitMemberRecordImpl(RecordKind &Record) {
FieldListBuilder.writeMemberType(Record);
}
void visitKnownRecordImpl(FieldListRecord &FieldList) {}
FieldListRecordBuilder &FieldListBuilder;
MemoryTypeTableBuilder &TypeTableBuilder;
};
}
}
#endif

View File

@ -63,6 +63,11 @@ public:
TypeIndex writeRecord(TypeRecordBuilder &builder);
virtual TypeIndex writeRecord(llvm::StringRef record) = 0;
ArrayRef<TypeRecordKind> getRecordKinds() const { return RecordKinds; }
private:
std::vector<TypeRecordKind> RecordKinds;
};
}
}

View File

@ -0,0 +1,91 @@
//===- SequencedItemStream.h ------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_DEBUGINFO_MSF_SEQUENCEDITEMSTREAM_H
#define LLVM_DEBUGINFO_MSF_SEQUENCEDITEMSTREAM_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/DebugInfo/MSF/MSFError.h"
#include "llvm/DebugInfo/MSF/StreamInterface.h"
#include "llvm/Support/Error.h"
#include <cstdint>
#include <memory>
#include <type_traits>
namespace llvm {
namespace msf {
template <typename T> struct SequencedItemTraits {
static size_t length(const T &Item) = delete;
static ArrayRef<uint8_t> bytes(const T &Item) = delete;
};
/// SequencedItemStream represents a sequence of objects stored in a
/// standard container but for which it is useful to view as a stream of
/// contiguous bytes. An example of this might be if you have a std::vector
/// of TPI records, where each record contains a byte sequence that
/// represents that one record serialized, but where each consecutive item
/// might not be allocated immediately after the previous item. Using a
/// SequencedItemStream, we can adapt the VarStreamArray class to trivially
/// extract one item at a time, allowing the data to be used anywhere a
/// VarStreamArray could be used.
template <typename T, typename Traits = SequencedItemTraits<T>>
class SequencedItemStream : public ReadableStream {
public:
SequencedItemStream() {}
Error readBytes(uint32_t Offset, uint32_t Size,
ArrayRef<uint8_t> &Buffer) const override {
auto ExpectedIndex = translateOffsetIndex(Offset);
if (!ExpectedIndex)
return ExpectedIndex.takeError();
const auto &Item = Items[*ExpectedIndex];
if (Size > Traits::length(Item))
return make_error<MSFError>(msf_error_code::insufficient_buffer);
Buffer = Traits::bytes(Item).take_front(Size);
return Error::success();
}
Error readLongestContiguousChunk(uint32_t Offset,
ArrayRef<uint8_t> &Buffer) const override {
auto ExpectedIndex = translateOffsetIndex(Offset);
if (!ExpectedIndex)
return ExpectedIndex.takeError();
Buffer = Traits::bytes(Items[*ExpectedIndex]);
return Error::success();
}
void setItems(ArrayRef<T> ItemArray) { Items = ItemArray; }
uint32_t getLength() const override {
uint32_t Size = 0;
for (const auto &Item : Items)
Size += Traits::length(Item);
return Size;
}
private:
Expected<uint32_t> translateOffsetIndex(uint32_t Offset) const {
uint32_t CurrentOffset = 0;
uint32_t CurrentIndex = 0;
for (const auto &Item : Items) {
if (CurrentOffset >= Offset)
break;
CurrentOffset += Traits::length(Item);
++CurrentIndex;
}
if (CurrentOffset != Offset)
return make_error<MSFError>(msf_error_code::insufficient_buffer);
return CurrentIndex;
}
ArrayRef<T> Items;
};
} // end namespace msf
} // end namespace llvm
#endif // LLVM_DEBUGINFO_MSF_SEQUENCEDITEMSTREAM_H

View File

@ -10,6 +10,7 @@
#ifndef LLVM_DEBUGINFO_MSF_STREAMARRAY_H
#define LLVM_DEBUGINFO_MSF_STREAMARRAY_H
#include "llvm/DebugInfo/MSF/SequencedItemStream.h"
#include "llvm/DebugInfo/MSF/StreamRef.h"
#include "llvm/Support/Error.h"

View File

@ -92,6 +92,8 @@ public:
Expected<SymbolStream &> getPDBSymbolStream();
Expected<NameHashTable &> getStringTable();
BumpPtrAllocator &getAllocator() { return Allocator; }
private:
BumpPtrAllocator &Allocator;

View File

@ -28,6 +28,7 @@ class MSFBuilder;
namespace pdb {
class DbiStreamBuilder;
class InfoStreamBuilder;
class TpiStreamBuilder;
class PDBFileBuilder {
public:
@ -40,6 +41,7 @@ public:
msf::MSFBuilder &getMsfBuilder();
InfoStreamBuilder &getInfoBuilder();
DbiStreamBuilder &getDbiBuilder();
TpiStreamBuilder &getTpiBuilder();
Expected<std::unique_ptr<PDBFile>>
build(std::unique_ptr<msf::WritableStream> PdbFileBuffer);
@ -54,6 +56,7 @@ private:
std::unique_ptr<msf::MSFBuilder> Msf;
std::unique_ptr<InfoStreamBuilder> Info;
std::unique_ptr<DbiStreamBuilder> Dbi;
std::unique_ptr<TpiStreamBuilder> Tpi;
};
}
}

View File

@ -266,6 +266,34 @@ struct PDB_UniqueId {
char Guid[16];
};
// The header preceeding the global TPI stream.
// This corresponds to `HDR` in PDB/dbi/tpi.h.
struct TpiStreamHeader {
struct EmbeddedBuf {
support::little32_t Off;
support::ulittle32_t Length;
};
support::ulittle32_t Version;
support::ulittle32_t HeaderSize;
support::ulittle32_t TypeIndexBegin;
support::ulittle32_t TypeIndexEnd;
support::ulittle32_t TypeRecordBytes;
// The following members correspond to `TpiHash` in PDB/dbi/tpi.h.
support::ulittle16_t HashStreamIndex;
support::ulittle16_t HashAuxStreamIndex;
support::ulittle32_t HashKeySize;
support::ulittle32_t NumHashBuckets;
EmbeddedBuf HashValueBuffer;
EmbeddedBuf IndexOffsetBuffer;
EmbeddedBuf HashAdjBuffer;
};
const uint32_t MinTpiHashBuckets = 0x1000;
const uint32_t MaxTpiHashBuckets = 0x40000;
/// The header preceeding the global PDB Stream (Stream 1)
struct InfoStreamHeader {
support::ulittle32_t Version;

View File

@ -27,7 +27,7 @@ namespace pdb {
class PDBFile;
class TpiStream {
struct HeaderInfo;
friend class TpiStreamBuilder;
public:
TpiStream(const PDBFile &File,
@ -66,7 +66,7 @@ private:
msf::FixedStreamArray<TypeIndexOffset> TypeIndexOffsets;
msf::FixedStreamArray<TypeIndexOffset> HashAdjustments;
const HeaderInfo *Header;
const TpiStreamHeader *Header;
};
}
}

View File

@ -0,0 +1,76 @@
//===- TpiStreamBuilder.h - PDB Tpi Stream Creation -------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_DEBUGINFO_PDB_RAW_PDBTPISTREAMBUILDER_H
#define LLVM_DEBUGINFO_PDB_RAW_PDBTPISTREAMBUILDER_H
#include "llvm/ADT/Optional.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/MSF/ByteStream.h"
#include "llvm/DebugInfo/MSF/SequencedItemStream.h"
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Error.h"
#include <vector>
namespace llvm {
namespace codeview {
class TypeRecord;
}
namespace msf {
struct MSFLayout;
class ReadableStreamRef;
class WritableStream;
template <> struct SequencedItemTraits<llvm::codeview::CVType> {
static size_t length(const codeview::CVType &Item) { return Item.Length; }
static ArrayRef<uint8_t> bytes(const codeview::CVType &Item) {
return Item.RawData;
}
};
}
namespace pdb {
class PDBFile;
class TpiStream;
struct TpiStreamHeader;
class TpiStreamBuilder {
public:
explicit TpiStreamBuilder(BumpPtrAllocator &Allocator);
~TpiStreamBuilder();
TpiStreamBuilder(const TpiStreamBuilder &) = delete;
TpiStreamBuilder &operator=(const TpiStreamBuilder &) = delete;
void setVersionHeader(PdbRaw_TpiVer Version);
void addTypeRecord(const codeview::CVType &Record);
Expected<std::unique_ptr<TpiStream>> build(PDBFile &File,
const msf::WritableStream &Buffer);
Error commit(const msf::MSFLayout &Layout, const msf::WritableStream &Buffer);
uint32_t calculateSerializedLength() const;
private:
Error finalize();
BumpPtrAllocator &Allocator;
Optional<PdbRaw_TpiVer> VerHeader;
std::vector<codeview::CVType> TypeRecords;
msf::SequencedItemStream<codeview::CVType> TypeRecordStream;
const TpiStreamHeader *Header;
};
}
}
#endif

View File

@ -40,7 +40,8 @@ using namespace llvm::codeview;
using namespace llvm::msf;
CodeViewDebug::CodeViewDebug(AsmPrinter *AP)
: DebugHandlerBase(AP), OS(*Asm->OutStreamer), CurFn(nullptr) {
: DebugHandlerBase(AP), OS(*Asm->OutStreamer), Allocator(),
TypeTable(Allocator), CurFn(nullptr) {
// If module doesn't have named metadata anchors or COFF debug section
// is not available, skip any debug info related stuff.
if (!MMI->getModule()->getNamedMetadata("llvm.dbg.cu") ||

View File

@ -36,6 +36,7 @@ struct ClassInfo;
/// \brief Collects and handles line tables information in a CodeView format.
class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
MCStreamer &OS;
llvm::BumpPtrAllocator Allocator;
codeview::MemoryTypeTableBuilder TypeTable;
/// Represents the most general definition range.

View File

@ -81,14 +81,17 @@ Error CVTypeVisitor::visitTypeRecord(const CVRecord<TypeLeafKind> &Record) {
else
return ExpectedKind.takeError();
CVType RecordCopy = Record;
RecordCopy.Type = Kind;
switch (Kind) {
default:
if (auto EC = Callbacks.visitUnknownType(Record))
if (auto EC = Callbacks.visitUnknownType(RecordCopy))
return EC;
break;
#define TYPE_RECORD(EnumName, EnumVal, Name) \
case EnumName: { \
if (auto EC = visitKnownRecord<Name##Record>(Record, Callbacks)) \
if (auto EC = visitKnownRecord<Name##Record>(RecordCopy, Callbacks)) \
return EC; \
break; \
}
@ -101,7 +104,7 @@ Error CVTypeVisitor::visitTypeRecord(const CVRecord<TypeLeafKind> &Record) {
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
}
if (auto EC = Callbacks.visitTypeEnd(Record))
if (auto EC = Callbacks.visitTypeEnd(RecordCopy))
return EC;
return Error::success();

View File

@ -13,7 +13,7 @@ using namespace llvm;
using namespace codeview;
TypeRecordBuilder::TypeRecordBuilder(TypeRecordKind Kind)
: Stream(Buffer), Writer(Stream) {
: Kind(Kind), Stream(Buffer), Writer(Stream) {
writeTypeRecordKind(Kind);
}

View File

@ -257,7 +257,9 @@ TypeIndex TypeTableBuilder::writeKnownType(const BuildInfoRecord &Record) {
}
TypeIndex TypeTableBuilder::writeRecord(TypeRecordBuilder &Builder) {
return writeRecord(Builder.str());
TypeIndex I = writeRecord(Builder.str());
RecordKinds.push_back(Builder.kind());
return I;
}
TypeIndex TypeTableBuilder::writeFieldList(FieldListRecordBuilder &FieldList) {

View File

@ -45,7 +45,8 @@ add_pdb_impl_folder(Raw
Raw/RawError.cpp
Raw/RawSession.cpp
Raw/SymbolStream.cpp
Raw/TpiStream.cpp)
Raw/TpiStream.cpp
Raw/TpiStreamBuilder.cpp)
list(APPEND LIBPDB_ADDITIONAL_HEADER_DIRS "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/PDB/Raw")
list(APPEND LIBPDB_ADDITIONAL_HEADER_DIRS "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/PDB")

View File

@ -19,6 +19,8 @@
#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h"
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h"
using namespace llvm;
using namespace llvm::codeview;
@ -58,6 +60,12 @@ DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() {
return *Dbi;
}
TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() {
if (!Tpi)
Tpi = llvm::make_unique<TpiStreamBuilder>(Allocator);
return *Tpi;
}
Expected<msf::MSFLayout> PDBFileBuilder::finalizeMsfLayout() const {
if (Info) {
uint32_t Length = Info->calculateSerializedLength();
@ -69,6 +77,11 @@ Expected<msf::MSFLayout> PDBFileBuilder::finalizeMsfLayout() const {
if (auto EC = Msf->setStreamSize(StreamDBI, Length))
return std::move(EC);
}
if (Tpi) {
uint32_t Length = Tpi->calculateSerializedLength();
if (auto EC = Msf->setStreamSize(StreamTPI, Length))
return std::move(EC);
}
return Msf->build();
}
@ -96,6 +109,13 @@ PDBFileBuilder::build(std::unique_ptr<msf::WritableStream> PdbFileBuffer) {
File->Dbi = std::move(*ExpectedDbi);
}
if (Tpi) {
auto ExpectedTpi = Tpi->build(*File, *PdbFileBuffer);
if (!ExpectedTpi)
return ExpectedTpi.takeError();
File->Tpi = std::move(*ExpectedTpi);
}
if (File->Info && File->Dbi && File->Info->getAge() != File->Dbi->getAge())
return llvm::make_error<RawError>(
raw_error_code::corrupt_file,
@ -144,5 +164,10 @@ Error PDBFileBuilder::commit(const msf::WritableStream &Buffer) {
return EC;
}
if (Tpi) {
if (auto EC = Tpi->commit(Layout, Buffer))
return EC;
}
return Buffer.commit();
}

View File

@ -31,35 +31,6 @@ using namespace llvm::support;
using namespace llvm::msf;
using namespace llvm::pdb;
namespace {
const uint32_t MinHashBuckets = 0x1000;
const uint32_t MaxHashBuckets = 0x40000;
}
// This corresponds to `HDR` in PDB/dbi/tpi.h.
struct TpiStream::HeaderInfo {
struct EmbeddedBuf {
little32_t Off;
ulittle32_t Length;
};
ulittle32_t Version;
ulittle32_t HeaderSize;
ulittle32_t TypeIndexBegin;
ulittle32_t TypeIndexEnd;
ulittle32_t TypeRecordBytes;
// The following members correspond to `TpiHash` in PDB/dbi/tpi.h.
ulittle16_t HashStreamIndex;
ulittle16_t HashAuxStreamIndex;
ulittle32_t HashKeySize;
ulittle32_t NumHashBuckets;
EmbeddedBuf HashValueBuffer;
EmbeddedBuf IndexOffsetBuffer;
EmbeddedBuf HashAdjBuffer;
};
TpiStream::TpiStream(const PDBFile &File,
std::unique_ptr<MappedBlockStream> Stream)
: Pdb(File), Stream(std::move(Stream)) {}
@ -175,7 +146,7 @@ Error TpiStream::verifyHashValues() {
Error TpiStream::reload() {
StreamReader Reader(*Stream);
if (Reader.bytesRemaining() < sizeof(HeaderInfo))
if (Reader.bytesRemaining() < sizeof(TpiStreamHeader))
return make_error<RawError>(raw_error_code::corrupt_file,
"TPI Stream does not contain a header.");
@ -187,7 +158,7 @@ Error TpiStream::reload() {
return make_error<RawError>(raw_error_code::corrupt_file,
"Unsupported TPI Version.");
if (Header->HeaderSize != sizeof(HeaderInfo))
if (Header->HeaderSize != sizeof(TpiStreamHeader))
return make_error<RawError>(raw_error_code::corrupt_file,
"Corrupt TPI Header size.");
@ -195,8 +166,8 @@ Error TpiStream::reload() {
return make_error<RawError>(raw_error_code::corrupt_file,
"TPI Stream expected 4 byte hash key size.");
if (Header->NumHashBuckets < MinHashBuckets ||
Header->NumHashBuckets > MaxHashBuckets)
if (Header->NumHashBuckets < MinTpiHashBuckets ||
Header->NumHashBuckets > MaxTpiHashBuckets)
return make_error<RawError>(raw_error_code::corrupt_file,
"TPI Stream Invalid number of hash buckets.");
@ -205,40 +176,44 @@ Error TpiStream::reload() {
return EC;
// Hash indices, hash values, etc come from the hash stream.
if (Header->HashStreamIndex >= Pdb.getNumStreams())
return make_error<RawError>(raw_error_code::corrupt_file,
"Invalid TPI hash stream index.");
auto HS = MappedBlockStream::createIndexedStream(
Pdb.getMsfLayout(), Pdb.getMsfBuffer(), Header->HashStreamIndex);
StreamReader HSR(*HS);
if (Header->HashStreamIndex != kInvalidStreamIndex) {
if (Header->HashStreamIndex >= Pdb.getNumStreams())
return make_error<RawError>(raw_error_code::corrupt_file,
"Invalid TPI hash stream index.");
uint32_t NumHashValues = Header->HashValueBuffer.Length / sizeof(ulittle32_t);
if (NumHashValues != NumTypeRecords())
return make_error<RawError>(
raw_error_code::corrupt_file,
"TPI hash count does not match with the number of type records.");
HSR.setOffset(Header->HashValueBuffer.Off);
if (auto EC = HSR.readArray(HashValues, NumHashValues))
return EC;
auto HS = MappedBlockStream::createIndexedStream(
Pdb.getMsfLayout(), Pdb.getMsfBuffer(), Header->HashStreamIndex);
StreamReader HSR(*HS);
HSR.setOffset(Header->IndexOffsetBuffer.Off);
uint32_t NumTypeIndexOffsets =
Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset);
if (auto EC = HSR.readArray(TypeIndexOffsets, NumTypeIndexOffsets))
return EC;
uint32_t NumHashValues =
Header->HashValueBuffer.Length / sizeof(ulittle32_t);
if (NumHashValues != NumTypeRecords())
return make_error<RawError>(
raw_error_code::corrupt_file,
"TPI hash count does not match with the number of type records.");
HSR.setOffset(Header->HashValueBuffer.Off);
if (auto EC = HSR.readArray(HashValues, NumHashValues))
return EC;
HSR.setOffset(Header->HashAdjBuffer.Off);
uint32_t NumHashAdjustments =
Header->HashAdjBuffer.Length / sizeof(TypeIndexOffset);
if (auto EC = HSR.readArray(HashAdjustments, NumHashAdjustments))
return EC;
HSR.setOffset(Header->IndexOffsetBuffer.Off);
uint32_t NumTypeIndexOffsets =
Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset);
if (auto EC = HSR.readArray(TypeIndexOffsets, NumTypeIndexOffsets))
return EC;
HashStream = std::move(HS);
HSR.setOffset(Header->HashAdjBuffer.Off);
uint32_t NumHashAdjustments =
Header->HashAdjBuffer.Length / sizeof(TypeIndexOffset);
if (auto EC = HSR.readArray(HashAdjustments, NumHashAdjustments))
return EC;
// TPI hash table is a parallel array for the type records.
// Verify that the hash values match with type records.
if (auto EC = verifyHashValues())
return EC;
HashStream = std::move(HS);
// TPI hash table is a parallel array for the type records.
// Verify that the hash values match with type records.
if (auto EC = verifyHashValues())
return EC;
}
return Error::success();
}

View File

@ -0,0 +1,95 @@
#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h"
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
#include "llvm/DebugInfo/MSF/StreamWriter.h"
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
#include "llvm/Support/Allocator.h"
using namespace llvm;
using namespace llvm::msf;
using namespace llvm::pdb;
using namespace llvm::support;
TpiStreamBuilder::TpiStreamBuilder(BumpPtrAllocator &Allocator)
: Allocator(Allocator), Header(nullptr) {}
TpiStreamBuilder::~TpiStreamBuilder() {}
void TpiStreamBuilder::setVersionHeader(PdbRaw_TpiVer Version) {
VerHeader = Version;
}
void TpiStreamBuilder::addTypeRecord(const codeview::CVType &Record) {
TypeRecords.push_back(Record);
TypeRecordStream.setItems(TypeRecords);
}
Error TpiStreamBuilder::finalize() {
if (Header)
return Error::success();
TpiStreamHeader *H = Allocator.Allocate<TpiStreamHeader>();
uint32_t Count = TypeRecords.size();
H->Version = *VerHeader;
H->HeaderSize = sizeof(TpiStreamHeader);
H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex;
H->TypeIndexEnd = H->TypeIndexBegin + Count;
H->TypeRecordBytes = TypeRecordStream.getLength();
H->HashStreamIndex = kInvalidStreamIndex;
H->HashAuxStreamIndex = kInvalidStreamIndex;
H->HashKeySize = sizeof(ulittle32_t);
H->NumHashBuckets = MinTpiHashBuckets;
H->HashValueBuffer.Length = 0;
H->HashAdjBuffer.Length = 0;
H->IndexOffsetBuffer.Length = 0;
Header = H;
return Error::success();
}
uint32_t TpiStreamBuilder::calculateSerializedLength() const {
return sizeof(TpiStreamHeader) + TypeRecordStream.getLength();
}
Expected<std::unique_ptr<TpiStream>>
TpiStreamBuilder::build(PDBFile &File, const msf::WritableStream &Buffer) {
if (!VerHeader.hasValue())
return make_error<RawError>(raw_error_code::unspecified,
"Missing TPI Stream Version");
if (auto EC = finalize())
return std::move(EC);
auto StreamData = MappedBlockStream::createIndexedStream(File.getMsfLayout(),
Buffer, StreamTPI);
auto Tpi = llvm::make_unique<TpiStream>(File, std::move(StreamData));
Tpi->Header = Header;
Tpi->TypeRecords = VarStreamArray<codeview::CVType>(TypeRecordStream);
return std::move(Tpi);
}
Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout,
const msf::WritableStream &Buffer) {
if (auto EC = finalize())
return EC;
auto InfoS =
WritableMappedBlockStream::createIndexedStream(Layout, Buffer, StreamTPI);
StreamWriter Writer(*InfoS);
if (auto EC = Writer.writeObject(*Header))
return EC;
auto RecordArray = VarStreamArray<codeview::CVType>(TypeRecordStream);
if (auto EC = Writer.writeArray(RecordArray))
return EC;
return Error::success();
}

View File

@ -1,10 +1,10 @@
RUN: llvm-pdbdump pdb2yaml -dbi-module-info -dbi-module-source-info \
RUN: -dbi-stream -pdb-stream -stream-directory -stream-metadata \
RUN: %p/Inputs/empty.pdb > %t.1
RUN: -dbi-stream -pdb-stream -tpi-stream -stream-directory \
RUN: -stream-metadata %p/Inputs/empty.pdb > %t.1
RUN: llvm-pdbdump yaml2pdb -pdb=%t.2 %t.1
RUN: llvm-pdbdump raw -headers %p/Inputs/empty.pdb | FileCheck %s
RUN: llvm-pdbdump raw -headers %t.2 | FileCheck %s
RUN: llvm-pdbdump raw -headers -tpi-records %p/Inputs/empty.pdb | FileCheck %s
RUN: llvm-pdbdump raw -headers -tpi-records %t.2 | FileCheck %s
CHECK: FileHeaders {
CHECK-NEXT: BlockSize: 4096
@ -17,13 +17,16 @@ CHECK-NEXT: NumDirectoryBlocks: 1
CHECK-NEXT: DirectoryBlocks: [23]
CHECK-NEXT: NumStreams: 17
CHECK-NEXT: }
CHECK-NEXT: PDB Stream {
CHECK: PDB Stream {
CHECK-NEXT: Version: 20000404
CHECK-NEXT: Signature: 0x54E507E2
CHECK-NEXT: Age: 1
CHECK-NEXT: Guid: {0B355641-86A0-A249-896F-9988FAE52FF0}
CHECK-NEXT: }
CHECK-NEXT: DBI Stream {
CHECK: Type Info Stream (TPI) {
CHECK-NEXT: TPI Version: 20040203
CHECK-NEXT: Record count: 75
CHECK: DBI Stream {
CHECK-NEXT: Dbi Version: 19990903
CHECK-NEXT: Age: 1
CHECK-NEXT: Incremental Linking: Yes

View File

@ -10,8 +10,8 @@
; stream metadata, since the layout of the MSF file might be different
; (for example if we don't write the entire stream)
;
; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -pdb-stream %p/Inputs/empty.pdb > %t.1
; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -pdb-stream -tpi-stream %p/Inputs/empty.pdb > %t.1
; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2 %t.1
; RUN: llvm-pdbdump pdb2yaml -pdb-stream %p/Inputs/empty.pdb > %t.3
; RUN: llvm-pdbdump pdb2yaml -pdb-stream %t.2 > %t.4
; RUN: llvm-pdbdump pdb2yaml -pdb-stream -tpi-stream %p/Inputs/empty.pdb > %t.3
; RUN: llvm-pdbdump pdb2yaml -pdb-stream -tpi-stream %t.2 > %t.4
; RUN: diff %t.3 %t.4

View File

@ -9,6 +9,7 @@
#include "CodeViewYaml.h"
#include "PdbYaml.h"
#include "YamlSerializationContext.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/EnumTables.h"
@ -269,34 +270,20 @@ template <> struct ScalarTraits<APSInt> {
static bool mustQuote(StringRef Scalar) { return false; }
};
void MappingContextTraits<CVType, YamlTypeDumperCallbacks>::mapping(
IO &IO, CVType &Record, YamlTypeDumperCallbacks &Dumper) {
void MappingContextTraits<CVType, pdb::yaml::SerializationContext>::mapping(
IO &IO, CVType &Record, pdb::yaml::SerializationContext &Context) {
if (IO.outputting()) {
codeview::TypeDeserializer Deserializer;
codeview::TypeVisitorCallbackPipeline Pipeline;
Pipeline.addCallbackToPipeline(Deserializer);
Pipeline.addCallbackToPipeline(Dumper);
Pipeline.addCallbackToPipeline(Context.Dumper);
codeview::CVTypeVisitor Visitor(Pipeline);
consumeError(Visitor.visitTypeRecord(Record));
}
}
void MappingContextTraits<FieldListRecord, YamlTypeDumperCallbacks>::mapping(
IO &IO, FieldListRecord &FieldList, YamlTypeDumperCallbacks &Dumper) {
if (IO.outputting()) {
codeview::TypeDeserializer Deserializer;
codeview::TypeVisitorCallbackPipeline Pipeline;
Pipeline.addCallbackToPipeline(Deserializer);
Pipeline.addCallbackToPipeline(Dumper);
codeview::CVTypeVisitor Visitor(Pipeline);
consumeError(Visitor.visitFieldListMemberStream(FieldList.Data));
}
}
void MappingTraits<StringIdRecord>::mapping(IO &IO, StringIdRecord &String) {
IO.mapRequired("Id", String.Id);
IO.mapRequired("String", String.String);
@ -549,13 +536,23 @@ llvm::codeview::yaml::YamlTypeDumperCallbacks::visitTypeBegin(
}
void llvm::codeview::yaml::YamlTypeDumperCallbacks::visitKnownRecordImpl(
const char *Name, const CVType &Type, FieldListRecord &FieldList) {
std::vector<llvm::pdb::yaml::PdbTpiRecord> Records;
const char *Name, const CVType &CVR, FieldListRecord &FieldList) {
std::vector<llvm::pdb::yaml::PdbTpiRecord> FieldListRecords;
if (YamlIO.outputting()) {
FieldListRecordSplitter Splitter(Records);
// If we are outputting, then `FieldList.Data` contains a huge chunk of data
// representing the serialized list of members. We need to split it up into
// individual CVType records where each record represents an individual
// member. This way, we can simply map the entire thing as a Yaml sequence,
// which will recurse back to the standard handler for top-level fields
// (top-level and member fields all have the exact same Yaml syntax so use
// the same parser).
//
// If we are not outputting, then the array contains no data starting out,
// and is instead populated from the sequence represented by the yaml --
// again, using the same logic that we use for top-level records.
FieldListRecordSplitter Splitter(FieldListRecords);
CVTypeVisitor V(Splitter);
consumeError(V.visitFieldListMemberStream(FieldList.Data));
}
YamlIO.mapRequired(Name, Records);
YamlIO.mapRequired("FieldList", FieldListRecords, Context);
}

View File

@ -11,15 +11,23 @@
#define LLVM_TOOLS_LLVMPDBDUMP_CODEVIEWYAML_H
#include "llvm/DebugInfo/CodeView/CodeView.h"
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
#include "llvm/Support/YAMLTraits.h"
namespace llvm {
namespace pdb {
namespace yaml {
struct SerializationContext;
}
}
namespace codeview {
namespace yaml {
class YamlTypeDumperCallbacks : public TypeVisitorCallbacks {
public:
YamlTypeDumperCallbacks(llvm::yaml::IO &IO) : YamlIO(IO) {}
YamlTypeDumperCallbacks(llvm::yaml::IO &IO,
llvm::pdb::yaml::SerializationContext &Context)
: YamlIO(IO), Context(Context) {}
virtual Expected<TypeLeafKind>
visitTypeBegin(const CVRecord<TypeLeafKind> &Record) override;
@ -42,13 +50,19 @@ private:
YamlIO.mapRequired(Name, Record);
}
void visitKnownRecordImpl(const char *Name, const CVType &Type,
void visitKnownRecordImpl(const char *Name, const CVType &CVR,
FieldListRecord &FieldList);
llvm::yaml::IO &YamlIO;
llvm::pdb::yaml::SerializationContext &Context;
};
}
}
namespace pdb {
namespace yaml {
struct SerializationContext;
}
}
}
namespace llvm {
@ -58,10 +72,9 @@ template <> struct MappingTraits<codeview::MemberPointerInfo> {
};
template <>
struct MappingContextTraits<codeview::CVType,
codeview::yaml::YamlTypeDumperCallbacks> {
struct MappingContextTraits<codeview::CVType, pdb::yaml::SerializationContext> {
static void mapping(IO &IO, codeview::CVType &Obj,
codeview::yaml::YamlTypeDumperCallbacks &Context);
pdb::yaml::SerializationContext &Context);
};
template <> struct ScalarEnumerationTraits<codeview::TypeLeafKind> {
@ -77,13 +90,6 @@ template <> struct ScalarEnumerationTraits<codeview::TypeLeafKind> {
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
template <>
struct MappingContextTraits<codeview::FieldListRecord,
codeview::yaml::YamlTypeDumperCallbacks> {
static void mapping(IO &IO, codeview::FieldListRecord &Record,
codeview::yaml::YamlTypeDumperCallbacks &Context);
};
}
}

View File

@ -10,9 +10,11 @@
#include "PdbYaml.h"
#include "CodeViewYaml.h"
#include "YamlSerializationContext.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/CodeView/TypeSerializationVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
#include "llvm/DebugInfo/PDB/PDBExtras.h"
#include "llvm/DebugInfo/PDB/PDBTypes.h"
@ -134,7 +136,7 @@ void MappingTraits<PdbObject>::mapping(IO &IO, PdbObject &Obj) {
IO.mapOptional("StreamMap", Obj.StreamMap);
IO.mapOptional("PdbStream", Obj.PdbStream);
IO.mapOptional("DbiStream", Obj.DbiStream);
IO.mapOptional("TpiStream", Obj.TpiStream);
IO.mapOptionalWithContext("TpiStream", Obj.TpiStream, Obj.Allocator);
}
void MappingTraits<MSFHeaders>::mapping(IO &IO, MSFHeaders &Obj) {
@ -181,10 +183,18 @@ void MappingTraits<PdbDbiStream>::mapping(IO &IO, PdbDbiStream &Obj) {
IO.mapOptional("Modules", Obj.ModInfos);
}
void MappingTraits<PdbTpiStream>::mapping(IO &IO,
pdb::yaml::PdbTpiStream &Obj) {
void MappingContextTraits<PdbTpiStream, BumpPtrAllocator>::mapping(
IO &IO, pdb::yaml::PdbTpiStream &Obj, BumpPtrAllocator &Allocator) {
// Create a single serialization context that will be passed through the
// entire process of serializing / deserializing a Tpi Stream. This is
// especially important when we are going from Pdb -> Yaml because we need
// to maintain state in a TypeTableBuilder across mappings, and at the end of
// the entire process, we need to have one TypeTableBuilder that has every
// record.
pdb::yaml::SerializationContext Context(IO, Allocator);
IO.mapRequired("Version", Obj.Version);
IO.mapRequired("Records", Obj.Records);
IO.mapRequired("Records", Obj.Records, Context);
}
void MappingTraits<NamedStreamMapping>::mapping(IO &IO,
@ -199,21 +209,40 @@ void MappingTraits<PdbDbiModuleInfo>::mapping(IO &IO, PdbDbiModuleInfo &Obj) {
IO.mapOptional("SourceFiles", Obj.SourceFiles);
}
void MappingTraits<PdbTpiRecord>::mapping(IO &IO,
pdb::yaml::PdbTpiRecord &Obj) {
void MappingContextTraits<PdbTpiRecord, pdb::yaml::SerializationContext>::
mapping(IO &IO, pdb::yaml::PdbTpiRecord &Obj,
pdb::yaml::SerializationContext &Context) {
codeview::TypeVisitorCallbackPipeline Pipeline;
codeview::TypeDeserializer Deserializer;
codeview::TypeSerializationVisitor Serializer(Context.FieldListBuilder,
Context.TypeTableBuilder);
if (IO.outputting()) {
codeview::TypeDeserializer Deserializer;
codeview::yaml::YamlTypeDumperCallbacks Callbacks(IO);
codeview::TypeVisitorCallbackPipeline Pipeline;
// For PDB to Yaml, deserialize into a high level record type, then dump it.
Pipeline.addCallbackToPipeline(Deserializer);
Pipeline.addCallbackToPipeline(Callbacks);
codeview::CVTypeVisitor Visitor(Pipeline);
consumeError(Visitor.visitTypeRecord(Obj.Record));
Pipeline.addCallbackToPipeline(Context.Dumper);
} else {
codeview::yaml::YamlTypeDumperCallbacks Callbacks(IO);
codeview::CVTypeVisitor Visitor(Callbacks);
consumeError(Visitor.visitTypeRecord(Obj.Record));
// For Yaml to PDB, extract from the high level record type, then write it
// to bytes.
Pipeline.addCallbackToPipeline(Context.Dumper);
Pipeline.addCallbackToPipeline(Serializer);
}
codeview::CVTypeVisitor Visitor(Pipeline);
consumeError(Visitor.visitTypeRecord(Obj.Record));
if (!IO.outputting()) {
// For Yaml to PDB, we need to update the input Object with the bytes for
// this record.
ArrayRef<StringRef> Records = Context.TypeTableBuilder.getRecords();
ArrayRef<codeview::TypeRecordKind> Kinds =
Context.TypeTableBuilder.getRecordKinds();
StringRef ThisRecord = Records.back();
Obj.Record.Type = static_cast<codeview::TypeLeafKind>(Kinds.back());
Obj.Record.Data =
ArrayRef<uint8_t>(ThisRecord.bytes_begin(), ThisRecord.bytes_end());
Obj.Record.RawData = Obj.Record.Data;
Obj.Record.Length = ThisRecord.size();
}
}

View File

@ -26,6 +26,8 @@ namespace llvm {
namespace pdb {
namespace yaml {
struct SerializationContext;
struct MSFHeaders {
msf::SuperBlock SuperBlock;
uint32_t NumDirectoryBlocks;
@ -79,12 +81,16 @@ struct PdbTpiStream {
};
struct PdbObject {
explicit PdbObject(BumpPtrAllocator &Allocator) : Allocator(Allocator) {}
Optional<MSFHeaders> Headers;
Optional<std::vector<uint32_t>> StreamSizes;
Optional<std::vector<StreamBlockList>> StreamMap;
Optional<PdbInfoStream> PdbStream;
Optional<PdbDbiStream> DbiStream;
Optional<PdbTpiStream> TpiStream;
BumpPtrAllocator &Allocator;
};
}
}
@ -117,8 +123,10 @@ template <> struct MappingTraits<pdb::yaml::PdbDbiStream> {
static void mapping(IO &IO, pdb::yaml::PdbDbiStream &Obj);
};
template <> struct MappingTraits<pdb::yaml::PdbTpiStream> {
static void mapping(IO &IO, pdb::yaml::PdbTpiStream &Obj);
template <>
struct MappingContextTraits<pdb::yaml::PdbTpiStream, llvm::BumpPtrAllocator> {
static void mapping(IO &IO, pdb::yaml::PdbTpiStream &Obj,
llvm::BumpPtrAllocator &Allocator);
};
template <> struct MappingTraits<pdb::yaml::NamedStreamMapping> {
@ -129,8 +137,11 @@ template <> struct MappingTraits<pdb::yaml::PdbDbiModuleInfo> {
static void mapping(IO &IO, pdb::yaml::PdbDbiModuleInfo &Obj);
};
template <> struct MappingTraits<pdb::yaml::PdbTpiRecord> {
static void mapping(IO &IO, pdb::yaml::PdbTpiRecord &Obj);
template <>
struct MappingContextTraits<pdb::yaml::PdbTpiRecord,
pdb::yaml::SerializationContext> {
static void mapping(IO &IO, pdb::yaml::PdbTpiRecord &Obj,
pdb::yaml::SerializationContext &Context);
};
}
}

View File

@ -21,7 +21,8 @@
using namespace llvm;
using namespace llvm::pdb;
YAMLOutputStyle::YAMLOutputStyle(PDBFile &File) : File(File), Out(outs()) {}
YAMLOutputStyle::YAMLOutputStyle(PDBFile &File)
: File(File), Out(outs()), Obj(File.getAllocator()) {}
Error YAMLOutputStyle::dump() {
if (opts::pdb2yaml::StreamDirectory)

View File

@ -0,0 +1,36 @@
//===- YamlSerializationContext.h ----------------------------- *- C++ --*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVMPDBDUMP_YAMLSERIALIZATIONCONTEXT_H
#define LLVM_TOOLS_LLVMPDBDUMP_YAMLSERIALIZATIONCONTEXT_H
#include "CodeViewYaml.h"
#include "PdbYaml.h"
#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h"
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
namespace llvm {
namespace yaml {
class IO;
}
namespace pdb {
namespace yaml {
struct SerializationContext {
explicit SerializationContext(llvm::yaml::IO &IO, BumpPtrAllocator &Allocator)
: Dumper(IO, *this), TypeTableBuilder(Allocator) {}
codeview::yaml::YamlTypeDumperCallbacks Dumper;
codeview::MemoryTypeTableBuilder TypeTableBuilder;
codeview::FieldListRecordBuilder FieldListBuilder;
};
}
}
}
#endif

View File

@ -50,6 +50,8 @@
#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
#include "llvm/DebugInfo/PDB/Raw/RawError.h"
#include "llvm/DebugInfo/PDB/Raw/RawSession.h"
#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h"
#include "llvm/Support/COM.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ConvertUTF.h"
@ -306,7 +308,7 @@ static void yamlToPdb(StringRef Path) {
std::unique_ptr<MemoryBuffer> &Buffer = ErrorOrBuffer.get();
llvm::yaml::Input In(Buffer->getBuffer());
pdb::yaml::PdbObject YamlObj;
pdb::yaml::PdbObject YamlObj(Allocator);
In >> YamlObj;
if (!YamlObj.Headers.hasValue())
ExitOnErr(make_error<GenericError>(generic_error_code::unspecified,
@ -382,6 +384,13 @@ static void yamlToPdb(StringRef Path) {
}
}
if (YamlObj.TpiStream.hasValue()) {
auto &TpiBuilder = Builder.getTpiBuilder();
TpiBuilder.setVersionHeader(YamlObj.TpiStream->Version);
for (const auto &R : YamlObj.TpiStream->Records)
TpiBuilder.addTypeRecord(R.Record);
}
ExitOnErr(Builder.commit(*FileByteStream));
}

View File

@ -331,8 +331,15 @@ static bool isMipsArch(unsigned Arch) {
return false;
}
}
namespace {
struct TypeTableBuilder {
TypeTableBuilder() : Allocator(), Builder(Allocator) {}
static llvm::codeview::MemoryTypeTableBuilder CVTypes;
llvm::BumpPtrAllocator Allocator;
llvm::codeview::MemoryTypeTableBuilder Builder;
};
}
static TypeTableBuilder CVTypes;
/// @brief Creates an format-specific object file dumper.
static std::error_code createDumper(const ObjectFile *Obj,
@ -429,7 +436,7 @@ static void dumpObject(const ObjectFile *Obj) {
if (opts::CodeView)
Dumper->printCodeViewDebugInfo();
if (opts::CodeViewMergedTypes)
Dumper->mergeCodeViewTypes(CVTypes);
Dumper->mergeCodeViewTypes(CVTypes.Builder);
}
if (Obj->isMachO()) {
if (opts::MachODataInCode)
@ -534,7 +541,7 @@ int main(int argc, const char *argv[]) {
if (opts::CodeViewMergedTypes) {
ScopedPrinter W(outs());
dumpCodeViewMergedTypes(W, CVTypes);
dumpCodeViewMergedTypes(W, CVTypes.Builder);
}
return 0;