Add support for a chain of stat caches in the FileManager, rather than
only supporting a single stat cache. The immediate benefit of this
change is that we can now generate a PCH/AST file when including
another PCH file; in the future, the chain of stat caches will likely
be useful with multiple levels of PCH files.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@84263 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/FileManager.h b/include/clang/Basic/FileManager.h
index 7c9113c..5e7ac4f 100644
--- a/include/clang/Basic/FileManager.h
+++ b/include/clang/Basic/FileManager.h
@@ -71,16 +71,38 @@
}
};
-// FIXME: This is a lightweight shim that is used by FileManager to cache
-// 'stat' system calls. We will use it with PTH to identify if caching
-// stat calls in PTH files is a performance win.
+/// \brief Abstract interface for introducing a FileManager cache for 'stat'
+/// system calls, which is used by precompiled and pretokenized headers to
+/// improve performance.
class StatSysCallCache {
+protected:
+ llvm::OwningPtr<StatSysCallCache> NextStatCache;
+
public:
virtual ~StatSysCallCache() {}
- virtual int stat(const char *path, struct stat *buf) = 0;
+ virtual int stat(const char *path, struct stat *buf) {
+ if (getNextStatCache())
+ return getNextStatCache()->stat(path, buf);
+
+ return ::stat(path, buf);
+ }
+
+ /// \brief Sets the next stat call cache in the chain of stat caches.
+ /// Takes ownership of the given stat cache.
+ void setNextStatCache(StatSysCallCache *Cache) {
+ NextStatCache.reset(Cache);
+ }
+
+ /// \brief Retrieve the next stat call cache in the chain.
+ StatSysCallCache *getNextStatCache() { return NextStatCache.get(); }
+
+ /// \brief Retrieve the next stat call cache in the chain, transferring
+ /// ownership of this cache (and, transitively, all of the remaining caches)
+ /// to the caller.
+ StatSysCallCache *takeNextStatCache() { return NextStatCache.take(); }
};
-/// \brief A stat listener that can be used by FileManager to keep
+/// \brief A stat "cache" that can be used by FileManager to keep
/// track of the results of stat() calls that occur throughout the
/// execution of the front end.
class MemorizeStatCalls : public StatSysCallCache {
@@ -144,13 +166,22 @@
FileManager();
~FileManager();
- /// setStatCache - Installs the provided StatSysCallCache object within
- /// the FileManager. Ownership of this object is transferred to the
- /// FileManager.
- void setStatCache(StatSysCallCache *statCache) {
- StatCache.reset(statCache);
- }
+ /// \brief Installs the provided StatSysCallCache object within
+ /// the FileManager.
+ ///
+ /// Ownership of this object is transferred to the FileManager.
+ ///
+ /// \param statCache the new stat cache to install. Ownership of this
+ /// object is transferred to the FileManager.
+ ///
+ /// \param AtBeginning whether this new stat cache must be installed at the
+ /// beginning of the chain of stat caches. Otherwise, it will be added to
+ /// the end of the chain.
+ void addStatCache(StatSysCallCache *statCache, bool AtBeginning = false);
+ /// \brief Removes the provided StatSysCallCache object from the file manager.
+ void removeStatCache(StatSysCallCache *statCache);
+
/// getDirectory - Lookup, cache, and verify the specified directory. This
/// returns null if the directory doesn't exist.
///
diff --git a/include/clang/Frontend/PCHReader.h b/include/clang/Frontend/PCHReader.h
index 27e4d3f..f1f58fa 100644
--- a/include/clang/Frontend/PCHReader.h
+++ b/include/clang/Frontend/PCHReader.h
@@ -169,6 +169,11 @@
/// \brief The AST context into which we'll read the PCH file.
ASTContext *Context;
+ /// \brief The PCH stat cache installed by this PCHReader, if any.
+ ///
+ /// The dynamic type of this stat cache is always PCHStatCache
+ void *StatCache;
+
/// \brief The AST consumer.
ASTConsumer *Consumer;
@@ -492,8 +497,8 @@
/// \param isysroot If non-NULL, the system include path specified by the
/// user. This is only used with relocatable PCH files. If non-NULL,
/// a relocatable PCH file will use the default path "/".
- PCHReader(SourceManager &SourceMgr, FileManager &FileMgr,
- Diagnostic &Diags, const char *isysroot = 0);
+ PCHReader(SourceManager &SourceMgr, FileManager &FileMgr,
+ Diagnostic &Diags, const char *isysroot = 0);
~PCHReader();
/// \brief Load the precompiled header designated by the given file
diff --git a/lib/Basic/FileManager.cpp b/lib/Basic/FileManager.cpp
index df86f9d..ee4309d 100644
--- a/lib/Basic/FileManager.cpp
+++ b/lib/Basic/FileManager.cpp
@@ -149,6 +149,41 @@
delete &UniqueFiles;
}
+void FileManager::addStatCache(StatSysCallCache *statCache, bool AtBeginning) {
+ assert(statCache && "No stat cache provided?");
+ if (AtBeginning || StatCache.get() == 0) {
+ statCache->setNextStatCache(StatCache.take());
+ StatCache.reset(statCache);
+ return;
+ }
+
+ StatSysCallCache *LastCache = StatCache.get();
+ while (LastCache->getNextStatCache())
+ LastCache = LastCache->getNextStatCache();
+
+ LastCache->setNextStatCache(statCache);
+}
+
+void FileManager::removeStatCache(StatSysCallCache *statCache) {
+ if (!statCache)
+ return;
+
+ if (StatCache.get() == statCache) {
+ // This is the first stat cache.
+ StatCache.reset(StatCache->takeNextStatCache());
+ return;
+ }
+
+ // Find the stat cache in the list.
+ StatSysCallCache *PrevCache = StatCache.get();
+ while (PrevCache && PrevCache->getNextStatCache() != statCache)
+ PrevCache = PrevCache->getNextStatCache();
+ if (PrevCache)
+ PrevCache->setNextStatCache(statCache->getNextStatCache());
+ else
+ assert(false && "Stat cache not found for removal");
+}
+
/// getDirectory - Lookup, cache, and verify the specified directory. This
/// returns null if the directory doesn't exist.
///
@@ -290,8 +325,8 @@
}
int MemorizeStatCalls::stat(const char *path, struct stat *buf) {
- int result = ::stat(path, buf);
-
+ int result = StatSysCallCache::stat(path, buf);
+
if (result != 0) {
// Cache failed 'stat' results.
struct stat empty;
diff --git a/lib/Frontend/CacheTokens.cpp b/lib/Frontend/CacheTokens.cpp
index e7fc566..169fd5e 100644
--- a/lib/Frontend/CacheTokens.cpp
+++ b/lib/Frontend/CacheTokens.cpp
@@ -516,7 +516,7 @@
~StatListener() {}
int stat(const char *path, struct stat *buf) {
- int result = ::stat(path, buf);
+ int result = StatSysCallCache::stat(path, buf);
if (result != 0) // Failed 'stat'.
PM.insert(path, PTHEntry());
@@ -553,7 +553,8 @@
PTHWriter PW(*OS, PP);
// Install the 'stat' system call listener in the FileManager.
- PP.getFileManager().setStatCache(new StatListener(PW.getPM()));
+ StatListener *StatCache = new StatListener(PW.getPM());
+ PP.getFileManager().addStatCache(StatCache, /*AtBeginning=*/true);
// Lex through the entire file. This will populate SourceManager with
// all of the header information.
@@ -562,7 +563,7 @@
do { PP.Lex(Tok); } while (Tok.isNot(tok::eof));
// Generate the PTH file.
- PP.getFileManager().setStatCache(0);
+ PP.getFileManager().removeStatCache(StatCache);
PW.GeneratePTH(&MainFileName);
}
diff --git a/lib/Frontend/GeneratePCH.cpp b/lib/Frontend/GeneratePCH.cpp
index bc45cc4..0e4f83f 100644
--- a/lib/Frontend/GeneratePCH.cpp
+++ b/lib/Frontend/GeneratePCH.cpp
@@ -53,7 +53,7 @@
// Install a stat() listener to keep track of all of the stat()
// calls.
StatCalls = new MemorizeStatCalls;
- PP.getFileManager().setStatCache(StatCalls);
+ PP.getFileManager().addStatCache(StatCalls, /*AtBeginning=*/true);
}
void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) {
diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp
index d9d3960..141f7c8 100644
--- a/lib/Frontend/PCHReader.cpp
+++ b/lib/Frontend/PCHReader.cpp
@@ -335,8 +335,6 @@
PP.setCounterValue(Value);
}
-
-
//===----------------------------------------------------------------------===//
// PCH reader implementation
//===----------------------------------------------------------------------===//
@@ -345,7 +343,7 @@
const char *isysroot)
: Listener(new PCHValidator(PP, *this)), SourceMgr(PP.getSourceManager()),
FileMgr(PP.getFileManager()), Diags(PP.getDiagnostics()),
- SemaObj(0), PP(&PP), Context(Context), Consumer(0),
+ SemaObj(0), PP(&PP), Context(Context), StatCache(0), Consumer(0),
IdentifierTableData(0), IdentifierLookupTable(0),
IdentifierOffsets(0),
MethodPoolLookupTable(0), MethodPoolLookupTableData(0),
@@ -362,7 +360,7 @@
PCHReader::PCHReader(SourceManager &SourceMgr, FileManager &FileMgr,
Diagnostic &Diags, const char *isysroot)
: SourceMgr(SourceMgr), FileMgr(FileMgr), Diags(Diags),
- SemaObj(0), PP(0), Context(0), Consumer(0),
+ SemaObj(0), PP(0), Context(0), StatCache(0), Consumer(0),
IdentifierTableData(0), IdentifierLookupTable(0),
IdentifierOffsets(0),
MethodPoolLookupTable(0), MethodPoolLookupTableData(0),
@@ -794,7 +792,7 @@
// If we don't get a hit in the PCH file just forward to 'stat'.
if (I == Cache->end()) {
++NumStatMisses;
- return ::stat(path, buf);
+ return StatSysCallCache::stat(path, buf);
}
++NumStatHits;
@@ -1352,13 +1350,16 @@
}
break;
- case pch::STAT_CACHE:
- FileMgr.setStatCache(
- new PCHStatCache((const unsigned char *)BlobStart + Record[0],
- (const unsigned char *)BlobStart,
- NumStatHits, NumStatMisses));
+ case pch::STAT_CACHE: {
+ PCHStatCache *MyStatCache =
+ new PCHStatCache((const unsigned char *)BlobStart + Record[0],
+ (const unsigned char *)BlobStart,
+ NumStatHits, NumStatMisses);
+ FileMgr.addStatCache(MyStatCache);
+ StatCache = MyStatCache;
break;
-
+ }
+
case pch::EXT_VECTOR_DECLS:
if (!ExtVectorDecls.empty()) {
Error("duplicate EXT_VECTOR_DECLS record in PCH file");
@@ -1466,7 +1467,8 @@
SourceMgr.ClearPreallocatedSLocEntries();
// Remove the stat cache.
- FileMgr.setStatCache(0);
+ if (StatCache)
+ FileMgr.removeStatCache((PCHStatCache*)StatCache);
return IgnorePCH;
}
diff --git a/lib/Lex/PTHLexer.cpp b/lib/Lex/PTHLexer.cpp
index 36ace8b..f804f82 100644
--- a/lib/Lex/PTHLexer.cpp
+++ b/lib/Lex/PTHLexer.cpp
@@ -679,7 +679,8 @@
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);
+ if (I == Cache.end())
+ return StatSysCallCache::stat(path, buf);
const PTHStatData& Data = *I;
diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp
index bfa090a..e41bc5c 100644
--- a/lib/Lex/Preprocessor.cpp
+++ b/lib/Lex/Preprocessor.cpp
@@ -122,7 +122,7 @@
void Preprocessor::setPTHManager(PTHManager* pm) {
PTH.reset(pm);
- FileMgr.setStatCache(PTH->createStatCache());
+ FileMgr.addStatCache(PTH->createStatCache());
}
void Preprocessor::DumpToken(const Token &Tok, bool DumpFlags) const {
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 2a7f132..66f05bf 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -45,7 +45,7 @@
${LLVM_SOURCE_DIR}/utils/lit/lit.py
-sv ${CLANG_TEST_EXTRA_ARGS}
${CMAKE_CURRENT_BINARY_DIR}/${testdir}
- DEPENDS clang clang-cc index-test
+ DEPENDS clang clang-cc index-test c-index-test
COMMENT "Running Clang regression tests in ${testdir}")
endforeach()
@@ -62,7 +62,7 @@
${LLVM_SOURCE_DIR}/utils/lit/lit.py
-sv ${CLANG_TEST_EXTRA_ARGS}
${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS clang clang-cc index-test
+ DEPENDS clang clang-cc index-test c-index-test
COMMENT "Running Clang regression tests")
add_custom_target(clang-c++tests
@@ -78,6 +78,6 @@
${LLVM_SOURCE_DIR}/utils/lit/lit.py
-sv ${CLANG_TEST_EXTRA_ARGS}
${CMAKE_CURRENT_SOURCE_DIR}/../utils/C++Tests
- DEPENDS clang clang-cc index-test
+ DEPENDS clang clang-cc index-test c-index-test
COMMENT "Running Clang regression tests")
endif()
diff --git a/test/Index/c-index-pch.c b/test/Index/c-index-pch.c
new file mode 100644
index 0000000..2298c8f
--- /dev/null
+++ b/test/Index/c-index-pch.c
@@ -0,0 +1,7 @@
+// RUN: clang-cc -emit-pch -x c -o %t.pch %S/c-index-pch.h &&
+// RUN: clang-cc -include-pch %t.pch -x c -emit-pch -o %t.ast %s &&
+// RUN: c-index-test %t.ast all | FileCheck -check-prefix=ALL %s
+// CHECK-ALL: FunctionDecl=foo
+// CHECK-ALL: VarDecl=bar
+// CHECK-ALL: FunctionDecl=wibble
+void wibble(int i);
diff --git a/test/Index/c-index-pch.h b/test/Index/c-index-pch.h
new file mode 100644
index 0000000..6dda180
--- /dev/null
+++ b/test/Index/c-index-pch.h
@@ -0,0 +1,7 @@
+#ifndef C_INDEX_PCH_H
+#define C_INDEX_PCH_H
+
+void foo(int i, float f);
+extern int bar;
+
+#endif // C_INDEX_PCH_H