aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp')
-rw-r--r--contrib/llvm-project/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp251
1 files changed, 251 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp b/contrib/llvm-project/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp
new file mode 100644
index 000000000000..799cffb7116e
--- /dev/null
+++ b/contrib/llvm-project/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp
@@ -0,0 +1,251 @@
+#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;
+};
+} // namespace
+
+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));
+
+ // Seed the first record with an appropriate record prefix.
+ RecordPrefix Prefix(getTypeLeafKind(RecordKind));
+ CVType Type(&Prefix, sizeof(Prefix));
+ cantFail(Mapping.visitTypeBegin(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());
+
+ // Member Records aren't length-prefixed, they only have a 2-byte TypeLeafKind
+ // at the beginning.
+ cantFail(SegmentWriter.writeEnum(CVMR.Kind));
+
+ // Let the Mapping handle the rest.
+ cantFail(Mapping.visitMemberBegin(CVMR));
+ cantFail(Mapping.visitKnownMember(CVMR, Record));
+ cantFail(Mapping.visitMemberEnd(CVMR));
+
+ // Make sure it's padded to 4 bytes.
+ addPadding(SegmentWriter);
+ assert(getCurrentSegmentLength() % 4 == 0);
+
+ // The maximum length of a single segment is 64KB minus the size to insert a
+ // continuation. So if we are over that, inject a continuation between the
+ // previous member and the member that was just written, then end the previous
+ // segment after the continuation and begin a new one with the just-written
+ // member.
+ if (getCurrentSegmentLength() > MaxSegmentLength) {
+ // We need to inject some bytes before the member we just wrote but after
+ // the previous member. Save off the length of the member we just wrote so
+ // that we can do some sanity checking on it.
+ uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset;
+ (void) MemberLength;
+ insertSegmentEnd(OriginalOffset);
+ // Since this member now becomes a new top-level record, it should have
+ // gotten a RecordPrefix injected, and that RecordPrefix + the member we
+ // just wrote should now constitute the entirety of the current "new"
+ // segment.
+ 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);
+
+ // We need to make space for the continuation record. For now we can't fill
+ // out the length or the TypeIndex of the back-reference, but we need the
+ // space to at least be there.
+ 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);
+
+ // Seek to the end so that we can keep writing against the new segment.
+ 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);
+
+ // Write the length to the RecordPrefix, making sure it does not include
+ // sizeof(RecordPrefix.Length)
+ RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data());
+ 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 CVType(Data);
+}
+
+std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) {
+ RecordPrefix Prefix(getTypeLeafKind(*Kind));
+ CVType Type(&Prefix, sizeof(Prefix));
+ cantFail(Mapping.visitTypeEnd(Type));
+
+ // We're now done, and we have a series of segments each beginning at an
+ // offset specified in the SegmentOffsets array. We now need to iterate
+ // over each segment and post-process them in the following two ways:
+ // 1) Each top-level record has a RecordPrefix whose type is either
+ // LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0.
+ // Those should all be set to the correct length now.
+ // 2) Each continuation record has an IndexRef field which we set to the
+ // magic value 0xB0C0B0C0. Now that the caller has told us the TypeIndex
+ // they want this sequence to start from, we can go through and update
+ // each one.
+ //
+ // Logically, the sequence of records we've built up looks like this:
+ //
+ // SegmentOffsets[0]: <Length> (Initially: uninitialized)
+ // SegmentOffsets[0]+2: LF_FIELDLIST
+ // SegmentOffsets[0]+4: Member[0]
+ // SegmentOffsets[0]+?: ...
+ // SegmentOffsets[0]+?: Member[4]
+ // SegmentOffsets[1]-8: LF_INDEX
+ // SegmentOffsets[1]-6: 0
+ // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
+ //
+ // SegmentOffsets[1]: <Length> (Initially: uninitialized)
+ // SegmentOffsets[1]+2: LF_FIELDLIST
+ // SegmentOffsets[1]+4: Member[0]
+ // SegmentOffsets[1]+?: ...
+ // SegmentOffsets[1]+?: Member[s]
+ // SegmentOffsets[2]-8: LF_INDEX
+ // SegmentOffsets[2]-6: 0
+ // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
+ //
+ // ...
+ //
+ // SegmentOffsets[N]: <Length> (Initially: uninitialized)
+ // SegmentOffsets[N]+2: LF_FIELDLIST
+ // SegmentOffsets[N]+4: Member[0]
+ // SegmentOffsets[N]+?: ...
+ // SegmentOffsets[N]+?: Member[t]
+ //
+ // And this is the way we have laid them out in the serialization buffer. But
+ // we cannot actually commit them to the underlying stream this way, due to
+ // the topological sorting requirement of a type stream (specifically,
+ // TypeIndex references can only point backwards, not forwards). So the
+ // sequence that we return to the caller contains the records in reverse
+ // order, which is the proper order for committing the serialized records.
+
+ 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;
+}
+
+// Explicitly instantiate the member function for each known type so that we can
+// implement this in the cpp file.
+#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"