Skip to content

Commit 14dd14d

Browse files
authored
Merge pull request #79550 from artemcm/EBM_CrossImportsPerSourceFile
[Dependency Scanning] Perform cross-import overlay resolution per source-file
2 parents 88daaf8 + 214fe59 commit 14dd14d

File tree

4 files changed

+190
-70
lines changed

4 files changed

+190
-70
lines changed

Diff for: include/swift/DependencyScan/ModuleDependencyScanner.h

+23-27
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.

Diff for: lib/DependencyScan/ModuleDependencyScanner.cpp

+136-38
Original file line numberDiff line numberDiff line change
@@ -561,42 +561,141 @@ 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+
llvm::StringMap<ModuleDependencyIDSet> perSourceFileDependencies;
589+
const ModuleDependencyIDSet directSwiftDepsSet{
590+
mainModuleInfo.getImportedSwiftDependencies().begin(),
591+
mainModuleInfo.getImportedSwiftDependencies().end()};
592+
const ModuleDependencyIDSet directClangDepsSet{
593+
mainModuleInfo.getImportedClangDependencies().begin(),
594+
mainModuleInfo.getImportedClangDependencies().end()};
595+
596+
// A utility to map an import identifier to one of the
597+
// known resolved module dependencies
598+
auto getModuleIDForImportIdentifier =
599+
[directSwiftDepsSet, directClangDepsSet](
600+
const std::string &importIdentifierStr) -> ModuleDependencyID {
601+
if (auto textualDepIt = directSwiftDepsSet.find(
602+
{importIdentifierStr, ModuleDependencyKind::SwiftInterface});
603+
textualDepIt != directSwiftDepsSet.end())
604+
return *textualDepIt;
605+
else if (auto binaryDepIt = directSwiftDepsSet.find(
606+
{importIdentifierStr, ModuleDependencyKind::SwiftBinary});
607+
binaryDepIt != directSwiftDepsSet.end())
608+
return *binaryDepIt;
609+
else if (auto clangDepIt = directClangDepsSet.find(
610+
{importIdentifierStr, ModuleDependencyKind::Clang});
611+
clangDepIt != directClangDepsSet.end())
612+
return *clangDepIt;
613+
llvm_unreachable(
614+
"Unresolved import during cross-import overlay resolution");
615+
};
574616

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);
617+
// Collect the set of directly-imported module dependencies
618+
// for each source file in the source module under scan.
619+
for (const auto &import : mainModuleInfo.getModuleImports()) {
620+
auto importResolvedModuleID =
621+
getModuleIDForImportIdentifier(import.importIdentifier);
622+
for (const auto &importLocation : import.importLocations)
623+
perSourceFileDependencies[importLocation.bufferIdentifier].insert(
624+
importResolvedModuleID);
625+
}
626+
627+
// For each source-file, build a set of module dependencies of the
628+
// module under scan corresponding to a sub-graph of modules only reachable
629+
// from this source file's direct imports.
630+
for (auto &keyValPair : perSourceFileDependencies) {
631+
const auto &bufferIdentifier = keyValPair.getKey();
632+
auto &directDependencyIDs = keyValPair.second;
633+
SmallVector<ModuleDependencyID, 8> worklist{directDependencyIDs.begin(),
634+
directDependencyIDs.end()};
635+
while (!worklist.empty()) {
636+
auto moduleID = worklist.pop_back_val();
637+
perSourceFileDependencies[bufferIdentifier].insert(moduleID);
638+
if (isSwiftDependencyKind(moduleID.Kind)) {
639+
for (const auto &directSwiftDepID :
640+
cache.getImportedSwiftDependencies(moduleID)) {
641+
if (perSourceFileDependencies[bufferIdentifier].count(directSwiftDepID))
642+
continue;
643+
worklist.push_back(directSwiftDepID);
596644
}
597645
}
646+
for (const auto &directClangDepID :
647+
cache.getImportedClangDependencies(moduleID)) {
648+
if (perSourceFileDependencies[bufferIdentifier].count(directClangDepID))
649+
continue;
650+
worklist.push_back(directClangDepID);
651+
}
598652
}
599653
}
654+
655+
// Within a provided set of module dependencies reachable via
656+
// direct imports from a given file, determine the available and required
657+
// cross-import overlays.
658+
auto discoverCrossImportOverlayFilesForModuleSet =
659+
[&mainModuleName, &cache, &scanASTContext, &newOverlays,
660+
&overlayFiles](const ModuleDependencyIDSet &inputDependencies) {
661+
for (auto moduleID : inputDependencies) {
662+
auto moduleName = moduleID.ModuleName;
663+
// Do not look for overlays of main module under scan
664+
if (moduleName == mainModuleName)
665+
continue;
666+
667+
auto dependencies =
668+
cache.findDependency(moduleName, moduleID.Kind).value();
669+
// Collect a map from secondary module name to cross-import overlay
670+
// names.
671+
auto overlayMap = dependencies->collectCrossImportOverlayNames(
672+
scanASTContext, moduleName, overlayFiles);
673+
if (overlayMap.empty())
674+
continue;
675+
for (const auto &dependencyId : inputDependencies) {
676+
auto moduleName = dependencyId.ModuleName;
677+
// Do not look for overlays of main module under scan
678+
if (moduleName == mainModuleName)
679+
continue;
680+
// check if any explicitly imported modules can serve as a
681+
// secondary module, and add the overlay names to the
682+
// dependencies list.
683+
for (auto overlayName : overlayMap[moduleName]) {
684+
if (overlayName.str() != mainModuleName &&
685+
std::find_if(inputDependencies.begin(),
686+
inputDependencies.end(),
687+
[&](ModuleDependencyID Id) {
688+
return moduleName == overlayName.str();
689+
}) == inputDependencies.end()) {
690+
newOverlays.insert(overlayName);
691+
}
692+
}
693+
}
694+
}
695+
};
696+
697+
for (const auto &keyValPair : perSourceFileDependencies)
698+
discoverCrossImportOverlayFilesForModuleSet(keyValPair.second);
600699
}
601700

