clang 24.0.0git
DependencyScanningFilesystem.cpp
Go to the documentation of this file.
1//===- DependencyScanningFilesystem.cpp - Optimized Scanning FS -----------===//
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
11#include "llvm/Support/MemoryBuffer.h"
12#include "llvm/Support/Threading.h"
13#include <optional>
14
15using namespace clang;
16using namespace dependencies;
17
18llvm::ErrorOr<DependencyScanningWorkerFilesystem::TentativeEntry>
19DependencyScanningWorkerFilesystem::readFile(StringRef Filename,
20 llvm::vfs::FileSystem &FS) {
21 // Load the file and its content from the file system.
22 auto MaybeFile = FS.openFileForRead(Filename);
23 if (!MaybeFile)
24 return MaybeFile.getError();
25 auto File = std::move(*MaybeFile);
26
27 auto MaybeStat = File->status();
28 if (!MaybeStat)
29 return MaybeStat.getError();
30 auto Stat = std::move(*MaybeStat);
31
32 auto MaybeBuffer = File->getBuffer(Stat.getName());
33 if (!MaybeBuffer)
34 return MaybeBuffer.getError();
35 auto Buffer = std::move(*MaybeBuffer);
36
37 // If the file size changed between read and stat, pretend it didn't.
38 if (Stat.getSize() != Buffer->getBufferSize())
39 Stat = llvm::vfs::Status::copyWithNewSize(Stat, Buffer->getBufferSize());
40
41 return TentativeEntry(Stat, std::move(Buffer));
42}
43
45 EntryRef Ref) {
46 auto &Entry = Ref.Entry;
47
48 if (Entry.isError() || Entry.isDirectory())
49 return false;
50
51 CachedFileContents *Contents = Entry.getCachedContents();
52 assert(Contents && "contents not initialized");
53
54 // Double-checked locking.
55 if (Contents->DepDirectives.load())
56 return true;
57
58 std::lock_guard<std::mutex> GuardLock(Contents->ValueLock);
59
60 // Double-checked locking.
61 if (Contents->DepDirectives.load())
62 return true;
63
65 // Scan the file for preprocessor directives that might affect the
66 // dependencies.
67 if (scanSourceForDependencyDirectives(Contents->Original->getBuffer(),
68 Contents->DepDirectiveTokens,
69 Directives)) {
70 Contents->DepDirectiveTokens.clear();
71 // FIXME: Propagate the diagnostic if desired by the client.
72 Contents->DepDirectives.store(new std::optional<DependencyDirectivesTy>());
73 return false;
74 }
75
76 // This function performed double-checked locking using `DepDirectives`.
77 // Assigning it must be the last thing this function does, otherwise other
78 // threads may skip the critical section (`DepDirectives != nullptr`), leading
79 // to a data race.
80 Contents->DepDirectives.store(
81 new std::optional<DependencyDirectivesTy>(std::move(Directives)));
82 return true;
83}
84
87 // This heuristic was chosen using a empirical testing on a
88 // reasonably high core machine (iMacPro 18 cores / 36 threads). The cache
89 // sharding gives a performance edge by reducing the lock contention.
90 // FIXME: A better heuristic might also consider the OS to account for
91 // the different cost of lock contention on different OSes.
92 NumShards =
93 std::max(2u, llvm::hardware_concurrency().compute_thread_count() / 4);
94 CacheShards = std::make_unique<CacheShard[]>(NumShards);
95}
96
99 StringRef Filename) const {
100 assert(llvm::sys::path::is_absolute_gnu(Filename));
101 return CacheShards[llvm::hash_value(Filename) % NumShards];
102}
103
106 llvm::sys::fs::UniqueID UID) const {
107 auto Hash = llvm::hash_combine(UID.getDevice(), UID.getFile());
108 return CacheShards[Hash % NumShards];
109}
110
111std::vector<DependencyScanningFilesystemSharedCache::OutOfDateEntry>
113 llvm::vfs::FileSystem &UnderlyingFS) const {
114 // Iterate through all shards and look for cached stat errors.
115 std::vector<OutOfDateEntry> InvalidDiagInfo;
116 for (unsigned i = 0; i < NumShards; i++) {
117 const CacheShard &Shard = CacheShards[i];
118 std::lock_guard<std::mutex> LockGuard(Shard.CacheLock);
119 for (const auto &[Path, State] : Shard.CacheByFilename) {
120 const CachedFileSystemEntry *Entry = State.Entry;
121 // Skip slots without a resolved entry: real-path-only entries from
122 // getRealPath, or uncached negative stats. Runs post-scan, so no
123 // in-progress slots remain.
124 if (!Entry)
125 continue;
126 llvm::ErrorOr<llvm::vfs::Status> Status = UnderlyingFS.status(Path);
127 if (Status) {
128 if (Entry->getError()) {
129 // This is the case where we have cached the non-existence
130 // of the file at Path first, and a file at the path is created
131 // later. The cache entry is not invalidated (as we have no good
132 // way to do it now), which may lead to missing file build errors.
133 InvalidDiagInfo.emplace_back(Path.data());
134 } else {
135 llvm::vfs::Status CachedStatus = Entry->getStatus();
136 if (Status->getType() == llvm::sys::fs::file_type::regular_file &&
137 Status->getType() == CachedStatus.getType()) {
138 // We only check regular files. Directory files sizes could change
139 // due to content changes, and reporting directory size changes can
140 // lead to false positives.
141 // TODO: At the moment, we do not detect symlinks to files whose
142 // size may change. We need to decide if we want to detect cached
143 // symlink size changes. We can also expand this to detect file
144 // type changes.
145 uint64_t CachedSize = CachedStatus.getSize();
146 uint64_t ActualSize = Status->getSize();
147 if (CachedSize != ActualSize) {
148 // This is the case where the cached file has a different size
149 // from the actual file that comes from the underlying FS.
150 InvalidDiagInfo.emplace_back(Path.data(), CachedSize, ActualSize);
151 }
152 }
153 }
154 }
155 }
156 }
157 return InvalidDiagInfo;
158}
159
160namespace {
161using InProgressEntry =
163using SlotResolved = llvm::ErrorOr<const CachedFileSystemEntry *>;
164using SlotProducer = std::shared_ptr<InProgressEntry>;
165using SlotAcquisitionResult = std::variant<SlotResolved, SlotProducer>;
166
167/// Returns a resolved entry if one is already present or in-flight under
168/// \p K; otherwise installs a fresh \c InProgressEntry and returns it as a
169/// producer slot.
170template <typename Map, typename Key>
171SlotAcquisitionResult acquireSlot(std::mutex &CacheLock, Map &M, const Key &K) {
172 std::shared_ptr<InProgressEntry> Pending;
173 {
174 std::lock_guard<std::mutex> ShardLock(CacheLock);
175 auto &State = M[K];
176
177 // Cache hit.
178 if (State.Entry)
179 return SlotResolved{State.Entry};
180
181 if (!State.InProgress) {
182 State.InProgress = std::make_shared<InProgressEntry>();
183 return SlotProducer{State.InProgress};
184 }
185
186 // Copy the shared_ptr so the slot survives our wait once the shard lock
187 // is released and the producer resets State.InProgress on publish.
188 Pending = State.InProgress;
189 }
190
191 // Wait off the shard lock so unrelated keys in this shard aren't blocked.
192 std::unique_lock<std::mutex> EntryLock(Pending->Mutex);
193 Pending->CondVar.wait(EntryLock, [&] { return Pending->Done; });
194 return SlotResolved{Pending->Result};
195}
196} // namespace
197
198const CachedRealPath *
200 StringRef Filename) const {
201 assert(llvm::sys::path::is_absolute_gnu(Filename));
202 std::lock_guard<std::mutex> LockGuard(CacheLock);
203 auto It = CacheByFilename.find(Filename);
204 return It == CacheByFilename.end() ? nullptr : It->getValue().RealPath;
205}
206
208 getOrEmplaceRealPathForFilename(StringRef Filename,
209 llvm::ErrorOr<llvm::StringRef> RealPath) {
210 std::lock_guard<std::mutex> LockGuard(CacheLock);
211
212 const CachedRealPath *&StoredRealPath = CacheByFilename[Filename].RealPath;
213 if (!StoredRealPath) {
214 auto OwnedRealPath = [&]() -> CachedRealPath {
215 if (!RealPath)
216 return RealPath.getError();
217 return RealPath->str();
218 }();
219
220 StoredRealPath = new (RealPathStorage.Allocate())
221 CachedRealPath(std::move(OwnedRealPath));
222 }
223
224 return *StoredRealPath;
225}
226
230 if (LocalFS) {
231 auto OverlayVFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(
232 std::move(LocalFS));
233 OverlayVFS->pushOverlay(std::move(GlobalFS));
234 GlobalFS = std::move(OverlayVFS);
235 }
236 return GlobalFS;
237}
238
244 llvm::vfs::ProxyFileSystem>(
245 combineGlobalAndLocalFS(FS, LocalFS)),
246 Service(Service), WorkingDirForCacheLookup(llvm::errc::invalid_argument),
247 LocalFS(std::move(LocalFS)), BaseFS(FS) {
248 updateWorkingDirForCacheLookup();
249}
250
251const CachedFileSystemEntry *DependencyScanningWorkerFilesystem::createEntry(
252 llvm::ErrorOr<TentativeEntry> &TEntry,
253 llvm::SpecificBumpPtrAllocator<CachedFileContents> &ContentsStorage,
254 llvm::SpecificBumpPtrAllocator<CachedFileSystemEntry> &EntryStorage) {
255 if (TEntry) {
256 CachedFileContents *StoredContents = nullptr;
257 if (TEntry->Contents)
258 StoredContents = new (ContentsStorage.Allocate())
259 CachedFileContents(std::move(TEntry->Contents));
260 return new (EntryStorage.Allocate())
261 CachedFileSystemEntry(std::move(TEntry->Status), StoredContents);
262 } else {
263 return new (EntryStorage.Allocate())
264 CachedFileSystemEntry(TEntry.getError());
265 }
266}
267
269DependencyScanningWorkerFilesystem::resolveUIDThroughSharedCache(
270 StringRef OriginalFilename, const llvm::vfs::Status &Stat) {
271 auto &UIDShard = Service.getSharedCache().getShardForUID(Stat.getUniqueID());
272 auto UIDSlot = acquireSlot(UIDShard.CacheLock, UIDShard.EntriesByUID,
273 Stat.getUniqueID());
274 if (auto *Resolved = std::get_if<SlotResolved>(&UIDSlot)) {
275 assert(*Resolved && **Resolved &&
276 "in-progress UID slot fulfilled without an entry");
277 return **Resolved;
278 }
279 auto UIDProducer = std::move(std::get<SlotProducer>(UIDSlot));
280
281 auto TEntry = Stat.isDirectory() ? TentativeEntry(Stat)
282 : readFile(OriginalFilename, *BaseFS);
283
284 // Allocate the entry and bind the UID slot under one shard-lock acquisition
285 // (BumpPtrAllocator isn't thread-safe). On read-failure, the entry wraps
286 // the open error so concurrent UID waiters surface it rather than racing
287 // to retry the open.
288 const CachedFileSystemEntry *SharedEntry;
289 {
290 std::lock_guard<std::mutex> ShardLock(UIDShard.CacheLock);
291 auto &State = UIDShard.EntriesByUID[Stat.getUniqueID()];
292 assert(!State.Entry && "UID slot already published an entry");
293 SharedEntry =
294 createEntry(TEntry, UIDShard.ContentsStorage, UIDShard.EntryStorage);
295 State.Entry = SharedEntry;
296 State.InProgress.reset();
297 }
298 UIDProducer->publish(SharedEntry);
299 return SharedEntry;
300}
301
303DependencyScanningWorkerFilesystem::createCachedFSEntryForLocal(
304 StringRef OriginalFilename, const llvm::vfs::Status &Stat) {
305 assert(LocalFS);
306 auto TEntry = Stat.isDirectory() ? TentativeEntry(Stat)
307 : readFile(OriginalFilename, *LocalFS);
308 return createEntry(TEntry, LocalContentsStorage, LocalEntryStorage);
309}
310
311llvm::ErrorOr<const CachedFileSystemEntry *>
312DependencyScanningWorkerFilesystem::resolveFilenameThroughSharedCache(
313 StringRef OriginalFilename, StringRef FilenameForLookup) {
314 if (LocalFS) {
315 if (auto Stat = LocalFS->status(OriginalFilename)) {
316 return createCachedFSEntryForLocal(OriginalFilename, *Stat);
317 }
318 }
319 assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
320 auto &FilenameShard =
321 Service.getSharedCache().getShardForFilename(FilenameForLookup);
322 auto FilenameSlot =
323 acquireSlot(FilenameShard.CacheLock, FilenameShard.CacheByFilename,
324 FilenameForLookup);
325 if (auto *Resolved = std::get_if<SlotResolved>(&FilenameSlot))
326 return *Resolved;
327 auto FilenameProducer = std::move(std::get<SlotProducer>(FilenameSlot));
328
329 // Compute the outcome. Three cases:
330 // - Stat succeeded: delegate to the UID resolver.
331 // - Stat failed, cacheable: defer error-entry allocation to the critical
332 // section below so allocate+bind+reset share one shard-lock acquisition.
333 // - Stat failed, not cacheable: publish the error to current waiters but
334 // don't persist; a later separate query re-runs the stat (so a file
335 // created mid-scan becomes visible).
336 auto Stat = BaseFS->status(OriginalFilename);
337 const bool ShouldCacheNegativeStat =
338 !Stat && Service.getOpts().CacheNegativeStats &&
339 shouldCacheNegativeStatsForPath(OriginalFilename);
340 llvm::ErrorOr<const CachedFileSystemEntry *> Result = std::error_code{};
341 if (Stat)
342 Result = resolveUIDThroughSharedCache(OriginalFilename, *Stat);
343 else if (!ShouldCacheNegativeStat)
344 Result = Stat.getError();
345
346 // Bind the result and reset the in-flight slot under a single critical
347 // section. The cached-negative case allocates here so allocate+bind+reset
348 // share one shard-lock acquisition.
349 {
350 std::lock_guard<std::mutex> ShardLock(FilenameShard.CacheLock);
351 auto &State = FilenameShard.CacheByFilename[FilenameForLookup];
352 assert(!State.Entry && "filename slot already published an entry");
353 if (ShouldCacheNegativeStat) {
354 auto *Entry = new (FilenameShard.EntryStorage.Allocate())
355 CachedFileSystemEntry(Stat.getError());
356 State.Entry = Entry;
357 Result = Entry;
358 } else if (Result) {
359 State.Entry = *Result;
360 }
361 State.InProgress.reset();
362 }
363 FilenameProducer->publish(Result);
364 return Result;
365}
366
367llvm::ErrorOr<EntryRef>
369 StringRef OriginalFilename) {
370 SmallString<256> PathBuf;
371 auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf);
372 if (!FilenameForLookup)
373 return FilenameForLookup.getError();
374
375 auto &Local = LocalCache[*FilenameForLookup];
376 if (Local.File)
377 return EntryRef(OriginalFilename, *Local.File).unwrapError();
378
379 auto MaybeEntry =
380 resolveFilenameThroughSharedCache(OriginalFilename, *FilenameForLookup);
381 if (!MaybeEntry)
382 return MaybeEntry.getError();
383 Local.File = *MaybeEntry;
384 return EntryRef(OriginalFilename, **MaybeEntry).unwrapError();
385}
386
387llvm::ErrorOr<llvm::vfs::Status>
389 SmallString<256> OwnedFilename;
390 StringRef Filename = Path.toStringRef(OwnedFilename);
391
392 llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename);
393 if (!Result)
394 return Result.getError();
395 return Result->getStatus();
396}
397
399 // While some VFS overlay filesystems may implement more-efficient
400 // mechanisms for `exists` queries, `DependencyScanningWorkerFilesystem`
401 // typically wraps `RealFileSystem` which does not specialize `exists`,
402 // so it is not likely to benefit from such optimizations. Instead,
403 // it is more-valuable to have this query go through the
404 // cached-`status` code-path of the `DependencyScanningWorkerFilesystem`.
405 llvm::ErrorOr<llvm::vfs::Status> Status = status(Path);
406 return Status && Status->exists();
407}
408
409namespace {
410
411/// The VFS that is used by clang consumes the \c CachedFileSystemEntry using
412/// this subclass.
413class DepScanFile final : public llvm::vfs::File {
414public:
415 DepScanFile(std::unique_ptr<llvm::MemoryBuffer> Buffer,
416 llvm::vfs::Status Stat)
417 : Buffer(std::move(Buffer)), Stat(std::move(Stat)) {}
418
419 static llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> create(EntryRef Entry);
420
421 llvm::ErrorOr<llvm::vfs::Status> status() override { return Stat; }
422
423 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
424 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
425 bool IsVolatile) override {
426 return llvm::MemoryBuffer::getMemBuffer(Buffer->getMemBufferRef(),
427 RequiresNullTerminator);
428 }
429
430 std::error_code close() override { return {}; }
431
432private:
433 std::unique_ptr<llvm::MemoryBuffer> Buffer;
434 llvm::vfs::Status Stat;
435};
436
437} // end anonymous namespace
438
439llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
440DepScanFile::create(EntryRef Entry) {
441 assert(!Entry.isError() && "error");
442
443 if (Entry.isDirectory())
444 return std::make_error_code(std::errc::is_a_directory);
445
446 auto Result = std::make_unique<DepScanFile>(
447 llvm::MemoryBuffer::getMemBuffer(Entry.getContents(),
448 Entry.getStatus().getName(),
449 /*RequiresNullTerminator=*/false),
450 Entry.getStatus());
451
452 return llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>(
453 std::unique_ptr<llvm::vfs::File>(std::move(Result)));
454}
455
456llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
458 SmallString<256> OwnedFilename;
459 StringRef Filename = Path.toStringRef(OwnedFilename);
460
461 llvm::ErrorOr<EntryRef> Result = getOrCreateFileSystemEntry(Filename);
462 if (!Result)
463 return Result.getError();
464 return DepScanFile::create(Result.get());
465}
466
467std::error_code
469 SmallVectorImpl<char> &Output) {
470 SmallString<256> OwnedFilename;
471 StringRef OriginalFilename = Path.toStringRef(OwnedFilename);
472
473 SmallString<256> PathBuf;
474 auto FilenameForLookup = tryGetFilenameForLookup(OriginalFilename, PathBuf);
475 if (!FilenameForLookup)
476 return FilenameForLookup.getError();
477
478 auto HandleCachedRealPath =
479 [&Output](const CachedRealPath &RealPath) -> std::error_code {
480 if (!RealPath)
481 return RealPath.getError();
482 Output.assign(RealPath->begin(), RealPath->end());
483 return {};
484 };
485
486 // If we already have the result in local cache, no work required.
487 auto &Local = LocalCache[*FilenameForLookup];
488 if (Local.RealPath)
489 return HandleCachedRealPath(*Local.RealPath);
490
491 // If we have the result in the shared cache, cache it locally.
492 auto &Shard =
493 Service.getSharedCache().getShardForFilename(*FilenameForLookup);
494 if (const auto *ShardRealPath =
495 Shard.findRealPathByFilename(*FilenameForLookup)) {
496 Local.RealPath = ShardRealPath;
497 return HandleCachedRealPath(*Local.RealPath);
498 }
499
500 // If we don't know the real path, compute it...
501 std::error_code EC = getUnderlyingFS().getRealPath(OriginalFilename, Output);
502 llvm::ErrorOr<llvm::StringRef> ComputedRealPath = EC;
503 if (!EC)
504 ComputedRealPath = StringRef{Output.data(), Output.size()};
505
506 // ...and try to write it into the shared cache. In case some other thread won
507 // this race and already wrote its own result there, just adopt it. Write
508 // whatever is in the shared cache into the local one.
509 const auto &RealPath = Shard.getOrEmplaceRealPathForFilename(
510 *FilenameForLookup, ComputedRealPath);
511 Local.RealPath = &RealPath;
512 return HandleCachedRealPath(*Local.RealPath);
513}
514
516 const Twine &Path) {
517 std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path);
518 updateWorkingDirForCacheLookup();
519 return EC;
520}
521
522void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() {
523 llvm::ErrorOr<std::string> CWD =
524 getUnderlyingFS().getCurrentWorkingDirectory();
525 if (!CWD) {
526 WorkingDirForCacheLookup = CWD.getError();
527 } else if (!llvm::sys::path::is_absolute_gnu(*CWD)) {
528 WorkingDirForCacheLookup = llvm::errc::invalid_argument;
529 } else {
530 WorkingDirForCacheLookup = *CWD;
531 }
532 assert(!WorkingDirForCacheLookup ||
533 llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup));
534}
535
536llvm::ErrorOr<StringRef>
537DependencyScanningWorkerFilesystem::tryGetFilenameForLookup(
538 StringRef OriginalFilename, llvm::SmallVectorImpl<char> &PathBuf) const {
539 StringRef FilenameForLookup;
540 if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
541 FilenameForLookup = OriginalFilename;
542 } else if (!WorkingDirForCacheLookup) {
543 return WorkingDirForCacheLookup.getError();
544 } else {
545 StringRef RelFilename = OriginalFilename;
546 RelFilename.consume_front("./");
547 PathBuf.assign(WorkingDirForCacheLookup->begin(),
548 WorkingDirForCacheLookup->end());
549 llvm::sys::path::append(PathBuf, RelFilename);
550 FilenameForLookup = StringRef{PathBuf.begin(), PathBuf.size()};
551 }
552 assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
553 return FilenameForLookup;
554}
555
static IntrusiveRefCntPtr< llvm::vfs::FileSystem > combineGlobalAndLocalFS(IntrusiveRefCntPtr< llvm::vfs::FileSystem > GlobalFS, IntrusiveRefCntPtr< llvm::vfs::FileSystem > LocalFS)
An in-memory representation of a file system entity that is of interest to the dependency scanning fi...
CacheShard & getShardForFilename(StringRef Filename) const
Returns shard for the given key.
std::vector< OutOfDateEntry > getOutOfDateEntries(llvm::vfs::FileSystem &UnderlyingFS) const
Visits all cached entries and re-stat an entry using UnderlyingFS to check if the cache contains out-...
The dependency scanning service contains shared configuration and state that is used by the individua...
std::error_code getRealPath(const Twine &Path, SmallVectorImpl< char > &Output) override
DependencyScanningWorkerFilesystem(DependencyScanningService &Service, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS, IntrusiveRefCntPtr< llvm::vfs::FileSystem > LocalFS=nullptr)
bool ensureDirectiveTokensArePopulated(EntryRef Entry)
Ensure the directive tokens are populated for this file entry.
bool exists(const Twine &Path) override
Check whether Path exists.
llvm::ErrorOr< EntryRef > getOrCreateFileSystemEntry(StringRef Filename)
Returns entry for the given filename.
llvm::ErrorOr< std::unique_ptr< llvm::vfs::File > > openFileForRead(const Twine &Path) override
std::error_code setCurrentWorkingDirectory(const Twine &Path) override
llvm::ErrorOr< llvm::vfs::Status > status(const Twine &Path) override
Reference to a CachedFileSystemEntry.
llvm::ErrorOr< EntryRef > unwrapError() const
If the cached entry represents an error, promotes it into ErrorOr.
llvm::ErrorOr< std::string > CachedRealPath
bool shouldCacheNegativeStatsForPath(StringRef Path)
Top level wrappers for InstallAPI frontend operations.
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
bool scanSourceForDependencyDirectives(StringRef Input, SmallVectorImpl< dependency_directives_scan::Token > &Tokens, SmallVectorImpl< dependency_directives_scan::Directive > &Directives, DiagnosticsEngine *Diags=nullptr, SourceLocation InputSourceLoc=SourceLocation())
Scan the input for the preprocessor directives that might have an effect on the dependencies for a co...
@ Result
The result type of a method or function.
Definition TypeBase.h:906
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30
hash_code hash_value(const clang::dependencies::ModuleID &ID)
__builtin_bitreverse32 uint32_t __builtin_riscv_aaddu_u32 __builtin_riscv_asubu_u32 __builtin_elementwise_add_sat __builtin_elementwise_sub_sat int32_t uint32_t uint32_t __builtin_riscv_mhacc_i32 __builtin_riscv_mhaccu_u32 __builtin_riscv_mhaccsu_i32 __builtin_riscv_mulq_i32 int64_t
Contents and directive tokens of a cached file entry.
std::unique_ptr< llvm::MemoryBuffer > Original
Owning storage for the original contents.
SmallVector< dependency_directives_scan::Token, 10 > DepDirectiveTokens
std::atomic< const std::optional< DependencyDirectivesTy > * > DepDirectives
Accessor to the directive tokens that's atomic to avoid data races.
std::mutex ValueLock
The mutex that must be locked before mutating directive tokens.
llvm::StringMap< FilenameCacheState, llvm::BumpPtrAllocator > CacheByFilename
Map from filenames to their cached state.
llvm::SpecificBumpPtrAllocator< CachedRealPath > RealPathStorage
The backing storage for cached real paths.
const CachedRealPath * findRealPathByFilename(StringRef Filename) const
Returns the real path associated with the filename or nullptr if none is found.
std::mutex CacheLock
The mutex that needs to be locked before mutation of any member.
const CachedRealPath & getOrEmplaceRealPathForFilename(StringRef Filename, llvm::ErrorOr< StringRef > RealPath)
Returns the real path associated with the filename if there is some.
In-flight slot used to dedup concurrent producers for the same key.