juliehockett updated this revision to Diff 135520.
juliehockett added a comment.

Updating for parent diff changes


https://reviews.llvm.org/D43341

Files:
  clang-doc/BitcodeReader.cpp
  clang-doc/BitcodeReader.h
  clang-doc/BitcodeWriter.cpp
  clang-doc/BitcodeWriter.h
  clang-doc/CMakeLists.txt
  clang-doc/Reducer.cpp
  clang-doc/Reducer.h
  clang-doc/Representation.cpp
  clang-doc/Representation.h
  clang-doc/tool/ClangDocMain.cpp
  test/clang-doc/mapper-class.cpp
  test/clang-doc/mapper-enum.cpp
  test/clang-doc/mapper-function.cpp
  test/clang-doc/mapper-method.cpp
  test/clang-doc/mapper-namespace.cpp
  test/clang-doc/mapper-struct.cpp
  test/clang-doc/mapper-union.cpp
  test/clang-doc/namespace.cpp
  test/clang-doc/record.cpp

Index: test/clang-doc/record.cpp
===================================================================
--- /dev/null
+++ test/clang-doc/record.cpp
@@ -0,0 +1,107 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
+// RUN: llvm-bcanalyzer %t/docs/bc/docs.bc --dump | FileCheck %s
+
+union A { int X; int Y; };
+
+enum B { X, Y };
+
+struct C { int i; };
+
+class D {};
+
+class E {
+public:
+  E() {}
+  ~E() {}
+
+protected:
+  void ProtectedMethod();
+};
+
+void E::ProtectedMethod() {}
+
+class F : virtual private D, public E {};
+
+// CHECK: <BLOCKINFO_BLOCK/>
+// CHECK: <VersionBlock NumWords=1 BlockCodeSize=5>
+  // CHECK: <Version abbrevid=4 op0=1/>
+// CHECK: </VersionBlock>
+// CHECK: <FunctionBlock NumWords=12 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'E'
+  // CHECK: <IsDefinition abbrevid=8 op0=1/>
+  // CHECK: <Parent abbrevid=6 op0=6 (offset mismatch: 0 vs 7509)/> blob data = 'c:@S@E'
+  // CHECK: <TypeBlock NumWords=3 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=4/> blob data = 'void'
+  // CHECK: </TypeBlock>
+// CHECK: </FunctionBlock>
+// CHECK: <FunctionBlock NumWords=12 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=2/> blob data = '~E'
+  // CHECK: <IsDefinition abbrevid=8 op0=1/>
+  // CHECK: <Parent abbrevid=6 op0=6 (offset mismatch: 0 vs 7957)/> blob data = 'c:@S@E'
+  // CHECK: <TypeBlock NumWords=3 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=4/> blob data = 'void'
+  // CHECK: </TypeBlock>
+// CHECK: </FunctionBlock>
+// CHECK: <FunctionBlock NumWords=15 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=15/> blob data = 'ProtectedMethod'
+  // CHECK: <IsDefinition abbrevid=8 op0=1/>
+  // CHECK: <Parent abbrevid=6 op0=6 (offset mismatch: 0 vs 8501)/> blob data = 'c:@S@E'
+  // CHECK: <TypeBlock NumWords=3 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=4/> blob data = 'void'
+  // CHECK: </TypeBlock>
+// CHECK: </FunctionBlock>
+// CHECK: <RecordBlock NumWords=18 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'A'
+  // CHECK: <IsDefinition abbrevid=9 op0=1/>
+  // CHECK: <TagType abbrevid=10 op0=2/>
+  // CHECK: <MemberTypeBlock NumWords=5 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=3/> blob data = 'int'
+    // CHECK: <Name abbrevid=5 op0=4/> blob data = 'A::X'
+    // CHECK: <Access abbrevid=6 op0=3/>
+  // CHECK: </MemberTypeBlock>
+  // CHECK: <MemberTypeBlock NumWords=5 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=3/> blob data = 'int'
+    // CHECK: <Name abbrevid=5 op0=4/> blob data = 'A::Y'
+    // CHECK: <Access abbrevid=6 op0=3/>
+  // CHECK: </MemberTypeBlock>
+// CHECK: </RecordBlock>
+// CHECK: <RecordBlock NumWords=11 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'C'
+  // CHECK: <IsDefinition abbrevid=9 op0=1/>
+  // CHECK: <MemberTypeBlock NumWords=5 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=3/> blob data = 'int'
+    // CHECK: <Name abbrevid=5 op0=4/> blob data = 'C::i'
+    // CHECK: <Access abbrevid=6 op0=3/>
+  // CHECK: </MemberTypeBlock>
+// CHECK: </RecordBlock>
+// CHECK: <RecordBlock NumWords=4 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'D'
+  // CHECK: <IsDefinition abbrevid=9 op0=1/>
+  // CHECK: <TagType abbrevid=10 op0=3/>
+// CHECK: </RecordBlock>
+// CHECK: <RecordBlock NumWords=4 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'E'
+  // CHECK: <IsDefinition abbrevid=9 op0=1/>
+  // CHECK: <TagType abbrevid=10 op0=3/>
+// CHECK: </RecordBlock>
+// CHECK: <RecordBlock NumWords=11 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'F'
+  // CHECK: <IsDefinition abbrevid=9 op0=1/>
+  // CHECK: <TagType abbrevid=10 op0=3/>
+  // CHECK: <Parent abbrevid=6 op0=6/> blob data = 'c:@S@E'
+  // CHECK: <VParent abbrevid=7 op0=6/> blob data = 'c:@S@D'
+// CHECK: </RecordBlock>
+// CHECK: <EnumBlock NumWords=14 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'B'
+  // CHECK: <IsDefinition abbrevid=7 op0=1/>
+  // CHECK: <TypeBlock NumWords=3 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=1/> blob data = 'X'
+  // CHECK: </TypeBlock>
+  // CHECK: <TypeBlock NumWords=3 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=1/> blob data = 'Y'
+  // CHECK: </TypeBlock>
+// CHECK: </EnumBlock>
Index: test/clang-doc/namespace.cpp
===================================================================
--- /dev/null
+++ test/clang-doc/namespace.cpp
@@ -0,0 +1,67 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
+// RUN: llvm-bcanalyzer %t/docs/bc/docs.bc --dump | FileCheck %s
+
+namespace A {
+
+void f();
+void f() {};
+
+} // A
+
+namespace A {
+namespace B {
+
+enum E { X };
+
+E func(int i) { 
+  return X;
+}
+
+}
+}
+
+// CHECK: <BLOCKINFO_BLOCK/>
+// CHECK: <VersionBlock NumWords=1 BlockCodeSize=5>
+  // CHECK: <Version abbrevid=4 op0=1/>
+// CHECK: </VersionBlock>
+// CHECK: <NamespaceBlock NumWords=3 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'A'
+// CHECK: </NamespaceBlock>
+// CHECK: <NamespaceBlock NumWords=6 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'B'
+  // CHECK: <Namespace abbrevid=5 op0=6/> blob data = 'c:@N@A'
+// CHECK: </NamespaceBlock>
+// CHECK: <FunctionBlock NumWords=12 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'f'
+  // CHECK: <Namespace abbrevid=5 op0=6/> blob data = 'c:@N@A'
+  // CHECK: <IsDefinition abbrevid=8 op0=1/>
+  // CHECK: <TypeBlock NumWords=3 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=4/> blob data = 'void'
+  // CHECK: </TypeBlock>
+// CHECK: </FunctionBlock>
+// CHECK: <FunctionBlock NumWords=25 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=4/> blob data = 'func'
+  // CHECK: <Namespace abbrevid=5 op0=10/> blob data = 'c:@N@A@N@B'
+  // CHECK: <Namespace abbrevid=5 op0=6/> blob data = 'c:@N@A'
+  // CHECK: <IsDefinition abbrevid=8 op0=1/>
+  // CHECK: <TypeBlock NumWords=5 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=12/> blob data = 'enum A::B::E'
+  // CHECK: </TypeBlock>
+  // CHECK: <FieldTypeBlock NumWords=5 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=3/> blob data = 'int'
+    // CHECK: <Name abbrevid=5 op0=1/> blob data = 'i'
+  // CHECK: </FieldTypeBlock>
+// CHECK: </FunctionBlock>
+// CHECK: <EnumBlock NumWords=17 BlockCodeSize=5>
+  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'E'
+  // CHECK: <Namespace abbrevid=5 op0=10/> blob data = 'c:@N@A@N@B'
+  // CHECK: <Namespace abbrevid=5 op0=6/> blob data = 'c:@N@A'
+  // CHECK: <IsDefinition abbrevid=7 op0=1/>
+  // CHECK: <TypeBlock NumWords=4 BlockCodeSize=5>
+    // CHECK: <Type abbrevid=4 op0=7/> blob data = 'A::B::X'
+  // CHECK: </TypeBlock>
+// CHECK: </EnumBlock>
Index: test/clang-doc/mapper-union.cpp
===================================================================
--- test/clang-doc/mapper-union.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// RUN: rm -rf %t
-// RUN: mkdir %t
-// RUN: echo "" > %t/compile_flags.txt
-// RUN: cp "%s" "%t/test.cpp"
-// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/c:@u...@d.bc --dump | FileCheck %s
-
-union D { int X; int Y; };
-// CHECK: <BLOCKINFO_BLOCK/>
-// CHECK: <VersionBlock NumWords=1 BlockCodeSize=5>
-  // CHECK: <Version abbrevid=4 op0=1/>
-// CHECK: </VersionBlock>
-// CHECK: <RecordBlock NumWords=18 BlockCodeSize=5>
-  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'D'
-  // CHECK: <IsDefinition abbrevid=9 op0=1/>
-  // CHECK: <TagType abbrevid=10 op0=2/>
-  // CHECK: <MemberTypeBlock NumWords=5 BlockCodeSize=5>
-    // CHECK: <Type abbrevid=4 op0=3/> blob data = 'int'
-    // CHECK: <Name abbrevid=5 op0=4/> blob data = 'D::X'
-    // CHECK: <Access abbrevid=6 op0=3/>
-  // CHECK: </MemberTypeBlock>
-  // CHECK: <MemberTypeBlock NumWords=5 BlockCodeSize=5>
-    // CHECK: <Type abbrevid=4 op0=3/> blob data = 'int'
-    // CHECK: <Name abbrevid=5 op0=4/> blob data = 'D::Y'
-    // CHECK: <Access abbrevid=6 op0=3/>
-  // CHECK: </MemberTypeBlock>
-// CHECK: </RecordBlock>
-
-
Index: test/clang-doc/mapper-struct.cpp
===================================================================
--- test/clang-doc/mapper-struct.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-// RUN: rm -rf %t
-// RUN: mkdir %t
-// RUN: echo "" > %t/compile_flags.txt
-// RUN: cp "%s" "%t/test.cpp"
-// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/c:@s...@c.bc --dump | FileCheck %s
-
-struct C { int i; };
-// CHECK: <BLOCKINFO_BLOCK/>
-// CHECK: <VersionBlock NumWords=1 BlockCodeSize=5>
-  // CHECK: <Version abbrevid=4 op0=1/>
-// CHECK: </VersionBlock>
-// CHECK: <RecordBlock NumWords=11 BlockCodeSize=5>
-  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'C'
-  // CHECK: <IsDefinition abbrevid=9 op0=1/>
-  // CHECK: <MemberTypeBlock NumWords=5 BlockCodeSize=5>
-    // CHECK: <Type abbrevid=4 op0=3/> blob data = 'int'
-    // CHECK: <Name abbrevid=5 op0=4/> blob data = 'C::i'
-    // CHECK: <Access abbrevid=6 op0=3/>
-  // CHECK: </MemberTypeBlock>
-// CHECK: </RecordBlock>
-
-
Index: test/clang-doc/mapper-namespace.cpp
===================================================================
--- test/clang-doc/mapper-namespace.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-// RUN: rm -rf %t
-// RUN: mkdir %t
-// RUN: echo "" > %t/compile_flags.txt
-// RUN: cp "%s" "%t/test.cpp"
-// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/c:@n...@a.bc --dump | FileCheck %s
-
-namespace A {}
-// CHECK: <BLOCKINFO_BLOCK/>
-// CHECK: <VersionBlock NumWords=1 BlockCodeSize=5>
-  // CHECK: <Version abbrevid=4 op0=1/>
-// CHECK: </VersionBlock>
-// CHECK: <NamespaceBlock NumWords=3 BlockCodeSize=5>
-  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'A'
-// CHECK: </NamespaceBlock>
-
-
Index: test/clang-doc/mapper-method.cpp
===================================================================
--- test/clang-doc/mapper-method.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// RUN: rm -rf %t
-// RUN: mkdir %t
-// RUN: echo "" > %t/compile_flags.txt
-// RUN: cp "%s" "%t/test.cpp"
-// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/c:@S@G@F@Method#I#.bc --dump | FileCheck %s
-
-class G {
-public: 
-	int Method(int param) { return param; }
-};
-// CHECK: <BLOCKINFO_BLOCK/>
-// CHECK: <VersionBlock NumWords=1 BlockCodeSize=5>
-  // CHECK: <Version abbrevid=4 op0=1/>
-// CHECK: </VersionBlock>
-// CHECK: <FunctionBlock NumWords=21 BlockCodeSize=5>
-  // CHECK: <Name abbrevid=4 op0=6/> blob data = 'Method'
-  // CHECK: <IsDefinition abbrevid=8 op0=1/>
-  // CHECK: <Parent abbrevid=6 op0=6 (offset mismatch: 0 vs 7541)/> blob data = 'c:@S@G'
-  // CHECK: <TypeBlock NumWords=3 BlockCodeSize=5>
-    // CHECK: <Type abbrevid=4 op0=3/> blob data = 'int'
-  // CHECK: </TypeBlock>
-  // CHECK: <FieldTypeBlock NumWords=6 BlockCodeSize=5>
-    // CHECK: <Type abbrevid=4 op0=3/> blob data = 'int'
-    // CHECK: <Name abbrevid=5 op0=5/> blob data = 'param'
-  // CHECK: </FieldTypeBlock>
-// CHECK: </FunctionBlock>
-
-
-
-
Index: test/clang-doc/mapper-function.cpp
===================================================================
--- test/clang-doc/mapper-function.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// RUN: rm -rf %t
-// RUN: mkdir %t
-// RUN: echo "" > %t/compile_flags.txt
-// RUN: cp "%s" "%t/test.cpp"
-// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/c:@F@F#I#.bc --dump | FileCheck %s
-
-int F(int param) { return param; }
-// CHECK: <BLOCKINFO_BLOCK/>
-// CHECK: <VersionBlock NumWords=1 BlockCodeSize=5>
-  // CHECK: <Version abbrevid=4 op0=1/>
-// CHECK: </VersionBlock>
-// CHECK: <FunctionBlock NumWords=17 BlockCodeSize=5>
-  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'F'
-  // CHECK: <IsDefinition abbrevid=8 op0=1/>
-  // CHECK: <TypeBlock NumWords=3 BlockCodeSize=5>
-    // CHECK: <Type abbrevid=4 op0=3/> blob data = 'int'
-  // CHECK: </TypeBlock>
-  // CHECK: <FieldTypeBlock NumWords=6 BlockCodeSize=5>
-    // CHECK: <Type abbrevid=4 op0=3/> blob data = 'int'
-    // CHECK: <Name abbrevid=5 op0=5/> blob data = 'param'
-  // CHECK: </FieldTypeBlock>
-// CHECK: </FunctionBlock>
-
Index: test/clang-doc/mapper-enum.cpp
===================================================================
--- test/clang-doc/mapper-enum.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// RUN: rm -rf %t
-// RUN: mkdir %t
-// RUN: echo "" > %t/compile_flags.txt
-// RUN: cp "%s" "%t/test.cpp"
-// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/c:@e...@b.bc --dump | FileCheck %s
-
-enum B { X, Y };
-// CHECK: <BLOCKINFO_BLOCK/>
-// CHECK: <VersionBlock NumWords=1 BlockCodeSize=5>
-  // CHECK: <Version abbrevid=4 op0=1/>
-// CHECK: </VersionBlock>
-// CHECK: <EnumBlock NumWords=14 BlockCodeSize=5>
-  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'B'
-  // CHECK: <IsDefinition abbrevid=7 op0=1/>
-  // CHECK: <TypeBlock NumWords=3 BlockCodeSize=5>
-    // CHECK: <Type abbrevid=4 op0=1/> blob data = 'X'
-  // CHECK: </TypeBlock>
-  // CHECK: <TypeBlock NumWords=3 BlockCodeSize=5>
-    // CHECK: <Type abbrevid=4 op0=1/> blob data = 'Y'
-  // CHECK: </TypeBlock>
-// CHECK: </EnumBlock>
-
-
-
Index: test/clang-doc/mapper-class.cpp
===================================================================
--- test/clang-doc/mapper-class.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-// RUN: rm -rf %t
-// RUN: mkdir %t
-// RUN: echo "" > %t/compile_flags.txt
-// RUN: cp "%s" "%t/test.cpp"
-// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/c:@s...@e.bc --dump | FileCheck %s
-
-class E {};
-// CHECK: <BLOCKINFO_BLOCK/>
-// CHECK: <VersionBlock NumWords=1 BlockCodeSize=5>
-  // CHECK: <Version abbrevid=4 op0=1/>
-// CHECK: </VersionBlock>
-// CHECK: <RecordBlock NumWords=4 BlockCodeSize=5>
-  // CHECK: <Name abbrevid=4 op0=1/> blob data = 'E'
-  // CHECK: <IsDefinition abbrevid=9 op0=1/>
-  // CHECK: <TagType abbrevid=10 op0=3/>
-// CHECK: </RecordBlock>
-
Index: clang-doc/tool/ClangDocMain.cpp
===================================================================
--- clang-doc/tool/ClangDocMain.cpp
+++ clang-doc/tool/ClangDocMain.cpp
@@ -13,7 +13,9 @@
 //===----------------------------------------------------------------------===//
 
 #include <string>