602701
std::vector<ModuleDependencyID>
@@ -627,8 +726,8 @@ ModuleDependencyScanner::performDependencyScan(
627726
// binary Swift modules already encode their dependencies on cross-import overlays
628727
// with explicit imports.
629728
if (ScanCompilerInvocation.getLangOptions().EnableCrossImportOverlays)
630-
discoverCrossImportOverlayDependencies(
631-
rootModuleID.ModuleName, allModules.getArrayRef().slice(1), cache,
729+
resolveCrossImportOverlayDependencies(
730+
rootModuleID.ModuleName, cache,
632731
[&](ModuleDependencyID id) { allModules.insert(id); });
633732

634733
if (ScanCompilerInvocation.getSearchPathOptions().BridgingHeaderChaining) {
@@ -1230,15 +1329,15 @@ void ModuleDependencyScanner::resolveSwiftOverlayDependenciesForModule(
12301329
cache.setSwiftOverlayDependencies(moduleID, swiftOverlayDependencies.getArrayRef());
12311330
}
12321331

1233-
void ModuleDependencyScanner::discoverCrossImportOverlayDependencies(
1234-
StringRef mainModuleName, ArrayRef<ModuleDependencyID> allDependencies,
1332+
void ModuleDependencyScanner::resolveCrossImportOverlayDependencies(
1333+
StringRef mainModuleName,
12351334
ModuleDependenciesCache &cache,
12361335
llvm::function_ref<void(ModuleDependencyID)> action) {
12371336
// Modules explicitly imported. Only these can be secondary module.
12381337
llvm::SetVector<Identifier> newOverlays;
12391338
std::vector<std::pair<std::string, std::string>> overlayFiles;
1240-
discoverCrossImportOverlayFiles(mainModuleName, allDependencies, cache,
1241-
ScanASTContext, newOverlays, overlayFiles);
1339+
discoverCrossImportOverlayFiles(mainModuleName, cache, ScanASTContext,
1340+
newOverlays, overlayFiles);
12421341

12431342
// No new cross-import overlays are found, return.
12441343
if (newOverlays.empty())
@@ -1260,11 +1359,10 @@ void ModuleDependencyScanner::discoverCrossImportOverlayDependencies(
12601359

12611360
// Record the dummy main module's direct dependencies. The dummy main module
12621361
// only directly depend on these newly discovered overlay modules.
1263-
if (cache.findDependency(dummyMainName, ModuleDependencyKind::SwiftSource)) {
1362+
if (cache.findDependency(dummyMainID))
12641363
cache.updateDependency(dummyMainID, dummyMainDependencies);
1265-
} else {
1364+
else
12661365
cache.recordDependency(dummyMainName, dummyMainDependencies);
1267-
}
12681366

12691367
ModuleDependencyIDSetVector allModules =
12701368
resolveImportedModuleDependencies(dummyMainID, cache);

Diff for: test/CAS/cross_import.swift

+5-5
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 \
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)