juliehockett updated this revision to Diff 134546.
juliehockett edited the summary of this revision.
juliehockett added a comment.

Moving the entire implementation of the bitcode reader to this patch (from the 
mapper patch, here <https://reviews.llvm.org/D41102>) and cleaning up 
implementation


https://reviews.llvm.org/D43341

Files:
  clang-doc/CMakeLists.txt
  clang-doc/ClangDocBinary.cpp
  clang-doc/ClangDocBinary.h
  clang-doc/ClangDocReducer.cpp
  clang-doc/ClangDocReducer.h
  clang-doc/ClangDocRepresentation.cpp
  clang-doc/ClangDocRepresentation.h
  clang-doc/tool/ClangDocMain.cpp
  test/clang-doc/mapper-class.cpp
  test/clang-doc/mapper-enum.cpp
  test/clang-doc/mapper-namespace.cpp
  test/clang-doc/mapper-record.cpp
  test/clang-doc/mapper-struct.cpp
  test/clang-doc/mapper-union.cpp

Index: test/clang-doc/mapper-union.cpp
===================================================================
--- test/clang-doc/mapper-union.cpp
+++ /dev/null
@@ -1,26 +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 -docs=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/A.bc --dump | FileCheck %s
-
-union A { int X; int Y; };
-// CHECK: <BLOCKINFO_BLOCK/>
-// CHECK: <RecordBlock NumWords=22 BlockCodeSize=5>
-  // CHECK: <FullyQualifiedName abbrevid=4 op0=1/> blob data = 'A'
-  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'A'
-  // CHECK: <TagType abbrevid=9 op0=2/>
-  // CHECK: <NamedTypeBlock NumWords=6 BlockCodeSize=5>
-    // CHECK: <ID abbrevid=4 op0=2/>
-    // CHECK: <Type abbrevid=5 op0=3/> blob data = 'int'
-    // CHECK: <Name abbrevid=6 op0=4/> blob data = 'A::X'
-    // CHECK: <Access abbrevid=7 op0=3/>
-  // CHECK: </NamedTypeBlock>
-  // CHECK: <NamedTypeBlock NumWords=6 BlockCodeSize=5>
-    // CHECK: <ID abbrevid=4 op0=2/>
-    // CHECK: <Type abbrevid=5 op0=3/> blob data = 'int'
-    // CHECK: <Name abbrevid=6 op0=4/> blob data = 'A::Y'
-    // CHECK: <Access abbrevid=7 op0=3/>
-  // CHECK: </NamedTypeBlock>
-// CHECK: </RecordBlock>
Index: test/clang-doc/mapper-struct.cpp
===================================================================
--- test/clang-doc/mapper-struct.cpp
+++ /dev/null
@@ -1,19 +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 -docs=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/C.bc --dump | FileCheck %s
-
-struct C { int i; };
-// CHECK: <BLOCKINFO_BLOCK/>
-// CHECK: <RecordBlock NumWords=13 BlockCodeSize=5>
-  // CHECK: <FullyQualifiedName abbrevid=4 op0=1/> blob data = 'C'
-  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'C'
-  // CHECK: <NamedTypeBlock NumWords=6 BlockCodeSize=5>
-    // CHECK: <ID abbrevid=4 op0=2/>
-    // CHECK: <Type abbrevid=5 op0=3/> blob data = 'int'
-    // CHECK: <Name abbrevid=6 op0=4/> blob data = 'C::i'
-    // CHECK: <Access abbrevid=7 op0=3/>
-  // CHECK: </NamedTypeBlock>
-// CHECK: </RecordBlock>
Index: test/clang-doc/mapper-record.cpp
===================================================================
--- /dev/null
+++ test/clang-doc/mapper-record.cpp
@@ -0,0 +1,155 @@
+// 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 -docs=%t/docs
+// RUN llvm-bcanalyzer %t/docs/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: <FunctionBlock NumWords=19 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=4/> blob data = 'E::E'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'E'
+  // CHECK: <Namespace abbrevid=6 op0=1/> blob data = 'E'
+  // CHECK: <MangledName abbrevid=8 op0=9/> blob data = '_ZN1EC1Ev'
+  // CHECK: <Parent abbrevid=9 op0=1/> blob data = 'E'
+  // CHECK: <NamedTypeBlock NumWords=4 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=3/>
+    // CHECK: <Type abbrevid=5 op0=4/> blob data = 'void'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+// CHECK: </FunctionBlock>
+// CHECK: <FunctionBlock NumWords=20 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=5/> blob data = 'E::~E'
+  // CHECK: <Name abbrevid=5 op0=2/> blob data = '~E'
+  // CHECK: <Namespace abbrevid=6 op0=1/> blob data = 'E'
+  // CHECK: <MangledName abbrevid=8 op0=9/> blob data = '_ZN1ED1Ev'
+  // CHECK: <Parent abbrevid=9 op0=1/> blob data = 'E'
+  // CHECK: <NamedTypeBlock NumWords=4 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=3/>
+    // CHECK: <Type abbrevid=5 op0=4/> blob data = 'void'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+// CHECK: </FunctionBlock>
+// CHECK: <FunctionBlock NumWords=29 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=18/> blob data = 'E::ProtectedMethod'
+  // CHECK: <Name abbrevid=5 op0=15/> blob data = 'ProtectedMethod'
+  // CHECK: <Namespace abbrevid=6 op0=1/> blob data = 'E'
+  // CHECK: <MangledName abbrevid=8 op0=24/> blob data = '_ZN1E15ProtectedMethodEv'
+  // CHECK: <Parent abbrevid=9 op0=1/> blob data = 'E'
+  // CHECK: <NamedTypeBlock NumWords=4 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=3/>
+    // CHECK: <Type abbrevid=5 op0=4/> blob data = 'void'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+// CHECK: </FunctionBlock>
+// CHECK: <RecordBlock NumWords=22 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=1/> blob data = 'A'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'A'
+  // CHECK: <TagType abbrevid=8 op0=2/>
+  // CHECK: <NamedTypeBlock NumWords=6 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=2/>
+    // CHECK: <Type abbrevid=5 op0=3/> blob data = 'int'
+    // CHECK: <Name abbrevid=6 op0=4/> blob data = 'A::X'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+  // CHECK: <NamedTypeBlock NumWords=6 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=2/>
+    // CHECK: <Type abbrevid=5 op0=3/> blob data = 'int'
+    // CHECK: <Name abbrevid=6 op0=4/> blob data = 'A::Y'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+// CHECK: </RecordBlock>
+// CHECK: <RecordBlock NumWords=14 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=1/> blob data = 'C'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'C'
+  // CHECK: <TagType abbrevid=8 op0=2/>
+  // CHECK: <NamedTypeBlock NumWords=6 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=2/>
+    // CHECK: <Type abbrevid=5 op0=3/> blob data = 'int'
+    // CHECK: <Name abbrevid=6 op0=4/> blob data = 'C::i'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+// CHECK: </RecordBlock>
+// CHECK: <RecordBlock NumWords=5 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=1/> blob data = 'D'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'D'
+  // CHECK: <TagType abbrevid=8 op0=3/>
+// CHECK: </RecordBlock>
+// CHECK: <RecordBlock NumWords=5 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=1/> blob data = 'E'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'E'
+  // CHECK: <TagType abbrevid=8 op0=3/>
+// CHECK: </RecordBlock>
+// CHECK: <RecordBlock NumWords=12 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=1/> blob data = 'F'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'F'
+  // CHECK: <TagType abbrevid=8 op0=3/>
+  // CHECK: <Parent abbrevid=9 op0=7/> blob data = 'class E'
+  // CHECK: <VParent abbrevid=10 op0=7/> blob data = 'class D'
+// CHECK: </RecordBlock>
+// CHECK: <EnumBlock NumWords=18 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=1/> blob data = 'B'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'B'
+  // CHECK: <Scoped abbrevid=8 op0=2/>
+  // CHECK: <NamedTypeBlock NumWords=4 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=2/>
+    // CHECK: <Type abbrevid=5 op0=1/> blob data = 'X'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+  // CHECK: <NamedTypeBlock NumWords=4 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=2/>
+    // CHECK: <Type abbrevid=5 op0=1/> blob data = 'Y'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+// CHECK: </EnumBlock>
+// CHECK: <NonDefBlock NumWords=7 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=4/> blob data = 'F::F'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'F'
+  // CHECK: <Namespace abbrevid=6 op0=1/> blob data = 'F'
+  // CHECK: <Type abbrevid=7 op0=2/>
+// CHECK: </NonDefBlock>
+// CHECK: <NonDefBlock NumWords=7 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=4/> blob data = 'A::A'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'A'
+  // CHECK: <Namespace abbrevid=6 op0=1/> blob data = 'A'
+  // CHECK: <Type abbrevid=7 op0=2/>
+// CHECK: </NonDefBlock>
+// CHECK: <NonDefBlock NumWords=7 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=4/> blob data = 'C::C'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'C'
+  // CHECK: <Namespace abbrevid=6 op0=1/> blob data = 'C'
+  // CHECK: <Type abbrevid=7 op0=2/>
+// CHECK: </NonDefBlock>
+// CHECK: <NonDefBlock NumWords=7 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=4/> blob data = 'D::D'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'D'
+  // CHECK: <Namespace abbrevid=6 op0=1/> blob data = 'D'
+  // CHECK: <Type abbrevid=7 op0=2/>
+// CHECK: </NonDefBlock>
+// CHECK: <NonDefBlock NumWords=7 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=4/> blob data = 'E::E'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'E'
+  // CHECK: <Namespace abbrevid=6 op0=1/> blob data = 'E'
+  // CHECK: <Type abbrevid=7 op0=2/>
+// CHECK: </NonDefBlock>
Index: test/clang-doc/mapper-namespace.cpp
===================================================================
--- test/clang-doc/mapper-namespace.cpp
+++ test/clang-doc/mapper-namespace.cpp
@@ -3,11 +3,73 @@
 // RUN: echo "" > %t/compile_flags.txt
 // RUN: cp "%s" "%t/test.cpp"
 // RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -docs=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/A.bc --dump | FileCheck %s
+// RUN llvm-bcanalyzer %t/docs/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;
+}
+
+}
+}
 