+#include "BitcodeWriter.h"
 #include "ClangDoc.h"
+#include "Reducer.h"
 #include "clang/AST/AST.h"
 #include "clang/AST/Decl.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -86,26 +88,32 @@
       ArgAdjuster);
   if (Err) errs() << toString(std::move(Err)) << "\n";
 
+  // Reducing phase
+  outs() << "Reducing infos...\n";
+  auto Infos = doc::mergeInfos(Exec->get()->getToolResults());
+
   if (DumpResult) {
-    Exec->get()->getToolResults()->forEachResult([&](StringRef Key,
-                                                     StringRef Value) {
-      SmallString<128> IRRootPath;
-      sys::path::native(OutDirectory, IRRootPath);
-      std::error_code DirectoryStatus = sys::fs::create_directories(IRRootPath);
-      if (DirectoryStatus != OK) {
-        errs() << "Unable to create documentation directories.\n";
-        return;
-      }
-      sys::path::append(IRRootPath, Key + ".bc");
-      std::error_code OutErrorInfo;
-      raw_fd_ostream OS(IRRootPath, OutErrorInfo, sys::fs::F_None);
-      if (OutErrorInfo != OK) {
-        errs() << "Error opening documentation file.\n";
-        return;
-      }
-      OS << Value;
-      OS.close();
-    });
+    SmallString<2048> Buffer;
+    llvm::BitstreamWriter Stream(Buffer);
+    doc::ClangDocBitcodeWriter Writer(Stream);
+    Writer.writeBitstreamForInfoSet(*Infos);
+    SmallString<128> IRRootPath;
+    sys::path::native(OutDirectory, IRRootPath);
+    sys::path::append(IRRootPath, "bc");
+    std::error_code DirectoryStatus = sys::fs::create_directories(IRRootPath);
+    if (DirectoryStatus != OK) {
+      errs() << "Unable to create documentation directories.\n";
+      return 1;
+    }
+    sys::path::append(IRRootPath, "docs.bc");
+    std::error_code OutErrorInfo;
+    raw_fd_ostream OS(IRRootPath, OutErrorInfo, sys::fs::F_None);
+    if (OutErrorInfo != OK) {
+      errs() << "Error opening documentation file.\n";
+      return 1;
+    }
+    OS << Buffer;
+    OS.close();
   }
 
   return 0;
