blob: 1f4873c4f96938fb1616c98b0ccd15b2a2da7939 [file] [log] [blame]
//===- TypeSerializer.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_TYPESERIALIZER_H
#define LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZER_H
#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/BinaryStreamWriter.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Error.h"
namespace llvm {
namespace codeview {
class TypeSerializer : public TypeVisitorCallbacks {
struct SubRecord {
SubRecord(TypeLeafKind K, uint32_t S) : Kind(K), Size(S) {}
TypeLeafKind Kind;
uint32_t Size = 0;
};
struct RecordSegment {
SmallVector<SubRecord, 16> SubRecords;
uint32_t length() const {
uint32_t L = sizeof(RecordPrefix);
for (const auto &R : SubRecords) {
L += R.Size;
}
return L;
}
};
typedef SmallVector<MutableArrayRef<uint8_t>, 2> RecordList;
static constexpr uint8_t ContinuationLength = 8;
BumpPtrAllocator &RecordStorage;
RecordSegment CurrentSegment;
RecordList FieldListSegments;
TypeIndex LastTypeIndex;
Optional<TypeLeafKind> TypeKind;
Optional<TypeLeafKind> MemberKind;
std::vector<uint8_t> RecordBuffer;
MutableBinaryByteStream Stream;
BinaryStreamWriter Writer;
TypeRecordMapping Mapping;
RecordList SeenRecords;
StringMap<TypeIndex> HashedRecords;
bool isInFieldList() const;
TypeIndex calcNextTypeIndex() const;
TypeIndex incrementTypeIndex();
MutableArrayRef<uint8_t> getCurrentSubRecordData();
MutableArrayRef<uint8_t> getCurrentRecordData();
Error writeRecordPrefix(TypeLeafKind Kind);
TypeIndex insertRecordBytesPrivate(MutableArrayRef<uint8_t> Record);
Expected<MutableArrayRef<uint8_t>>
addPadding(MutableArrayRef<uint8_t> Record);
public:
explicit TypeSerializer(BumpPtrAllocator &Storage);
ArrayRef<MutableArrayRef<uint8_t>> records() const;
TypeIndex getLastTypeIndex() const;
TypeIndex insertRecordBytes(MutableArrayRef<uint8_t> Record);
Expected<TypeIndex> visitTypeEndGetIndex(CVType &Record);
Error visitTypeBegin(CVType &Record) override;
Error visitTypeEnd(CVType &Record) override;
Error visitMemberBegin(CVMemberRecord &Record) override;
Error visitMemberEnd(CVMemberRecord &Record) override;
#define TYPE_RECORD(EnumName, EnumVal, Name) \
virtual Error visitKnownRecord(CVType &CVR, Name##Record &Record) override { \
return visitKnownRecordImpl(CVR, Record); \
}
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
Error visitKnownMember(CVMemberRecord &CVR, Name##Record &Record) override { \
return visitKnownMemberImpl<Name##Record>(CVR, Record); \
}
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
private:
template <typename RecordKind>
Error visitKnownRecordImpl(CVType &CVR, RecordKind &Record) {
return Mapping.visitKnownRecord(CVR, Record);
}
template <typename RecordType>
Error visitKnownMemberImpl(CVMemberRecord &CVR, RecordType &Record) {
assert(CVR.Kind == static_cast<TypeLeafKind>(Record.getKind()));
if (auto EC = Writer.writeEnum(CVR.Kind))
return EC;
if (auto EC = Mapping.visitKnownMember(CVR, Record))
return EC;
// Get all the data that was just written and is yet to be committed to
// the current segment. Then pad it to 4 bytes.
MutableArrayRef<uint8_t> ThisRecord = getCurrentSubRecordData();
auto ExpectedRecord = addPadding(ThisRecord);
if (!ExpectedRecord)
return ExpectedRecord.takeError();
ThisRecord = *ExpectedRecord;
CurrentSegment.SubRecords.emplace_back(CVR.Kind, ThisRecord.size());
CVR.Data = ThisRecord;
// Both the last subrecord and the total length of this segment should be
// multiples of 4.
assert(ThisRecord.size() % 4 == 0);
assert(CurrentSegment.length() % 4 == 0);
return Error::success();
}
};
}
}
#endif