From cc5888d833caf90ebda37f24da40d2cd06b4d820 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Sat, 31 Jul 2010 00:40:00 +0000 Subject: [PATCH] Implement dependency analysis for the precompiled preamble. If any of the files in the precompiled preamble have changed since it was build, force the preamble to be rebuilt. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@109937 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/SourceManager.h | 6 ++ include/clang/Frontend/ASTUnit.h | 13 +++- lib/Basic/Diagnostic.cpp | 33 ++++----- lib/Frontend/ASTUnit.cpp | 105 ++++++++++++++++++++++++---- 4 files changed, 125 insertions(+), 32 deletions(-) diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h index b6a1ac4492..dc314f1f07 100644 --- a/include/clang/Basic/SourceManager.h +++ b/include/clang/Basic/SourceManager.h @@ -110,6 +110,12 @@ namespace SrcMgr { Buffer.setPointer(B); Buffer.setInt(false); } + + /// \brief Get the underlying buffer, returning NULL if the buffer is not + /// yet available. + const llvm::MemoryBuffer *getRawBuffer() const { + return Buffer.getPointer(); + } /// \brief Replace the existing buffer (which will be deleted) /// with the given buffer. diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index 56e73d9be4..b9db4beafd 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -21,6 +21,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Index/ASTLocation.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" #include "llvm/System/Path.h" #include "llvm/Support/Timer.h" #include @@ -28,6 +29,7 @@ #include #include #include +#include namespace llvm { class MemoryBuffer; @@ -135,14 +137,21 @@ private: /// \brief The size of the source buffer that we've reserved for the main /// file within the precompiled preamble. unsigned PreambleReservedSize; - + + /// \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; + /// \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. llvm::MemoryBuffer *SavedMainFileBuffer; /// \brief The group of timers associated with this translation unit. - llvm::OwningPtr TimerGroup; + llvm::OwningPtr TimerGroup; /// \brief The timers we've created from the various parses, reparses, etc. /// involved in this translation unit. diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index 74937910d9..0dc57e4ecb 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -251,6 +251,23 @@ Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) { ArgToStringFn = DummyArgToStringFn; ArgToStringCookie = 0; + AllExtensionsSilenced = 0; + IgnoreAllWarnings = false; + WarningsAsErrors = false; + ErrorsAsFatal = false; + SuppressSystemWarnings = false; + SuppressAllDiagnostics = false; + ShowOverloads = Ovl_All; + ExtBehavior = Ext_Ignore; + + ErrorLimit = 0; + TemplateBacktraceLimit = 0; + CustomDiagInfo = 0; + + // Set all mappings to 'unset'. + DiagMappingsStack.clear(); + DiagMappingsStack.push_back(DiagMappings()); + Reset(); } @@ -315,31 +332,15 @@ bool Diagnostic::isBuiltinExtensionDiag(unsigned DiagID, } void Diagnostic::Reset() { - AllExtensionsSilenced = 0; - IgnoreAllWarnings = false; - WarningsAsErrors = false; - ErrorsAsFatal = false; - SuppressSystemWarnings = false; - SuppressAllDiagnostics = false; - ShowOverloads = Ovl_All; - ExtBehavior = Ext_Ignore; - ErrorOccurred = false; FatalErrorOccurred = false; - ErrorLimit = 0; - TemplateBacktraceLimit = 0; NumWarnings = 0; NumErrors = 0; NumErrorsSuppressed = 0; - CustomDiagInfo = 0; CurDiagID = ~0U; LastDiagLevel = Ignored; DelayedDiagID = 0; - - // Set all mappings to 'unset'. - DiagMappingsStack.clear(); - DiagMappingsStack.push_back(DiagMappings()); } /// getDescription - Given a diagnostic ID, return a description of the diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 7f42fa949e..d7597bcf5b 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -36,6 +36,7 @@ #include "llvm/Support/Timer.h" #include #include +#include using namespace clang; ASTUnit::ASTUnit(bool _MainFileIsAST) @@ -626,14 +627,72 @@ llvm::MemoryBuffer *ASTUnit::BuildPrecompiledPreamble() { NewPreamble.second.first) == 0) { // The preamble has not changed. We may be able to re-use the precompiled // preamble. - // FIXME: Check that none of the files used by the preamble have changed. + // 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. + llvm::StringMap > OverriddenFiles; + for (PreprocessorOptions::remapped_file_iterator + R = PreprocessorOpts.remapped_file_begin(), + REnd = PreprocessorOpts.remapped_file_end(); + !AnyFileChanged && R != REnd; + ++R) { + struct stat StatBuf; + if (stat(R->second.c_str(), &StatBuf)) { + // If we can't stat the file we're remapping to, assume that something + // horrible happened. + AnyFileChanged = true; + break; + } - // Okay! Re-use the precompiled preamble. - return CreatePaddedMainFileBuffer(NewPreamble.first, - CreatedPreambleBuffer, - PreambleReservedSize, - FrontendOpts.Inputs[0].second); + OverriddenFiles[R->first] = std::make_pair(StatBuf.st_size, + StatBuf.st_mtime); + } + for (PreprocessorOptions::remapped_file_buffer_iterator + R = PreprocessorOpts.remapped_file_buffer_begin(), + REnd = PreprocessorOpts.remapped_file_buffer_end(); + !AnyFileChanged && R != REnd; + ++R) { + // FIXME: Should we actually compare the contents of file->buffer + // remappings? + OverriddenFiles[R->first] = std::make_pair(R->second->getBufferSize(), + 0); + } + + // Check whether anything has changed. + for (llvm::StringMap >::iterator + F = FilesInPreamble.begin(), FEnd = FilesInPreamble.end(); + !AnyFileChanged && F != FEnd; + ++F) { + llvm::StringMap >::iterator Overridden + = OverriddenFiles.find(F->first()); + 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. + struct stat StatBuf; + if (stat(F->first(), &StatBuf)) { + // If we can't stat the file, assume that something horrible happened. + AnyFileChanged = true; + } else if (StatBuf.st_size != F->second.first || + StatBuf.st_mtime != F->second.second) + AnyFileChanged = true; + } + + if (!AnyFileChanged) { + // Okay! Re-use the precompiled preamble. + return CreatePaddedMainFileBuffer(NewPreamble.first, + CreatedPreambleBuffer, + PreambleReservedSize, + FrontendOpts.Inputs[0].second); + } } // We can't reuse the previously-computed preamble. Build a new one. @@ -768,14 +827,31 @@ llvm::MemoryBuffer *ASTUnit::BuildPrecompiledPreamble() { delete NewPreamble.first; if (PreambleTimer) PreambleTimer->stopTimer(); - if (PreambleTimer) - PreambleTimer->stopTimer(); return 0; } // Keep track of the preamble we precompiled. PreambleFile = FrontendOpts.OutputFile; + + // 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(); + const llvm::MemoryBuffer *MainFileBuffer + = SourceMgr.getBuffer(SourceMgr.getMainFileID()); + for (SourceManager::fileinfo_iterator F = SourceMgr.fileinfo_begin(), + FEnd = SourceMgr.fileinfo_end(); + F != FEnd; + ++F) { + const FileEntry *File = F->second->Entry; + if (!File || F->second->getRawBuffer() == MainFileBuffer) + continue; + + FilesInPreamble[File->getName()] + = std::make_pair(F->second->getSize(), File->getModificationTime()); + } + if (PreambleTimer) PreambleTimer->stopTimer(); @@ -913,6 +989,13 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) { Timers.push_back(ReparsingTimer); } + // Remap files. + // FIXME: Do we want to remove old mappings for these files? + Invocation->getPreprocessorOpts().clearRemappedFiles(); + for (unsigned I = 0; I != NumRemappedFiles; ++I) + Invocation->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, + RemappedFiles[I].second); + // If we have a preamble file lying around, build or reuse the precompiled // preamble. llvm::MemoryBuffer *OverrideMainBuffer = 0; @@ -922,12 +1005,6 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) { // Clear out the diagnostics state. getDiagnostics().Reset(); - // Remap files. - Invocation->getPreprocessorOpts().clearRemappedFiles(); - for (unsigned I = 0; I != NumRemappedFiles; ++I) - Invocation->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, - RemappedFiles[I].second); - // Parse the sources bool Result = Parse(OverrideMainBuffer); if (ReparsingTimer) -- 2.40.0