From a3c58f95ab9a1bd71c13f6b14da82e3d1b43d466 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Tue, 9 Dec 2025 10:23:27 -0800 Subject: [PATCH] Add caching of unsuccessful Swift dependency lookups Use it to avoid unnecessary re-queries of Swift dependencies during a given scanning action. --- include/swift/AST/ModuleDependencies.h | 9 +++++++++ lib/AST/ModuleDependencies.cpp | 10 +++++++++- .../ModuleDependencyScanner.cpp | 20 ++++++++++++++----- .../basic_query_metrics.swift | 2 +- .../module_deps_cache_reuse.swift | 2 ++ 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index f70410465355f..350317bf83c94 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -1073,6 +1073,9 @@ class ModuleDependenciesCache { private: /// Discovered dependencies ModuleDependenciesKindMap ModuleDependenciesMap; + /// A set of module identifiers for which a scanning action failed + /// to discover a Swift module dependency + llvm::StringSet<> negativeSwiftDependencyCache; /// Set containing all of the Clang modules that have already been seen. llvm::DenseSet alreadySeenClangModules; /// Name of the module under scan @@ -1111,6 +1114,10 @@ class ModuleDependenciesCache { bool hasDependency(StringRef moduleName) const; /// Whether we have cached dependency information for the given Swift module. bool hasSwiftDependency(StringRef moduleName) const; + /// Whether we have previously failed a lookup of a Swift dependency for the + /// given identifier. + bool hasNegativeSwiftDependency(StringRef moduleName) const; + /// Report the number of recorded Clang dependencies int numberOfClangDependencies() const; /// Report the number of recorded Swift dependencies @@ -1232,6 +1239,8 @@ class ModuleDependenciesCache { void addVisibleClangModules(ModuleDependencyID moduleID, const std::vector &moduleNames); + /// Add an identifier to the set of failed Swift module queries + void recordFailedSwiftDependencyLookup(StringRef moduleIdentifier); StringRef getMainModuleName() const { return mainScanModuleName; } diff --git a/lib/AST/ModuleDependencies.cpp b/lib/AST/ModuleDependencies.cpp index 0e843a8651bd1..2126280c3ff54 100644 --- a/lib/AST/ModuleDependencies.cpp +++ b/lib/AST/ModuleDependencies.cpp @@ -760,6 +760,10 @@ bool ModuleDependenciesCache::hasSwiftDependency(StringRef moduleName) const { return findSwiftDependency(moduleName).has_value(); } +bool ModuleDependenciesCache::hasNegativeSwiftDependency(StringRef moduleName) const { + return negativeSwiftDependencyCache.contains(moduleName); +} + int ModuleDependenciesCache::numberOfClangDependencies() const { return ModuleDependenciesMap.at(ModuleDependencyKind::Clang).size(); } @@ -767,7 +771,6 @@ int ModuleDependenciesCache::numberOfSwiftDependencies() const { return ModuleDependenciesMap.at(ModuleDependencyKind::SwiftInterface).size() + ModuleDependenciesMap.at(ModuleDependencyKind::SwiftBinary).size(); } - void ModuleDependenciesCache::recordDependency( StringRef moduleName, ModuleDependencyInfo dependency) { auto dependenciesKind = dependency.getKind(); @@ -946,6 +949,11 @@ void ModuleDependenciesCache::addVisibleClangModules( updateDependency(moduleID, updatedDependencyInfo); } +void ModuleDependenciesCache::recordFailedSwiftDependencyLookup( + StringRef moduleIdentifier) { + negativeSwiftDependencyCache.insert(moduleIdentifier); +} + llvm::StringSet<> &ModuleDependenciesCache::getVisibleClangModules( ModuleDependencyID moduleID) const { ASSERT(moduleID.Kind == ModuleDependencyKind::SwiftSource || diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index 6d492ae752e89..c26ca1d7b10d5 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -26,6 +26,7 @@ #include "swift/Basic/Defer.h" #include "swift/Basic/FileTypes.h" #include "swift/Basic/PrettyStackTrace.h" +#include "swift/Basic/Statistic.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/Frontend/CompileJobCacheKey.h" #include "swift/Frontend/ModuleInterfaceLoader.h" @@ -1389,6 +1390,13 @@ void ModuleDependencyScanner::resolveSwiftImportsForModule( // Avoid querying the underlying Clang module here if (moduleID.ModuleName == dependsOn.importIdentifier) continue; + // Avoid querying Swift module dependencies previously discovered + if (DependencyCache.hasSwiftDependency(dependsOn.importIdentifier)) + continue; + // Avoid querying Swift module dependencies which have already produced + // in a negative (not found) result + if (DependencyCache.hasNegativeSwiftDependency(dependsOn.importIdentifier)) + continue; ScanningThreadPool.async( scanForSwiftModuleDependency, getModuleImportIdentifier(dependsOn.importIdentifier), @@ -1428,10 +1436,13 @@ void ModuleDependencyScanner::resolveSwiftImportsForModule( moduleImport.importIdentifier)) importedSwiftDependencies.insert( {moduleImport.importIdentifier, cachedInfo.value()->getKind()}); - else + else { ScanDiagnosticReporter.diagnoseFailureOnOnlyIncompatibleCandidates( moduleImport, lookupResult.incompatibleCandidates, DependencyCache, std::nullopt); + DependencyCache + .recordFailedSwiftDependencyLookup(moduleImport.importIdentifier); + } }; for (const auto &importInfo : moduleDependencyInfo.getModuleImports()) @@ -1575,10 +1586,9 @@ void ModuleDependencyScanner::resolveSwiftOverlayDependenciesForModule( auto moduleName = moduleIdentifier.str(); { std::lock_guard guard(lookupResultLock); - if (DependencyCache.hasDependency(moduleName, - ModuleDependencyKind::SwiftInterface) || - DependencyCache.hasDependency(moduleName, - ModuleDependencyKind::SwiftBinary)) + if (DependencyCache.hasDependency(moduleName, ModuleDependencyKind::SwiftInterface) || + DependencyCache.hasDependency(moduleName, ModuleDependencyKind::SwiftBinary) || + DependencyCache.hasNegativeSwiftDependency(moduleName)) return; } diff --git a/test/ScanDependencies/basic_query_metrics.swift b/test/ScanDependencies/basic_query_metrics.swift index 4b47ff4dc7cb4..25b472eeb1830 100644 --- a/test/ScanDependencies/basic_query_metrics.swift +++ b/test/ScanDependencies/basic_query_metrics.swift @@ -7,7 +7,7 @@ // RUN: cat %t/remarks.txt | %FileCheck %s // Ensure that despite being a common dependency to multiple Swift modules, only 1 query is performed to find 'C' -// CHECK: remark: Number of Swift module queries: '6' +// CHECK: remark: Number of Swift module queries: '3' // CHECK: remark: Number of named Clang module queries: '1' // CHEKC: remark: Number of recorded Clang module dependencies queried by-name from a Swift client: '1' // CHECK: remark: Number of recorded Swift module dependencies: '2' diff --git a/test/ScanDependencies/module_deps_cache_reuse.swift b/test/ScanDependencies/module_deps_cache_reuse.swift index 62f808df56f35..02f8ca9906340 100644 --- a/test/ScanDependencies/module_deps_cache_reuse.swift +++ b/test/ScanDependencies/module_deps_cache_reuse.swift @@ -28,6 +28,8 @@ import SubE // CHECK-REMARK-SAVE: remark: Incremental module scan: Serializing module scanning dependency cache to: // CHECK-REMARK-LOAD: remark: Incremental module scan: Re-using serialized module scanning dependency cache from: +// 'SwiftShims', 'C' and 'B' are Clang modules which are directly imorted from Swift code in this test, without a corresponding Swift overlay module. Because we do not serialize negative Swift dependency lookup results, resolving a dependency on these modules involves a query for whether a Swift module under this name can be found. This query fails and the subsequent query for this identifier as a Clang dependency is then able to re-use the loaded serialized cache. +// CHECK-REMARK-LOAD: remark: Number of Swift module queries: '3' // FIXME: Today, we do not serialize dependencies of the main source module which results in a lookup for 'C' even though // it is fully redundant. // CHECK-REMARK-LOAD: remark: Number of named Clang module queries: '1'