Index: clang-doc/Representation.h
===================================================================
--- clang-doc/Representation.h
+++ clang-doc/Representation.h
@@ -123,6 +123,59 @@
 
 // TODO: Add functionality to include separate markdown pages.
 
+class InfoSet {
+ public:
+  InfoSet() {}
+  InfoSet &operator=(const InfoSet &) = delete;
+  InfoSet &&operator=(const InfoSet &&) = delete;
+
+  void insert(StringRef Key, std::unique_ptr<NamespaceInfo> &&I);
+  void insert(StringRef Key, std::unique_ptr<RecordInfo> &&I);
+  void insert(StringRef Key, std::unique_ptr<EnumInfo> &&I);
+  void insert(StringRef Key, std::unique_ptr<FunctionInfo> &&I);
+
+  // Returns the info with a Key, if it exists. Valid until next insert().
+  template <typename T>
+  std::unique_ptr<T>& find(StringRef Key);
+
+  const std::vector<std::unique_ptr<NamespaceInfo>> &getNamespaceInfos() const {
+    return NamespaceInfos;
+  }
+  const std::vector<std::unique_ptr<FunctionInfo>> &getFunctionInfos() const {
+    return FunctionInfos;
+  }
+  const std::vector<std::unique_ptr<RecordInfo>> &getRecordInfos() const {
+    return RecordInfos;
+  }
+  const std::vector<std::unique_ptr<EnumInfo>> &getEnumInfos() const {
+    return EnumInfos;
+  }
+
+ private:
+  // Merge symbols L and R, preferring data from L in case of conflict.
+  void mergeInfo(std::unique_ptr<NamespaceInfo> &L,
+                 std::unique_ptr<NamespaceInfo> &R);
+  void mergeInfo(std::unique_ptr<RecordInfo> &L,
+                 std::unique_ptr<RecordInfo> &R);
+  void mergeInfo(std::unique_ptr<EnumInfo> &L, std::unique_ptr<EnumInfo> &R);
+  void mergeInfo(std::unique_ptr<FunctionInfo> &L,
+                 std::unique_ptr<FunctionInfo> &R);
+  void mergeInfo(std::unique_ptr<TypeInfo> &L, std::unique_ptr<TypeInfo> &R);
+  void mergeInfo(std::unique_ptr<FieldTypeInfo> &L,
+                 std::unique_ptr<FieldTypeInfo> &R);
+  void mergeInfo(std::unique_ptr<MemberTypeInfo> &L,
+                 std::unique_ptr<MemberTypeInfo> &R);
+
+  void mergeInfoBase(Info *L, Info *R);
+  void mergeSymbolInfoBase(SymbolInfo *L, SymbolInfo *R);
+
+  std::vector<std::unique_ptr<NamespaceInfo>> NamespaceInfos;
+  std::vector<std::unique_ptr<FunctionInfo>> FunctionInfos;
+  std::vector<std::unique_ptr<RecordInfo>> RecordInfos;
+  std::vector<std::unique_ptr<EnumInfo>> EnumInfos;
+  llvm::DenseMap<StringRef, size_t> InfoIndex;
+};
+
 }  // namespace doc
 }  // namespace clang
 
