#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h"
using namespace llvm;
using namespace llvm::codeview;
namespace {
struct ContinuationRecord {
ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)};
ulittle16_t Size{0};
ulittle32_t IndexRef{0xB0C0B0C0};
};
struct SegmentInjection {
SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; }
ContinuationRecord Cont;
RecordPrefix Prefix;
};
}
static void addPadding(BinaryStreamWriter &Writer) {
uint32_t Align = Writer.getOffset() % 4;
if (Align == 0)
return;
int PaddingBytes = 4 - Align;
while (PaddingBytes > 0) {
uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
cantFail(Writer.writeInteger(Pad));
--PaddingBytes;
}
}
static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST);
static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST);
static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord);
static constexpr uint32_t MaxSegmentLength =
MaxRecordLength - ContinuationLength;
static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) {
return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST
: LF_METHODLIST;
}
ContinuationRecordBuilder::ContinuationRecordBuilder()
: SegmentWriter(Buffer), Mapping(SegmentWriter) {}
ContinuationRecordBuilder::~ContinuationRecordBuilder() {}
void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) {
assert(!Kind.hasValue());
Kind = RecordKind;
Buffer.clear();
SegmentWriter.setOffset(0);
SegmentOffsets.clear();
SegmentOffsets.push_back(0);
assert(SegmentWriter.getOffset() == 0);
assert(SegmentWriter.getLength() == 0);
const SegmentInjection *FLI =
(RecordKind == ContinuationRecordKind::FieldList)
? &InjectFieldList
: &InjectMethodOverloadList;
const uint8_t *FLIB = reinterpret_cast<const uint8_t *>(FLI);
InjectedSegmentBytes =
ArrayRef<uint8_t>(FLIB, FLIB + sizeof(SegmentInjection));
CVType Type;
Type.Type = getTypeLeafKind(RecordKind);
cantFail(Mapping.visitTypeBegin(Type));
RecordPrefix Prefix;
Prefix.RecordLen = 0;
Prefix.RecordKind = Type.Type;
cantFail(SegmentWriter.writeObject(Prefix));
}
template <typename RecordType>
void ContinuationRecordBuilder::writeMemberType(RecordType &Record) {
assert(Kind.hasValue());
uint32_t OriginalOffset = SegmentWriter.getOffset();
CVMemberRecord CVMR;
CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind());
cantFail(SegmentWriter.writeEnum(CVMR.Kind));
cantFail(Mapping.visitMemberBegin(CVMR));
cantFail(Mapping.visitKnownMember(CVMR, Record));
cantFail(Mapping.visitMemberEnd(CVMR));
addPadding(SegmentWriter);
assert(getCurrentSegmentLength() % 4 == 0);
if (getCurrentSegmentLength() > MaxSegmentLength) {
uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset;
(void) MemberLength;
insertSegmentEnd(OriginalOffset);
assert(getCurrentSegmentLength() == MemberLength + sizeof(RecordPrefix));
}
assert(getCurrentSegmentLength() % 4 == 0);
assert(getCurrentSegmentLength() <= MaxSegmentLength);
}
uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const {
return SegmentWriter.getOffset() - SegmentOffsets.back();
}
void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) {
uint32_t SegmentBegin = SegmentOffsets.back();
(void)SegmentBegin;
assert(Offset > SegmentBegin);
assert(Offset - SegmentBegin <= MaxSegmentLength);
Buffer.insert(Offset, InjectedSegmentBytes);
uint32_t NewSegmentBegin = Offset + ContinuationLength;
uint32_t SegmentLength = NewSegmentBegin - SegmentOffsets.back();
(void) SegmentLength;
assert(SegmentLength % 4 == 0);
assert(SegmentLength <= MaxRecordLength);
SegmentOffsets.push_back(NewSegmentBegin);
SegmentWriter.setOffset(SegmentWriter.getLength());
assert(SegmentWriter.bytesRemaining() == 0);
}
CVType ContinuationRecordBuilder::createSegmentRecord(
uint32_t OffBegin, uint32_t OffEnd, Optional<TypeIndex> RefersTo) {
assert(OffEnd - OffBegin <= USHRT_MAX);
MutableArrayRef<uint8_t> Data = Buffer.data();
Data = Data.slice(OffBegin, OffEnd - OffBegin);
CVType Type;
Type.Type = getTypeLeafKind(*Kind);
Type.RecordData = Data;
RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data());
assert(Prefix->RecordKind == Type.Type);
Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen);
if (RefersTo.hasValue()) {
auto Continuation = Data.take_back(ContinuationLength);
ContinuationRecord *CR =
reinterpret_cast<ContinuationRecord *>(Continuation.data());
assert(CR->Kind == TypeLeafKind::LF_INDEX);
assert(CR->IndexRef == 0xB0C0B0C0);
CR->IndexRef = RefersTo->getIndex();
}
return Type;
}
std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) {
CVType Type;
Type.Type = getTypeLeafKind(*Kind);
cantFail(Mapping.visitTypeEnd(Type));
std::vector<CVType> Types;
Types.reserve(SegmentOffsets.size());
auto SO = makeArrayRef(SegmentOffsets);
uint32_t End = SegmentWriter.getOffset();
Optional<TypeIndex> RefersTo;
for (uint32_t Offset : reverse(SO)) {
Types.push_back(createSegmentRecord(Offset, End, RefersTo));
End = Offset;
RefersTo = Index++;
}
Kind.reset();
return Types;
}
#define TYPE_RECORD(EnumName, EnumVal, Name)
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
template void llvm::codeview::ContinuationRecordBuilder::writeMemberType( \
Name##Record &Record);
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"