Skip to content

Commit 02b7a4d

Browse files
committed
[Dependency Scanning] Implement parallel imported module resolution
'ModuleDependencyScanner' maintains a Thread Pool along with a pool of workers which are capable of executing a filesystem lookup of a named module dependency. When resolving imports of a given Swift module, each import's resolution operation can be issued asunchronously.
1 parent c2e1e5c commit 02b7a4d

File tree

9 files changed

+475
-278
lines changed

9 files changed

+475
-278
lines changed

include/swift/AST/ModuleDependencies.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,9 @@ class ModuleDependencyInfo {
662662
/// Whether the dependencies are for a Swift module: either Textual, Source, Binary, or Placeholder.
663663
bool isSwiftModule() const;
664664

665+
/// Whether the dependencies are for a textual interface Swift module or a Source Swift module.
666+
bool isTextualSwiftModule() const;
667+
665668
/// Whether the dependencies are for a textual Swift module.
666669
bool isSwiftInterfaceModule() const;
667670

@@ -1001,13 +1004,20 @@ class ModuleDependenciesCache {
10011004
ModuleDependenciesCache &operator=(const ModuleDependenciesCache &) = delete;
10021005

10031006
public:
1007+
/// Whether we have cached dependency information for the given module.
1008+
bool hasDependency(const ModuleDependencyID &moduleID) const;
10041009
/// Whether we have cached dependency information for the given module.
10051010
bool hasDependency(StringRef moduleName,
10061011
llvm::Optional<ModuleDependencyKind> kind) const;
1012+
/// Whether we have cached dependency information for the given module Name.
1013+
bool hasDependency(StringRef moduleName) const;
10071014

10081015
SwiftDependencyScanningService &getScanService() {
10091016
return globalScanningService;
10101017
}
1018+
const SwiftDependencyScanningService &getScanService() const {
1019+
return globalScanningService;
1020+
}
10111021
const llvm::DenseSet<clang::tooling::dependencies::ModuleID>& getAlreadySeenClangModules() const {
10121022
return alreadySeenClangModules;
10131023
}
@@ -1035,6 +1045,12 @@ class ModuleDependenciesCache {
10351045
findDependency(StringRef moduleName,
10361046
llvm::Optional<ModuleDependencyKind> kind) const;
10371047

1048+
/// Look for module dependencies for a module with the given name
1049+
///
1050+
/// \returns the cached result, or \c None if there is no cached entry.
1051+
llvm::Optional<const ModuleDependencyInfo *>
1052+
findDependency(StringRef moduleName) const;
1053+
10381054
/// Record dependencies for the given module.
10391055
void recordDependency(StringRef moduleName,
10401056
ModuleDependencyInfo dependencies);

include/swift/DependencyScan/ScanDependencies.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,6 @@ llvm::ErrorOr<swiftscan_dependency_graph_t>
6161
performModuleScan(CompilerInstance &instance,
6262
ModuleDependenciesCache &cache);
6363

64-
llvm::ErrorOr<swiftscan_dependency_graph_t>
65-
performParallelModuleScan(CompilerInstance &instance,
66-
ModuleDependenciesCache &cache);
67-
6864
/// Scans the main module of \c instance for all direct module imports
6965
llvm::ErrorOr<swiftscan_import_set_t>
7066
performModulePrescan(CompilerInstance &instance,

include/swift/Frontend/FrontendOptions.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,8 @@ class FrontendOptions {
366366
/// Emit remarks indicating use of the serialized module dependency scanning cache.
367367
bool EmitDependencyScannerCacheRemarks = false;
368368

369-
/// Whether the dependency scanner invocation should use multiple threads.
369+
/// Whether the dependency scanner invocation should resolve imports
370+
/// to filesystem modules in parallel.
370371
bool ParallelDependencyScan = false;
371372

372373
/// When performing an incremental build, ensure that cross-module incremental

include/swift/Serialization/ModuleDependencyScanner.h

Lines changed: 73 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,53 +15,49 @@
1515
#include "swift/AST/ModuleDependencies.h"
1616
#include "swift/Frontend/ModuleInterfaceLoader.h"
1717
#include "swift/Serialization/SerializedModuleLoader.h"
18+
#include "llvm/Support/ThreadPool.h"
1819

1920
namespace swift {
2021
class DependencyTracker;
2122
}
2223

2324
namespace swift {
2425

26+
/// A dependency scanning worker which performs filesystem lookup
27+
/// of a named module dependency.
2528
class ModuleDependencyScanningWorker {
26-
private:
27-
ModuleDependencyScanningWorker(SwiftDependencyScanningService &globalScanningService,
28-
const CompilerInvocation &ScanCompilerInvocation,
29-
const SILOptions &SILOptions,
30-
ASTContext &ScanASTContext,
31-
DependencyTracker &DependencyTracker,
32-
DiagnosticEngine &diags);
29+
public:
30+
ModuleDependencyScanningWorker(
31+
SwiftDependencyScanningService &globalScanningService,
32+
const CompilerInvocation &ScanCompilerInvocation,
33+
const SILOptions &SILOptions, ASTContext &ScanASTContext,
34+
DependencyTracker &DependencyTracker, DiagnosticEngine &diags);
3335

36+
private:
3437
/// Retrieve the module dependencies for the module with the given name.
35-
llvm::Optional<const ModuleDependencyInfo *> scanForModuleDependency(
36-
StringRef moduleName, ModuleDependenciesCache &cache,
37-
bool optionalDependencyLookup = false, bool isTestableImport = false,
38-
llvm::Optional<ModuleDependencyID> dependencyOf = llvm::None);
38+
ModuleDependencyVector
39+
scanFilesystemForModuleDependency(StringRef moduleName,
40+
const ModuleDependenciesCache &cache,
41+
bool isTestableImport = false);
3942

4043
/// Retrieve the module dependencies for the Clang module with the given name.
41-
llvm::Optional<const ModuleDependencyInfo *>
42-
scanForClangModuleDependency(StringRef moduleName,
43-
ModuleDependenciesCache &cache);
44+
ModuleDependencyVector
45+
scanFilesystemForClangModuleDependency(StringRef moduleName,
46+
const ModuleDependenciesCache &cache);
4447

4548
/// Retrieve the module dependencies for the Swift module with the given name.
46-
llvm::Optional<const ModuleDependencyInfo *>
47-
scanForSwiftModuleDependency(StringRef moduleName,
48-
ModuleDependenciesCache &cache);
49-
49+
ModuleDependencyVector
50+
scanFilesystemForSwiftModuleDependency(StringRef moduleName,
51+
const ModuleDependenciesCache &cache);
5052

51-
private:
52-
DiagnosticEngine &Diagnostics;
53-
54-
// An AST delegate for interface scanning
53+
// An AST delegate for interface scanning.
5554
std::unique_ptr<InterfaceSubContextDelegateImpl> ScanningASTDelegate;
56-
// Clang scanner tool
55+
// The Clang scanner tool used by this worker.
5756
clang::tooling::dependencies::DependencyScanningTool clangScanningTool;
58-
59-
// Swift and Clang module loaders acting as the corresponding
60-
// scanners.
57+
// Swift and Clang module loaders acting as scanners.
6158
std::unique_ptr<ModuleInterfaceLoader> swiftScannerModuleLoader;
6259
std::unique_ptr<ClangImporter> clangScannerModuleLoader;
63-
64-
// Only the parent scanner can use this
60+
// Restrict access to the parent scanner class.
6561
friend class ModuleDependencyScanner;
6662
};
6763

@@ -72,33 +68,59 @@ class ModuleDependencyScanner {
7268
const SILOptions &SILOptions,
7369
ASTContext &ScanASTContext,
7470
DependencyTracker &DependencyTracker,
75-
DiagnosticEngine &diags);
71+
DiagnosticEngine &diags, bool ParallelScan);
7672

7773
/// Identify the scanner invocation's main module's dependencies
7874
llvm::ErrorOr<ModuleDependencyInfo> getMainModuleDependencyInfo(
7975
ModuleDecl *mainModule,
8076
llvm::Optional<SwiftDependencyTracker> tracker = llvm::None);
8177

82-
/// Resolve module dependencies of the given module, direct and transitive.
78+
/// Resolve module dependencies of the given module, computing a full
79+
/// transitive closure dependency graph.
8380
std::vector<ModuleDependencyID>
84-
resolveDependencies(ModuleDependencyID moduleID,
85-
ModuleDependenciesCache &cache);
81+
getModuleDependencies(ModuleDependencyID moduleID,
82+
ModuleDependenciesCache &cache);
8683

87-
/// Retrieve the module dependencies for the Clang module with the given name.
84+
/// Query the module dependency info for the Clang module with the given name.
85+
/// Explicit by-name lookups are useful for batch mode scanning.
8886
llvm::Optional<const ModuleDependencyInfo *>
89-
scanForClangModuleDependency(StringRef moduleName,
90-
ModuleDependenciesCache &cache);
87+
getNamedClangModuleDependencyInfo(StringRef moduleName,
88+
ModuleDependenciesCache &cache);
9189

92-
/// Retrieve the module dependencies for the Swift module with the given name.
90+
/// Query the module dependency info for the Swift module with the given name.
91+
/// Explicit by-name lookups are useful for batch mode scanning.
9392
llvm::Optional<const ModuleDependencyInfo *>
94-
scanForSwiftModuleDependency(StringRef moduleName,
95-
ModuleDependenciesCache &cache);
93+
getNamedSwiftModuleDependencyInfo(StringRef moduleName,
94+
ModuleDependenciesCache &cache);
9695

9796
private:
9897
/// Resolve the direct dependencies of the given module.
9998
std::vector<ModuleDependencyID>
100-
resolveModuleImports(ModuleDependencyID moduleID,
101-
ModuleDependenciesCache &cache);
99+
resolveDirectModuleDependencies(ModuleDependencyID moduleID,
100+
ModuleDependenciesCache &cache);
101+
102+
/// Resolve imported module names of a given module to concrete
103+
/// modules. If `ParallelScan` is enabled, this operation is multithreaded.
104+
void
105+
resolveImportDependencies(const ModuleDependencyID &moduleID,
106+
ModuleDependenciesCache &cache,
107+
ModuleDependencyIDSetVector &directDependencies);
108+
109+
/// If a module has a bridging header, execute a dependency scan
110+
/// on it and record the dependencies.
111+
void resolveBridgingHeaderDependencies(
112+
const ModuleDependencyID &moduleID, ModuleDependenciesCache &cache,
113+
std::vector<std::string> &allClangModules,
114+
llvm::StringSet<> &alreadyKnownModules,
115+
ModuleDependencyIDSetVector &directDependencies);
116+
117+
/// Resolve all module dependencies comprised of Swift overlays
118+
/// of this module's Clang module dependencies.
119+
void resolveSwiftOverlayDependencies(
120+
const ModuleDependencyID &moduleID,
121+
const std::vector<std::string> &clangDependencies,
122+
ModuleDependenciesCache &cache,
123+
ModuleDependencyIDSetVector &swiftOverlayDependencies);
102124

103125
/// Identify all cross-import overlay modules of the specified
104126
/// dependency set and apply an action for each.
@@ -107,13 +129,22 @@ class ModuleDependencyScanner {
107129
ModuleDependenciesCache &cache,
108130
llvm::function_ref<void(ModuleDependencyID)> action);
109131

132+
/// Perform an operation utilizing one of the Scanning workers
133+
/// available to this scanner.
134+
template <typename Function, typename... Args>
135+
auto withDependencyScanningWorker(Function &&F, Args &&...ArgList);
136+
110137
private:
111138
const CompilerInvocation &ScanCompilerInvocation;
112139
ASTContext &ScanASTContext;
113140
DiagnosticEngine &Diagnostics;
114141

115-
// For now, the sole worker doing the scanning
116-
ModuleDependencyScanningWorker ScanningWorker;
142+
/// The available pool of workers for filesystem module search
143+
unsigned NumThreads;
144+
std::list<std::unique_ptr<ModuleDependencyScanningWorker>> Workers;
145+
llvm::ThreadPool ScanningThreadPool;
146+
/// Protect worker access.
147+
std::mutex WorkersLock;
117148
};
118149

119150
/// A module "loader" that looks for .swiftinterface and .swiftmodule files

lib/AST/ModuleDependencies.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ bool ModuleDependencyInfo::isSwiftModule() const {
3232
isSwiftBinaryModule() || isSwiftPlaceholderModule();
3333
}
3434

35+
bool ModuleDependencyInfo::isTextualSwiftModule() const {
36+
return isSwiftInterfaceModule() || isSwiftSourceModule();
37+
}
38+
3539
ModuleDependencyKind &operator++(ModuleDependencyKind &e) {
3640
if (e == ModuleDependencyKind::LastKind) {
3741
llvm_unreachable(
@@ -656,11 +660,34 @@ ModuleDependenciesCache::findDependency(
656660
return optionalDep;
657661
}
658662

663+
llvm::Optional<const ModuleDependencyInfo *>
664+
ModuleDependenciesCache::findDependency(StringRef moduleName) const {
665+
for (auto kind = ModuleDependencyKind::FirstKind;
666+
kind != ModuleDependencyKind::LastKind; ++kind) {
667+
if (auto found = findDependency(moduleName, kind))
668+
return found;
669+
}
670+
return llvm::None;
671+
}
672+
673+
bool ModuleDependenciesCache::hasDependency(const ModuleDependencyID &moduleID) const {
674+
return hasDependency(moduleID.ModuleName, moduleID.Kind);
675+
}
676+
659677
bool ModuleDependenciesCache::hasDependency(
660678
StringRef moduleName, llvm::Optional<ModuleDependencyKind> kind) const {
661679
return findDependency(moduleName, kind).has_value();
662680
}
663681

682+
bool ModuleDependenciesCache::hasDependency(StringRef moduleName) const {
683+
for (auto kind = ModuleDependencyKind::FirstKind;
684+
kind != ModuleDependencyKind::LastKind; ++kind) {
685+
if (findDependency(moduleName, kind).has_value())
686+
return true;
687+
}
688+
return false;
689+
}
690+
664691
void ModuleDependenciesCache::recordDependency(
665692
StringRef moduleName, ModuleDependencyInfo dependencies) {
666693
auto dependenciesKind = dependencies.getKind();
@@ -675,7 +702,8 @@ void ModuleDependenciesCache::recordDependency(
675702
void ModuleDependenciesCache::recordDependencies(
676703
ModuleDependencyVector moduleDependencies) {
677704
for (const auto &dep : moduleDependencies) {
678-
recordDependency(dep.first.ModuleName, dep.second);
705+
if (!hasDependency(dep.first))
706+
recordDependency(dep.first.ModuleName, dep.second);
679707
if (dep.second.getKind() == ModuleDependencyKind::Clang) {
680708
auto clangModuleDetails = dep.second.getAsClangModule();
681709
addSeenClangModule(clang::tooling::dependencies::ModuleID{

lib/AST/SearchPathOptions.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ ModuleSearchPathLookup::searchPathsContainingFile(
101101
llvm::SmallSet<std::pair<ModuleSearchPathKind, unsigned>, 4> ResultIds;
102102

103103
for (auto &Filename : Filenames) {
104-
for (auto &Entry : LookupTable[Filename]) {
104+
for (auto &Entry : LookupTable.lookup(Filename)) {
105105
if (ResultIds.insert(std::make_pair(Entry->getKind(), Entry->getIndex()))
106106
.second) {
107107
Result.push_back(Entry.get());

lib/DependencyScan/ScanDependencies.cpp

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -831,10 +831,8 @@ static void writeJSON(llvm::raw_ostream &out,
831831
/*trailingComma=*/false);
832832
} else if (swiftBinaryDeps) {
833833
out << "\"swiftPrebuiltExternal\": {\n";
834-
bool hasCompiledModulePath =
835-
swiftBinaryDeps->compiled_module_path.data &&
836-
get_C_string(swiftBinaryDeps->compiled_module_path)[0] != '\0';
837-
assert(hasCompiledModulePath &&
834+
assert(swiftBinaryDeps->compiled_module_path.data &&
835+
get_C_string(swiftBinaryDeps->compiled_module_path)[0] != '\0' &&
838836
"Expected .swiftmodule for a Binary Swift Module Dependency.");
839837

840838
writeJSONSingleField(out, "compiledModulePath",
@@ -1231,9 +1229,7 @@ static bool diagnoseCycle(CompilerInstance &instance,
12311229
while (!openSet.empty()) {
12321230
auto &lastOpen = openSet.back();
12331231
auto beforeSize = openSet.size();
1234-
1235-
auto optionalDepInfo = cache.findDependency(lastOpen);
1236-
assert(optionalDepInfo.has_value() &&
1232+
assert(cache.findDependency(lastOpen).has_value() &&
12371233
"Missing dependency info during cycle diagnosis.");
12381234

12391235
for (const auto &dep : cache.getAllDependencies(lastOpen)) {
@@ -1618,7 +1614,8 @@ swift::dependencies::performModuleScan(CompilerInstance &instance,
16181614
auto scanner = ModuleDependencyScanner(
16191615
cache.getScanService(), instance.getInvocation(),
16201616
instance.getSILOptions(), instance.getASTContext(),
1621-
*instance.getDependencyTracker(), instance.getDiags());
1617+
*instance.getDependencyTracker(), instance.getDiags(),
1618+
instance.getInvocation().getFrontendOptions().ParallelDependencyScan);
16221619

16231620
// Identify imports of the main module and add an entry for it
16241621
// to the dependency graph.
@@ -1638,7 +1635,7 @@ swift::dependencies::performModuleScan(CompilerInstance &instance,
16381635
cache.recordDependency(mainModuleName, std::move(*mainModuleDepInfo));
16391636

16401637
// Perform the full module scan starting at the main module.
1641-
auto allModules = scanner.resolveDependencies(mainModuleID, cache);
1638+
auto allModules = scanner.getModuleDependencies(mainModuleID, cache);
16421639

16431640
#ifndef NDEBUG
16441641
// Verify that all collected dependencies have had their
@@ -1669,7 +1666,8 @@ swift::dependencies::performModulePrescan(CompilerInstance &instance,
16691666
auto scanner = ModuleDependencyScanner(
16701667
cache.getScanService(), instance.getInvocation(),
16711668
instance.getSILOptions(), instance.getASTContext(),
1672-
*instance.getDependencyTracker(), instance.getDiags());
1669+
*instance.getDependencyTracker(), instance.getDiags(),
1670+
instance.getInvocation().getFrontendOptions().ParallelDependencyScan);
16731671
// Execute import prescan, and write JSON output to the output stream
16741672
auto mainDependencies =
16751673
scanner.getMainModuleDependencyInfo(instance.getMainModule());
@@ -1698,7 +1696,8 @@ swift::dependencies::performBatchModuleScan(
16981696
auto scanner = ModuleDependencyScanner(
16991697
cache.getScanService(), instance.getInvocation(),
17001698
instance.getSILOptions(), instance.getASTContext(),
1701-
*instance.getDependencyTracker(), instance.getDiags());
1699+
*instance.getDependencyTracker(), instance.getDiags(),
1700+
instance.getInvocation().getFrontendOptions().ParallelDependencyScan);
17021701

17031702
StringRef moduleName = entry.moduleName;
17041703
bool isClang = !entry.isSwift;
@@ -1707,9 +1706,9 @@ swift::dependencies::performBatchModuleScan(
17071706
// Loading the clang module using Clang importer.
17081707
// This action will populate the cache with the main module's
17091708
// dependencies.
1710-
rootDeps = scanner.scanForClangModuleDependency(moduleName, cache);
1709+
rootDeps = scanner.getNamedClangModuleDependencyInfo(moduleName, cache);
17111710
} else {
1712-
rootDeps = scanner.scanForSwiftModuleDependency(moduleName, cache);
1711+
rootDeps = scanner.getNamedSwiftModuleDependencyInfo(moduleName, cache);
17131712
}
17141713
if (!rootDeps.has_value()) {
17151714
// We cannot find the clang module, abort.
@@ -1722,7 +1721,7 @@ swift::dependencies::performBatchModuleScan(
17221721
ModuleDependencyID moduleID{
17231722
moduleName.str(), isClang ? ModuleDependencyKind::Clang
17241723
: ModuleDependencyKind::SwiftInterface};
1725-
auto allDependencies = scanner.resolveDependencies(moduleID, cache);
1724+
auto allDependencies = scanner.getModuleDependencies(moduleID, cache);
17261725
batchScanResult.push_back(
17271726
generateFullDependencyGraph(instance, cache, allDependencies));
17281727
});

lib/Frontend/Frontend.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include "llvm/Support/Path.h"
5454
#include "llvm/Support/Process.h"
5555
#include "llvm/Support/VirtualOutputBackends.h"
56+
#include "llvm/Support/ThreadPool.h"
5657
#include <llvm/ADT/StringExtras.h>
5758

5859
using namespace swift;

0 commit comments

Comments
 (0)