-namespace A {}
 // CHECK: <BLOCKINFO_BLOCK/>
 // CHECK: <NamespaceBlock NumWords=5 BlockCodeSize=5>
   // CHECK: <FullyQualifiedName abbrevid=4 op0=1/> blob data = 'A'
   // CHECK: <Name abbrevid=5 op0=1/> blob data = 'A'
 // CHECK: </NamespaceBlock>
+// CHECK: <NamespaceBlock NumWords=7 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=4/> blob data = 'A::B'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'B'
+  // CHECK: <Namespace abbrevid=6 op0=1/> blob data = 'A'
+// CHECK: </NamespaceBlock>
+// CHECK: <FunctionBlock NumWords=17 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=4/> blob data = 'A::f'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'f'
+  // CHECK: <Namespace abbrevid=6 op0=1/> blob data = 'A'
+  // CHECK: <MangledName abbrevid=8 op0=9/> blob data = '_ZN1A1fEv'
+  // CHECK: <NamedTypeBlock NumWords=4 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=3/>
+    // CHECK: <Type abbrevid=5 op0=4/> blob data = 'void'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+// CHECK: </FunctionBlock>
+// CHECK: <FunctionBlock NumWords=30 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=10/> blob data = 'A::B::func'
+  // CHECK: <Name abbrevid=5 op0=4/> blob data = 'func'
+  // CHECK: <Namespace abbrevid=6 op0=4/> blob data = 'A::B'
+  // CHECK: <MangledName abbrevid=8 op0=14/> blob data = '_ZN1A1B4funcEi'
+  // CHECK: <NamedTypeBlock NumWords=6 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=3/>
+    // CHECK: <Type abbrevid=5 op0=12/> blob data = 'enum A::B::E'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+  // CHECK: <NamedTypeBlock NumWords=6 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=1/>
+    // CHECK: <Type abbrevid=5 op0=3/> blob data = 'int'
+    // CHECK: <Name abbrevid=6 op0=1/> blob data = 'i'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+// CHECK: </FunctionBlock>
+// CHECK: <EnumBlock NumWords=16 BlockCodeSize=5>
+  // CHECK: <FullyQualifiedName abbrevid=4 op0=7/> blob data = 'A::B::E'
+  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'E'
+  // CHECK: <Namespace abbrevid=6 op0=4/> blob data = 'A::B'
+  // CHECK: <Scoped abbrevid=8 op0=192/>
+  // CHECK: <NamedTypeBlock NumWords=5 BlockCodeSize=5>
+    // CHECK: <ID abbrevid=4 op0=2/>
+    // CHECK: <Type abbrevid=5 op0=7/> blob data = 'A::B::X'
+    // CHECK: <Access abbrevid=7 op0=3/>
+  // CHECK: </NamedTypeBlock>
+// CHECK: </EnumBlock>
Index: test/clang-doc/mapper-enum.cpp
===================================================================
--- test/clang-doc/mapper-enum.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 -docs=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/B.bc --dump | FileCheck %s
-
-enum B { X, Y };
-// CHECK: <BLOCKINFO_BLOCK/>
-// CHECK: <EnumBlock NumWords=17 BlockCodeSize=5>
-  // CHECK: <FullyQualifiedName abbrevid=4 op0=1/> blob data = 'B'
-  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'B'
-  // CHECK: <NamedTypeBlock NumWords=4 BlockCodeSize=5>
-    // CHECK: <ID abbrevid=4 op0=2/>
-    // CHECK: <Type abbrevid=5 op0=1/> blob data = 'X'
-    // CHECK: <Access abbrevid=7 op0=3/>
-  // CHECK: </NamedTypeBlock>
-  // CHECK: <NamedTypeBlock NumWords=4 BlockCodeSize=5>
-    // CHECK: <ID abbrevid=4 op0=2/>
-    // CHECK: <Type abbrevid=5 op0=1/> blob data = 'Y'
-    // CHECK: <Access abbrevid=7 op0=3/>
-  // CHECK: </NamedTypeBlock>
-// CHECK: </EnumBlock>
Index: test/clang-doc/mapper-class.cpp
===================================================================
--- test/clang-doc/mapper-class.cpp
+++ /dev/null
@@ -1,14 +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 -docs=%t/docs
-// RUN: llvm-bcanalyzer %t/docs/E.bc --dump | FileCheck %s
-
-class E {};
-// CHECK: <BLOCKINFO_BLOCK/>
-// CHECK: <RecordBlock NumWords=5 BlockCodeSize=5>
-  // CHECK: <FullyQualifiedName abbrevid=4 op0=1/> blob data = 'E'
-  // CHECK: <Name abbrevid=5 op0=1/> blob data = 'E'
-  // CHECK: <TagType abbrevid=9 op0=3/>
-// CHECK: </RecordBlock>
Index: clang-doc/tool/ClangDocMain.cpp
===================================================================
--- clang-doc/tool/ClangDocMain.cpp
+++ clang-doc/tool/ClangDocMain.cpp
@@ -10,6 +10,7 @@
 #include <string>
 #include "ClangDoc.h"
 #include "ClangDocBinary.h"
