PTH: Cache stat information for files in the PTH file.  Hook up FileManager
 to use this stat information in the PTH file using a 'StatSysCallCache' object.

Performance impact (Cocoa.h, PTH):
- number of stat calls reduces from 1230 to 425
- fsyntax-only: time improves by 4.2% 

We can reduce the number of stat calls to almost zero by caching negative stat
calls and directory stat calls in the PTH file as well.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@64353 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Lex/PTHLexer.cpp b/lib/Lex/PTHLexer.cpp
index 9f878b6..9a973c9 100644
--- a/lib/Lex/PTHLexer.cpp
+++ b/lib/Lex/PTHLexer.cpp
@@ -25,6 +25,7 @@
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/System/Host.h"
+#include <sys/stat.h>
 using namespace clang;
 
 #define DISK_TOKEN_SIZE (1+1+2+4+4)
@@ -49,6 +50,19 @@
   return V;
 }
 
+static inline uint64_t ReadUnalignedLE64(const unsigned char *&Data) {
+  uint64_t V = ((uint64_t)Data[0])  |
+    ((uint64_t)Data[1] << 8)  |
+    ((uint64_t)Data[2] << 16) |
+    ((uint64_t)Data[3] << 24) |
+    ((uint64_t)Data[1] << 32) |
+    ((uint64_t)Data[2] << 40) |
+    ((uint64_t)Data[3] << 48) |
+    ((uint64_t)Data[3] << 56);
+  Data += 8;
+  return V;
+}
+
 static inline uint32_t ReadLE32(const unsigned char *&Data) {
   // Hosts that directly support little-endian 32-bit loads can just
   // use them.  Big-endian hosts need a bswap.
@@ -353,7 +367,11 @@
                "'buckets' must have a 4-byte alignment");
       }
 
-  
+  unsigned getNumBuckets() const { return NumBuckets; }
+  unsigned getNumEntries() const { return NumEntries; }
+  const unsigned char* const getBase() const { return Base; }
+  const unsigned char* const getBuckets() const { return Buckets; }
+
   bool isEmpty() const { return NumEntries == 0; }
   
   class iterator {
@@ -451,32 +469,37 @@
   uint32_t getPPCondOffset() const { return PPCondOff; }  
 };
   
-class VISIBILITY_HIDDEN PTHFileLookupTrait {
+  
+class VISIBILITY_HIDDEN PTHFileLookupCommonTrait {
 public:
-  typedef PTHFileData      data_type;
-  typedef const FileEntry* external_key_type;
   typedef const char*      internal_key_type;
   
   static bool EqualKey(const char* a, const char* b) {
     return strcmp(a, b) == 0;
   }
-
+  
   static unsigned ComputeHash(const char* x) {
     return BernsteinHash(x);
   }
-
-  static const char* GetInternalKey(const FileEntry* FE) {
-    return FE->getName();
-  }
   
   static std::pair<unsigned, unsigned>
   ReadKeyDataLength(const unsigned char*& d) {
-    return std::make_pair((unsigned) ReadUnalignedLE16(d), 8U);
+    return std::make_pair((unsigned) ReadUnalignedLE16(d), 8U + (4+4+2+8+8));
   }
   
   static const char* ReadKey(const unsigned char* d, unsigned) {
     return (const char*) d;
   }
+};
+  
+class VISIBILITY_HIDDEN PTHFileLookupTrait : public PTHFileLookupCommonTrait {
+public:
+  typedef const FileEntry* external_key_type;
+  typedef PTHFileData      data_type;
+  
+  static const char* GetInternalKey(const FileEntry* FE) {
+    return FE->getName();
+  }
   
   static PTHFileData ReadData(const unsigned char* d, unsigned) {
     uint32_t x = ::ReadUnalignedLE32(d);
@@ -737,3 +760,70 @@
   assert(PP && "No preprocessor set yet!");
   return new PTHLexer(*PP, FID, data, ppcond, *this); 
 }
+
+//===----------------------------------------------------------------------===//
+// 'stat' caching.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class VISIBILITY_HIDDEN PTHStatData {
+public:
+  const ino_t ino;
+  const dev_t dev;
+  const mode_t mode;
+  const time_t mtime;
+  const off_t size;
+  
+  PTHStatData(ino_t i, dev_t d, mode_t mo, time_t m, off_t s)
+  : ino(i), dev(d), mode(mo), mtime(m), size(s) {}  
+};
+  
+class VISIBILITY_HIDDEN PTHStatLookupTrait : public PTHFileLookupCommonTrait {
+public:
+  typedef internal_key_type external_key_type;  // const char*
+  typedef PTHStatData data_type;
+    
+  static const char* GetInternalKey(external_key_type x) { return x; }
+  
+  static data_type ReadData(const unsigned char* d, unsigned) {
+    d += 4 * 2; // Skip the first 2 words.
+    ino_t ino = (ino_t) ReadUnalignedLE32(d);
+    dev_t dev = (dev_t) ReadUnalignedLE32(d);
+    mode_t mode = (mode_t) ReadUnalignedLE16(d);
+    time_t mtime = (time_t) ReadUnalignedLE64(d);    
+    return data_type(ino, dev, mode, mtime, (off_t) ReadUnalignedLE64(d));
+  }
+};
+}
+
+class VISIBILITY_HIDDEN PTHStatCache : public StatSysCallCache {
+  typedef OnDiskChainedHashTable<PTHStatLookupTrait> CacheTy;
+  CacheTy Cache;
+
+public:  
+  PTHStatCache(PTHFileLookup &FL) :
+    Cache(FL.getNumBuckets(), FL.getNumEntries(), FL.getBuckets(),
+          FL.getBase()) {}
+
+  ~PTHStatCache() {}
+  
+  int stat(const char *path, struct stat *buf) {
+    // Do the lookup for the file's data in the PTH file.
+    CacheTy::iterator I = Cache.find(path);
+
+    // If we don't get a hit in the PTH file just forward to 'stat'.
+    if (I == Cache.end()) return ::stat(path, buf);
+    
+    const PTHStatData& Data = *I;
+    buf->st_ino = Data.ino;
+    buf->st_dev = Data.dev;
+    buf->st_mtime = Data.mtime;
+    buf->st_mode = Data.mode;
+    buf->st_size = Data.size;
+    return 0;
+  }
+};
+
+StatSysCallCache *PTHManager::createStatCache() {
+  return new PTHStatCache(*((PTHFileLookup*) FileLookup));
+}
diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp
index db5ca2b..69aa7cb 100644
--- a/lib/Lex/Preprocessor.cpp
+++ b/lib/Lex/Preprocessor.cpp
@@ -32,6 +32,7 @@
 #include "clang/Lex/ScratchBuffer.h"
 #include "clang/Lex/LexDiagnostic.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/FileManager.h"
 #include "clang/Basic/TargetInfo.h"
 #include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/SmallVector.h"
@@ -117,6 +118,11 @@
   delete Callbacks;
 }
 
+void Preprocessor::setPTHManager(PTHManager* pm) {
+  PTH.reset(pm);
+  FileMgr.setStatCache(PTH->createStatCache());
+}
+
 void Preprocessor::DumpToken(const Token &Tok, bool DumpFlags) const {
   llvm::cerr << tok::getTokenName(Tok.getKind()) << " '"
              << getSpelling(Tok) << "'";