Skip to content

Commit 1fbbebe

Browse files
authored
Merge pull request swiftlang#79063 from swiftlang/elsh/rel/pcmo-imports
[6.1][Package CMO] Enable serializing decls imported with `@_spiOnly` or `package import`
2 parents 7ea1c69 + 4d7a7ca commit 1fbbebe

File tree

4 files changed

+119
-34
lines changed

4 files changed

+119
-34
lines changed

include/swift/AST/Module.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,11 +1016,6 @@ class ModuleDecl
10161016
/// This assumes that \p module was imported.
10171017
bool isImportedImplementationOnly(const ModuleDecl *module) const;
10181018

1019-
/// Returns true if decl context or its content can be serialized by
1020-
/// cross-module-optimization.
1021-
/// The \p ctxt can e.g. be a NominalType or the context of a function.
1022-
bool canBeUsedForCrossModuleOptimization(DeclContext *ctxt) const;
1023-
10241019
/// Finds all top-level decls of this module.
10251020
///
10261021
/// This does a simple local lookup, not recursively looking through imports.

lib/AST/Module.cpp

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3010,33 +3010,6 @@ bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const {
30103010
return true;
30113011
}
30123012

3013-
bool ModuleDecl::
3014-
canBeUsedForCrossModuleOptimization(DeclContext *ctxt) const {
3015-
ModuleDecl *moduleOfCtxt = ctxt->getParentModule();
3016-
3017-
// If the context defined in the same module - or is the same module, it's
3018-
// fine.
3019-
if (moduleOfCtxt == this)
3020-
return true;
3021-
3022-
// See if context is imported in a "regular" way, i.e. not with
3023-
// @_implementationOnly, `package import` or @_spiOnly.
3024-
ModuleDecl::ImportFilter filter = {
3025-
ModuleDecl::ImportFilterKind::ImplementationOnly,
3026-
ModuleDecl::ImportFilterKind::PackageOnly,
3027-
ModuleDecl::ImportFilterKind::SPIOnly
3028-
};
3029-
SmallVector<ImportedModule, 4> results;
3030-
getImportedModules(results, filter);
3031-
3032-
auto &imports = getASTContext().getImportCache();
3033-
for (auto &desc : results) {
3034-
if (imports.isImportedBy(moduleOfCtxt, desc.importedModule))
3035-
return false;
3036-
}
3037-
return true;
3038-
}
3039-
30403013
void SourceFile::lookupImportedSPIGroups(
30413014
const ModuleDecl *importedModule,
30423015
llvm::SmallSetVector<Identifier, 4> &spiGroups) const {

lib/SILOptimizer/IPO/CrossModuleOptimization.cpp

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#define DEBUG_TYPE "cross-module-serialization-setup"
1818
#include "swift/AST/Module.h"
19+
#include "swift/AST/ImportCache.h"
1920
#include "swift/Basic/Assertions.h"
2021
#include "swift/IRGen/TBDGen.h"
2122
#include "swift/SIL/ApplySite.h"
@@ -103,6 +104,11 @@ class CrossModuleOptimization {
103104
bool canSerializeType(CanType type);
104105
bool canSerializeDecl(NominalTypeDecl *decl);
105106

107+
/// Check whether decls imported with certain access levels or attributes
108+
/// can be serialized.
109+
/// The \p ctxt can e.g. be a NominalType or the context of a function.
110+
bool checkImports(DeclContext *ctxt) const;
111+
106112
bool canUseFromInline(DeclContext *declCtxt);
107113

108114
bool canUseFromInline(SILFunction *func);
@@ -736,7 +742,12 @@ static bool couldBeLinkedStatically(DeclContext *funcCtxt, SILModule &module) {
736742
// The stdlib module is always linked dynamically.
737743
if (funcModule == module.getASTContext().getStdlibModule())
738744
return false;
739-
745+
746+
// An sdk or system module should be linked dynamically.
747+
if (isPackageCMOEnabled(module.getSwiftModule()) &&
748+
funcModule->isNonUserModule())
749+
return false;
750+
740751
// Conservatively assume the function is in a statically linked module.
741752
return true;
742753
}
@@ -746,7 +757,7 @@ bool CrossModuleOptimization::canUseFromInline(DeclContext *declCtxt) {
746757
if (everything)
747758
return true;
748759

749-
if (!M.getSwiftModule()->canBeUsedForCrossModuleOptimization(declCtxt))
760+
if (!checkImports(declCtxt))
750761
return false;
751762

752763
/// If we are emitting a TBD file, the TBD file only contains public symbols
@@ -762,6 +773,52 @@ bool CrossModuleOptimization::canUseFromInline(DeclContext *declCtxt) {
762773
return true;
763774
}
764775

776+
bool CrossModuleOptimization::checkImports(DeclContext *ctxt) const {
777+
ModuleDecl *moduleOfCtxt = ctxt->getParentModule();
778+
779+
// If the context defined in the same module - or is the same module, it's
780+
// fine.
781+
if (moduleOfCtxt == M.getSwiftModule())
782+
return true;
783+
784+
ModuleDecl::ImportFilter filter;
785+
786+
if (isPackageCMOEnabled(M.getSwiftModule())) {
787+
// If Package CMO is enabled, decls imported with `package import`
788+
// or `@_spiOnly import` into this module should be allowed to be
789+
// serialized. They are used in decls with `package` or higher
790+
// access level, with or without @_spi; a client of this module
791+
// should be able to access them directly if in the same package.
792+
filter = { ModuleDecl::ImportFilterKind::ImplementationOnly };
793+
} else {
794+
// See if context is imported in a "regular" way, i.e. not with
795+
// @_implementationOnly, `package import` or @_spiOnly.
796+
filter = {
797+
ModuleDecl::ImportFilterKind::ImplementationOnly,
798+
ModuleDecl::ImportFilterKind::PackageOnly,
799+
ModuleDecl::ImportFilterKind::SPIOnly
800+
};
801+
}
802+
SmallVector<ImportedModule, 4> results;
803+
M.getSwiftModule()->getImportedModules(results, filter);
804+
805+
auto &imports = M.getSwiftModule()->getASTContext().getImportCache();
806+
for (auto &desc : results) {
807+
if (imports.isImportedBy(moduleOfCtxt, desc.importedModule)) {
808+
// E.g. `@_implementationOnly import QuartzCore_Private.CALayerPrivate`
809+
// imports `Foundation` as its transitive dependency module; use of a
810+
// a `public` decl in `Foundation` such as `IndexSet` in a function
811+
// signature should not block serialization in Package CMO given the
812+
// function has `package` or higher access level.
813+
if (isPackageCMOEnabled(M.getSwiftModule()) &&
814+
moduleOfCtxt->isNonUserModule())
815+
continue;
816+
return false;
817+
}
818+
}
819+
return true;
820+
}
821+
765822
/// Returns true if the function \p func can be used from a serialized function.
766823
bool CrossModuleOptimization::canUseFromInline(SILFunction *function) {
767824
if (everything)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// RUN: %target-swift-frontend %t/CoreA.swift \
5+
// RUN: -module-name=CoreA -package-name Pkg \
6+
// RUN: -parse-as-library -emit-module \
7+
// RUN: -emit-module-path %t/CoreA.swiftmodule -I%t \
8+
// RUN: -O -wmo -enable-library-evolution
9+
10+
// RUN: %target-swift-frontend %t/CoreB.swift \
11+
// RUN: -module-name=CoreB -package-name Pkg \
12+
// RUN: -parse-as-library -emit-module \
13+
// RUN: -emit-module-path %t/CoreB.swiftmodule -I%t \
14+
// RUN: -O -wmo -enable-library-evolution
15+
16+
// RUN: %target-swift-frontend %t/Lib.swift \
17+
// RUN: -module-name=Lib -package-name Pkg \
18+
// RUN: -parse-as-library -emit-module \
19+
// RUN: -experimental-spi-only-imports \
20+
// RUN: -emit-module-path %t/Lib.swiftmodule -I %t \
21+
// RUN: -experimental-package-cmo -experimental-allow-non-resilient-access \
22+
// RUN: -O -wmo -enable-library-evolution -Rmodule-loading 2> %t/Lib-result.txt
23+
// RUN: %target-sil-opt %t/Lib.swiftmodule -I %t -sil-verify-all -o %t/Lib.sil
24+
// RUN: %FileCheck %s < %t/Lib.sil
25+
26+
// REQUIRES: swift_in_compiler
27+
28+
29+
//--- Lib.swift
30+
package import CoreA
31+
@_spiOnly public import CoreB
32+
33+
/// PkgStruct is imported with `package import` and should be serialized.
34+
// CHECK-DAG: sil package [serialized_for_package] [canonical] @$s3Lib7libFuncyy5CoreA9PkgStructVF : $@convention(thin) (@in_guaranteed PkgStruct) -> () {
35+
package func libFunc(_ arg: PkgStruct) {
36+
print(arg.pkgVar)
37+
}
38+
39+
/// PubStruct is imported with `@_spiOnly public import` and should be serialized.
40+
// CHECK-DAG: sil [serialized_for_package] [canonical] @$s3Lib7spiFuncyy5CoreB15PubStructForSPIVF : $@convention(thin) (@in_guaranteed PubStructForSPI) -> () {
41+
@_spi(InCoreB)
42+
public func spiFunc(_ arg: PubStructForSPI) {
43+
print(arg.pubVarForSPI)
44+
}
45+
46+
//--- CoreA.swift
47+
package struct PkgStruct {
48+
package var pkgVar: Int
49+
package init(_ arg: Int) {
50+
self.pkgVar = arg
51+
}
52+
}
53+
54+
//--- CoreB.swift
55+
public struct PubStructForSPI {
56+
public var pubVarForSPI: String
57+
public init(_ arg: String) {
58+
self.pubVarForSPI = arg
59+
}
60+
}

0 commit comments

Comments
 (0)