1 | //===-- APINotesWriter.cpp - API Notes Writer -------------------*- C++ -*-===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "clang/APINotes/APINotesWriter.h" |
10 | #include "APINotesFormat.h" |
11 | #include "clang/APINotes/Types.h" |
12 | #include "clang/Basic/FileManager.h" |
13 | #include "llvm/ADT/DenseMap.h" |
14 | #include "llvm/ADT/StringMap.h" |
15 | #include "llvm/Bitstream/BitstreamWriter.h" |
16 | #include "llvm/Support/DJB.h" |
17 | #include "llvm/Support/OnDiskHashTable.h" |
18 | #include "llvm/Support/VersionTuple.h" |
19 | |
20 | namespace clang { |
21 | namespace api_notes { |
22 | class APINotesWriter::Implementation { |
23 | friend class APINotesWriter; |
24 | |
25 | template <typename T> |
26 | using VersionedSmallVector = |
27 | llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1>; |
28 | |
29 | std::string ModuleName; |
30 | const FileEntry *SourceFile; |
31 | |
32 | /// Scratch space for bitstream writing. |
33 | llvm::SmallVector<uint64_t, 64> Scratch; |
34 | |
35 | /// Mapping from strings to identifier IDs. |
36 | llvm::StringMap<IdentifierID> IdentifierIDs; |
37 | |
38 | /// Information about contexts (Objective-C classes or protocols or C++ |
39 | /// namespaces). |
40 | /// |
41 | /// Indexed by the parent context ID, context kind and the identifier ID of |
42 | /// this context and provides both the context ID and information describing |
43 | /// the context within that module. |
44 | llvm::DenseMap<ContextTableKey, |
45 | std::pair<unsigned, VersionedSmallVector<ContextInfo>>> |
46 | Contexts; |
47 | |
48 | /// Information about parent contexts for each context. |
49 | /// |
50 | /// Indexed by context ID, provides the parent context ID. |
51 | llvm::DenseMap<uint32_t, uint32_t> ParentContexts; |
52 | |
53 | /// Mapping from context IDs to the identifier ID holding the name. |
54 | llvm::DenseMap<unsigned, unsigned> ContextNames; |
55 | |
56 | /// Information about Objective-C properties. |
57 | /// |
58 | /// Indexed by the context ID, property name, and whether this is an |
59 | /// instance property. |
60 | llvm::DenseMap< |
61 | std::tuple<unsigned, unsigned, char>, |
62 | llvm::SmallVector<std::pair<VersionTuple, ObjCPropertyInfo>, 1>> |
63 | ObjCProperties; |
64 | |
65 | /// Information about Objective-C methods. |
66 | /// |
67 | /// Indexed by the context ID, selector ID, and Boolean (stored as a char) |
68 | /// indicating whether this is a class or instance method. |
69 | llvm::DenseMap<std::tuple<unsigned, unsigned, char>, |
70 | llvm::SmallVector<std::pair<VersionTuple, ObjCMethodInfo>, 1>> |
71 | ObjCMethods; |
72 | |
73 | /// Information about C++ methods. |
74 | /// |
75 | /// Indexed by the context ID and name ID. |
76 | llvm::DenseMap<SingleDeclTableKey, |
77 | llvm::SmallVector<std::pair<VersionTuple, CXXMethodInfo>, 1>> |
78 | CXXMethods; |
79 | |
80 | /// Mapping from selectors to selector ID. |
81 | llvm::DenseMap<StoredObjCSelector, SelectorID> SelectorIDs; |
82 | |
83 | /// Information about global variables. |
84 | /// |
85 | /// Indexed by the context ID, identifier ID. |
86 | llvm::DenseMap< |
87 | SingleDeclTableKey, |
88 | llvm::SmallVector<std::pair<VersionTuple, GlobalVariableInfo>, 1>> |
89 | GlobalVariables; |
90 | |
91 | /// Information about global functions. |
92 | /// |
93 | /// Indexed by the context ID, identifier ID. |
94 | llvm::DenseMap< |
95 | SingleDeclTableKey, |
96 | llvm::SmallVector<std::pair<VersionTuple, GlobalFunctionInfo>, 1>> |
97 | GlobalFunctions; |
98 | |
99 | /// Information about enumerators. |
100 | /// |
101 | /// Indexed by the identifier ID. |
102 | llvm::DenseMap< |
103 | unsigned, llvm::SmallVector<std::pair<VersionTuple, EnumConstantInfo>, 1>> |
104 | EnumConstants; |
105 | |
106 | /// Information about tags. |
107 | /// |
108 | /// Indexed by the context ID, identifier ID. |
109 | llvm::DenseMap<SingleDeclTableKey, |
110 | llvm::SmallVector<std::pair<VersionTuple, TagInfo>, 1>> |
111 | Tags; |
112 | |
113 | /// Information about typedefs. |
114 | /// |
115 | /// Indexed by the context ID, identifier ID. |
116 | llvm::DenseMap<SingleDeclTableKey, |
117 | llvm::SmallVector<std::pair<VersionTuple, TypedefInfo>, 1>> |
118 | Typedefs; |
119 | |
120 | /// Retrieve the ID for the given identifier. |
121 | IdentifierID getIdentifier(StringRef Identifier) { |
122 | if (Identifier.empty()) |
123 | return 0; |
124 | |
125 | auto Known = IdentifierIDs.find(Key: Identifier); |
126 | if (Known != IdentifierIDs.end()) |
127 | return Known->second; |
128 | |
129 | // Add to the identifier table. |
130 | Known = IdentifierIDs.insert(KV: {Identifier, IdentifierIDs.size() + 1}).first; |
131 | return Known->second; |
132 | } |
133 | |
134 | /// Retrieve the ID for the given selector. |
135 | SelectorID getSelector(ObjCSelectorRef SelectorRef) { |
136 | // Translate the selector reference into a stored selector. |
137 | StoredObjCSelector Selector; |
138 | Selector.NumArgs = SelectorRef.NumArgs; |
139 | Selector.Identifiers.reserve(N: SelectorRef.Identifiers.size()); |
140 | for (auto piece : SelectorRef.Identifiers) |
141 | Selector.Identifiers.push_back(Elt: getIdentifier(Identifier: piece)); |
142 | |
143 | // Look for the stored selector. |
144 | auto Known = SelectorIDs.find(Val: Selector); |
145 | if (Known != SelectorIDs.end()) |
146 | return Known->second; |
147 | |
148 | // Add to the selector table. |
149 | Known = SelectorIDs.insert(KV: {Selector, SelectorIDs.size()}).first; |
150 | return Known->second; |
151 | } |
152 | |
153 | private: |
154 | void writeBlockInfoBlock(llvm::BitstreamWriter &Stream); |
155 | void writeControlBlock(llvm::BitstreamWriter &Stream); |
156 | void writeIdentifierBlock(llvm::BitstreamWriter &Stream); |
157 | void writeContextBlock(llvm::BitstreamWriter &Stream); |
158 | void writeObjCPropertyBlock(llvm::BitstreamWriter &Stream); |
159 | void writeObjCMethodBlock(llvm::BitstreamWriter &Stream); |
160 | void writeCXXMethodBlock(llvm::BitstreamWriter &Stream); |
161 | void writeObjCSelectorBlock(llvm::BitstreamWriter &Stream); |
162 | void writeGlobalVariableBlock(llvm::BitstreamWriter &Stream); |
163 | void writeGlobalFunctionBlock(llvm::BitstreamWriter &Stream); |
164 | void writeEnumConstantBlock(llvm::BitstreamWriter &Stream); |
165 | void writeTagBlock(llvm::BitstreamWriter &Stream); |
166 | void writeTypedefBlock(llvm::BitstreamWriter &Stream); |
167 | |
168 | public: |
169 | Implementation(llvm::StringRef ModuleName, const FileEntry *SF) |
170 | : ModuleName(std::string(ModuleName)), SourceFile(SF) {} |
171 | |
172 | void writeToStream(llvm::raw_ostream &OS); |
173 | }; |
174 | |
175 | void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &OS) { |
176 | llvm::SmallVector<char, 0> Buffer; |
177 | |
178 | { |
179 | llvm::BitstreamWriter Stream(Buffer); |
180 | |
181 | // Emit the signature. |
182 | for (unsigned char Byte : API_NOTES_SIGNATURE) |
183 | Stream.Emit(Val: Byte, NumBits: 8); |
184 | |
185 | // Emit the blocks. |
186 | writeBlockInfoBlock(Stream); |
187 | writeControlBlock(Stream); |
188 | writeIdentifierBlock(Stream); |
189 | writeContextBlock(Stream); |
190 | writeObjCPropertyBlock(Stream); |
191 | writeObjCMethodBlock(Stream); |
192 | writeCXXMethodBlock(Stream); |
193 | writeObjCSelectorBlock(Stream); |
194 | writeGlobalVariableBlock(Stream); |
195 | writeGlobalFunctionBlock(Stream); |
196 | writeEnumConstantBlock(Stream); |
197 | writeTagBlock(Stream); |
198 | writeTypedefBlock(Stream); |
199 | } |
200 | |
201 | OS.write(Ptr: Buffer.data(), Size: Buffer.size()); |
202 | OS.flush(); |
203 | } |
204 | |
205 | namespace { |
206 | /// Record the name of a block. |
207 | void emitBlockID(llvm::BitstreamWriter &Stream, unsigned ID, |
208 | llvm::StringRef Name) { |
209 | Stream.EmitRecord(Code: llvm::bitc::BLOCKINFO_CODE_SETBID, |
210 | Vals: llvm::ArrayRef<unsigned>{ID}); |
211 | |
212 | // Emit the block name if present. |
213 | if (Name.empty()) |
214 | return; |
215 | Stream.EmitRecord( |
216 | Code: llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, |
217 | Vals: llvm::ArrayRef<unsigned char>( |
218 | const_cast<unsigned char *>( |
219 | reinterpret_cast<const unsigned char *>(Name.data())), |
220 | Name.size())); |
221 | } |
222 | |
223 | /// Record the name of a record within a block. |
224 | void emitRecordID(llvm::BitstreamWriter &Stream, unsigned ID, |
225 | llvm::StringRef Name) { |
226 | assert(ID < 256 && "can't fit record ID in next to name" ); |
227 | |
228 | llvm::SmallVector<unsigned char, 64> Buffer; |
229 | Buffer.resize(N: Name.size() + 1); |
230 | Buffer[0] = ID; |
231 | memcpy(dest: Buffer.data() + 1, src: Name.data(), n: Name.size()); |
232 | |
233 | Stream.EmitRecord(Code: llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Vals: Buffer); |
234 | } |
235 | } // namespace |
236 | |
237 | void APINotesWriter::Implementation::writeBlockInfoBlock( |
238 | llvm::BitstreamWriter &Stream) { |
239 | llvm::BCBlockRAII Scope(Stream, llvm::bitc::BLOCKINFO_BLOCK_ID, 2); |
240 | |
241 | #define BLOCK(Block) emitBlockID(Stream, Block##_ID, #Block) |
242 | #define BLOCK_RECORD(NameSpace, Block) \ |
243 | emitRecordID(Stream, NameSpace::Block, #Block) |
244 | BLOCK(CONTROL_BLOCK); |
245 | BLOCK_RECORD(control_block, METADATA); |
246 | BLOCK_RECORD(control_block, MODULE_NAME); |
247 | |
248 | BLOCK(IDENTIFIER_BLOCK); |
249 | BLOCK_RECORD(identifier_block, IDENTIFIER_DATA); |
250 | |
251 | BLOCK(OBJC_CONTEXT_BLOCK); |
252 | BLOCK_RECORD(context_block, CONTEXT_ID_DATA); |
253 | |
254 | BLOCK(OBJC_PROPERTY_BLOCK); |
255 | BLOCK_RECORD(objc_property_block, OBJC_PROPERTY_DATA); |
256 | |
257 | BLOCK(OBJC_METHOD_BLOCK); |
258 | BLOCK_RECORD(objc_method_block, OBJC_METHOD_DATA); |
259 | |
260 | BLOCK(OBJC_SELECTOR_BLOCK); |
261 | BLOCK_RECORD(objc_selector_block, OBJC_SELECTOR_DATA); |
262 | |
263 | BLOCK(GLOBAL_VARIABLE_BLOCK); |
264 | BLOCK_RECORD(global_variable_block, GLOBAL_VARIABLE_DATA); |
265 | |
266 | BLOCK(GLOBAL_FUNCTION_BLOCK); |
267 | BLOCK_RECORD(global_function_block, GLOBAL_FUNCTION_DATA); |
268 | #undef BLOCK_RECORD |
269 | #undef BLOCK |
270 | } |
271 | |
272 | void APINotesWriter::Implementation::writeControlBlock( |
273 | llvm::BitstreamWriter &Stream) { |
274 | llvm::BCBlockRAII Scope(Stream, CONTROL_BLOCK_ID, 3); |
275 | |
276 | control_block::MetadataLayout Metadata(Stream); |
277 | Metadata.emit(buffer&: Scratch, data: VERSION_MAJOR, data: VERSION_MINOR); |
278 | |
279 | control_block::ModuleNameLayout ModuleName(Stream); |
280 | ModuleName.emit(buffer&: Scratch, data&: this->ModuleName); |
281 | |
282 | if (SourceFile) { |
283 | control_block::SourceFileLayout SourceFile(Stream); |
284 | SourceFile.emit(buffer&: Scratch, data: this->SourceFile->getSize(), |
285 | data: this->SourceFile->getModificationTime()); |
286 | } |
287 | } |
288 | |
289 | namespace { |
290 | /// Used to serialize the on-disk identifier table. |
291 | class IdentifierTableInfo { |
292 | public: |
293 | using key_type = StringRef; |
294 | using key_type_ref = key_type; |
295 | using data_type = IdentifierID; |
296 | using data_type_ref = const data_type &; |
297 | using hash_value_type = uint32_t; |
298 | using offset_type = unsigned; |
299 | |
300 | hash_value_type ComputeHash(key_type_ref Key) { return llvm::djbHash(Buffer: Key); } |
301 | |
302 | std::pair<unsigned, unsigned> |
303 | EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref) { |
304 | uint32_t KeyLength = Key.size(); |
305 | uint32_t DataLength = sizeof(uint32_t); |
306 | |
307 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
308 | writer.write<uint16_t>(Val: KeyLength); |
309 | writer.write<uint16_t>(Val: DataLength); |
310 | return {KeyLength, DataLength}; |
311 | } |
312 | |
313 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { OS << Key; } |
314 | |
315 | void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) { |
316 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
317 | writer.write<uint32_t>(Val: Data); |
318 | } |
319 | }; |
320 | } // namespace |
321 | |
322 | void APINotesWriter::Implementation::writeIdentifierBlock( |
323 | llvm::BitstreamWriter &Stream) { |
324 | llvm::BCBlockRAII restoreBlock(Stream, IDENTIFIER_BLOCK_ID, 3); |
325 | |
326 | if (IdentifierIDs.empty()) |
327 | return; |
328 | |
329 | llvm::SmallString<4096> HashTableBlob; |
330 | uint32_t Offset; |
331 | { |
332 | llvm::OnDiskChainedHashTableGenerator<IdentifierTableInfo> Generator; |
333 | for (auto &II : IdentifierIDs) |
334 | Generator.insert(Key: II.first(), Data: II.second); |
335 | |
336 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
337 | // Make sure that no bucket is at offset 0 |
338 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
339 | endian: llvm::endianness::little); |
340 | Offset = Generator.Emit(Out&: BlobStream); |
341 | } |
342 | |
343 | identifier_block::IdentifierDataLayout IdentifierData(Stream); |
344 | IdentifierData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
345 | } |
346 | |
347 | namespace { |
348 | /// Used to serialize the on-disk Objective-C context table. |
349 | class ContextIDTableInfo { |
350 | public: |
351 | using key_type = ContextTableKey; |
352 | using key_type_ref = key_type; |
353 | using data_type = unsigned; |
354 | using data_type_ref = const data_type &; |
355 | using hash_value_type = size_t; |
356 | using offset_type = unsigned; |
357 | |
358 | hash_value_type ComputeHash(key_type_ref Key) { |
359 | return static_cast<size_t>(Key.hashValue()); |
360 | } |
361 | |
362 | std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &OS, key_type_ref, |
363 | data_type_ref) { |
364 | uint32_t KeyLength = sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint32_t); |
365 | uint32_t DataLength = sizeof(uint32_t); |
366 | |
367 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
368 | writer.write<uint16_t>(Val: KeyLength); |
369 | writer.write<uint16_t>(Val: DataLength); |
370 | return {KeyLength, DataLength}; |
371 | } |
372 | |
373 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
374 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
375 | writer.write<uint32_t>(Val: Key.parentContextID); |
376 | writer.write<uint8_t>(Val: Key.contextKind); |
377 | writer.write<uint32_t>(Val: Key.contextID); |
378 | } |
379 | |
380 | void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) { |
381 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
382 | writer.write<uint32_t>(Val: Data); |
383 | } |
384 | }; |
385 | |
386 | /// Localized helper to make a type dependent, thwarting template argument |
387 | /// deduction. |
388 | template <typename T> struct MakeDependent { typedef T Type; }; |
389 | |
390 | /// Retrieve the serialized size of the given VersionTuple, for use in |
391 | /// on-disk hash tables. |
392 | unsigned getVersionTupleSize(const VersionTuple &VT) { |
393 | unsigned size = sizeof(uint8_t) + /*major*/ sizeof(uint32_t); |
394 | if (VT.getMinor()) |
395 | size += sizeof(uint32_t); |
396 | if (VT.getSubminor()) |
397 | size += sizeof(uint32_t); |
398 | if (VT.getBuild()) |
399 | size += sizeof(uint32_t); |
400 | return size; |
401 | } |
402 | |
403 | /// Determine the size of an array of versioned information, |
404 | template <typename T> |
405 | unsigned getVersionedInfoSize( |
406 | const llvm::SmallVectorImpl<std::pair<llvm::VersionTuple, T>> &VI, |
407 | llvm::function_ref<unsigned(const typename MakeDependent<T>::Type &)> |
408 | getInfoSize) { |
409 | unsigned result = sizeof(uint16_t); // # of elements |
410 | for (const auto &E : VI) { |
411 | result += getVersionTupleSize(E.first); |
412 | result += getInfoSize(E.second); |
413 | } |
414 | return result; |
415 | } |
416 | |
417 | /// Emit a serialized representation of a version tuple. |
418 | void emitVersionTuple(raw_ostream &OS, const VersionTuple &VT) { |
419 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
420 | |
421 | // First byte contains the number of components beyond the 'major' component. |
422 | uint8_t descriptor; |
423 | if (VT.getBuild()) |
424 | descriptor = 3; |
425 | else if (VT.getSubminor()) |
426 | descriptor = 2; |
427 | else if (VT.getMinor()) |
428 | descriptor = 1; |
429 | else |
430 | descriptor = 0; |
431 | writer.write<uint8_t>(Val: descriptor); |
432 | |
433 | // Write the components. |
434 | writer.write<uint32_t>(Val: VT.getMajor()); |
435 | if (auto minor = VT.getMinor()) |
436 | writer.write<uint32_t>(Val: *minor); |
437 | if (auto subminor = VT.getSubminor()) |
438 | writer.write<uint32_t>(Val: *subminor); |
439 | if (auto build = VT.getBuild()) |
440 | writer.write<uint32_t>(Val: *build); |
441 | } |
442 | |
443 | /// Emit versioned information. |
444 | template <typename T> |
445 | void emitVersionedInfo( |
446 | raw_ostream &OS, llvm::SmallVectorImpl<std::pair<VersionTuple, T>> &VI, |
447 | llvm::function_ref<void(raw_ostream &, |
448 | const typename MakeDependent<T>::Type &)> |
449 | emitInfo) { |
450 | std::sort(VI.begin(), VI.end(), |
451 | [](const std::pair<VersionTuple, T> &LHS, |
452 | const std::pair<VersionTuple, T> &RHS) -> bool { |
453 | assert((&LHS == &RHS || LHS.first != RHS.first) && |
454 | "two entries for the same version" ); |
455 | return LHS.first < RHS.first; |
456 | }); |
457 | |
458 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
459 | writer.write<uint16_t>(VI.size()); |
460 | for (const auto &E : VI) { |
461 | emitVersionTuple(OS, E.first); |
462 | emitInfo(OS, E.second); |
463 | } |
464 | } |
465 | |
466 | /// On-disk hash table info key base for handling versioned data. |
467 | template <typename Derived, typename KeyType, typename UnversionedDataType> |
468 | class VersionedTableInfo { |
469 | Derived &asDerived() { return *static_cast<Derived *>(this); } |
470 | |
471 | const Derived &asDerived() const { |
472 | return *static_cast<const Derived *>(this); |
473 | } |
474 | |
475 | public: |
476 | using key_type = KeyType; |
477 | using key_type_ref = key_type; |
478 | using data_type = |
479 | llvm::SmallVector<std::pair<llvm::VersionTuple, UnversionedDataType>, 1>; |
480 | using data_type_ref = data_type &; |
481 | using hash_value_type = size_t; |
482 | using offset_type = unsigned; |
483 | |
484 | std::pair<unsigned, unsigned> |
485 | EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref Data) { |
486 | uint32_t KeyLength = asDerived().getKeyLength(Key); |
487 | uint32_t DataLength = |
488 | getVersionedInfoSize(Data, [this](const UnversionedDataType &UI) { |
489 | return asDerived().getUnversionedInfoSize(UI); |
490 | }); |
491 | |
492 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
493 | writer.write<uint16_t>(Val: KeyLength); |
494 | writer.write<uint16_t>(Val: DataLength); |
495 | return {KeyLength, DataLength}; |
496 | } |
497 | |
498 | void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) { |
499 | emitVersionedInfo( |
500 | OS, Data, [this](llvm::raw_ostream &OS, const UnversionedDataType &UI) { |
501 | asDerived().emitUnversionedInfo(OS, UI); |
502 | }); |
503 | } |
504 | }; |
505 | |
506 | /// Emit a serialized representation of the common entity information. |
507 | void emitCommonEntityInfo(raw_ostream &OS, const CommonEntityInfo &CEI) { |
508 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
509 | |
510 | uint8_t payload = 0; |
511 | if (auto swiftPrivate = CEI.isSwiftPrivate()) { |
512 | payload |= 0x01; |
513 | if (*swiftPrivate) |
514 | payload |= 0x02; |
515 | } |
516 | payload <<= 1; |
517 | payload |= CEI.Unavailable; |
518 | payload <<= 1; |
519 | payload |= CEI.UnavailableInSwift; |
520 | |
521 | writer.write<uint8_t>(Val: payload); |
522 | |
523 | writer.write<uint16_t>(Val: CEI.UnavailableMsg.size()); |
524 | OS.write(Ptr: CEI.UnavailableMsg.c_str(), Size: CEI.UnavailableMsg.size()); |
525 | |
526 | writer.write<uint16_t>(Val: CEI.SwiftName.size()); |
527 | OS.write(Ptr: CEI.SwiftName.c_str(), Size: CEI.SwiftName.size()); |
528 | } |
529 | |
530 | /// Retrieve the serialized size of the given CommonEntityInfo, for use in |
531 | /// on-disk hash tables. |
532 | unsigned getCommonEntityInfoSize(const CommonEntityInfo &CEI) { |
533 | return 5 + CEI.UnavailableMsg.size() + CEI.SwiftName.size(); |
534 | } |
535 | |
536 | // Retrieve the serialized size of the given CommonTypeInfo, for use |
537 | // in on-disk hash tables. |
538 | unsigned getCommonTypeInfoSize(const CommonTypeInfo &CTI) { |
539 | return 2 + (CTI.getSwiftBridge() ? CTI.getSwiftBridge()->size() : 0) + 2 + |
540 | (CTI.getNSErrorDomain() ? CTI.getNSErrorDomain()->size() : 0) + |
541 | getCommonEntityInfoSize(CEI: CTI); |
542 | } |
543 | |
544 | /// Emit a serialized representation of the common type information. |
545 | void emitCommonTypeInfo(raw_ostream &OS, const CommonTypeInfo &CTI) { |
546 | emitCommonEntityInfo(OS, CEI: CTI); |
547 | |
548 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
549 | if (auto swiftBridge = CTI.getSwiftBridge()) { |
550 | writer.write<uint16_t>(Val: swiftBridge->size() + 1); |
551 | OS.write(Ptr: swiftBridge->c_str(), Size: swiftBridge->size()); |
552 | } else { |
553 | writer.write<uint16_t>(Val: 0); |
554 | } |
555 | if (auto nsErrorDomain = CTI.getNSErrorDomain()) { |
556 | writer.write<uint16_t>(Val: nsErrorDomain->size() + 1); |
557 | OS.write(Ptr: nsErrorDomain->c_str(), Size: CTI.getNSErrorDomain()->size()); |
558 | } else { |
559 | writer.write<uint16_t>(Val: 0); |
560 | } |
561 | } |
562 | |
563 | /// Used to serialize the on-disk Objective-C property table. |
564 | class ContextInfoTableInfo |
565 | : public VersionedTableInfo<ContextInfoTableInfo, unsigned, ContextInfo> { |
566 | public: |
567 | unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); } |
568 | |
569 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
570 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
571 | writer.write<uint32_t>(Val: Key); |
572 | } |
573 | |
574 | hash_value_type ComputeHash(key_type_ref Key) { |
575 | return static_cast<size_t>(llvm::hash_value(value: Key)); |
576 | } |
577 | |
578 | unsigned getUnversionedInfoSize(const ContextInfo &OCI) { |
579 | return getCommonTypeInfoSize(CTI: OCI) + 1; |
580 | } |
581 | |
582 | void emitUnversionedInfo(raw_ostream &OS, const ContextInfo &OCI) { |
583 | emitCommonTypeInfo(OS, CTI: OCI); |
584 | |
585 | uint8_t payload = 0; |
586 | if (auto swiftImportAsNonGeneric = OCI.getSwiftImportAsNonGeneric()) |
587 | payload |= (0x01 << 1) | (uint8_t)swiftImportAsNonGeneric.value(); |
588 | payload <<= 2; |
589 | if (auto swiftObjCMembers = OCI.getSwiftObjCMembers()) |
590 | payload |= (0x01 << 1) | (uint8_t)swiftObjCMembers.value(); |
591 | payload <<= 3; |
592 | if (auto nullable = OCI.getDefaultNullability()) |
593 | payload |= (0x01 << 2) | static_cast<uint8_t>(*nullable); |
594 | payload = (payload << 1) | (OCI.hasDesignatedInits() ? 1 : 0); |
595 | |
596 | OS << payload; |
597 | } |
598 | }; |
599 | } // namespace |
600 | |
601 | void APINotesWriter::Implementation::writeContextBlock( |
602 | llvm::BitstreamWriter &Stream) { |
603 | llvm::BCBlockRAII restoreBlock(Stream, OBJC_CONTEXT_BLOCK_ID, 3); |
604 | |
605 | if (Contexts.empty()) |
606 | return; |
607 | |
608 | { |
609 | llvm::SmallString<4096> HashTableBlob; |
610 | uint32_t Offset; |
611 | { |
612 | llvm::OnDiskChainedHashTableGenerator<ContextIDTableInfo> Generator; |
613 | for (auto &OC : Contexts) |
614 | Generator.insert(Key: OC.first, Data: OC.second.first); |
615 | |
616 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
617 | // Make sure that no bucket is at offset 0 |
618 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
619 | endian: llvm::endianness::little); |
620 | Offset = Generator.Emit(Out&: BlobStream); |
621 | } |
622 | |
623 | context_block::ContextIDLayout ContextID(Stream); |
624 | ContextID.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
625 | } |
626 | |
627 | { |
628 | llvm::SmallString<4096> HashTableBlob; |
629 | uint32_t Offset; |
630 | { |
631 | llvm::OnDiskChainedHashTableGenerator<ContextInfoTableInfo> Generator; |
632 | for (auto &OC : Contexts) |
633 | Generator.insert(Key: OC.second.first, Data&: OC.second.second); |
634 | |
635 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
636 | // Make sure that no bucket is at offset 0 |
637 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
638 | endian: llvm::endianness::little); |
639 | Offset = Generator.Emit(Out&: BlobStream); |
640 | } |
641 | |
642 | context_block::ContextInfoLayout ContextInfo(Stream); |
643 | ContextInfo.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
644 | } |
645 | } |
646 | |
647 | namespace { |
648 | /// Retrieve the serialized size of the given VariableInfo, for use in |
649 | /// on-disk hash tables. |
650 | unsigned getVariableInfoSize(const VariableInfo &VI) { |
651 | return 2 + getCommonEntityInfoSize(CEI: VI) + 2 + VI.getType().size(); |
652 | } |
653 | |
654 | /// Emit a serialized representation of the variable information. |
655 | void emitVariableInfo(raw_ostream &OS, const VariableInfo &VI) { |
656 | emitCommonEntityInfo(OS, CEI: VI); |
657 | |
658 | uint8_t bytes[2] = {0, 0}; |
659 | if (auto nullable = VI.getNullability()) { |
660 | bytes[0] = 1; |
661 | bytes[1] = static_cast<uint8_t>(*nullable); |
662 | } else { |
663 | // Nothing to do. |
664 | } |
665 | |
666 | OS.write(Ptr: reinterpret_cast<const char *>(bytes), Size: 2); |
667 | |
668 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
669 | writer.write<uint16_t>(Val: VI.getType().size()); |
670 | OS.write(Ptr: VI.getType().data(), Size: VI.getType().size()); |
671 | } |
672 | |
673 | /// Used to serialize the on-disk Objective-C property table. |
674 | class ObjCPropertyTableInfo |
675 | : public VersionedTableInfo<ObjCPropertyTableInfo, |
676 | std::tuple<unsigned, unsigned, char>, |
677 | ObjCPropertyInfo> { |
678 | public: |
679 | unsigned getKeyLength(key_type_ref) { |
680 | return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); |
681 | } |
682 | |
683 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
684 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
685 | writer.write<uint32_t>(Val: std::get<0>(t&: Key)); |
686 | writer.write<uint32_t>(Val: std::get<1>(t&: Key)); |
687 | writer.write<uint8_t>(Val: std::get<2>(t&: Key)); |
688 | } |
689 | |
690 | hash_value_type ComputeHash(key_type_ref Key) { |
691 | return static_cast<size_t>(llvm::hash_value(arg: Key)); |
692 | } |
693 | |
694 | unsigned getUnversionedInfoSize(const ObjCPropertyInfo &OPI) { |
695 | return getVariableInfoSize(VI: OPI) + 1; |
696 | } |
697 | |
698 | void emitUnversionedInfo(raw_ostream &OS, const ObjCPropertyInfo &OPI) { |
699 | emitVariableInfo(OS, VI: OPI); |
700 | |
701 | uint8_t flags = 0; |
702 | if (auto value = OPI.getSwiftImportAsAccessors()) { |
703 | flags |= 1 << 0; |
704 | flags |= value.value() << 1; |
705 | } |
706 | OS << flags; |
707 | } |
708 | }; |
709 | } // namespace |
710 | |
711 | void APINotesWriter::Implementation::writeObjCPropertyBlock( |
712 | llvm::BitstreamWriter &Stream) { |
713 | llvm::BCBlockRAII Scope(Stream, OBJC_PROPERTY_BLOCK_ID, 3); |
714 | |
715 | if (ObjCProperties.empty()) |
716 | return; |
717 | |
718 | { |
719 | llvm::SmallString<4096> HashTableBlob; |
720 | uint32_t Offset; |
721 | { |
722 | llvm::OnDiskChainedHashTableGenerator<ObjCPropertyTableInfo> Generator; |
723 | for (auto &OP : ObjCProperties) |
724 | Generator.insert(Key: OP.first, Data&: OP.second); |
725 | |
726 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
727 | // Make sure that no bucket is at offset 0 |
728 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
729 | endian: llvm::endianness::little); |
730 | Offset = Generator.Emit(Out&: BlobStream); |
731 | } |
732 | |
733 | objc_property_block::ObjCPropertyDataLayout ObjCPropertyData(Stream); |
734 | ObjCPropertyData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
735 | } |
736 | } |
737 | |
738 | namespace { |
739 | unsigned getFunctionInfoSize(const FunctionInfo &); |
740 | void emitFunctionInfo(llvm::raw_ostream &, const FunctionInfo &); |
741 | |
742 | /// Used to serialize the on-disk Objective-C method table. |
743 | class ObjCMethodTableInfo |
744 | : public VersionedTableInfo<ObjCMethodTableInfo, |
745 | std::tuple<unsigned, unsigned, char>, |
746 | ObjCMethodInfo> { |
747 | public: |
748 | unsigned getKeyLength(key_type_ref) { |
749 | return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t); |
750 | } |
751 | |
752 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
753 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
754 | writer.write<uint32_t>(Val: std::get<0>(t&: Key)); |
755 | writer.write<uint32_t>(Val: std::get<1>(t&: Key)); |
756 | writer.write<uint8_t>(Val: std::get<2>(t&: Key)); |
757 | } |
758 | |
759 | hash_value_type ComputeHash(key_type_ref key) { |
760 | return static_cast<size_t>(llvm::hash_value(arg: key)); |
761 | } |
762 | |
763 | unsigned getUnversionedInfoSize(const ObjCMethodInfo &OMI) { |
764 | return getFunctionInfoSize(OMI) + 1; |
765 | } |
766 | |
767 | void emitUnversionedInfo(raw_ostream &OS, const ObjCMethodInfo &OMI) { |
768 | uint8_t flags = 0; |
769 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
770 | flags = (flags << 1) | OMI.DesignatedInit; |
771 | flags = (flags << 1) | OMI.RequiredInit; |
772 | writer.write<uint8_t>(Val: flags); |
773 | |
774 | emitFunctionInfo(OS, OMI); |
775 | } |
776 | }; |
777 | |
778 | /// Used to serialize the on-disk C++ method table. |
779 | class CXXMethodTableInfo |
780 | : public VersionedTableInfo<CXXMethodTableInfo, SingleDeclTableKey, |
781 | CXXMethodInfo> { |
782 | public: |
783 | unsigned getKeyLength(key_type_ref) { |
784 | return sizeof(uint32_t) + sizeof(uint32_t); |
785 | } |
786 | |
787 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
788 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
789 | writer.write<uint32_t>(Val: Key.parentContextID); |
790 | writer.write<uint32_t>(Val: Key.nameID); |
791 | } |
792 | |
793 | hash_value_type ComputeHash(key_type_ref key) { |
794 | return static_cast<size_t>(key.hashValue()); |
795 | } |
796 | |
797 | unsigned getUnversionedInfoSize(const CXXMethodInfo &OMI) { |
798 | return getFunctionInfoSize(OMI); |
799 | } |
800 | |
801 | void emitUnversionedInfo(raw_ostream &OS, const CXXMethodInfo &OMI) { |
802 | emitFunctionInfo(OS, OMI); |
803 | } |
804 | }; |
805 | } // namespace |
806 | |
807 | void APINotesWriter::Implementation::writeObjCMethodBlock( |
808 | llvm::BitstreamWriter &Stream) { |
809 | llvm::BCBlockRAII Scope(Stream, OBJC_METHOD_BLOCK_ID, 3); |
810 | |
811 | if (ObjCMethods.empty()) |
812 | return; |
813 | |
814 | { |
815 | llvm::SmallString<4096> HashTableBlob; |
816 | uint32_t Offset; |
817 | { |
818 | llvm::OnDiskChainedHashTableGenerator<ObjCMethodTableInfo> Generator; |
819 | for (auto &OM : ObjCMethods) |
820 | Generator.insert(Key: OM.first, Data&: OM.second); |
821 | |
822 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
823 | // Make sure that no bucket is at offset 0 |
824 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
825 | endian: llvm::endianness::little); |
826 | Offset = Generator.Emit(Out&: BlobStream); |
827 | } |
828 | |
829 | objc_method_block::ObjCMethodDataLayout ObjCMethodData(Stream); |
830 | ObjCMethodData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
831 | } |
832 | } |
833 | |
834 | void APINotesWriter::Implementation::writeCXXMethodBlock( |
835 | llvm::BitstreamWriter &Stream) { |
836 | llvm::BCBlockRAII Scope(Stream, CXX_METHOD_BLOCK_ID, 3); |
837 | |
838 | if (CXXMethods.empty()) |
839 | return; |
840 | |
841 | { |
842 | llvm::SmallString<4096> HashTableBlob; |
843 | uint32_t Offset; |
844 | { |
845 | llvm::OnDiskChainedHashTableGenerator<CXXMethodTableInfo> Generator; |
846 | for (auto &MD : CXXMethods) |
847 | Generator.insert(Key: MD.first, Data&: MD.second); |
848 | |
849 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
850 | // Make sure that no bucket is at offset 0 |
851 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
852 | endian: llvm::endianness::little); |
853 | Offset = Generator.Emit(Out&: BlobStream); |
854 | } |
855 | |
856 | cxx_method_block::CXXMethodDataLayout CXXMethodData(Stream); |
857 | CXXMethodData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
858 | } |
859 | } |
860 | |
861 | namespace { |
862 | /// Used to serialize the on-disk Objective-C selector table. |
863 | class ObjCSelectorTableInfo { |
864 | public: |
865 | using key_type = StoredObjCSelector; |
866 | using key_type_ref = const key_type &; |
867 | using data_type = SelectorID; |
868 | using data_type_ref = data_type; |
869 | using hash_value_type = unsigned; |
870 | using offset_type = unsigned; |
871 | |
872 | hash_value_type ComputeHash(key_type_ref Key) { |
873 | return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(Selector: Key); |
874 | } |
875 | |
876 | std::pair<unsigned, unsigned> |
877 | EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref) { |
878 | uint32_t KeyLength = |
879 | sizeof(uint16_t) + sizeof(uint32_t) * Key.Identifiers.size(); |
880 | uint32_t DataLength = sizeof(uint32_t); |
881 | |
882 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
883 | writer.write<uint16_t>(Val: KeyLength); |
884 | writer.write<uint16_t>(Val: DataLength); |
885 | return {KeyLength, DataLength}; |
886 | } |
887 | |
888 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
889 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
890 | writer.write<uint16_t>(Val: Key.NumArgs); |
891 | for (auto Identifier : Key.Identifiers) |
892 | writer.write<uint32_t>(Val: Identifier); |
893 | } |
894 | |
895 | void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) { |
896 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
897 | writer.write<uint32_t>(Val: Data); |
898 | } |
899 | }; |
900 | } // namespace |
901 | |
902 | void APINotesWriter::Implementation::writeObjCSelectorBlock( |
903 | llvm::BitstreamWriter &Stream) { |
904 | llvm::BCBlockRAII Scope(Stream, OBJC_SELECTOR_BLOCK_ID, 3); |
905 | |
906 | if (SelectorIDs.empty()) |
907 | return; |
908 | |
909 | { |
910 | llvm::SmallString<4096> HashTableBlob; |
911 | uint32_t Offset; |
912 | { |
913 | llvm::OnDiskChainedHashTableGenerator<ObjCSelectorTableInfo> Generator; |
914 | for (auto &S : SelectorIDs) |
915 | Generator.insert(Key: S.first, Data: S.second); |
916 | |
917 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
918 | // Make sure that no bucket is at offset 0 |
919 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
920 | endian: llvm::endianness::little); |
921 | Offset = Generator.Emit(Out&: BlobStream); |
922 | } |
923 | |
924 | objc_selector_block::ObjCSelectorDataLayout ObjCSelectorData(Stream); |
925 | ObjCSelectorData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
926 | } |
927 | } |
928 | |
929 | namespace { |
930 | /// Used to serialize the on-disk global variable table. |
931 | class GlobalVariableTableInfo |
932 | : public VersionedTableInfo<GlobalVariableTableInfo, SingleDeclTableKey, |
933 | GlobalVariableInfo> { |
934 | public: |
935 | unsigned getKeyLength(key_type_ref) { |
936 | return sizeof(uint32_t) + sizeof(uint32_t); |
937 | } |
938 | |
939 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
940 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
941 | writer.write<uint32_t>(Val: Key.parentContextID); |
942 | writer.write<uint32_t>(Val: Key.nameID); |
943 | } |
944 | |
945 | hash_value_type ComputeHash(key_type_ref Key) { |
946 | return static_cast<size_t>(Key.hashValue()); |
947 | } |
948 | |
949 | unsigned getUnversionedInfoSize(const GlobalVariableInfo &GVI) { |
950 | return getVariableInfoSize(VI: GVI); |
951 | } |
952 | |
953 | void emitUnversionedInfo(raw_ostream &OS, const GlobalVariableInfo &GVI) { |
954 | emitVariableInfo(OS, VI: GVI); |
955 | } |
956 | }; |
957 | } // namespace |
958 | |
959 | void APINotesWriter::Implementation::writeGlobalVariableBlock( |
960 | llvm::BitstreamWriter &Stream) { |
961 | llvm::BCBlockRAII Scope(Stream, GLOBAL_VARIABLE_BLOCK_ID, 3); |
962 | |
963 | if (GlobalVariables.empty()) |
964 | return; |
965 | |
966 | { |
967 | llvm::SmallString<4096> HashTableBlob; |
968 | uint32_t Offset; |
969 | { |
970 | llvm::OnDiskChainedHashTableGenerator<GlobalVariableTableInfo> Generator; |
971 | for (auto &GV : GlobalVariables) |
972 | Generator.insert(Key: GV.first, Data&: GV.second); |
973 | |
974 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
975 | // Make sure that no bucket is at offset 0 |
976 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
977 | endian: llvm::endianness::little); |
978 | Offset = Generator.Emit(Out&: BlobStream); |
979 | } |
980 | |
981 | global_variable_block::GlobalVariableDataLayout GlobalVariableData(Stream); |
982 | GlobalVariableData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
983 | } |
984 | } |
985 | |
986 | namespace { |
987 | unsigned getParamInfoSize(const ParamInfo &PI) { |
988 | return getVariableInfoSize(VI: PI) + 1; |
989 | } |
990 | |
991 | void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) { |
992 | emitVariableInfo(OS, VI: PI); |
993 | |
994 | uint8_t flags = 0; |
995 | if (auto noescape = PI.isNoEscape()) { |
996 | flags |= 0x01; |
997 | if (*noescape) |
998 | flags |= 0x02; |
999 | } |
1000 | flags <<= 3; |
1001 | if (auto RCC = PI.getRetainCountConvention()) |
1002 | flags |= static_cast<uint8_t>(RCC.value()) + 1; |
1003 | |
1004 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
1005 | writer.write<uint8_t>(Val: flags); |
1006 | } |
1007 | |
1008 | /// Retrieve the serialized size of the given FunctionInfo, for use in on-disk |
1009 | /// hash tables. |
1010 | unsigned getFunctionInfoSize(const FunctionInfo &FI) { |
1011 | unsigned size = getCommonEntityInfoSize(CEI: FI) + 2 + sizeof(uint64_t); |
1012 | size += sizeof(uint16_t); |
1013 | for (const auto &P : FI.Params) |
1014 | size += getParamInfoSize(PI: P); |
1015 | size += sizeof(uint16_t) + FI.ResultType.size(); |
1016 | return size; |
1017 | } |
1018 | |
1019 | /// Emit a serialized representation of the function information. |
1020 | void emitFunctionInfo(raw_ostream &OS, const FunctionInfo &FI) { |
1021 | emitCommonEntityInfo(OS, CEI: FI); |
1022 | |
1023 | uint8_t flags = 0; |
1024 | flags |= FI.NullabilityAudited; |
1025 | flags <<= 3; |
1026 | if (auto RCC = FI.getRetainCountConvention()) |
1027 | flags |= static_cast<uint8_t>(RCC.value()) + 1; |
1028 | |
1029 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
1030 | |
1031 | writer.write<uint8_t>(Val: flags); |
1032 | writer.write<uint8_t>(Val: FI.NumAdjustedNullable); |
1033 | writer.write<uint64_t>(Val: FI.NullabilityPayload); |
1034 | |
1035 | writer.write<uint16_t>(Val: FI.Params.size()); |
1036 | for (const auto &PI : FI.Params) |
1037 | emitParamInfo(OS, PI); |
1038 | |
1039 | writer.write<uint16_t>(Val: FI.ResultType.size()); |
1040 | writer.write(Val: ArrayRef<char>{FI.ResultType.data(), FI.ResultType.size()}); |
1041 | } |
1042 | |
1043 | /// Used to serialize the on-disk global function table. |
1044 | class GlobalFunctionTableInfo |
1045 | : public VersionedTableInfo<GlobalFunctionTableInfo, SingleDeclTableKey, |
1046 | GlobalFunctionInfo> { |
1047 | public: |
1048 | unsigned getKeyLength(key_type_ref) { |
1049 | return sizeof(uint32_t) + sizeof(uint32_t); |
1050 | } |
1051 | |
1052 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
1053 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
1054 | writer.write<uint32_t>(Val: Key.parentContextID); |
1055 | writer.write<uint32_t>(Val: Key.nameID); |
1056 | } |
1057 | |
1058 | hash_value_type ComputeHash(key_type_ref Key) { |
1059 | return static_cast<size_t>(Key.hashValue()); |
1060 | } |
1061 | |
1062 | unsigned getUnversionedInfoSize(const GlobalFunctionInfo &GFI) { |
1063 | return getFunctionInfoSize(FI: GFI); |
1064 | } |
1065 | |
1066 | void emitUnversionedInfo(raw_ostream &OS, const GlobalFunctionInfo &GFI) { |
1067 | emitFunctionInfo(OS, FI: GFI); |
1068 | } |
1069 | }; |
1070 | } // namespace |
1071 | |
1072 | void APINotesWriter::Implementation::writeGlobalFunctionBlock( |
1073 | llvm::BitstreamWriter &Stream) { |
1074 | llvm::BCBlockRAII Scope(Stream, GLOBAL_FUNCTION_BLOCK_ID, 3); |
1075 | |
1076 | if (GlobalFunctions.empty()) |
1077 | return; |
1078 | |
1079 | { |
1080 | llvm::SmallString<4096> HashTableBlob; |
1081 | uint32_t Offset; |
1082 | { |
1083 | llvm::OnDiskChainedHashTableGenerator<GlobalFunctionTableInfo> Generator; |
1084 | for (auto &F : GlobalFunctions) |
1085 | Generator.insert(Key: F.first, Data&: F.second); |
1086 | |
1087 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
1088 | // Make sure that no bucket is at offset 0 |
1089 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
1090 | endian: llvm::endianness::little); |
1091 | Offset = Generator.Emit(Out&: BlobStream); |
1092 | } |
1093 | |
1094 | global_function_block::GlobalFunctionDataLayout GlobalFunctionData(Stream); |
1095 | GlobalFunctionData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
1096 | } |
1097 | } |
1098 | |
1099 | namespace { |
1100 | /// Used to serialize the on-disk global enum constant. |
1101 | class EnumConstantTableInfo |
1102 | : public VersionedTableInfo<EnumConstantTableInfo, unsigned, |
1103 | EnumConstantInfo> { |
1104 | public: |
1105 | unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); } |
1106 | |
1107 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
1108 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
1109 | writer.write<uint32_t>(Val: Key); |
1110 | } |
1111 | |
1112 | hash_value_type ComputeHash(key_type_ref Key) { |
1113 | return static_cast<size_t>(llvm::hash_value(value: Key)); |
1114 | } |
1115 | |
1116 | unsigned getUnversionedInfoSize(const EnumConstantInfo &ECI) { |
1117 | return getCommonEntityInfoSize(CEI: ECI); |
1118 | } |
1119 | |
1120 | void emitUnversionedInfo(raw_ostream &OS, const EnumConstantInfo &ECI) { |
1121 | emitCommonEntityInfo(OS, CEI: ECI); |
1122 | } |
1123 | }; |
1124 | } // namespace |
1125 | |
1126 | void APINotesWriter::Implementation::writeEnumConstantBlock( |
1127 | llvm::BitstreamWriter &Stream) { |
1128 | llvm::BCBlockRAII Scope(Stream, ENUM_CONSTANT_BLOCK_ID, 3); |
1129 | |
1130 | if (EnumConstants.empty()) |
1131 | return; |
1132 | |
1133 | { |
1134 | llvm::SmallString<4096> HashTableBlob; |
1135 | uint32_t Offset; |
1136 | { |
1137 | llvm::OnDiskChainedHashTableGenerator<EnumConstantTableInfo> Generator; |
1138 | for (auto &EC : EnumConstants) |
1139 | Generator.insert(Key: EC.first, Data&: EC.second); |
1140 | |
1141 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
1142 | // Make sure that no bucket is at offset 0 |
1143 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
1144 | endian: llvm::endianness::little); |
1145 | Offset = Generator.Emit(Out&: BlobStream); |
1146 | } |
1147 | |
1148 | enum_constant_block::EnumConstantDataLayout EnumConstantData(Stream); |
1149 | EnumConstantData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
1150 | } |
1151 | } |
1152 | |
1153 | namespace { |
1154 | template <typename Derived, typename UnversionedDataType> |
1155 | class CommonTypeTableInfo |
1156 | : public VersionedTableInfo<Derived, SingleDeclTableKey, |
1157 | UnversionedDataType> { |
1158 | public: |
1159 | using key_type_ref = typename CommonTypeTableInfo::key_type_ref; |
1160 | using hash_value_type = typename CommonTypeTableInfo::hash_value_type; |
1161 | |
1162 | unsigned getKeyLength(key_type_ref) { |
1163 | return sizeof(uint32_t) + sizeof(IdentifierID); |
1164 | } |
1165 | |
1166 | void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { |
1167 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
1168 | writer.write<uint32_t>(Key.parentContextID); |
1169 | writer.write<IdentifierID>(Key.nameID); |
1170 | } |
1171 | |
1172 | hash_value_type ComputeHash(key_type_ref Key) { |
1173 | return static_cast<size_t>(Key.hashValue()); |
1174 | } |
1175 | |
1176 | unsigned getUnversionedInfoSize(const UnversionedDataType &UDT) { |
1177 | return getCommonTypeInfoSize(UDT); |
1178 | } |
1179 | |
1180 | void emitUnversionedInfo(raw_ostream &OS, const UnversionedDataType &UDT) { |
1181 | emitCommonTypeInfo(OS, UDT); |
1182 | } |
1183 | }; |
1184 | |
1185 | /// Used to serialize the on-disk tag table. |
1186 | class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> { |
1187 | public: |
1188 | unsigned getUnversionedInfoSize(const TagInfo &TI) { |
1189 | return 2 + (TI.SwiftImportAs ? TI.SwiftImportAs->size() : 0) + |
1190 | 2 + (TI.SwiftRetainOp ? TI.SwiftRetainOp->size() : 0) + |
1191 | 2 + (TI.SwiftReleaseOp ? TI.SwiftReleaseOp->size() : 0) + |
1192 | 2 + getCommonTypeInfoSize(CTI: TI); |
1193 | } |
1194 | |
1195 | void emitUnversionedInfo(raw_ostream &OS, const TagInfo &TI) { |
1196 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
1197 | |
1198 | uint8_t Flags = 0; |
1199 | if (auto extensibility = TI.EnumExtensibility) { |
1200 | Flags |= static_cast<uint8_t>(extensibility.value()) + 1; |
1201 | assert((Flags < (1 << 2)) && "must fit in two bits" ); |
1202 | } |
1203 | |
1204 | Flags <<= 2; |
1205 | if (auto value = TI.isFlagEnum()) |
1206 | Flags |= (value.value() << 1 | 1 << 0); |
1207 | |
1208 | writer.write<uint8_t>(Val: Flags); |
1209 | |
1210 | if (auto Copyable = TI.isSwiftCopyable()) |
1211 | writer.write<uint8_t>(Val: *Copyable ? kSwiftCopyable : kSwiftNonCopyable); |
1212 | else |
1213 | writer.write<uint8_t>(Val: 0); |
1214 | |
1215 | if (auto ImportAs = TI.SwiftImportAs) { |
1216 | writer.write<uint16_t>(Val: ImportAs->size() + 1); |
1217 | OS.write(Ptr: ImportAs->c_str(), Size: ImportAs->size()); |
1218 | } else { |
1219 | writer.write<uint16_t>(Val: 0); |
1220 | } |
1221 | if (auto RetainOp = TI.SwiftRetainOp) { |
1222 | writer.write<uint16_t>(Val: RetainOp->size() + 1); |
1223 | OS.write(Ptr: RetainOp->c_str(), Size: RetainOp->size()); |
1224 | } else { |
1225 | writer.write<uint16_t>(Val: 0); |
1226 | } |
1227 | if (auto ReleaseOp = TI.SwiftReleaseOp) { |
1228 | writer.write<uint16_t>(Val: ReleaseOp->size() + 1); |
1229 | OS.write(Ptr: ReleaseOp->c_str(), Size: ReleaseOp->size()); |
1230 | } else { |
1231 | writer.write<uint16_t>(Val: 0); |
1232 | } |
1233 | |
1234 | emitCommonTypeInfo(OS, CTI: TI); |
1235 | } |
1236 | }; |
1237 | } // namespace |
1238 | |
1239 | void APINotesWriter::Implementation::writeTagBlock( |
1240 | llvm::BitstreamWriter &Stream) { |
1241 | llvm::BCBlockRAII Scope(Stream, TAG_BLOCK_ID, 3); |
1242 | |
1243 | if (Tags.empty()) |
1244 | return; |
1245 | |
1246 | { |
1247 | llvm::SmallString<4096> HashTableBlob; |
1248 | uint32_t Offset; |
1249 | { |
1250 | llvm::OnDiskChainedHashTableGenerator<TagTableInfo> Generator; |
1251 | for (auto &T : Tags) |
1252 | Generator.insert(Key: T.first, Data&: T.second); |
1253 | |
1254 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
1255 | // Make sure that no bucket is at offset 0 |
1256 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
1257 | endian: llvm::endianness::little); |
1258 | Offset = Generator.Emit(Out&: BlobStream); |
1259 | } |
1260 | |
1261 | tag_block::TagDataLayout TagData(Stream); |
1262 | TagData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
1263 | } |
1264 | } |
1265 | |
1266 | namespace { |
1267 | /// Used to serialize the on-disk typedef table. |
1268 | class TypedefTableInfo |
1269 | : public CommonTypeTableInfo<TypedefTableInfo, TypedefInfo> { |
1270 | public: |
1271 | unsigned getUnversionedInfoSize(const TypedefInfo &TI) { |
1272 | return 1 + getCommonTypeInfoSize(CTI: TI); |
1273 | } |
1274 | |
1275 | void emitUnversionedInfo(raw_ostream &OS, const TypedefInfo &TI) { |
1276 | llvm::support::endian::Writer writer(OS, llvm::endianness::little); |
1277 | |
1278 | uint8_t Flags = 0; |
1279 | if (auto swiftWrapper = TI.SwiftWrapper) |
1280 | Flags |= static_cast<uint8_t>(*swiftWrapper) + 1; |
1281 | |
1282 | writer.write<uint8_t>(Val: Flags); |
1283 | |
1284 | emitCommonTypeInfo(OS, CTI: TI); |
1285 | } |
1286 | }; |
1287 | } // namespace |
1288 | |
1289 | void APINotesWriter::Implementation::writeTypedefBlock( |
1290 | llvm::BitstreamWriter &Stream) { |
1291 | llvm::BCBlockRAII Scope(Stream, TYPEDEF_BLOCK_ID, 3); |
1292 | |
1293 | if (Typedefs.empty()) |
1294 | return; |
1295 | |
1296 | { |
1297 | llvm::SmallString<4096> HashTableBlob; |
1298 | uint32_t Offset; |
1299 | { |
1300 | llvm::OnDiskChainedHashTableGenerator<TypedefTableInfo> Generator; |
1301 | for (auto &T : Typedefs) |
1302 | Generator.insert(Key: T.first, Data&: T.second); |
1303 | |
1304 | llvm::raw_svector_ostream BlobStream(HashTableBlob); |
1305 | // Make sure that no bucket is at offset 0 |
1306 | llvm::support::endian::write<uint32_t>(os&: BlobStream, value: 0, |
1307 | endian: llvm::endianness::little); |
1308 | Offset = Generator.Emit(Out&: BlobStream); |
1309 | } |
1310 | |
1311 | typedef_block::TypedefDataLayout TypedefData(Stream); |
1312 | TypedefData.emit(buffer&: Scratch, data&: Offset, data&: HashTableBlob); |
1313 | } |
1314 | } |
1315 | |
1316 | // APINotesWriter |
1317 | |
1318 | APINotesWriter::APINotesWriter(llvm::StringRef ModuleName, const FileEntry *SF) |
1319 | : Implementation(new class Implementation(ModuleName, SF)) {} |
1320 | |
1321 | APINotesWriter::~APINotesWriter() = default; |
1322 | |
1323 | void APINotesWriter::writeToStream(llvm::raw_ostream &OS) { |
1324 | Implementation->writeToStream(OS); |
1325 | } |
1326 | |
1327 | ContextID APINotesWriter::addContext(std::optional<ContextID> ParentCtxID, |
1328 | llvm::StringRef Name, ContextKind Kind, |
1329 | const ContextInfo &Info, |
1330 | llvm::VersionTuple SwiftVersion) { |
1331 | IdentifierID NameID = Implementation->getIdentifier(Identifier: Name); |
1332 | |
1333 | uint32_t RawParentCtxID = ParentCtxID ? ParentCtxID->Value : -1; |
1334 | ContextTableKey Key(RawParentCtxID, static_cast<uint8_t>(Kind), NameID); |
1335 | auto Known = Implementation->Contexts.find(Val: Key); |
1336 | if (Known == Implementation->Contexts.end()) { |
1337 | unsigned NextID = Implementation->Contexts.size() + 1; |
1338 | |
1339 | Implementation::VersionedSmallVector<ContextInfo> EmptyVersionedInfo; |
1340 | Known = Implementation->Contexts |
1341 | .insert(KV: std::make_pair( |
1342 | x&: Key, y: std::make_pair(x&: NextID, y&: EmptyVersionedInfo))) |
1343 | .first; |
1344 | |
1345 | Implementation->ContextNames[NextID] = NameID; |
1346 | Implementation->ParentContexts[NextID] = RawParentCtxID; |
1347 | } |
1348 | |
1349 | // Add this version information. |
1350 | auto &VersionedVec = Known->second.second; |
1351 | bool Found = false; |
1352 | for (auto &Versioned : VersionedVec) { |
1353 | if (Versioned.first == SwiftVersion) { |
1354 | Versioned.second |= Info; |
1355 | Found = true; |
1356 | break; |
1357 | } |
1358 | } |
1359 | |
1360 | if (!Found) |
1361 | VersionedVec.push_back(Elt: {SwiftVersion, Info}); |
1362 | |
1363 | return ContextID(Known->second.first); |
1364 | } |
1365 | |
1366 | void APINotesWriter::addObjCProperty(ContextID CtxID, StringRef Name, |
1367 | bool IsInstanceProperty, |
1368 | const ObjCPropertyInfo &Info, |
1369 | VersionTuple SwiftVersion) { |
1370 | IdentifierID NameID = Implementation->getIdentifier(Identifier: Name); |
1371 | Implementation |
1372 | ->ObjCProperties[std::make_tuple(args&: CtxID.Value, args&: NameID, args&: IsInstanceProperty)] |
1373 | .push_back(Elt: {SwiftVersion, Info}); |
1374 | } |
1375 | |
1376 | void APINotesWriter::addObjCMethod(ContextID CtxID, ObjCSelectorRef Selector, |
1377 | bool IsInstanceMethod, |
1378 | const ObjCMethodInfo &Info, |
1379 | VersionTuple SwiftVersion) { |
1380 | SelectorID SelID = Implementation->getSelector(SelectorRef: Selector); |
1381 | auto Key = std::tuple<unsigned, unsigned, char>{CtxID.Value, SelID, |
1382 | IsInstanceMethod}; |
1383 | Implementation->ObjCMethods[Key].push_back(Elt: {SwiftVersion, Info}); |
1384 | |
1385 | // If this method is a designated initializer, update the class to note that |
1386 | // it has designated initializers. |
1387 | if (Info.DesignatedInit) { |
1388 | assert(Implementation->ParentContexts.contains(CtxID.Value)); |
1389 | uint32_t ParentCtxID = Implementation->ParentContexts[CtxID.Value]; |
1390 | ContextTableKey CtxKey(ParentCtxID, |
1391 | static_cast<uint8_t>(ContextKind::ObjCClass), |
1392 | Implementation->ContextNames[CtxID.Value]); |
1393 | assert(Implementation->Contexts.contains(CtxKey)); |
1394 | auto &VersionedVec = Implementation->Contexts[CtxKey].second; |
1395 | bool Found = false; |
1396 | for (auto &Versioned : VersionedVec) { |
1397 | if (Versioned.first == SwiftVersion) { |
1398 | Versioned.second.setHasDesignatedInits(true); |
1399 | Found = true; |
1400 | break; |
1401 | } |
1402 | } |
1403 | |
1404 | if (!Found) { |
1405 | VersionedVec.push_back(Elt: {SwiftVersion, ContextInfo()}); |
1406 | VersionedVec.back().second.setHasDesignatedInits(true); |
1407 | } |
1408 | } |
1409 | } |
1410 | |
1411 | void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name, |
1412 | const CXXMethodInfo &Info, |
1413 | VersionTuple SwiftVersion) { |
1414 | IdentifierID NameID = Implementation->getIdentifier(Identifier: Name); |
1415 | SingleDeclTableKey Key(CtxID.Value, NameID); |
1416 | Implementation->CXXMethods[Key].push_back(Elt: {SwiftVersion, Info}); |
1417 | } |
1418 | |
1419 | void APINotesWriter::addGlobalVariable(std::optional<Context> Ctx, |
1420 | llvm::StringRef Name, |
1421 | const GlobalVariableInfo &Info, |
1422 | VersionTuple SwiftVersion) { |
1423 | IdentifierID VariableID = Implementation->getIdentifier(Identifier: Name); |
1424 | SingleDeclTableKey Key(Ctx, VariableID); |
1425 | Implementation->GlobalVariables[Key].push_back(Elt: {SwiftVersion, Info}); |
1426 | } |
1427 | |
1428 | void APINotesWriter::addGlobalFunction(std::optional<Context> Ctx, |
1429 | llvm::StringRef Name, |
1430 | const GlobalFunctionInfo &Info, |
1431 | VersionTuple SwiftVersion) { |
1432 | IdentifierID NameID = Implementation->getIdentifier(Identifier: Name); |
1433 | SingleDeclTableKey Key(Ctx, NameID); |
1434 | Implementation->GlobalFunctions[Key].push_back(Elt: {SwiftVersion, Info}); |
1435 | } |
1436 | |
1437 | void APINotesWriter::addEnumConstant(llvm::StringRef Name, |
1438 | const EnumConstantInfo &Info, |
1439 | VersionTuple SwiftVersion) { |
1440 | IdentifierID EnumConstantID = Implementation->getIdentifier(Identifier: Name); |
1441 | Implementation->EnumConstants[EnumConstantID].push_back(Elt: {SwiftVersion, Info}); |
1442 | } |
1443 | |
1444 | void APINotesWriter::addTag(std::optional<Context> Ctx, llvm::StringRef Name, |
1445 | const TagInfo &Info, VersionTuple SwiftVersion) { |
1446 | IdentifierID TagID = Implementation->getIdentifier(Identifier: Name); |
1447 | SingleDeclTableKey Key(Ctx, TagID); |
1448 | Implementation->Tags[Key].push_back(Elt: {SwiftVersion, Info}); |
1449 | } |
1450 | |
1451 | void APINotesWriter::addTypedef(std::optional<Context> Ctx, |
1452 | llvm::StringRef Name, const TypedefInfo &Info, |
1453 | VersionTuple SwiftVersion) { |
1454 | IdentifierID TypedefID = Implementation->getIdentifier(Identifier: Name); |
1455 | SingleDeclTableKey Key(Ctx, TypedefID); |
1456 | Implementation->Typedefs[Key].push_back(Elt: {SwiftVersion, Info}); |
1457 | } |
1458 | } // namespace api_notes |
1459 | } // namespace clang |
1460 | |