r305890 - Moved code hanlding precompiled preamble out of the ASTUnit.

Ilya Biryukov via cfe-commits cfe-commits at lists.llvm.org
Wed Jun 21 03:24:59 PDT 2017


Author: ibiryukov
Date: Wed Jun 21 05:24:58 2017
New Revision: 305890

URL: http://llvm.org/viewvc/llvm-project?rev=305890&view=rev
Log:
Moved code hanlding precompiled preamble out of the ASTUnit.

Reviewers: bkramer, krasimir, arphaman, akyrtzi, klimek

Reviewed By: klimek

Subscribers: mgorny, klimek, cfe-commits

Differential Revision: https://reviews.llvm.org/D34287

Added:
    cfe/trunk/include/clang/Frontend/PrecompiledPreamble.h
    cfe/trunk/lib/Frontend/PrecompiledPreamble.cpp
Modified:
    cfe/trunk/include/clang/Frontend/ASTUnit.h
    cfe/trunk/lib/Frontend/ASTUnit.cpp
    cfe/trunk/lib/Frontend/CMakeLists.txt

Modified: cfe/trunk/include/clang/Frontend/ASTUnit.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/ASTUnit.h?rev=305890&r1=305889&r2=305890&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/ASTUnit.h (original)
+++ cfe/trunk/include/clang/Frontend/ASTUnit.h Wed Jun 21 05:24:58 2017
@@ -25,6 +25,7 @@
 #include "clang/Lex/PreprocessingRecord.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Serialization/ASTBitCodes.h"
+#include "clang/Frontend/PrecompiledPreamble.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
@@ -199,103 +200,15 @@ private:
   /// of that loading. It must be cleared when preamble is recreated.
   llvm::StringMap<SourceLocation> PreambleSrcLocCache;
 
-public:
-  class PreambleData {
-    const FileEntry *File;
-    std::vector<char> Buffer;
-    mutable unsigned NumLines;
-    
-  public:
-    PreambleData() : File(nullptr), NumLines(0) { }
-    
-    void assign(const FileEntry *F, const char *begin, const char *end) {
-      File = F;
-      Buffer.assign(begin, end);
-      NumLines = 0;
-    }
-
-    void clear() { Buffer.clear(); File = nullptr; NumLines = 0; }
-
-    size_t size() const { return Buffer.size(); }
-    bool empty() const { return Buffer.empty(); }
-
-    const char *getBufferStart() const { return &Buffer[0]; }
-
-    unsigned getNumLines() const {
-      if (NumLines)
-        return NumLines;
-      countLines();
-      return NumLines;
-    }
-
-    SourceRange getSourceRange(const SourceManager &SM) const {
-      SourceLocation FileLoc = SM.getLocForStartOfFile(SM.getPreambleFileID());
-      return SourceRange(FileLoc, FileLoc.getLocWithOffset(size()-1));
-    }
-
-  private:
-    void countLines() const;
-  };
-
-  const PreambleData &getPreambleData() const {
-    return Preamble;
-  }
-
-  /// Data used to determine if a file used in the preamble has been changed.
-  struct PreambleFileHash {
-    /// All files have size set.
-    off_t Size;
-
-    /// Modification time is set for files that are on disk.  For memory
-    /// buffers it is zero.
-    time_t ModTime;
-
-    /// Memory buffers have MD5 instead of modification time.  We don't
-    /// compute MD5 for on-disk files because we hope that modification time is
-    /// enough to tell if the file was changed.
-    llvm::MD5::MD5Result MD5;
-
-    static PreambleFileHash createForFile(off_t Size, time_t ModTime);
-    static PreambleFileHash
-    createForMemoryBuffer(const llvm::MemoryBuffer *Buffer);
-
-    friend bool operator==(const PreambleFileHash &LHS,
-                           const PreambleFileHash &RHS);
-
-    friend bool operator!=(const PreambleFileHash &LHS,
-                           const PreambleFileHash &RHS) {
-      return !(LHS == RHS);
-    }
-  };
-
 private:
-  /// \brief The contents of the preamble that has been precompiled to
-  /// \c PreambleFile.
-  PreambleData Preamble;
-
-  /// \brief Whether the preamble ends at the start of a new line.
-  /// 
-  /// Used to inform the lexer as to whether it's starting at the beginning of
-  /// a line after skipping the preamble.
-  bool PreambleEndsAtStartOfLine;
-
-  /// \brief Keeps track of the files that were used when computing the 
-  /// preamble, with both their buffer size and their modification time.
-  ///
-  /// If any of the files have changed from one compile to the next,
-  /// the preamble must be thrown away.
-  llvm::StringMap<PreambleFileHash> FilesInPreamble;
+  /// The contents of the preamble.
+  llvm::Optional<PrecompiledPreamble> Preamble;
 
   /// \brief When non-NULL, this is the buffer used to store the contents of
   /// the main file when it has been padded for use with the precompiled
   /// preamble.
   std::unique_ptr<llvm::MemoryBuffer> SavedMainFileBuffer;
 
-  /// \brief When non-NULL, this is the buffer used to store the
-  /// contents of the preamble when it has been padded to build the
-  /// precompiled preamble.
-  std::unique_ptr<llvm::MemoryBuffer> PreambleBuffer;
-
   /// \brief The number of warnings that occurred while parsing the preamble.
   ///
   /// This value will be used to restore the state of the \c DiagnosticsEngine
@@ -438,21 +351,6 @@ private:
              std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer,
              IntrusiveRefCntPtr<vfs::FileSystem> VFS);
 
