Skip to content

Commit a756e21

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 e81ffbd commit a756e21

File tree

4 files changed

+193
-74
lines changed

4 files changed

+193
-74
lines changed

include/swift/DependencyScan/ModuleDependencyScanner.h

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -100,52 +100,47 @@ class ModuleDependencyScanner {
100100
/// closure for the given module.
101101
/// 1. Swift modules imported directly or via another Swift dependency
102102
/// 2. Clang modules imported directly or via a Swift dependency
103-
/// 3. Clang modules imported via textual header inputs to Swift modules (bridging headers)
104-
/// 4. Swift overlay modules of all of the transitively imported Clang modules that have one
103+
/// 3. Clang modules imported via textual header inputs to Swift modules
104+
/// (bridging headers)
105+
/// 4. Swift overlay modules of all of the transitively imported Clang modules
106+
/// that have one
105107
ModuleDependencyIDSetVector
106108
resolveImportedModuleDependencies(const ModuleDependencyID &rootModuleID,
107109
ModuleDependenciesCache &cache);
108-
void
109-
resolveSwiftModuleDependencies(const ModuleDependencyID &rootModuleID,
110-
ModuleDependenciesCache &cache,
111-
ModuleDependencyIDSetVector &discoveredSwiftModules);
112-
void
113-
resolveAllClangModuleDependencies(ArrayRef<ModuleDependencyID> swiftModules,
114-
ModuleDependenciesCache &cache,
115-
ModuleDependencyIDSetVector &discoveredClangModules);
116-
void
117-
resolveHeaderDependencies(ArrayRef<ModuleDependencyID> swiftModules,
118-
ModuleDependenciesCache &cache,
119-
ModuleDependencyIDSetVector &discoveredHeaderDependencyClangModules);
120-
void
121-
resolveSwiftOverlayDependencies(ArrayRef<ModuleDependencyID> swiftModules,
122-
ModuleDependenciesCache &cache,
123-
ModuleDependencyIDSetVector &discoveredDependencies);
110+
void resolveSwiftModuleDependencies(
111+
const ModuleDependencyID &rootModuleID, ModuleDependenciesCache &cache,
112+
ModuleDependencyIDSetVector &discoveredSwiftModules);
113+
void resolveAllClangModuleDependencies(
114+
ArrayRef<ModuleDependencyID> swiftModules, ModuleDependenciesCache &cache,
115+
ModuleDependencyIDSetVector &discoveredClangModules);
116+
void resolveHeaderDependencies(
117+
ArrayRef<ModuleDependencyID> swiftModules, ModuleDependenciesCache &cache,
118+
ModuleDependencyIDSetVector &discoveredHeaderDependencyClangModules);
119+
void resolveSwiftOverlayDependencies(
120+
ArrayRef<ModuleDependencyID> swiftModules, ModuleDependenciesCache &cache,
121+
ModuleDependencyIDSetVector &discoveredDependencies);
124122

125123
/// Resolve all of a given module's imports to a Swift module, if one exists.
126-
void
127-
resolveSwiftImportsForModule(const ModuleDependencyID &moduleID,
128-
ModuleDependenciesCache &cache,
129-
ModuleDependencyIDSetVector &importedSwiftDependencies);
124+
void resolveSwiftImportsForModule(
125+
const ModuleDependencyID &moduleID, ModuleDependenciesCache &cache,
126+
ModuleDependencyIDSetVector &importedSwiftDependencies);
130127

131-
/// If a module has a bridging header or other header inputs, execute a dependency scan
132-
/// on it and record the dependencies.
128+
/// If a module has a bridging header or other header inputs, execute a
129+
/// dependency scan on it and record the dependencies.
133130
void resolveHeaderDependenciesForModule(
134131
const ModuleDependencyID &moduleID, ModuleDependenciesCache &cache,
135132
ModuleDependencyIDSetVector &headerClangModuleDependencies);
136133

137134
/// Resolve all module dependencies comprised of Swift overlays
138135
/// of this module's Clang module dependencies.
139136
void resolveSwiftOverlayDependenciesForModule(
140-
const ModuleDependencyID &moduleID,
141-
ModuleDependenciesCache &cache,
137+
const ModuleDependencyID &moduleID, ModuleDependenciesCache &cache,
142138
ModuleDependencyIDSetVector &swiftOverlayDependencies);
143139

144-
/// Identify all cross-import overlay modules of the specified
145-
/// dependency set and apply an action for each.
146-
void discoverCrossImportOverlayDependencies(
147-
StringRef mainModuleName, ArrayRef<ModuleDependencyID> allDependencies,
148-
ModuleDependenciesCache &cache,
140+
/// Identify all cross-import overlay module dependencies of the
141+
/// source module under scan and apply an action for each.
142+
void resolveCrossImportOverlayDependencies(
143+
StringRef mainModuleName, ModuleDependenciesCache &cache,
149144
llvm::function_ref<void(ModuleDependencyID)> action);
150145

151146
/// Perform an operation utilizing one of the Scanning workers

lib/DependencyScan/ModuleDependencyScanner.cpp

Lines changed: 136 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -532,42 +532,141 @@ ModuleDependencyScanner::getNamedSwiftModuleDependencyInfo(
532532
return cache.findDependency(moduleName);
533533
}
534534

535+
/// For the dependency set of the main module, discover all
536+
/// cross-import overlays and their corresponding '.swiftcrossimport'
537+
/// files. Cross-import overlay dependencies are required when
538+
/// the two constituent modules are imported *from the same source file*,
539+
/// directly or indirectly.
540+
///
541+
/// Given a complete module dependency graph in this stage of the scan,
542+
/// the algorithm for discovering cross-import overlays is:
543+
/// 1. For each source file of the module under scan construct a
544+
/// set of module dependnecies only reachable from this source file.
545+
/// 2. For each module set constructed in (1), perform pair-wise lookup
546+
/// of cross import files for each pair of modules in the set.
547+
///
548+
/// Notably, if for some pair of modules 'A' and 'B' there exists
549+
/// a cross-import overlay '_A_B', and these two modules are not reachable
550+
/// from any single source file via direct or indirect imports, then
551+
/// the cross-import overlay module is not required for compilation.
535552
static void discoverCrossImportOverlayFiles(
536-
StringRef mainModuleName, ArrayRef<ModuleDependencyID> allDependencies,
537-
ModuleDependenciesCache &cache, ASTContext &scanASTContext,
538-
llvm::SetVector<Identifier> &newOverlays,
553+
StringRef mainModuleName, ModuleDependenciesCache &cache,
554+
ASTContext &scanASTContext, llvm::SetVector<Identifier> &newOverlays,
539555
std::vector<std::pair<std::string, std::string>> &overlayFiles) {
540-
for (auto moduleID : allDependencies) {
541-
auto moduleName = moduleID.ModuleName;
542-
// Do not look for overlays of main module under scan
543-
if (moduleName == mainModuleName)
544-
continue;
556+
auto mainModuleInfo = cache.findKnownDependency(ModuleDependencyID{
557+
mainModuleName.str(), ModuleDependencyKind::SwiftSource});
558+
559+
llvm::StringMap<ModuleDependencyIDSet> perSourceFileDependencies;
560+
const ModuleDependencyIDSet directSwiftDepsSet{
561+
mainModuleInfo.getImportedSwiftDependencies().begin(),
562+
mainModuleInfo.getImportedSwiftDependencies().end()};
563+
const ModuleDependencyIDSet directClangDepsSet{
564+
mainModuleInfo.getImportedClangDependencies().begin(),
565+
mainModuleInfo.getImportedClangDependencies().end()};
566+
567+
// A utility to map an import identifier to one of the
568+
// known resolved module dependencies
569+
auto getModuleIDForImportIdentifier =
570+
[directSwiftDepsSet, directClangDepsSet](
571+
const std::string &importIdentifierStr) -> ModuleDependencyID {
572+
if (auto textualDepIt = directSwiftDepsSet.find(
573+
{importIdentifierStr, ModuleDependencyKind::SwiftInterface});
574+
textualDepIt != directSwiftDepsSet.end())
575+
return *textualDepIt;
576+
else if (auto binaryDepIt = directSwiftDepsSet.find(
577+
{importIdentifierStr, ModuleDependencyKind::SwiftBinary});
578+
binaryDepIt != directSwiftDepsSet.end())
579+
return *binaryDepIt;
580+
else if (auto clangDepIt = directClangDepsSet.find(
581+
{importIdentifierStr, ModuleDependencyKind::Clang});
582+
clangDepIt != directClangDepsSet.end())
583+
return *clangDepIt;
584+
llvm_unreachable(
585+
"Unresolved import during cross-import overlay resolution");
586+
};
545587

546-
auto dependencies = cache.findDependency(moduleName, moduleID.Kind).value();
547-
// Collect a map from secondary module name to cross-import overlay names.
548-
auto overlayMap = dependencies->collectCrossImportOverlayNames(
549-
scanASTContext, moduleName, overlayFiles);
550-
if (overlayMap.empty())
551-
continue;
552-
for (const auto &dependencyId : allDependencies) {
553-
auto moduleName = dependencyId.ModuleName;
554-
// Do not look for overlays of main module under scan
555-
if (moduleName == mainModuleName)
556-
continue;
557-
// check if any explicitly imported modules can serve as a
558-
// secondary module, and add the overlay names to the
559-
// dependencies list.
560-
for (auto overlayName : overlayMap[moduleName]) {
561-
if (overlayName.str() != mainModuleName &&
562-
std::find_if(allDependencies.begin(), allDependencies.end(),
563-
[&](ModuleDependencyID Id) {
564-
return moduleName == overlayName.str();
565-
}) == allDependencies.end()) {
566-
newOverlays.insert(overlayName);
588+
// Collect the set of directly-imported module dependencies
589+
// for each source file in the source module under scan.
590+
for (const auto &import : mainModuleInfo.getModuleImports()) {
591+
auto importResolvedModuleID =
592+
getModuleIDForImportIdentifier(import.importIdentifier);
593+
for (const auto &importLocation : import.importLocations)
594+
perSourceFileDependencies[importLocation.bufferIdentifier].insert(
595+
importResolvedModuleID);
596+
}
597+
598+
// For each source-file, build a set of module dependencies of the
599+
// module under scan corresponding to a sub-graph of modules only reachable
600+
// from this source file's direct imports.
601+
for (auto &keyValPair : perSourceFileDependencies) {
602+
const auto &bufferIdentifier = keyValPair.getKey();
603+
auto &directDependencyIDs = keyValPair.second;
604+
SmallVector<ModuleDependencyID, 8> worklist{directDependencyIDs.begin(),
605+
directDependencyIDs.end()};
606+
while (!worklist.empty()) {
607+
auto moduleID = worklist.pop_back_val();
608+
perSourceFileDependencies[bufferIdentifier].insert(moduleID);
609+
if (isSwiftDependencyKind(moduleID.Kind)) {
610+
for (const auto &directSwiftDepID :
611+
cache.getImportedSwiftDependencies(moduleID)) {
612+
if (perSourceFileDependencies[bufferIdentifier].count(directSwiftDepID))
613+
continue;
614+
worklist.push_back(directSwiftDepID);
567615
}
568616
}
617+
for (const auto &directClangDepID :
618+
cache.getImportedClangDependencies(moduleID)) {
619+
if (perSourceFileDependencies[bufferIdentifier].count(directClangDepID))
620+
continue;
621+
worklist.push_back(directClangDepID);
622+
}
569623
}
570624
}
625+
626+
// Within a provided set of module dependencies reachable via
627+
// direct imports from a given file, determine the available and required
628+
// cross-import overlays.
629+
auto discoverCrossImportOverlayFilesForModuleSet =
630+
[&mainModuleName, &cache, &scanASTContext, &newOverlays,
631+
&overlayFiles](const ModuleDependencyIDSet &inputDependencies) {
632+
for (auto moduleID : inputDependencies) {
633+
auto moduleName = moduleID.ModuleName;
634+
// Do not look for overlays of main module under scan
635+
if (moduleName == mainModuleName)
636+
continue;
637+
638+
auto dependencies =
639+
cache.findDependency(moduleName, moduleID.Kind).value();
640+
// Collect a map from secondary module name to cross-import overlay
641+
// names.
642+
auto overlayMap = dependencies->collectCrossImportOverlayNames(
643+
scanASTContext, moduleName, overlayFiles);
644+
if (overlayMap.empty())
645+
continue;
646+
for (const auto &dependencyId : inputDependencies) {
647+
auto moduleName = dependencyId.ModuleName;
648+
// Do not look for overlays of main module under scan
649+
if (moduleName == mainModuleName)
650+
continue;
651+
// check if any explicitly imported modules can serve as a
652+
// secondary module, and add the overlay names to the
653+
// dependencies list.
654+
for (auto overlayName : overlayMap[moduleName]) {
655+
if (overlayName.str() != mainModuleName &&
656+
std::find_if(inputDependencies.begin(),
657+
inputDependencies.end(),
658+
[&](ModuleDependencyID Id) {
659+
return moduleName == overlayName.str();
660+
}) == inputDependencies.end()) {
661+
newOverlays.insert(overlayName);
662+
}
663+
}
664+
}
665+
}
666+
};
667+
668+
for (const auto &keyValPair : perSourceFileDependencies)
669+
discoverCrossImportOverlayFilesForModuleSet(keyValPair.second);
571670
}
572671

573672
std::vector<ModuleDependencyID>
@@ -598,8 +697,8 @@ ModuleDependencyScanner::performDependencyScan(
598697
// binary Swift modules already encode their dependencies on cross-import overlays
599698
// with explicit imports.
600699
if (ScanCompilerInvocation.getLangOptions().EnableCrossImportOverlays)
601-
discoverCrossImportOverlayDependencies(
602-
rootModuleID.ModuleName, allModules.getArrayRef().slice(1), cache,
700+
resolveCrossImportOverlayDependencies(
701+
rootModuleID.ModuleName, cache,
603702
[&](ModuleDependencyID id) { allModules.insert(id); });
604703

605704
return allModules.takeVector();
@@ -1154,15 +1253,15 @@ void ModuleDependencyScanner::resolveSwiftOverlayDependenciesForModule(
11541253
cache.setSwiftOverlayDependencies(moduleID, swiftOverlayDependencies.getArrayRef());
11551254
}
11561255

1157-
void ModuleDependencyScanner::discoverCrossImportOverlayDependencies(
1158-
StringRef mainModuleName, ArrayRef<ModuleDependencyID> allDependencies,
1256+
void ModuleDependencyScanner::resolveCrossImportOverlayDependencies(
1257+
StringRef mainModuleName,
11591258
ModuleDependenciesCache &cache,
11601259
llvm::function_ref<void(ModuleDependencyID)> action) {
11611260
// Modules explicitly imported. Only these can be secondary module.
11621261
llvm::SetVector<Identifier> newOverlays;
11631262
std::vector<std::pair<std::string, std::string>> overlayFiles;
1164-
discoverCrossImportOverlayFiles(mainModuleName, allDependencies, cache,
1165-
ScanASTContext, newOverlays, overlayFiles);
1263+
discoverCrossImportOverlayFiles(mainModuleName, cache, ScanASTContext,
1264+
newOverlays, overlayFiles);
11661265

11671266
// No new cross-import overlays are found, return.
11681267
if (newOverlays.empty())
@@ -1184,11 +1283,10 @@ void ModuleDependencyScanner::discoverCrossImportOverlayDependencies(
11841283

11851284
// Record the dummy main module's direct dependencies. The dummy main module
11861285
// only directly depend on these newly discovered overlay modules.
1187-
if (cache.findDependency(dummyMainName, ModuleDependencyKind::SwiftSource)) {
1286+
if (cache.findDependency(dummyMainID))
11881287
cache.updateDependency(dummyMainID, dummyMainDependencies);
1189-
} else {
1288+
else
11901289
cache.recordDependency(dummyMainName, dummyMainDependencies);
1191-
}
11921290

11931291
ModuleDependencyIDSetVector allModules =
11941292
resolveImportedModuleDependencies(dummyMainID, cache);

test/CAS/cross_import.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@
3232
// RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps.json Test > %t/MyApp.cmd
3333
// RUN: %FileCheck %s --input-file=%t/MyApp.cmd --check-prefix CMD
3434
// CMD: -swift-module-cross-import
35-
// CMD-NEXT: B
36-
// CMD-NEXT: A.swiftoverlay
37-
// CMD-NEXT: -swift-module-cross-import
38-
// CMD-NEXT: C
39-
// CMD-NEXT: A.swiftoverlay
35+
// CMD-NEXT: [[CMI1:[B|C]]]
36+
// CMD-NEXT: [[CMI1]].swiftcrossimport{{/|\\}}A.swiftoverlay
37+
// CMD: -swift-module-cross-import
38+
// CMD-NEXT: [[CMI2:[B|C]]]
39+
// CMD-NEXT: [[CMI2]].swiftcrossimport{{/|\\}}A.swiftoverlay
4040

4141
// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule \
4242
// RUN: -emit-module-interface-path %t/Test.swiftinterface \
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)