Skip to content

Commit 0442ccd

Browse files
committed
[Dependency Scanning] Perform cross-import overlay resolution per source-file
Previous implementation took the entire transitive dependency set and cross-referenced all of its members to determine which ones introduce requried cross-import overlays. That implementation differed from the cross-import overlay loading logic during source compilation, where a corrsponding cross-import overlay module is only requested if the two constituent modules are reachable via direct 'import's from *the same source file*. Meaning the dependency scanner before this change would report cross-import overlay dependencies which never got loaded by the corresponding client source compile. This change implements a new implementation of cross-import overlay discovery which first computes sub-graphs of module dependencies directly reachable by 'import's for each source file of the module under scan and then performs pairwise cross-import overlay query per each such sub-graph. Resolves rdar://145157171
1 parent 63b2f58 commit 0442ccd

File tree

3 files changed

+192
-66
lines changed

3 files changed

+192
-66
lines changed

include/swift/DependencyScan/ModuleDependencyScanner.h

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -112,51 +112,47 @@ class ModuleDependencyScanner {
112112
/// closure for the given module.
113113
/// 1. Swift modules imported directly or via another Swift dependency
114114
/// 2. Clang modules imported directly or via a Swift dependency
115-
/// 3. Clang modules imported via textual header inputs to Swift modules (bridging headers)
116-
/// 4. Swift overlay modules of all of the transitively imported Clang modules that have one
115+
/// 3. Clang modules imported via textual header inputs to Swift modules
116+
/// (bridging headers)
117+
/// 4. Swift overlay modules of all of the transitively imported Clang modules
118+
/// that have one
117119
ModuleDependencyIDSetVector
118120
resolveImportedModuleDependencies(const ModuleDependencyID &rootModuleID,
119121
ModuleDependenciesCache &cache);
120-
void
121-
resolveSwiftModuleDependencies(const ModuleDependencyID &rootModuleID,
122-
ModuleDependenciesCache &cache,
123-
ModuleDependencyIDSetVector &discoveredSwiftModules);
124-
void
125-
resolveAllClangModuleDependencies(ArrayRef<ModuleDependencyID> swiftModules,
126-
ModuleDependenciesCache &cache,
127-
ModuleDependencyIDSetVector &discoveredClangModules);
122+
void resolveSwiftModuleDependencies(
123+
const ModuleDependencyID &rootModuleID, ModuleDependenciesCache &cache,
124+
ModuleDependencyIDSetVector &discoveredSwiftModules);
125+
void resolveAllClangModuleDependencies(
126+
ArrayRef<ModuleDependencyID> swiftModules, ModuleDependenciesCache &cache,
127+
ModuleDependencyIDSetVector &discoveredClangModules);
128128
void resolveHeaderDependencies(
129129
ArrayRef<ModuleDependencyID> swiftModules, ModuleDependenciesCache &cache,
130130
ModuleDependencyIDSetVector &discoveredHeaderDependencyClangModules);
131-
void
132-
resolveSwiftOverlayDependencies(ArrayRef<ModuleDependencyID> swiftModules,
133-
ModuleDependenciesCache &cache,
134-
ModuleDependencyIDSetVector &discoveredDependencies);
131+
void resolveSwiftOverlayDependencies(
132+
ArrayRef<ModuleDependencyID> swiftModules, ModuleDependenciesCache &cache,
133+
ModuleDependencyIDSetVector &discoveredDependencies);
135134

136135
/// Resolve all of a given module's imports to a Swift module, if one exists.
137-
void
138-
resolveSwiftImportsForModule(const ModuleDependencyID &moduleID,
139-
ModuleDependenciesCache &cache,
140-
ModuleDependencyIDSetVector &importedSwiftDependencies);
136+
void resolveSwiftImportsForModule(
137+
const ModuleDependencyID &moduleID, ModuleDependenciesCache &cache,
138+
ModuleDependencyIDSetVector &importedSwiftDependencies);
141139

142-
/// If a module has a bridging header or other header inputs, execute a dependency scan
143-
/// on it and record the dependencies.
140+
/// If a module has a bridging header or other header inputs, execute a
141+
/// dependency scan on it and record the dependencies.
144142
void resolveHeaderDependenciesForModule(
145143
const ModuleDependencyID &moduleID, ModuleDependenciesCache &cache,
146144
ModuleDependencyIDSetVector &headerClangModuleDependencies);
147145

148146
/// Resolve all module dependencies comprised of Swift overlays
149147
/// of this module's Clang module dependencies.
150148
void resolveSwiftOverlayDependenciesForModule(
151-
const ModuleDependencyID &moduleID,
152-
ModuleDependenciesCache &cache,
149+
const ModuleDependencyID &moduleID, ModuleDependenciesCache &cache,
153150
ModuleDependencyIDSetVector &swiftOverlayDependencies);
154151

155-
/// Identify all cross-import overlay modules of the specified
156-
/// dependency set and apply an action for each.
157-
void discoverCrossImportOverlayDependencies(
158-
StringRef mainModuleName, ArrayRef<ModuleDependencyID> allDependencies,
159-
ModuleDependenciesCache &cache,
152+
/// Identify all cross-import overlay module dependencies of the
153+
/// source module under scan and apply an action for each.
154+
void resolveCrossImportOverlayDependencies(
155+
StringRef mainModuleName, ModuleDependenciesCache &cache,
160156
llvm::function_ref<void(ModuleDependencyID)> action);
161157

162158
/// Performance BridgingHeader Chaining.

lib/DependencyScan/ModuleDependencyScanner.cpp

Lines changed: 143 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -561,42 +561,147 @@ ModuleDependencyScanner::getNamedSwiftModuleDependencyInfo(
561561
return cache.findDependency(moduleName);
562562
}
563563

564+
/// For the dependency set of the main module, discover all
565+
/// cross-import overlays and their corresponding '.swiftcrossimport'
566+
/// files. Cross-import overlay dependencies are required when
567+
/// the two constituent modules are imported *from the same source file*,
568+
/// directly or indirectly.
569+
///
570+
/// Given a complete module dependency graph in this stage of the scan,
571+
/// the algorithm for discovering cross-import overlays is:
572+
/// 1. For each source file of the module under scan construct a
573+
/// set of module dependnecies only reachable from this source file.
574+
/// 2. For each module set constructed in (1), perform pair-wise lookup
575+
/// of cross import files for each pair of modules in the set.
576+
///
577+
/// Notably, if for some pair of modules 'A' and 'B' there exists
578+
/// a cross-import overlay '_A_B', and these two modules are not reachable
579+
/// from any single source file via direct or indirect imports, then
580+
/// the cross-import overlay module is not required for compilation.
564581
static void discoverCrossImportOverlayFiles(
565-
StringRef mainModuleName, ArrayRef<ModuleDependencyID> allDependencies,
566-
ModuleDependenciesCache &cache, ASTContext &scanASTContext,
567-
llvm::SetVector<Identifier> &newOverlays,
582+
StringRef mainModuleName, ModuleDependenciesCache &cache,
583+
ASTContext &scanASTContext, llvm::SetVector<Identifier> &newOverlays,
568584
std::vector<std::pair<std::string, std::string>> &overlayFiles) {
569-
for (auto moduleID : allDependencies) {
570-
auto moduleName = moduleID.ModuleName;
571-
// Do not look for overlays of main module under scan
572-
if (moduleName == mainModuleName)
573-
continue;
585+
auto mainModuleInfo = cache.findKnownDependency(ModuleDependencyID{
586+
mainModuleName.str(), ModuleDependencyKind::SwiftSource});
587+
588+
std::unordered_map<std::string, ModuleDependencyIDSetVector>
589+
perSourceFileDependencies;
590+
const ModuleDependencyIDSet directSwiftDepsSet{
591+
mainModuleInfo.getImportedSwiftDependencies().begin(),
592+
mainModuleInfo.getImportedSwiftDependencies().end()};
593+
const ModuleDependencyIDSet directClangDepsSet{
594+
mainModuleInfo.getImportedClangDependencies().begin(),
595+
mainModuleInfo.getImportedClangDependencies().end()};
596+
597+
// A utility to map an import identifier to one of the
598+
// known resolved module dependencies
599+
auto getModuleIDForImportIdentifier =
600+
[directSwiftDepsSet, directClangDepsSet](
601+
const std::string &importIdentifierStr) -> ModuleDependencyID {
602+
if (auto textualDepIt = directSwiftDepsSet.find(
603+
{importIdentifierStr, ModuleDependencyKind::SwiftInterface});
604+
textualDepIt != directSwiftDepsSet.end())
605+
return *textualDepIt;
606+
else if (auto binaryDepIt = directSwiftDepsSet.find(
607+
{importIdentifierStr, ModuleDependencyKind::SwiftBinary});
608+
binaryDepIt != directSwiftDepsSet.end())
609+
return *binaryDepIt;
610+
else if (auto clangDepIt = directClangDepsSet.find(
611+
{importIdentifierStr, ModuleDependencyKind::Clang});
612+
clangDepIt != directClangDepsSet.end())
613+
return *clangDepIt;
614+
llvm_unreachable(
615+
"Unresolved import during cross-import overlay resolution");
616+
};
574617

575-
auto dependencies = cache.findDependency(moduleName, moduleID.Kind).value();
576-
// Collect a map from secondary module name to cross-import overlay names.
577-
auto overlayMap = dependencies->collectCrossImportOverlayNames(
578-
scanASTContext, moduleName, overlayFiles);
579-
if (overlayMap.empty())
580-
continue;
581-
for (const auto &dependencyId : allDependencies) {
582-
auto moduleName = dependencyId.ModuleName;
583-
// Do not look for overlays of main module under scan
584-
if (moduleName == mainModuleName)
585-
continue;
586-
// check if any explicitly imported modules can serve as a
587-
// secondary module, and add the overlay names to the
588-
// dependencies list.
589-
for (auto overlayName : overlayMap[moduleName]) {
590-
if (overlayName.str() != mainModuleName &&
591-
std::find_if(allDependencies.begin(), allDependencies.end(),
592-
[&](ModuleDependencyID Id) {
593-
return moduleName == overlayName.str();
594-
}) == allDependencies.end()) {
595-
newOverlays.insert(overlayName);
618+
// Collect the set of directly-imported module dependencies
619+
// for each source file in the source module under scan.
620+
for (const auto &import : mainModuleInfo.getModuleImports()) {
621+
auto importResolvedModuleID =
622+
getModuleIDForImportIdentifier(import.importIdentifier);
623+
for (const auto &importLocation : import.importLocations) {
624+
if (perSourceFileDependencies.find(importLocation.bufferIdentifier) ==
625+
perSourceFileDependencies.end())
626+
perSourceFileDependencies.insert(
627+
{importLocation.bufferIdentifier, ModuleDependencyIDSetVector()});
628+
perSourceFileDependencies.at(importLocation.bufferIdentifier)
629+
.insert(importResolvedModuleID);
630+
}
631+
}
632+
633+
// For each source-file, build a set of module dependencies of the
634+
// module under scan corresponding to a sub-graph of modules only reachable
635+
// from this source file's direct imports.
636+
for (auto &[sourceBufferIdentifierStr, dependencyIDs] :
637+
perSourceFileDependencies) {
638+
SmallVector<ModuleDependencyID, 8> worklist{dependencyIDs.begin(),
639+
dependencyIDs.end()};
640+
while (!worklist.empty()) {
641+
auto moduleID = worklist.pop_back_val();
642+
dependencyIDs.insert(moduleID);
643+
if (isSwiftDependencyKind(moduleID.Kind)) {
644+
for (const auto &directSwiftDepID :
645+
cache.getImportedSwiftDependencies(moduleID)) {
646+
if (dependencyIDs.contains(directSwiftDepID))
647+
continue;
648+
worklist.push_back(directSwiftDepID);
596649
}
597650
}
651+
for (const auto &directClangDepID :
652+
cache.getImportedClangDependencies(moduleID)) {
653+
if (dependencyIDs.contains(directClangDepID))
654+
continue;
655+
worklist.push_back(directClangDepID);
656+
}
598657
}
599658
}
659+
660+
// Within a provided set of module dependencies reachable via
661+
// direct imports from a given file, determine the available and required
662+
// cross-import overlays.
663+
auto discoverCrossImportOverlayFilesForModuleSet =
664+
[&mainModuleName, &cache, &scanASTContext, &newOverlays,
665+
&overlayFiles](const ArrayRef<ModuleDependencyID> inputDependencies) {
666+
for (auto moduleID : inputDependencies) {
667+
auto moduleName = moduleID.ModuleName;
668+
// Do not look for overlays of main module under scan
669+
if (moduleName == mainModuleName)
670+
continue;
671+
672+
auto dependencies =
673+
cache.findDependency(moduleName, moduleID.Kind).value();
674+
// Collect a map from secondary module name to cross-import overlay
675+
// names.
676+
auto overlayMap = dependencies->collectCrossImportOverlayNames(
677+
scanASTContext, moduleName, overlayFiles);
678+
if (overlayMap.empty())
679+
continue;
680+
for (const auto &dependencyId : inputDependencies) {
681+
auto moduleName = dependencyId.ModuleName;
682+
// Do not look for overlays of main module under scan
683+
if (moduleName == mainModuleName)
684+
continue;
685+
// check if any explicitly imported modules can serve as a
686+
// secondary module, and add the overlay names to the
687+
// dependencies list.
688+
for (auto overlayName : overlayMap[moduleName]) {
689+
if (overlayName.str() != mainModuleName &&
690+
std::find_if(inputDependencies.begin(),
691+
inputDependencies.end(),
692+
[&](ModuleDependencyID Id) {
693+
return moduleName == overlayName.str();
694+
}) == inputDependencies.end()) {
695+
newOverlays.insert(overlayName);
696+
}
697+
}
698+
}
699+
}
700+
};
701+
702+
for (const auto &[sourceBufferIdentifierStr, dependencyIDs] :
703+
perSourceFileDependencies)
704+
discoverCrossImportOverlayFilesForModuleSet(dependencyIDs.getArrayRef());
600705
}
601706

602707
std::vector<ModuleDependencyID>
@@ -627,8 +732,8 @@ ModuleDependencyScanner::performDependencyScan(
627732
// binary Swift modules already encode their dependencies on cross-import overlays
628733
// with explicit imports.
629734
if (ScanCompilerInvocation.getLangOptions().EnableCrossImportOverlays)
630-
discoverCrossImportOverlayDependencies(
631-
rootModuleID.ModuleName, allModules.getArrayRef().slice(1), cache,
735+
resolveCrossImportOverlayDependencies(
736+
rootModuleID.ModuleName, cache,
632737
[&](ModuleDependencyID id) { allModules.insert(id); });
633738

634739
if (ScanCompilerInvocation.getSearchPathOptions().BridgingHeaderChaining) {
@@ -1230,17 +1335,17 @@ void ModuleDependencyScanner::resolveSwiftOverlayDependenciesForModule(
12301335
cache.setSwiftOverlayDependencies(moduleID, swiftOverlayDependencies.getArrayRef());
12311336
}
12321337

1233-
void ModuleDependencyScanner::discoverCrossImportOverlayDependencies(
1234-
StringRef mainModuleName, ArrayRef<ModuleDependencyID> allDependencies,
1338+
void ModuleDependencyScanner::resolveCrossImportOverlayDependencies(
1339+
StringRef mainModuleName,
12351340
ModuleDependenciesCache &cache,
12361341
llvm::function_ref<void(ModuleDependencyID)> action) {
12371342
// Modules explicitly imported. Only these can be secondary module.
12381343
llvm::SetVector<Identifier> newOverlays;
12391344
std::vector<std::pair<std::string, std::string>> overlayFiles;
1240-
discoverCrossImportOverlayFiles(mainModuleName, allDependencies, cache,
1241-
ScanASTContext, newOverlays, overlayFiles);
1345+
discoverCrossImportOverlayFiles(mainModuleName, cache, ScanASTContext,
1346+
newOverlays, overlayFiles);
12421347

1243-
// No new cross-import overlays are found, return.
1348+
// No new cross-import overlays are ffound, return.
12441349
if (newOverlays.empty())
12451350
return;
12461351

@@ -1260,11 +1365,10 @@ void ModuleDependencyScanner::discoverCrossImportOverlayDependencies(
12601365

12611366
// Record the dummy main module's direct dependencies. The dummy main module
12621367
// only directly depend on these newly discovered overlay modules.
1263-
if (cache.findDependency(dummyMainName, ModuleDependencyKind::SwiftSource)) {
1368+
if (cache.findDependency(dummyMainID))
12641369
cache.updateDependency(dummyMainID, dummyMainDependencies);
1265-
} else {
1370+
else
12661371
cache.recordDependency(dummyMainName, dummyMainDependencies);
1267-
}
12681372

12691373
ModuleDependencyIDSetVector allModules =
12701374
resolveImportedModuleDependencies(dummyMainID, cache);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %empty-directory(%t/module-cache)
3+
// RUN: split-file %s %t
4+
5+
// Run a dependency scan on a source file that imports both constituents of a cross-import overlay to ensure the cross-import overlay dependency is registered
6+
// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/module-cache %t/C.swift -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -I %S/Inputs/CHeaders/ExtraCModules -module-name FineGrainedCrossImportTestModule -enable-cross-import-overlays
7+
// Check the contents of the JSON output
8+
// RUN: %validate-json %t/deps.json | %FileCheck %s --check-prefix CHECK-TOGETHER
9+
10+
// Run a dependency scan on two source files that separately import constituents of a cross-import overlay and ensure that the cross-import overlay dependency is *not* registered
11+
// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/module-cache %t/A.swift %t/B.swift -o %t/deps_disjoint.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -I %S/Inputs/CHeaders/ExtraCModules -module-name FineGrainedCrossImportTestModule -enable-cross-import-overlays
12+
// Check the contents of the JSON output
13+
// RUN: %validate-json %t/deps_disjoint.json | %FileCheck %s --check-prefix CHECK-DISJOINT
14+
15+
//--- A.swift
16+
import E
17+
18+
//--- B.swift
19+
import SubE
20+
21+
//--- C.swift
22+
import E
23+
import SubE
24+
25+
// CHECK-TOGETHER: "swift": "_cross_import_E"
26+
// CHECK-DISJOINT-NOT: "swift": "_cross_import_E"

0 commit comments

Comments
 (0)