-  struct ComputedPreamble {
-    llvm::MemoryBuffer *Buffer;
-    std::unique_ptr<llvm::MemoryBuffer> Owner;
-    unsigned Size;
-    bool PreambleEndsAtStartOfLine;
-    ComputedPreamble(llvm::MemoryBuffer *Buffer,
-                     std::unique_ptr<llvm::MemoryBuffer> Owner, unsigned Size,
-                     bool PreambleEndsAtStartOfLine)
-        : Buffer(Buffer), Owner(std::move(Owner)), Size(Size),
-          PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {}
-  };
-  ComputedPreamble ComputePreamble(CompilerInvocation &Invocation,
-                                   unsigned MaxLines,
-                                   IntrusiveRefCntPtr<vfs::FileSystem> VFS);
-
   std::unique_ptr<llvm::MemoryBuffer> getMainBufferWithPrecompiledPreamble(
       std::shared_ptr<PCHContainerOperations> PCHContainerOps,
       const CompilerInvocation &PreambleInvocationIn,
@@ -607,12 +505,6 @@ public:
   void findFileRegionDecls(FileID File, unsigned Offset, unsigned Length,
                            SmallVectorImpl<Decl *> &Decls);
 
-  /// \brief Add a new top-level declaration, identified by its ID in
-  /// the precompiled preamble.
-  void addTopLevelDeclFromPreamble(serialization::DeclID D) {
-    TopLevelDeclsInPreamble.push_back(D);
-  }
-
   /// \brief Retrieve a reference to the current top-level name hash value.
   ///
   /// Note: This is used internally by the top-level tracking action

Added: cfe/trunk/include/clang/Frontend/PrecompiledPreamble.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/PrecompiledPreamble.h?rev=305890&view=auto
==============================================================================
--- cfe/trunk/include/clang/Frontend/PrecompiledPreamble.h (added)
+++ cfe/trunk/include/clang/Frontend/PrecompiledPreamble.h Wed Jun 21 05:24:58 2017
@@ -0,0 +1,248 @@
+//===--- PrecompiledPreamble.h - Build precompiled preambles ----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Helper class to build precompiled preamble.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_FRONTEND_PRECOMPILED_PREAMBLE_H
+#define LLVM_CLANG_FRONTEND_PRECOMPILED_PREAMBLE_H
+
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/MD5.h"
+#include <memory>
+#include <system_error>
+#include <type_traits>
+
+namespace llvm {
+class MemoryBuffer;
+}
+
+namespace clang {
+namespace vfs {
+class FileSystem;
+}
+
+class CompilerInstance;
+class CompilerInvocation;
+class DeclGroupRef;
+class PCHContainerOperations;
+
+/// A size of the preamble and a flag required by
+/// PreprocessorOptions::PrecompiledPreambleBytes.
+struct PreambleBounds {
+  PreambleBounds(unsigned Size, bool PreambleEndsAtStartOfLine)
+      : Size(Size), PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {}
+
+  /// \brief Size of the preamble in bytes.
+  unsigned Size;
+  /// \brief Whether the preamble ends at the start of a new line.
+  ///
+  /// Used to inform the lexer as to whether it's starting at the beginning of
+  /// a line after skipping the preamble.
+  bool PreambleEndsAtStartOfLine;
+};
+
+/// \brief Runs lexer to compute suggested preamble bounds.
+PreambleBounds ComputePreambleBounds(const LangOptions &LangOpts,
+                                     llvm::MemoryBuffer *Buffer,
+                                     unsigned MaxLines);
+
+class PreambleCallbacks;
+
+/// A class holding a PCH and all information to check whether it is valid to
+/// reuse the PCH for the subsequent runs. Use BuildPreamble to create PCH and
+/// CanReusePreamble + AddImplicitPreamble to make use of it.
+class PrecompiledPreamble {
+  class TempPCHFile;
+  struct PreambleFileHash;
+
+public:
+  /// \brief Try to build PrecompiledPreamble for \p Invocation. See
+  /// BuildPreambleError for possible error codes.
+  ///
+  /// \param Invocation Original CompilerInvocation with options to compile the
+  /// file.
+  ///
+  /// \param MainFileBuffer Buffer with the contents of the main file.
+  ///
+  /// \param Bounds Bounds of the preamble, result of calling
+  /// ComputePreambleBounds.
+  ///
+  /// \param Diagnostics Diagnostics engine to be used while building the
+  /// preamble.
+  ///
+  /// \param VFS An instance of vfs::FileSystem to be used for file
+  /// accesses.
+  ///
+  /// \param PCHContainerOps An instance of PCHContainerOperations.
+  ///
+  /// \param Callbacks A set of callbacks to be executed when building
+  /// the preamble.
+  static llvm::ErrorOr<PrecompiledPreamble>
+  Build(const CompilerInvocation &Invocation,
+        const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,
+        DiagnosticsEngine &Diagnostics, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
+        std::shared_ptr<PCHContainerOperations> PCHContainerOps,
+        PreambleCallbacks &Callbacks);
+
+  PrecompiledPreamble(PrecompiledPreamble &&) = default;
+  PrecompiledPreamble &operator=(PrecompiledPreamble &&) = default;
+
+  /// PreambleBounds used to build the preamble
+  PreambleBounds getBounds() const;
+
+  /// Check whether PrecompiledPreamble can be reused for the new contents(\p
+  /// MainFileBuffer) of the main file.
+  bool CanReuse(const CompilerInvocation &Invocation,
+                const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,
+                vfs::FileSystem *VFS) const;
+
+  /// Changes options inside \p CI to use PCH from this preamble. Also remaps
+  /// main file to \p MainFileBuffer.
+  void AddImplicitPreamble(CompilerInvocation &CI,
+                           llvm::MemoryBuffer *MainFileBuffer) const;
+
+private:
+  PrecompiledPreamble(TempPCHFile PCHFile, std::vector<char> PreambleBytes,
+                      bool PreambleEndsAtStartOfLine,
+                      llvm::StringMap<PreambleFileHash> FilesInPreamble);
+
+  /// A temp file that would be deleted on destructor call. If destructor is not
+  /// called for any reason, the file will be deleted at static objects'
+  /// destruction.
+  /// An assertion will fire if two TempPCHFiles are created with the same name,
+  /// so it's not intended to be used outside preamble-handling.
+  class TempPCHFile {
+  public:
+    // A main method used to construct TempPCHFile.
+    static llvm::ErrorOr<TempPCHFile> CreateNewPreamblePCHFile();
+
+    /// Call llvm::sys::fs::createTemporaryFile to create a new temporary file.
+    static llvm::ErrorOr<TempPCHFile> createInSystemTempDir(const Twine &Prefix,
+                                                            StringRef Suffix);
+    /// Create a new instance of TemporaryFile for file at \p Path. Use with
+    /// extreme caution, there's an assertion checking that there's only a
+    /// single instance of TempPCHFile alive for each path.
+    static llvm::ErrorOr<TempPCHFile> createFromCustomPath(const Twine &Path);
+
+  private:
+    TempPCHFile(std::string FilePath);
+
+  public:
+    TempPCHFile(TempPCHFile &&Other);
+    TempPCHFile &operator=(TempPCHFile &&Other);
+
+    TempPCHFile(const TempPCHFile &) = delete;
+    ~TempPCHFile();
+
+    /// A path where temporary file is stored.
+    llvm::StringRef getFilePath() const;
+
+  private:
+    void RemoveFileIfPresent();
+
+  private:
+    llvm::Optional<std::string> FilePath;
+  };
+
+  /// Data used to determine if a file used in the preamble has been changed.
+  struct PreambleFileHash {
+    /// All files have size set.
+    off_t Size = 0;
+
+    /// Modification time is set for files that are on disk.  For memory
+    /// buffers it is zero.
+    time_t ModTime = 0;
+
+    /// Memory buffers have MD5 instead of modification time.  We don't
+    /// compute MD5 for on-disk files because we hope that modification time is
+    /// enough to tell if the file was changed.
+    llvm::MD5::MD5Result MD5 = {};
+
+    static PreambleFileHash createForFile(off_t Size, time_t ModTime);
+    static PreambleFileHash
+    createForMemoryBuffer(const llvm::MemoryBuffer *Buffer);
+
+    friend bool operator==(const PreambleFileHash &LHS,
+                           const PreambleFileHash &RHS) {
+      return LHS.Size == RHS.Size && LHS.ModTime == RHS.ModTime &&
+             LHS.MD5 == RHS.MD5;
+    }
+    friend bool operator!=(const PreambleFileHash &LHS,
+                           const PreambleFileHash &RHS) {
+      return !(LHS == RHS);
+    }
+  };
+
+  /// Manages the lifetime of temporary file that stores a PCH.
+  TempPCHFile PCHFile;
+  /// Keeps track of the files that were used when computing the
+  /// preamble, with both their buffer size and their modification time.
+  ///
+  /// If any of the files have changed from one compile to the next,
+  /// the preamble must be thrown away.
+  llvm::StringMap<PreambleFileHash> FilesInPreamble;
+  /// The contents of the file that was used to precompile the preamble. Only
+  /// contains first PreambleBounds::Size bytes. Used to compare if the relevant
+  /// part of the file has not changed, so that preamble can be reused.
+  std::vector<char> PreambleBytes;
+  /// See PreambleBounds::PreambleEndsAtStartOfLine
+  bool PreambleEndsAtStartOfLine;
+};
+
+/// A set of callbacks to gather useful information while building a preamble.
+class PreambleCallbacks {
+public:
+  virtual ~PreambleCallbacks() = default;
+
+  /// Called after FrontendAction::Execute(), but before
+  /// FrontendAction::EndSourceFile(). Can be used to transfer ownership of
+  /// various CompilerInstance fields before they are destroyed.
+  virtual void AfterExecute(CompilerInstance &CI);
+  /// Called after PCH has been emitted. \p Writer may be used to retrieve
+  /// information about AST, serialized in PCH.
+  virtual void AfterPCHEmitted(ASTWriter &Writer);
+  /// Called for each TopLevelDecl.
+  /// NOTE: To allow more flexibility a custom ASTConsumer could probably be
+  /// used instead, but having only this method allows a simpler API.
+  virtual void HandleTopLevelDecl(DeclGroupRef DG);
+  /// Called for each macro defined in the Preamble.
+  /// NOTE: To allow more flexibility a custom PPCallbacks could probably be
+  /// used instead, but having only this method allows a simpler API.
+  virtual void HandleMacroDefined(const Token &MacroNameTok,
+                                  const MacroDirective *MD);
+};
+
+enum class BuildPreambleError {
+  PreambleIsEmpty = 1,
+  CouldntCreateTempFile,
+  CouldntCreateTargetInfo,
+  CouldntCreateVFSOverlay,
+  BeginSourceFileFailed,
+  CouldntEmitPCH
+};
+
+class BuildPreambleErrorCategory final : public std::error_category {
+public:
+  const char *name() const noexcept override;
+  std::string message(int condition) const override;
+};
+
+std::error_code make_error_code(BuildPreambleError Error);
+} // namespace clang
+
+namespace std {
+template <>
+struct is_error_code_enum<clang::BuildPreambleError> : std::true_type {};
+} // namespace std
+
+#endif

Modified: cfe/trunk/lib/Frontend/ASTUnit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/ASTUnit.cpp?rev=305890&r1=305889&r2=305890&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/ASTUnit.cpp (original)
+++ cfe/trunk/lib/Frontend/ASTUnit.cpp Wed Jun 21 05:24:58 2017
@@ -79,17 +79,6 @@ namespace {
       }
     }
   };
-  
-  struct OnDiskData {
-    /// \brief The file in which the precompiled preamble is stored.
-    std::string PreambleFile;
-
-    /// \brief Erase the preamble file.
-    void CleanPreambleFile();
-
-    /// \brief Erase temporary files and the preamble file.
-    void Cleanup();
-  };
 
   template <class T>
   std::unique_ptr<T> valueOrNull(llvm::ErrorOr<std::unique_ptr<T>> Val) {
@@ -105,81 +94,68 @@ namespace {
     Output = std::move(*Val);
     return true;
   }
-}
-
-static llvm::sys::SmartMutex<false> &getOnDiskMutex() {
-  static llvm::sys::SmartMutex<false> M(/* recursive = */ true);
-  return M;
-}
 