Index: clang-doc/Representation.cpp
===================================================================
--- /dev/null
+++ clang-doc/Representation.cpp
@@ -0,0 +1,159 @@
+///===-- ClangDocRepresentation.cpp - ClangDocRepresenation -----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Representation.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+void InfoSet::mergeInfoBase(Info *L, Info *R) {
+  if (L->Name.empty()) L->Name = R->Name;
+  if (L->Namespace.empty()) L->Namespace = std::move(R->Namespace);
+  std::move(R->Description.begin(), R->Description.end(),
+            std::back_inserter(L->Description));
+}
+
+void InfoSet::mergeSymbolInfoBase(SymbolInfo *L, SymbolInfo *R) {
+  mergeInfoBase(L, R);
+  L->Loc.insert(L->Loc.end(), R->Loc.begin(), R->Loc.end());
+}
+
+void InfoSet::mergeInfo(std::unique_ptr<TypeInfo> &L,
+                        std::unique_ptr<TypeInfo> &R) {
+  if (L->TypeUSR.empty()) L->TypeUSR = R->TypeUSR;
+  std::move(R->Description.begin(), R->Description.end(),
+            std::back_inserter(L->Description));
+}
+
+void InfoSet::mergeInfo(std::unique_ptr<FieldTypeInfo> &L,
+                        std::unique_ptr<FieldTypeInfo> &R) {
+  if (L->TypeUSR.empty()) L->TypeUSR = R->TypeUSR;
+  if (L->Name.empty()) L->Name = R->Name;
+  std::move(R->Description.begin(), R->Description.end(),
+            std::back_inserter(L->Description));
+}
+
+void InfoSet::mergeInfo(std::unique_ptr<MemberTypeInfo> &L,
+                        std::unique_ptr<MemberTypeInfo> &R) {
+  if (L->TypeUSR.empty()) L->TypeUSR = R->TypeUSR;
+  if (L->Name.empty()) L->Name = R->Name;
+  if (!L->Access) L->Access = R->Access;
+  std::move(R->Description.begin(), R->Description.end(),
+            std::back_inserter(L->Description));
+}
+
+void InfoSet::mergeInfo(std::unique_ptr<NamespaceInfo> &L,
+                        std::unique_ptr<NamespaceInfo> &R) {
+  mergeInfoBase(L.get(), R.get());
+}
+
+void InfoSet::mergeInfo(std::unique_ptr<RecordInfo> &L,
+                        std::unique_ptr<RecordInfo> &R) {
+  mergeSymbolInfoBase(L.get(), R.get());
+  if (!L->TagType) L->TagType = R->TagType;
+  if (L->Members.empty())
+    L->Members = std::move(R->Members);
+  else if (L->Members.size() == R->Members.size()) {
+    for (unsigned i = 0; i < L->Members.size(); ++i)
+      mergeInfo(L->Members[i], R->Members[i]);
+  }
+  if (L->ParentUSRs.empty()) L->ParentUSRs = std::move(R->ParentUSRs);
+  if (L->VirtualParentUSRs.empty())
+    L->VirtualParentUSRs = std::move(R->VirtualParentUSRs);
+}
+
+void InfoSet::mergeInfo(std::unique_ptr<EnumInfo> &L,
+                        std::unique_ptr<EnumInfo> &R) {
+  mergeSymbolInfoBase(L.get(), R.get());
+  if (!L->Scoped) L->Scoped = R->Scoped;
+  if (L->Members.empty())
+    L->Members = std::move(R->Members);
+  else if (L->Members.size() == R->Members.size()) {
+    for (unsigned i = 0; i < L->Members.size(); ++i)
+      mergeInfo(L->Members[i], R->Members[i]);
+  }
+}
+
+void InfoSet::mergeInfo(std::unique_ptr<FunctionInfo> &L,
+                        std::unique_ptr<FunctionInfo> &R) {
+  mergeSymbolInfoBase(L.get(), R.get());
+  if (L->ParentUSR.empty()) L->ParentUSR = R->ParentUSR;
+  if (!L->Access) L->Access = R->Access;
+  mergeInfo(L->ReturnType, R->ReturnType);
+  if (L->Params.empty())
+    L->Params = std::move(R->Params);
+  else if (L->Params.size() == R->Params.size()) {
+    for (unsigned i = 0; i < L->Params.size(); ++i)
+      mergeInfo(L->Params[i], R->Params[i]);
+  }
+}
+
+#define FIND_FUNC(X)                                          \
+  template <>                                                 \
+  std::unique_ptr<X> &InfoSet::find(StringRef Key) {          \
+    auto I = InfoIndex.find(Key);                             \
+    return I == InfoIndex.end() ? nullptr : X##s[I->second]; \
+  }
+
+// FIND_FUNC(NamespaceInfo)
+// FIND_FUNC(RecordInfo)
+// FIND_FUNC(EnumInfo)
+// FIND_FUNC(FunctionInfo)
+
+#undef FIND_FUNC
+
+void InfoSet::insert(StringRef Key, std::unique_ptr<RecordInfo> &&I) {
+  auto R = InfoIndex.try_emplace(Key, RecordInfos.size());
+  if (R.second)
+    RecordInfos.push_back(std::move(I));
+  else {
+    if (I->IsDefinition) {
+      mergeInfo(I, RecordInfos[R.first->second]);
+      RecordInfos[R.first->second] = std::move(I);
+      return;
+    }
+    mergeInfo(RecordInfos[R.first->second], I);
+  }
+}
+
+#define INSERT_FUNC(X)                                          \
+  void InfoSet::insert(StringRef Key, std::unique_ptr<X> &&I) { \
+    auto R = InfoIndex.try_emplace(Key, X##s.size());           \
+    if (R.second)                                               \
+      X##s.push_back(std::move(I));                             \
+    else {                                                      \
+      if (I->IsDefinition) {                                     \
+        mergeInfo(I, X##s[R.first->second]);             \
+        X##s[R.first->second] = std::move(I);            \
+        return;                                                 \
+      }                                                         \
+      mergeInfo(X##s[R.first->second], I);               \
+    }                                                           \
+  }
+
+// INSERT_FUNC(RecordInfo)
+INSERT_FUNC(EnumInfo)
+INSERT_FUNC(FunctionInfo)
+
+#undef INSERT_FUNC
+
+void InfoSet::insert(StringRef Key, std::unique_ptr<NamespaceInfo> &&I) {
+  auto R = InfoIndex.try_emplace(Key, NamespaceInfos.size());
+  if (R.second)
+    NamespaceInfos.push_back(std::move(I));
+  else
+    mergeInfo(NamespaceInfos[R.first->second], I);
+}
+
+}  // namespace doc
+}  // namespace clang
Index: clang-doc/Reducer.h
===================================================================
--- /dev/null
+++ clang-doc/Reducer.h
@@ -0,0 +1,26 @@
+///===-- ClangDocReducer.h - ClangDocReducer -------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REDUCER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REDUCER_H
+
+#include "Representation.h"
+#include "clang/Tooling/Execution.h"
+#include "clang/Tooling/Tooling.h"
+
+namespace clang {
+namespace doc {
+
+// Combine occurrences of the same info across translation units.
+std::unique_ptr<InfoSet> mergeInfos(tooling::ToolResults *Results);
+
+}  // namespace doc
+}  // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REDUCER_H
Index: clang-doc/Reducer.cpp
===================================================================
--- /dev/null
+++ clang-doc/Reducer.cpp
@@ -0,0 +1,32 @@
+///===-- ClangDocReducer.h - ClangDocReducer -------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BitcodeReader.h"
+#include "Reducer.h"
+#include "Representation.h"
+
+namespace clang {
+namespace doc {
+
+std::unique_ptr<InfoSet> mergeInfos(tooling::ToolResults *Results) {
+  std::unique_ptr<InfoSet> UniqueInfos = llvm::make_unique<InfoSet>();
+  doc::ClangDocBitcodeReader Reader;
+  bool Err = false;
+  Results->forEachResult([&](StringRef Key, StringRef Value) {
+    if (!Reader.readBitstreamToInfoSet(Key, Value, UniqueInfos)) {
+      Err = true;
+      return;
+    }
+  });
+  if (Err) return nullptr;
+  return std::move(UniqueInfos);
+}
+
+}  // namespace doc
+}  // namespace clang
Index: clang-doc/CMakeLists.txt
===================================================================
--- clang-doc/CMakeLists.txt
+++ clang-doc/CMakeLists.txt
@@ -3,8 +3,11 @@
   )
 
 add_clang_library(clangDoc
+  BitcodeReader.cpp
   BitcodeWriter.cpp
   Mapper.cpp
+  Reducer.cpp
+  Representation.cpp
 
   LINK_LIBS
   clangAnalysis
Index: clang-doc/BitcodeWriter.h
===================================================================
--- clang-doc/BitcodeWriter.h
+++ clang-doc/BitcodeWriter.h
@@ -151,6 +151,7 @@
 
   template <typename T>
   void writeBitstream(const T &I, bool WriteBlockInfo = false);
+  void writeBitstreamForInfoSet(InfoSet &ISet);
 
  private:
   class AbbreviationMap {
Index: clang-doc/BitcodeWriter.cpp
===================================================================
--- clang-doc/BitcodeWriter.cpp
+++ clang-doc/BitcodeWriter.cpp
@@ -396,5 +396,13 @@
 
 #undef EMITINFO
 
+void ClangDocBitcodeWriter::writeBitstreamForInfoSet(InfoSet &ISet) {
+  emitBlockInfoBlock();
+  for (const auto &I : ISet.getNamespaceInfos()) writeBitstream(*I);
+  for (const auto &I : ISet.getFunctionInfos()) writeBitstream(*I);
+  for (const auto &I : ISet.getRecordInfos()) writeBitstream(*I);
+  for (const auto &I : ISet.getEnumInfos()) writeBitstream(*I);
+}
+
 }  // namespace doc
 }  // namespace clang
