1 //===- LazyRandomTypeCollection.cpp ---------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
10 #include "llvm/ADT/ArrayRef.h"
11 #include "llvm/ADT/STLExtras.h"
12 #include "llvm/ADT/StringRef.h"
13 #include "llvm/DebugInfo/CodeView/CodeView.h"
14 #include "llvm/DebugInfo/CodeView/CodeViewError.h"
15 #include "llvm/DebugInfo/CodeView/RecordName.h"
16 #include "llvm/DebugInfo/CodeView/RecordSerialization.h"
17 #include "llvm/Support/BinaryStreamReader.h"
18 #include "llvm/Support/Error.h"
25 using namespace llvm::codeview
;
27 static void error(Error
&&EC
) {
28 assert(!static_cast<bool>(EC
));
30 consumeError(std::move(EC
));
33 LazyRandomTypeCollection::LazyRandomTypeCollection(uint32_t RecordCountHint
)
34 : LazyRandomTypeCollection(CVTypeArray(), RecordCountHint
,
35 PartialOffsetArray()) {}
37 LazyRandomTypeCollection::LazyRandomTypeCollection(
38 const CVTypeArray
&Types
, uint32_t RecordCountHint
,
39 PartialOffsetArray PartialOffsets
)
40 : NameStorage(Allocator
), Types(Types
), PartialOffsets(PartialOffsets
) {
41 Records
.resize(RecordCountHint
);
44 LazyRandomTypeCollection::LazyRandomTypeCollection(ArrayRef
<uint8_t> Data
,
45 uint32_t RecordCountHint
)
46 : LazyRandomTypeCollection(RecordCountHint
) {
49 LazyRandomTypeCollection::LazyRandomTypeCollection(StringRef Data
,
50 uint32_t RecordCountHint
)
51 : LazyRandomTypeCollection(ArrayRef(Data
.bytes_begin(), Data
.bytes_end()),
54 LazyRandomTypeCollection::LazyRandomTypeCollection(const CVTypeArray
&Types
,
56 : LazyRandomTypeCollection(Types
, NumRecords
, PartialOffsetArray()) {}
58 void LazyRandomTypeCollection::reset(BinaryStreamReader
&Reader
,
59 uint32_t RecordCountHint
) {
61 PartialOffsets
= PartialOffsetArray();
63 error(Reader
.readArray(Types
, Reader
.bytesRemaining()));
65 // Clear and then resize, to make sure existing data gets destroyed.
67 Records
.resize(RecordCountHint
);
70 void LazyRandomTypeCollection::reset(StringRef Data
, uint32_t RecordCountHint
) {
71 BinaryStreamReader
Reader(Data
, llvm::endianness::little
);
72 reset(Reader
, RecordCountHint
);
75 void LazyRandomTypeCollection::reset(ArrayRef
<uint8_t> Data
,
76 uint32_t RecordCountHint
) {
77 BinaryStreamReader
Reader(Data
, llvm::endianness::little
);
78 reset(Reader
, RecordCountHint
);
81 uint32_t LazyRandomTypeCollection::getOffsetOfType(TypeIndex Index
) {
82 error(ensureTypeExists(Index
));
83 assert(contains(Index
));
85 return Records
[Index
.toArrayIndex()].Offset
;
88 CVType
LazyRandomTypeCollection::getType(TypeIndex Index
) {
89 assert(!Index
.isSimple());
91 auto EC
= ensureTypeExists(Index
);
93 assert(contains(Index
));
95 return Records
[Index
.toArrayIndex()].Type
;
98 std::optional
<CVType
> LazyRandomTypeCollection::tryGetType(TypeIndex Index
) {
102 if (auto EC
= ensureTypeExists(Index
)) {
103 consumeError(std::move(EC
));
107 assert(contains(Index
));
108 return Records
[Index
.toArrayIndex()].Type
;
111 StringRef
LazyRandomTypeCollection::getTypeName(TypeIndex Index
) {
112 if (Index
.isNoneType() || Index
.isSimple())
113 return TypeIndex::simpleTypeName(Index
);
115 // Try to make sure the type exists. Even if it doesn't though, it may be
116 // because we're dumping a symbol stream with no corresponding type stream
117 // present, in which case we still want to be able to print <unknown UDT>
118 // for the type names.
119 if (auto EC
= ensureTypeExists(Index
)) {
120 consumeError(std::move(EC
));
121 return "<unknown UDT>";
124 uint32_t I
= Index
.toArrayIndex();
125 ensureCapacityFor(Index
);
126 if (Records
[I
].Name
.data() == nullptr) {
127 StringRef Result
= NameStorage
.save(computeTypeName(*this, Index
));
128 Records
[I
].Name
= Result
;
130 return Records
[I
].Name
;
133 bool LazyRandomTypeCollection::contains(TypeIndex Index
) {
134 if (Index
.isSimple() || Index
.isNoneType())
137 if (Records
.size() <= Index
.toArrayIndex())
139 if (!Records
[Index
.toArrayIndex()].Type
.valid())
144 uint32_t LazyRandomTypeCollection::size() { return Count
; }
146 uint32_t LazyRandomTypeCollection::capacity() { return Records
.size(); }
148 Error
LazyRandomTypeCollection::ensureTypeExists(TypeIndex TI
) {
150 return Error::success();
152 return visitRangeForType(TI
);
155 void LazyRandomTypeCollection::ensureCapacityFor(TypeIndex Index
) {
156 assert(!Index
.isSimple());
157 uint32_t MinSize
= Index
.toArrayIndex() + 1;
159 if (MinSize
<= capacity())
162 uint32_t NewCapacity
= MinSize
* 3 / 2;
164 assert(NewCapacity
> capacity());
165 Records
.resize(NewCapacity
);
168 Error
LazyRandomTypeCollection::visitRangeForType(TypeIndex TI
) {
169 assert(!TI
.isSimple());
170 if (PartialOffsets
.empty())
171 return fullScanForType(TI
);
173 auto Next
= llvm::upper_bound(PartialOffsets
, TI
,
174 [](TypeIndex Value
, const TypeIndexOffset
&IO
) {
175 return Value
< IO
.Type
;
178 assert(Next
!= PartialOffsets
.begin());
179 auto Prev
= std::prev(Next
);
181 TypeIndex TIB
= Prev
->Type
;
183 // They've asked us to fetch a type index, but the entry we found in the
184 // partial offsets array has already been visited. Since we visit an entire
185 // block every time, that means this record should have been previously
186 // discovered. Ultimately, this means this is a request for a non-existent
188 return make_error
<CodeViewError
>("Invalid type index");
192 if (Next
== PartialOffsets
.end()) {
193 TIE
= TypeIndex::fromArrayIndex(capacity());
198 visitRange(TIB
, Prev
->Offset
, TIE
);
199 return Error::success();
202 std::optional
<TypeIndex
> LazyRandomTypeCollection::getFirst() {
203 TypeIndex TI
= TypeIndex::fromArrayIndex(0);
204 if (auto EC
= ensureTypeExists(TI
)) {
205 consumeError(std::move(EC
));
211 std::optional
<TypeIndex
> LazyRandomTypeCollection::getNext(TypeIndex Prev
) {
212 // We can't be sure how long this type stream is, given that the initial count
213 // given to the constructor is just a hint. So just try to make sure the next
214 // record exists, and if anything goes wrong, we must be at the end.
215 if (auto EC
= ensureTypeExists(Prev
+ 1)) {
216 consumeError(std::move(EC
));
223 Error
LazyRandomTypeCollection::fullScanForType(TypeIndex TI
) {
224 assert(!TI
.isSimple());
225 assert(PartialOffsets
.empty());
227 TypeIndex CurrentTI
= TypeIndex::fromArrayIndex(0);
228 auto Begin
= Types
.begin();
231 // In the case of type streams which we don't know the number of records of,
232 // it's possible to search for a type index triggering a full scan, but then
233 // later additional records are added since we didn't know how many there
234 // would be until we did a full visitation, then you try to access the new
235 // type triggering another full scan. To avoid this, we assume that if the
236 // database has some records, this must be what's going on. We can also
237 // assume that this index must be larger than the largest type index we've
238 // visited, so we start from there and scan forward.
239 uint32_t Offset
= Records
[LargestTypeIndex
.toArrayIndex()].Offset
;
240 CurrentTI
= LargestTypeIndex
+ 1;
241 Begin
= Types
.at(Offset
);
245 auto End
= Types
.end();
246 while (Begin
!= End
) {
247 ensureCapacityFor(CurrentTI
);
248 LargestTypeIndex
= std::max(LargestTypeIndex
, CurrentTI
);
249 auto Idx
= CurrentTI
.toArrayIndex();
250 Records
[Idx
].Type
= *Begin
;
251 Records
[Idx
].Offset
= Begin
.offset();
256 if (CurrentTI
<= TI
) {
257 return make_error
<CodeViewError
>("Type Index does not exist!");
259 return Error::success();
262 void LazyRandomTypeCollection::visitRange(TypeIndex Begin
, uint32_t BeginOffset
,
264 auto RI
= Types
.at(BeginOffset
);
265 assert(RI
!= Types
.end());
267 ensureCapacityFor(End
);
268 while (Begin
!= End
) {
269 LargestTypeIndex
= std::max(LargestTypeIndex
, Begin
);
270 auto Idx
= Begin
.toArrayIndex();
271 Records
[Idx
].Type
= *RI
;
272 Records
[Idx
].Offset
= RI
.offset();
279 bool LazyRandomTypeCollection::replaceType(TypeIndex
&Index
, CVType Data
,
281 llvm_unreachable("Method cannot be called");