-static void cleanupOnDiskMapAtExit();
+/// \brief Get a source buffer for \p MainFilePath, handling all file-to-file
+/// and file-to-buffer remappings inside \p Invocation.
+static std::unique_ptr<llvm::MemoryBuffer>
+getBufferForFileHandlingRemapping(const CompilerInvocation &Invocation,
+                                  vfs::FileSystem *VFS,
+                                  StringRef FilePath) {
+  const auto &PreprocessorOpts = Invocation.getPreprocessorOpts();
+
+  // Try to determine if the main file has been remapped, either from the
+  // command line (to another file) or directly through the compiler
+  // invocation (to a memory buffer).
+  llvm::MemoryBuffer *Buffer = nullptr;
+  std::unique_ptr<llvm::MemoryBuffer> BufferOwner;
+  auto FileStatus = VFS->status(FilePath);
+  if (FileStatus) {
+    llvm::sys::fs::UniqueID MainFileID = FileStatus->getUniqueID();
 
-typedef llvm::DenseMap<const ASTUnit *,
-                       std::unique_ptr<OnDiskData>> OnDiskDataMap;
-static OnDiskDataMap &getOnDiskDataMap() {
-  static OnDiskDataMap M;
-  static bool hasRegisteredAtExit = false;
-  if (!hasRegisteredAtExit) {
-    hasRegisteredAtExit = true;
-    atexit(cleanupOnDiskMapAtExit);
-  }
-  return M;
-}
+    // Check whether there is a file-file remapping of the main file
+    for (const auto &RF : PreprocessorOpts.RemappedFiles) {
+      std::string MPath(RF.first);
+      auto MPathStatus = VFS->status(MPath);
+      if (MPathStatus) {
+        llvm::sys::fs::UniqueID MID = MPathStatus->getUniqueID();
+        if (MainFileID == MID) {
+          // We found a remapping. Try to load the resulting, remapped source.
+          BufferOwner = valueOrNull(VFS->getBufferForFile(RF.second));
+          if (!BufferOwner)
+            return nullptr;
+        }
+      }
+    }
 
-static void cleanupOnDiskMapAtExit() {
-  // Use the mutex because there can be an alive thread destroying an ASTUnit.
-  llvm::MutexGuard Guard(getOnDiskMutex());
-  for (const auto &I : getOnDiskDataMap()) {
-    // We don't worry about freeing the memory associated with OnDiskDataMap.
-    // All we care about is erasing stale files.
-    I.second->Cleanup();
+    // Check whether there is a file-buffer remapping. It supercedes the
+    // file-file remapping.
+    for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) {
+      std::string MPath(RB.first);
+      auto MPathStatus = VFS->status(MPath);
+      if (MPathStatus) {
+        llvm::sys::fs::UniqueID MID = MPathStatus->getUniqueID();
+        if (MainFileID == MID) {
+          // We found a remapping.
+          BufferOwner.reset();
+          Buffer = const_cast<llvm::MemoryBuffer *>(RB.second);
+        }
+      }
+    }
   }
-}
-
-static OnDiskData &getOnDiskData(const ASTUnit *AU) {
-  // We require the mutex since we are modifying the structure of the
-  // DenseMap.
-  llvm::MutexGuard Guard(getOnDiskMutex());
-  OnDiskDataMap &M = getOnDiskDataMap();
-  auto &D = M[AU];
-  if (!D)
-    D = llvm::make_unique<OnDiskData>();
-  return *D;
-}
 
-static void erasePreambleFile(const ASTUnit *AU) {
-  getOnDiskData(AU).CleanPreambleFile();
-}
-
-static void removeOnDiskEntry(const ASTUnit *AU) {
-  // We require the mutex since we are modifying the structure of the
-  // DenseMap.
-  llvm::MutexGuard Guard(getOnDiskMutex());
-  OnDiskDataMap &M = getOnDiskDataMap();
-  OnDiskDataMap::iterator I = M.find(AU);
-  if (I != M.end()) {
-    I->second->Cleanup();
-    M.erase(I);
+  // If the main source file was not remapped, load it now.
+  if (!Buffer && !BufferOwner) {
+    BufferOwner = valueOrNull(VFS->getBufferForFile(FilePath));
+    if (!BufferOwner)
+      return nullptr;
   }
-}
-
-static void setPreambleFile(const ASTUnit *AU, StringRef preambleFile) {
-  getOnDiskData(AU).PreambleFile = preambleFile;
-}
-
-static const std::string &getPreambleFile(const ASTUnit *AU) {
-  return getOnDiskData(AU).PreambleFile;  
-}
 
-void OnDiskData::CleanPreambleFile() {
-  if (!PreambleFile.empty()) {
-    llvm::sys::fs::remove(PreambleFile);
-    PreambleFile.clear();
-  }
+  if (BufferOwner)
+    return BufferOwner;
+  if (!Buffer)
+    return nullptr;
+  return llvm::MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(), FilePath);
 }
-
-void OnDiskData::Cleanup() {
-  CleanPreambleFile();
 }
 
 struct ASTUnit::ASTWriterData {
@@ -233,9 +209,6 @@ ASTUnit::~ASTUnit() {
 
   clearFileLevelDecls();
 
-  // Clean up the temporary files and the preamble file.
-  removeOnDiskEntry(this);
-
   // Free the buffers associated with remapped files. We are required to
   // perform this operation here because we explicitly request that the
   // compiler instance *not* free these buffers for each invocation of the
@@ -575,16 +548,24 @@ private:
 
   /// \brief Diagnostic consumer that saves each diagnostic it is given.
 class StoredDiagnosticConsumer : public DiagnosticConsumer {
-  SmallVectorImpl<StoredDiagnostic> &StoredDiags;
+  SmallVectorImpl<StoredDiagnostic> *StoredDiags;
+  SmallVectorImpl<ASTUnit::StandaloneDiagnostic> *StandaloneDiags;
+  const LangOptions *LangOpts;
   SourceManager *SourceMgr;
 
 public:
-  explicit StoredDiagnosticConsumer(
-                          SmallVectorImpl<StoredDiagnostic> &StoredDiags)
-    : StoredDiags(StoredDiags), SourceMgr(nullptr) {}
+  StoredDiagnosticConsumer(
+      SmallVectorImpl<StoredDiagnostic> *StoredDiags,
+      SmallVectorImpl<ASTUnit::StandaloneDiagnostic> *StandaloneDiags)
+      : StoredDiags(StoredDiags), StandaloneDiags(StandaloneDiags),
+        LangOpts(nullptr), SourceMgr(nullptr) {
+    assert((StoredDiags || StandaloneDiags) &&
+           "No output collections were passed to StoredDiagnosticConsumer.");
+  }
 
   void BeginSourceFile(const LangOptions &LangOpts,
                        const Preprocessor *PP = nullptr) override {
+    this->LangOpts = &LangOpts;
     if (PP)
       SourceMgr = &PP->getSourceManager();
   }
@@ -603,8 +584,9 @@ class CaptureDroppedDiagnostics {
 
 public:
   CaptureDroppedDiagnostics(bool RequestCapture, DiagnosticsEngine &Diags,
-                          SmallVectorImpl<StoredDiagnostic> &StoredDiags)
-    : Diags(Diags), Client(StoredDiags), PreviousClient(nullptr)
+                            SmallVectorImpl<StoredDiagnostic> *StoredDiags,
+                            SmallVectorImpl<ASTUnit::StandaloneDiagnostic> *StandaloneDiags)
+      : Diags(Diags), Client(StoredDiags, StandaloneDiags), PreviousClient(nullptr)
   {
     if (RequestCapture || Diags.getClient() == nullptr) {
       OwningPreviousClient = Diags.takeClient();
@@ -621,16 +603,35 @@ public:
 
 } // anonymous namespace
 
+static ASTUnit::StandaloneDiagnostic
+makeStandaloneDiagnostic(const LangOptions &LangOpts,
+                         const StoredDiagnostic &InDiag);
+
 void StoredDiagnosticConsumer::HandleDiagnostic(DiagnosticsEngine::Level Level,
-                                              const Diagnostic &Info) {
+                                                const Diagnostic &Info) {
   // Default implementation (Warnings/errors count).
   DiagnosticConsumer::HandleDiagnostic(Level, Info);
 
   // Only record the diagnostic if it's part of the source manager we know
   // about. This effectively drops diagnostics from modules we're building.
   // FIXME: In the long run, ee don't want to drop source managers from modules.
-  if (!Info.hasSourceManager() || &Info.getSourceManager() == SourceMgr)
-    StoredDiags.emplace_back(Level, Info);
+  if (!Info.hasSourceManager() || &Info.getSourceManager() == SourceMgr) {
+    StoredDiagnostic *ResultDiag = nullptr;
+    if (StoredDiags) {
+      StoredDiags->emplace_back(Level, Info);
+      ResultDiag = &StoredDiags->back();
+    }
+
+    if (StandaloneDiags) {
+      llvm::Optional<StoredDiagnostic> StoredDiag = llvm::None;
+      if (!ResultDiag) {
+        StoredDiag.emplace(Level, Info);
+        ResultDiag = StoredDiag.getPointer();
+      }
+      StandaloneDiags->push_back(
+          makeStandaloneDiagnostic(*LangOpts, *ResultDiag));
+    }
+  }
 }
 
 IntrusiveRefCntPtr<ASTReader> ASTUnit::getASTReader() const {
@@ -665,7 +666,7 @@ void ASTUnit::ConfigureDiags(IntrusiveRe
                              ASTUnit &AST, bool CaptureDiagnostics) {
   assert(Diags.get() && "no DiagnosticsEngine was provided");
   if (CaptureDiagnostics)
-    Diags->setClient(new StoredDiagnosticConsumer(AST.StoredDiagnostics));
+    Diags->setClient(new StoredDiagnosticConsumer(&AST.StoredDiagnostics, nullptr));
 }
 
 std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
@@ -780,6 +781,11 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFr
 
 namespace {
 
+/// \brief Add the given macro to the hash of all top-level entities.
+void AddDefinedMacroToHash(const Token &MacroNameTok, unsigned &Hash) {
+  Hash = llvm::HashString(MacroNameTok.getIdentifierInfo()->getName(), Hash);
+}
+
 /// \brief Preprocessor callback class that updates a hash value with the names 
 /// of all macros that have been defined by the translation unit.
 class MacroDefinitionTrackerPPCallbacks : public PPCallbacks {
@@ -790,7 +796,7 @@ public:
 
   void MacroDefined(const Token &MacroNameTok,
                     const MacroDirective *MD) override {
-    Hash = llvm::HashString(MacroNameTok.getIdentifierInfo()->getName(), Hash);
+    AddDefinedMacroToHash(MacroNameTok, Hash);
   }
 };
 
@@ -916,45 +922,30 @@ public:
   }
 };
 
-class PrecompilePreambleAction : public ASTFrontendAction {
-  ASTUnit &Unit;
-  bool HasEmittedPreamblePCH;
-
+class ASTUnitPreambleCallbacks : public PreambleCallbacks {
 public:
-  explicit PrecompilePreambleAction(ASTUnit &Unit)
-      : Unit(Unit), HasEmittedPreamblePCH(false) {}
+  ASTUnitPreambleCallbacks(llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags)
+      : StoredDiags(StoredDiags) {}
 
-  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
-                                                 StringRef InFile) override;
-  bool hasEmittedPreamblePCH() const { return HasEmittedPreamblePCH; }
-  void setHasEmittedPreamblePCH() { HasEmittedPreamblePCH = true; }
-  bool shouldEraseOutputFiles() override { return !hasEmittedPreamblePCH(); }
+  unsigned getHash() const { return Hash; }
 
-  bool hasCodeCompletionSupport() const override { return false; }
-  bool hasASTFileSupport() const override { return false; }
-  TranslationUnitKind getTranslationUnitKind() override { return TU_Prefix; }
-};
+  std::vector<Decl *> takeTopLevelDecls() { return std::move(TopLevelDecls); }
 
-class PrecompilePreambleConsumer : public PCHGenerator {
-  ASTUnit &Unit;
-  unsigned &Hash;
-  std::vector<Decl *> TopLevelDecls;
-  PrecompilePreambleAction *Action;
-  std::unique_ptr<raw_ostream> Out;
+  std::vector<serialization::DeclID> takeTopLevelDeclIDs() {
+    return std::move(TopLevelDeclIDs);
+  }
 
-public:
-  PrecompilePreambleConsumer(ASTUnit &Unit, PrecompilePreambleAction *Action,
-                             const Preprocessor &PP, StringRef isysroot,
-                             std::unique_ptr<raw_ostream> Out)
-      : PCHGenerator(PP, "", isysroot, std::make_shared<PCHBuffer>(),
-                     ArrayRef<std::shared_ptr<ModuleFileExtension>>(),
-                     /*AllowASTWithErrors=*/true),
-        Unit(Unit), Hash(Unit.getCurrentTopLevelHashValue()), Action(Action),
-        Out(std::move(Out)) {
-    Hash = 0;
+  void AfterPCHEmitted(ASTWriter &Writer) override {
+    TopLevelDeclIDs.reserve(TopLevelDecls.size());
+    for (Decl *D : TopLevelDecls) {
+      // Invalid top-level decls may not have been serialized.
+      if (D->isInvalidDecl())
+        continue;
+      TopLevelDeclIDs.push_back(Writer.getDeclID(D));
+    }
   }
 
-  bool HandleTopLevelDecl(DeclGroupRef DG) override {
+  void HandleTopLevelDecl(DeclGroupRef DG) override {
     for (Decl *D : DG) {
       // FIXME: Currently ObjC method declarations are incorrectly being
       // reported as top-level declarations, even though their DeclContext
@@ -965,59 +956,23 @@ public:
       AddTopLevelDeclarationToHash(D, Hash);
       TopLevelDecls.push_back(D);
     }
-    return true;
   }
 
-  void HandleTranslationUnit(ASTContext &Ctx) override {
-    PCHGenerator::HandleTranslationUnit(Ctx);
-    if (hasEmittedPCH()) {
-      // Write the generated bitstream to "Out".
-      *Out << getPCH();
-      // Make sure it hits disk now.
-      Out->flush();
-      // Free the buffer.
-      llvm::SmallVector<char, 0> Empty;
-      getPCH() = std::move(Empty);
-
-      // Translate the top-level declarations we captured during
-      // parsing into declaration IDs in the precompiled
-      // preamble. This will allow us to deserialize those top-level
-      // declarations when requested.
-      for (Decl *D : TopLevelDecls) {
-        // Invalid top-level decls may not have been serialized.
-        if (D->isInvalidDecl())
-          continue;
-        Unit.addTopLevelDeclFromPreamble(getWriter().getDeclID(D));
-      }
-
-      Action->setHasEmittedPreamblePCH();
-    }
+  void HandleMacroDefined(const Token &MacroNameTok,
+                          const MacroDirective *MD) override {
+    AddDefinedMacroToHash(MacroNameTok, Hash);
   }
+
+private:
+  llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags;
+  unsigned Hash = 0;
+  std::vector<Decl *> TopLevelDecls;
+  std::vector<serialization::DeclID> TopLevelDeclIDs;
+  llvm::SmallVector<ASTUnit::StandaloneDiagnostic, 4> PreambleDiags;
 };
 
 } // anonymous namespace
 
-std::unique_ptr<ASTConsumer>
-PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI,
-                                            StringRef InFile) {
-  std::string Sysroot;
-  std::string OutputFile;
-  std::unique_ptr<raw_ostream> OS =
-      GeneratePCHAction::ComputeASTConsumerArguments(CI, InFile, Sysroot,
-                                                     OutputFile);
-  if (!OS)
-    return nullptr;
-
-  if (!CI.getFrontendOpts().RelocatablePCH)
-    Sysroot.clear();
-
-  CI.getPreprocessor().addPPCallbacks(
-      llvm::make_unique<MacroDefinitionTrackerPPCallbacks>(
-                                           Unit.getCurrentTopLevelHashValue()));
-  return llvm::make_unique<PrecompilePreambleConsumer>(
-      Unit, this, CI.getPreprocessor(), Sysroot, std::move(OS));
-}
-
 static bool isNonDriverDiag(const StoredDiagnostic &StoredDiag) {
   return StoredDiag.getLocation().isValid();
 }
@@ -1125,15 +1080,9 @@ bool ASTUnit::Parse(std::shared_ptr<PCHC
   
   // If the main file has been overridden due to the use of a preamble,
   // make that override happen and introduce the preamble.
-  PreprocessorOptions &PreprocessorOpts = Clang->getPreprocessorOpts();
   if (OverrideMainBuffer) {
-    PreprocessorOpts.addRemappedFile(OriginalSourceFile,
-                                     OverrideMainBuffer.get());
-    PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size();
-    PreprocessorOpts.PrecompiledPreambleBytes.second
-                                                    = PreambleEndsAtStartOfLine;
-    PreprocessorOpts.ImplicitPCHInclude = getPreambleFile(this);
-    PreprocessorOpts.DisablePCHValidation = true;
+    assert(Preamble && "No preamble was built, but OverrideMainBuffer is not null");
+    Preamble->AddImplicitPreamble(Clang->getInvocation(), OverrideMainBuffer.get());
     
     // The stored diagnostic has the old source manager in it; update
     // the locations to refer into the new source manager. Since we've
@@ -1186,116 +1135,6 @@ error:
   return true;
 }
 
-/// \brief Simple function to retrieve a path for a preamble precompiled header.
-static std::string GetPreamblePCHPath() {
-  // FIXME: This is a hack so that we can override the preamble file during
-  // crash-recovery testing, which is the only case where the preamble files
-  // are not necessarily cleaned up.
-  const char *TmpFile = ::getenv("CINDEXTEST_PREAMBLE_FILE");
-  if (TmpFile)
-    return TmpFile;
-
-  SmallString<128> Path;
-  llvm::sys::fs::createTemporaryFile("preamble", "pch", Path);
-
-  return Path.str();
-}
-
-/// \brief Compute the preamble for the main file, providing the source buffer
-/// that corresponds to the main file along with a pair (bytes, start-of-line)
-/// that describes the preamble.
-ASTUnit::ComputedPreamble
-ASTUnit::ComputePreamble(CompilerInvocation &Invocation, unsigned MaxLines,
-                         IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
-  FrontendOptions &FrontendOpts = Invocation.getFrontendOpts();
-  PreprocessorOptions &PreprocessorOpts = Invocation.getPreprocessorOpts();
-  
-  // Try to determine if the main file has been remapped, either from the 
-  // command line (to another file) or directly through the compiler invocation
-  // (to a memory buffer).
-  llvm::MemoryBuffer *Buffer = nullptr;
-  std::unique_ptr<llvm::MemoryBuffer> BufferOwner;
-  std::string MainFilePath(FrontendOpts.Inputs[0].getFile());
-  auto MainFileStatus = VFS->status(MainFilePath);
-  if (MainFileStatus) {
-    llvm::sys::fs::UniqueID MainFileID = MainFileStatus->getUniqueID();
-
-    // Check whether there is a file-file remapping of the main file
-    for (const auto &RF : PreprocessorOpts.RemappedFiles) {
-      std::string MPath(RF.first);
-      auto MPathStatus = VFS->status(MPath);
-      if (MPathStatus) {
-        llvm::sys::fs::UniqueID MID = MPathStatus->getUniqueID();
-        if (MainFileID == MID) {
-          // We found a remapping. Try to load the resulting, remapped source.
-          BufferOwner = valueOrNull(VFS->getBufferForFile(RF.second));
-          if (!BufferOwner)
-            return ComputedPreamble(nullptr, nullptr, 0, true);
-        }
-      }
-    }
-
-    // Check whether there is a file-buffer remapping. It supercedes the
-    // file-file remapping.
-    for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) {
-      std::string MPath(RB.first);
-      auto MPathStatus = VFS->status(MPath);
-      if (MPathStatus) {
-        llvm::sys::fs::UniqueID MID = MPathStatus->getUniqueID();
-        if (MainFileID == MID) {
-          // We found a remapping.
-          BufferOwner.reset();
-          Buffer = const_cast<llvm::MemoryBuffer *>(RB.second);
-        }
-      }
-    }
-  }
-  
-  // If the main source file was not remapped, load it now.
-  if (!Buffer && !BufferOwner) {
-    BufferOwner = valueOrNull(VFS->getBufferForFile(FrontendOpts.Inputs[0].getFile()));
-    if (!BufferOwner)
-      return ComputedPreamble(nullptr, nullptr, 0, true);
-  }
-
-  if (!Buffer)
-    Buffer = BufferOwner.get();
-  auto Pre = Lexer::ComputePreamble(Buffer->getBuffer(),
-                                    *Invocation.getLangOpts(), MaxLines);
-  return ComputedPreamble(Buffer, std::move(BufferOwner), Pre.first,
-                          Pre.second);
-}
-
-ASTUnit::PreambleFileHash
-ASTUnit::PreambleFileHash::createForFile(off_t Size, time_t ModTime) {
-  PreambleFileHash Result;
-  Result.Size = Size;
-  Result.ModTime = ModTime;
-  Result.MD5 = {};
-  return Result;
-}
-
-ASTUnit::PreambleFileHash ASTUnit::PreambleFileHash::createForMemoryBuffer(
-    const llvm::MemoryBuffer *Buffer) {
-  PreambleFileHash Result;
-  Result.Size = Buffer->getBufferSize();
-  Result.ModTime = 0;
-
-  llvm::MD5 MD5Ctx;
-  MD5Ctx.update(Buffer->getBuffer().data());
-  MD5Ctx.final(Result.MD5);
-
-  return Result;
-}
-
-namespace clang {
-bool operator==(const ASTUnit::PreambleFileHash &LHS,
-                const ASTUnit::PreambleFileHash &RHS) {
-  return LHS.Size == RHS.Size && LHS.ModTime == RHS.ModTime &&
-         LHS.MD5 == RHS.MD5;
-}
-} // namespace clang
-
 static std::pair<unsigned, unsigned>
 makeStandaloneRange(CharSourceRange Range, const SourceManager &SM,
                     const LangOptions &LangOpts) {
@@ -1367,135 +1206,41 @@ ASTUnit::getMainBufferWithPrecompiledPre
     const CompilerInvocation &PreambleInvocationIn,
     IntrusiveRefCntPtr<vfs::FileSystem> VFS, bool AllowRebuild,
     unsigned MaxLines) {
-  assert(VFS && "VFS is null");
-
-  auto PreambleInvocation =
-      std::make_shared<CompilerInvocation>(PreambleInvocationIn);
-  FrontendOptions &FrontendOpts = PreambleInvocation->getFrontendOpts();
-  PreprocessorOptions &PreprocessorOpts
-    = PreambleInvocation->getPreprocessorOpts();
-
-  ComputedPreamble NewPreamble =
-      ComputePreamble(*PreambleInvocation, MaxLines, VFS);
-
-  if (!NewPreamble.Size) {
-    // We couldn't find a preamble in the main source. Clear out the current
-    // preamble, if we have one. It's obviously no good any more.
-    Preamble.clear();
-    erasePreambleFile(this);
 
-    // The next time we actually see a preamble, precompile it.
-    PreambleRebuildCounter = 1;
+  auto MainFilePath =
+      PreambleInvocationIn.getFrontendOpts().Inputs[0].getFile();
+  std::unique_ptr<llvm::MemoryBuffer> MainFileBuffer =
+      getBufferForFileHandlingRemapping(PreambleInvocationIn, VFS.get(),
+                                        MainFilePath);
+  if (!MainFileBuffer)
     return nullptr;
-  }
-  
-  if (!Preamble.empty()) {
-    // We've previously computed a preamble. Check whether we have the same
-    // preamble now that we did before, and that there's enough space in
-    // the main-file buffer within the precompiled preamble to fit the
-    // new main file.
-    if (Preamble.size() == NewPreamble.Size &&
-        PreambleEndsAtStartOfLine == NewPreamble.PreambleEndsAtStartOfLine &&
-        memcmp(Preamble.getBufferStart(), NewPreamble.Buffer->getBufferStart(),
-               NewPreamble.Size) == 0) {
-      // The preamble has not changed. We may be able to re-use the precompiled
-      // preamble.
-
-      // Check that none of the files used by the preamble have changed.
-      bool AnyFileChanged = false;
-          
-      // First, make a record of those files that have been overridden via
-      // remapping or unsaved_files.
-      std::map<llvm::sys::fs::UniqueID, PreambleFileHash> OverriddenFiles;
-      for (const auto &R : PreprocessorOpts.RemappedFiles) {
-        if (AnyFileChanged)
-          break;
-
-        vfs::Status Status;
-        if (!moveOnNoError(VFS->status(R.second), Status)) {
-          // If we can't stat the file we're remapping to, assume that something
-          // horrible happened.
-          AnyFileChanged = true;
-          break;
-        }
 
-        OverriddenFiles[Status.getUniqueID()] = PreambleFileHash::createForFile(
-            Status.getSize(),
-            llvm::sys::toTimeT(Status.getLastModificationTime()));
-      }
-
-      for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) {
-        if (AnyFileChanged)
-          break;
-
-        vfs::Status Status;
-        if (!moveOnNoError(VFS->status(RB.first), Status)) {
-          AnyFileChanged = true;
-          break;
-        }
+  PreambleBounds Bounds =
+      ComputePreambleBounds(*PreambleInvocationIn.getLangOpts(),
+                            MainFileBuffer.get(), MaxLines);
+  if (!Bounds.Size)
+    return nullptr;
 
-        OverriddenFiles[Status.getUniqueID()] =
-            PreambleFileHash::createForMemoryBuffer(RB.second);
-      }
-       
-      // Check whether anything has changed.
-      for (llvm::StringMap<PreambleFileHash>::iterator
-             F = FilesInPreamble.begin(), FEnd = FilesInPreamble.end();
-           !AnyFileChanged && F != FEnd; 
-           ++F) {
-        vfs::Status Status;
-        if (!moveOnNoError(VFS->status(F->first()), Status)) {
-          // If we can't stat the file, assume that something horrible happened.
-          AnyFileChanged = true;
-          break;
-        }
+  if (Preamble) {
+    if (Preamble->CanReuse(PreambleInvocationIn, MainFileBuffer.get(), Bounds,
+                           VFS.get())) {
+      // Okay! We can re-use the precompiled preamble.
+
+      // Set the state of the diagnostic object to mimic its state
+      // after parsing the preamble.
+      getDiagnostics().Reset();
+      ProcessWarningOptions(getDiagnostics(),
+                            PreambleInvocationIn.getDiagnosticOpts());
+      getDiagnostics().setNumWarnings(NumWarningsInPreamble);
 
-        std::map<llvm::sys::fs::UniqueID, PreambleFileHash>::iterator Overridden
-          = OverriddenFiles.find(Status.getUniqueID());
-        if (Overridden != OverriddenFiles.end()) {
-          // This file was remapped; check whether the newly-mapped file 
-          // matches up with the previous mapping.
-          if (Overridden->second != F->second)
-            AnyFileChanged = true;
-          continue;
-        }
-        
-        // The file was not remapped; check whether it has changed on disk.
-        if (Status.getSize() != uint64_t(F->second.Size) ||
-            llvm::sys::toTimeT(Status.getLastModificationTime()) !=
-                F->second.ModTime)
-          AnyFileChanged = true;
-      }
-          
-      if (!AnyFileChanged) {
-        // Okay! We can re-use the precompiled preamble.
-
-        // Set the state of the diagnostic object to mimic its state
-        // after parsing the preamble.
-        getDiagnostics().Reset();
-        ProcessWarningOptions(getDiagnostics(), 
-                              PreambleInvocation->getDiagnosticOpts());
-        getDiagnostics().setNumWarnings(NumWarningsInPreamble);
-
-        return llvm::MemoryBuffer::getMemBufferCopy(
-            NewPreamble.Buffer->getBuffer(), FrontendOpts.Inputs[0].getFile());
-      }
+      PreambleRebuildCounter = 1;
+      return MainFileBuffer;
+    } else {
+      Preamble.reset();
+      PreambleDiagnostics.clear();
+      TopLevelDeclsInPreamble.clear();
+      PreambleRebuildCounter = 1;
     }
-
-    // If we aren't allowed to rebuild the precompiled preamble, just
-    // return now.
-    if (!AllowRebuild)
-      return nullptr;
-
-    // We can't reuse the previously-computed preamble. Build a new one.
-    Preamble.clear();
-    PreambleDiagnostics.clear();
-    erasePreambleFile(this);
-    PreambleRebuildCounter = 1;
-  } else if (!AllowRebuild) {
-    // We aren't allowed to rebuild the precompiled preamble; just
-    // return now.
-    return nullptr;
   }
 
   // If the preamble rebuild counter > 1, it's because we previously
@@ -1506,164 +1251,63 @@ ASTUnit::getMainBufferWithPrecompiledPre
     return nullptr;
   }
 
-  // Create a temporary file for the precompiled preamble. In rare 
-  // circumstances, this can fail.
-  std::string PreamblePCHPath = GetPreamblePCHPath();
-  if (PreamblePCHPath.empty()) {
-    // Try again next time.
-    PreambleRebuildCounter = 1;
-    return nullptr;
-  }
-  
-  // We did not previously compute a preamble, or it can't be reused anyway.
-  SimpleTimer PreambleTimer(WantTiming);
-  PreambleTimer.setOutput("Precompiling preamble");
-
-  // Save the preamble text for later; we'll need to compare against it for
-  // subsequent reparses.
-  StringRef MainFilename = FrontendOpts.Inputs[0].getFile();
-  Preamble.assign(FileMgr->getFile(MainFilename),
-                  NewPreamble.Buffer->getBufferStart(),
-                  NewPreamble.Buffer->getBufferStart() + NewPreamble.Size);
-  PreambleEndsAtStartOfLine = NewPreamble.PreambleEndsAtStartOfLine;
-
-  PreambleBuffer = llvm::MemoryBuffer::getMemBufferCopy(
-      NewPreamble.Buffer->getBuffer().slice(0, Preamble.size()), MainFilename);
-
-  // Remap the main source file to the preamble buffer.
-  StringRef MainFilePath = FrontendOpts.Inputs[0].getFile();
-  PreprocessorOpts.addRemappedFile(MainFilePath, PreambleBuffer.get());
-
-  // Tell the compiler invocation to generate a temporary precompiled header.
-  FrontendOpts.ProgramAction = frontend::GeneratePCH;
-  // FIXME: Generate the precompiled header into memory?
-  FrontendOpts.OutputFile = PreamblePCHPath;
-  PreprocessorOpts.PrecompiledPreambleBytes.first = 0;
-  PreprocessorOpts.PrecompiledPreambleBytes.second = false;
-  
-  // Create the compiler instance to use for building the precompiled preamble.
-  std::unique_ptr<CompilerInstance> Clang(
-      new CompilerInstance(std::move(PCHContainerOps)));
-
-  // Recover resources if we crash before exiting this method.
-  llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance>
-    CICleanup(Clang.get());
-
-  Clang->setInvocation(std::move(PreambleInvocation));
-  OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile();
-  
-  // Set up diagnostics, capturing all of the diagnostics produced.
-  Clang->setDiagnostics(&getDiagnostics());
-  
-  // Create the target instance.
-  Clang->setTarget(TargetInfo::CreateTargetInfo(
-      Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
-  if (!Clang->hasTarget()) {
-    llvm::sys::fs::remove(FrontendOpts.OutputFile);
-    Preamble.clear();
-    PreambleRebuildCounter = DefaultPreambleRebuildInterval;
-    PreprocessorOpts.RemappedFileBuffers.pop_back();
-    return nullptr;
-  }
-  
-  // Inform the target of the language options.
-  //
-  // FIXME: We shouldn't need to do this, the target should be immutable once
-  // created. This complexity should be lifted elsewhere.
-  Clang->getTarget().adjust(Clang->getLangOpts());
-  
-  assert(Clang->getFrontendOpts().Inputs.size() == 1 &&
-         "Invocation must have exactly one source file!");
-  assert(Clang->getFrontendOpts().Inputs[0].getKind().getFormat() ==
-             InputKind::Source &&
-         "FIXME: AST inputs not yet supported here!");
-  assert(Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() !=
-             InputKind::LLVM_IR &&
-         "IR inputs not support here!");
-  
-  // Clear out old caches and data.
-  getDiagnostics().Reset();
-  ProcessWarningOptions(getDiagnostics(), Clang->getDiagnosticOpts());
-  checkAndRemoveNonDriverDiags(StoredDiagnostics);
-  TopLevelDecls.clear();
-  TopLevelDeclsInPreamble.clear();
-  PreambleDiagnostics.clear();
-
-  VFS = createVFSFromCompilerInvocation(Clang->getInvocation(),
-                                        getDiagnostics(), VFS);
-  if (!VFS)
+  assert(!Preamble && "No Preamble should be stored at that point");
+  // If we aren't allowed to rebuild the precompiled preamble, just
+  // return now.
+  if (!AllowRebuild)
     return nullptr;
 
-  // Create a file manager object to provide access to and cache the filesystem.
-  Clang->setFileManager(new FileManager(Clang->getFileSystemOpts(), VFS));
-
-  // Create the source manager.
-  Clang->setSourceManager(new SourceManager(getDiagnostics(),
-                                            Clang->getFileManager()));
-
-  auto PreambleDepCollector = std::make_shared<DependencyCollector>();
-  Clang->addDependencyCollector(PreambleDepCollector);
-
-  std::unique_ptr<PrecompilePreambleAction> Act;
-  Act.reset(new PrecompilePreambleAction(*this));
-  if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) {
-    llvm::sys::fs::remove(FrontendOpts.OutputFile);
-    Preamble.clear();
-    PreambleRebuildCounter = DefaultPreambleRebuildInterval;
-    PreprocessorOpts.RemappedFileBuffers.pop_back();
-    return nullptr;
+  SmallVector<StandaloneDiagnostic, 4> NewPreambleDiagsStandalone;
+  SmallVector<StoredDiagnostic, 4> NewPreambleDiags;
+  ASTUnitPreambleCallbacks Callbacks(NewPreambleDiags);
+  {
+    llvm::Optional<CaptureDroppedDiagnostics> Capture;
+    if (CaptureDiagnostics)
+      Capture.emplace(/*RequestCapture=*/true, *Diagnostics, &NewPreambleDiags,
+                      &NewPreambleDiagsStandalone);
+
+    // We did not previously compute a preamble, or it can't be reused anyway.
+    SimpleTimer PreambleTimer(WantTiming);
+    PreambleTimer.setOutput("Precompiling preamble");
+
+    llvm::ErrorOr<PrecompiledPreamble> NewPreamble = PrecompiledPreamble::Build(
+        PreambleInvocationIn, MainFileBuffer.get(), Bounds, *Diagnostics, VFS,
+        PCHContainerOps, Callbacks);
+    if (NewPreamble) {
+      Preamble = std::move(*NewPreamble);
+      PreambleRebuildCounter = 1;
+    } else {
+      switch (static_cast<BuildPreambleError>(NewPreamble.getError().value())) {
+      case BuildPreambleError::CouldntCreateTempFile:
+      case BuildPreambleError::PreambleIsEmpty:
+        // Try again next time.
+        PreambleRebuildCounter = 1;
+        break;
+      case BuildPreambleError::CouldntCreateTargetInfo:
+      case BuildPreambleError::BeginSourceFileFailed:
+      case BuildPreambleError::CouldntEmitPCH:
+      case BuildPreambleError::CouldntCreateVFSOverlay:
+        // These erros are more likely to repeat, retry after some period.
+        PreambleRebuildCounter = DefaultPreambleRebuildInterval;
+        break;
+      default:
+        llvm_unreachable("unexpected BuildPreambleError");
+      }
+      return nullptr;
+    }
   }
-  
-  Act->Execute();
 
-  // Transfer any diagnostics generated when parsing the preamble into the set
-  // of preamble diagnostics.
-  for (stored_diag_iterator I = stored_diag_afterDriver_begin(),
-                            E = stored_diag_end();
-       I != E; ++I)
-    PreambleDiagnostics.push_back(
-        makeStandaloneDiagnostic(Clang->getLangOpts(), *I));
+  assert(Preamble && "Preamble wasn't built");
 
-  Act->EndSourceFile();
-
-  checkAndRemoveNonDriverDiags(StoredDiagnostics);
+  TopLevelDecls.clear();
+  TopLevelDeclsInPreamble = Callbacks.takeTopLevelDeclIDs();
+  PreambleTopLevelHashValue = Callbacks.getHash();
 
-  if (!Act->hasEmittedPreamblePCH()) {
-    // The preamble PCH failed (e.g. there was a module loading fatal error),
-    // so no precompiled header was generated. Forget that we even tried.
-    // FIXME: Should we leave a note for ourselves to try again?
-    llvm::sys::fs::remove(FrontendOpts.OutputFile);
-    Preamble.clear();
-    TopLevelDeclsInPreamble.clear();
-    PreambleRebuildCounter = DefaultPreambleRebuildInterval;
-    PreprocessorOpts.RemappedFileBuffers.pop_back();
-    return nullptr;
-  }
-  
-  // Keep track of the preamble we precompiled.
-  setPreambleFile(this, FrontendOpts.OutputFile);
   NumWarningsInPreamble = getDiagnostics().getNumWarnings();
-  
-  // Keep track of all of the files that the source manager knows about,
-  // so we can verify whether they have changed or not.
-  FilesInPreamble.clear();
-  SourceManager &SourceMgr = Clang->getSourceManager();
-  for (auto &Filename : PreambleDepCollector->getDependencies()) {
-    const FileEntry *File = Clang->getFileManager().getFile(Filename);
-    if (!File || File == SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()))
-      continue;
-    if (time_t ModTime = File->getModificationTime()) {
-      FilesInPreamble[File->getName()] = PreambleFileHash::createForFile(
-          File->getSize(), ModTime);
-    } else {
-      llvm::MemoryBuffer *Buffer = SourceMgr.getMemoryBufferForFile(File);
-      FilesInPreamble[File->getName()] =
-          PreambleFileHash::createForMemoryBuffer(Buffer);
-    }
-  }
 
-  PreambleRebuildCounter = 1;
-  PreprocessorOpts.RemappedFileBuffers.pop_back();
+  checkAndRemoveNonDriverDiags(NewPreambleDiags);
+  StoredDiagnostics = std::move(NewPreambleDiags);
+  PreambleDiagnostics = std::move(NewPreambleDiagsStandalone);
 
   // If the hash of top-level entities differs from the hash of the top-level
   // entities the last time we rebuilt the preamble, clear out the completion
@@ -1673,11 +1317,12 @@ ASTUnit::getMainBufferWithPrecompiledPre
     PreambleTopLevelHashValue = CurrentTopLevelHashValue;
   }
 
-  return llvm::MemoryBuffer::getMemBufferCopy(NewPreamble.Buffer->getBuffer(),
-                                              MainFilename);
+  return MainFileBuffer;
 }
 
 void ASTUnit::RealizeTopLevelDeclsFromPreamble() {
+  assert(Preamble && "Should only be called when preamble was built");
+
   std::vector<Decl *> Resolved;
   Resolved.reserve(TopLevelDeclsInPreamble.size());
   ExternalASTSource &Source = *getASTContext().getExternalSource();
@@ -1995,8 +1640,8 @@ ASTUnit *ASTUnit::LoadFromCommandLine(
 
   {
 
-    CaptureDroppedDiagnostics Capture(CaptureDiagnostics, *Diags, 
-                                      StoredDiagnostics);
+    CaptureDroppedDiagnostics Capture(CaptureDiagnostics, *Diags,
+                                      &StoredDiagnostics, nullptr);
 
     CI = clang::createInvocationFromCommandLine(
         llvm::makeArrayRef(ArgBegin, ArgEnd), Diags);
@@ -2101,7 +1746,7 @@ bool ASTUnit::Reparse(std::shared_ptr<PC
   // If we have a preamble file lying around, or if we might try to
   // build a precompiled preamble, do so now.
   std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer;
-  if (!getPreambleFile(this).empty() || PreambleRebuildCounter > 0)
+  if (Preamble || PreambleRebuildCounter > 0)
     OverrideMainBuffer =
         getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS);
 
@@ -2435,7 +2080,7 @@ void ASTUnit::CodeComplete(
   Clang->setDiagnostics(&Diag);
   CaptureDroppedDiagnostics Capture(true, 
                                     Clang->getDiagnostics(), 
-                                    StoredDiagnostics);
+                                    &StoredDiagnostics, nullptr);
   ProcessWarningOptions(Diag, Inv.getDiagnosticOpts());
 
   // Create the target instance.
@@ -2484,7 +2129,7 @@ void ASTUnit::CodeComplete(
   // point is within the main file, after the end of the precompiled
   // preamble.
   std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer;
-  if (!getPreambleFile(this).empty()) {
+  if (Preamble) {
     std::string CompleteFilePath(File);
 
     auto VFS = FileMgr.getVirtualFileSystem();
@@ -2506,14 +2151,8 @@ void ASTUnit::CodeComplete(
   // If the main file has been overridden due to the use of a preamble,
   // make that override happen and introduce the preamble.
   if (OverrideMainBuffer) {
-    PreprocessorOpts.addRemappedFile(OriginalSourceFile,
-                                     OverrideMainBuffer.get());
-    PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size();
-    PreprocessorOpts.PrecompiledPreambleBytes.second
-                                                    = PreambleEndsAtStartOfLine;
-    PreprocessorOpts.ImplicitPCHInclude = getPreambleFile(this);
-    PreprocessorOpts.DisablePCHValidation = true;
-
+    assert(Preamble && "No preamble was built, but OverrideMainBuffer is not null");
+    Preamble->AddImplicitPreamble(Clang->getInvocation(), OverrideMainBuffer.get());
     OwnedBuffers.push_back(OverrideMainBuffer.release());
   } else {
     PreprocessorOpts.PrecompiledPreambleBytes.first = 0;
@@ -2760,11 +2399,11 @@ SourceLocation ASTUnit::mapLocationFromP
   if (SourceMgr)
     PreambleID = SourceMgr->getPreambleFileID();
 
-  if (Loc.isInvalid() || Preamble.empty() || PreambleID.isInvalid())
+  if (Loc.isInvalid() || !Preamble || PreambleID.isInvalid())
     return Loc;
 
   unsigned Offs;
-  if (SourceMgr->isInFileID(Loc, PreambleID, &Offs) && Offs < Preamble.size()) {
+  if (SourceMgr->isInFileID(Loc, PreambleID, &Offs) && Offs < Preamble->getBounds().Size) {
     SourceLocation FileLoc
         = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID());
     return FileLoc.getLocWithOffset(Offs);
@@ -2781,12 +2420,12 @@ SourceLocation ASTUnit::mapLocationToPre
   if (SourceMgr)
     PreambleID = SourceMgr->getPreambleFileID();
 
-  if (Loc.isInvalid() || Preamble.empty() || PreambleID.isInvalid())
+  if (Loc.isInvalid() || !Preamble || PreambleID.isInvalid())
     return Loc;
 
   unsigned Offs;
   if (SourceMgr->isInFileID(Loc, SourceMgr->getMainFileID(), &Offs) &&
-      Offs < Preamble.size()) {
+      Offs < Preamble->getBounds().Size) {
     SourceLocation FileLoc = SourceMgr->getLocForStartOfFile(PreambleID);
     return FileLoc.getLocWithOffset(Offs);
   }
@@ -2932,17 +2571,6 @@ InputKind ASTUnit::getInputKind() const
   return InputKind(Lang, Fmt, PP);
 }
 
-void ASTUnit::PreambleData::countLines() const {
-  NumLines = 0;
-  if (empty())
-    return;
-
-  NumLines = std::count(Buffer.begin(), Buffer.end(), '\n');
-
-  if (Buffer.back() != '\n')
-    ++NumLines;
-}
-
 #ifndef NDEBUG
 ASTUnit::ConcurrencyState::ConcurrencyState() {
   Mutex = new llvm::sys::MutexImpl(/*recursive=*/true);

Modified: cfe/trunk/lib/Frontend/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CMakeLists.txt?rev=305890&r1=305889&r2=305890&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CMakeLists.txt (original)
+++ cfe/trunk/lib/Frontend/CMakeLists.txt Wed Jun 21 05:24:58 2017
@@ -38,6 +38,7 @@ add_clang_library(clangFrontend
   ModuleDependencyCollector.cpp
   MultiplexConsumer.cpp
   PCHContainerOperations.cpp
+  PrecompiledPreamble.cpp
   PrintPreprocessedOutput.cpp
   SerializedDiagnosticPrinter.cpp
   SerializedDiagnosticReader.cpp

Added: cfe/trunk/lib/Frontend/PrecompiledPreamble.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PrecompiledPreamble.cpp?rev=305890&view=auto
==============================================================================
--- cfe/trunk/lib/Frontend/PrecompiledPreamble.cpp (added)
+++ cfe/trunk/lib/Frontend/PrecompiledPreamble.cpp Wed Jun 21 05:24:58 2017
@@ -0,0 +1,561 @@
+//===--- PrecompiledPreamble.cpp - Build precompiled preambles --*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Helper class to build precompiled preamble.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Frontend/PrecompiledPreamble.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/FrontendOptions.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "clang/Serialization/ASTWriter.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Mutex.h"
+#include "llvm/Support/MutexGuard.h"
+
+using namespace clang;
+
+namespace {
+
+/// Keeps a track of files to be deleted in destructor.
+class TemporaryFiles {
+public:
+  // A static instance to be used by all clients.
+  static TemporaryFiles &getInstance();
+
+private:
+  // Disallow constructing the class directly.
+  TemporaryFiles() = default;
+  // Disallow copy.
+  TemporaryFiles(const TemporaryFiles &) = delete;
+
+public:
+  ~TemporaryFiles();
+
+  /// Adds \p File to a set of tracked files.
+  void addFile(StringRef File);
+
+  /// Remove \p File from disk and from the set of tracked files.
+  void removeFile(StringRef File);
+
+private:
+  llvm::sys::SmartMutex<false> Mutex;
+  llvm::StringSet<> Files;
+};
+
+TemporaryFiles &TemporaryFiles::getInstance() {
+  static TemporaryFiles Instance;
+  return Instance;
+}
+
+TemporaryFiles::~TemporaryFiles() {
+  llvm::MutexGuard Guard(Mutex);
+  for (const auto &File : Files)
+    llvm::sys::fs::remove(File.getKey());
+}
+
+void TemporaryFiles::addFile(StringRef File) {
+  llvm::MutexGuard Guard(Mutex);
+  auto IsInserted = Files.insert(File).second;
+  assert(IsInserted && "File has already been added");
+}
+
+void TemporaryFiles::removeFile(StringRef File) {
+  llvm::MutexGuard Guard(Mutex);
+  auto WasPresent = Files.erase(File);
+  assert(WasPresent && "File was not tracked");
+  llvm::sys::fs::remove(File);
+}
+
+class PreambleMacroCallbacks : public PPCallbacks {
+public:
+  PreambleMacroCallbacks(PreambleCallbacks &Callbacks) : Callbacks(Callbacks) {}
+
+  void MacroDefined(const Token &MacroNameTok,
+                    const MacroDirective *MD) override {
+    Callbacks.HandleMacroDefined(MacroNameTok, MD);
+  }
+
+private:
+  PreambleCallbacks &Callbacks;
+};
+
+class PrecompilePreambleAction : public ASTFrontendAction {
+public:
+  PrecompilePreambleAction(PreambleCallbacks &Callbacks)
+      : Callbacks(Callbacks) {}
+
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+                                                 StringRef InFile) override;
+
+  bool hasEmittedPreamblePCH() const { return HasEmittedPreamblePCH; }
+
+  void setEmittedPreamblePCH(ASTWriter &Writer) {
+    this->HasEmittedPreamblePCH = true;
+    Callbacks.AfterPCHEmitted(Writer);
+  }
+
+  bool shouldEraseOutputFiles() override { return !hasEmittedPreamblePCH(); }
+  bool hasCodeCompletionSupport() const override { return false; }
+  bool hasASTFileSupport() const override { return false; }
+  TranslationUnitKind getTranslationUnitKind() override { return TU_Prefix; }
+
+private:
+  friend class PrecompilePreambleConsumer;
+
+  bool HasEmittedPreamblePCH = false;
+  PreambleCallbacks &Callbacks;
+};
+
+class PrecompilePreambleConsumer : public PCHGenerator {
+public:
+  PrecompilePreambleConsumer(PrecompilePreambleAction &Action,
+                             const Preprocessor &PP, StringRef isysroot,
+                             std::unique_ptr<raw_ostream> Out)
+      : PCHGenerator(PP, "", isysroot, std::make_shared<PCHBuffer>(),
+                     ArrayRef<std::shared_ptr<ModuleFileExtension>>(),
+                     /*AllowASTWithErrors=*/true),
+        Action(Action), Out(std::move(Out)) {}
+
+  bool HandleTopLevelDecl(DeclGroupRef DG) override {
+    Action.Callbacks.HandleTopLevelDecl(DG);
+    return true;
+  }
+
+  void HandleTranslationUnit(ASTContext &Ctx) override {
+    PCHGenerator::HandleTranslationUnit(Ctx);
+    if (!hasEmittedPCH())
+      return;
+
+    // Write the generated bitstream to "Out".
+    *Out << getPCH();
+    // Make sure it hits disk now.
+    Out->flush();
+    // Free the buffer.
+    llvm::SmallVector<char, 0> Empty;
+    getPCH() = std::move(Empty);
+
+    Action.setEmittedPreamblePCH(getWriter());
+  }
+
+private:
+  PrecompilePreambleAction &Action;
+  std::unique_ptr<raw_ostream> Out;
+};
+
+std::unique_ptr<ASTConsumer>
+PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI,
+
+                                            StringRef InFile) {
+  std::string Sysroot;
+  std::string OutputFile;
+  std::unique_ptr<raw_ostream> OS =
+      GeneratePCHAction::ComputeASTConsumerArguments(CI, InFile, Sysroot,
+                                                     OutputFile);
+  if (!OS)
+    return nullptr;
+
+  if (!CI.getFrontendOpts().RelocatablePCH)
+    Sysroot.clear();
+
+  CI.getPreprocessor().addPPCallbacks(
+      llvm::make_unique<PreambleMacroCallbacks>(Callbacks));
+  return llvm::make_unique<PrecompilePreambleConsumer>(
+      *this, CI.getPreprocessor(), Sysroot, std::move(OS));
+}
+
+template <class T> bool moveOnNoError(llvm::ErrorOr<T> Val, T &Output) {
+  if (!Val)
+    return false;
+  Output = std::move(*Val);
+  return true;
+}
+
+} // namespace
+
+PreambleBounds clang::ComputePreambleBounds(const LangOptions &LangOpts,
+                                            llvm::MemoryBuffer *Buffer,
+                                            unsigned MaxLines) {
+  auto Pre = Lexer::ComputePreamble(Buffer->getBuffer(), LangOpts, MaxLines);
+  return PreambleBounds(Pre.first, Pre.second);
+}
+
+llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
+    const CompilerInvocation &Invocation,
+    const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,
+    DiagnosticsEngine &Diagnostics, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
+    std::shared_ptr<PCHContainerOperations> PCHContainerOps,
+    PreambleCallbacks &Callbacks) {
+  assert(VFS && "VFS is null");
+
+  if (!Bounds.Size)
+    return BuildPreambleError::PreambleIsEmpty;
+
+  auto PreambleInvocation = std::make_shared<CompilerInvocation>(Invocation);
+  FrontendOptions &FrontendOpts = PreambleInvocation->getFrontendOpts();
+  PreprocessorOptions &PreprocessorOpts =
+      PreambleInvocation->getPreprocessorOpts();
+
+  // Create a temporary file for the precompiled preamble. In rare
+  // circumstances, this can fail.
+  llvm::ErrorOr<PrecompiledPreamble::TempPCHFile> PreamblePCHFile =
+      PrecompiledPreamble::TempPCHFile::CreateNewPreamblePCHFile();
+  if (!PreamblePCHFile)
+    return BuildPreambleError::CouldntCreateTempFile;
+
+  // Save the preamble text for later; we'll need to compare against it for
+  // subsequent reparses.
+  std::vector<char> PreambleBytes(MainFileBuffer->getBufferStart(),
+                                  MainFileBuffer->getBufferStart() +
+                                      Bounds.Size);
+  bool PreambleEndsAtStartOfLine = Bounds.PreambleEndsAtStartOfLine;
+
+  // Tell the compiler invocation to generate a temporary precompiled header.
+  FrontendOpts.ProgramAction = frontend::GeneratePCH;
+  // FIXME: Generate the precompiled header into memory?
+  FrontendOpts.OutputFile = PreamblePCHFile->getFilePath();
+  PreprocessorOpts.PrecompiledPreambleBytes.first = 0;
+  PreprocessorOpts.PrecompiledPreambleBytes.second = false;
+
+  // Create the compiler instance to use for building the precompiled preamble.
+  std::unique_ptr<CompilerInstance> Clang(
+      new CompilerInstance(std::move(PCHContainerOps)));
+
+  // Recover resources if we crash before exiting this method.
+  llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup(
+      Clang.get());
+
+  Clang->setInvocation(std::move(PreambleInvocation));
+  Clang->setDiagnostics(&Diagnostics);
+
+  // Create the target instance.
+  Clang->setTarget(TargetInfo::CreateTargetInfo(
+      Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
+  if (!Clang->hasTarget())
+    return BuildPreambleError::CouldntCreateTargetInfo;
+
+  // Inform the target of the language options.
+  //
+  // FIXME: We shouldn't need to do this, the target should be immutable once
+  // created. This complexity should be lifted elsewhere.
+  Clang->getTarget().adjust(Clang->getLangOpts());
+
+  assert(Clang->getFrontendOpts().Inputs.size() == 1 &&
+         "Invocation must have exactly one source file!");
+  assert(Clang->getFrontendOpts().Inputs[0].getKind().getFormat() ==
+             InputKind::Source &&
+         "FIXME: AST inputs not yet supported here!");
+  assert(Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() !=
+             InputKind::LLVM_IR &&
+         "IR inputs not support here!");
+
+  // Clear out old caches and data.
+  Diagnostics.Reset();
+  ProcessWarningOptions(Diagnostics, Clang->getDiagnosticOpts());
+
+  VFS =
+      createVFSFromCompilerInvocation(Clang->getInvocation(), Diagnostics, VFS);
+  if (!VFS)
+    return BuildPreambleError::CouldntCreateVFSOverlay;
+
+  // Create a file manager object to provide access to and cache the filesystem.
+  Clang->setFileManager(new FileManager(Clang->getFileSystemOpts(), VFS));
+
+  // Create the source manager.
+  Clang->setSourceManager(
+      new SourceManager(Diagnostics, Clang->getFileManager()));
+
+  auto PreambleDepCollector = std::make_shared<DependencyCollector>();
+  Clang->addDependencyCollector(PreambleDepCollector);
+
+  // Remap the main source file to the preamble buffer.
+  StringRef MainFilePath = FrontendOpts.Inputs[0].getFile();
+  auto PreambleInputBuffer = llvm::MemoryBuffer::getMemBufferCopy(
+      MainFileBuffer->getBuffer().slice(0, Bounds.Size), MainFilePath);
+  if (PreprocessorOpts.RetainRemappedFileBuffers) {
+    // MainFileBuffer will be deleted by unique_ptr after leaving the method.
+    PreprocessorOpts.addRemappedFile(MainFilePath, PreambleInputBuffer.get());
+  } else {
+    // In that case, remapped buffer will be deleted by CompilerInstance on
+    // BeginSourceFile, so we call release() to avoid double deletion.
+    PreprocessorOpts.addRemappedFile(MainFilePath,
+                                     PreambleInputBuffer.release());
+  }
+
+  std::unique_ptr<PrecompilePreambleAction> Act;
+  Act.reset(new PrecompilePreambleAction(Callbacks));
+  if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0]))
+    return BuildPreambleError::BeginSourceFileFailed;
+
+  Act->Execute();
+
+  // Run the callbacks.
+  Callbacks.AfterExecute(*Clang);
+
+  Act->EndSourceFile();
+
+  if (!Act->hasEmittedPreamblePCH())
+    return BuildPreambleError::CouldntEmitPCH;
+
+  // Keep track of all of the files that the source manager knows about,
+  // so we can verify whether they have changed or not.
+  llvm::StringMap<PrecompiledPreamble::PreambleFileHash> FilesInPreamble;
+
+  SourceManager &SourceMgr = Clang->getSourceManager();
+  for (auto &Filename : PreambleDepCollector->getDependencies()) {
+    const FileEntry *File = Clang->getFileManager().getFile(Filename);
+    if (!File || File == SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()))
+      continue;
+    if (time_t ModTime = File->getModificationTime()) {
+      FilesInPreamble[File->getName()] =
+          PrecompiledPreamble::PreambleFileHash::createForFile(File->getSize(),
+                                                               ModTime);
+    } else {
+      llvm::MemoryBuffer *Buffer = SourceMgr.getMemoryBufferForFile(File);
+      FilesInPreamble[File->getName()] =
+          PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(Buffer);
+    }
+  }
+
+  return PrecompiledPreamble(
+      std::move(*PreamblePCHFile), std::move(PreambleBytes),
+      PreambleEndsAtStartOfLine, std::move(FilesInPreamble));
+}
+
+PreambleBounds PrecompiledPreamble::getBounds() const {
+  return PreambleBounds(PreambleBytes.size(), PreambleEndsAtStartOfLine);
+}
+
+bool PrecompiledPreamble::CanReuse(const CompilerInvocation &Invocation,
+                                   const llvm::MemoryBuffer *MainFileBuffer,
+                                   PreambleBounds Bounds,
+                                   vfs::FileSystem *VFS) const {
+
+  assert(
+      Bounds.Size <= MainFileBuffer->getBufferSize() &&
+      "Buffer is too large. Bounds were calculated from a different buffer?");
+
+  auto PreambleInvocation = std::make_shared<CompilerInvocation>(Invocation);
+  PreprocessorOptions &PreprocessorOpts =
+      PreambleInvocation->getPreprocessorOpts();
+
+  if (!Bounds.Size)
+    return false;
+
+  // We've previously computed a preamble. Check whether we have the same
+  // preamble now that we did before, and that there's enough space in
+  // the main-file buffer within the precompiled preamble to fit the
+  // new main file.
+  if (PreambleBytes.size() != Bounds.Size ||
+      PreambleEndsAtStartOfLine != Bounds.PreambleEndsAtStartOfLine ||
+      memcmp(PreambleBytes.data(), MainFileBuffer->getBufferStart(),
+             Bounds.Size) != 0)
+    return false;
+  // The preamble has not changed. We may be able to re-use the precompiled
+  // preamble.
+
+  // Check that none of the files used by the preamble have changed.
+  // First, make a record of those files that have been overridden via
+  // remapping or unsaved_files.
+  std::map<llvm::sys::fs::UniqueID, PreambleFileHash> OverriddenFiles;
+  for (const auto &R : PreprocessorOpts.RemappedFiles) {
+    vfs::Status Status;
+    if (!moveOnNoError(VFS->status(R.second), Status)) {
+      // If we can't stat the file we're remapping to, assume that something
+      // horrible happened.
+      return false;
+    }
+
+    OverriddenFiles[Status.getUniqueID()] = PreambleFileHash::createForFile(
+        Status.getSize(), llvm::sys::toTimeT(Status.getLastModificationTime()));
+  }
+
+  for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) {
+    vfs::Status Status;
+    if (!moveOnNoError(VFS->status(RB.first), Status))
+      return false;
+
+    OverriddenFiles[Status.getUniqueID()] =
+        PreambleFileHash::createForMemoryBuffer(RB.second);
+  }
+
+  // Check whether anything has changed.
+  for (const auto &F : FilesInPreamble) {
+    vfs::Status Status;
+    if (!moveOnNoError(VFS->status(F.first()), Status)) {
+      // If we can't stat the file, assume that something horrible happened.
+      return false;
+    }
+
+    std::map<llvm::sys::fs::UniqueID, PreambleFileHash>::iterator Overridden =
+        OverriddenFiles.find(Status.getUniqueID());
+    if (Overridden != OverriddenFiles.end()) {
+      // This file was remapped; check whether the newly-mapped file
+      // matches up with the previous mapping.
+      if (Overridden->second != F.second)
+        return false;
+      continue;
+    }
+
+    // The file was not remapped; check whether it has changed on disk.
+    if (Status.getSize() != uint64_t(F.second.Size) ||
+        llvm::sys::toTimeT(Status.getLastModificationTime()) !=
+            F.second.ModTime)
+      return false;
+  }
+  return true;
+}
+
+void PrecompiledPreamble::AddImplicitPreamble(
+    CompilerInvocation &CI, llvm::MemoryBuffer *MainFileBuffer) const {
+  auto &PreprocessorOpts = CI.getPreprocessorOpts();
+
+  // Configure ImpicitPCHInclude.
+  PreprocessorOpts.PrecompiledPreambleBytes.first = PreambleBytes.size();
+  PreprocessorOpts.PrecompiledPreambleBytes.second = PreambleEndsAtStartOfLine;
+  PreprocessorOpts.ImplicitPCHInclude = PCHFile.getFilePath();
+  PreprocessorOpts.DisablePCHValidation = true;
+
+  // Remap main file to point to MainFileBuffer.
+  auto MainFilePath = CI.getFrontendOpts().Inputs[0].getFile();
+  PreprocessorOpts.addRemappedFile(MainFilePath, MainFileBuffer);
+}
+
+PrecompiledPreamble::PrecompiledPreamble(
+    TempPCHFile PCHFile, std::vector<char> PreambleBytes,
+    bool PreambleEndsAtStartOfLine,
+    llvm::StringMap<PreambleFileHash> FilesInPreamble)
+    : PCHFile(std::move(PCHFile)), FilesInPreamble(FilesInPreamble),
+      PreambleBytes(std::move(PreambleBytes)),
+      PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {}
+
+llvm::ErrorOr<PrecompiledPreamble::TempPCHFile>
+PrecompiledPreamble::TempPCHFile::CreateNewPreamblePCHFile() {
+  // FIXME: This is a hack so that we can override the preamble file during
+  // crash-recovery testing, which is the only case where the preamble files
+  // are not necessarily cleaned up.
+  const char *TmpFile = ::getenv("CINDEXTEST_PREAMBLE_FILE");
+  if (TmpFile)
+    return TempPCHFile::createFromCustomPath(TmpFile);
+  return TempPCHFile::createInSystemTempDir("preamble", "pch");
+}
+
+llvm::ErrorOr<PrecompiledPreamble::TempPCHFile>
+PrecompiledPreamble::TempPCHFile::createInSystemTempDir(const Twine &Prefix,
+                                                        StringRef Suffix) {
+  llvm::SmallString<64> File;
+  auto EC = llvm::sys::fs::createTemporaryFile(Prefix, Suffix, /*ref*/ File);
+  if (EC)
+    return EC;
+  return TempPCHFile(std::move(File).str());
+}
+
+llvm::ErrorOr<PrecompiledPreamble::TempPCHFile>
+PrecompiledPreamble::TempPCHFile::createFromCustomPath(const Twine &Path) {
+  return TempPCHFile(Path.str());
+}
+
+PrecompiledPreamble::TempPCHFile::TempPCHFile(std::string FilePath)
+    : FilePath(std::move(FilePath)) {
+  TemporaryFiles::getInstance().addFile(*this->FilePath);
+}
+
+PrecompiledPreamble::TempPCHFile::TempPCHFile(TempPCHFile &&Other) {
+  FilePath = std::move(Other.FilePath);
+  Other.FilePath = None;
+}
+
+PrecompiledPreamble::TempPCHFile &PrecompiledPreamble::TempPCHFile::
+operator=(TempPCHFile &&Other) {
+  RemoveFileIfPresent();
+
+  FilePath = std::move(Other.FilePath);
+  Other.FilePath = None;
+  return *this;
+}
+
+PrecompiledPreamble::TempPCHFile::~TempPCHFile() { RemoveFileIfPresent(); }
+
+void PrecompiledPreamble::TempPCHFile::RemoveFileIfPresent() {
+  if (FilePath) {
+    TemporaryFiles::getInstance().removeFile(*FilePath);
+    FilePath = None;
+  }
+}
+
+llvm::StringRef PrecompiledPreamble::TempPCHFile::getFilePath() const {
+  assert(FilePath && "TempPCHFile doesn't have a FilePath. Had it been moved?");
+  return *FilePath;
+}
+
+PrecompiledPreamble::PreambleFileHash
+PrecompiledPreamble::PreambleFileHash::createForFile(off_t Size,
+                                                     time_t ModTime) {
+  PreambleFileHash Result;
+  Result.Size = Size;
+  Result.ModTime = ModTime;
+  Result.MD5 = {};
+  return Result;
+}
+
+PrecompiledPreamble::PreambleFileHash
+PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(
+    const llvm::MemoryBuffer *Buffer) {
+  PreambleFileHash Result;
+  Result.Size = Buffer->getBufferSize();
+  Result.ModTime = 0;
+
+  llvm::MD5 MD5Ctx;
+  MD5Ctx.update(Buffer->getBuffer().data());
+  MD5Ctx.final(Result.MD5);
+
+  return Result;
+}
+
+void PreambleCallbacks::AfterExecute(CompilerInstance &CI) {}
+void PreambleCallbacks::AfterPCHEmitted(ASTWriter &Writer) {}
+void PreambleCallbacks::HandleTopLevelDecl(DeclGroupRef DG) {}
+void PreambleCallbacks::HandleMacroDefined(const Token &MacroNameTok,
+                                           const MacroDirective *MD) {}
+
+std::error_code clang::make_error_code(BuildPreambleError Error) {
+  return std::error_code(static_cast<int>(Error), BuildPreambleErrorCategory());
+}
+
+const char *BuildPreambleErrorCategory::name() const noexcept {
+  return "build-preamble.error";
+}
+
+std::string BuildPreambleErrorCategory::message(int condition) const {
+  switch (static_cast<BuildPreambleError>(condition)) {
+  case BuildPreambleError::PreambleIsEmpty:
+    return "Preamble is empty";
+  case BuildPreambleError::CouldntCreateTempFile:
+    return "Could not create temporary file for PCH";
+  case BuildPreambleError::CouldntCreateTargetInfo:
+    return "CreateTargetInfo() return null";
+  case BuildPreambleError::CouldntCreateVFSOverlay:
+    return "Could not create VFS Overlay";
+  case BuildPreambleError::BeginSourceFileFailed:
+    return "BeginSourceFile() return an error";
+  case BuildPreambleError::CouldntEmitPCH:
+    return "Could not emit PCH";
+  }
+  llvm_unreachable("unexpected BuildPreambleError");
+}




More information about the cfe-commits mailing list