Skip to content

Commit 440381c

Browse files
authored
Merge pull request #8484 from apple/jan_svoboda/lazy-dep-directives
[clang][deps] Lazy dependency directives rdar://125519128
2 parents 407ea67 + 4888053 commit 440381c

File tree

6 files changed

+74
-93
lines changed

6 files changed

+74
-93
lines changed

clang/include/clang/Tooling/DependencyScanning/DependencyScanningCASFilesystem.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,6 @@ class DependencyScanningCASFilesystem : public llvm::cas::ThreadSafeFileSystem {
7373
getDirectiveTokens(const Twine &Path);
7474

7575
private:
76-
/// Check whether the file should be scanned for preprocessor directives.
77-
bool shouldScanForDirectives(StringRef Filename);
78-
7976
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
8077

8178
struct FileEntry {

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ class EntryRef {
255255
/// The underlying cached entry.
256256
const CachedFileSystemEntry &Entry;
257257

258+
friend class DependencyScanningWorkerFilesystem;
259+
258260
public:
259261
EntryRef(StringRef Name, const CachedFileSystemEntry &Entry)
260262
: Filename(Name), Entry(Entry) {}
@@ -316,14 +318,15 @@ class DependencyScanningWorkerFilesystem
316318
///
317319
/// Attempts to use the local and shared caches first, then falls back to
318320
/// using the underlying filesystem.
319-
llvm::ErrorOr<EntryRef>
320-
getOrCreateFileSystemEntry(StringRef Filename,
321-
bool DisableDirectivesScanning = false);
321+
llvm::ErrorOr<EntryRef> getOrCreateFileSystemEntry(StringRef Filename);
322322

323-
private:
324-
/// Check whether the file should be scanned for preprocessor directives.
325-
bool shouldScanForDirectives(StringRef Filename);
323+
/// Ensure the directive tokens are populated for this file entry.
324+
///
325+
/// Returns true if the directive tokens are populated for this file entry,
326+
/// false if not (i.e. this entry is not a file or its scan fails).
327+
bool ensureDirectiveTokensArePopulated(EntryRef Entry);
326328

329+
private:
327330
/// For a filename that's not yet associated with any entry in the caches,
328331
/// uses the underlying filesystem to either look up the entry based in the
329332
/// shared cache indexed by unique ID, or creates new entry from scratch.
@@ -333,11 +336,6 @@ class DependencyScanningWorkerFilesystem
333336
computeAndStoreResult(StringRef OriginalFilename,
334337
StringRef FilenameForLookup);
335338

336-
/// Scan for preprocessor directives for the given entry if necessary and
337-
/// returns a wrapper object with reference semantics.
338-
EntryRef scanForDirectivesIfNecessary(const CachedFileSystemEntry &Entry,
339-
StringRef Filename, bool Disable);
340-
341339
/// Represents a filesystem entry that has been stat-ed (and potentially read)
342340
/// and that's about to be inserted into the cache as `CachedFileSystemEntry`.
343341
struct TentativeEntry {

clang/lib/Tooling/DependencyScanning/DependencyScanningCASFilesystem.cpp

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -200,35 +200,11 @@ DependencyScanningCASFilesystem::getOriginal(cas::CASID InputDataID) {
200200
return Blob.takeError();
201201
}
202202

203-
/// Whitelist file extensions that should be minimized, treating no extension as
204-
/// a source file that should be minimized.
205-
///
206-
/// This is kinda hacky, it would be better if we knew what kind of file Clang
207-
/// was expecting instead.
208-
static bool shouldScanForDirectivesBasedOnExtension(StringRef Filename) {
209-
StringRef Ext = llvm::sys::path::extension(Filename);
210-
if (Ext.empty())
211-
return true; // C++ standard library
212-
return llvm::StringSwitch<bool>(Ext)
213-
.CasesLower(".c", ".cc", ".cpp", ".c++", ".cxx", true)
214-
.CasesLower(".h", ".hh", ".hpp", ".h++", ".hxx", true)
215-
.CasesLower(".m", ".mm", true)
216-
.CasesLower(".i", ".ii", ".mi", ".mmi", true)
217-
.CasesLower(".def", ".inc", true)
218-
.Default(false);
219-
}
220-
221203
static bool shouldCacheStatFailures(StringRef Filename) {
222204
StringRef Ext = llvm::sys::path::extension(Filename);
223205
if (Ext.empty())
224206
return false; // This may be the module cache directory.
225-
return shouldScanForDirectivesBasedOnExtension(
226-
Filename); // Only cache stat failures on source files.
227-
}
228-
229-
bool DependencyScanningCASFilesystem::shouldScanForDirectives(
230-
StringRef RawFilename) {
231-
return shouldScanForDirectivesBasedOnExtension(RawFilename);
207+
return true;
232208
}
233209

234210
llvm::cas::CachingOnDiskFileSystem &
@@ -274,10 +250,6 @@ DependencyScanningCASFilesystem::lookupPath(const Twine &Path) {
274250
return LookupPathResult{&Entry, std::error_code()};
275251
}
276252

277-
if (shouldScanForDirectives(PathRef))
278-
scanForDirectives(*CAS.getReference(*FileID), PathRef, Entry.DepTokens,
279-
Entry.DepDirectives);
280-
281253
Entry.Buffer = std::move(*Buffer);
282254
Entry.Status = llvm::vfs::Status(
283255
PathRef, MaybeStatus->getUniqueID(),
@@ -362,7 +334,18 @@ DependencyScanningCASFilesystem::openFileForRead(const Twine &Path) {
362334
std::optional<ArrayRef<dependency_directives_scan::Directive>>
363335
DependencyScanningCASFilesystem::getDirectiveTokens(const Twine &Path) {
364336
LookupPathResult Result = lookupPath(Path);
365-
if (Result.Entry && !Result.Entry->DepDirectives.empty())
366-
return ArrayRef(Result.Entry->DepDirectives);
337+
338+
if (Result.Entry) {
339+
if (Result.Entry->DepDirectives.empty()) {
340+
SmallString<256> PathStorage;
341+
StringRef PathRef = Path.toStringRef(PathStorage);
342+
FileEntry &Entry = const_cast<FileEntry &>(*Result.Entry);
343+
scanForDirectives(*Entry.CASContents, PathRef, Entry.DepTokens,
344+
Entry.DepDirectives);
345+
}
346+
347+
if (!Result.Entry->DepDirectives.empty())
348+
return ArrayRef(Result.Entry->DepDirectives);
349+
}
367350
return std::nullopt;
368351
}

clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp

Lines changed: 16 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,25 @@ DependencyScanningWorkerFilesystem::readFile(StringRef Filename) {
4646
return TentativeEntry(Stat, std::move(Buffer), std::move(CASContents));
4747
}
4848

49-
EntryRef DependencyScanningWorkerFilesystem::scanForDirectivesIfNecessary(
50-
const CachedFileSystemEntry &Entry, StringRef Filename, bool Disable) {
51-
if (Entry.isError() || Entry.isDirectory() || Disable ||
52-
!shouldScanForDirectives(Filename))
53-
return EntryRef(Filename, Entry);
49+
bool DependencyScanningWorkerFilesystem::ensureDirectiveTokensArePopulated(
50+
EntryRef Ref) {
51+
auto &Entry = Ref.Entry;
52+
53+
if (Entry.isError() || Entry.isDirectory())
54+
return false;
5455

5556
CachedFileContents *Contents = Entry.getCachedContents();
5657
assert(Contents && "contents not initialized");
5758

5859
// Double-checked locking.
5960
if (Contents->DepDirectives.load())
60-
return EntryRef(Filename, Entry);
61+
return true;
6162

6263
std::lock_guard<std::mutex> GuardLock(Contents->ValueLock);
6364

6465
// Double-checked locking.
6566
if (Contents->DepDirectives.load())
66-
return EntryRef(Filename, Entry);
67+
return true;
6768

6869
SmallVector<dependency_directives_scan::Directive, 64> Directives;
6970
// Scan the file for preprocessor directives that might affect the
@@ -74,16 +75,16 @@ EntryRef DependencyScanningWorkerFilesystem::scanForDirectivesIfNecessary(
7475
Contents->DepDirectiveTokens.clear();
7576
// FIXME: Propagate the diagnostic if desired by the client.
7677
Contents->DepDirectives.store(new std::optional<DependencyDirectivesTy>());
77-
return EntryRef(Filename, Entry);
78+
return false;
7879
}
7980

8081
// This function performed double-checked locking using `DepDirectives`.
8182
// Assigning it must be the last thing this function does, otherwise other
82-
// threads may skip the
83-
// critical section (`DepDirectives != nullptr`), leading to a data race.
83+
// threads may skip the critical section (`DepDirectives != nullptr`), leading
84+
// to a data race.
8485
Contents->DepDirectives.store(
8586
new std::optional<DependencyDirectivesTy>(std::move(Directives)));
86-
return EntryRef(Filename, Entry);
87+
return true;
8788
}
8889

8990
DependencyScanningFilesystemSharedCache::
@@ -167,34 +168,11 @@ DependencyScanningFilesystemSharedCache::CacheShard::
167168
return *EntriesByFilename.insert({Filename, &Entry}).first->getValue();
168169
}
169170

170-
/// Whitelist file extensions that should be minimized, treating no extension as
171-
/// a source file that should be minimized.
172-
///
173-
/// This is kinda hacky, it would be better if we knew what kind of file Clang
174-
/// was expecting instead.
175-
static bool shouldScanForDirectivesBasedOnExtension(StringRef Filename) {
176-
StringRef Ext = llvm::sys::path::extension(Filename);
177-
if (Ext.empty())
178-
return true; // C++ standard library
179-
return llvm::StringSwitch<bool>(Ext)
180-
.CasesLower(".c", ".cc", ".cpp", ".c++", ".cxx", true)
181-
.CasesLower(".h", ".hh", ".hpp", ".h++", ".hxx", true)
182-
.CasesLower(".m", ".mm", true)
183-
.CasesLower(".i", ".ii", ".mi", ".mmi", true)
184-
.CasesLower(".def", ".inc", true)
185-
.Default(false);
186-
}
187-
188171
static bool shouldCacheStatFailures(StringRef Filename) {
189172
StringRef Ext = llvm::sys::path::extension(Filename);
190173
if (Ext.empty())
191174
return false; // This may be the module cache directory.
192-
// Only cache stat failures on files that are not expected to change during
193-
// the build.
194-
StringRef FName = llvm::sys::path::filename(Filename);
195-
if (FName == "module.modulemap" || FName == "module.map")
196-
return true;
197-
return shouldScanForDirectivesBasedOnExtension(Filename);
175+
return true;
198176
}
199177

200178
DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem(
@@ -207,11 +185,6 @@ DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem(
207185
updateWorkingDirForCacheLookup();
208186
}
209187

210-
bool DependencyScanningWorkerFilesystem::shouldScanForDirectives(
211-
StringRef Filename) {
212-
return shouldScanForDirectivesBasedOnExtension(Filename);
213-
}
214-
215188
const CachedFileSystemEntry &
216189
DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID(
217190
TentativeEntry TEntry) {
@@ -265,7 +238,7 @@ DependencyScanningWorkerFilesystem::computeAndStoreResult(
265238

266239
llvm::ErrorOr<EntryRef>
267240
DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry(
268-
StringRef OriginalFilename, bool DisableDirectivesScanning) {
241+
StringRef OriginalFilename) {
269242
StringRef FilenameForLookup;
270243
SmallString<256> PathBuf;
271244
if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
@@ -282,15 +255,11 @@ DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry(
282255
assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
283256
if (const auto *Entry =
284257
findEntryByFilenameWithWriteThrough(FilenameForLookup))
285-
return scanForDirectivesIfNecessary(*Entry, OriginalFilename,
286-
DisableDirectivesScanning)
287-
.unwrapError();
258+
return EntryRef(OriginalFilename, *Entry).unwrapError();
288259
auto MaybeEntry = computeAndStoreResult(OriginalFilename, FilenameForLookup);
289260
if (!MaybeEntry)
290261
return MaybeEntry.getError();
291-
return scanForDirectivesIfNecessary(*MaybeEntry, OriginalFilename,
292-
DisableDirectivesScanning)
293-
.unwrapError();
262+
return EntryRef(OriginalFilename, *MaybeEntry).unwrapError();
294263
}
295264

296265
llvm::ErrorOr<llvm::vfs::Status>

clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,8 @@ class DependencyScanningAction : public tooling::ToolAction {
507507
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
508508
if (llvm::ErrorOr<EntryRef> Entry =
509509
LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
510-
return Entry->getDirectiveTokens();
510+
if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
511+
return Entry->getDirectiveTokens();
511512
return std::nullopt;
512513
};
513514
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
4+
// This test checks that source files with uncommon extensions still undergo
5+
// dependency directives scan. If header.pch would not and b.h would, the scan
6+
// would fail when parsing `void function(B)` and not knowing the symbol B.
7+
8+
//--- module.modulemap
9+
module __PCH { header "header.pch" }
10+
module B { header "b.h" }
11+
12+
//--- header.pch
13+
#include "b.h"
14+
void function(B);
15+
16+
//--- b.h
17+
typedef int B;
18+
19+
//--- tu.c
20+
int main() {
21+
function(0);
22+
return 0;
23+
}
24+
25+
//--- cdb.json.in
26+
[{
27+
"directory": "DIR",
28+
"file": "DIR/tu.c",
29+
"command": "clang -c DIR/tu.c -fmodules -fmodules-cache-path=DIR/cache -fimplicit-module-maps -include DIR/header.pch"
30+
}]
31+
32+
// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.in > %t/cdb.json
33+
// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full > %t/deps.json

0 commit comments

Comments
 (0)