xref: /src/contrib/llvm-project/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1044eb2f6SDimitry Andric #include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h"
2044eb2f6SDimitry Andric 
3044eb2f6SDimitry Andric using namespace llvm;
4044eb2f6SDimitry Andric using namespace llvm::codeview;
5044eb2f6SDimitry Andric 
6044eb2f6SDimitry Andric namespace {
7044eb2f6SDimitry Andric struct ContinuationRecord {
8044eb2f6SDimitry Andric   ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)};
9044eb2f6SDimitry Andric   ulittle16_t Size{0};
10044eb2f6SDimitry Andric   ulittle32_t IndexRef{0xB0C0B0C0};
11044eb2f6SDimitry Andric };
12044eb2f6SDimitry Andric 
13044eb2f6SDimitry Andric struct SegmentInjection {
SegmentInjection__anond2a7404d0111::SegmentInjection14044eb2f6SDimitry Andric   SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; }
15044eb2f6SDimitry Andric 
16044eb2f6SDimitry Andric   ContinuationRecord Cont;
17044eb2f6SDimitry Andric   RecordPrefix Prefix;
18044eb2f6SDimitry Andric };
19044eb2f6SDimitry Andric } // namespace
20044eb2f6SDimitry Andric 
addPadding(BinaryStreamWriter & Writer)21044eb2f6SDimitry Andric static void addPadding(BinaryStreamWriter &Writer) {
22044eb2f6SDimitry Andric   uint32_t Align = Writer.getOffset() % 4;
23044eb2f6SDimitry Andric   if (Align == 0)
24044eb2f6SDimitry Andric     return;
25044eb2f6SDimitry Andric 
26044eb2f6SDimitry Andric   int PaddingBytes = 4 - Align;
27044eb2f6SDimitry Andric   while (PaddingBytes > 0) {
28044eb2f6SDimitry Andric     uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
29044eb2f6SDimitry Andric     cantFail(Writer.writeInteger(Pad));
30044eb2f6SDimitry Andric     --PaddingBytes;
31044eb2f6SDimitry Andric   }
32044eb2f6SDimitry Andric }
33044eb2f6SDimitry Andric 
34044eb2f6SDimitry Andric static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST);
35044eb2f6SDimitry Andric static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST);
36044eb2f6SDimitry Andric 
37044eb2f6SDimitry Andric static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord);
38044eb2f6SDimitry Andric static constexpr uint32_t MaxSegmentLength =
39044eb2f6SDimitry Andric     MaxRecordLength - ContinuationLength;
40044eb2f6SDimitry Andric 
getTypeLeafKind(ContinuationRecordKind CK)41044eb2f6SDimitry Andric static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) {
42044eb2f6SDimitry Andric   return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST
43044eb2f6SDimitry Andric                                                    : LF_METHODLIST;
44044eb2f6SDimitry Andric }
45044eb2f6SDimitry Andric 
ContinuationRecordBuilder()46044eb2f6SDimitry Andric ContinuationRecordBuilder::ContinuationRecordBuilder()
47044eb2f6SDimitry Andric     : SegmentWriter(Buffer), Mapping(SegmentWriter) {}
48044eb2f6SDimitry Andric 
49145449b1SDimitry Andric ContinuationRecordBuilder::~ContinuationRecordBuilder() = default;
50044eb2f6SDimitry Andric 
begin(ContinuationRecordKind RecordKind)51044eb2f6SDimitry Andric void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) {
52145449b1SDimitry Andric   assert(!Kind);
53044eb2f6SDimitry Andric   Kind = RecordKind;
54044eb2f6SDimitry Andric   Buffer.clear();
55044eb2f6SDimitry Andric   SegmentWriter.setOffset(0);
56044eb2f6SDimitry Andric   SegmentOffsets.clear();
57044eb2f6SDimitry Andric   SegmentOffsets.push_back(0);
58044eb2f6SDimitry Andric   assert(SegmentWriter.getOffset() == 0);
59044eb2f6SDimitry Andric   assert(SegmentWriter.getLength() == 0);
60044eb2f6SDimitry Andric 
61044eb2f6SDimitry Andric   const SegmentInjection *FLI =
62044eb2f6SDimitry Andric       (RecordKind == ContinuationRecordKind::FieldList)
63044eb2f6SDimitry Andric           ? &InjectFieldList
64044eb2f6SDimitry Andric           : &InjectMethodOverloadList;
65044eb2f6SDimitry Andric   const uint8_t *FLIB = reinterpret_cast<const uint8_t *>(FLI);
66044eb2f6SDimitry Andric   InjectedSegmentBytes =
67044eb2f6SDimitry Andric       ArrayRef<uint8_t>(FLIB, FLIB + sizeof(SegmentInjection));
68044eb2f6SDimitry Andric 
69e6d15924SDimitry Andric   // Seed the first record with an appropriate record prefix.
70e6d15924SDimitry Andric   RecordPrefix Prefix(getTypeLeafKind(RecordKind));
71e6d15924SDimitry Andric   CVType Type(&Prefix, sizeof(Prefix));
72044eb2f6SDimitry Andric   cantFail(Mapping.visitTypeBegin(Type));
73044eb2f6SDimitry Andric 
74044eb2f6SDimitry Andric   cantFail(SegmentWriter.writeObject(Prefix));
75044eb2f6SDimitry Andric }
76044eb2f6SDimitry Andric 
77044eb2f6SDimitry Andric template <typename RecordType>
writeMemberType(RecordType & Record)78044eb2f6SDimitry Andric void ContinuationRecordBuilder::writeMemberType(RecordType &Record) {
79145449b1SDimitry Andric   assert(Kind);
80044eb2f6SDimitry Andric 
81044eb2f6SDimitry Andric   uint32_t OriginalOffset = SegmentWriter.getOffset();
82044eb2f6SDimitry Andric   CVMemberRecord CVMR;
83044eb2f6SDimitry Andric   CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind());
84044eb2f6SDimitry Andric 
85044eb2f6SDimitry Andric   // Member Records aren't length-prefixed, they only have a 2-byte TypeLeafKind
86044eb2f6SDimitry Andric   // at the beginning.
87044eb2f6SDimitry Andric   cantFail(SegmentWriter.writeEnum(CVMR.Kind));
88044eb2f6SDimitry Andric 
89044eb2f6SDimitry Andric   // Let the Mapping handle the rest.
90044eb2f6SDimitry Andric   cantFail(Mapping.visitMemberBegin(CVMR));
91044eb2f6SDimitry Andric   cantFail(Mapping.visitKnownMember(CVMR, Record));
92044eb2f6SDimitry Andric   cantFail(Mapping.visitMemberEnd(CVMR));
93044eb2f6SDimitry Andric 
94044eb2f6SDimitry Andric   // Make sure it's padded to 4 bytes.
95044eb2f6SDimitry Andric   addPadding(SegmentWriter);
96044eb2f6SDimitry Andric   assert(getCurrentSegmentLength() % 4 == 0);
97044eb2f6SDimitry Andric 
98044eb2f6SDimitry Andric   // The maximum length of a single segment is 64KB minus the size to insert a
99044eb2f6SDimitry Andric   // continuation.  So if we are over that, inject a continuation between the
100044eb2f6SDimitry Andric   // previous member and the member that was just written, then end the previous
101044eb2f6SDimitry Andric   // segment after the continuation and begin a new one with the just-written
102044eb2f6SDimitry Andric   // member.
103044eb2f6SDimitry Andric   if (getCurrentSegmentLength() > MaxSegmentLength) {
104044eb2f6SDimitry Andric     // We need to inject some bytes before the member we just wrote but after
105044eb2f6SDimitry Andric     // the previous member.  Save off the length of the member we just wrote so
106c0981da4SDimitry Andric     // that we can do validate it.
107044eb2f6SDimitry Andric     uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset;
108044eb2f6SDimitry Andric     (void) MemberLength;
109044eb2f6SDimitry Andric     insertSegmentEnd(OriginalOffset);
110044eb2f6SDimitry Andric     // Since this member now becomes a new top-level record, it should have
111044eb2f6SDimitry Andric     // gotten a RecordPrefix injected, and that RecordPrefix + the member we
112044eb2f6SDimitry Andric     // just wrote should now constitute the entirety of the current "new"
113044eb2f6SDimitry Andric     // segment.
114044eb2f6SDimitry Andric     assert(getCurrentSegmentLength() == MemberLength + sizeof(RecordPrefix));
115044eb2f6SDimitry Andric   }
116044eb2f6SDimitry Andric 
117044eb2f6SDimitry Andric   assert(getCurrentSegmentLength() % 4 == 0);
118044eb2f6SDimitry Andric   assert(getCurrentSegmentLength() <= MaxSegmentLength);
119044eb2f6SDimitry Andric }
120044eb2f6SDimitry Andric 
getCurrentSegmentLength() const121044eb2f6SDimitry Andric uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const {
122044eb2f6SDimitry Andric   return SegmentWriter.getOffset() - SegmentOffsets.back();
123044eb2f6SDimitry Andric }
124044eb2f6SDimitry Andric 
insertSegmentEnd(uint32_t Offset)125044eb2f6SDimitry Andric void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) {
126044eb2f6SDimitry Andric   uint32_t SegmentBegin = SegmentOffsets.back();
127044eb2f6SDimitry Andric   (void)SegmentBegin;
128044eb2f6SDimitry Andric   assert(Offset > SegmentBegin);
129044eb2f6SDimitry Andric   assert(Offset - SegmentBegin <= MaxSegmentLength);
130044eb2f6SDimitry Andric 
131044eb2f6SDimitry Andric   // We need to make space for the continuation record.  For now we can't fill
132044eb2f6SDimitry Andric   // out the length or the TypeIndex of the back-reference, but we need the
133044eb2f6SDimitry Andric   // space to at least be there.
134044eb2f6SDimitry Andric   Buffer.insert(Offset, InjectedSegmentBytes);
135044eb2f6SDimitry Andric 
136044eb2f6SDimitry Andric   uint32_t NewSegmentBegin = Offset + ContinuationLength;
137044eb2f6SDimitry Andric   uint32_t SegmentLength = NewSegmentBegin - SegmentOffsets.back();
138044eb2f6SDimitry Andric   (void) SegmentLength;
139044eb2f6SDimitry Andric 
140044eb2f6SDimitry Andric   assert(SegmentLength % 4 == 0);
141044eb2f6SDimitry Andric   assert(SegmentLength <= MaxRecordLength);
142044eb2f6SDimitry Andric   SegmentOffsets.push_back(NewSegmentBegin);
143044eb2f6SDimitry Andric 
144044eb2f6SDimitry Andric   // Seek to the end so that we can keep writing against the new segment.
145044eb2f6SDimitry Andric   SegmentWriter.setOffset(SegmentWriter.getLength());
146044eb2f6SDimitry Andric   assert(SegmentWriter.bytesRemaining() == 0);
147044eb2f6SDimitry Andric }
148044eb2f6SDimitry Andric 
createSegmentRecord(uint32_t OffBegin,uint32_t OffEnd,std::optional<TypeIndex> RefersTo)149044eb2f6SDimitry Andric CVType ContinuationRecordBuilder::createSegmentRecord(
150e3b55780SDimitry Andric     uint32_t OffBegin, uint32_t OffEnd, std::optional<TypeIndex> RefersTo) {
151044eb2f6SDimitry Andric   assert(OffEnd - OffBegin <= USHRT_MAX);
152044eb2f6SDimitry Andric 
153044eb2f6SDimitry Andric   MutableArrayRef<uint8_t> Data = Buffer.data();
154044eb2f6SDimitry Andric   Data = Data.slice(OffBegin, OffEnd - OffBegin);
155044eb2f6SDimitry Andric 
156044eb2f6SDimitry Andric   // Write the length to the RecordPrefix, making sure it does not include
157044eb2f6SDimitry Andric   // sizeof(RecordPrefix.Length)
158044eb2f6SDimitry Andric   RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data());
159044eb2f6SDimitry Andric   Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen);
160044eb2f6SDimitry Andric 
161145449b1SDimitry Andric   if (RefersTo) {
162044eb2f6SDimitry Andric     auto Continuation = Data.take_back(ContinuationLength);
163044eb2f6SDimitry Andric     ContinuationRecord *CR =
164044eb2f6SDimitry Andric         reinterpret_cast<ContinuationRecord *>(Continuation.data());
165044eb2f6SDimitry Andric     assert(CR->Kind == TypeLeafKind::LF_INDEX);
166044eb2f6SDimitry Andric     assert(CR->IndexRef == 0xB0C0B0C0);
167044eb2f6SDimitry Andric     CR->IndexRef = RefersTo->getIndex();
168044eb2f6SDimitry Andric   }
169044eb2f6SDimitry Andric 
170e6d15924SDimitry Andric   return CVType(Data);
171044eb2f6SDimitry Andric }
172044eb2f6SDimitry Andric 
end(TypeIndex Index)173044eb2f6SDimitry Andric std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) {
174e6d15924SDimitry Andric   RecordPrefix Prefix(getTypeLeafKind(*Kind));
175e6d15924SDimitry Andric   CVType Type(&Prefix, sizeof(Prefix));
176044eb2f6SDimitry Andric   cantFail(Mapping.visitTypeEnd(Type));
177044eb2f6SDimitry Andric 
178044eb2f6SDimitry Andric   // We're now done, and we have a series of segments each beginning at an
179044eb2f6SDimitry Andric   // offset specified in the SegmentOffsets array.  We now need to iterate
180044eb2f6SDimitry Andric   // over each segment and post-process them in the following two ways:
181044eb2f6SDimitry Andric   // 1) Each top-level record has a RecordPrefix whose type is either
182044eb2f6SDimitry Andric   //    LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0.
183044eb2f6SDimitry Andric   //    Those should all be set to the correct length now.
184044eb2f6SDimitry Andric   // 2) Each continuation record has an IndexRef field which we set to the
185044eb2f6SDimitry Andric   //    magic value 0xB0C0B0C0.  Now that the caller has told us the TypeIndex
186044eb2f6SDimitry Andric   //    they want this sequence to start from, we can go through and update
187044eb2f6SDimitry Andric   //    each one.
188044eb2f6SDimitry Andric   //
189044eb2f6SDimitry Andric   // Logically, the sequence of records we've built up looks like this:
190044eb2f6SDimitry Andric   //
191044eb2f6SDimitry Andric   // SegmentOffsets[0]:   <Length>                    (Initially: uninitialized)
192044eb2f6SDimitry Andric   // SegmentOffsets[0]+2: LF_FIELDLIST
193044eb2f6SDimitry Andric   // SegmentOffsets[0]+4: Member[0]
194044eb2f6SDimitry Andric   // SegmentOffsets[0]+?: ...
195044eb2f6SDimitry Andric   // SegmentOffsets[0]+?: Member[4]
196044eb2f6SDimitry Andric   // SegmentOffsets[1]-8: LF_INDEX
197044eb2f6SDimitry Andric   // SegmentOffsets[1]-6: 0
198044eb2f6SDimitry Andric   // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
199044eb2f6SDimitry Andric   //
200044eb2f6SDimitry Andric   // SegmentOffsets[1]:   <Length>                    (Initially: uninitialized)
201044eb2f6SDimitry Andric   // SegmentOffsets[1]+2: LF_FIELDLIST
202044eb2f6SDimitry Andric   // SegmentOffsets[1]+4: Member[0]
203044eb2f6SDimitry Andric   // SegmentOffsets[1]+?: ...
204044eb2f6SDimitry Andric   // SegmentOffsets[1]+?: Member[s]
205044eb2f6SDimitry Andric   // SegmentOffsets[2]-8: LF_INDEX
206044eb2f6SDimitry Andric   // SegmentOffsets[2]-6: 0
207044eb2f6SDimitry Andric   // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
208044eb2f6SDimitry Andric   //
209044eb2f6SDimitry Andric   // ...
210044eb2f6SDimitry Andric   //
211044eb2f6SDimitry Andric   // SegmentOffsets[N]:   <Length>                    (Initially: uninitialized)
212044eb2f6SDimitry Andric   // SegmentOffsets[N]+2: LF_FIELDLIST
213044eb2f6SDimitry Andric   // SegmentOffsets[N]+4: Member[0]
214044eb2f6SDimitry Andric   // SegmentOffsets[N]+?: ...
215044eb2f6SDimitry Andric   // SegmentOffsets[N]+?: Member[t]
216044eb2f6SDimitry Andric   //
217044eb2f6SDimitry Andric   // And this is the way we have laid them out in the serialization buffer.  But
218044eb2f6SDimitry Andric   // we cannot actually commit them to the underlying stream this way, due to
219044eb2f6SDimitry Andric   // the topological sorting requirement of a type stream (specifically,
220044eb2f6SDimitry Andric   // TypeIndex references can only point backwards, not forwards).  So the
221044eb2f6SDimitry Andric   // sequence that we return to the caller contains the records in reverse
222044eb2f6SDimitry Andric   // order, which is the proper order for committing the serialized records.
223044eb2f6SDimitry Andric 
224044eb2f6SDimitry Andric   std::vector<CVType> Types;
225044eb2f6SDimitry Andric   Types.reserve(SegmentOffsets.size());
226044eb2f6SDimitry Andric 
227e3b55780SDimitry Andric   ArrayRef SO = SegmentOffsets;
228044eb2f6SDimitry Andric 
229044eb2f6SDimitry Andric   uint32_t End = SegmentWriter.getOffset();
230044eb2f6SDimitry Andric 
231e3b55780SDimitry Andric   std::optional<TypeIndex> RefersTo;
232044eb2f6SDimitry Andric   for (uint32_t Offset : reverse(SO)) {
233044eb2f6SDimitry Andric     Types.push_back(createSegmentRecord(Offset, End, RefersTo));
234044eb2f6SDimitry Andric 
235044eb2f6SDimitry Andric     End = Offset;
236044eb2f6SDimitry Andric     RefersTo = Index++;
237044eb2f6SDimitry Andric   }
238044eb2f6SDimitry Andric 
239044eb2f6SDimitry Andric   Kind.reset();
240044eb2f6SDimitry Andric   return Types;
241044eb2f6SDimitry Andric }
242044eb2f6SDimitry Andric 
243044eb2f6SDimitry Andric // Explicitly instantiate the member function for each known type so that we can
244044eb2f6SDimitry Andric // implement this in the cpp file.
245044eb2f6SDimitry Andric #define TYPE_RECORD(EnumName, EnumVal, Name)
246044eb2f6SDimitry Andric #define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
247044eb2f6SDimitry Andric #define MEMBER_RECORD(EnumName, EnumVal, Name)                                 \
248044eb2f6SDimitry Andric   template void llvm::codeview::ContinuationRecordBuilder::writeMemberType(    \
249044eb2f6SDimitry Andric       Name##Record &Record);
250044eb2f6SDimitry Andric #define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
251044eb2f6SDimitry Andric #include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
252