[cfe-commits] r109294 - in /cfe/trunk: include/clang/Frontend/ASTUnit.h lib/Frontend/ASTUnit.cpp

Douglas Gregor dgregor at apple.com
Fri Jul 23 16:58:40 PDT 2010


Author: dgregor
Date: Fri Jul 23 18:58:40 2010
New Revision: 109294

URL: http://llvm.org/viewvc/llvm-project?rev=109294&view=rev
Log:
Once we've built a precompiled preamble, keep track of the details of
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"


Modified:
    cfe/trunk/include/clang/Frontend/ASTUnit.h
    cfe/trunk/lib/Frontend/ASTUnit.cpp

Modified: cfe/trunk/include/clang/Frontend/ASTUnit.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/ASTUnit.h?rev=109294&r1=109293&r2=109294&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/ASTUnit.h (original)
+++ cfe/trunk/include/clang/Frontend/ASTUnit.h Fri Jul 23 18:58:40 2010
@@ -45,14 +45,6 @@
 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 @@
   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 @@
 
   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 {

Modified: cfe/trunk/lib/Frontend/ASTUnit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/ASTUnit.cpp?rev=109294&r1=109293&r2=109294&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/ASTUnit.cpp (original)
+++ cfe/trunk/lib/Frontend/ASTUnit.cpp Fri Jul 23 18:58:40 2010
@@ -37,10 +37,6 @@
 #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() {
   ConcurrencyCheckValue = CheckLocked;
   CleanTemporaryFiles();
+  if (!PreambleFile.empty())
+    PreambleFile.eraseFromDisk();
 }
 
 void ASTUnit::CleanTemporaryFiles() {
@@ -427,35 +425,41 @@
   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 @@
       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 @@
                                                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 @@
                             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 @@
   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 @@
   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 @@
   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 @@
   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;
 }





More information about the cfe-commits mailing list