]> granicus.if.org Git - clang/commitdiff
Once we've built a precompiled preamble, keep track of the details of
authorDouglas Gregor <dgregor@apple.com>
Fri, 23 Jul 2010 23:58:40 +0000 (23:58 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 23 Jul 2010 23:58:40 +0000 (23:58 +0000)
that preamble (the preamble text, preamble file, reserved main file
size). Check these details when we try to rebuild the precompiled
preamble, and when nothing has changed, re-use the precompiled
preamble.

This code is still very much a WIP, and can't even properly be tested
because we have no way to use the precompiled preamble yet. "Trust me"

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@109294 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Frontend/ASTUnit.h
lib/Frontend/ASTUnit.cpp

index 950da64c2280ac1c3ef34f73c401ca6a5f9b024d..19c7ae5078f40fe4b3d8fc991a0dbd745b7972a2 100644 (file)
@@ -45,14 +45,6 @@ class SourceManager;
 class TargetInfo;
 
 using namespace idx;
-
-class PrecompiledPreamble {
-  llvm::sys::Path PreambleFile;
-  
-public:
-  ~PrecompiledPreamble();
-  
-};
   
 /// \brief Utility class for loading a ASTContext from a PCH file.
 ///
@@ -125,6 +117,17 @@ private:
   unsigned int ConcurrencyCheckValue;
   static const unsigned int CheckLocked = 28573289;
   static const unsigned int CheckUnlocked = 9803453;
+
+  /// \brief The file in which the precompiled preamble is stored.
+  llvm::sys::Path PreambleFile;
+  
+  /// \brief The contents of the preamble that has been precompiled to
+  /// \c PreambleFile.
+  std::vector<char> Preamble;
+
+  /// \brief The size of the source buffer that we've reserved for the main 
+  /// file within the precompiled preamble.
+  unsigned PreambleReservedSize;
   
   ASTUnit(const ASTUnit&); // DO NOT IMPLEMENT
   ASTUnit &operator=(const ASTUnit &); // DO NOT IMPLEMENT
@@ -133,7 +136,12 @@ private:
 
   void CleanTemporaryFiles();
   bool Parse();
-  void BuildPrecompiledPreamble();
+  
+  std::pair<llvm::MemoryBuffer *, unsigned> ComputePreamble(
+                                                CompilerInvocation &Invocation,
+                                                          bool &CreatedBuffer);
+  
+  std::pair<llvm::MemoryBuffer *, bool> BuildPrecompiledPreamble();
   
 public:
   class ConcurrencyCheck {
index 41627a547457af693059a8cba50682a55ad6b18f..a1441c6cd3c10ab91b25d42e4d98e8441fd0a7cc 100644 (file)
 #include <cstdio>
 using namespace clang;
 
-PrecompiledPreamble::~PrecompiledPreamble() {
-  PreambleFile.eraseFromDisk();
-}
-
 ASTUnit::ASTUnit(bool _MainFileIsAST)
   : CaptureDiagnostics(false), MainFileIsAST(_MainFileIsAST), 
     ConcurrencyCheckValue(CheckUnlocked) { }
@@ -48,6 +44,8 @@ ASTUnit::ASTUnit(bool _MainFileIsAST)
 ASTUnit::~ASTUnit() {
   ConcurrencyCheckValue = CheckLocked;
   CleanTemporaryFiles();
+  if (!PreambleFile.empty())
+    PreambleFile.eraseFromDisk();
 }
 
 void ASTUnit::CleanTemporaryFiles() {
@@ -427,35 +425,41 @@ static std::string GetPreamblePCHPath() {
   return P.str();
 }
 
-void ASTUnit::BuildPrecompiledPreamble() {
-  CompilerInvocation PreambleInvocation(*Invocation);
-  FrontendOptions &FrontendOpts = PreambleInvocation.getFrontendOpts();
+/// \brief Compute the preamble for the main file, providing
+std::pair<llvm::MemoryBuffer *, unsigned> 
+ASTUnit::ComputePreamble(CompilerInvocation &Invocation, bool &CreatedBuffer) {
+  FrontendOptions &FrontendOpts = Invocation.getFrontendOpts();
   PreprocessorOptions &PreprocessorOpts
-    = PreambleInvocation.getPreprocessorOpts();
-
+    = Invocation.getPreprocessorOpts();
+  CreatedBuffer = false;
+  
   // 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 = 0;  
+  llvm::MemoryBuffer *Buffer = 0;
   llvm::sys::PathWithStatus MainFilePath(FrontendOpts.Inputs[0].second);
   if (const llvm::sys::FileStatus *MainFileStatus = MainFilePath.getFileStatus()) {
     // Check whether there is a file-file remapping of the main file
     for (PreprocessorOptions::remapped_file_iterator
-           M = PreprocessorOpts.remapped_file_begin(),
-           E = PreprocessorOpts.remapped_file_end();
+          M = PreprocessorOpts.remapped_file_begin(),
+          E = PreprocessorOpts.remapped_file_end();
          M != E;
          ++M) {
       llvm::sys::PathWithStatus MPath(M->first);    
       if (const llvm::sys::FileStatus *MStatus = MPath.getFileStatus()) {
         if (MainFileStatus->uniqueID == MStatus->uniqueID) {
           // We found a remapping. Try to load the resulting, remapped source.
-          if (Buffer)
+          if (CreatedBuffer) {
             delete Buffer;
+            CreatedBuffer = false;
+          }
+          
           Buffer = llvm::MemoryBuffer::getFile(M->second);
           if (!Buffer)
-            return;
+            return std::make_pair((llvm::MemoryBuffer*)0, 0);
+          CreatedBuffer = true;
           
-          // Remove the file-file remapping.
+          // Remove this remapping. We've captured the buffer already.
           M = PreprocessorOpts.eraseRemappedFile(M);
           E = PreprocessorOpts.remapped_file_end();
         }
@@ -473,58 +477,135 @@ void ASTUnit::BuildPrecompiledPreamble() {
       if (const llvm::sys::FileStatus *MStatus = MPath.getFileStatus()) {
         if (MainFileStatus->uniqueID == MStatus->uniqueID) {
           // We found a remapping. 
-          if (Buffer)
+          if (CreatedBuffer) {
             delete Buffer;
-          Buffer = const_cast<llvm::MemoryBuffer *>(M->second);
+            CreatedBuffer = false;
+          }
           
-          // Remove the file-buffer remapping.
+          Buffer = const_cast<llvm::MemoryBuffer *>(M->second);
+
+          // Remove this remapping. We've captured the buffer already.
           M = PreprocessorOpts.eraseRemappedFile(M);
           E = PreprocessorOpts.remapped_file_buffer_end();
         }
       }
-    }    
+    }
   }
   
   // If the main source file was not remapped, load it now.
   if (!Buffer) {
     Buffer = llvm::MemoryBuffer::getFile(FrontendOpts.Inputs[0].second);
     if (!Buffer)
-      return;
+      return std::make_pair((llvm::MemoryBuffer*)0, 0);    
+    
+    CreatedBuffer = true;
+  }
+  
+  return std::make_pair(Buffer, Lexer::ComputePreamble(Buffer));
+}
+
+/// \brief Attempt to build or re-use a precompiled preamble when (re-)parsing
+/// the source file.
+///
+/// This routine will compute the preamble of the main source file. If a
+/// non-trivial preamble is found, it will precompile that preamble into a 
+/// precompiled header so that the precompiled preamble can be used to reduce
+/// reparsing time. If a precompiled preamble has already been constructed,
+/// this routine will determine if it is still valid and, if so, avoid 
+/// rebuilding the precompiled preamble.
+///
+/// \returns A pair of (main-buffer, created), where main-buffer is the buffer
+/// containing the contents of the main file and "created" is a boolean flag 
+/// that is true if the buffer was created by this routine (and, therefore,
+/// should be destroyed by the caller). The buffer will only be non-NULL when
+/// a precompiled preamble has been generated.
+std::pair<llvm::MemoryBuffer *, bool> ASTUnit::BuildPrecompiledPreamble() {
+  typedef std::pair<llvm::MemoryBuffer *, bool> Result;
+  
+  CompilerInvocation PreambleInvocation(*Invocation);
+  FrontendOptions &FrontendOpts = PreambleInvocation.getFrontendOpts();
+  PreprocessorOptions &PreprocessorOpts
+    = PreambleInvocation.getPreprocessorOpts();
+
+  bool CreatedPreambleBuffer = false;
+  std::pair<llvm::MemoryBuffer *, unsigned> NewPreamble 
+    = ComputePreamble(PreambleInvocation, CreatedPreambleBuffer);
+
+  if (!NewPreamble.second) {
+    // 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();
+    if (!PreambleFile.empty()) {
+      PreambleFile.eraseFromDisk();
+      PreambleFile.clear();
+    }
+    if (CreatedPreambleBuffer)
+      delete NewPreamble.first;
+    
+    return Result(0, false);
   }
   
-  // Try to compute the preamble.
-  unsigned PreambleLength = Lexer::ComputePreamble(Buffer);
-  if (PreambleLength == 0)
-    return;
+  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.second &&
+        NewPreamble.first->getBufferSize() < PreambleReservedSize &&
+        memcmp(&Preamble[0], NewPreamble.first->getBufferStart(),
+               NewPreamble.second) == 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.
+          
+          
+      // Okay! Re-use the precompiled preamble.
+      return Result(NewPreamble.first, CreatedPreambleBuffer);
+    }
+    
+    // We can't reuse the previously-computed preamble. Build a new one.
+    Preamble.clear();
+    PreambleFile.eraseFromDisk();
+  } 
+    
+  // We did not previously compute a preamble, or it can't be reused anyway.
   
   // Create a new buffer that stores the preamble. The buffer also contains
   // extra space for the original contents of the file (which will be present
   // when we actually parse the file) along with more room in case the file
-  // grows.
-  unsigned PreambleBufferSize = Buffer->getBufferSize();
-  if (PreambleBufferSize < 4096)
-    PreambleBufferSize = 8192;
+  // grows.  
+  PreambleReservedSize = NewPreamble.first->getBufferSize();
+  if (PreambleReservedSize < 4096)
+    PreambleReservedSize = 8192;
   else
-    PreambleBufferSize *= 2;
-  
+    PreambleReservedSize *= 2;
+
   llvm::MemoryBuffer *PreambleBuffer
-    = llvm::MemoryBuffer::getNewUninitMemBuffer(PreambleBufferSize,
+    = llvm::MemoryBuffer::getNewUninitMemBuffer(PreambleReservedSize,
                                                 FrontendOpts.Inputs[0].second);
   memcpy(const_cast<char*>(PreambleBuffer->getBufferStart()), 
-         Buffer->getBufferStart(), PreambleLength);
-  memset(const_cast<char*>(PreambleBuffer->getBufferStart()) + PreambleLength
-         ' ', PreambleBufferSize - PreambleLength - 1);
+         NewPreamble.first->getBufferStart(), Preamble.size());
+  memset(const_cast<char*>(PreambleBuffer->getBufferStart()) + Preamble.size()
+         ' ', PreambleReservedSize - Preamble.size() - 1);
   const_cast<char*>(PreambleBuffer->getBufferEnd())[-1] = 0;
-  delete Buffer;
+  
+  // Save the preamble text for later; we'll need to compare against it for
+  // subsequent reparses.
+  Preamble.assign(NewPreamble.first->getBufferStart(), 
+                  NewPreamble.first->getBufferStart() + Preamble.size());
   
   // Remap the main source file to the preamble buffer.
+  llvm::sys::PathWithStatus MainFilePath(FrontendOpts.Inputs[0].second);
   PreprocessorOpts.addRemappedFile(MainFilePath.str(), PreambleBuffer);
   
   // Tell the compiler invocation to generate a temporary precompiled header.
   FrontendOpts.ProgramAction = frontend::GeneratePCH;
   // FIXME: Set ChainedPCH, once it is ready.
   // FIXME: Generate the precompiled header into memory?
-  FrontendOpts.OutputFile = GetPreamblePCHPath();
+  if (PreambleFile.isEmpty())
+    FrontendOpts.OutputFile = GetPreamblePCHPath();
+  else
+    FrontendOpts.OutputFile = PreambleFile.str();
   
   // Create the compiler instance to use for building the precompiled preamble.
   CompilerInstance Clang;
@@ -540,7 +621,12 @@ void ASTUnit::BuildPrecompiledPreamble() {
                                                Clang.getTargetOpts()));
   if (!Clang.hasTarget()) {
     Clang.takeDiagnosticClient();
-    return;
+    llvm::sys::Path(FrontendOpts.OutputFile).eraseFromDisk();
+    Preamble.clear();
+    if (CreatedPreambleBuffer)
+      delete NewPreamble.first;
+
+    return Result(0, false);
   }
   
   // Inform the target of the language options.
@@ -577,7 +663,12 @@ void ASTUnit::BuildPrecompiledPreamble() {
                             Clang.getFrontendOpts().Inputs[0].first)) {
     Clang.takeDiagnosticClient();
     Clang.takeInvocation();
-    return;
+    llvm::sys::Path(FrontendOpts.OutputFile).eraseFromDisk();
+    Preamble.clear();
+    if (CreatedPreambleBuffer)
+      delete NewPreamble.first;
+    
+    return Result(0, false);
   }
   
   Act->Execute();
@@ -585,8 +676,22 @@ void ASTUnit::BuildPrecompiledPreamble() {
   Clang.takeDiagnosticClient();
   Clang.takeInvocation();
   
-  // FIXME: Keep track of the actual preamble header we created!
+  if (Diagnostics->getNumErrors() > 0) {
+    // There were errors parsing the preamble, so no precompiled header was
+    // generated. Forget that we even tried.
+    // FIXME: Should we leave a note for ourselves to try again?
+    llvm::sys::Path(FrontendOpts.OutputFile).eraseFromDisk();
+    Preamble.clear();
+    if (CreatedPreambleBuffer)
+      delete NewPreamble.first;
+    
+    return Result(0, false);
+  }
+  
+  // Keep track of the preamble we precompiled.
+  PreambleFile = FrontendOpts.OutputFile;
   fprintf(stderr, "Preamble PCH: %s\n", FrontendOpts.OutputFile.c_str());
+  return Result(NewPreamble.first, CreatedPreambleBuffer);
 }
 
 ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
@@ -609,12 +714,17 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
   AST->OnlyLocalDecls = OnlyLocalDecls;
   AST->Invocation.reset(CI);
   
+  std::pair<llvm::MemoryBuffer *, bool> PrecompiledPreamble;
+  
   if (PrecompilePreamble)
-    AST->BuildPrecompiledPreamble();
+    PrecompiledPreamble = AST->BuildPrecompiledPreamble();
   
   if (!AST->Parse())
     return AST.take();
   
+  if (PrecompiledPreamble.second)
+    delete PrecompiledPreamble.first;
+  
   return 0;
 }
 
@@ -694,6 +804,12 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) {
   if (!Invocation.get())
     return true;
   
+  // If we have a preamble file lying around, build or reuse the precompiled
+  // preamble.
+  std::pair<llvm::MemoryBuffer *, bool> PrecompiledPreamble(0, false);
+  if (!PreambleFile.empty())
+    PrecompiledPreamble = BuildPrecompiledPreamble();
+    
   // Clear out the diagnostics state.
   getDiagnostics().Reset();
   
@@ -701,7 +817,13 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) {
   Invocation->getPreprocessorOpts().clearRemappedFiles();
   for (unsigned I = 0; I != NumRemappedFiles; ++I)
     Invocation->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first,
-                                                       RemappedFiles[I].second);
+                                                      RemappedFiles[I].second);
+
+  // Parse the sources
+  bool Result = Parse();
   
-  return Parse();
+  if (PrecompiledPreamble.second)
+    delete PrecompiledPreamble.first;
+
+  return Result;
 }