|  | #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() = default; | 
|  |  | 
|  | void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) { | 
|  | assert(!Kind); | 
|  | 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); | 
|  |  | 
|  | 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 validate 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, std::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) { | 
|  | 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()); | 
|  |  | 
|  | ArrayRef SO = SegmentOffsets; | 
|  |  | 
|  | uint32_t End = SegmentWriter.getOffset(); | 
|  |  | 
|  | std::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" |