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
This commit is contained in:
Douglas Gregor 2009-10-16 18:18:30 +00:00
Родитель 5f04881eb0
Коммит 52e7108f51
11 изменённых файлов: 125 добавлений и 36 удалений

Просмотреть файл

@ -71,16 +71,38 @@ public:
} }
}; };
// FIXME: This is a lightweight shim that is used by FileManager to cache /// \brief Abstract interface for introducing a FileManager cache for 'stat'
// 'stat' system calls. We will use it with PTH to identify if caching /// system calls, which is used by precompiled and pretokenized headers to
// stat calls in PTH files is a performance win. /// improve performance.
class StatSysCallCache { class StatSysCallCache {
protected:
llvm::OwningPtr<StatSysCallCache> NextStatCache;
public: public:
virtual ~StatSysCallCache() {} 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 /// track of the results of stat() calls that occur throughout the
/// execution of the front end. /// execution of the front end.
class MemorizeStatCalls : public StatSysCallCache { class MemorizeStatCalls : public StatSysCallCache {
@ -144,12 +166,21 @@ public:
FileManager(); FileManager();
~FileManager(); ~FileManager();
/// setStatCache - Installs the provided StatSysCallCache object within /// \brief Installs the provided StatSysCallCache object within
/// the FileManager. Ownership of this object is transferred to the /// the FileManager.
/// FileManager. ///
void setStatCache(StatSysCallCache *statCache) { /// Ownership of this object is transferred to the FileManager.
StatCache.reset(statCache); ///
} /// \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 /// getDirectory - Lookup, cache, and verify the specified directory. This
/// returns null if the directory doesn't exist. /// returns null if the directory doesn't exist.

Просмотреть файл

@ -169,6 +169,11 @@ private:
/// \brief The AST context into which we'll read the PCH file. /// \brief The AST context into which we'll read the PCH file.
ASTContext *Context; 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. /// \brief The AST consumer.
ASTConsumer *Consumer; ASTConsumer *Consumer;

Просмотреть файл

@ -149,6 +149,41 @@ FileManager::~FileManager() {
delete &UniqueFiles; 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 /// getDirectory - Lookup, cache, and verify the specified directory. This
/// returns null if the directory doesn't exist. /// returns null if the directory doesn't exist.
/// ///
@ -290,7 +325,7 @@ void FileManager::PrintStats() const {
} }
int MemorizeStatCalls::stat(const char *path, struct stat *buf) { int MemorizeStatCalls::stat(const char *path, struct stat *buf) {
int result = ::stat(path, buf); int result = StatSysCallCache::stat(path, buf);
if (result != 0) { if (result != 0) {
// Cache failed 'stat' results. // Cache failed 'stat' results.

Просмотреть файл

@ -516,7 +516,7 @@ public:
~StatListener() {} ~StatListener() {}
int stat(const char *path, struct stat *buf) { int stat(const char *path, struct stat *buf) {
int result = ::stat(path, buf); int result = StatSysCallCache::stat(path, buf);
if (result != 0) // Failed 'stat'. if (result != 0) // Failed 'stat'.
PM.insert(path, PTHEntry()); PM.insert(path, PTHEntry());
@ -553,7 +553,8 @@ void clang::CacheTokens(Preprocessor &PP, llvm::raw_fd_ostream* OS) {
PTHWriter PW(*OS, PP); PTHWriter PW(*OS, PP);
// Install the 'stat' system call listener in the FileManager. // 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 // Lex through the entire file. This will populate SourceManager with
// all of the header information. // all of the header information.
@ -562,7 +563,7 @@ void clang::CacheTokens(Preprocessor &PP, llvm::raw_fd_ostream* OS) {
do { PP.Lex(Tok); } while (Tok.isNot(tok::eof)); do { PP.Lex(Tok); } while (Tok.isNot(tok::eof));
// Generate the PTH file. // Generate the PTH file.
PP.getFileManager().setStatCache(0); PP.getFileManager().removeStatCache(StatCache);
PW.GeneratePTH(&MainFileName); PW.GeneratePTH(&MainFileName);
} }

Просмотреть файл

@ -53,7 +53,7 @@ PCHGenerator::PCHGenerator(const Preprocessor &PP,
// Install a stat() listener to keep track of all of the stat() // Install a stat() listener to keep track of all of the stat()
// calls. // calls.
StatCalls = new MemorizeStatCalls; StatCalls = new MemorizeStatCalls;
PP.getFileManager().setStatCache(StatCalls); PP.getFileManager().addStatCache(StatCalls, /*AtBeginning=*/true);
} }
void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) { void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) {

Просмотреть файл

@ -335,8 +335,6 @@ void PCHValidator::ReadCounter(unsigned Value) {
PP.setCounterValue(Value); PP.setCounterValue(Value);
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// PCH reader implementation // PCH reader implementation
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -345,7 +343,7 @@ PCHReader::PCHReader(Preprocessor &PP, ASTContext *Context,
const char *isysroot) const char *isysroot)
: Listener(new PCHValidator(PP, *this)), SourceMgr(PP.getSourceManager()), : Listener(new PCHValidator(PP, *this)), SourceMgr(PP.getSourceManager()),
FileMgr(PP.getFileManager()), Diags(PP.getDiagnostics()), 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), IdentifierTableData(0), IdentifierLookupTable(0),
IdentifierOffsets(0), IdentifierOffsets(0),
MethodPoolLookupTable(0), MethodPoolLookupTableData(0), MethodPoolLookupTable(0), MethodPoolLookupTableData(0),
@ -362,7 +360,7 @@ PCHReader::PCHReader(Preprocessor &PP, ASTContext *Context,
PCHReader::PCHReader(SourceManager &SourceMgr, FileManager &FileMgr, PCHReader::PCHReader(SourceManager &SourceMgr, FileManager &FileMgr,
Diagnostic &Diags, const char *isysroot) Diagnostic &Diags, const char *isysroot)
: SourceMgr(SourceMgr), FileMgr(FileMgr), Diags(Diags), : 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), IdentifierTableData(0), IdentifierLookupTable(0),
IdentifierOffsets(0), IdentifierOffsets(0),
MethodPoolLookupTable(0), MethodPoolLookupTableData(0), MethodPoolLookupTable(0), MethodPoolLookupTableData(0),
@ -794,7 +792,7 @@ public:
// If we don't get a hit in the PCH file just forward to 'stat'. // If we don't get a hit in the PCH file just forward to 'stat'.
if (I == Cache->end()) { if (I == Cache->end()) {
++NumStatMisses; ++NumStatMisses;
return ::stat(path, buf); return StatSysCallCache::stat(path, buf);
} }
++NumStatHits; ++NumStatHits;
@ -1352,12 +1350,15 @@ PCHReader::ReadPCHBlock() {
} }
break; break;
case pch::STAT_CACHE: case pch::STAT_CACHE: {
FileMgr.setStatCache( PCHStatCache *MyStatCache =
new PCHStatCache((const unsigned char *)BlobStart + Record[0], new PCHStatCache((const unsigned char *)BlobStart + Record[0],
(const unsigned char *)BlobStart, (const unsigned char *)BlobStart,
NumStatHits, NumStatMisses)); NumStatHits, NumStatMisses);
FileMgr.addStatCache(MyStatCache);
StatCache = MyStatCache;
break; break;
}
case pch::EXT_VECTOR_DECLS: case pch::EXT_VECTOR_DECLS:
if (!ExtVectorDecls.empty()) { if (!ExtVectorDecls.empty()) {
@ -1466,7 +1467,8 @@ PCHReader::PCHReadResult PCHReader::ReadPCH(const std::string &FileName) {
SourceMgr.ClearPreallocatedSLocEntries(); SourceMgr.ClearPreallocatedSLocEntries();
// Remove the stat cache. // Remove the stat cache.
FileMgr.setStatCache(0); if (StatCache)
FileMgr.removeStatCache((PCHStatCache*)StatCache);
return IgnorePCH; return IgnorePCH;
} }

Просмотреть файл

@ -679,7 +679,8 @@ public:
CacheTy::iterator I = Cache.find(path); CacheTy::iterator I = Cache.find(path);
// If we don't get a hit in the PTH file just forward to 'stat'. // 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; const PTHStatData& Data = *I;

Просмотреть файл

@ -122,7 +122,7 @@ Preprocessor::~Preprocessor() {
void Preprocessor::setPTHManager(PTHManager* pm) { void Preprocessor::setPTHManager(PTHManager* pm) {
PTH.reset(pm); PTH.reset(pm);
FileMgr.setStatCache(PTH->createStatCache()); FileMgr.addStatCache(PTH->createStatCache());
} }
void Preprocessor::DumpToken(const Token &Tok, bool DumpFlags) const { void Preprocessor::DumpToken(const Token &Tok, bool DumpFlags) const {

Просмотреть файл

@ -45,7 +45,7 @@ if(PYTHONINTERP_FOUND)
${LLVM_SOURCE_DIR}/utils/lit/lit.py ${LLVM_SOURCE_DIR}/utils/lit/lit.py
-sv ${CLANG_TEST_EXTRA_ARGS} -sv ${CLANG_TEST_EXTRA_ARGS}
${CMAKE_CURRENT_BINARY_DIR}/${testdir} ${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}") COMMENT "Running Clang regression tests in ${testdir}")
endforeach() endforeach()
@ -62,7 +62,7 @@ if(PYTHONINTERP_FOUND)
${LLVM_SOURCE_DIR}/utils/lit/lit.py ${LLVM_SOURCE_DIR}/utils/lit/lit.py
-sv ${CLANG_TEST_EXTRA_ARGS} -sv ${CLANG_TEST_EXTRA_ARGS}
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS clang clang-cc index-test DEPENDS clang clang-cc index-test c-index-test
COMMENT "Running Clang regression tests") COMMENT "Running Clang regression tests")
add_custom_target(clang-c++tests add_custom_target(clang-c++tests
@ -78,6 +78,6 @@ if(PYTHONINTERP_FOUND)
${LLVM_SOURCE_DIR}/utils/lit/lit.py ${LLVM_SOURCE_DIR}/utils/lit/lit.py
-sv ${CLANG_TEST_EXTRA_ARGS} -sv ${CLANG_TEST_EXTRA_ARGS}
${CMAKE_CURRENT_SOURCE_DIR}/../utils/C++Tests ${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") COMMENT "Running Clang regression tests")
endif() endif()

7
test/Index/c-index-pch.c Normal file
Просмотреть файл

@ -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);

7
test/Index/c-index-pch.h Normal file
Просмотреть файл

@ -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