Skip to content

Commit f668034

Browse files
committed
[clang][deps] Squash caches for original and minimized files
The minimizing and caching filesystem used by the dependency scanner keeps minimized and original files in separate caches. This setup is not well suited for dealing with files that are sometimes minimized and sometimes not. Such files are being stat-ed and read twice, which is wasteful and also means the two versions of the file can get "out of sync". This patch squashes the two caches together. When a file is stat-ed or read, its original contents are populated. If a file needs to be minimized, we give the minimizer the already loaded contents instead of reading the file again. Reviewed By: dexonsmith Differential Revision: https://reviews.llvm.org/D115346
1 parent 02fc8d5 commit f668034

File tree

5 files changed

+190
-125
lines changed

5 files changed

+190
-125
lines changed

clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h

Lines changed: 94 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,31 @@ namespace dependencies {
2626
/// the dependency scanning filesystem.
2727
///
2828
/// It represents one of the following:
29-
/// - an opened source file with minimized contents and a stat value.
30-
/// - an opened source file with original contents and a stat value.
31-
/// - a directory entry with its stat value.
32-
/// - an error value to represent a file system error.
29+
/// - opened file with original contents and a stat value,
30+
/// - opened file with original contents, minimized contents and a stat value,
31+
/// - directory entry with its stat value,
32+
/// - filesystem error,
3333
/// - uninitialized entry with unknown status.
3434
class CachedFileSystemEntry {
3535
public:
3636
/// Creates an uninitialized entry.
37-
CachedFileSystemEntry() : MaybeStat(llvm::vfs::Status()) {}
37+
CachedFileSystemEntry()
38+
: MaybeStat(llvm::vfs::Status()), MinimizedContentsAccess(nullptr) {}
3839

3940
/// Initialize the cached file system entry.
4041
void init(llvm::ErrorOr<llvm::vfs::Status> &&MaybeStatus, StringRef Filename,
41-
llvm::vfs::FileSystem &FS, bool ShouldMinimize = true);
42+
llvm::vfs::FileSystem &FS);
4243

4344
/// Initialize the entry as file with minimized or original contents.
4445
///
4546
/// The filesystem opens the file even for `stat` calls open to avoid the
4647
/// issues with stat + open of minimized files that might lead to a
4748
/// mismatching size of the file.
48-
llvm::ErrorOr<llvm::vfs::Status>
49-
initFile(StringRef Filename, llvm::vfs::FileSystem &FS, bool Minimize = true);
49+
llvm::ErrorOr<llvm::vfs::Status> initFile(StringRef Filename,
50+
llvm::vfs::FileSystem &FS);
51+
52+
/// Minimize contents of the file.
53+
void minimizeFile();
5054

5155
/// \returns True if the entry is initialized.
5256
bool isInitialized() const {
@@ -56,13 +60,38 @@ class CachedFileSystemEntry {
5660
/// \returns True if the current entry points to a directory.
5761
bool isDirectory() const { return MaybeStat && MaybeStat->isDirectory(); }
5862

59-
/// \returns The error or the file's contents.
60-
llvm::ErrorOr<StringRef> getContents() const {
63+
/// \returns The error or the file's original contents.
64+
llvm::ErrorOr<StringRef> getOriginalContents() const {
65+
if (!MaybeStat)
66+
return MaybeStat.getError();
67+
assert(!MaybeStat->isDirectory() && "not a file");
68+
assert(isInitialized() && "not initialized");
69+
assert(OriginalContents && "not read");
70+
return OriginalContents->getBuffer();
71+
}
72+
73+
/// \returns The error or the file's minimized contents.
74+
llvm::ErrorOr<StringRef> getMinimizedContents() const {
6175
if (!MaybeStat)
6276
return MaybeStat.getError();
6377
assert(!MaybeStat->isDirectory() && "not a file");
6478
assert(isInitialized() && "not initialized");
65-
return Contents->getBuffer();
79+
llvm::MemoryBuffer *Buffer = MinimizedContentsAccess.load();
80+
assert(Buffer && "not minimized");
81+
return Buffer->getBuffer();
82+
}
83+
84+
/// \returns True if this entry represents a file that can be read.
85+
bool isReadable() const { return MaybeStat && !MaybeStat->isDirectory(); }
86+
87+
/// \returns True if this cached entry needs to be updated.
88+
bool needsUpdate(bool ShouldBeMinimized) const {
89+
return isReadable() && needsMinimization(ShouldBeMinimized);
90+
}
91+
92+
/// \returns True if the contents of this entry need to be minimized.
93+
bool needsMinimization(bool ShouldBeMinimized) const {
94+
return ShouldBeMinimized && !MinimizedContentsAccess.load();
6695
}
6796

6897
/// \returns The error or the status of the entry.
@@ -83,15 +112,16 @@ class CachedFileSystemEntry {
83112
return PPSkippedRangeMapping;
84113
}
85114

86-
CachedFileSystemEntry(CachedFileSystemEntry &&) = default;
87-
CachedFileSystemEntry &operator=(CachedFileSystemEntry &&) = default;
88-
89-
CachedFileSystemEntry(const CachedFileSystemEntry &) = delete;
90-
CachedFileSystemEntry &operator=(const CachedFileSystemEntry &) = delete;
91-
92115
private:
93116
llvm::ErrorOr<llvm::vfs::Status> MaybeStat;
94-
std::unique_ptr<llvm::MemoryBuffer> Contents;
117+
std::unique_ptr<llvm::MemoryBuffer> OriginalContents;
118+
119+
/// Owning storage for the minimized file contents.
120+
std::unique_ptr<llvm::MemoryBuffer> MinimizedContentsStorage;
121+
/// Atomic view of the minimized file contents.
122+
/// This prevents data races when multiple threads call `needsMinimization`.
123+
std::atomic<llvm::MemoryBuffer *> MinimizedContentsAccess;
124+
95125
PreprocessorSkippedRangeMapping PPSkippedRangeMapping;
96126
};
97127

@@ -108,61 +138,70 @@ class DependencyScanningFilesystemSharedCache {
108138
CachedFileSystemEntry Value;
109139
};
110140

141+
DependencyScanningFilesystemSharedCache();
142+
111143
/// Returns a cache entry for the corresponding key.
112144
///
113145
/// A new cache entry is created if the key is not in the cache. This is a
114146
/// thread safe call.
115-
SharedFileSystemEntry &get(StringRef Key, bool Minimized);
147+
SharedFileSystemEntry &get(StringRef Key);
116148

117149
private:
118-
class SingleCache {
119-
public:
120-
SingleCache();
121-
122-
SharedFileSystemEntry &get(StringRef Key);
123-
124-
private:
125-
struct CacheShard {
126-
std::mutex CacheLock;
127-
llvm::StringMap<SharedFileSystemEntry, llvm::BumpPtrAllocator> Cache;
128-
};
129-
std::unique_ptr<CacheShard[]> CacheShards;
130-
unsigned NumShards;
150+
struct CacheShard {
151+
std::mutex CacheLock;
152+
llvm::StringMap<SharedFileSystemEntry, llvm::BumpPtrAllocator> Cache;
131153
};
132-
133-
SingleCache CacheMinimized;
134-
SingleCache CacheOriginal;
154+
std::unique_ptr<CacheShard[]> CacheShards;
155+
unsigned NumShards;
135156
};
136157

137158
/// This class is a local cache, that caches the 'stat' and 'open' calls to the
138159
/// underlying real file system. It distinguishes between minimized and original
139160
/// files.
140161
class DependencyScanningFilesystemLocalCache {
141-
private:
142-
using SingleCache =
143-
llvm::StringMap<const CachedFileSystemEntry *, llvm::BumpPtrAllocator>;
162+
llvm::StringMap<const CachedFileSystemEntry *, llvm::BumpPtrAllocator> Cache;
144163

145-
SingleCache CacheMinimized;
146-
SingleCache CacheOriginal;
147-
148-
SingleCache &selectCache(bool Minimized) {
149-
return Minimized ? CacheMinimized : CacheOriginal;
164+
public:
165+
const CachedFileSystemEntry *getCachedEntry(StringRef Filename) {
166+
return Cache[Filename];
150167
}
168+
};
169+
170+
/// Reference to a CachedFileSystemEntry.
171+
/// If the underlying entry is an opened file, this wrapper returns the correct
172+
/// contents (original or minimized) and ensures consistency with file size
173+
/// reported by status.
174+
class EntryRef {
175+
/// For entry that is an opened file, this bit signifies whether its contents
176+
/// are minimized.
177+
bool Minimized;
178+
179+
/// The underlying cached entry.
180+
const CachedFileSystemEntry *Entry;
151181

152182
public:
153-
void setCachedEntry(StringRef Filename, bool Minimized,
154-
const CachedFileSystemEntry *Entry) {
155-
SingleCache &Cache = selectCache(Minimized);
156-
bool IsInserted = Cache.try_emplace(Filename, Entry).second;
157-
(void)IsInserted;
158-
assert(IsInserted && "local cache is updated more than once");
183+
EntryRef(bool Minimized, const CachedFileSystemEntry *Entry)
184+
: Minimized(Minimized), Entry(Entry) {}
185+
186+
llvm::ErrorOr<llvm::vfs::Status> getStatus() const {
187+
auto MaybeStat = Entry->getStatus();
188+
if (!MaybeStat || MaybeStat->isDirectory())
189+
return MaybeStat;
190+
return llvm::vfs::Status::copyWithNewSize(*MaybeStat,
191+
getContents()->size());
192+
}
193+
194+
bool isDirectory() const { return Entry->isDirectory(); }
195+
196+
StringRef getName() const { return Entry->getName(); }
197+
198+
llvm::ErrorOr<StringRef> getContents() const {
199+
return Minimized ? Entry->getMinimizedContents()
200+
: Entry->getOriginalContents();
159201
}
160202

161-
const CachedFileSystemEntry *getCachedEntry(StringRef Filename,
162-
bool Minimized) {
163-
SingleCache &Cache = selectCache(Minimized);
164-
auto It = Cache.find(Filename);
165-
return It == Cache.end() ? nullptr : It->getValue();
203+
const PreprocessorSkippedRangeMapping *getPPSkippedRangeMapping() const {
204+
return Minimized ? &Entry->getPPSkippedRangeMapping() : nullptr;
166205
}
167206
};
168207

@@ -197,8 +236,7 @@ class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem {
197236
/// Check whether the file should be minimized.
198237
bool shouldMinimize(StringRef Filename);
199238

200-
llvm::ErrorOr<const CachedFileSystemEntry *>
201-
getOrCreateFileSystemEntry(const StringRef Filename);
239+
llvm::ErrorOr<EntryRef> getOrCreateFileSystemEntry(StringRef Filename);
202240

203241
/// The global cache shared between worker threads.
204242
DependencyScanningFilesystemSharedCache &SharedCache;

0 commit comments

Comments
 (0)