+#include "ClangDocReducer.h"
 #include "clang/AST/AST.h"
 #include "clang/AST/Decl.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -99,26 +100,31 @@
       Exec->get()->execute(newFrontendActionFactory(&Finder), ArgAdjuster);
   if (Err) errs() << toString(std::move(Err)) << "\n";
 
+  // Reducing phase
+  errs() << "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);
+    Writer.writeAllBitstream(*Infos, Stream);
+    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/ClangDocRepresentation.h
===================================================================
--- clang-doc/ClangDocRepresentation.h
+++ clang-doc/ClangDocRepresentation.h
@@ -106,6 +106,46 @@
 
 // TODO: Add functionality to include separate markdown pages.
 
+class InfoSet {
+public:
+  InfoSet() {}
+
+  template <typename T>
+  void insert(StringRef Key, T &I);
+
+  // Returns the info with a Key, if it exists. Valid until next insert().
+  template <typename T>
+  T* find(StringRef Key);
+  
+  void removeNonDef(StringRef Key);
+
+  const std::vector<NamespaceInfo>& getNamespaceInfos() const { return NamespaceInfos; }
+  const std::vector<FunctionInfo>& getFunctionInfos() const { return FunctionInfos; }
+  const std::vector<RecordInfo>& getRecordInfos() const { return RecordInfos; }
+  const std::vector<EnumInfo>& getEnumInfos() const { return EnumInfos; }
+  const llvm::StringMap<NonDefInfo>& getNonDefInfos() const { return NonDefInfos; }
+
+private:
+  // Merge symbols L and R, preferring data from L in case of conflict.
+  // The two symbols must have the same ID.
+  template <typename L, typename R>
+  void mergeInfo(L *Left, R *Right);
+
+  void mergeInfoBase(Info *Left, Info *Right);
+  void mergeSymbolInfoBase(SymbolInfo *Left, SymbolInfo *Right);
+  void mergeNamedType(NamedType *Left, NamedType *Right);
+
+  std::vector<NamespaceInfo> NamespaceInfos;
+  std::vector<FunctionInfo> FunctionInfos;
+  std::vector<RecordInfo> RecordInfos;
+  std::vector<EnumInfo> EnumInfos;
+  llvm::DenseMap<StringRef, size_t> InfoIndex;
+  llvm::StringMap<NonDefInfo> NonDefInfos;
+};
+
+
+
+
 }  // namespace doc
 }  // namespace clang
 
