Implementation of pre-compiled headers (PCH) based on lazy
de-serialization of abstract syntax trees.

PCH support serializes the contents of the abstract syntax tree (AST)
to a bitstream. When the PCH file is read, declarations are serialized
as-needed. For example, a declaration of a variable "x" will be
deserialized only when its VarDecl can be found by a client, e.g.,
based on name lookup for "x" or traversing the entire contents of the
owner of "x".

This commit provides the framework for serialization and (lazy)
deserialization, along with support for variable and typedef
declarations (along with several kinds of types). More
declarations/types, along with important auxiliary structures (source
manager, preprocessor, etc.), will follow.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68732 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/tools/clang-cc/ASTConsumers.h b/tools/clang-cc/ASTConsumers.h
index 970dfec..e7bc962 100644
--- a/tools/clang-cc/ASTConsumers.h
+++ b/tools/clang-cc/ASTConsumers.h
@@ -68,6 +68,11 @@
                                  const std::string& EmitDir,
                                  Diagnostic &Diags);
   
+ASTConsumer *CreatePCHGenerator(Diagnostic &Diags,
+                                const LangOptions &Features,
+                                const std::string& InFile,
+                                const std::string& OutFile);
+
 ASTConsumer *CreateBlockRewriter(const std::string& InFile,
                                  const std::string& OutFile,
                                  Diagnostic &Diags,
diff --git a/tools/clang-cc/GeneratePCH.cpp b/tools/clang-cc/GeneratePCH.cpp
new file mode 100644
index 0000000..a233348
--- /dev/null
+++ b/tools/clang-cc/GeneratePCH.cpp
@@ -0,0 +1,78 @@
+//===--- GeneratePCH.cpp - AST Consumer for PCH Generation ------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the CreatePCHGenerate function, which creates an
+//  ASTConsume that generates a PCH file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Frontend/PCHWriter.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTConsumer.h"
+#include "llvm/Bitcode/BitstreamWriter.h"
+#include "llvm/System/Path.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Streams.h"
+#include <string>
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+  class VISIBILITY_HIDDEN PCHGenerator : public ASTConsumer {
+    Diagnostic &Diags;
+    std::string OutFile;
+
+  public:
+    explicit PCHGenerator(Diagnostic &Diags, const std::string &OutFile)
+      : Diags(Diags), OutFile(OutFile) { }
+
+    virtual void HandleTranslationUnit(ASTContext &Ctx);
+  };
+}
+
+void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) {
+  if (Diags.hasErrorOccurred())
+    return;
+
+ // Write the PCH contents into a buffer
+  std::vector<unsigned char> Buffer;
+  BitstreamWriter Stream(Buffer);
+  PCHWriter Writer(Stream);
+
+  // Emit the PCH file
+  Writer.WritePCH(Ctx);
+
+  // Open up the PCH file.
+  std::string ErrMsg;
+  llvm::raw_fd_ostream Out(OutFile.c_str(), true, ErrMsg);
+  
+  if (!ErrMsg.empty()) {
+    llvm::errs() << "PCH error: " << ErrMsg << "\n";
+    return;
+  }
+
+  // Write the generated bitstream to "Out".
+  Out.write((char *)&Buffer.front(), Buffer.size());
+
+  // Make sure it hits disk now.
+  Out.flush();
+}
+
+namespace clang {
+
+ASTConsumer *CreatePCHGenerator(Diagnostic &Diags,
+                                const LangOptions &Features,
+                                const std::string& InFile,
+                                const std::string& OutFile) {
+  return new PCHGenerator(Diags, OutFile);
+}
+
+}
diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp
index b10323a..f8c44d6 100644
--- a/tools/clang-cc/clang-cc.cpp
+++ b/tools/clang-cc/clang-cc.cpp
@@ -29,6 +29,7 @@
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/InitHeaderSearch.h"
 #include "clang/Frontend/PathDiagnosticClients.h"
+#include "clang/Frontend/PCHReader.h"
 #include "clang/Frontend/TextDiagnosticBuffer.h"
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Analysis/PathDiagnostic.h"
@@ -194,6 +195,7 @@
   DumpRawTokens,                // Dump out raw tokens.
   RunAnalysis,                  // Run one or more source code analyses. 
   GeneratePTH,                  // Generate pre-tokenized header.
+  GeneratePCH,                  // Generate pre-compiled header.
   InheritanceView               // View C++ inheritance for a specified class.
 };
 
@@ -229,6 +231,8 @@
                         "Print DeclContexts and their Decls"),
              clEnumValN(GeneratePTH, "emit-pth",
                         "Generate pre-tokenized header file"),
+             clEnumValN(GeneratePCH, "emit-pch",
+                        "Generate pre-compiled header file"),
              clEnumValN(TestSerialization, "test-pickling",
                         "Run prototype serialization code"),
              clEnumValN(EmitAssembly, "S",
@@ -975,6 +979,10 @@
 ImplicitIncludePTH("include-pth", llvm::cl::value_desc("file"),
                    llvm::cl::desc("Include file before parsing"));
 
+static llvm::cl::opt<std::string>
+ImplicitIncludePCH("include-pch", llvm::cl::value_desc("file"),
+                   llvm::cl::desc("Include precompiled header file"));
+
 // Append a #define line to Buf for Macro.  Macro should be of the form XXX,
 // in which case we emit "#define XXX 1" or "XXX=Y z W" in which case we emit
 // "#define XXX Y z W".  To get a #define with no value, use "XXX=".
@@ -1516,6 +1524,9 @@
     // FIXME: Allow user to tailor where the file is written.
     return CreateASTSerializer(InFile, OutputFile, Diag);
     
+  case GeneratePCH:
+    return CreatePCHGenerator(Diag, LangOpts, InFile, OutputFile);    
+
   case RewriteObjC:
     return CreateCodeRewriterTest(InFile, OutputFile, Diag, LangOpts);
 
@@ -1683,6 +1694,18 @@
                                       PP.getSelectorTable(),
                                       /* FreeMemory = */ !DisableFree));
     
+    if (!ImplicitIncludePCH.empty()) {
+      // The user has asked us to include a precompiled header. Load
+      // the precompiled header into the AST context.
+      llvm::OwningPtr<PCHReader> Reader(
+                                   new clang::PCHReader(*ContextOwner.get()));
+      if (Reader->ReadPCH(ImplicitIncludePCH))
+        return;
+
+      llvm::OwningPtr<ExternalASTSource> Source(Reader.take());
+      ContextOwner->setExternalSource(Source);
+    }
+
     ParseAST(PP, Consumer.get(), *ContextOwner.get(), Stats);
     
     if (FixItRewrite)