Index: clang-doc/BitcodeReader.h
===================================================================
--- /dev/null
+++ clang-doc/BitcodeReader.h
@@ -0,0 +1,96 @@
+//===--  BitcodeReader.h - ClangDoc Bitcode Reader --------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a writer for serializing the clang-doc internal
+// representation to LLVM bitcode. The writer takes in a stream and emits the
+// generated bitcode to that stream.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BITCODE_READER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BITCODE_READER_H
+
+#include <initializer_list>
+#include <vector>
+#include "BitcodeWriter.h"
+#include "Representation.h"
+#include "clang/AST/AST.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Bitcode/BitstreamReader.h"
+#include "llvm/Bitcode/BitstreamWriter.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+// Class to read bitstream into an InfoSet collection
+class ClangDocBitcodeReader {
+ public:
+  ClangDocBitcodeReader() {}
+  using RecordData = SmallVector<uint64_t, 128>;
+
+  bool readBitstreamToInfoSet(StringRef Key, SmallString<2048> Bits,
+                              std::unique_ptr<InfoSet> &IS);
+
+ private:
+  enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin };
+
+  // Sream validation and overview
+  bool validateStream(llvm::BitstreamCursor &Stream);
+  bool readVersion(llvm::BitstreamCursor &Stream);
+  bool readBlockInfoBlock(llvm::BitstreamCursor &Stream);
+
+  // Reading records
+  template <typename T>
+  bool readRecord(llvm::BitstreamCursor &Stream, unsigned ID, T &I);
+
+  bool parseRecord(unsigned ID, StringRef Blob, unsigned VersionNo);
+  bool parseRecord(unsigned ID, StringRef Blob,
+                   std::unique_ptr<NamespaceInfo> &I);
+  bool parseRecord(unsigned ID, StringRef Blob, std::unique_ptr<RecordInfo> &I);
+  bool parseRecord(unsigned ID, StringRef Blob, std::unique_ptr<EnumInfo> &I);
+  bool parseRecord(unsigned ID, StringRef Blob,
+                   std::unique_ptr<FunctionInfo> &I);
+  bool parseRecord(unsigned ID, StringRef Blob, std::unique_ptr<TypeInfo> &I);
+  bool parseRecord(unsigned ID, StringRef Blob,
+                   std::unique_ptr<FieldTypeInfo> &I);
+  bool parseRecord(unsigned ID, StringRef Blob,
+                   std::unique_ptr<MemberTypeInfo> &I);
+  bool parseRecord(unsigned ID, StringRef Blob,
+                   std::unique_ptr<CommentInfo> &I);
+
+  // Reading blocks
+  template <typename T>
+  bool readBlock(llvm::BitstreamCursor &Stream, unsigned ID, T &I);
+  template <typename T>
+  bool readSubBlock(llvm::BitstreamCursor &Stream, unsigned ID, T &I);
+  template <typename T>
+  bool readBlockToInfo(llvm::BitstreamCursor &Stream,
+                       std::unique_ptr<InfoSet> &IS, unsigned ID, StringRef Key);
+
+  // Helpers
+  template <typename T>
+  std::unique_ptr<T> makeInfo(unsigned ID);
+  template <typename T>
+  std::unique_ptr<CommentInfo> &getCommentInfo(T &I);
+  template <typename Iy, typename Ty>
+  bool addTypeInfo(Iy &I, Ty &&T);
+  Cursor skipUntilRecordOrBlock(llvm::BitstreamCursor &Stream,
+                                unsigned &BlockOrRecordID);
+
+  SmallVector<uint64_t, 1024> Record;
+  Optional<llvm::BitstreamBlockInfo> BlockInfo;
+  std::map<unsigned, StringRef> RecordNames;
+};
+
+}  // namespace doc
+}  // namespace clang
+
+#endif  // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BITCODE_READER_H
Index: clang-doc/BitcodeReader.cpp
===================================================================
--- /dev/null
+++ clang-doc/BitcodeReader.cpp
@@ -0,0 +1,446 @@
+//===--  BitcodeReader.cpp - ClangDoc Bitcode Reader ------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BitcodeReader.h"
+#include "llvm/ADT/IndexedMap.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+// Read records
+
+template <typename T>
+bool ClangDocBitcodeReader::readRecord(llvm::BitstreamCursor &Stream,
+                                       unsigned ID, T &I) {
+  Record.clear();
+  StringRef Blob;
+  unsigned RecID = Stream.readRecord(ID, Record, &Blob);
+  return parseRecord(RecID, Blob, I);
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob,
+                                        unsigned VersionNo) {
+  if (ID == VERSION && Record[0] == VersionNo) return true;
+  return false;
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob,
+                                        std::unique_ptr<NamespaceInfo> &I) {
+  switch (ID) {
+    case NAMESPACE_NAME:
+      I->Name = Blob;
+      return true;
+    case NAMESPACE_NAMESPACE:
+      I->Namespace.push_back(Blob);
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob,
+                                        std::unique_ptr<RecordInfo> &I) {
+  switch (ID) {
+    case RECORD_NAME:
+      I->Name = Blob;
+      return true;
+    case RECORD_NAMESPACE:
+      I->Namespace.push_back(Blob);
+      return true;
+    case RECORD_ISDEFINITION:
+      I->IsDefinition = (bool)Record[0];
+      return true;
+    case RECORD_PARENT:
+      I->ParentUSRs.push_back(Blob);
+      return true;
+    case RECORD_VPARENT:
+      I->VirtualParentUSRs.push_back(Blob);
+      return true;
+    case RECORD_LOCATION:
+      I->Loc.emplace_back(Location{(int)Record[0], Blob});
+      return true;
+    case RECORD_TAG_TYPE:
+      I->TagType = (TagTypeKind)Record[0];
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob,
+                                        std::unique_ptr<EnumInfo> &I) {
+  switch (ID) {
+    case ENUM_NAME:
+      I->Name = Blob;
+      return true;
+    case ENUM_NAMESPACE:
+      I->Namespace.push_back(Blob);
+      return true;
+    case ENUM_ISDEFINITION:
+      I->IsDefinition = (bool)Record[0];
+      return true;
+    case ENUM_LOCATION:
+      I->Loc.emplace_back(Location{(int)Record[0], Blob});
+      return true;
+    case ENUM_SCOPED:
+      I->Scoped = Record[0];
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob,
+                                        std::unique_ptr<FunctionInfo> &I) {
+  switch (ID) {
+    case FUNCTION_NAME:
+      I->Name = Blob;
+      return true;
+    case FUNCTION_NAMESPACE:
+      I->Namespace.push_back(Blob);
+      return true;
+    case FUNCTION_PARENT:
+      I->ParentUSR = Blob;
+      return true;
+    case FUNCTION_ISDEFINITION:
+      I->IsDefinition = (bool)Record[0];
+      return true;
+    case FUNCTION_LOCATION:
+      I->Loc.emplace_back(Location{(int)Record[0], Blob});
+      return true;
+    case FUNCTION_ACCESS:
+      I->Access = (AccessSpecifier)Record[0];
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob,
+                                        std::unique_ptr<TypeInfo> &I) {
+  switch (ID) {
+    case TYPE_TYPE:
+      I->TypeUSR = Blob;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob,
+                                        std::unique_ptr<FieldTypeInfo> &I) {
+  switch (ID) {
+    case FIELD_TYPE_TYPE:
+      I->TypeUSR = Blob;
+      return true;
+    case FIELD_TYPE_NAME:
+      I->Name = Blob;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob,
+                                        std::unique_ptr<MemberTypeInfo> &I) {
+  switch (ID) {
+    case MEMBER_TYPE_TYPE:
+      I->TypeUSR = Blob;
+      return true;
+    case MEMBER_TYPE_NAME:
+      I->Name = Blob;
+      return true;
+    case MEMBER_TYPE_ACCESS:
+      I->Access = (AccessSpecifier)Record[0];
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob,
+                                        std::unique_ptr<CommentInfo> &I) {
+  switch (ID) {
+    case COMMENT_KIND:
+      I->Kind = Blob;
+      return true;
+    case COMMENT_TEXT:
+      I->Text = Blob;
+      return true;
+    case COMMENT_NAME:
+      I->Name = Blob;
+      return true;
+    case COMMENT_DIRECTION:
+      I->Direction = Blob;
+      return true;
+    case COMMENT_PARAMNAME:
+      I->ParamName = Blob;
+      return true;
+    case COMMENT_CLOSENAME:
+      I->CloseName = Blob;
+      return true;
+    case COMMENT_ATTRKEY:
+      I->AttrKeys.push_back(Blob);
+      return true;
+    case COMMENT_ATTRVAL:
+      I->AttrValues.push_back(Blob);
+      return true;
+    case COMMENT_ARG:
+      I->Args.push_back(Blob);
+      return true;
+    case COMMENT_POSITION:
+      I->Position.push_back(Blob);
+      return true;
+    case COMMENT_SELFCLOSING:
+      I->SelfClosing = Record[0];
+      return true;
+    case COMMENT_EXPLICIT:
+      I->Explicit = Record[0];
+      return true;
+    default:
+      return false;
+  }
+}
+
+// Helpers
+
+template <typename T>
+std::unique_ptr<CommentInfo> &ClangDocBitcodeReader::getCommentInfo(T &I) {
+  I->Description.emplace_back(llvm::make_unique<CommentInfo>());
+  return I->Description.back();
+}
+
+template <>
+std::unique_ptr<CommentInfo> &ClangDocBitcodeReader::getCommentInfo(
+    const unsigned int &I) {
+  errs() << "Cannot not have comment subblock.\n";
+  exit(1);
+}
+
+template <>
+std::unique_ptr<CommentInfo> &ClangDocBitcodeReader::getCommentInfo(
+    std::unique_ptr<CommentInfo> &I) {
+  I->Children.emplace_back(llvm::make_unique<CommentInfo>());
+  return I->Children.back();
+}
+
+template <typename Iy, typename Ty>
+bool ClangDocBitcodeReader::addTypeInfo(Iy &I, Ty &&T) {
+  errs() << "Cannot not have type subblock.\n";
+  exit(1);
+}
+
+template <>
+bool ClangDocBitcodeReader::addTypeInfo(std::unique_ptr<EnumInfo> &I,
+                                        std::unique_ptr<TypeInfo> &&T) {
+  I->Members.emplace_back(std::move(T));
+  return true;
+}
+
+template <>
+bool ClangDocBitcodeReader::addTypeInfo(std::unique_ptr<RecordInfo> &I,
+                                        std::unique_ptr<MemberTypeInfo> &&T) {
+  I->Members.emplace_back(std::move(T));
+  return true;
+}
+
+template <>
+bool ClangDocBitcodeReader::addTypeInfo(std::unique_ptr<FunctionInfo> &I,
+                                        std::unique_ptr<TypeInfo> &&T) {
+  I->ReturnType = std::move(T);
+  return true;
+}
+
+template <>
+bool ClangDocBitcodeReader::addTypeInfo(std::unique_ptr<FunctionInfo> &I,
+                                        std::unique_ptr<FieldTypeInfo> &&T) {
+  I->Params.emplace_back(std::move(T));
+  return true;
+}
+
+ClangDocBitcodeReader::Cursor ClangDocBitcodeReader::skipUntilRecordOrBlock(
+    llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) {
+  BlockOrRecordID = 0;
+
+  while (!Stream.AtEndOfStream()) {
+    unsigned Code = Stream.ReadCode();
+
+    switch ((llvm::bitc::FixedAbbrevIDs)Code) {
+      case llvm::bitc::ENTER_SUBBLOCK:
+        BlockOrRecordID = Stream.ReadSubBlockID();
+        return Cursor::BlockBegin;
+      case llvm::bitc::END_BLOCK:
+        if (Stream.ReadBlockEnd()) return Cursor::BadBlock;
+        return Cursor::BlockEnd;
+      case llvm::bitc::DEFINE_ABBREV:
+        Stream.ReadAbbrevRecord();
+        continue;
+      case llvm::bitc::UNABBREV_RECORD:
+        return Cursor::BadBlock;
+      default:
+        BlockOrRecordID = Code;
+        return Cursor::Record;
+    }
+  }
+  llvm_unreachable("Premature stream end.");
+}
+
+// Read blocks
+
+template <typename Ty>
+bool ClangDocBitcodeReader::readBlock(llvm::BitstreamCursor &Stream,
+                                      unsigned ID, Ty &I) {
+  if (Stream.EnterSubBlock(ID)) return false;
+
+  while (true) {
+    unsigned BlockOrCode = 0;
+    Cursor Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
+
+    switch (Res) {
+      case Cursor::BadBlock:
+        return false;
+      case Cursor::BlockEnd:
+        return true;
+      case Cursor::BlockBegin:
+        if (readSubBlock(Stream, BlockOrCode, I)) continue;
+        if (!Stream.SkipBlock()) return false;
+        continue;
+      case Cursor::Record:
+        break;
+    }
+    if (!readRecord(Stream, BlockOrCode, I)) return false;
+  }
+}
+
+template <typename Ty>
+bool ClangDocBitcodeReader::readSubBlock(llvm::BitstreamCursor &Stream,
+                                         unsigned ID, Ty &I) {
+  switch (ID) {
+    // Blocks can only have Comment or TypeInfo subblocks
+    case BI_COMMENT_BLOCK_ID:
+      if (readBlock(Stream, ID, getCommentInfo(I))) return true;
+      return false;
+    case BI_TYPE_BLOCK_ID: {
+      std::unique_ptr<TypeInfo> T = llvm::make_unique<TypeInfo>();
+      if (readBlock(Stream, ID, T)) {
+        if (addTypeInfo(I, std::move(T))) return true;
+      }
+      return false;
+    }
+    case BI_FIELD_TYPE_BLOCK_ID: {
+      std::unique_ptr<FieldTypeInfo> T = llvm::make_unique<FieldTypeInfo>();
+      if (readBlock(Stream, ID, T)) {
+        if (addTypeInfo(I, std::move(T))) return true;
+      }
+      return false;
+    }
+    case BI_MEMBER_TYPE_BLOCK_ID: {
+      std::unique_ptr<MemberTypeInfo> T = llvm::make_unique<MemberTypeInfo>();
+      if (readBlock(Stream, ID, T)) {
+        if (addTypeInfo(I, std::move(T))) return true;
+      }
+      return false;
+    }
+    default:
+      errs() << "Invalid subblock type.\n";
+      exit(1);
+  }
+}
+
+// Validation and overview
+
+bool ClangDocBitcodeReader::validateStream(llvm::BitstreamCursor &Stream) {
+  if (Stream.AtEndOfStream()) return false;
+
+  // Sniff for the signature.
+  if (Stream.Read(8) != 'D' || Stream.Read(8) != 'O' || Stream.Read(8) != 'C' ||
+      Stream.Read(8) != 'S')
+    return false;
+  return true;
+}
+
+bool ClangDocBitcodeReader::readBlockInfoBlock(llvm::BitstreamCursor &Stream) {
+  BlockInfo = Stream.ReadBlockInfoBlock(/*ReadBlockInfoNames=*/true);
+  if (!BlockInfo) return false;
+  Stream.setBlockInfo(&*BlockInfo);
+  // Extract the record names associated with each field
+  for (unsigned i = BI_FIRST; i <= BI_LAST; ++i) {
+    for (const auto &N : (*BlockInfo).getBlockInfo(i)->RecordNames)
+      RecordNames[N.first] = N.second;
+  }
+  return true;
+}
+
+// Entry point
+
+bool ClangDocBitcodeReader::readBitstreamToInfoSet(
+    StringRef Key, SmallString<2048> Bits, std::unique_ptr<InfoSet> &IS) {
+  BitstreamCursor Stream(Bits);
+  if (!validateStream(Stream)) return false;
+
+  // Read the top level blocks.
+  while (!Stream.AtEndOfStream()) {
+    unsigned Code = Stream.ReadCode();
+    if (Code != bitc::ENTER_SUBBLOCK) return false;
+
+    switch (auto ID = Stream.ReadSubBlockID()) {
+      // NamedType and Comment blocks should not appear at the top level
+      case BI_TYPE_BLOCK_ID:
+      case BI_FIELD_TYPE_BLOCK_ID:
+      case BI_MEMBER_TYPE_BLOCK_ID:
+      case BI_COMMENT_BLOCK_ID:
+        errs() << "Invalid top level block.\n";
+        return false;
+      case BI_VERSION_BLOCK_ID:
+        if (readBlock(Stream, ID, VERSION_NUMBER)) continue;
+        return false;
+      case llvm::bitc::BLOCKINFO_BLOCK_ID:
+        if (readBlockInfoBlock(Stream)) continue;
+        return false;
+      default:
+        if (!Stream.SkipBlock()) return false;
+        continue;
+      case BI_NAMESPACE_BLOCK_ID:
+        if (readBlockToInfo<NamespaceInfo>(Stream, IS, ID, Key))
+          continue;
+        return false;
+      case BI_RECORD_BLOCK_ID:
+        if (readBlockToInfo<RecordInfo>(Stream, IS, ID, Key))
+          continue;
+        return false;
+      case BI_ENUM_BLOCK_ID:
+        if (readBlockToInfo<EnumInfo>(Stream, IS, ID, Key))
+          continue;
+        return false;
+      case BI_FUNCTION_BLOCK_ID:
+        if (readBlockToInfo<FunctionInfo>(Stream, IS, ID, Key))
+          continue;
+        return false;
+    }
+  }
+  return true;
+}
+
+template <typename T>
+bool ClangDocBitcodeReader::readBlockToInfo(llvm::BitstreamCursor &Stream,
+                                            std::unique_ptr<InfoSet> &IS,
+                                            unsigned ID,
+                                            StringRef Key) {
+  auto I = llvm::make_unique<T>();
+  if (readBlock(Stream, ID, I)) {
+    IS->insert(Key, std::move(I));
+    return true;
+  }
+  return false;
+}
+
+}  // namespace doc
+}  // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to