Index: clang-doc/ClangDocRepresentation.cpp
===================================================================
--- /dev/null
+++ clang-doc/ClangDocRepresentation.cpp
@@ -0,0 +1,178 @@
+///===-- 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 "ClangDocRepresentation.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+void InfoSet::mergeInfoBase(Info *Left, Info *Right) {
+  if (Left->FullyQualifiedName.empty())
+    Left->FullyQualifiedName = Right->FullyQualifiedName;
+  if (Left->SimpleName.empty()) Left->SimpleName = Right->SimpleName;
+  if (Left->Namespace.empty()) Left->Namespace = Right->Namespace;
+  for (const auto &CI : Right->Description) Left->Description.emplace_back(CI);
+}
+
+void InfoSet::mergeSymbolInfoBase(SymbolInfo *Left, SymbolInfo *Right) {
+  mergeInfoBase(Left, Right);
+  if (!Left->DefLoc.LineNumber)
+    Left->DefLoc.LineNumber = Right->DefLoc.LineNumber;
+  if (Left->DefLoc.Filename.empty())
+    Left->DefLoc.Filename = Right->DefLoc.Filename;
+  for (const auto &L : Right->Loc) Left->Loc.emplace_back(L);
+}
+
+void InfoSet::mergeNamedType(NamedType *Left, NamedType *Right) {
+  if (Left->Type.empty()) Left->Type = Right->Type;
+  if (Left->Name.empty()) Left->Name = Right->Name;
+  if (!Left->Access) Left->Access = Right->Access;
+  for (const auto &CI : Right->Description) Left->Description.emplace_back(CI);
+}
+
+template <>
+void InfoSet::mergeInfo(NamespaceInfo *Left, NamespaceInfo *Right) {
+  mergeInfoBase(Left, Right);
+}
+
+template <>
+void InfoSet::mergeInfo(RecordInfo *Left, RecordInfo *Right) {
+  mergeSymbolInfoBase(Left, Right);
+  if (!Left->TagType) Left->TagType = Right->TagType;
+  if (Left->Members.empty())
+    Left->Members = Right->Members;
+  else if (Left->Members.size() == Right->Members.size()) {
+    for (unsigned i = 0; i < Left->Members.size(); ++i)
+      mergeNamedType(&Left->Members[i], &Right->Members[i]);
+  }
+  if (Left->Parents.empty()) Left->Parents = Right->Parents;
+  if (Left->VirtualParents.empty()) Left->VirtualParents = Right->VirtualParents;
+}
+
+template <>
+void InfoSet::mergeInfo(EnumInfo *Left, EnumInfo *Right) {
+  mergeSymbolInfoBase(Left, Right);
+  if (!Left->Scoped) Left->Scoped = Right->Scoped;
+  if (Left->Members.empty())
+    Left->Members = Right->Members;
+  else if (Left->Members.size() == Right->Members.size()) {
+    for (unsigned i = 0; i < Left->Members.size(); ++i)
+      mergeNamedType(&Left->Members[i], &Right->Members[i]);
+  }
+}
+
+template <>
+void InfoSet::mergeInfo(FunctionInfo *Left, FunctionInfo *Right) {
+  mergeSymbolInfoBase(Left, Right);
+  if (Left->MangledName.empty()) Left->MangledName = Right->MangledName;
+  if (Left->Parent.empty()) Left->Parent = Right->Parent;
+  if (!Left->Access) Left->Access = Right->Access;
+  mergeNamedType(&Left->ReturnType, &Right->ReturnType);
+  if (Left->Params.empty())
+    Left->Params = Right->Params;
+  else if (Left->Params.size() == Right->Params.size()) {
+    for (unsigned i = 0; i < Left->Params.size(); ++i)
+      mergeNamedType(&Left->Params[i], &Right->Params[i]);
+  }
+}
+
+#define FIND_FUNC(X)                                                \
+  template <>                                                       \
+  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
+
+template <>
+NonDefInfo* InfoSet::find(StringRef Key)  {
+  auto I = NonDefInfos.find(Key);
+  return I == NonDefInfos.end() ? nullptr : &I->second;
+}
+
+#define INSERT_FUNC(X)                                              \
+  template <>                                                       \
+  void InfoSet::insert(StringRef Key, X &I) {                       \
+    auto R = InfoIndex.try_emplace(Key, X##s.size());               \
+    if (auto *NonDef = find<NonDefInfo>(Key)) {               \
+      mergeSymbolInfoBase(&I, NonDef);                              \
+      removeNonDef(Key);                                            \
+    }                                                               \
+    if (R.second)                                                   \
+      X##s.push_back(std::move(I));                                 \
+    else {                                                          \
+      X *E = &X##s[R.first->second];                                \
+      mergeInfo(E, &I);                                             \
+    }                                                               \
+  }
+
+INSERT_FUNC(RecordInfo)
+INSERT_FUNC(EnumInfo)
+INSERT_FUNC(FunctionInfo)
+
+#undef INSERT_FUNC
+
+template <> 
+void InfoSet::insert(StringRef Key, NamespaceInfo &I) {
+  auto R = InfoIndex.try_emplace(Key, NamespaceInfos.size());                                                      \
+  if (R.second)
+    NamespaceInfos.push_back(std::move(I));
+  else {
+    NamespaceInfo *E = &NamespaceInfos[R.first->second];
+    mergeInfo(E, &I);
+  }
+}
+
+template <>
+void InfoSet::insert(StringRef Key, NonDefInfo &I) {
+  switch (I.Type) {
+    case NonDefInfo::NAMESPACE:
+      if (auto *E = find<NamespaceInfo>(Key)) {
+        mergeInfoBase(E, &I);
+        return;
+      }
+    case NonDefInfo::RECORD:
+      if (auto *E = find<RecordInfo>(Key)) {
+        mergeSymbolInfoBase(E, &I);
+        return;
+      }
+    case NonDefInfo::ENUM: 
+      if (auto *E = find<EnumInfo>(Key)) {
+        mergeSymbolInfoBase(E, &I);
+        return;
+      }
+    case NonDefInfo::FUNCTION:
+      if (auto *E = find<FunctionInfo>(Key)) {
+        mergeSymbolInfoBase(E, &I);
+        return;
+      }
+  }
+  if (auto *E = find<NonDefInfo>(Key)) {
+    mergeSymbolInfoBase(E, &I);
+    return;
+  }
+  NonDefInfos[Key] = std::move(I);
+}
+
+void InfoSet::removeNonDef(StringRef Key) {
+  NonDefInfos.erase(Key);
+}
+
+}  // namespace doc
+}  // namespace clang
Index: clang-doc/ClangDocReducer.h
===================================================================
--- /dev/null
+++ clang-doc/ClangDocReducer.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 "ClangDocRepresentation.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/ClangDocReducer.cpp
===================================================================
--- /dev/null
+++ clang-doc/ClangDocReducer.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 "ClangDocBinary.h"
+#include "ClangDocReducer.h"
+#include "ClangDocRepresentation.h"
+
+namespace clang {
+namespace doc {
+
+std::unique_ptr<InfoSet> mergeInfos(tooling::ToolResults *Results) {
+  std::unique_ptr<InfoSet> UniqueInfos = llvm::make_unique<InfoSet>();
+  doc::ClangDocBinaryReader Reader;
+  bool Err = false;
+  Results->forEachResult([&](StringRef Key, StringRef Value) {
+    if (!Reader.readBitstreamToInfoSet(Value, UniqueInfos)) {
+      Err = true;
+      return;
+    }
+  });
+  if (Err) return nullptr;
+  return std::move(UniqueInfos);
+}
+
+}  // namespace doc
+}  // namespace clang
Index: clang-doc/ClangDocBinary.h
===================================================================
--- clang-doc/ClangDocBinary.h
+++ clang-doc/ClangDocBinary.h
@@ -42,6 +42,7 @@
   template <typename T>
   void writeBitstream(const T &I, BitstreamWriter &Stream,
                       bool writeBlockInfo = false);
