Skip to content

Commit 0be8357

Browse files
authored
Merge pull request #29950 from brentdax/less-crossover
Make cross-importing faster
2 parents c315bff + b543060 commit 0be8357

File tree

9 files changed

+115
-57
lines changed

9 files changed

+115
-57
lines changed

include/swift/AST/Module.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,18 @@ class ModuleDecl : public DeclContext, public TypeDecl {
310310
/// Add a file declaring a cross-import overlay.
311311
void addCrossImportOverlayFile(StringRef file);
312312

313+
/// If this method returns \c false, the module does not declare any
314+
/// cross-import overlays.
315+
///
316+
/// This is a quick check you can use to bail out of expensive logic early;
317+
/// however, a \c true return doesn't guarantee that the module declares
318+
/// cross-import overlays--it only means that it \em might declare some.
319+
///
320+
/// (Specifically, this method checks if the module loader found any
321+
/// swiftoverlay files, but does not load the files to see if they list any
322+
/// overlay modules.)
323+
bool mightDeclareCrossImportOverlays() const;
324+
313325
/// Append to \p overlayNames the names of all modules that this module
314326
/// declares should be imported when \p bystanderName is imported.
315327
///

lib/AST/Module.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,6 +1564,10 @@ void ModuleDecl::addCrossImportOverlayFile(StringRef file) {
15641564
.push_back(new (ctx) OverlayFile(ctx.AllocateCopy(file)));
15651565
}
15661566

1567+
bool ModuleDecl::mightDeclareCrossImportOverlays() const {
1568+
return !declaredCrossImports.empty();
1569+
}
1570+
15671571
void ModuleDecl::
15681572
findDeclaredCrossImportOverlays(Identifier bystanderName,
15691573
SmallVectorImpl<Identifier> &overlayNames,

lib/Sema/NameBinding.cpp

Lines changed: 80 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,13 @@ namespace {
184184
/// often enormous.
185185
SmallSetVector<ImportedModuleDesc, 64> crossImportableModules;
186186

187+
/// The subset of \c crossImportableModules which may declare cross-imports.
188+
///
189+
/// This is a performance optimization. Since most modules do not register
190+
/// any cross-imports, we can usually compare against this list, which is
191+
/// much, much smaller than \c crossImportableModules.
192+
SmallVector<ImportedModuleDesc, 16> crossImportDeclaringModules;
193+
187194
/// The index of the next module in \c visibleModules that should be
188195
/// cross-imported.
189196
size_t nextModuleToCrossImport = 0;
@@ -231,13 +238,21 @@ namespace {
231238
/// then adds them to \c unboundImports using source locations from \p I.
232239
void crossImport(ModuleDecl *M, UnboundImport &I);
233240

241+
/// Discovers any cross-imports between \p newImport and
242+
/// \p oldImports and adds them to \c unboundImports, using source
243+
/// locations from \p I.
244+
void findCrossImportsInLists(UnboundImport &I,
245+
ArrayRef<ImportedModuleDesc> declaring,
246+
ArrayRef<ImportedModuleDesc> bystanding,
247+
bool shouldDiagnoseRedundantCrossImports);
248+
234249
/// Discovers any cross-imports between \p declaringImport and
235250
/// \p bystandingImport and adds them to \c unboundImports, using source
236251
/// locations from \p I.
237252
void findCrossImports(UnboundImport &I,
238253
const ImportedModuleDesc &declaringImport,
239254
const ImportedModuleDesc &bystandingImport,
240-
SmallVectorImpl<Identifier> &overlayNames);
255+
bool shouldDiagnoseRedundantCrossImports);
241256

242257
/// Load a module referenced by an import statement.
243258
///
@@ -853,55 +868,72 @@ void NameBinder::crossImport(ModuleDecl *M, UnboundImport &I) {
853868
// FIXME: Should we warn if M doesn't reexport underlyingModule?
854869
SF.addSeparatelyImportedOverlay(M, I.getUnderlyingModule().get());
855870

856-
// FIXME: Most of the comparisons we do here are probably unnecessary. We
857-
// only need to findCrossImports() on pairs where at least one of the two
858-
// modules declares cross-imports, and most modules don't. This is low-hanging
859-
// performance fruit.
860-
861871
auto newImports = crossImportableModules.getArrayRef()
862-
.slice(nextModuleToCrossImport);
872+
.drop_front(nextModuleToCrossImport);
873+
874+
if (newImports.empty())
875+
// Nothing to do except crash when we read past the end of
876+
// crossImportableModules in that assert at the bottom.
877+
return;
878+
863879
for (auto &newImport : newImports) {
864880
if (!canCrossImport(newImport))
865881
continue;
866882

867-
// Search imports up to, but not including or after, `newImport`.
883+
// First we check if any of the imports of modules that have declared
884+
// cross-imports have declared one with this module.
885+
findCrossImportsInLists(I, crossImportDeclaringModules, {newImport},
886+
/*shouldDiagnoseRedundantCrossImports=*/false);
887+
888+
// If this module doesn't declare any cross-imports, we're done with this
889+
// import.
890+
if (!newImport.module.second->mightDeclareCrossImportOverlays())
891+
continue;
892+
893+
// Fine, we need to do the slow-but-rare thing: check if this import
894+
// declares a cross-import with any previous one.
868895
auto oldImports =
869-
make_range(crossImportableModules.getArrayRef().data(), &newImport);
870-
for (auto &oldImport : oldImports) {
871-
if (!canCrossImport(oldImport))
896+
// Slice from the start of crossImportableModules up to newImport.
897+
llvm::makeArrayRef(crossImportableModules.getArrayRef().data(),
898+
&newImport);
899+
findCrossImportsInLists(I, {newImport}, oldImports,
900+
/*shouldDiagnoseRedundantCrossImports=*/true);
901+
902+
// Add this to the list of imports everyone needs to check against.
903+
crossImportDeclaringModules.push_back(newImport);
904+
}
905+
906+
// Catch potential memory smashers
907+
assert(newImports.data() ==
908+
&crossImportableModules[nextModuleToCrossImport] &&
909+
"findCrossImports() should never mutate visibleModules");
910+
911+
nextModuleToCrossImport = crossImportableModules.size();
912+
}
913+
914+
void
915+
NameBinder::findCrossImportsInLists(UnboundImport &I,
916+
ArrayRef<ImportedModuleDesc> declaring,
917+
ArrayRef<ImportedModuleDesc> bystanding,
918+
bool shouldDiagnoseRedundantCrossImports) {
919+
for (auto &declaringImport : declaring) {
920+
if (!canCrossImport(declaringImport))
921+
continue;
922+
923+
for (auto &bystandingImport : bystanding) {
924+
if (!canCrossImport(bystandingImport))
872925
continue;
873926

874-
SmallVector<Identifier, 2> newImportOverlays;
875-
findCrossImports(I, newImport, oldImport, newImportOverlays);
876-
877-
SmallVector<Identifier, 2> oldImportOverlays;
878-
findCrossImports(I, oldImport, newImport, oldImportOverlays);
879-
880-
// If both sides of the cross-import declare some of the same overlays,
881-
// this will cause some strange name lookup behavior; let's warn about it.
882-
for (auto name : newImportOverlays) {
883-
if (llvm::is_contained(oldImportOverlays, name)) {
884-
ctx.Diags.diagnose(I.importLoc, diag::cross_imported_by_both_modules,
885-
newImport.module.second->getName(),
886-
oldImport.module.second->getName(), name);
887-
}
888-
}
889-
890-
// If findCrossImports() ever changed the visibleModules list, we'd see
891-
// memory smashers here.
892-
assert(newImports.data() ==
893-
&crossImportableModules[nextModuleToCrossImport] &&
894-
"findCrossImports() should never mutate visibleModules");
927+
findCrossImports(I, declaringImport, bystandingImport,
928+
shouldDiagnoseRedundantCrossImports);
895929
}
896930
}
897-
898-
nextModuleToCrossImport = crossImportableModules.size();
899931
}
900932

901933
void NameBinder::findCrossImports(UnboundImport &I,
902934
const ImportedModuleDesc &declaringImport,
903935
const ImportedModuleDesc &bystandingImport,
904-
SmallVectorImpl<Identifier> &names) {
936+
bool shouldDiagnoseRedundantCrossImports) {
905937
assert(&declaringImport != &bystandingImport);
906938

907939
LLVM_DEBUG(
@@ -913,9 +945,17 @@ void NameBinder::findCrossImports(UnboundImport &I,
913945
ctx.Stats->getFrontendCounters().NumCrossImportsChecked++;
914946

915947
// Find modules we need to import.
948+
SmallVector<Identifier, 4> names;
916949
declaringImport.module.second->findDeclaredCrossImportOverlays(
917950
bystandingImport.module.second->getName(), names, I.importLoc);
918951

952+
// If we're diagnosing cases where we cross-import in both directions, get the
953+
// inverse list. Otherwise, leave the list empty.
954+
SmallVector<Identifier, 4> oppositeNames;
955+
if (shouldDiagnoseRedundantCrossImports)
956+
bystandingImport.module.second->findDeclaredCrossImportOverlays(
957+
declaringImport.module.second->getName(), oppositeNames, I.importLoc);
958+
919959
if (ctx.Stats && !names.empty())
920960
ctx.Stats->getFrontendCounters().NumCrossImportsFound++;
921961

@@ -929,6 +969,11 @@ void NameBinder::findCrossImports(UnboundImport &I,
929969
unboundImports.emplace_back(declaringImport.module.second->getASTContext(),
930970
I, name, declaringImport, bystandingImport);
931971

972+
if (llvm::is_contained(oppositeNames, name))
973+
ctx.Diags.diagnose(I.importLoc, diag::cross_imported_by_both_modules,
974+
declaringImport.module.second->getName(),
975+
bystandingImport.module.second->getName(), name);
976+
932977
LLVM_DEBUG({
933978
auto &crossImportOptions = unboundImports.back().options;
934979
llvm::dbgs() << " ";
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
%YAML 1.2
2+
---
3+
version: 1
4+
modules:
5+
- name: _NeverImportedOverlay
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
#include "clang_library_submodule.h"
12
void fromClangLibrary();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void fromClangLibrarySubmodule();
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
module ClangLibrary { header "clang_library.h" }
1+
module ClangLibrary {
2+
header "clang_library.h"
3+
module Submodule {
4+
header "clang_library_submodule.h"
5+
}
6+
}

test/CrossImport/loading.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DTHIN_LIBRARY -emit-dependencies-path - | %FileCheck -check-prefixes=DEPS,DEPS-MOST -DMODULE=ThinLibrary -DTARGET=%module-target-triple %s
2424
// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DFAT_LIBRARY -emit-dependencies-path - | %FileCheck -check-prefixes=DEPS,DEPS-MOST -DMODULE=FatLibrary -DTARGET=%module-target-triple %s
2525
// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DCLANG_LIBRARY -emit-dependencies-path - | %FileCheck -check-prefixes=DEPS,DEPS-MOST -DMODULE=ClangLibrary -DTARGET=%module-target-triple %s
26+
// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DCLANG_LIBRARY_SUBMODULE -emit-dependencies-path - | %FileCheck -check-prefixes=DEPS,DEPS-MOST -DMODULE=ClangLibrary -DTARGET=%module-target-triple %s
2627
// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DSWIFT_FRAMEWORK -emit-dependencies-path - | %FileCheck -check-prefixes=DEPS,DEPS-MOST -DMODULE=SwiftFramework -DTARGET=%module-target-triple %s
2728
// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DCLANG_FRAMEWORK -emit-dependencies-path - | %FileCheck -check-prefixes=DEPS,DEPS-MOST -DMODULE=ClangFramework -DTARGET=%module-target-triple %s
2829
// RUN: %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DOVERLAID_CLANG_FRAMEWORK -emit-dependencies-path - | %FileCheck -check-prefix=DEPS -DMODULE=OverlaidClangFramework -DTARGET=%module-target-triple %s
@@ -35,6 +36,7 @@
3536
// RUN: not %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DDONT_ALWAYS_IMPORT -DTHIN_LIBRARY 2>/dev/null
3637
// RUN: not %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DDONT_ALWAYS_IMPORT -DFAT_LIBRARY 2>/dev/null
3738
// RUN: not %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DDONT_ALWAYS_IMPORT -DCLANG_LIBRARY 2>/dev/null
39+
// RUN: not %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DDONT_ALWAYS_IMPORT -DCLANG_LIBRARY_SUBMODULE 2>/dev/null
3840
// RUN: not %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DDONT_ALWAYS_IMPORT -DSWIFT_FRAMEWORK 2>/dev/null
3941
// RUN: not %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DDONT_ALWAYS_IMPORT -DCLANG_FRAMEWORK 2>/dev/null
4042
// RUN: not %target-typecheck-verify-swift -enable-cross-import-overlays -I %t/include -I %t/lib/swift -F %t/Frameworks -DDONT_ALWAYS_IMPORT -DOVERLAID_CLANG_FRAMEWORK 2>/dev/null
@@ -108,6 +110,10 @@ import FatLibrary
108110
import ClangLibrary
109111
#endif
110112

113+
#if CLANG_LIBRARY_SUBMODULE
114+
import ClangLibrary.Submodule
115+
#endif
116+
111117
#if SWIFT_FRAMEWORK
112118
import SwiftFramework
113119
#endif

test/CrossImport/skip-submodules.swift

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)