xref: /freebsd-src/contrib/llvm-project/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
10b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h"
20b57cec5SDimitry Andric 
30b57cec5SDimitry Andric using namespace llvm;
40b57cec5SDimitry Andric using namespace llvm::codeview;
50b57cec5SDimitry Andric 
60b57cec5SDimitry Andric namespace {
70b57cec5SDimitry Andric struct ContinuationRecord {
80b57cec5SDimitry Andric   ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)};
90b57cec5SDimitry Andric   ulittle16_t Size{0};
100b57cec5SDimitry Andric   ulittle32_t IndexRef{0xB0C0B0C0};
110b57cec5SDimitry Andric };
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric struct SegmentInjection {
SegmentInjection__anon99b4e10a0111::SegmentInjection140b57cec5SDimitry Andric   SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; }
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric   ContinuationRecord Cont;
170b57cec5SDimitry Andric   RecordPrefix Prefix;
180b57cec5SDimitry Andric };
190b57cec5SDimitry Andric } // namespace
200b57cec5SDimitry Andric 
addPadding(BinaryStreamWriter & Writer)210b57cec5SDimitry Andric static void addPadding(BinaryStreamWriter &Writer) {
220b57cec5SDimitry Andric   uint32_t Align = Writer.getOffset() % 4;
230b57cec5SDimitry Andric   if (Align == 0)
240b57cec5SDimitry Andric     return;
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric   int PaddingBytes = 4 - Align;
270b57cec5SDimitry Andric   while (PaddingBytes > 0) {
280b57cec5SDimitry Andric     uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
290b57cec5SDimitry Andric     cantFail(Writer.writeInteger(Pad));
300b57cec5SDimitry Andric     --PaddingBytes;
310b57cec5SDimitry Andric   }
320b57cec5SDimitry Andric }
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST);
350b57cec5SDimitry Andric static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST);
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord);
380b57cec5SDimitry Andric static constexpr uint32_t MaxSegmentLength =
390b57cec5SDimitry Andric     MaxRecordLength - ContinuationLength;
400b57cec5SDimitry Andric 
getTypeLeafKind(ContinuationRecordKind CK)410b57cec5SDimitry Andric static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) {
420b57cec5SDimitry Andric   return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST
430b57cec5SDimitry Andric                                                    : LF_METHODLIST;
440b57cec5SDimitry Andric }
450b57cec5SDimitry Andric 
ContinuationRecordBuilder()460b57cec5SDimitry Andric ContinuationRecordBuilder::ContinuationRecordBuilder()
470b57cec5SDimitry Andric     : SegmentWriter(Buffer), Mapping(SegmentWriter) {}
480b57cec5SDimitry Andric 
4981ad6265SDimitry Andric ContinuationRecordBuilder::~ContinuationRecordBuilder() = default;
500b57cec5SDimitry Andric 
begin(ContinuationRecordKind RecordKind)510b57cec5SDimitry Andric void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) {
5281ad6265SDimitry Andric   assert(!Kind);
530b57cec5SDimitry Andric   Kind = RecordKind;
540b57cec5SDimitry Andric   Buffer.clear();
550b57cec5SDimitry Andric   SegmentWriter.setOffset(0);
560b57cec5SDimitry Andric   SegmentOffsets.clear();
570b57cec5SDimitry Andric   SegmentOffsets.push_back(0);
580b57cec5SDimitry Andric   assert(SegmentWriter.getOffset() == 0);
590b57cec5SDimitry Andric   assert(SegmentWriter.getLength() == 0);
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric   const SegmentInjection *FLI =
620b57cec5SDimitry Andric       (RecordKind == ContinuationRecordKind::FieldList)
630b57cec5SDimitry Andric           ? &InjectFieldList
640b57cec5SDimitry Andric           : &InjectMethodOverloadList;
650b57cec5SDimitry Andric   const uint8_t *FLIB = reinterpret_cast<const uint8_t *>(FLI);
660b57cec5SDimitry Andric   InjectedSegmentBytes =
670b57cec5SDimitry Andric       ArrayRef<uint8_t>(FLIB, FLIB + sizeof(SegmentInjection));
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric   // Seed the first record with an appropriate record prefix.
700b57cec5SDimitry Andric   RecordPrefix Prefix(getTypeLeafKind(RecordKind));
710b57cec5SDimitry Andric   CVType Type(&Prefix, sizeof(Prefix));
720b57cec5SDimitry Andric   cantFail(Mapping.visitTypeBegin(Type));
730b57cec5SDimitry Andric 
740b57cec5SDimitry Andric   cantFail(SegmentWriter.writeObject(Prefix));
750b57cec5SDimitry Andric }
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric template <typename RecordType>
writeMemberType(RecordType & Record)780b57cec5SDimitry Andric void ContinuationRecordBuilder::writeMemberType(RecordType &Record) {
7981ad6265SDimitry Andric   assert(Kind);
800b57cec5SDimitry Andric 
810b57cec5SDimitry Andric   uint32_t OriginalOffset = SegmentWriter.getOffset();
820b57cec5SDimitry Andric   CVMemberRecord CVMR;
830b57cec5SDimitry Andric   CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind());
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric   // Member Records aren't length-prefixed, they only have a 2-byte TypeLeafKind
860b57cec5SDimitry Andric   // at the beginning.
870b57cec5SDimitry Andric   cantFail(SegmentWriter.writeEnum(CVMR.Kind));
880b57cec5SDimitry Andric 
890b57cec5SDimitry Andric   // Let the Mapping handle the rest.
900b57cec5SDimitry Andric   cantFail(Mapping.visitMemberBegin(CVMR));
910b57cec5SDimitry Andric   cantFail(Mapping.visitKnownMember(CVMR, Record));
920b57cec5SDimitry Andric   cantFail(Mapping.visitMemberEnd(CVMR));
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric   // Make sure it's padded to 4 bytes.
950b57cec5SDimitry Andric   addPadding(SegmentWriter);
960b57cec5SDimitry Andric   assert(getCurrentSegmentLength() % 4 == 0);
970b57cec5SDimitry Andric 
980b57cec5SDimitry Andric   // The maximum length of a single segment is 64KB minus the size to insert a
990b57cec5SDimitry Andric   // continuation.  So if we are over that, inject a continuation between the
1000b57cec5SDimitry Andric   // previous member and the member that was just written, then end the previous
1010b57cec5SDimitry Andric   // segment after the continuation and begin a new one with the just-written
1020b57cec5SDimitry Andric   // member.
1030b57cec5SDimitry Andric   if (getCurrentSegmentLength() > MaxSegmentLength) {
1040b57cec5SDimitry Andric     // We need to inject some bytes before the member we just wrote but after
1050b57cec5SDimitry Andric     // the previous member.  Save off the length of the member we just wrote so
106349cc55cSDimitry Andric     // that we can do validate it.
1070b57cec5SDimitry Andric     uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset;
1080b57cec5SDimitry Andric     (void) MemberLength;
1090b57cec5SDimitry Andric     insertSegmentEnd(OriginalOffset);
1100b57cec5SDimitry Andric     // Since this member now becomes a new top-level record, it should have
1110b57cec5SDimitry Andric     // gotten a RecordPrefix injected, and that RecordPrefix + the member we
1120b57cec5SDimitry Andric     // just wrote should now constitute the entirety of the current "new"
1130b57cec5SDimitry Andric     // segment.
1140b57cec5SDimitry Andric     assert(getCurrentSegmentLength() == MemberLength + sizeof(RecordPrefix));
1150b57cec5SDimitry Andric   }
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric   assert(getCurrentSegmentLength() % 4 == 0);
1180b57cec5SDimitry Andric   assert(getCurrentSegmentLength() <= MaxSegmentLength);
1190b57cec5SDimitry Andric }
1200b57cec5SDimitry Andric 
getCurrentSegmentLength() const1210b57cec5SDimitry Andric uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const {
1220b57cec5SDimitry Andric   return SegmentWriter.getOffset() - SegmentOffsets.back();
1230b57cec5SDimitry Andric }
1240b57cec5SDimitry Andric 
insertSegmentEnd(uint32_t Offset)1250b57cec5SDimitry Andric void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) {
1260b57cec5SDimitry Andric   uint32_t SegmentBegin = SegmentOffsets.back();
1270b57cec5SDimitry Andric   (void)SegmentBegin;
1280b57cec5SDimitry Andric   assert(Offset > SegmentBegin);
1290b57cec5SDimitry Andric   assert(Offset - SegmentBegin <= MaxSegmentLength);
1300b57cec5SDimitry Andric 
1310b57cec5SDimitry Andric   // We need to make space for the continuation record.  For now we can't fill
1320b57cec5SDimitry Andric   // out the length or the TypeIndex of the back-reference, but we need the
1330b57cec5SDimitry Andric   // space to at least be there.
1340b57cec5SDimitry Andric   Buffer.insert(Offset, InjectedSegmentBytes);
1350b57cec5SDimitry Andric 
1360b57cec5SDimitry Andric   uint32_t NewSegmentBegin = Offset + ContinuationLength;
1370b57cec5SDimitry Andric   uint32_t SegmentLength = NewSegmentBegin - SegmentOffsets.back();
1380b57cec5SDimitry Andric   (void) SegmentLength;
1390b57cec5SDimitry Andric 
1400b57cec5SDimitry Andric   assert(SegmentLength % 4 == 0);
1410b57cec5SDimitry Andric   assert(SegmentLength <= MaxRecordLength);
1420b57cec5SDimitry Andric   SegmentOffsets.push_back(NewSegmentBegin);
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric   // Seek to the end so that we can keep writing against the new segment.
1450b57cec5SDimitry Andric   SegmentWriter.setOffset(SegmentWriter.getLength());
1460b57cec5SDimitry Andric   assert(SegmentWriter.bytesRemaining() == 0);
1470b57cec5SDimitry Andric }
1480b57cec5SDimitry Andric 
createSegmentRecord(uint32_t OffBegin,uint32_t OffEnd,std::optional<TypeIndex> RefersTo)1490b57cec5SDimitry Andric CVType ContinuationRecordBuilder::createSegmentRecord(
150*bdd1243dSDimitry Andric     uint32_t OffBegin, uint32_t OffEnd, std::optional<TypeIndex> RefersTo) {
1510b57cec5SDimitry Andric   assert(OffEnd - OffBegin <= USHRT_MAX);
1520b57cec5SDimitry Andric 
1530b57cec5SDimitry Andric   MutableArrayRef<uint8_t> Data = Buffer.data();
1540b57cec5SDimitry Andric   Data = Data.slice(OffBegin, OffEnd - OffBegin);
1550b57cec5SDimitry Andric 
1560b57cec5SDimitry Andric   // Write the length to the RecordPrefix, making sure it does not include
1570b57cec5SDimitry Andric   // sizeof(RecordPrefix.Length)
1580b57cec5SDimitry Andric   RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data());
1590b57cec5SDimitry Andric   Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen);
1600b57cec5SDimitry Andric 
16181ad6265SDimitry Andric   if (RefersTo) {
1620b57cec5SDimitry Andric     auto Continuation = Data.take_back(ContinuationLength);
1630b57cec5SDimitry Andric     ContinuationRecord *CR =
1640b57cec5SDimitry Andric         reinterpret_cast<ContinuationRecord *>(Continuation.data());
1650b57cec5SDimitry Andric     assert(CR->Kind == TypeLeafKind::LF_INDEX);
1660b57cec5SDimitry Andric     assert(CR->IndexRef == 0xB0C0B0C0);
1670b57cec5SDimitry Andric     CR->IndexRef = RefersTo->getIndex();
1680b57cec5SDimitry Andric   }
1690b57cec5SDimitry Andric 
1700b57cec5SDimitry Andric   return CVType(Data);
1710b57cec5SDimitry Andric }
1720b57cec5SDimitry Andric 
end(TypeIndex Index)1730b57cec5SDimitry Andric std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) {
1740b57cec5SDimitry Andric   RecordPrefix Prefix(getTypeLeafKind(*Kind));
1750b57cec5SDimitry Andric   CVType Type(&Prefix, sizeof(Prefix));
1760b57cec5SDimitry Andric   cantFail(Mapping.visitTypeEnd(Type));
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric   // We're now done, and we have a series of segments each beginning at an
1790b57cec5SDimitry Andric   // offset specified in the SegmentOffsets array.  We now need to iterate
1800b57cec5SDimitry Andric   // over each segment and post-process them in the following two ways:
1810b57cec5SDimitry Andric   // 1) Each top-level record has a RecordPrefix whose type is either
1820b57cec5SDimitry Andric   //    LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0.
1830b57cec5SDimitry Andric   //    Those should all be set to the correct length now.
1840b57cec5SDimitry Andric   // 2) Each continuation record has an IndexRef field which we set to the
1850b57cec5SDimitry Andric   //    magic value 0xB0C0B0C0.  Now that the caller has told us the TypeIndex
1860b57cec5SDimitry Andric   //    they want this sequence to start from, we can go through and update
1870b57cec5SDimitry Andric   //    each one.
1880b57cec5SDimitry Andric   //
1890b57cec5SDimitry Andric   // Logically, the sequence of records we've built up looks like this:
1900b57cec5SDimitry Andric   //
1910b57cec5SDimitry Andric   // SegmentOffsets[0]:   <Length>                    (Initially: uninitialized)
1920b57cec5SDimitry Andric   // SegmentOffsets[0]+2: LF_FIELDLIST
1930b57cec5SDimitry Andric   // SegmentOffsets[0]+4: Member[0]
1940b57cec5SDimitry Andric   // SegmentOffsets[0]+?: ...
1950b57cec5SDimitry Andric   // SegmentOffsets[0]+?: Member[4]
1960b57cec5SDimitry Andric   // SegmentOffsets[1]-8: LF_INDEX
1970b57cec5SDimitry Andric   // SegmentOffsets[1]-6: 0
1980b57cec5SDimitry Andric   // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
1990b57cec5SDimitry Andric   //
2000b57cec5SDimitry Andric   // SegmentOffsets[1]:   <Length>                    (Initially: uninitialized)
2010b57cec5SDimitry Andric   // SegmentOffsets[1]+2: LF_FIELDLIST
2020b57cec5SDimitry Andric   // SegmentOffsets[1]+4: Member[0]
2030b57cec5SDimitry Andric   // SegmentOffsets[1]+?: ...
2040b57cec5SDimitry Andric   // SegmentOffsets[1]+?: Member[s]
2050b57cec5SDimitry Andric   // SegmentOffsets[2]-8: LF_INDEX
2060b57cec5SDimitry Andric   // SegmentOffsets[2]-6: 0
2070b57cec5SDimitry Andric   // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
2080b57cec5SDimitry Andric   //
2090b57cec5SDimitry Andric   // ...
2100b57cec5SDimitry Andric   //
2110b57cec5SDimitry Andric   // SegmentOffsets[N]:   <Length>                    (Initially: uninitialized)
2120b57cec5SDimitry Andric   // SegmentOffsets[N]+2: LF_FIELDLIST
2130b57cec5SDimitry Andric   // SegmentOffsets[N]+4: Member[0]
2140b57cec5SDimitry Andric   // SegmentOffsets[N]+?: ...
2150b57cec5SDimitry Andric   // SegmentOffsets[N]+?: Member[t]
2160b57cec5SDimitry Andric   //
2170b57cec5SDimitry Andric   // And this is the way we have laid them out in the serialization buffer.  But
2180b57cec5SDimitry Andric   // we cannot actually commit them to the underlying stream this way, due to
2190b57cec5SDimitry Andric   // the topological sorting requirement of a type stream (specifically,
2200b57cec5SDimitry Andric   // TypeIndex references can only point backwards, not forwards).  So the
2210b57cec5SDimitry Andric   // sequence that we return to the caller contains the records in reverse
2220b57cec5SDimitry Andric   // order, which is the proper order for committing the serialized records.
2230b57cec5SDimitry Andric 
2240b57cec5SDimitry Andric   std::vector<CVType> Types;
2250b57cec5SDimitry Andric   Types.reserve(SegmentOffsets.size());
2260b57cec5SDimitry Andric 
227*bdd1243dSDimitry Andric   ArrayRef SO = SegmentOffsets;
2280b57cec5SDimitry Andric 
2290b57cec5SDimitry Andric   uint32_t End = SegmentWriter.getOffset();
2300b57cec5SDimitry Andric 
231*bdd1243dSDimitry Andric   std::optional<TypeIndex> RefersTo;
2320b57cec5SDimitry Andric   for (uint32_t Offset : reverse(SO)) {
2330b57cec5SDimitry Andric     Types.push_back(createSegmentRecord(Offset, End, RefersTo));
2340b57cec5SDimitry Andric 
2350b57cec5SDimitry Andric     End = Offset;
2360b57cec5SDimitry Andric     RefersTo = Index++;
2370b57cec5SDimitry Andric   }
2380b57cec5SDimitry Andric 
2390b57cec5SDimitry Andric   Kind.reset();
2400b57cec5SDimitry Andric   return Types;
2410b57cec5SDimitry Andric }
2420b57cec5SDimitry Andric 
2430b57cec5SDimitry Andric // Explicitly instantiate the member function for each known type so that we can
2440b57cec5SDimitry Andric // implement this in the cpp file.
2450b57cec5SDimitry Andric #define TYPE_RECORD(EnumName, EnumVal, Name)
2460b57cec5SDimitry Andric #define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
2470b57cec5SDimitry Andric #define MEMBER_RECORD(EnumName, EnumVal, Name)                                 \
2480b57cec5SDimitry Andric   template void llvm::codeview::ContinuationRecordBuilder::writeMemberType(    \
2490b57cec5SDimitry Andric       Name##Record &Record);
2500b57cec5SDimitry Andric #define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
2510b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
252