+  void writeAllBitstream(InfoSet &I, BitstreamWriter &Stream);
 
  private:
   void emitBlockInfoBlock(BitstreamWriter &Stream);
@@ -65,6 +66,36 @@
   AbbreviationMap Abbrevs;
 };
 
+// Class to read bitstream into an InfoSet collection
+class ClangDocBinaryReader {
+ public:
+  ClangDocBinaryReader() {}
+  using RecordData = SmallVector<uint64_t, 128>;
+
+  // Does not merge infos
+  std::unique_ptr<InfoSet> readBitstream(SmallString<2048> Bits);
+  bool readBitstreamToInfoSet(SmallString<2048> Bits, std::unique_ptr<InfoSet> &IS);
+
+ private:
+  enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin };
+  bool validateStream(llvm::BitstreamCursor &Stream);
+  bool readBlockInfoBlock(llvm::BitstreamCursor &Stream);
+  template <typename T>
+  CommentInfo &getCommentInfo(T &I);
+  template <typename T>
+  bool addNamedType(T &I, NamedType &NT);
+  template <typename T>
+  bool readRecord(llvm::BitstreamCursor &Stream, unsigned ID, T &I);
+  template <typename T>
+  bool readBlock(llvm::BitstreamCursor &Stream, unsigned ID, T &I);
+  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
 
