This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGf806be5eaae1: APINotes: add initial stub of APINotesWriter 
(authored by Saleem Abdulrasool <abdul...@google.com>, committed by 
compnerd).

Changed prior to commit:
  https://reviews.llvm.org/D92797?vs=310049&id=551157#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D92797/new/

https://reviews.llvm.org/D92797

Files:
  clang/include/clang/APINotes/APINotesWriter.h
  clang/lib/APINotes/APINotesFormat.h
  clang/lib/APINotes/APINotesWriter.cpp
  clang/lib/APINotes/CMakeLists.txt

Index: clang/lib/APINotes/CMakeLists.txt
===================================================================
--- clang/lib/APINotes/CMakeLists.txt
+++ clang/lib/APINotes/CMakeLists.txt
@@ -2,6 +2,7 @@
   Support)
 add_clang_library(clangAPINotes
   APINotesTypes.cpp
+  APINotesWriter.cpp
   APINotesYAMLCompiler.cpp
   LINK_LIBS
     clangBasic)
Index: clang/lib/APINotes/APINotesWriter.cpp
===================================================================
--- /dev/null
+++ clang/lib/APINotes/APINotesWriter.cpp
@@ -0,0 +1,1165 @@
+//===-- APINotesWriter.cpp - API Notes Writer -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/APINotes/APINotesWriter.h"
+#include "APINotesFormat.h"
+#include "clang/APINotes/Types.h"
+#include "clang/Basic/FileManager.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Bitstream/BitstreamWriter.h"
+#include "llvm/Support/DJB.h"
+#include "llvm/Support/OnDiskHashTable.h"
+#include "llvm/Support/VersionTuple.h"
+
+namespace clang {
+namespace api_notes {
+class APINotesWriter::Implementation {
+  template <typename T>
+  using VersionedSmallVector =
+      llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1>;
+
+  std::string ModuleName;
+  const FileEntry *SourceFile;
+
+  /// Scratch space for bitstream writing.
+  llvm::SmallVector<uint64_t, 64> Scratch;
+
+#if defined(__APPLE__) && defined(SWIFT_DOWNSTREAM)
+  bool SwiftImportAsMember = false;
+#endif
+
+  /// Mapping from strings to identifier IDs.
+  llvm::StringMap<IdentifierID> IdentifierIDs;
+
+  /// Information about Objective-C contexts (classes or protocols).
+  ///
+  /// Indexed by the identifier ID and a bit indication whether we're looking
+  /// for a class (0) or protocol (1) and provides both the context ID and
+  /// information describing the context within that module.
+  llvm::DenseMap<std::pair<unsigned, char>,
+                 std::pair<unsigned, VersionedSmallVector<ObjCContextInfo>>>
+      ObjCContexts;
+
+  /// Information about Objective-C properties.
+  ///
+  /// Indexed by the context ID, property name, and whether this is an
+  /// instance property.
+  llvm::DenseMap<
+      std::tuple<unsigned, unsigned, char>,
+      llvm::SmallVector<std::pair<VersionTuple, ObjCPropertyInfo>, 1>>
+      ObjCProperties;
+
+  /// Information about Objective-C methods.
+  ///
+  /// Indexed by the context ID, selector ID, and Boolean (stored as a char)
+  /// indicating whether this is a class or instance method.
+  llvm::DenseMap<std::tuple<unsigned, unsigned, char>,
+                 llvm::SmallVector<std::pair<VersionTuple, ObjCMethodInfo>, 1>>
+      ObjCMethods;
+
+  /// Mapping from selectors to selector ID.
+  llvm::DenseMap<StoredObjCSelector, SelectorID> SelectorIDs;
+
+  /// Information about global variables.
+  ///
+  /// Indexed by the identifier ID.
+  llvm::DenseMap<unsigned, llvm::SmallVector<
+                               std::pair<VersionTuple, GlobalVariableInfo>, 1>>
+      GlobalVariables;
+
+  /// Information about global functions.
+  ///
+  /// Indexed by the identifier ID.
+  llvm::DenseMap<unsigned, llvm::SmallVector<
+                               std::pair<VersionTuple, GlobalFunctionInfo>, 1>>
+      GlobalFunctions;
+
+  /// Information about enumerators.
+  ///
+  /// Indexed by the identifier ID.
+  llvm::DenseMap<
+      unsigned, llvm::SmallVector<std::pair<VersionTuple, EnumConstantInfo>, 1>>
+      EnumConstants;
+
+  /// Information about tags.
+  ///
+  /// Indexed by the identifier ID.
+  llvm::DenseMap<unsigned,
+                 llvm::SmallVector<std::pair<VersionTuple, TagInfo>, 1>>
+      Tags;
+
+  /// Information about typedefs.
+  ///
+  /// Indexed by the identifier ID.
+  llvm::DenseMap<unsigned,
+                 llvm::SmallVector<std::pair<VersionTuple, TypedefInfo>, 1>>
+      Typedefs;
+
+  void writeBlockInfoBlock(llvm::BitstreamWriter &Stream);
+  void writeControlBlock(llvm::BitstreamWriter &Stream);
+  void writeIdentifierBlock(llvm::BitstreamWriter &Stream);
+  void writeObjCContextBlock(llvm::BitstreamWriter &Stream);
+  void writeObjCPropertyBlock(llvm::BitstreamWriter &Stream);
+  void writeObjCMethodBlock(llvm::BitstreamWriter &Stream);
+  void writeObjCSelectorBlock(llvm::BitstreamWriter &Stream);
+  void writeGlobalVariableBlock(llvm::BitstreamWriter &Stream);
+  void writeGlobalFunctionBlock(llvm::BitstreamWriter &Stream);
+  void writeEnumConstantBlock(llvm::BitstreamWriter &Stream);
+  void writeTagBlock(llvm::BitstreamWriter &Stream);
+  void writeTypedefBlock(llvm::BitstreamWriter &Stream);
+
+public:
+  Implementation(llvm::StringRef ModuleName, const FileEntry *SF)
+      : ModuleName(std::string(ModuleName)), SourceFile(SF) {}
+
+  void writeToStream(llvm::raw_ostream &OS);
+};
+
+void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &OS) {
+  llvm::SmallVector<char, 0> Buffer;
+
+  {
+    llvm::BitstreamWriter Stream(Buffer);
+
+    // Emit the signature.
+    for (unsigned char Byte : API_NOTES_SIGNATURE)
+      Stream.Emit(Byte, 8);
+
+    // Emit the blocks.
+    writeBlockInfoBlock(Stream);
+    writeControlBlock(Stream);
+    writeIdentifierBlock(Stream);
+    writeObjCContextBlock(Stream);
+    writeObjCPropertyBlock(Stream);
+    writeObjCMethodBlock(Stream);
+    writeObjCSelectorBlock(Stream);
+    writeGlobalVariableBlock(Stream);
+    writeGlobalFunctionBlock(Stream);
+    writeEnumConstantBlock(Stream);
+    writeTagBlock(Stream);
+    writeTypedefBlock(Stream);
+  }
+
+  OS.write(Buffer.data(), Buffer.size());
+  OS.flush();
+}
+
+namespace {
+/// Record the name of a block.
+void emitBlockID(llvm::BitstreamWriter &Stream, unsigned ID,
+                 llvm::StringRef Name) {
+  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID,
+                    llvm::ArrayRef<unsigned>{ID});
+
+  // Emit the block name if present.
+  if (Name.empty())
+    return;
+  Stream.EmitRecord(
+      llvm::bitc::BLOCKINFO_CODE_BLOCKNAME,
+      llvm::ArrayRef<unsigned char>(
+          const_cast<unsigned char *>(
+              reinterpret_cast<const unsigned char *>(Name.data())),
+          Name.size()));
+}
+
+/// Record the name of a record within a block.
+void emitRecordID(llvm::BitstreamWriter &Stream, unsigned ID,
+                  llvm::StringRef Name) {
+  assert(ID < 256 && "can't fit record ID in next to name");
+
+  llvm::SmallVector<unsigned char, 64> Buffer;
+  Buffer.resize(Name.size() + 1);
+  Buffer[0] = ID;
+  memcpy(Buffer.data() + 1, Name.data(), Name.size());
+
+  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Buffer);
+}
+} // namespace
+
+void APINotesWriter::Implementation::writeBlockInfoBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII Scope(Stream, llvm::bitc::BLOCKINFO_BLOCK_ID, 2);
+
+#define BLOCK(Block) emitBlockID(Stream, Block##_ID, #Block)
+#define BLOCK_RECORD(NameSpace, Block)                                         \
+  emitRecordID(Stream, NameSpace::Block, #Block)
+  BLOCK(CONTROL_BLOCK);
+  BLOCK_RECORD(control_block, METADATA);
+  BLOCK_RECORD(control_block, MODULE_NAME);
+
+  BLOCK(IDENTIFIER_BLOCK);
+  BLOCK_RECORD(identifier_block, IDENTIFIER_DATA);
+
+  BLOCK(OBJC_CONTEXT_BLOCK);
+  BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_ID_DATA);
+
+  BLOCK(OBJC_PROPERTY_BLOCK);
+  BLOCK_RECORD(objc_property_block, OBJC_PROPERTY_DATA);
+
+  BLOCK(OBJC_METHOD_BLOCK);
+  BLOCK_RECORD(objc_method_block, OBJC_METHOD_DATA);
+
+  BLOCK(OBJC_SELECTOR_BLOCK);
+  BLOCK_RECORD(objc_selector_block, OBJC_SELECTOR_DATA);
+
+  BLOCK(GLOBAL_VARIABLE_BLOCK);
+  BLOCK_RECORD(global_variable_block, GLOBAL_VARIABLE_DATA);
+
+  BLOCK(GLOBAL_FUNCTION_BLOCK);
+  BLOCK_RECORD(global_function_block, GLOBAL_FUNCTION_DATA);
+#undef BLOCK_RECORD
+#undef BLOCK
+}
+
+void APINotesWriter::Implementation::writeControlBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII Scope(Stream, CONTROL_BLOCK_ID, 3);
+
+  control_block::MetadataLayout Metadata(Stream);
+  Metadata.emit(Scratch, VERSION_MAJOR, VERSION_MINOR);
+
+  control_block::ModuleNameLayout ModuleName(Stream);
+  ModuleName.emit(Scratch, this->ModuleName);
+
+#if defined(__APPLE__) && defined(SWIFT_DOWNSTREAM)
+  if (SwiftInferImportAsMember) {
+    control_block::ModuleOptionsLayout ModuleOptions(Stream);
+    ModuleOptions.emit(Scratch, SwiftInferImportAsMember);
+  }
+#endif
+
+  if (SourceFile) {
+    control_block::SourceFileLayout SourceFile(Stream);
+    SourceFile.emit(Scratch, this->SourceFile->getSize(),
+                    this->SourceFile->getModificationTime());
+  }
+}
+
+namespace {
+/// Used to serialize the on-disk identifier table.
+class IdentifierTableInfo {
+public:
+  using key_type = StringRef;
+  using key_type_ref = key_type;
+  using data_type = IdentifierID;
+  using data_type_ref = const data_type &;
+  using hash_value_type = uint32_t;
+  using offset_type = unsigned;
+
+  hash_value_type ComputeHash(key_type_ref Key) { return llvm::djbHash(Key); }
+
+  std::pair<unsigned, unsigned>
+  EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref) {
+    uint32_t KeyLength = Key.size();
+    uint32_t DataLength = sizeof(uint32_t);
+
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint16_t>(KeyLength);
+    writer.write<uint16_t>(DataLength);
+    return {KeyLength, DataLength};
+  }
+
+  void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) { OS << Key; }
+
+  void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint32_t>(Data);
+  }
+};
+} // namespace
+
+void APINotesWriter::Implementation::writeIdentifierBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII restoreBlock(Stream, IDENTIFIER_BLOCK_ID, 3);
+
+  if (IdentifierIDs.empty())
+    return;
+
+  llvm::SmallString<4096> HashTableBlob;
+  uint32_t Offset;
+  {
+    llvm::OnDiskChainedHashTableGenerator<IdentifierTableInfo> Generator;
+    for (auto &II : IdentifierIDs)
+      Generator.insert(II.first(), II.second);
+
+    llvm::raw_svector_ostream BlobStream(HashTableBlob);
+    // Make sure that no bucket is at offset 0
+    llvm::support::endian::write<uint32_t>(BlobStream, 0,
+                                           llvm::support::little);
+    Offset = Generator.Emit(BlobStream);
+  }
+
+  identifier_block::IdentifierDataLayout IdentifierData(Stream);
+  IdentifierData.emit(Scratch, Offset, HashTableBlob);
+}
+
+namespace {
+/// Used to serialize the on-disk Objective-C context table.
+class ObjCContextIDTableInfo {
+public:
+  using key_type = std::pair<unsigned, char>; // identifier ID, is-protocol
+  using key_type_ref = key_type;
+  using data_type = unsigned;
+  using data_type_ref = const data_type &;
+  using hash_value_type = size_t;
+  using offset_type = unsigned;
+
+  hash_value_type ComputeHash(key_type_ref Key) {
+    return static_cast<size_t>(llvm::hash_value(Key));
+  }
+
+  std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &OS, key_type_ref,
+                                                  data_type_ref) {
+    uint32_t KeyLength = sizeof(uint32_t) + 1;
+    uint32_t DataLength = sizeof(uint32_t);
+
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint16_t>(KeyLength);
+    writer.write<uint16_t>(DataLength);
+    return {KeyLength, DataLength};
+  }
+
+  void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint32_t>(Key.first);
+    writer.write<uint8_t>(Key.second);
+  }
+
+  void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint32_t>(Data);
+  }
+};
+
+/// Localized helper to make a type dependent, thwarting template argument
+/// deduction.
+template <typename T> struct MakeDependent { typedef T Type; };
+
+/// Retrieve the serialized size of the given VersionTuple, for use in
+/// on-disk hash tables.
+unsigned getVersionTupleSize(const VersionTuple &VT) {
+  unsigned size = sizeof(uint8_t) + /*major*/ sizeof(uint32_t);
+  if (VT.getMinor())
+    size += sizeof(uint32_t);
+  if (VT.getSubminor())
+    size += sizeof(uint32_t);
+  if (VT.getBuild())
+    size += sizeof(uint32_t);
+  return size;
+}
+
+/// Determine the size of an array of versioned information,
+template <typename T>
+unsigned getVersionedInfoSize(
+    const llvm::SmallVectorImpl<std::pair<llvm::VersionTuple, T>> &VI,
+    llvm::function_ref<unsigned(const typename MakeDependent<T>::Type &)>
+        getInfoSize) {
+  unsigned result = sizeof(uint16_t); // # of elements
+  for (const auto &E : VI) {
+    result += getVersionTupleSize(E.first);
+    result += getInfoSize(E.second);
+  }
+  return result;
+}
+
+/// Emit a serialized representation of a version tuple.
+void emitVersionTuple(raw_ostream &OS, const VersionTuple &VT) {
+  llvm::support::endian::Writer writer(OS, llvm::support::little);
+
+  // First byte contains the number of components beyond the 'major' component.
+  uint8_t descriptor;
+  if (VT.getBuild())
+    descriptor = 3;
+  else if (VT.getSubminor())
+    descriptor = 2;
+  else if (VT.getMinor())
+    descriptor = 1;
+  else
+    descriptor = 0;
+  writer.write<uint8_t>(descriptor);
+
+  // Write the components.
+  writer.write<uint32_t>(VT.getMajor());
+  if (auto minor = VT.getMinor())
+    writer.write<uint32_t>(*minor);
+  if (auto subminor = VT.getSubminor())
+    writer.write<uint32_t>(*subminor);
+  if (auto build = VT.getBuild())
+    writer.write<uint32_t>(*build);
+}
+
+/// Emit versioned information.
+template <typename T>
+void emitVersionedInfo(
+    raw_ostream &OS, llvm::SmallVectorImpl<std::pair<VersionTuple, T>> &VI,
+    llvm::function_ref<void(raw_ostream &,
+                            const typename MakeDependent<T>::Type &)>
+        emitInfo) {
+  std::sort(VI.begin(), VI.end(),
+            [](const std::pair<VersionTuple, T> &LHS,
+               const std::pair<VersionTuple, T> &RHS) -> bool {
+              assert(LHS.first != RHS.first &&
+                     "two entries for the same version");
+              return LHS.first < RHS.first;
+            });
+
+  llvm::support::endian::Writer writer(OS, llvm::support::little);
+  writer.write<uint16_t>(VI.size());
+  for (const auto &E : VI) {
+    emitVersionTuple(OS, E.first);
+    emitInfo(OS, E.second);
+  }
+}
+
+/// On-disk hash table info key base for handling versioned data.
+template <typename Derived, typename KeyType, typename UnversionedDataType>
+class VersionedTableInfo {
+  Derived &asDerived() { return *static_cast<Derived *>(this); }
+
+  const Derived &asDerived() const {
+    return *static_cast<const Derived *>(this);
+  }
+
+public:
+  using key_type = KeyType;
+  using key_type_ref = key_type;
+  using data_type =
+      llvm::SmallVector<std::pair<llvm::VersionTuple, UnversionedDataType>, 1>;
+  using data_type_ref = data_type &;
+  using hash_value_type = size_t;
+  using offset_type = unsigned;
+
+  hash_value_type ComputeHash(key_type_ref Key) {
+    return llvm::hash_value(Key);
+  }
+
+  std::pair<unsigned, unsigned>
+  EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref Data) {
+    uint32_t KeyLength = asDerived().getKeyLength(Key);
+    uint32_t DataLength =
+        getVersionedInfoSize(Data, [this](const UnversionedDataType &UI) {
+          return asDerived().getUnversionedInfoSize(UI);
+        });
+
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint16_t>(KeyLength);
+    writer.write<uint16_t>(DataLength);
+    return {KeyLength, DataLength};
+  }
+
+  void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) {
+    emitVersionedInfo(
+        OS, Data, [this](llvm::raw_ostream &OS, const UnversionedDataType &UI) {
+          asDerived().emitUnversionedInfo(OS, UI);
+        });
+  }
+};
+
+/// Emit a serialized representation of the common entity information.
+void emitCommonEntityInfo(raw_ostream &OS, const CommonEntityInfo &CEI) {
+  llvm::support::endian::Writer writer(OS, llvm::support::little);
+
+  uint8_t payload = 0;
+  if (auto swiftPrivate = CEI.isSwiftPrivate()) {
+    payload |= 0x01;
+    if (*swiftPrivate)
+      payload |= 0x02;
+  }
+  payload <<= 1;
+  payload |= CEI.Unavailable;
+  payload <<= 1;
+  payload |= CEI.UnavailableInSwift;
+
+  writer.write<uint8_t>(payload);
+
+  writer.write<uint16_t>(CEI.UnavailableMsg.size());
+  OS.write(CEI.UnavailableMsg.c_str(), CEI.UnavailableMsg.size());
+
+  writer.write<uint16_t>(CEI.SwiftName.size());
+  OS.write(CEI.SwiftName.c_str(), CEI.SwiftName.size());
+}
+
+/// Retrieve the serialized size of the given CommonEntityInfo, for use in
+/// on-disk hash tables.
+unsigned getCommonEntityInfoSize(const CommonEntityInfo &CEI) {
+  return 5 + CEI.UnavailableMsg.size() + CEI.SwiftName.size();
+}
+
+// Retrieve the serialized size of the given CommonTypeInfo, for use
+// in on-disk hash tables.
+unsigned getCommonTypeInfoSize(const CommonTypeInfo &CTI) {
+  return 2 + (CTI.getSwiftBridge() ? CTI.getSwiftBridge()->size() : 0) + 2 +
+         (CTI.getNSErrorDomain() ? CTI.getNSErrorDomain()->size() : 0) +
+         getCommonEntityInfoSize(CTI);
+}
+
+/// Emit a serialized representation of the common type information.
+void emitCommonTypeInfo(raw_ostream &OS, const CommonTypeInfo &CTI) {
+  emitCommonEntityInfo(OS, CTI);
+
+  llvm::support::endian::Writer writer(OS, llvm::support::little);
+  if (auto swiftBridge = CTI.getSwiftBridge()) {
+    writer.write<uint16_t>(swiftBridge->size() + 1);
+    OS.write(swiftBridge->c_str(), swiftBridge->size());
+  } else {
+    writer.write<uint16_t>(0);
+  }
+  if (auto nsErrorDomain = CTI.getNSErrorDomain()) {
+    writer.write<uint16_t>(nsErrorDomain->size() + 1);
+    OS.write(nsErrorDomain->c_str(), CTI.getNSErrorDomain()->size());
+  } else {
+    writer.write<uint16_t>(0);
+  }
+}
+
+/// Used to serialize the on-disk Objective-C property table.
+class ObjCContextInfoTableInfo
+    : public VersionedTableInfo<ObjCContextInfoTableInfo, unsigned,
+                                ObjCContextInfo> {
+public:
+  unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); }
+
+  void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint32_t>(Key);
+  }
+
+  unsigned getUnversionedInfoSize(const ObjCContextInfo &OCI) {
+    return getCommonTypeInfoSize(OCI) + 1;
+  }
+
+  void emitUnversionedInfo(raw_ostream &OS, const ObjCContextInfo &OCI) {
+    emitCommonTypeInfo(OS, OCI);
+
+    uint8_t payload = 0;
+    if (auto swiftImportAsNonGeneric = OCI.getSwiftImportAsNonGeneric())
+      payload |= (0x01 << 1) | swiftImportAsNonGeneric.value();
+    payload <<= 2;
+    if (auto swiftObjCMembers = OCI.getSwiftObjCMembers())
+      payload |= (0x01 << 1) | swiftObjCMembers.value();
+    payload <<= 3;
+    if (auto nullable = OCI.getDefaultNullability())
+      payload |= (0x01 << 2) | static_cast<uint8_t>(*nullable);
+    payload = (payload << 1) | (OCI.hasDesignatedInits() ? 1 : 0);
+
+    OS << payload;
+  }
+};
+} // namespace
+
+void APINotesWriter::Implementation::writeObjCContextBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII restoreBlock(Stream, OBJC_CONTEXT_BLOCK_ID, 3);
+
+  if (ObjCContexts.empty())
+    return;
+
+  {
+    llvm::SmallString<4096> HashTableBlob;
+    uint32_t Offset;
+    {
+      llvm::OnDiskChainedHashTableGenerator<ObjCContextIDTableInfo> Generator;
+      for (auto &OC : ObjCContexts)
+        Generator.insert(OC.first, OC.second.first);
+
+      llvm::raw_svector_ostream BlobStream(HashTableBlob);
+      // Make sure that no bucket is at offset 0
+      llvm::support::endian::write<uint32_t>(BlobStream, 0,
+                                             llvm::support::little);
+      Offset = Generator.Emit(BlobStream);
+    }
+
+    objc_context_block::ObjCContextIDLayout ObjCContextID(Stream);
+    ObjCContextID.emit(Scratch, Offset, HashTableBlob);
+  }
+
+  {
+    llvm::SmallString<4096> HashTableBlob;
+    uint32_t Offset;
+    {
+      llvm::OnDiskChainedHashTableGenerator<ObjCContextInfoTableInfo> Generator;
+      for (auto &OC : ObjCContexts)
+        Generator.insert(OC.second.first, OC.second.second);
+
+      llvm::raw_svector_ostream BlobStream(HashTableBlob);
+      // Make sure that no bucket is at offset 0
+      llvm::support::endian::write<uint32_t>(BlobStream, 0,
+                                             llvm::support::little);
+      Offset = Generator.Emit(BlobStream);
+    }
+
+    objc_context_block::ObjCContextInfoLayout ObjCContextInfo(Stream);
+    ObjCContextInfo.emit(Scratch, Offset, HashTableBlob);
+  }
+}
+
+namespace {
+/// Retrieve the serialized size of the given VariableInfo, for use in
+/// on-disk hash tables.
+unsigned getVariableInfoSize(const VariableInfo &VI) {
+  return 2 + getCommonEntityInfoSize(VI) + 2 + VI.getType().size();
+}
+
+/// Emit a serialized representation of the variable information.
+void emitVariableInfo(raw_ostream &OS, const VariableInfo &VI) {
+  emitCommonEntityInfo(OS, VI);
+
+  uint8_t bytes[2] = {0, 0};
+  if (auto nullable = VI.getNullability()) {
+    bytes[0] = 1;
+    bytes[1] = static_cast<uint8_t>(*nullable);
+  } else {
+    // Nothing to do.
+  }
+
+  OS.write(reinterpret_cast<const char *>(bytes), 2);
+
+  llvm::support::endian::Writer writer(OS, llvm::support::little);
+  writer.write<uint16_t>(VI.getType().size());
+  OS.write(VI.getType().data(), VI.getType().size());
+}
+
+/// Used to serialize the on-disk Objective-C property table.
+class ObjCPropertyTableInfo
+    : public VersionedTableInfo<ObjCPropertyTableInfo,
+                                std::tuple<unsigned, unsigned, char>,
+                                ObjCPropertyInfo> {
+public:
+  unsigned getKeyLength(key_type_ref) {
+    return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
+  }
+
+  void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint32_t>(std::get<0>(Key));
+    writer.write<uint32_t>(std::get<1>(Key));
+    writer.write<uint8_t>(std::get<2>(Key));
+  }
+
+  unsigned getUnversionedInfoSize(const ObjCPropertyInfo &OPI) {
+    return getVariableInfoSize(OPI) + 1;
+  }
+
+  void emitUnversionedInfo(raw_ostream &OS, const ObjCPropertyInfo &OPI) {
+    emitVariableInfo(OS, OPI);
+
+    uint8_t flags = 0;
+    if (auto value = OPI.getSwiftImportAsAccessors()) {
+      flags |= 1 << 0;
+      flags |= value.value() << 1;
+    }
+    OS << flags;
+  }
+};
+} // namespace
+
+void APINotesWriter::Implementation::writeObjCPropertyBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII Scope(Stream, OBJC_PROPERTY_BLOCK_ID, 3);
+
+  if (ObjCProperties.empty())
+    return;
+
+  {
+    llvm::SmallString<4096> HashTableBlob;
+    uint32_t Offset;
+    {
+      llvm::OnDiskChainedHashTableGenerator<ObjCPropertyTableInfo> Generator;
+      for (auto &OP : ObjCProperties)
+        Generator.insert(OP.first, OP.second);
+
+      llvm::raw_svector_ostream BlobStream(HashTableBlob);
+      // Make sure that no bucket is at offset 0
+      llvm::support::endian::write<uint32_t>(BlobStream, 0,
+                                             llvm::support::little);
+      Offset = Generator.Emit(BlobStream);
+    }
+
+    objc_property_block::ObjCPropertyDataLayout ObjCPropertyData(Stream);
+    ObjCPropertyData.emit(Scratch, Offset, HashTableBlob);
+  }
+}
+
+namespace {
+unsigned getFunctionInfoSize(const FunctionInfo &);
+void emitFunctionInfo(llvm::raw_ostream &, const FunctionInfo &);
+
+/// Used to serialize the on-disk Objective-C method table.
+class ObjCMethodTableInfo
+    : public VersionedTableInfo<ObjCMethodTableInfo,
+                                std::tuple<unsigned, unsigned, char>,
+                                ObjCMethodInfo> {
+public:
+  unsigned getKeyLength(key_type_ref) {
+    return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
+  }
+
+  void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint32_t>(std::get<0>(Key));
+    writer.write<uint32_t>(std::get<1>(Key));
+    writer.write<uint8_t>(std::get<2>(Key));
+  }
+
+  unsigned getUnversionedInfoSize(const ObjCMethodInfo &OMI) {
+    return getFunctionInfoSize(OMI) + 1;
+  }
+
+  void emitUnversionedInfo(raw_ostream &OS, const ObjCMethodInfo &OMI) {
+    uint8_t flags = 0;
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    flags = (flags << 1) | OMI.DesignatedInit;
+    flags = (flags << 1) | OMI.RequiredInit;
+    writer.write<uint8_t>(flags);
+
+    emitFunctionInfo(OS, OMI);
+  }
+};
+} // namespace
+
+void APINotesWriter::Implementation::writeObjCMethodBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII Scope(Stream, OBJC_METHOD_BLOCK_ID, 3);
+
+  if (ObjCMethods.empty())
+    return;
+
+  {
+    llvm::SmallString<4096> HashTableBlob;
+    uint32_t Offset;
+    {
+      llvm::OnDiskChainedHashTableGenerator<ObjCMethodTableInfo> Generator;
+      for (auto &OM : ObjCMethods)
+        Generator.insert(OM.first, OM.second);
+
+      llvm::raw_svector_ostream BlobStream(HashTableBlob);
+      // Make sure that no bucket is at offset 0
+      llvm::support::endian::write<uint32_t>(BlobStream, 0,
+                                             llvm::support::little);
+      Offset = Generator.Emit(BlobStream);
+    }
+
+    objc_method_block::ObjCMethodDataLayout ObjCMethodData(Stream);
+    ObjCMethodData.emit(Scratch, Offset, HashTableBlob);
+  }
+}
+
+namespace {
+/// Used to serialize the on-disk Objective-C selector table.
+class ObjCSelectorTableInfo {
+public:
+  using key_type = StoredObjCSelector;
+  using key_type_ref = const key_type &;
+  using data_type = SelectorID;
+  using data_type_ref = data_type;
+  using hash_value_type = unsigned;
+  using offset_type = unsigned;
+
+  hash_value_type ComputeHash(key_type_ref Key) {
+    return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(Key);
+  }
+
+  std::pair<unsigned, unsigned>
+  EmitKeyDataLength(raw_ostream &OS, key_type_ref Key, data_type_ref) {
+    uint32_t KeyLength =
+        sizeof(uint16_t) + sizeof(uint32_t) * Key.Identifiers.size();
+    uint32_t DataLength = sizeof(uint32_t);
+
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint16_t>(KeyLength);
+    writer.write<uint16_t>(DataLength);
+    return {KeyLength, DataLength};
+  }
+
+  void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint16_t>(Key.NumPieces);
+    for (auto Identifier : Key.Identifiers)
+      writer.write<uint32_t>(Identifier);
+  }
+
+  void EmitData(raw_ostream &OS, key_type_ref, data_type_ref Data, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint32_t>(Data);
+  }
+};
+} // namespace
+
+void APINotesWriter::Implementation::writeObjCSelectorBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII Scope(Stream, OBJC_SELECTOR_BLOCK_ID, 3);
+
+  if (SelectorIDs.empty())
+    return;
+
+  {
+    llvm::SmallString<4096> HashTableBlob;
+    uint32_t Offset;
+    {
+      llvm::OnDiskChainedHashTableGenerator<ObjCSelectorTableInfo> Generator;
+      for (auto &S : SelectorIDs)
+        Generator.insert(S.first, S.second);
+
+      llvm::raw_svector_ostream BlobStream(HashTableBlob);
+      // Make sure that no bucket is at offset 0
+      llvm::support::endian::write<uint32_t>(BlobStream, 0,
+                                             llvm::support::little);
+      Offset = Generator.Emit(BlobStream);
+    }
+
+    objc_selector_block::ObjCSelectorDataLayout ObjCSelectorData(Stream);
+    ObjCSelectorData.emit(Scratch, Offset, HashTableBlob);
+  }
+}
+
+namespace {
+/// Used to serialize the on-disk global variable table.
+class GlobalVariableTableInfo
+    : public VersionedTableInfo<GlobalVariableTableInfo, unsigned,
+                                GlobalVariableInfo> {
+public:
+  unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); }
+
+  void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint32_t>(Key);
+  }
+
+  unsigned getUnversionedInfoSize(const GlobalVariableInfo &GVI) {
+    return getVariableInfoSize(GVI);
+  }
+
+  void emitUnversionedInfo(raw_ostream &OS, const GlobalVariableInfo &GVI) {
+    emitVariableInfo(OS, GVI);
+  }
+};
+} // namespace
+
+void APINotesWriter::Implementation::writeGlobalVariableBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII Scope(Stream, GLOBAL_VARIABLE_BLOCK_ID, 3);
+
+  if (GlobalVariables.empty())
+    return;
+
+  {
+    llvm::SmallString<4096> HashTableBlob;
+    uint32_t Offset;
+    {
+      llvm::OnDiskChainedHashTableGenerator<GlobalVariableTableInfo> Generator;
+      for (auto &GV : GlobalVariables)
+        Generator.insert(GV.first, GV.second);
+
+      llvm::raw_svector_ostream BlobStream(HashTableBlob);
+      // Make sure that no bucket is at offset 0
+      llvm::support::endian::write<uint32_t>(BlobStream, 0,
+                                             llvm::support::little);
+      Offset = Generator.Emit(BlobStream);
+    }
+
+    global_variable_block::GlobalVariableDataLayout GlobalVariableData(Stream);
+    GlobalVariableData.emit(Scratch, Offset, HashTableBlob);
+  }
+}
+
+namespace {
+unsigned getParamInfoSize(const ParamInfo &PI) {
+  return getVariableInfoSize(PI) + 1;
+}
+
+void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
+  emitVariableInfo(OS, PI);
+
+  uint8_t flags = 0;
+  if (auto noescape = PI.isNoEscape()) {
+    flags |= 0x01;
+    if (*noescape)
+      flags |= 0x02;
+  }
+  flags <<= 3;
+  if (auto RCC = PI.getRetainCountConvention())
+    flags |= static_cast<uint8_t>(RCC.value()) + 1;
+
+  llvm::support::endian::Writer writer(OS, llvm::support::little);
+  writer.write<uint8_t>(flags);
+}
+
+/// Retrieve the serialized size of the given FunctionInfo, for use in on-disk
+/// hash tables.
+unsigned getFunctionInfoSize(const FunctionInfo &FI) {
+  unsigned size = getCommonEntityInfoSize(FI) + 2 + sizeof(uint64_t);
+  size += sizeof(uint16_t);
+  for (const auto &P : FI.Params)
+    size += getParamInfoSize(P);
+  size += sizeof(uint16_t) + FI.ResultType.size();
+  return size;
+}
+
+/// Emit a serialized representation of the function information.
+static void emitFunctionInfo(raw_ostream &OS, const FunctionInfo &FI) {
+  emitCommonEntityInfo(OS, FI);
+
+  uint8_t flags = 0;
+  flags |= FI.NullabilityAudited;
+  flags <<= 3;
+  if (auto RCC = FI.getRetainCountConvention())
+    flags |= static_cast<uint8_t>(RCC.value()) + 1;
+
+  llvm::support::endian::Writer writer(OS, llvm::support::little);
+
+  writer.write<uint8_t>(flags);
+  writer.write<uint8_t>(FI.NumAdjustedNullable);
+  writer.write<uint64_t>(FI.NullabilityPayload);
+
+  writer.write<uint16_t>(FI.Params.size());
+  for (const auto &PI : FI.Params)
+    emitParamInfo(OS, PI);
+
+  writer.write<uint16_t>(FI.ResultType.size());
+  writer.write(ArrayRef<char>{FI.ResultType.data(), FI.ResultType.size()});
+}
+
+/// Used to serialize the on-disk global function table.
+class GlobalFunctionTableInfo
+    : public VersionedTableInfo<GlobalFunctionTableInfo, unsigned,
+                                GlobalFunctionInfo> {
+public:
+  unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); }
+
+  void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint32_t>(Key);
+  }
+
+  unsigned getUnversionedInfoSize(const GlobalFunctionInfo &GFI) {
+    return getFunctionInfoSize(GFI);
+  }
+
+  void emitUnversionedInfo(raw_ostream &OS, const GlobalFunctionInfo &GFI) {
+    emitFunctionInfo(OS, GFI);
+  }
+};
+} // namespace
+
+void APINotesWriter::Implementation::writeGlobalFunctionBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII Scope(Stream, GLOBAL_FUNCTION_BLOCK_ID, 3);
+
+  if (GlobalFunctions.empty())
+    return;
+
+  {
+    llvm::SmallString<4096> HashTableBlob;
+    uint32_t Offset;
+    {
+      llvm::OnDiskChainedHashTableGenerator<GlobalFunctionTableInfo> Generator;
+      for (auto &F : GlobalFunctions)
+        Generator.insert(F.first, F.second);
+
+      llvm::raw_svector_ostream BlobStream(HashTableBlob);
+      // Make sure that no bucket is at offset 0
+      llvm::support::endian::write<uint32_t>(BlobStream, 0,
+                                             llvm::support::little);
+      Offset = Generator.Emit(BlobStream);
+    }
+
+    global_function_block::GlobalFunctionDataLayout GlobalFunctionData(Stream);
+    GlobalFunctionData.emit(Scratch, Offset, HashTableBlob);
+  }
+}
+
+namespace {
+/// Used to serialize the on-disk global enum constant.
+class EnumConstantTableInfo
+    : public VersionedTableInfo<EnumConstantTableInfo, unsigned,
+                                EnumConstantInfo> {
+public:
+  unsigned getKeyLength(key_type_ref) { return sizeof(uint32_t); }
+
+  void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<uint32_t>(Key);
+  }
+
+  unsigned getUnversionedInfoSize(const EnumConstantInfo &ECI) {
+    return getCommonEntityInfoSize(ECI);
+  }
+
+  void emitUnversionedInfo(raw_ostream &OS, const EnumConstantInfo &ECI) {
+    emitCommonEntityInfo(OS, ECI);
+  }
+};
+} // namespace
+
+void APINotesWriter::Implementation::writeEnumConstantBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII Scope(Stream, ENUM_CONSTANT_BLOCK_ID, 3);
+
+  if (EnumConstants.empty())
+    return;
+
+  {
+    llvm::SmallString<4096> HashTableBlob;
+    uint32_t Offset;
+    {
+      llvm::OnDiskChainedHashTableGenerator<EnumConstantTableInfo> Generator;
+      for (auto &EC : EnumConstants)
+        Generator.insert(EC.first, EC.second);
+
+      llvm::raw_svector_ostream BlobStream(HashTableBlob);
+      // Make sure that no bucket is at offset 0
+      llvm::support::endian::write<uint32_t>(BlobStream, 0,
+                                             llvm::support::little);
+      Offset = Generator.Emit(BlobStream);
+    }
+
+    enum_constant_block::EnumConstantDataLayout EnumConstantData(Stream);
+    EnumConstantData.emit(Scratch, Offset, HashTableBlob);
+  }
+}
+
+namespace {
+template <typename Derived, typename UnversionedDataType>
+class CommonTypeTableInfo
+    : public VersionedTableInfo<Derived, unsigned, UnversionedDataType> {
+public:
+  using key_type_ref = typename CommonTypeTableInfo::key_type_ref;
+
+  unsigned getKeyLength(key_type_ref) { return sizeof(IdentifierID); }
+
+  void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+    writer.write<IdentifierID>(Key);
+  }
+
+  unsigned getUnversionedInfoSize(const UnversionedDataType &UDT) {
+    return getCommonTypeInfoSize(UDT);
+  }
+
+  void emitUnversionedInfo(raw_ostream &OS, const UnversionedDataType &UDT) {
+    emitCommonTypeInfo(OS, UDT);
+  }
+};
+
+/// Used to serialize the on-disk tag table.
+class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
+public:
+  unsigned getUnversionedInfoSize(const TagInfo &TI) {
+    return 1 + getCommonTypeInfoSize(TI);
+  }
+
+  void emitUnversionedInfo(raw_ostream &OS, const TagInfo &TI) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+
+    uint8_t Flags = 0;
+    if (auto extensibility = TI.EnumExtensibility) {
+      Flags |= static_cast<uint8_t>(extensibility.value()) + 1;
+      assert((Flags < (1 << 2)) && "must fit in two bits");
+    }
+
+    Flags <<= 2;
+    if (auto value = TI.isFlagEnum())
+      Flags |= (value.value() << 1 | 1 << 0);
+
+    writer.write<uint8_t>(Flags);
+
+    emitCommonTypeInfo(OS, TI);
+  }
+};
+} // namespace
+
+void APINotesWriter::Implementation::writeTagBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII Scope(Stream, TAG_BLOCK_ID, 3);
+
+  if (Tags.empty())
+    return;
+
+  {
+    llvm::SmallString<4096> HashTableBlob;
+    uint32_t Offset;
+    {
+      llvm::OnDiskChainedHashTableGenerator<TagTableInfo> Generator;
+      for (auto &T : Tags)
+        Generator.insert(T.first, T.second);
+
+      llvm::raw_svector_ostream BlobStream(HashTableBlob);
+      // Make sure that no bucket is at offset 0
+      llvm::support::endian::write<uint32_t>(BlobStream, 0,
+                                             llvm::support::little);
+      Offset = Generator.Emit(BlobStream);
+    }
+
+    tag_block::TagDataLayout TagData(Stream);
+    TagData.emit(Scratch, Offset, HashTableBlob);
+  }
+}
+
+namespace {
+/// Used to serialize the on-disk typedef table.
+class TypedefTableInfo
+    : public CommonTypeTableInfo<TypedefTableInfo, TypedefInfo> {
+public:
+  unsigned getUnversionedInfoSize(const TypedefInfo &TI) {
+    return 1 + getCommonTypeInfoSize(TI);
+  }
+
+  void emitUnversionedInfo(raw_ostream &OS, const TypedefInfo &TI) {
+    llvm::support::endian::Writer writer(OS, llvm::support::little);
+
+    uint8_t Flags = 0;
+    if (auto swiftWrapper = TI.SwiftWrapper)
+      Flags |= static_cast<uint8_t>(*swiftWrapper) + 1;
+
+    writer.write<uint8_t>(Flags);
+
+    emitCommonTypeInfo(OS, TI);
+  }
+};
+} // namespace
+
+void APINotesWriter::Implementation::writeTypedefBlock(
+    llvm::BitstreamWriter &Stream) {
+  llvm::BCBlockRAII Scope(Stream, TYPEDEF_BLOCK_ID, 3);
+
+  if (Typedefs.empty())
+    return;
+
+  {
+    llvm::SmallString<4096> HashTableBlob;
+    uint32_t Offset;
+    {
+      llvm::OnDiskChainedHashTableGenerator<TypedefTableInfo> Generator;
+      for (auto &T : Typedefs)
+        Generator.insert(T.first, T.second);
+
+      llvm::raw_svector_ostream BlobStream(HashTableBlob);
+      // Make sure that no bucket is at offset 0
+      llvm::support::endian::write<uint32_t>(BlobStream, 0,
+                                             llvm::support::little);
+      Offset = Generator.Emit(BlobStream);
+    }
+
+    typedef_block::TypedefDataLayout TypedefData(Stream);
+    TypedefData.emit(Scratch, Offset, HashTableBlob);
+  }
+}
+
+// APINotesWriter
+
+APINotesWriter::APINotesWriter(llvm::StringRef ModuleName, const FileEntry *SF)
+    : Implementation(new class Implementation(ModuleName, SF)) {}
+
+APINotesWriter::~APINotesWriter() = default;
+
+void APINotesWriter::writeToStream(llvm::raw_ostream &OS) {
+  Implementation->writeToStream(OS);
+}
+} // namespace api_notes
+} // namespace clang
Index: clang/lib/APINotes/APINotesFormat.h
===================================================================
--- clang/lib/APINotes/APINotesFormat.h
+++ clang/lib/APINotes/APINotesFormat.h
@@ -252,4 +252,36 @@
 } // namespace api_notes
 } // namespace clang
 
