Skip to content

Commit 5aef4af

Browse files
committed
[clang][deps] Implement efficient in-process ModuleCache
1 parent c84d8e8 commit 5aef4af

File tree

5 files changed

+125
-1
lines changed

5 files changed

+125
-1
lines changed

clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGSERVICE_H
1010
#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGSERVICE_H
1111

12+
#include "clang/Tooling/DependencyScanning/InProcessModuleCache.h"
1213
#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h"
1314
#include "llvm/ADT/BitmaskEnum.h"
1415

@@ -99,6 +100,8 @@ class DependencyScanningService {
99100
return SharedCache;
100101
}
101102

103+
ModuleCacheMutexes &getModuleCacheMutexes() { return ModuleCacheMutexes; }
104+
102105
private:
103106
const ScanningMode Mode;
104107
const ScanningOutputFormat Format;
@@ -110,6 +113,8 @@ class DependencyScanningService {
110113
const bool TraceVFS;
111114
/// The global file system cache.
112115
DependencyScanningFilesystemSharedCache SharedCache;
116+
/// The global module cache mutexes.
117+
ModuleCacheMutexes ModuleCacheMutexes;
113118
};
114119

115120
} // end namespace dependencies
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_INPROCESSMODULECACHE_H
10+
#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_INPROCESSMODULECACHE_H
11+
12+
#include "clang/Serialization/ModuleCache.h"
13+
#include "llvm/ADT/StringMap.h"
14+
15+
#include <shared_mutex>
16+
17+
namespace clang {
18+
namespace tooling {
19+
namespace dependencies {
20+
struct ModuleCacheMutexes {
21+
std::mutex Mutex;
22+
llvm::StringMap<std::unique_ptr<std::shared_timed_mutex>> Map;
23+
};
24+
25+
IntrusiveRefCntPtr<ModuleCache>
26+
makeInProcessModuleCache(ModuleCacheMutexes &Mutexes);
27+
} // namespace dependencies
28+
} // namespace tooling
29+
} // namespace clang
30+
31+
#endif

clang/lib/Tooling/DependencyScanning/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_clang_library(clangDependencyScanning
1010
DependencyScanningService.cpp
1111
DependencyScanningWorker.cpp
1212
DependencyScanningTool.cpp
13+
InProcessModuleCache.cpp
1314
ModuleDepCollector.cpp
1415

1516
DEPENDS

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "clang/Lex/PreprocessorOptions.h"
2323
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
2424
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
25+
#include "clang/Tooling/DependencyScanning/InProcessModuleCache.h"
2526
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
2627
#include "clang/Tooling/Tooling.h"
2728
#include "llvm/ADT/IntrusiveRefCntPtr.h"
@@ -315,9 +316,11 @@ class DependencyScanningAction : public tooling::ToolAction {
315316
Scanned = true;
316317

317318
// Create a compiler instance to handle the actual work.
318-
ScanInstanceStorage.emplace(std::move(PCHContainerOps));
319+
auto ModCache = makeInProcessModuleCache(Service.getModuleCacheMutexes());
320+
ScanInstanceStorage.emplace(std::move(PCHContainerOps), ModCache.get());
319321
CompilerInstance &ScanInstance = *ScanInstanceStorage;
320322
ScanInstance.setInvocation(std::move(Invocation));
323+
ScanInstance.setBuildingModule(false);
321324

322325
// Create the compiler's actual diagnostics engine.
323326
sanitizeDiagOpts(ScanInstance.getDiagnosticOpts());
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/Tooling/DependencyScanning/InProcessModuleCache.h"
10+
11+
#include "clang/Serialization/InMemoryModuleCache.h"
12+
#include "llvm/Support/AdvisoryLock.h"
13+
14+
#include <mutex>
15+
16+
using namespace clang;
17+
using namespace tooling;
18+
using namespace dependencies;
19+
20+
namespace {
21+
class ReaderWriterLock : public llvm::AdvisoryLock {
22+
// TODO: Consider using std::atomic::{wait,notify_all} when we move to C++20.
23+
std::unique_lock<std::shared_timed_mutex> OwningLock;
24+
25+
public:
26+
ReaderWriterLock(std::shared_timed_mutex &Mutex)
27+
: OwningLock(Mutex, std::defer_lock) {}
28+
29+
Expected<bool> tryLock() override { return OwningLock.try_lock(); }
30+
31+
std::error_code unsafeMaybeUnlock() override {
32+
// Unlocking the mutex here would trigger UB and we don't expect this to be
33+
// actually called when compiling scanning modules due to the generous
34+
// default timeout of 90 seconds.
35+
return {};
36+
}
37+
38+
llvm::WaitForUnlockResult
39+
waitForUnlockFor(std::chrono::seconds MaxSeconds) override {
40+
assert(!OwningLock);
41+
std::shared_lock Lock(*OwningLock.mutex(), MaxSeconds);
42+
return Lock ? llvm::WaitForUnlockResult::Success
43+
: llvm::WaitForUnlockResult::Timeout;
44+
}
45+
46+
~ReaderWriterLock() override = default;
47+
};
48+
49+
class InProcessModuleCache : public ModuleCache {
50+
ModuleCacheMutexes &Mutexes;
51+
52+
// TODO: If we changed the InMemoryModuleCache API and relied on strict
53+
// context hash, we could probably create more efficient thread-safe
54+
// implementation of the InMemoryModuleCache such that it does need to be
55+
// recreated for each translation unit.
56+
InMemoryModuleCache InMemory;
57+
58+
public:
59+
InProcessModuleCache(ModuleCacheMutexes &Mutexes) : Mutexes(Mutexes) {}
60+
61+
void prepareForGetLock(StringRef Filename) override {}
62+
63+
std::unique_ptr<llvm::AdvisoryLock> getLock(StringRef Filename) override {
64+
auto &Mtx = [&]() -> std::shared_timed_mutex & {
65+
std::lock_guard Lock(Mutexes.Mutex);
66+
auto &Mutex = Mutexes.Map[Filename];
67+
if (!Mutex)
68+
Mutex = std::make_unique<std::shared_timed_mutex>();
69+
return *Mutex;
70+
}();
71+
return std::make_unique<ReaderWriterLock>(Mtx);
72+
}
73+
74+
InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
75+
const InMemoryModuleCache &getInMemoryModuleCache() const override {
76+
return InMemory;
77+
}
78+
};
79+
} // namespace
80+
81+
IntrusiveRefCntPtr<ModuleCache>
82+
dependencies::makeInProcessModuleCache(ModuleCacheMutexes &Mutexes) {
83+
return llvm::makeIntrusiveRefCnt<InProcessModuleCache>(Mutexes);
84+
}

0 commit comments

Comments
 (0)