blob: fd4d1853fa544f047c244a48925ffacaa9e86545 [file] [log] [blame]
//===- TypeSerialzier.cpp ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
#include "llvm/Support/BinaryStreamWriter.h"
#include <string.h>
using namespace llvm;
using namespace llvm::codeview;
bool TypeSerializer::isInFieldList() const {
return TypeKind.hasValue() && *TypeKind == TypeLeafKind::LF_FIELDLIST;
}
TypeIndex TypeSerializer::calcNextTypeIndex() const {
if (LastTypeIndex.isNoneType())
return TypeIndex(TypeIndex::FirstNonSimpleIndex);
else
return TypeIndex(LastTypeIndex.getIndex() + 1);
}
TypeIndex TypeSerializer::incrementTypeIndex() {
TypeIndex Previous = LastTypeIndex;
LastTypeIndex = calcNextTypeIndex();
return Previous;
}
MutableArrayRef<uint8_t> TypeSerializer::getCurrentSubRecordData() {
assert(isInFieldList());
return getCurrentRecordData().drop_front(CurrentSegment.length());
}
MutableArrayRef<uint8_t> TypeSerializer::getCurrentRecordData() {
return MutableArrayRef<uint8_t>(RecordBuffer).take_front(Writer.getOffset());
}
Error TypeSerializer::writeRecordPrefix(TypeLeafKind Kind) {
RecordPrefix Prefix;
Prefix.RecordKind = Kind;
Prefix.RecordLen = 0;
if (auto EC = Writer.writeObject(Prefix))
return EC;
return Error::success();
}
TypeIndex
TypeSerializer::insertRecordBytesPrivate(MutableArrayRef<uint8_t> Record) {
assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!");
StringRef S(reinterpret_cast<const char *>(Record.data()), Record.size());
TypeIndex NextTypeIndex = calcNextTypeIndex();
auto Result = HashedRecords.try_emplace(S, NextTypeIndex);
if (Result.second) {
LastTypeIndex = NextTypeIndex;
SeenRecords.push_back(Record);
}
return Result.first->getValue();
}
Expected<MutableArrayRef<uint8_t>>
TypeSerializer::addPadding(MutableArrayRef<uint8_t> Record) {
uint32_t Align = Record.size() % 4;
if (Align == 0)
return Record;
int PaddingBytes = 4 - Align;
int N = PaddingBytes;
while (PaddingBytes > 0) {
uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
if (auto EC = Writer.writeInteger(Pad))
return std::move(EC);
--PaddingBytes;
}
return MutableArrayRef<uint8_t>(Record.data(), Record.size() + N);
}
TypeSerializer::TypeSerializer(BumpPtrAllocator &Storage)
: RecordStorage(Storage), LastTypeIndex(),
RecordBuffer(MaxRecordLength * 2),
Stream(RecordBuffer, llvm::support::little), Writer(Stream),
Mapping(Writer) {
// RecordBuffer needs to be able to hold enough data so that if we are 1
// byte short of MaxRecordLen, and then we try to write MaxRecordLen bytes,
// we won't overflow.
}
ArrayRef<MutableArrayRef<uint8_t>> TypeSerializer::records() const {
return SeenRecords;
}
TypeIndex TypeSerializer::getLastTypeIndex() const { return LastTypeIndex; }
TypeIndex TypeSerializer::insertRecordBytes(MutableArrayRef<uint8_t> Record) {
assert(!TypeKind.hasValue() && "Already in a type mapping!");
assert(Writer.getOffset() == 0 && "Stream has data already!");
return insertRecordBytesPrivate(Record);
}
Error TypeSerializer::visitTypeBegin(CVType &Record) {
assert(!TypeKind.hasValue() && "Already in a type mapping!");
assert(Writer.getOffset() == 0 && "Stream has data already!");
if (auto EC = writeRecordPrefix(Record.kind()))
return EC;
TypeKind = Record.kind();
if (auto EC = Mapping.visitTypeBegin(Record))
return EC;
return Error::success();
}
Expected<TypeIndex> TypeSerializer::visitTypeEndGetIndex(CVType &Record) {
assert(TypeKind.hasValue() && "Not in a type mapping!");
if (auto EC = Mapping.visitTypeEnd(Record))
return std::move(EC);
// Update the record's length and fill out the CVType members to point to
// the stable memory holding the record's data.
auto ThisRecordData = getCurrentRecordData();
auto ExpectedData = addPadding(ThisRecordData);
if (!ExpectedData)
return ExpectedData.takeError();
ThisRecordData = *ExpectedData;
RecordPrefix *Prefix =
reinterpret_cast<RecordPrefix *>(ThisRecordData.data());
Prefix->RecordLen = ThisRecordData.size() - sizeof(uint16_t);
uint8_t *Copy = RecordStorage.Allocate<uint8_t>(ThisRecordData.size());
::memcpy(Copy, ThisRecordData.data(), ThisRecordData.size());
ThisRecordData = MutableArrayRef<uint8_t>(Copy, ThisRecordData.size());
Record = CVType(*TypeKind, ThisRecordData);
TypeIndex InsertedTypeIndex = insertRecordBytesPrivate(ThisRecordData);
// Write out each additional segment in reverse order, and update each
// record's continuation index to point to the previous one.
for (auto X : reverse(FieldListSegments)) {
auto CIBytes = X.take_back(sizeof(uint32_t));
support::ulittle32_t *CI =
reinterpret_cast<support::ulittle32_t *>(CIBytes.data());
assert(*CI == 0xB0C0B0C0 && "Invalid TypeIndex placeholder");
*CI = InsertedTypeIndex.getIndex();
InsertedTypeIndex = insertRecordBytesPrivate(X);
}
TypeKind.reset();
Writer.setOffset(0);
FieldListSegments.clear();
CurrentSegment.SubRecords.clear();
return InsertedTypeIndex;
}
Error TypeSerializer::visitTypeEnd(CVType &Record) {
auto ExpectedIndex = visitTypeEndGetIndex(Record);
if (!ExpectedIndex)
return ExpectedIndex.takeError();
return Error::success();
}
Error TypeSerializer::visitMemberBegin(CVMemberRecord &Record) {
assert(isInFieldList() && "Not in a field list!");
assert(!MemberKind.hasValue() && "Already in a member record!");
MemberKind = Record.Kind;
if (auto EC = Mapping.visitMemberBegin(Record))
return EC;
return Error::success();
}
Error TypeSerializer::visitMemberEnd(CVMemberRecord &Record) {
if (auto EC = Mapping.visitMemberEnd(Record))
return EC;
// Check if this subrecord makes the current segment not fit in 64K minus
// the space for a continuation record (8 bytes). If the segment does not
// fit, insert a continuation record.
if (Writer.getOffset() > MaxRecordLength - ContinuationLength) {
MutableArrayRef<uint8_t> Data = getCurrentRecordData();
SubRecord LastSubRecord = CurrentSegment.SubRecords.back();
uint32_t CopySize = CurrentSegment.length() - LastSubRecord.Size;
auto CopyData = Data.take_front(CopySize);
auto LeftOverData = Data.drop_front(CopySize);
assert(LastSubRecord.Size == LeftOverData.size());
// Allocate stable storage for the record and copy the old record plus
// continuation over.
uint16_t LengthWithSize = CopySize + ContinuationLength;
assert(LengthWithSize <= MaxRecordLength);
RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(CopyData.data());
Prefix->RecordLen = LengthWithSize - sizeof(uint16_t);
uint8_t *SegmentBytes = RecordStorage.Allocate<uint8_t>(LengthWithSize);
auto SavedSegment = MutableArrayRef<uint8_t>(SegmentBytes, LengthWithSize);
MutableBinaryByteStream CS(SavedSegment, llvm::support::little);
BinaryStreamWriter CW(CS);
if (auto EC = CW.writeBytes(CopyData))
return EC;
if (auto EC = CW.writeEnum(TypeLeafKind::LF_INDEX))
return EC;
if (auto EC = CW.writeInteger<uint16_t>(0))
return EC;
if (auto EC = CW.writeInteger<uint32_t>(0xB0C0B0C0))
return EC;
FieldListSegments.push_back(SavedSegment);
// Write a new placeholder record prefix to mark the start of this new
// top-level record.
Writer.setOffset(0);
if (auto EC = writeRecordPrefix(TypeLeafKind::LF_FIELDLIST))
return EC;
// Then move over the subrecord that overflowed the old segment to the
// beginning of this segment. Note that we have to use memmove here
// instead of Writer.writeBytes(), because the new and old locations
// could overlap.
::memmove(Stream.data().data() + sizeof(RecordPrefix), LeftOverData.data(),
LeftOverData.size());
// And point the segment writer at the end of that subrecord.
Writer.setOffset(LeftOverData.size() + sizeof(RecordPrefix));
CurrentSegment.SubRecords.clear();
CurrentSegment.SubRecords.push_back(LastSubRecord);
}
// Update the CVMemberRecord since we may have shifted around or gotten
// padded.
Record.Data = getCurrentSubRecordData();
MemberKind.reset();
return Error::success();
}