+namespace llvm {
+template <> struct DenseMapInfo<clang::api_notes::StoredObjCSelector> {
+  typedef DenseMapInfo<unsigned> UnsignedInfo;
+
+  static inline clang::api_notes::StoredObjCSelector getEmptyKey() {
+    return clang::api_notes::StoredObjCSelector{UnsignedInfo::getEmptyKey(),
+                                                {}};
+  }
+
+  static inline clang::api_notes::StoredObjCSelector getTombstoneKey() {
+    return clang::api_notes::StoredObjCSelector{UnsignedInfo::getTombstoneKey(),
+                                                {}};
+  }
+
+  static unsigned
+  getHashValue(const clang::api_notes::StoredObjCSelector &Selector) {
+    auto hash = llvm::hash_value(Selector.NumPieces);
+    hash = hash_combine(hash, Selector.Identifiers.size());
+    for (auto piece : Selector.Identifiers)
+      hash = hash_combine(hash, static_cast<unsigned>(piece));
+    // FIXME: Mix upper/lower 32-bit values together to produce
+    // unsigned rather than truncating.
+    return hash;
+  }
+
+  static bool isEqual(const clang::api_notes::StoredObjCSelector &LHS,
+                      const clang::api_notes::StoredObjCSelector &RHS) {
+    return LHS.NumPieces == RHS.NumPieces && LHS.Identifiers == RHS.Identifiers;
+  }
+};
+} // namespace llvm
+
 #endif
Index: clang/include/clang/APINotes/APINotesWriter.h
===================================================================
--- /dev/null
+++ clang/include/clang/APINotes/APINotesWriter.h
@@ -0,0 +1,37 @@
+//===-- APINotesWriter.h - API Notes Writer ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_APINOTES_WRITER_H
+#define LLVM_CLANG_APINOTES_WRITER_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <memory>
+
+namespace clang {
+class FileEntry;
+
+namespace api_notes {
+class APINotesWriter {
+  class Implementation;
+  std::unique_ptr<Implementation> Implementation;
+
+public:
+  APINotesWriter(llvm::StringRef ModuleName, const FileEntry *SF);
+  ~APINotesWriter();
+
+  APINotesWriter(const APINotesWriter &) = delete;
+  APINotesWriter &operator=(const APINotesWriter &) = delete;
+
+  void writeToStream(llvm::raw_ostream &OS);
+};
+} // namespace api_notes
+} // namespace clang
+
+#endif
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D92797: APINote... Saleem Abdulrasool via Phabricator via cfe-commits

Reply via email to