Skip to content

Commit 2de333c

Browse files
authored
Merge pull request #79035 from swiftlang/elsh/pcmo-imports
Package CMO: Enable serializing decls imported with `@_spiOnly` or `package import`.
2 parents c4b65be + 67594f1 commit 2de333c

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
@@ -1052,11 +1052,6 @@ class ModuleDecl
10521052
/// This assumes that \p module was imported.
10531053
bool isImportedImplementationOnly(const ModuleDecl *module) const;
10541054

1055-
/// Returns true if decl context or its content can be serialized by
1056-
/// cross-module-optimization.
1057-
/// The \p ctxt can e.g. be a NominalType or the context of a function.
1058-
bool canBeUsedForCrossModuleOptimization(DeclContext *ctxt) const;
1059-
10601055
/// Finds all top-level decls of this module.
10611056
///
10621057
/// 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
@@ -3060,33 +3060,6 @@ bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const {
30603060
return true;
30613061
}
30623062

3063-
bool ModuleDecl::
3064-
canBeUsedForCrossModuleOptimization(DeclContext *ctxt) const {
3065-
ModuleDecl *moduleOfCtxt = ctxt->getParentModule();
3066-
3067-
// If the context defined in the same module - or is the same module, it's
3068-
// fine.
3069-
if (moduleOfCtxt == this)
3070-
return true;
3071-
3072-
// See if context is imported in a "regular" way, i.e. not with
3073-
// @_implementationOnly, `package import` or @_spiOnly.
3074-
ModuleDecl::ImportFilter filter = {
3075-
ModuleDecl::ImportFilterKind::ImplementationOnly,
3076-
ModuleDecl::ImportFilterKind::PackageOnly,
3077-
ModuleDecl::ImportFilterKind::SPIOnly
3078-
};
3079-
SmallVector<ImportedModule, 4> results;
3080-
getImportedModules(results, filter);
3081-
3082-
auto &imports = getASTContext().getImportCache();
3083-
for (auto &desc : results) {
3084-
if (imports.isImportedBy(moduleOfCtxt, desc.importedModule))
3085-
return false;
3086-
}
3087-
return true;
3088-
}
3089-
30903063
void SourceFile::lookupImportedSPIGroups(
30913064
const ModuleDecl *importedModule,
30923065
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);
@@ -745,7 +751,12 @@ static bool couldBeLinkedStatically(DeclContext *funcCtxt, SILModule &module) {
745751
// The stdlib module is always linked dynamically.
746752
if (funcModule == module.getASTContext().getStdlibModule())
747753
return false;
748-
754+
755+
// An sdk or system module should be linked dynamically.
756+
if (isPackageCMOEnabled(module.getSwiftModule()) &&
757+
funcModule->isNonUserModule())
758+
return false;
759+
749760
// Conservatively assume the function is in a statically linked module.
750761
return true;
751762
}
@@ -755,7 +766,7 @@ bool CrossModuleOptimization::canUseFromInline(DeclContext *declCtxt) {
755766
if (everything)
756767
return true;
757768

758-
if (!M.getSwiftModule()->canBeUsedForCrossModuleOptimization(declCtxt))
769+
if (!checkImports(declCtxt))
759770
return false;
760771

761772
/// If we are emitting a TBD file, the TBD file only contains public symbols
@@ -771,6 +782,52 @@ bool CrossModuleOptimization::canUseFromInline(DeclContext *declCtxt) {
771782
return true;
772783
}
773784

785+
bool CrossModuleOptimization::checkImports(DeclContext *ctxt) const {
786+
ModuleDecl *moduleOfCtxt = ctxt->getParentModule();
787+
788+
// If the context defined in the same module - or is the same module, it's
789+
// fine.
790+
if (moduleOfCtxt == M.getSwiftModule())
791+
return true;
792+
793+
ModuleDecl::ImportFilter filter;
794+
795+
if (isPackageCMOEnabled(M.getSwiftModule())) {
796+
// If Package CMO is enabled, decls imported with `package import`
797+
// or `@_spiOnly import` into this module should be allowed to be
798+
// serialized. They are used in decls with `package` or higher
799+
// access level, with or without @_spi; a client of this module
800+
// should be able to access them directly if in the same package.
801+
filter = { ModuleDecl::ImportFilterKind::ImplementationOnly };
802+
} else {
803+
// See if context is imported in a "regular" way, i.e. not with
804+
// @_implementationOnly, `package import` or @_spiOnly.
805+
filter = {
806+
ModuleDecl::ImportFilterKind::ImplementationOnly,
807+
ModuleDecl::ImportFilterKind::PackageOnly,
808+
ModuleDecl::ImportFilterKind::SPIOnly
809+
};
810+
}
811+
SmallVector<ImportedModule, 4> results;
812+
M.getSwiftModule()->getImportedModules(results, filter);
813+
814+
auto &imports = M.getSwiftModule()->getASTContext().getImportCache();
815+
for (auto &desc : results) {
816+
if (imports.isImportedBy(moduleOfCtxt, desc.importedModule)) {
817+
// E.g. `@_implementationOnly import QuartzCore_Private.CALayerPrivate`
818+
// imports `Foundation` as its transitive dependency module; use of a
819+
// a `public` decl in `Foundation` such as `IndexSet` in a function
820+
// signature should not block serialization in Package CMO given the
821+
// function has `package` or higher access level.
822+
if (isPackageCMOEnabled(M.getSwiftModule()) &&
823+
moduleOfCtxt->isNonUserModule())
824+
continue;
825+
return false;
826+
}
827+
}
828+
return true;
829+
}
830+
774831
/// Returns true if the function \p func can be used from a serialized function.
775832
bool CrossModuleOptimization::canUseFromInline(SILFunction *function) {
776833
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)