Skip to content

Commit 7dadb10

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

File tree

5 files changed

+128
-1
lines changed

5 files changed

+128
-1
lines changed

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

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

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

1516
namespace clang {
@@ -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_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: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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_mutex> OwningLock;
24+
25+
public:
26+
ReaderWriterLock(std::shared_mutex &Mutex)
27+
: OwningLock(Mutex, std::defer_lock) {}
28+
29+
Expected<bool> tryLock() override { return OwningLock.try_lock(); }
30+
31+
llvm::WaitForUnlockResult
32+
waitForUnlockFor(std::chrono::seconds MaxSeconds) override {
33+
assert(!OwningLock);
34+
// We do not respect the timeout here. It's very generous for implicit
35+
// modules, so we'd typically only reach it if the owner crashed (but so did
36+
// we, since we run in the same process), or encountered deadlock.
37+
(void)MaxSeconds;
38+
std::shared_lock Lock(*OwningLock.mutex());
39+
return llvm::WaitForUnlockResult::Success;
40+
}
41+
42+
std::error_code unsafeMaybeUnlock() override {
43+
// Unlocking the mutex here would trigger UB and we don't expect this to be
44+
// actually called when compiling scanning modules due to the no-timeout
45+
// guarantee above.
46+
return {};
47+
}
48+
49+
~ReaderWriterLock() override = default;
50+
};
51+
52+
class InProcessModuleCache : public ModuleCache {
53+
ModuleCacheMutexes &Mutexes;
54+
55+
// TODO: If we changed the InMemoryModuleCache API and relied on strict
56+
// context hash, we could probably create more efficient thread-safe
57+
// implementation of the InMemoryModuleCache such that it does need to be
58+
// recreated for each translation unit.
59+
InMemoryModuleCache InMemory;
60+
61+
public:
62+
InProcessModuleCache(ModuleCacheMutexes &Mutexes) : Mutexes(Mutexes) {}
63+
64+
void prepareForGetLock(StringRef Filename) override {}
65+
66+
std::unique_ptr<llvm::AdvisoryLock> getLock(StringRef Filename) override {
67+
auto &Mtx = [&]() -> std::shared_mutex & {
68+
std::lock_guard Lock(Mutexes.Mutex);
69+
auto &Mutex = Mutexes.Map[Filename];
70+
if (!Mutex)
71+
Mutex = std::make_unique<std::shared_mutex>();
72+
return *Mutex;
73+
}();
74+
return std::make_unique<ReaderWriterLock>(Mtx);
75+
}
76+
77+
InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
78+
const InMemoryModuleCache &getInMemoryModuleCache() const override {
79+
return InMemory;
80+
}
81+
};
82+
} // namespace
83+
84+
IntrusiveRefCntPtr<ModuleCache>
85+
dependencies::makeInProcessModuleCache(ModuleCacheMutexes &Mutexes) {
86+
return llvm::makeIntrusiveRefCnt<InProcessModuleCache>(Mutexes);
87+
}

0 commit comments

Comments
 (0)