Index: include/clang/Frontend/ASTUnit.h =================================================================== --- include/clang/Frontend/ASTUnit.h +++ include/clang/Frontend/ASTUnit.h @@ -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 @@ /// of that loading. It must be cleared when preamble is recreated. llvm::StringMap PreambleSrcLocCache; -public: - class PreambleData { - const FileEntry *File; - std::vector 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 FilesInPreamble; + /// The contents of the preamble. + llvm::Optional 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 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 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 @@ std::unique_ptr OverrideMainBuffer, IntrusiveRefCntPtr VFS); - struct ComputedPreamble { - llvm::MemoryBuffer *Buffer; - std::unique_ptr Owner; - unsigned Size; - bool PreambleEndsAtStartOfLine; - ComputedPreamble(llvm::MemoryBuffer *Buffer, - std::unique_ptr Owner, unsigned Size, - bool PreambleEndsAtStartOfLine) - : Buffer(Buffer), Owner(std::move(Owner)), Size(Size), - PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {} - }; - ComputedPreamble ComputePreamble(CompilerInvocation &Invocation, - unsigned MaxLines, - IntrusiveRefCntPtr VFS); - std::unique_ptr getMainBufferWithPrecompiledPreamble( std::shared_ptr PCHContainerOps, const CompilerInvocation &PreambleInvocationIn, @@ -607,12 +505,6 @@ void findFileRegionDecls(FileID File, unsigned Offset, unsigned Length, SmallVectorImpl &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 Index: include/clang/Frontend/PrecompiledPreamble.h =================================================================== --- /dev/null +++ include/clang/Frontend/PrecompiledPreamble.h @@ -0,0 +1,256 @@ +//===--- 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 +#include +#include + +namespace llvm { +class MemoryBuffer; +} + +namespace clang { +namespace vfs { +class FileSystem; +} + +class CompilerInstance; +class CompilerInvocation; +class DeclGroupRef; +class PCHContainerOperations; + +/// 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 +/// destructors. +/// An assertion will fire if two PCHTempFiles are created with the same name, +/// so it's not intended to be used outside preamble-handling. +class TempPCHFile { +public: + /// Call llvm::sys::fs::createTemporaryFile to create a new temporary file. + static llvm::ErrorOr 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 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 FilePath; +}; + +/// 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); + +/// 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); + } +}; + +/// 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 { +public: + PrecompiledPreamble(TempPCHFile PCHFile, std::vector PreambleBytes, + bool PreambleEndsAtStartOfLine, + llvm::StringMap FilesInPreamble); + + PrecompiledPreamble(PrecompiledPreamble &&) = default; + PrecompiledPreamble &operator=(PrecompiledPreamble &&) = default; + + /// Contents of the main file that were used for building preamble (i.e. the + /// include header of the file). + const std::vector &getBytes() const; + /// Same as PreambleBounds::PreambleEndsAtStartOfLine. + bool endsAtStartOfLine() const; + /// Other files that were read when building the preamble. + const llvm::StringMap &getFilesInPreamble() const; + +private: + /// Manages a lifetime of temporary file that stores 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 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 PreambleBytes; + /// See PreambleBounds::PreambleEndsAtStartOfLine + bool PreambleEndsAtStartOfLine; + + /// We don't expose PCHFile to avoid changing interface when we'll add an + /// in-memory PCH, so we declare this function as friend so that it has access + /// to PCHFile field. + friend void AddImplicitPreamble(CompilerInvocation &CI, + llvm::MemoryBuffer *MainFileBuffer, + const PrecompiledPreamble &Preamble); +}; + +/// 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); + +/// \brief Try to build PrecompiledPreamble for \p Invocation. +/// +/// \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. +llvm::ErrorOr +BuildPreamble(const CompilerInvocation &Invocation, + const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds, + DiagnosticsEngine &Diagnostics, + IntrusiveRefCntPtr VFS, + std::shared_ptr PCHContainerOps, + PreambleCallbacks &Callbacks); + +/// Check whether PrecompiledPreamble can be reused for the new contents(\p +/// MainFileBuffer) of the main file. +bool CanReusePreamble(const PrecompiledPreamble &Preamble, + const CompilerInvocation &Invocation, + const llvm::MemoryBuffer *MainFileBuffer, + PreambleBounds Bounds, vfs::FileSystem *VFS); + +/// Changes options inside \p CI to use PCH from \p Preamble. Also remaps main +/// file to \p MainFileBuffer. +void AddImplicitPreamble(CompilerInvocation &CI, + llvm::MemoryBuffer *MainFileBuffer, + const PrecompiledPreamble &Preamble); + +/// Remove options related to PrecompiledPreamble from \p CI. Note that it +/// doesn't restore the state \p CI had before calling AddImplicitPreamble, only +/// clears relevant settings, so that preamble is disabled in \p CI. +} // namespace clang + +namespace std { +template <> +struct is_error_code_enum : std::true_type {}; +} // namespace std + +#endif Index: lib/Frontend/ASTUnit.cpp =================================================================== --- lib/Frontend/ASTUnit.cpp +++ lib/Frontend/ASTUnit.cpp @@ -79,17 +79,6 @@ } } }; - - 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 std::unique_ptr valueOrNull(llvm::ErrorOr> Val) { @@ -105,81 +94,99 @@ Output = std::move(*Val); return true; } -} -static llvm::sys::SmartMutex &getOnDiskMutex() { - static llvm::sys::SmartMutex M(/* recursive = */ true); - return M; -} + /// A helper class, storing a either a raw pointer, or unique_ptr to an llvm::MemoryBuffer. + struct PossiblyOwnedBuffer { + PossiblyOwnedBuffer(std::nullptr_t) : Owner(nullptr), Buffer(nullptr) { + } -static void cleanupOnDiskMapAtExit(); + explicit PossiblyOwnedBuffer(llvm::MemoryBuffer *Buffer) + : Owner(nullptr), Buffer(Buffer) {} -typedef llvm::DenseMap> OnDiskDataMap; -static OnDiskDataMap &getOnDiskDataMap() { - static OnDiskDataMap M; - static bool hasRegisteredAtExit = false; - if (!hasRegisteredAtExit) { - hasRegisteredAtExit = true; - atexit(cleanupOnDiskMapAtExit); - } - return M; -} + explicit PossiblyOwnedBuffer(std::unique_ptr Owner) { + this->Owner = std::move(Owner); + this->Buffer = this->Owner.get(); + } -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(); - } -} + operator bool() const { return getBuffer(); } -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(); - return *D; -} + llvm::MemoryBuffer *getBuffer() const { return Buffer; } -static void erasePreambleFile(const ASTUnit *AU) { - getOnDiskData(AU).CleanPreambleFile(); -} + std::unique_ptr getBufferCopy(const Twine& BufferName) const { + return llvm::MemoryBuffer::getMemBufferCopy(getBuffer()->getBuffer(), BufferName); + } -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); - } -} + std::unique_ptr takeOrCopyAndTake(const Twine& BufferName) { + if (Owner) + return std::move(Owner); + if (Buffer) + return getBufferCopy(BufferName); + return nullptr; + } -static void setPreambleFile(const ASTUnit *AU, StringRef preambleFile) { - getOnDiskData(AU).PreambleFile = preambleFile; -} + private: + std::unique_ptr Owner; + llvm::MemoryBuffer *Buffer; + }; +/// \brief Get a source buffer for \p MainFilePath, handling all file-to-file +/// and file-to-buffer remappings inside \p Invocation. +static PossiblyOwnedBuffer +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 BufferOwner; + auto FileStatus = VFS->status(FilePath); + if (FileStatus) { + llvm::sys::fs::UniqueID MainFileID = FileStatus->getUniqueID(); -static const std::string &getPreambleFile(const ASTUnit *AU) { - return getOnDiskData(AU).PreambleFile; -} + // 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; + } + } + } -void OnDiskData::CleanPreambleFile() { - if (!PreambleFile.empty()) { - llvm::sys::fs::remove(PreambleFile); - PreambleFile.clear(); + // 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(RB.second); + } + } + } } -} -void OnDiskData::Cleanup() { - CleanPreambleFile(); + // If the main source file was not remapped, load it now. + if (!Buffer && !BufferOwner) { + BufferOwner = valueOrNull(VFS->getBufferForFile(FilePath)); + if (!BufferOwner) + return nullptr; + } + + if (BufferOwner) + return PossiblyOwnedBuffer(std::move(BufferOwner)); + return PossiblyOwnedBuffer(Buffer); +} } struct ASTUnit::ASTWriterData { @@ -233,9 +240,6 @@ 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 +579,24 @@ /// \brief Diagnostic consumer that saves each diagnostic it is given. class StoredDiagnosticConsumer : public DiagnosticConsumer { - SmallVectorImpl &StoredDiags; + SmallVectorImpl *StoredDiags; + SmallVectorImpl *StandaloneDiags; + const LangOptions *LangOpts; SourceManager *SourceMgr; public: - explicit StoredDiagnosticConsumer( - SmallVectorImpl &StoredDiags) - : StoredDiags(StoredDiags), SourceMgr(nullptr) {} + StoredDiagnosticConsumer( + SmallVectorImpl *StoredDiags, + SmallVectorImpl *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 +615,9 @@ public: CaptureDroppedDiagnostics(bool RequestCapture, DiagnosticsEngine &Diags, - SmallVectorImpl &StoredDiags) - : Diags(Diags), Client(StoredDiags), PreviousClient(nullptr) + SmallVectorImpl *StoredDiags, + SmallVectorImpl *StandaloneDiags) + : Diags(Diags), Client(StoredDiags, StandaloneDiags), PreviousClient(nullptr) { if (RequestCapture || Diags.getClient() == nullptr) { OwningPreviousClient = Diags.takeClient(); @@ -621,16 +634,35 @@ } // 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 StoredDiag = llvm::None; + if (!ResultDiag) { + StoredDiag.emplace(Level, Info); + ResultDiag = StoredDiag.getPointer(); + } + StandaloneDiags->push_back( + makeStandaloneDiagnostic(*LangOpts, *ResultDiag)); + } + } } IntrusiveRefCntPtr ASTUnit::getASTReader() const { @@ -665,7 +697,7 @@ 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::LoadFromASTFile( @@ -780,6 +812,11 @@ 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 +827,7 @@ void MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) override { - Hash = llvm::HashString(MacroNameTok.getIdentifierInfo()->getName(), Hash); + AddDefinedMacroToHash(MacroNameTok, Hash); } }; @@ -916,45 +953,30 @@ } }; -class PrecompilePreambleAction : public ASTFrontendAction { - ASTUnit &Unit; - bool HasEmittedPreamblePCH; - +class ASTUnitPreambleCallbacks : public PreambleCallbacks { public: - explicit PrecompilePreambleAction(ASTUnit &Unit) - : Unit(Unit), HasEmittedPreamblePCH(false) {} + ASTUnitPreambleCallbacks(llvm::SmallVectorImpl &StoredDiags) + : StoredDiags(StoredDiags) {} - std::unique_ptr 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 takeTopLevelDecls() { return std::move(TopLevelDecls); } -class PrecompilePreambleConsumer : public PCHGenerator { - ASTUnit &Unit; - unsigned &Hash; - std::vector TopLevelDecls; - PrecompilePreambleAction *Action; - std::unique_ptr Out; + std::vector takeTopLevelDeclIDs() { + return std::move(TopLevelDeclIDs); + } -public: - PrecompilePreambleConsumer(ASTUnit &Unit, PrecompilePreambleAction *Action, - const Preprocessor &PP, StringRef isysroot, - std::unique_ptr Out) - : PCHGenerator(PP, "", isysroot, std::make_shared(), - ArrayRef>(), - /*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 +987,23 @@ 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 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 &StoredDiags; + unsigned Hash = 0; + std::vector TopLevelDecls; + std::vector TopLevelDeclIDs; + llvm::SmallVector PreambleDiags; }; } // anonymous namespace -std::unique_ptr -PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) { - std::string Sysroot; - std::string OutputFile; - std::unique_ptr OS = - GeneratePCHAction::ComputeASTConsumerArguments(CI, InFile, Sysroot, - OutputFile); - if (!OS) - return nullptr; - - if (!CI.getFrontendOpts().RelocatablePCH) - Sysroot.clear(); - - CI.getPreprocessor().addPPCallbacks( - llvm::make_unique( - Unit.getCurrentTopLevelHashValue())); - return llvm::make_unique( - Unit, this, CI.getPreprocessor(), Sysroot, std::move(OS)); -} - static bool isNonDriverDiag(const StoredDiagnostic &StoredDiag) { return StoredDiag.getLocation().isValid(); } @@ -1125,15 +1111,9 @@ // 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"); + AddImplicitPreamble(Clang->getInvocation(), OverrideMainBuffer.get(), *Preamble); // 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 +1166,6 @@ 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) { - 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 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(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 makeStandaloneRange(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts) { @@ -1367,135 +1237,40 @@ const CompilerInvocation &PreambleInvocationIn, IntrusiveRefCntPtr VFS, bool AllowRebuild, unsigned MaxLines) { - assert(VFS && "VFS is null"); - - auto PreambleInvocation = - std::make_shared(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(); + PossiblyOwnedBuffer 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 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; + PreambleBounds Bounds = + ComputePreambleBounds(*PreambleInvocationIn.getLangOpts(), + MainFileBuffer.getBuffer(), MaxLines); + if (!Bounds.Size) + return nullptr; - vfs::Status Status; - if (!moveOnNoError(VFS->status(RB.first), Status)) { - AnyFileChanged = true; - break; - } + if (Preamble) { + if (CanReusePreamble(*Preamble, PreambleInvocationIn, + MainFileBuffer.getBuffer(), Bounds, VFS.get())) { + // Okay! We can re-use the precompiled preamble. - OverriddenFiles[Status.getUniqueID()] = - PreambleFileHash::createForMemoryBuffer(RB.second); - } - - // Check whether anything has changed. - for (llvm::StringMap::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; - } + // 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::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.takeOrCopyAndTake(MainFilePath); + } 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 +1281,63 @@ 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 Clang( - new CompilerInstance(std::move(PCHContainerOps))); - - // Recover resources if we crash before exiting this method. - llvm::CrashRecoveryContextCleanupRegistrar - 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(); - Clang->addDependencyCollector(PreambleDepCollector); - - std::unique_ptr 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 NewPreambleDiagsStandalone; + SmallVector NewPreambleDiags; + ASTUnitPreambleCallbacks Callbacks(NewPreambleDiags); + { + llvm::Optional 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 NewPreamble = + BuildPreamble(PreambleInvocationIn, MainFileBuffer.getBuffer(), Bounds, + *Diagnostics, VFS, PCHContainerOps, Callbacks); + if (NewPreamble) { + Preamble = std::move(*NewPreamble); + PreambleRebuildCounter = 1; + } else { + switch (static_cast(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 +1347,12 @@ PreambleTopLevelHashValue = CurrentTopLevelHashValue; } - return llvm::MemoryBuffer::getMemBufferCopy(NewPreamble.Buffer->getBuffer(), - MainFilename); + return MainFileBuffer.takeOrCopyAndTake(MainFilePath); } void ASTUnit::RealizeTopLevelDeclsFromPreamble() { + assert(Preamble && "Should only be called when preamble was built"); + std::vector Resolved; Resolved.reserve(TopLevelDeclsInPreamble.size()); ExternalASTSource &Source = *getASTContext().getExternalSource(); @@ -1995,8 +1670,8 @@ { - CaptureDroppedDiagnostics Capture(CaptureDiagnostics, *Diags, - StoredDiagnostics); + CaptureDroppedDiagnostics Capture(CaptureDiagnostics, *Diags, + &StoredDiagnostics, nullptr); CI = clang::createInvocationFromCommandLine( llvm::makeArrayRef(ArgBegin, ArgEnd), Diags); @@ -2101,7 +1776,7 @@ // If we have a preamble file lying around, or if we might try to // build a precompiled preamble, do so now. std::unique_ptr OverrideMainBuffer; - if (!getPreambleFile(this).empty() || PreambleRebuildCounter > 0) + if (Preamble || PreambleRebuildCounter > 0) OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS); @@ -2435,7 +2110,7 @@ Clang->setDiagnostics(&Diag); CaptureDroppedDiagnostics Capture(true, Clang->getDiagnostics(), - StoredDiagnostics); + &StoredDiagnostics, nullptr); ProcessWarningOptions(Diag, Inv.getDiagnosticOpts()); // Create the target instance. @@ -2484,7 +2159,7 @@ // point is within the main file, after the end of the precompiled // preamble. std::unique_ptr OverrideMainBuffer; - if (!getPreambleFile(this).empty()) { + if (Preamble) { std::string CompleteFilePath(File); auto VFS = FileMgr.getVirtualFileSystem(); @@ -2506,14 +2181,8 @@ // 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"); + AddImplicitPreamble(Clang->getInvocation(), OverrideMainBuffer.get(), *Preamble); OwnedBuffers.push_back(OverrideMainBuffer.release()); } else { PreprocessorOpts.PrecompiledPreambleBytes.first = 0; @@ -2760,11 +2429,11 @@ 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->getBytes().size()) { SourceLocation FileLoc = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()); return FileLoc.getLocWithOffset(Offs); @@ -2781,12 +2450,12 @@ 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->getBytes().size()) { SourceLocation FileLoc = SourceMgr->getLocForStartOfFile(PreambleID); return FileLoc.getLocWithOffset(Offs); } @@ -2932,17 +2601,6 @@ 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); Index: lib/Frontend/CMakeLists.txt =================================================================== --- lib/Frontend/CMakeLists.txt +++ lib/Frontend/CMakeLists.txt @@ -38,6 +38,7 @@ ModuleDependencyCollector.cpp MultiplexConsumer.cpp PCHContainerOperations.cpp + PrecompiledPreamble.cpp PrintPreprocessedOutput.cpp SerializedDiagnosticPrinter.cpp SerializedDiagnosticReader.cpp Index: lib/Frontend/PrecompiledPreamble.cpp =================================================================== --- /dev/null +++ lib/Frontend/PrecompiledPreamble.cpp @@ -0,0 +1,573 @@ +//===--- 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 + 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 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 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 Out) + : PCHGenerator(PP, "", isysroot, std::make_shared(), + ArrayRef>(), + /*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 Empty; + getPCH() = std::move(Empty); + + Action.setEmittedPreamblePCH(getWriter()); + } + +private: + PrecompilePreambleAction &Action; + std::unique_ptr Out; +}; + +template bool moveOnNoError(llvm::ErrorOr Val, T &Output) { + if (!Val) + return false; + Output = std::move(*Val); + return true; +} + +/// \brief Simple function to create a path for a preamble precompiled header. +/// \return TempPCHFile instance that will manage the lifetime of the temporary +/// file. +llvm::ErrorOr 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"); +} + +} // namespace + +llvm::ErrorOr +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 +TempPCHFile::createFromCustomPath(const Twine &Path) { + return TempPCHFile(Path.str()); +} + +TempPCHFile::TempPCHFile(std::string FilePath) : FilePath(std::move(FilePath)) { + TemporaryFiles::getInstance().addFile(*this->FilePath); +} + +TempPCHFile::TempPCHFile(TempPCHFile &&Other) { + FilePath = std::move(Other.FilePath); + Other.FilePath = None; +} + +TempPCHFile &TempPCHFile::operator=(TempPCHFile &&Other) { + RemoveFileIfPresent(); + + FilePath = std::move(Other.FilePath); + Other.FilePath = None; + return *this; +} + +TempPCHFile::~TempPCHFile() { RemoveFileIfPresent(); } + +void TempPCHFile::RemoveFileIfPresent() { + if (FilePath) { + TemporaryFiles::getInstance().removeFile(*FilePath); + FilePath = None; + } +} + +llvm::StringRef TempPCHFile::getFilePath() const { + assert(FilePath && "TempPCHFile doesn't have a FilePath. Had it been moved?"); + return *FilePath; +} + +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); +} + +PreambleFileHash PreambleFileHash::createForFile(off_t Size, time_t ModTime) { + PreambleFileHash Result; + Result.Size = Size; + Result.ModTime = ModTime; + Result.MD5 = {}; + return Result; +} + +PreambleFileHash +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; +} + +bool clang::operator==(const PreambleFileHash &LHS, + const PreambleFileHash &RHS) { + return LHS.Size == RHS.Size && LHS.ModTime == RHS.ModTime && + LHS.MD5 == RHS.MD5; +} + +void PreambleCallbacks::AfterExecute(CompilerInstance &CI) {} +void PreambleCallbacks::AfterPCHEmitted(ASTWriter &Writer) {} +void PreambleCallbacks::HandleTopLevelDecl(DeclGroupRef DG) {} +void PreambleCallbacks::HandleMacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) {} + +PrecompiledPreamble::PrecompiledPreamble( + TempPCHFile PCHFile, std::vector PreambleBytes, + bool PreambleEndsAtStartOfLine, + llvm::StringMap FilesInPreamble) + : PCHFile(std::move(PCHFile)), FilesInPreamble(FilesInPreamble), + PreambleBytes(std::move(PreambleBytes)), + PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) {} + +const std::vector &PrecompiledPreamble::getBytes() const { + return PreambleBytes; +} + +bool PrecompiledPreamble::endsAtStartOfLine() const { + return PreambleEndsAtStartOfLine; +} + +const llvm::StringMap & +PrecompiledPreamble::getFilesInPreamble() const { + return FilesInPreamble; +} + +std::unique_ptr +PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::string Sysroot; + std::string OutputFile; + std::unique_ptr OS = + GeneratePCHAction::ComputeASTConsumerArguments(CI, InFile, Sysroot, + OutputFile); + if (!OS) + return nullptr; + + if (!CI.getFrontendOpts().RelocatablePCH) + Sysroot.clear(); + + CI.getPreprocessor().addPPCallbacks( + llvm::make_unique(Callbacks)); + return llvm::make_unique( + *this, CI.getPreprocessor(), Sysroot, std::move(OS)); +} + +std::error_code clang::make_error_code(BuildPreambleError Error) { + return std::error_code(static_cast(Error), BuildPreambleErrorCategory()); +} + +const char *BuildPreambleErrorCategory::name() const noexcept { + return "build-preamble.error"; +} + +std::string BuildPreambleErrorCategory::message(int condition) const { + switch (static_cast(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"); +} + +llvm::ErrorOr +clang::BuildPreamble(const CompilerInvocation &Invocation, + const llvm::MemoryBuffer *MainFileBuffer, + PreambleBounds Bounds, DiagnosticsEngine &Diagnostics, + IntrusiveRefCntPtr VFS, + std::shared_ptr PCHContainerOps, + PreambleCallbacks &Callbacks) { + assert(VFS && "VFS is null"); + + if (!Bounds.Size) + return BuildPreambleError::PreambleIsEmpty; + + auto PreambleInvocation = std::make_shared(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 PreamblePCHFile = CreateNewPreamblePCHFile(); + if (!PreamblePCHFile) + return BuildPreambleError::CouldntCreateTempFile; + + // Save the preamble text for later; we'll need to compare against it for + // subsequent reparses. + std::vector 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 Clang( + new CompilerInstance(std::move(PCHContainerOps))); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar 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(); + 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 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 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()] = + PreambleFileHash::createForFile(File->getSize(), ModTime); + } else { + llvm::MemoryBuffer *Buffer = SourceMgr.getMemoryBufferForFile(File); + FilesInPreamble[File->getName()] = + PreambleFileHash::createForMemoryBuffer(Buffer); + } + } + + return PrecompiledPreamble( + std::move(*PreamblePCHFile), std::move(PreambleBytes), + PreambleEndsAtStartOfLine, std::move(FilesInPreamble)); +} + +bool clang::CanReusePreamble(const PrecompiledPreamble &Preamble, + const CompilerInvocation &Invocation, + const llvm::MemoryBuffer *MainFileBuffer, + PreambleBounds Bounds, vfs::FileSystem *VFS) { + + assert( + Bounds.Size <= MainFileBuffer->getBufferSize() && + "Buffer is too large. Bounds were calculated from a different buffer?"); + + auto PreambleInvocation = std::make_shared(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 (Preamble.getBytes().size() != Bounds.Size || + Preamble.endsAtStartOfLine() != Bounds.PreambleEndsAtStartOfLine || + memcmp(Preamble.getBytes().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 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 : Preamble.getFilesInPreamble()) { + 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::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 clang::AddImplicitPreamble(CompilerInvocation &CI, + llvm::MemoryBuffer *MainFileBuffer, + const PrecompiledPreamble &Preamble) { + auto &PreprocessorOpts = CI.getPreprocessorOpts(); + + // Configure ImpicitPCHInclude. + PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.getBytes().size(); + PreprocessorOpts.PrecompiledPreambleBytes.second = + Preamble.endsAtStartOfLine(); + PreprocessorOpts.ImplicitPCHInclude = Preamble.PCHFile.getFilePath(); + PreprocessorOpts.DisablePCHValidation = true; + + // Remap main file to point to MainFileBuffer. + auto MainFilePath = CI.getFrontendOpts().Inputs[0].getFile(); + PreprocessorOpts.addRemappedFile(MainFilePath, MainFileBuffer); +}