Index: clang-doc/ClangDocBinary.cpp
===================================================================
--- clang-doc/ClangDocBinary.cpp
+++ clang-doc/ClangDocBinary.cpp
@@ -406,5 +406,486 @@
 
 #undef EMITINFO
 
+void ClangDocBinaryWriter::writeAllBitstream(InfoSet &ISet,
+                                             BitstreamWriter &Stream) {
+  emitBlockInfoBlock(Stream);
+  for (const auto &I : ISet.getNamespaceInfos()) writeBitstream(I, Stream);
+  for (const auto &I : ISet.getFunctionInfos()) writeBitstream(I, Stream);
+  for (const auto &I : ISet.getRecordInfos()) writeBitstream(I, Stream);
+  for (const auto &I : ISet.getEnumInfos()) writeBitstream(I, Stream);
+  for (const auto &I : ISet.getNonDefInfos()) writeBitstream(I.second, Stream);
+}
+
+// Reader
+
+template <>
+bool ClangDocBinaryReader::readRecord(llvm::BitstreamCursor &Stream,
+                                      unsigned ID, NamespaceInfo &I) {
+  // Read the record.
+  Record.clear();
+  StringRef Blob;
+  unsigned RecID = Stream.readRecord(ID, Record, &Blob);
+
+  switch ((DataTypes)RecID) {
+    // Locations
+    case NAMESPACE_FULLY_QUALIFIED_NAME:
+      I.FullyQualifiedName = Blob;
+      return true;
+    case NAMESPACE_NAME:
+      I.SimpleName = Blob;
+      return true;
+    case NAMESPACE_NAMESPACE:
+      I.Namespace = Blob;
+      return true;
+
+    default:
+      errs() << "Invalid record field in block.\n";
+      return false;
+  }
+}
+
+template <>
+bool ClangDocBinaryReader::readRecord(llvm::BitstreamCursor &Stream,
+                                      unsigned ID, RecordInfo &I) {
+  // Read the record.
+  Record.clear();
+  StringRef Blob;
+  unsigned RecID = Stream.readRecord(ID, Record, &Blob);
+
+  switch ((DataTypes)RecID) {
+    case RECORD_FULLY_QUALIFIED_NAME:
+      I.FullyQualifiedName = Blob;
+      return true;
+    case RECORD_NAME:
+      I.SimpleName = Blob;
+      return true;
+    case RECORD_NAMESPACE:
+      I.Namespace = Blob;
+      return true;
+    case RECORD_PARENT:
+      I.Parents.push_back(Blob);
+      return true;
+    case RECORD_VPARENT:
+      I.VirtualParents.push_back(Blob);
+      return true;
+    case RECORD_DEFLOCATION:
+      I.DefLoc = Location{(int)Record[0], 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:
+      errs() << "Invalid record field in block.\n";
+      return false;
+  }
+}
+
+template <>
+bool ClangDocBinaryReader::readRecord(llvm::BitstreamCursor &Stream,
+                                      unsigned ID, EnumInfo &I) {
+  // Read the record.
+  Record.clear();
+  StringRef Blob;
+  unsigned RecID = Stream.readRecord(ID, Record, &Blob);
+
+  switch ((DataTypes)RecID) {
+    case ENUM_FULLY_QUALIFIED_NAME:
+      I.FullyQualifiedName = Blob;
+      return true;
+    case ENUM_NAME:
+      I.SimpleName = Blob;
+      return true;
+    case ENUM_NAMESPACE:
+      I.Namespace = Blob;
+      return true;
+    case ENUM_DEFLOCATION:
+      I.DefLoc = Location{(int)Record[0], Blob};
+      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:
+      errs() << "Invalid record field in block.\n";
+      return false;
+  }
+}
+
+template <>
+bool ClangDocBinaryReader::readRecord(llvm::BitstreamCursor &Stream,
+                                      unsigned ID, FunctionInfo &I) {
+  // Read the record.
+  Record.clear();
+  StringRef Blob;
+  unsigned RecID = Stream.readRecord(ID, Record, &Blob);
+
+  switch ((DataTypes)RecID) {
+    case FUNCTION_FULLY_QUALIFIED_NAME:
+      I.FullyQualifiedName = Blob;
+      return true;
+    case FUNCTION_NAME:
+      I.SimpleName = Blob;
+      return true;
+    case FUNCTION_NAMESPACE:
+      I.Namespace = Blob;
+      return true;
+    case FUNCTION_PARENT:
+      I.Parent = Blob;
+      return true;
+    case FUNCTION_MANGLED_NAME:
+      I.MangledName = Blob;
+      return true;
+    case FUNCTION_DEFLOCATION:
+      I.DefLoc = Location{(int)Record[0], Blob};
+      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:
+      errs() << "Invalid record field in block.\n";
+      return false;
+  }
+}
+
+template <>
+bool ClangDocBinaryReader::readRecord(llvm::BitstreamCursor &Stream,
+                                      unsigned ID, NonDefInfo &I) {
+  // Read the record.
+  Record.clear();
+  StringRef Blob;
+  unsigned RecID = Stream.readRecord(ID, Record, &Blob);
+
+  switch ((DataTypes)RecID) {
+    case NONDEF_FULLY_QUALIFIED_NAME:
+      I.FullyQualifiedName = Blob;
+      return true;
+    case NONDEF_NAME:
+      I.SimpleName = Blob;
+      return true;
+    case NONDEF_NAMESPACE:
+      I.Namespace = Blob;
+      return true;
+    case NONDEF_TYPE:
+      I.Type = (NonDefInfo::InfoType)Record[0];
+      return true;
+    case NONDEF_LOCATION:
+      I.Loc.emplace_back(Location{(int)Record[0], Blob});
+      return true;
+
+    default:
+      errs() << "Invalid record field in block.\n";
+      return false;
+  }
+}
+
+template <>
+bool ClangDocBinaryReader::readRecord(llvm::BitstreamCursor &Stream,
+                                      unsigned ID, NamedType &I) {
+  // Read the record.
+  Record.clear();
+  StringRef Blob;
+  unsigned RecID = Stream.readRecord(ID, Record, &Blob);
+
+  switch ((DataTypes)RecID) {
+    case NAMED_TYPE_ID:
+      I.Field = (NamedType::FieldName) Record[0];
+      return true;
+    case NAMED_TYPE_TYPE:
+      I.Type = Blob;
+      return true;
+    case NAMED_TYPE_NAME:
+      I.Name = Blob;
+      return true;
+    case NAMED_TYPE_ACCESS:
+      I.Access = (AccessSpecifier)Record[0];
+      return true;
+
+    default:
+      errs() << "Invalid record field in block.\n";
+      return false;
+  }
+}
+
+template <>
+bool ClangDocBinaryReader::readRecord(llvm::BitstreamCursor &Stream,
+                                      unsigned ID, CommentInfo &I) {
+  // Read the record.
+  Record.clear();
+  StringRef Blob;
+  unsigned RecID = Stream.readRecord(ID, Record, &Blob);
+
+  switch ((DataTypes)RecID) {
+    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:
+      errs() << "Invalid record field in block.\n";
+      return false;
+  }
+}
+
+template <typename T>
+CommentInfo &ClangDocBinaryReader::getCommentInfo(T &I) {
+  I.Description.emplace_back(CommentInfo{});
+  return I.Description.back();
+}
+
+template <>
+CommentInfo &ClangDocBinaryReader::getCommentInfo(CommentInfo &I) {
+  I.Children.emplace_back(std::make_shared<CommentInfo>());
+  return *I.Children.back();
+}
+
+template <typename T>
+bool ClangDocBinaryReader::addNamedType(T &I, NamedType &NT) {
+  errs() << "Should not have a named type subblock.\n";
+  return false;
+}
+
+template <>
+bool ClangDocBinaryReader::addNamedType(RecordInfo &I, NamedType &NT) {
+  if (NT.Field == NamedType::MEMBER) {
+    I.Members.emplace_back(NT);
+    return true;
+  }
+  errs() << "Unknown field.\n";
+  return false;
+}
+
+template <>
+bool ClangDocBinaryReader::addNamedType(EnumInfo &I, NamedType &NT) {
+  if (NT.Field == NamedType::MEMBER) {
+    I.Members.emplace_back(NT);
+    return true;
+  }
+  errs() << "Unknown field.\n";
+  return false;
+}
+
+template <>
+bool ClangDocBinaryReader::addNamedType(FunctionInfo &I, NamedType &NT) {
+  switch (NT.Field) {
+    case (NamedType::PARAM):
+      I.Params.emplace_back(NT);
+      return true;
+    case (NamedType::RETTYPE):
+      // TODO: get rid of copy here
+      I.ReturnType = NT;
+      return true;
+    default:
+      errs() << "Unknown field.\n";
+      return false;
+  }
+}
+
+template <typename T>
+bool ClangDocBinaryReader::readBlock(llvm::BitstreamCursor &Stream, unsigned ID,
+                                     T &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:
+        switch (BlockOrCode) {
+          // Blocks can only have Comment or NamedType subblocks
+          case COMMENT_BLOCK_ID:
+            if (readBlock(Stream, COMMENT_BLOCK_ID, getCommentInfo(I)))
+              continue;
+            return false;
+          case NAMED_TYPE_BLOCK_ID: {
+            NamedType N;
+            if (readBlock(Stream, BlockOrCode, N)) {
+              if (addNamedType(I, N)) continue;
+            }
+            return false;
+          }
+          default:
+            errs() << "Invalid subblock type\n";
+            return false;
+        }
+        if (!Stream.SkipBlock()) return false;
+        continue;
+      case Cursor::Record:
+        break;
+    }
+    if (!readRecord(Stream, BlockOrCode, I)) return false;
+  }
+}
+
+ClangDocBinaryReader::Cursor ClangDocBinaryReader::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:
+        // We found a record.
+        BlockOrRecordID = Code;
+        return Cursor::Record;
+    }
+  }
+  llvm_unreachable("Premature stream end.");
+}
+
+bool ClangDocBinaryReader::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 ClangDocBinaryReader::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;
+}
+
+bool ClangDocBinaryReader::readBitstreamToInfoSet(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()) {
+      case llvm::bitc::BLOCKINFO_BLOCK_ID: 
+        if (readBlockInfoBlock(Stream)) continue;
+        return false;
+      case NAMESPACE_BLOCK_ID: {
+        NamespaceInfo N;
+        if (readBlock(Stream, ID, N)) {
+          IS->insert(N.FullyQualifiedName, N);
+          continue;
+        }
+        return false;
+      }
+      case NONDEF_BLOCK_ID: {
+        NonDefInfo N;
+        if (readBlock(Stream, ID, N)) {
+          IS->insert(N.FullyQualifiedName, N);
+          continue;
+        }
+        return false;
+      }
+      case RECORD_BLOCK_ID: {
+        RecordInfo N;
+        if (readBlock(Stream, ID, N)) {
+          IS->insert(N.FullyQualifiedName, N);
+          continue;
+        }
+        return false;
+      }
+      case ENUM_BLOCK_ID: {
+        EnumInfo N;
+        if (readBlock(Stream, ID, N)) {
+          IS->insert(N.FullyQualifiedName, N);
+          continue;
+        }
+        return false;
+      }
+      case FUNCTION_BLOCK_ID: {
+        FunctionInfo N;
+        if (readBlock(Stream, ID, N)) {
+          IS->insert(N.MangledName, N);
+          continue;
+        }
+        return false;
+      }
+      // NamedType and Comment blocks should not appear at the top level
+      case NAMED_TYPE_BLOCK_ID:
+      case COMMENT_BLOCK_ID:
+        errs() << "Invalid top level block.\n";
+        return false;
+      default:
+        if (!Stream.SkipBlock()) return false;
+        continue;
+    }
+  }
+  return true;
+}
+
+std::unique_ptr<InfoSet> ClangDocBinaryReader::readBitstream(SmallString<2048> Bits) {
+  std::unique_ptr<InfoSet> IS = llvm::make_unique<InfoSet>();
+  if (readBitstreamToInfoSet(Bits, IS))
+    return std::move(IS);
+  return nullptr;
+}
+
 }  // namespace doc
 }  // namespace clang
Index: clang-doc/CMakeLists.txt
===================================================================
--- clang-doc/CMakeLists.txt
+++ clang-doc/CMakeLists.txt
@@ -6,6 +6,8 @@
   ClangDoc.cpp
   ClangDocMapper.cpp
   ClangDocBinary.cpp
+  ClangDocReducer.cpp
+  ClangDocRepresentation.cpp
 
   LINK_LIBS
   clangAnalysis
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to