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.
getIdentifier(StringRef Identifier)121 IdentifierID getIdentifier(StringRef Identifier) {
122 if (Identifier.empty())
123 return 0;
124
125 auto Known = IdentifierIDs.find(Identifier);
126 if (Known != IdentifierIDs.end())
127 return Known->second;
128
129 // Add to the identifier table.
130 Known = IdentifierIDs.insert({Identifier, IdentifierIDs.size() + 1}).first;
131 return Known->second;
132 }
133
134 /// Retrieve the ID for the given selector.
getSelector(ObjCSelectorRef SelectorRef)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(SelectorRef.Identifiers.size());
140 for (auto piece : SelectorRef.Identifiers)
141 Selector.Identifiers.push_back(getIdentifier(piece));
142
143 // Look for the stored selector.
144 auto Known = SelectorIDs.find(Selector);
145 if (Known != SelectorIDs.end())
146 return Known->second;
147
148 // Add to the selector table.
149 Known = SelectorIDs.insert({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:
Implementation(llvm::StringRef ModuleName,const FileEntry * SF)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
writeToStream(llvm::raw_ostream & OS)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(Byte, 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(Buffer.data(), Buffer.size());
202 OS.flush();
203 }
204
205 namespace {
206 /// Record the name of a block.
emitBlockID(llvm::BitstreamWriter & Stream,unsigned ID,llvm::StringRef Name)207 void emitBlockID(llvm::BitstreamWriter &Stream, unsigned ID,
208 llvm::StringRef Name) {
209 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID,
210 llvm::ArrayRef<unsigned>{ID});
211
212 // Emit the block name if present.
213 if (Name.empty())
214 return;
215 Stream.EmitRecord(
216 llvm::bitc::BLOCKINFO_CODE_BLOCKNAME,
217 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.
emitRecordID(llvm::BitstreamWriter & Stream,unsigned ID,llvm::StringRef Name)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(Name.size() + 1);
230 Buffer[0] = ID;
231 memcpy(Buffer.data() + 1, Name.data(), Name.size());
232
233 Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Buffer);
234 }
235 } // namespace
236
writeBlockInfoBlock(llvm::BitstreamWriter & Stream)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
writeControlBlock(llvm::BitstreamWriter & Stream)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(Scratch, VERSION_MAJOR, VERSION_MINOR);
278
279 control_block::ModuleNameLayout ModuleName(Stream);
280 ModuleName.emit(Scratch, this->ModuleName);
281
282 if (SourceFile) {
283 control_block::SourceFileLayout SourceFile(Stream);
284 SourceFile.emit(Scratch, this->SourceFile->getSize(),
285 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
ComputeHash(key_type_ref Key)300 hash_value_type ComputeHash(key_type_ref Key) { return llvm::djbHash(Key); }
301
302 std::pair<unsigned, unsigned>
EmitKeyDataLength(raw_ostream & OS,key_type_ref Key,data_type_ref)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>(KeyLength);
309 writer.write<uint16_t>(DataLength);
310 return {KeyLength, DataLength};
311 }
312
EmitKey(raw_ostream & OS,key_type_ref Key,unsigned)313 void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { OS << Key; }
314
EmitData(raw_ostream & OS,key_type_ref,data_type_ref Data,unsigned)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>(Data);
318 }
319 };
320 } // namespace
321
writeIdentifierBlock(llvm::BitstreamWriter & Stream)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(II.first(), 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>(BlobStream, 0,
339 llvm::endianness::little);
340 Offset = Generator.Emit(BlobStream);
341 }
342
343 identifier_block::IdentifierDataLayout IdentifierData(Stream);
344 IdentifierData.emit(Scratch, Offset, 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
ComputeHash(key_type_ref Key)358 hash_value_type ComputeHash(key_type_ref Key) {
359 return static_cast<size_t>(Key.hashValue());
360 }
361
EmitKeyDataLength(raw_ostream & OS,key_type_ref,data_type_ref)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>(KeyLength);
369 writer.write<uint16_t>(DataLength);
370 return {KeyLength, DataLength};
371 }
372
EmitKey(raw_ostream & OS,key_type_ref Key,unsigned)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>(Key.parentContextID);
376 writer.write<uint8_t>(Key.contextKind);
377 writer.write<uint32_t>(Key.contextID);
378 }
379
EmitData(raw_ostream & OS,key_type_ref,data_type_ref Data,unsigned)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>(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.
getVersionTupleSize(const VersionTuple & VT)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>
getVersionedInfoSize(const llvm::SmallVectorImpl<std::pair<llvm::VersionTuple,T>> & VI,llvm::function_ref<unsigned (const typename MakeDependent<T>::Type &)> getInfoSize)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.
emitVersionTuple(raw_ostream & OS,const VersionTuple & VT)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>(descriptor);
432
433 // Write the components.
434 writer.write<uint32_t>(VT.getMajor());
435 if (auto minor = VT.getMinor())
436 writer.write<uint32_t>(*minor);
437 if (auto subminor = VT.getSubminor())
438 writer.write<uint32_t>(*subminor);
439 if (auto build = VT.getBuild())
440 writer.write<uint32_t>(*build);
441 }
442
443 /// Emit versioned information.
444 template <typename T>
emitVersionedInfo(raw_ostream & OS,llvm::SmallVectorImpl<std::pair<VersionTuple,T>> & VI,llvm::function_ref<void (raw_ostream &,const typename MakeDependent<T>::Type &)> emitInfo)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 {
asDerived()469 Derived &asDerived() { return *static_cast<Derived *>(this); }
470
asDerived() const471 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>
EmitKeyDataLength(raw_ostream & OS,key_type_ref Key,data_type_ref Data)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>(KeyLength);
494 writer.write<uint16_t>(DataLength);
495 return {KeyLength, DataLength};
496 }
497
EmitData(raw_ostream & OS,key_type_ref,data_type_ref Data,unsigned)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.
emitCommonEntityInfo(raw_ostream & OS,const CommonEntityInfo & CEI)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>(payload);
522
523 writer.write<uint16_t>(CEI.UnavailableMsg.size());
524 OS.write(CEI.UnavailableMsg.c_str(), CEI.UnavailableMsg.size());
525
526 writer.write<uint16_t>(CEI.SwiftName.size());
527 OS.write(CEI.SwiftName.c_str(), CEI.SwiftName.size());
528 }
529
530 /// Retrieve the serialized size of the given CommonEntityInfo, for use in
531 /// on-disk hash tables.
getCommonEntityInfoSize(const CommonEntityInfo & CEI)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.
getCommonTypeInfoSize(const CommonTypeInfo & CTI)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(CTI);
542 }
543
544 /// Emit a serialized representation of the common type information.
emitCommonTypeInfo(raw_ostream & OS,const CommonTypeInfo & CTI)545 void emitCommonTypeInfo(raw_ostream &OS, const CommonTypeInfo &CTI) {
546 emitCommonEntityInfo(OS, CTI);
547
548 llvm::support::endian::Writer writer(OS, llvm::endianness::little);
549 if (auto swiftBridge = CTI.getSwiftBridge()) {
550 writer.write<uint16_t>(swiftBridge->size() + 1);
551 OS.write(swiftBridge->c_str(), swiftBridge->size());
552 } else {
553 writer.write<uint16_t>(0);
554 }
555 if (auto nsErrorDomain = CTI.getNSErrorDomain()) {
556 writer.write<uint16_t>(nsErrorDomain->size() + 1);
557 OS.write(nsErrorDomain->c_str(), CTI.getNSErrorDomain()->size());
558 } else {
559 writer.write<uint16_t>(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:
getKeyLength(key_type_ref)567 unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); }
568
EmitKey(raw_ostream & OS,key_type_ref Key,unsigned)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>(Key);
572 }
573
ComputeHash(key_type_ref Key)574 hash_value_type ComputeHash(key_type_ref Key) {
575 return static_cast<size_t>(llvm::hash_value(Key));
576 }
577
getUnversionedInfoSize(const ContextInfo & OCI)578 unsigned getUnversionedInfoSize(const ContextInfo &OCI) {
579 return getCommonTypeInfoSize(OCI) + 1;
580 }
581
emitUnversionedInfo(raw_ostream & OS,const ContextInfo & OCI)582 void emitUnversionedInfo(raw_ostream &OS, const ContextInfo &OCI) {
583 emitCommonTypeInfo(OS, 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
writeContextBlock(llvm::BitstreamWriter & Stream)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(OC.first, 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>(BlobStream, 0,
619 llvm::endianness::little);
620 Offset = Generator.Emit(BlobStream);
621 }
622
623 context_block::ContextIDLayout ContextID(Stream);
624 ContextID.emit(Scratch, Offset, 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(OC.second.first, 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>(BlobStream, 0,
638 llvm::endianness::little);
639 Offset = Generator.Emit(BlobStream);
640 }
641
642 context_block::ContextInfoLayout ContextInfo(Stream);
643 ContextInfo.emit(Scratch, Offset, HashTableBlob);
644 }
645 }
646
647 namespace {
648 /// Retrieve the serialized size of the given VariableInfo, for use in
649 /// on-disk hash tables.
getVariableInfoSize(const VariableInfo & VI)650 unsigned getVariableInfoSize(const VariableInfo &VI) {
651 return 2 + getCommonEntityInfoSize(VI) + 2 + VI.getType().size();
652 }
653
654 /// Emit a serialized representation of the variable information.
emitVariableInfo(raw_ostream & OS,const VariableInfo & VI)655 void emitVariableInfo(raw_ostream &OS, const VariableInfo &VI) {
656 emitCommonEntityInfo(OS, 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(reinterpret_cast<const char *>(bytes), 2);
667
668 llvm::support::endian::Writer writer(OS, llvm::endianness::little);
669 writer.write<uint16_t>(VI.getType().size());
670 OS.write(VI.getType().data(), 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:
getKeyLength(key_type_ref)679 unsigned getKeyLength(key_type_ref) {
680 return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
681 }
682
EmitKey(raw_ostream & OS,key_type_ref Key,unsigned)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>(std::get<0>(Key));
686 writer.write<uint32_t>(std::get<1>(Key));
687 writer.write<uint8_t>(std::get<2>(Key));
688 }
689
ComputeHash(key_type_ref Key)690 hash_value_type ComputeHash(key_type_ref Key) {
691 return static_cast<size_t>(llvm::hash_value(Key));
692 }
693
getUnversionedInfoSize(const ObjCPropertyInfo & OPI)694 unsigned getUnversionedInfoSize(const ObjCPropertyInfo &OPI) {
695 return getVariableInfoSize(OPI) + 1;
696 }
697
emitUnversionedInfo(raw_ostream & OS,const ObjCPropertyInfo & OPI)698 void emitUnversionedInfo(raw_ostream &OS, const ObjCPropertyInfo &OPI) {
699 emitVariableInfo(OS, 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
writeObjCPropertyBlock(llvm::BitstreamWriter & Stream)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(OP.first, 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>(BlobStream, 0,
729 llvm::endianness::little);
730 Offset = Generator.Emit(BlobStream);
731 }
732
733 objc_property_block::ObjCPropertyDataLayout ObjCPropertyData(Stream);
734 ObjCPropertyData.emit(Scratch, Offset, 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:
getKeyLength(key_type_ref)748 unsigned getKeyLength(key_type_ref) {
749 return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
750 }
751
EmitKey(raw_ostream & OS,key_type_ref Key,unsigned)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>(std::get<0>(Key));
755 writer.write<uint32_t>(std::get<1>(Key));
756 writer.write<uint8_t>(std::get<2>(Key));
757 }
758
ComputeHash(key_type_ref key)759 hash_value_type ComputeHash(key_type_ref key) {
760 return static_cast<size_t>(llvm::hash_value(key));
761 }
762
getUnversionedInfoSize(const ObjCMethodInfo & OMI)763 unsigned getUnversionedInfoSize(const ObjCMethodInfo &OMI) {
764 return getFunctionInfoSize(OMI) + 1;
765 }
766
emitUnversionedInfo(raw_ostream & OS,const ObjCMethodInfo & OMI)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>(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:
getKeyLength(key_type_ref)783 unsigned getKeyLength(key_type_ref) {
784 return sizeof(uint32_t) + sizeof(uint32_t);
785 }
786
EmitKey(raw_ostream & OS,key_type_ref Key,unsigned)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>(Key.parentContextID);
790 writer.write<uint32_t>(Key.nameID);
791 }
792
ComputeHash(key_type_ref key)793 hash_value_type ComputeHash(key_type_ref key) {
794 return static_cast<size_t>(key.hashValue());
795 }
796
getUnversionedInfoSize(const CXXMethodInfo & OMI)797 unsigned getUnversionedInfoSize(const CXXMethodInfo &OMI) {
798 return getFunctionInfoSize(OMI);
799 }
800
emitUnversionedInfo(raw_ostream & OS,const CXXMethodInfo & OMI)801 void emitUnversionedInfo(raw_ostream &OS, const CXXMethodInfo &OMI) {
802 emitFunctionInfo(OS, OMI);
803 }
804 };
805 } // namespace
806
writeObjCMethodBlock(llvm::BitstreamWriter & Stream)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(OM.first, 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>(BlobStream, 0,
825 llvm::endianness::little);
826 Offset = Generator.Emit(BlobStream);
827 }
828
829 objc_method_block::ObjCMethodDataLayout ObjCMethodData(Stream);
830 ObjCMethodData.emit(Scratch, Offset, HashTableBlob);
831 }
832 }
833
writeCXXMethodBlock(llvm::BitstreamWriter & Stream)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(MD.first, 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>(BlobStream, 0,
852 llvm::endianness::little);
853 Offset = Generator.Emit(BlobStream);
854 }
855
856 cxx_method_block::CXXMethodDataLayout CXXMethodData(Stream);
857 CXXMethodData.emit(Scratch, Offset, 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
ComputeHash(key_type_ref Key)872 hash_value_type ComputeHash(key_type_ref Key) {
873 return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(Key);
874 }
875
876 std::pair<unsigned, unsigned>
EmitKeyDataLength(raw_ostream & OS,key_type_ref Key,data_type_ref)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>(KeyLength);
884 writer.write<uint16_t>(DataLength);
885 return {KeyLength, DataLength};
886 }
887
EmitKey(raw_ostream & OS,key_type_ref Key,unsigned)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>(Key.NumArgs);
891 for (auto Identifier : Key.Identifiers)
892 writer.write<uint32_t>(Identifier);
893 }
894
EmitData(raw_ostream & OS,key_type_ref,data_type_ref Data,unsigned)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>(Data);
898 }
899 };
900 } // namespace
901
writeObjCSelectorBlock(llvm::BitstreamWriter & Stream)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(S.first, 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>(BlobStream, 0,
920 llvm::endianness::little);
921 Offset = Generator.Emit(BlobStream);
922 }
923
924 objc_selector_block::ObjCSelectorDataLayout ObjCSelectorData(Stream);
925 ObjCSelectorData.emit(Scratch, Offset, 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:
getKeyLength(key_type_ref)935 unsigned getKeyLength(key_type_ref) {
936 return sizeof(uint32_t) + sizeof(uint32_t);
937 }
938
EmitKey(raw_ostream & OS,key_type_ref Key,unsigned)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>(Key.parentContextID);
942 writer.write<uint32_t>(Key.nameID);
943 }
944
ComputeHash(key_type_ref Key)945 hash_value_type ComputeHash(key_type_ref Key) {
946 return static_cast<size_t>(Key.hashValue());
947 }
948
getUnversionedInfoSize(const GlobalVariableInfo & GVI)949 unsigned getUnversionedInfoSize(const GlobalVariableInfo &GVI) {
950 return getVariableInfoSize(GVI);
951 }
952
emitUnversionedInfo(raw_ostream & OS,const GlobalVariableInfo & GVI)953 void emitUnversionedInfo(raw_ostream &OS, const GlobalVariableInfo &GVI) {
954 emitVariableInfo(OS, GVI);
955 }
956 };
957 } // namespace
958
writeGlobalVariableBlock(llvm::BitstreamWriter & Stream)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(GV.first, 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>(BlobStream, 0,
977 llvm::endianness::little);
978 Offset = Generator.Emit(BlobStream);
979 }
980
981 global_variable_block::GlobalVariableDataLayout GlobalVariableData(Stream);
982 GlobalVariableData.emit(Scratch, Offset, HashTableBlob);
983 }
984 }
985
986 namespace {
getParamInfoSize(const ParamInfo & PI)987 unsigned getParamInfoSize(const ParamInfo &PI) {
988 return getVariableInfoSize(PI) + 1;
989 }
990
emitParamInfo(raw_ostream & OS,const ParamInfo & PI)991 void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
992 emitVariableInfo(OS, 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>(flags);
1006 }
1007
1008 /// Retrieve the serialized size of the given FunctionInfo, for use in on-disk
1009 /// hash tables.
getFunctionInfoSize(const FunctionInfo & FI)1010 unsigned getFunctionInfoSize(const FunctionInfo &FI) {
1011 unsigned size = getCommonEntityInfoSize(FI) + 2 + sizeof(uint64_t);
1012 size += sizeof(uint16_t);
1013 for (const auto &P : FI.Params)
1014 size += getParamInfoSize(P);
1015 size += sizeof(uint16_t) + FI.ResultType.size();
1016 return size;
1017 }
1018
1019 /// Emit a serialized representation of the function information.
emitFunctionInfo(raw_ostream & OS,const FunctionInfo & FI)1020 void emitFunctionInfo(raw_ostream &OS, const FunctionInfo &FI) {
1021 emitCommonEntityInfo(OS, 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>(flags);
1032 writer.write<uint8_t>(FI.NumAdjustedNullable);
1033 writer.write<uint64_t>(FI.NullabilityPayload);
1034
1035 writer.write<uint16_t>(FI.Params.size());
1036 for (const auto &PI : FI.Params)
1037 emitParamInfo(OS, PI);
1038
1039 writer.write<uint16_t>(FI.ResultType.size());
1040 writer.write(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:
getKeyLength(key_type_ref)1048 unsigned getKeyLength(key_type_ref) {
1049 return sizeof(uint32_t) + sizeof(uint32_t);
1050 }
1051
EmitKey(raw_ostream & OS,key_type_ref Key,unsigned)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>(Key.parentContextID);
1055 writer.write<uint32_t>(Key.nameID);
1056 }
1057
ComputeHash(key_type_ref Key)1058 hash_value_type ComputeHash(key_type_ref Key) {
1059 return static_cast<size_t>(Key.hashValue());
1060 }
1061
getUnversionedInfoSize(const GlobalFunctionInfo & GFI)1062 unsigned getUnversionedInfoSize(const GlobalFunctionInfo &GFI) {
1063 return getFunctionInfoSize(GFI);
1064 }
1065
emitUnversionedInfo(raw_ostream & OS,const GlobalFunctionInfo & GFI)1066 void emitUnversionedInfo(raw_ostream &OS, const GlobalFunctionInfo &GFI) {
1067 emitFunctionInfo(OS, GFI);
1068 }
1069 };
1070 } // namespace
1071
writeGlobalFunctionBlock(llvm::BitstreamWriter & Stream)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(F.first, 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>(BlobStream, 0,
1090 llvm::endianness::little);
1091 Offset = Generator.Emit(BlobStream);
1092 }
1093
1094 global_function_block::GlobalFunctionDataLayout GlobalFunctionData(Stream);
1095 GlobalFunctionData.emit(Scratch, Offset, 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:
getKeyLength(key_type_ref)1105 unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); }
1106
EmitKey(raw_ostream & OS,key_type_ref Key,unsigned)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>(Key);
1110 }
1111
ComputeHash(key_type_ref Key)1112 hash_value_type ComputeHash(key_type_ref Key) {
1113 return static_cast<size_t>(llvm::hash_value(Key));
1114 }
1115
getUnversionedInfoSize(const EnumConstantInfo & ECI)1116 unsigned getUnversionedInfoSize(const EnumConstantInfo &ECI) {
1117 return getCommonEntityInfoSize(ECI);
1118 }
1119
emitUnversionedInfo(raw_ostream & OS,const EnumConstantInfo & ECI)1120 void emitUnversionedInfo(raw_ostream &OS, const EnumConstantInfo &ECI) {
1121 emitCommonEntityInfo(OS, ECI);
1122 }
1123 };
1124 } // namespace
1125
writeEnumConstantBlock(llvm::BitstreamWriter & Stream)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(EC.first, 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>(BlobStream, 0,
1144 llvm::endianness::little);
1145 Offset = Generator.Emit(BlobStream);
1146 }
1147
1148 enum_constant_block::EnumConstantDataLayout EnumConstantData(Stream);
1149 EnumConstantData.emit(Scratch, Offset, 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
getKeyLength(key_type_ref)1162 unsigned getKeyLength(key_type_ref) {
1163 return sizeof(uint32_t) + sizeof(IdentifierID);
1164 }
1165
EmitKey(raw_ostream & OS,key_type_ref Key,unsigned)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
ComputeHash(key_type_ref Key)1172 hash_value_type ComputeHash(key_type_ref Key) {
1173 return static_cast<size_t>(Key.hashValue());
1174 }
1175
getUnversionedInfoSize(const UnversionedDataType & UDT)1176 unsigned getUnversionedInfoSize(const UnversionedDataType &UDT) {
1177 return getCommonTypeInfoSize(UDT);
1178 }
1179
emitUnversionedInfo(raw_ostream & OS,const UnversionedDataType & UDT)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:
getUnversionedInfoSize(const TagInfo & TI)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(TI);
1193 }
1194
emitUnversionedInfo(raw_ostream & OS,const TagInfo & TI)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>(Flags);
1209
1210 if (auto Copyable = TI.isSwiftCopyable())
1211 writer.write<uint8_t>(*Copyable ? kSwiftCopyable : kSwiftNonCopyable);
1212 else
1213 writer.write<uint8_t>(0);
1214
1215 if (auto ImportAs = TI.SwiftImportAs) {
1216 writer.write<uint16_t>(ImportAs->size() + 1);
1217 OS.write(ImportAs->c_str(), ImportAs->size());
1218 } else {
1219 writer.write<uint16_t>(0);
1220 }
1221 if (auto RetainOp = TI.SwiftRetainOp) {
1222 writer.write<uint16_t>(RetainOp->size() + 1);
1223 OS.write(RetainOp->c_str(), RetainOp->size());
1224 } else {
1225 writer.write<uint16_t>(0);
1226 }
1227 if (auto ReleaseOp = TI.SwiftReleaseOp) {
1228 writer.write<uint16_t>(ReleaseOp->size() + 1);
1229 OS.write(ReleaseOp->c_str(), ReleaseOp->size());
1230 } else {
1231 writer.write<uint16_t>(0);
1232 }
1233
1234 emitCommonTypeInfo(OS, TI);
1235 }
1236 };
1237 } // namespace
1238
writeTagBlock(llvm::BitstreamWriter & Stream)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(T.first, 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>(BlobStream, 0,
1257 llvm::endianness::little);
1258 Offset = Generator.Emit(BlobStream);
1259 }
1260
1261 tag_block::TagDataLayout TagData(Stream);
1262 TagData.emit(Scratch, Offset, 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:
getUnversionedInfoSize(const TypedefInfo & TI)1271 unsigned getUnversionedInfoSize(const TypedefInfo &TI) {
1272 return 1 + getCommonTypeInfoSize(TI);
1273 }
1274
emitUnversionedInfo(raw_ostream & OS,const TypedefInfo & TI)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>(Flags);
1283
1284 emitCommonTypeInfo(OS, TI);
1285 }
1286 };
1287 } // namespace
1288
writeTypedefBlock(llvm::BitstreamWriter & Stream)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(T.first, 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>(BlobStream, 0,
1307 llvm::endianness::little);
1308 Offset = Generator.Emit(BlobStream);
1309 }
1310
1311 typedef_block::TypedefDataLayout TypedefData(Stream);
1312 TypedefData.emit(Scratch, Offset, HashTableBlob);
1313 }
1314 }
1315
1316 // APINotesWriter
1317
APINotesWriter(llvm::StringRef ModuleName,const FileEntry * SF)1318 APINotesWriter::APINotesWriter(llvm::StringRef ModuleName, const FileEntry *SF)
1319 : Implementation(new class Implementation(ModuleName, SF)) {}
1320
1321 APINotesWriter::~APINotesWriter() = default;
1322
writeToStream(llvm::raw_ostream & OS)1323 void APINotesWriter::writeToStream(llvm::raw_ostream &OS) {
1324 Implementation->writeToStream(OS);
1325 }
1326
addContext(std::optional<ContextID> ParentCtxID,llvm::StringRef Name,ContextKind Kind,const ContextInfo & Info,llvm::VersionTuple SwiftVersion)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(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(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(std::make_pair(
1342 Key, std::make_pair(NextID, 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({SwiftVersion, Info});
1362
1363 return ContextID(Known->second.first);
1364 }
1365
addObjCProperty(ContextID CtxID,StringRef Name,bool IsInstanceProperty,const ObjCPropertyInfo & Info,VersionTuple SwiftVersion)1366 void APINotesWriter::addObjCProperty(ContextID CtxID, StringRef Name,
1367 bool IsInstanceProperty,
1368 const ObjCPropertyInfo &Info,
1369 VersionTuple SwiftVersion) {
1370 IdentifierID NameID = Implementation->getIdentifier(Name);
1371 Implementation
1372 ->ObjCProperties[std::make_tuple(CtxID.Value, NameID, IsInstanceProperty)]
1373 .push_back({SwiftVersion, Info});
1374 }
1375
addObjCMethod(ContextID CtxID,ObjCSelectorRef Selector,bool IsInstanceMethod,const ObjCMethodInfo & Info,VersionTuple SwiftVersion)1376 void APINotesWriter::addObjCMethod(ContextID CtxID, ObjCSelectorRef Selector,
1377 bool IsInstanceMethod,
1378 const ObjCMethodInfo &Info,
1379 VersionTuple SwiftVersion) {
1380 SelectorID SelID = Implementation->getSelector(Selector);
1381 auto Key = std::tuple<unsigned, unsigned, char>{CtxID.Value, SelID,
1382 IsInstanceMethod};
1383 Implementation->ObjCMethods[Key].push_back({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({SwiftVersion, ContextInfo()});
1406 VersionedVec.back().second.setHasDesignatedInits(true);
1407 }
1408 }
1409 }
1410
addCXXMethod(ContextID CtxID,llvm::StringRef Name,const CXXMethodInfo & Info,VersionTuple SwiftVersion)1411 void APINotesWriter::addCXXMethod(ContextID CtxID, llvm::StringRef Name,
1412 const CXXMethodInfo &Info,
1413 VersionTuple SwiftVersion) {
1414 IdentifierID NameID = Implementation->getIdentifier(Name);
1415 SingleDeclTableKey Key(CtxID.Value, NameID);
1416 Implementation->CXXMethods[Key].push_back({SwiftVersion, Info});
1417 }
1418
addGlobalVariable(std::optional<Context> Ctx,llvm::StringRef Name,const GlobalVariableInfo & Info,VersionTuple SwiftVersion)1419 void APINotesWriter::addGlobalVariable(std::optional<Context> Ctx,
1420 llvm::StringRef Name,
1421 const GlobalVariableInfo &Info,
1422 VersionTuple SwiftVersion) {
1423 IdentifierID VariableID = Implementation->getIdentifier(Name);
1424 SingleDeclTableKey Key(Ctx, VariableID);
1425 Implementation->GlobalVariables[Key].push_back({SwiftVersion, Info});
1426 }
1427
addGlobalFunction(std::optional<Context> Ctx,llvm::StringRef Name,const GlobalFunctionInfo & Info,VersionTuple SwiftVersion)1428 void APINotesWriter::addGlobalFunction(std::optional<Context> Ctx,
1429 llvm::StringRef Name,
1430 const GlobalFunctionInfo &Info,
1431 VersionTuple SwiftVersion) {
1432 IdentifierID NameID = Implementation->getIdentifier(Name);
1433 SingleDeclTableKey Key(Ctx, NameID);
1434 Implementation->GlobalFunctions[Key].push_back({SwiftVersion, Info});
1435 }
1436
addEnumConstant(llvm::StringRef Name,const EnumConstantInfo & Info,VersionTuple SwiftVersion)1437 void APINotesWriter::addEnumConstant(llvm::StringRef Name,
1438 const EnumConstantInfo &Info,
1439 VersionTuple SwiftVersion) {
1440 IdentifierID EnumConstantID = Implementation->getIdentifier(Name);
1441 Implementation->EnumConstants[EnumConstantID].push_back({SwiftVersion, Info});
1442 }
1443
addTag(std::optional<Context> Ctx,llvm::StringRef Name,const TagInfo & Info,VersionTuple SwiftVersion)1444 void APINotesWriter::addTag(std::optional<Context> Ctx, llvm::StringRef Name,
1445 const TagInfo &Info, VersionTuple SwiftVersion) {
1446 IdentifierID TagID = Implementation->getIdentifier(Name);
1447 SingleDeclTableKey Key(Ctx, TagID);
1448 Implementation->Tags[Key].push_back({SwiftVersion, Info});
1449 }
1450
addTypedef(std::optional<Context> Ctx,llvm::StringRef Name,const TypedefInfo & Info,VersionTuple SwiftVersion)1451 void APINotesWriter::addTypedef(std::optional<Context> Ctx,
1452 llvm::StringRef Name, const TypedefInfo &Info,
1453 VersionTuple SwiftVersion) {
1454 IdentifierID TypedefID = Implementation->getIdentifier(Name);
1455 SingleDeclTableKey Key(Ctx, TypedefID);
1456 Implementation->Typedefs[Key].push_back({SwiftVersion, Info});
1457 }
1458 } // namespace api_notes
1459 } // namespace clang
1460