LLVM 20.0.0git
ContinuationRecordBuilder.cpp
Go to the documentation of this file.
2
3using namespace llvm;
4using namespace llvm::codeview;
5
6namespace {
7struct ContinuationRecord {
8 ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)};
10 ulittle32_t IndexRef{0xB0C0B0C0};
11};
12
13struct SegmentInjection {
14 SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; }
15
16 ContinuationRecord Cont;
18};
19} // namespace
20
21static void addPadding(BinaryStreamWriter &Writer) {
22 uint32_t Align = Writer.getOffset() % 4;
23 if (Align == 0)
24 return;
25
26 int PaddingBytes = 4 - Align;
27 while (PaddingBytes > 0) {
28 uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
29 cantFail(Writer.writeInteger(Pad));
30 --PaddingBytes;
31 }
32}
33
34static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST);
35static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST);
36
37static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord);
38static constexpr uint32_t MaxSegmentLength =
40
42 return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST
43 : LF_METHODLIST;
44}
45
47 : SegmentWriter(Buffer), Mapping(SegmentWriter) {}
48
50
52 assert(!Kind);
53 Kind = RecordKind;
54 Buffer.clear();
55 SegmentWriter.setOffset(0);
56 SegmentOffsets.clear();
57 SegmentOffsets.push_back(0);
58 assert(SegmentWriter.getOffset() == 0);
59 assert(SegmentWriter.getLength() == 0);
60
61 const SegmentInjection *FLI =
65 const uint8_t *FLIB = reinterpret_cast<const uint8_t *>(FLI);
66 InjectedSegmentBytes =
67 ArrayRef<uint8_t>(FLIB, FLIB + sizeof(SegmentInjection));
68
69 // Seed the first record with an appropriate record prefix.
70 RecordPrefix Prefix(getTypeLeafKind(RecordKind));
71 CVType Type(&Prefix, sizeof(Prefix));
73
74 cantFail(SegmentWriter.writeObject(Prefix));
75}
76
77template <typename RecordType>
79 assert(Kind);
80
81 uint32_t OriginalOffset = SegmentWriter.getOffset();
82 CVMemberRecord CVMR;
83 CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind());
84
85 // Member Records aren't length-prefixed, they only have a 2-byte TypeLeafKind
86 // at the beginning.
87 cantFail(SegmentWriter.writeEnum(CVMR.Kind));
88
89 // Let the Mapping handle the rest.
90 cantFail(Mapping.visitMemberBegin(CVMR));
91 cantFail(Mapping.visitKnownMember(CVMR, Record));
92 cantFail(Mapping.visitMemberEnd(CVMR));
93
94 // Make sure it's padded to 4 bytes.
95 addPadding(SegmentWriter);
96 assert(getCurrentSegmentLength() % 4 == 0);
97
98 // The maximum length of a single segment is 64KB minus the size to insert a
99 // continuation. So if we are over that, inject a continuation between the
100 // previous member and the member that was just written, then end the previous
101 // segment after the continuation and begin a new one with the just-written
102 // member.
103 if (getCurrentSegmentLength() > MaxSegmentLength) {
104 // We need to inject some bytes before the member we just wrote but after
105 // the previous member. Save off the length of the member we just wrote so
106 // that we can do validate it.
107 uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset;
108 (void) MemberLength;
109 insertSegmentEnd(OriginalOffset);
110 // Since this member now becomes a new top-level record, it should have
111 // gotten a RecordPrefix injected, and that RecordPrefix + the member we
112 // just wrote should now constitute the entirety of the current "new"
113 // segment.
114 assert(getCurrentSegmentLength() == MemberLength + sizeof(RecordPrefix));
115 }
116
117 assert(getCurrentSegmentLength() % 4 == 0);
118 assert(getCurrentSegmentLength() <= MaxSegmentLength);
119}
120
121uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const {
122 return SegmentWriter.getOffset() - SegmentOffsets.back();
123}
124
125void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) {
126 uint32_t SegmentBegin = SegmentOffsets.back();
127 (void)SegmentBegin;
128 assert(Offset > SegmentBegin);
129 assert(Offset - SegmentBegin <= MaxSegmentLength);
130
131 // We need to make space for the continuation record. For now we can't fill
132 // out the length or the TypeIndex of the back-reference, but we need the
133 // space to at least be there.
134 Buffer.insert(Offset, InjectedSegmentBytes);
135
136 uint32_t NewSegmentBegin = Offset + ContinuationLength;
137 uint32_t SegmentLength = NewSegmentBegin - SegmentOffsets.back();
138 (void) SegmentLength;
139
140 assert(SegmentLength % 4 == 0);
141 assert(SegmentLength <= MaxRecordLength);
142 SegmentOffsets.push_back(NewSegmentBegin);
143
144 // Seek to the end so that we can keep writing against the new segment.
145 SegmentWriter.setOffset(SegmentWriter.getLength());
146 assert(SegmentWriter.bytesRemaining() == 0);
147}
148
149CVType ContinuationRecordBuilder::createSegmentRecord(
150 uint32_t OffBegin, uint32_t OffEnd, std::optional<TypeIndex> RefersTo) {
151 assert(OffEnd - OffBegin <= USHRT_MAX);
152
154 Data = Data.slice(OffBegin, OffEnd - OffBegin);
155
156 // Write the length to the RecordPrefix, making sure it does not include
157 // sizeof(RecordPrefix.Length)
158 RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data());
159 Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen);
160
161 if (RefersTo) {
162 auto Continuation = Data.take_back(ContinuationLength);
163 ContinuationRecord *CR =
164 reinterpret_cast<ContinuationRecord *>(Continuation.data());
165 assert(CR->Kind == TypeLeafKind::LF_INDEX);
166 assert(CR->IndexRef == 0xB0C0B0C0);
167 CR->IndexRef = RefersTo->getIndex();
168 }
169
170 return CVType(Data);
171}
172
174 RecordPrefix Prefix(getTypeLeafKind(*Kind));
175 CVType Type(&Prefix, sizeof(Prefix));
176 cantFail(Mapping.visitTypeEnd(Type));
177
178 // We're now done, and we have a series of segments each beginning at an
179 // offset specified in the SegmentOffsets array. We now need to iterate
180 // over each segment and post-process them in the following two ways:
181 // 1) Each top-level record has a RecordPrefix whose type is either
182 // LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0.
183 // Those should all be set to the correct length now.
184 // 2) Each continuation record has an IndexRef field which we set to the
185 // magic value 0xB0C0B0C0. Now that the caller has told us the TypeIndex
186 // they want this sequence to start from, we can go through and update
187 // each one.
188 //
189 // Logically, the sequence of records we've built up looks like this:
190 //
191 // SegmentOffsets[0]: <Length> (Initially: uninitialized)
192 // SegmentOffsets[0]+2: LF_FIELDLIST
193 // SegmentOffsets[0]+4: Member[0]
194 // SegmentOffsets[0]+?: ...
195 // SegmentOffsets[0]+?: Member[4]
196 // SegmentOffsets[1]-8: LF_INDEX
197 // SegmentOffsets[1]-6: 0
198 // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
199 //
200 // SegmentOffsets[1]: <Length> (Initially: uninitialized)
201 // SegmentOffsets[1]+2: LF_FIELDLIST
202 // SegmentOffsets[1]+4: Member[0]
203 // SegmentOffsets[1]+?: ...
204 // SegmentOffsets[1]+?: Member[s]
205 // SegmentOffsets[2]-8: LF_INDEX
206 // SegmentOffsets[2]-6: 0
207 // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
208 //
209 // ...
210 //
211 // SegmentOffsets[N]: <Length> (Initially: uninitialized)
212 // SegmentOffsets[N]+2: LF_FIELDLIST
213 // SegmentOffsets[N]+4: Member[0]
214 // SegmentOffsets[N]+?: ...
215 // SegmentOffsets[N]+?: Member[t]
216 //
217 // And this is the way we have laid them out in the serialization buffer. But
218 // we cannot actually commit them to the underlying stream this way, due to
219 // the topological sorting requirement of a type stream (specifically,
220 // TypeIndex references can only point backwards, not forwards). So the
221 // sequence that we return to the caller contains the records in reverse
222 // order, which is the proper order for committing the serialized records.
223
224 std::vector<CVType> Types;
225 Types.reserve(SegmentOffsets.size());
226
227 ArrayRef SO = SegmentOffsets;
228
229 uint32_t End = SegmentWriter.getOffset();
230
231 std::optional<TypeIndex> RefersTo;
232 for (uint32_t Offset : reverse(SO)) {
233 Types.push_back(createSegmentRecord(Offset, End, RefersTo));
234
235 End = Offset;
236 RefersTo = Index++;
237 }
238
239 Kind.reset();
240 return Types;
241}
242
243// Explicitly instantiate the member function for each known type so that we can
244// implement this in the cpp file.
245#define TYPE_RECORD(EnumName, EnumVal, Name)
246#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
247#define MEMBER_RECORD(EnumName, EnumVal, Name) \
248 template void llvm::codeview::ContinuationRecordBuilder::writeMemberType( \
249 Name##Record &Record);
250#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
251#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
static constexpr uint32_t ContinuationLength
static TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK)
static constexpr uint32_t MaxSegmentLength
static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST)
static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST)
static void addPadding(BinaryStreamWriter &Writer)
bool End
Definition: ELF_riscv.cpp:480
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
MutableArrayRef< uint8_t > data()
void insert(uint64_t Offset, ArrayRef< uint8_t > Bytes)
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
Provides write only access to a subclass of WritableBinaryStream.
Error writeInteger(T Value)
Write the integer Value to the underlying stream in the specified endianness.
uint64_t bytesRemaining() const
void setOffset(uint64_t Off)
Error writeEnum(T Num)
Similar to writeInteger.
Error writeObject(const T &Obj)
Writes the object Obj to the underlying stream, as if by using memcpy.
MutableArrayRef - Represent a mutable reference to an array (0 or more elements consecutively in memo...
Definition: ArrayRef.h:307
size_t size() const
Definition: SmallVector.h:91
void push_back(const T &Elt)
Definition: SmallVector.h:426
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
void begin(ContinuationRecordKind RecordKind)
std::vector< CVType > end(TypeIndex Index)
A 32-bit type reference.
Definition: TypeIndex.h:96
Error visitTypeBegin(CVType &Record) override
Paired begin/end actions for all types.
Error visitMemberBegin(CVMemberRecord &Record) override
Error visitTypeEnd(CVType &Record) override
Error visitMemberEnd(CVMemberRecord &Record) override
CVRecord< TypeLeafKind > CVType
Definition: CVRecord.h:64
TypeLeafKind
Duplicate copy of the above enum, but using the official CV names.
Definition: CodeView.h:34
detail::packed_endian_specific_integral< uint16_t, llvm::endianness::little, unaligned > ulittle16_t
Definition: Endian.h:282
detail::packed_endian_specific_integral< uint32_t, llvm::endianness::little, unaligned > ulittle32_t
Definition: Endian.h:285
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ Offset
Definition: DWP.cpp:480
auto reverse(ContainerTy &&C)
Definition: STLExtras.h:419
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition: Error.h:756
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39
Helper object to track which of three possible relocation mechanisms are used for a particular value ...