aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/llvm16/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp
blob: 0adbdb59621876509a132110e402b5902f351743 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
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() = 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"