Skip to content

Commit 56d337d

Browse files
authored
Merge pull request #72249 from apple/es/pkg-cmo
2 parents d29f6a1 + a3250e4 commit 56d337d

File tree

14 files changed

+325
-21
lines changed

14 files changed

+325
-21
lines changed

include/swift/AST/SILOptions.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,14 @@ class SILOptions {
126126
/// Controls whether cross module optimization is enabled.
127127
CrossModuleOptimizationMode CMOMode = CrossModuleOptimizationMode::Off;
128128

129+
/// Optimization to perform default CMO within a package boundary.
130+
/// Unlike the existing CMO, package CMO can be built with
131+
/// -enable-library-evolution since package modules are required
132+
/// to be built in the same project. To enable this optimization, the
133+
/// module also needs to opt in to allow non-resilient access with
134+
/// -experimental-allow-non-resilient-access.
135+
bool EnableSerializePackage = true;
136+
129137
/// Enables the emission of stack protectors in functions.
130138
bool EnableStackProtection = true;
131139

include/swift/Option/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,10 @@ def Oplayground : Flag<["-"], "Oplayground">, Group<O_Group>,
984984
Flags<[HelpHidden, FrontendOption, ModuleInterfaceOption]>,
985985
HelpText<"Compile with optimizations appropriate for a playground">;
986986

987+
def ExperimentalPackageCMO : Flag<["-"], "experimental-package-cmo">,
988+
Flags<[FrontendOption]>,
989+
HelpText<"Enable optimization to perform defalut CMO within a package boundary">;
990+
987991
def EnbaleDefaultCMO : Flag<["-"], "enable-default-cmo">,
988992
Flags<[HelpHidden, FrontendOption]>,
989993
HelpText<"Perform conservative cross-module optimization">;

include/swift/SIL/SILLinkage.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,26 @@ inline bool hasPublicVisibility(SILLinkage linkage) {
289289
llvm_unreachable("Unhandled SILLinkage in switch.");
290290
}
291291

292+
inline bool hasPublicOrPackageVisibility(SILLinkage linkage, bool includePackage) {
293+
switch (linkage) {
294+
case SILLinkage::Public:
295+
case SILLinkage::PublicExternal:
296+
case SILLinkage::PublicNonABI:
297+
return true;
298+
case SILLinkage::Package:
299+
case SILLinkage::PackageExternal:
300+
case SILLinkage::PackageNonABI:
301+
return includePackage;
302+
case SILLinkage::Hidden:
303+
case SILLinkage::Shared:
304+
case SILLinkage::Private:
305+
case SILLinkage::HiddenExternal:
306+
return false;
307+
}
308+
309+
llvm_unreachable("Unhandled SILLinkage in switch.");
310+
}
311+
292312
inline bool hasSharedVisibility(SILLinkage linkage) {
293313
switch (linkage) {
294314
case SILLinkage::Shared:

lib/Driver/ToolChains.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
300300
inputArgs.AddLastArg(arguments, options::OPT_suppress_warnings);
301301
inputArgs.AddLastArg(arguments, options::OPT_suppress_remarks);
302302
inputArgs.AddLastArg(arguments, options::OPT_experimental_package_bypass_resilience);
303+
inputArgs.AddLastArg(arguments, options::OPT_ExperimentalPackageCMO);
303304
inputArgs.AddLastArg(arguments, options::OPT_profile_generate);
304305
inputArgs.AddLastArg(arguments, options::OPT_profile_use);
305306
inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping);

lib/Frontend/CompilerInvocation.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2341,6 +2341,18 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
23412341
} else if (Args.hasArg(OPT_EnbaleCMOEverything)) {
23422342
Opts.CMOMode = CrossModuleOptimizationMode::Everything;
23432343
}
2344+
2345+
if (Args.hasArg(OPT_ExperimentalPackageCMO)) {
2346+
if (!FEOpts.AllowNonResilientAccess) {
2347+
Diags.diagnose(SourceLoc(), diag::ignoring_option_requires_option,
2348+
"-experimental-package-cmo",
2349+
"-experimental-allow-non-resilient-access");
2350+
} else {
2351+
Opts.EnableSerializePackage = true;
2352+
Opts.CMOMode = CrossModuleOptimizationMode::Default;
2353+
}
2354+
}
2355+
23442356
Opts.EnableStackProtection =
23452357
Args.hasFlag(OPT_enable_stack_protector, OPT_disable_stack_protector,
23462358
Opts.EnableStackProtection);

lib/IRGen/IRGenSIL.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2446,8 +2446,10 @@ void IRGenModule::emitSILFunction(SILFunction *f) {
24462446
f->getLoweredFunctionType()->isPolymorphic())
24472447
return;
24482448

2449-
// Do not emit bodies of public_external functions.
2450-
if (hasPublicVisibility(f->getLinkage()) && f->isAvailableExternally())
2449+
// Do not emit bodies of public_external or package_external functions.
2450+
if (hasPublicOrPackageVisibility(f->getLinkage(),
2451+
f->getASTContext().SILOpts.EnableSerializePackage) &&
2452+
f->isAvailableExternally())
24512453
return;
24522454

24532455
PrettyStackTraceSILFunction stackTrace("emitting IR", f);

lib/SIL/IR/SILFunction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -898,8 +898,8 @@ bool SILFunction::hasValidLinkageForFragileRef() const {
898898
isAvailableExternally())
899899
return false;
900900

901-
// Otherwise, only public functions can be referenced.
902-
return hasPublicVisibility(getLinkage());
901+
// Otherwise, only public or package functions can be referenced.
902+
return hasPublicOrPackageVisibility(getLinkage(), getModule().getOptions().EnableSerializePackage);
903903
}
904904

905905
bool

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2419,7 +2419,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
24192419
}
24202420
if (F.isSerialized()) {
24212421
require(RefG->isSerialized()
2422-
|| hasPublicVisibility(RefG->getLinkage()),
2422+
|| hasPublicOrPackageVisibility(RefG->getLinkage(), F.getModule().getOptions().EnableSerializePackage),
24232423
"alloc_global inside fragile function cannot "
24242424
"reference a private or hidden symbol");
24252425
}
@@ -2438,7 +2438,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
24382438
}
24392439
if (F.isSerialized()) {
24402440
require(RefG->isSerialized()
2441-
|| hasPublicVisibility(RefG->getLinkage()),
2441+
|| hasPublicOrPackageVisibility(RefG->getLinkage(), F.getModule().getOptions().EnableSerializePackage),
24422442
"global_addr/value inside fragile function cannot "
24432443
"reference a private or hidden symbol");
24442444
}

lib/SILOptimizer/IPO/CrossModuleOptimization.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,26 @@ class InstructionVisitor : public SILCloner<InstructionVisitor> {
161161
}
162162
};
163163

164+
static bool isVisible(SILLinkage linkage, SILOptions options) {
165+
if (options.EnableSerializePackage)
166+
return linkage == SILLinkage::Public || linkage == SILLinkage::Package;
167+
return linkage == SILLinkage::Public;
168+
}
169+
static bool isVisible(AccessLevel accessLevel, SILOptions options) {
170+
if (options.EnableSerializePackage)
171+
return accessLevel == AccessLevel::Package || accessLevel == AccessLevel::Public;
172+
return accessLevel == AccessLevel::Public;
173+
}
174+
164175
/// Select functions in the module which should be serialized.
165176
void CrossModuleOptimization::serializeFunctionsInModule() {
166177

167178
FunctionFlags canSerializeFlags;
168179

169180
// Start with public functions.
170181
for (SILFunction &F : M) {
171-
if (F.getLinkage() == SILLinkage::Public || everything) {
182+
if (isVisible(F.getLinkage(), M.getOptions()) ||
183+
everything) {
172184
if (canSerializeFunction(&F, canSerializeFlags, /*maxDepth*/ 64)) {
173185
serializeFunction(&F, canSerializeFlags);
174186
}
@@ -185,7 +197,6 @@ bool CrossModuleOptimization::canSerializeFunction(
185197
FunctionFlags &canSerializeFlags,
186198
int maxDepth) {
187199
auto iter = canSerializeFlags.find(function);
188-
189200
// Avoid infinite recursion in case it's a cycle in the call graph.
190201
if (iter != canSerializeFlags.end())
191202
return iter->second;
@@ -270,7 +281,7 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
270281
// function is completely inlined afterwards.
271282
// Also, when emitting TBD files, we cannot introduce a new public symbol.
272283
if ((conservative || M.getOptions().emitTBD) &&
273-
!hasPublicVisibility(callee->getLinkage())) {
284+
!hasPublicOrPackageVisibility(callee->getLinkage(), M.getOptions().EnableSerializePackage)) {
274285
return false;
275286
}
276287

@@ -290,13 +301,12 @@ bool CrossModuleOptimization::canSerializeInstruction(SILInstruction *inst,
290301
// inline.
291302
if (!canUseFromInline(callee))
292303
return false;
293-
294304
return true;
295305
}
296306
if (auto *GAI = dyn_cast<GlobalAddrInst>(inst)) {
297307
SILGlobalVariable *global = GAI->getReferencedGlobal();
298308
if ((conservative || M.getOptions().emitTBD) &&
299-
!hasPublicVisibility(global->getLinkage())) {
309+
!hasPublicOrPackageVisibility(global->getLinkage(), M.getOptions().EnableSerializePackage)) {
300310
return false;
301311
}
302312

@@ -344,7 +354,7 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
344354
// function is completely inlined afterwards.
345355
// Also, when emitting TBD files, we cannot introduce a new public symbol.
346356
if ((conservative || M.getOptions().emitTBD) &&
347-
!hasPublicVisibility(referencedFunc->getLinkage())) {
357+
!hasPublicOrPackageVisibility(referencedFunc->getLinkage(), M.getOptions().EnableSerializePackage)) {
348358
return false;
349359
}
350360

@@ -368,7 +378,7 @@ bool CrossModuleOptimization::canSerializeType(SILType type) {
368378
if (conservative && subNT->getEffectiveAccess() < AccessLevel::Package) {
369379
return true;
370380
}
371-
381+
372382
// Exclude types which are defined in an @_implementationOnly imported
373383
// module. Such modules are not transitively available.
374384
if (!canUseFromInline(subNT)) {
@@ -542,15 +552,15 @@ void CrossModuleOptimization::serializeInstruction(SILInstruction *inst,
542552
}
543553
}
544554
serializeFunction(callee, canSerializeFlags);
545-
assert(callee->isSerialized() || callee->getLinkage() == SILLinkage::Public);
555+
assert(callee->isSerialized() || isVisible(callee->getLinkage(), M.getOptions()));
546556
return;
547557
}
548558
if (auto *GAI = dyn_cast<GlobalAddrInst>(inst)) {
549559
SILGlobalVariable *global = GAI->getReferencedGlobal();
550560
if (canSerializeGlobal(global)) {
551561
serializeGlobal(global);
552562
}
553-
if (!hasPublicVisibility(global->getLinkage())) {
563+
if (!hasPublicOrPackageVisibility(global->getLinkage(), M.getOptions().EnableSerializePackage)) {
554564
global->setLinkage(SILLinkage::Public);
555565
}
556566
return;
@@ -606,7 +616,7 @@ void CrossModuleOptimization::makeDeclUsableFromInline(ValueDecl *decl) {
606616
if (M.getSwiftModule() != decl->getDeclContext()->getParentModule())
607617
return;
608618

609-
if (decl->getFormalAccess() < AccessLevel::Public &&
619+
if (!isVisible(decl->getFormalAccess(), M.getOptions()) &&
610620
!decl->isUsableFromInline()) {
611621
// Mark the nominal type as "usableFromInline".
612622
// TODO: find a way to do this without modifying the AST. The AST should be

lib/Serialization/DeserializeSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ static std::optional<SILLinkage> fromStableSILLinkage(unsigned value) {
7777
case SIL_LINKAGE_SHARED: return SILLinkage::Shared;
7878
case SIL_LINKAGE_PRIVATE: return SILLinkage::Private;
7979
case SIL_LINKAGE_PUBLIC_EXTERNAL: return SILLinkage::PublicExternal;
80+
case SIL_LINKAGE_PACKAGE_EXTERNAL: return SILLinkage::PackageExternal;
8081
case SIL_LINKAGE_HIDDEN_EXTERNAL: return SILLinkage::HiddenExternal;
8182
}
8283

test/SILOptimizer/Inputs/cross-module/default-module.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
1-
21
import Submodule
32
import PrivateCModule
43

54
public func incrementByThree(_ x: Int) -> Int {
65
return incrementByOne(x) + 2
76
}
87

8+
package func pkgFunc(_ x: Int) -> Int {
9+
return subPkgFunc(x) + 2
10+
}
11+
912
public func incrementByThreeWithCall(_ x: Int) -> Int {
1013
return incrementByOneNoCMO(x) + 2
1114
}
1215

16+
package func pkgFuncNoCMO(_ x: Int) -> Int {
17+
return subPkgFuncNoCMO(x) + 2
18+
}
19+
1320
public func submoduleKlassMember() -> Int {
1421
let k = SubmoduleKlass()
1522
return k.i
1623
}
1724

25+
package func pkgSubmoduleKlassMember() -> Int {
26+
let k = PkgSubmoduleKlass()
27+
return k.i
28+
}
29+
1830
public final class ModuleKlass {
1931
public var i: Int
2032

@@ -28,11 +40,29 @@ public func moduleKlassMember() -> Int {
2840
return k.i
2941
}
3042

43+
package final class PkgModuleKlass {
44+
package var i: Int
45+
46+
package init() {
47+
i = 27
48+
}
49+
}
50+
51+
package func pkgModuleKlassMember() -> Int {
52+
let k = PkgModuleKlass()
53+
return k.i
54+
}
55+
3156
public struct ModuleStruct {
3257
public static var publicFunctionPointer: (Int) -> (Int) = incrementByThree
3358
public static var privateFunctionPointer: (Int) -> (Int) = { $0 }
3459
}
3560

61+
package struct PkgModuleStruct {
62+
package static var funcPointer: (Int) -> (Int) = pkgFunc
63+
package static var closurePointer: (Int) -> (Int) = { $0 }
64+
}
65+
3666
public func callPrivateCFunc() -> Int {
3767
return Int(privateCFunc())
3868
}

test/SILOptimizer/Inputs/cross-module/default-submodule.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,20 @@ public func incrementByOne(_ x: Int) -> Int {
33
return x + 1
44
}
55

6+
package func subPkgFunc(_ x: Int) -> Int {
7+
return x + 1
8+
}
9+
610
@_semantics("optimize.no.crossmodule")
711
public func incrementByOneNoCMO(_ x: Int) -> Int {
812
return x + 1
913
}
1014

15+
@_semantics("optimize.no.crossmodule")
16+
package func subPkgFuncNoCMO(_ x: Int) -> Int {
17+
return x + 1
18+
}
19+
1120
public final class SubmoduleKlass {
1221
public var i: Int
1322

@@ -16,3 +25,10 @@ public final class SubmoduleKlass {
1625
}
1726
}
1827

28+
package final class PkgSubmoduleKlass {
29+
package var i: Int
30+
31+
package init() {
32+
i = 27
33+
}
34+
}

test/SILOptimizer/default-cmo.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11

22
// RUN: %empty-directory(%t)
33

4-
// RUN: %target-build-swift -O -wmo -Xfrontend -enable-default-cmo -parse-as-library -emit-module -emit-module-path=%t/Submodule.swiftmodule -module-name=Submodule %S/Inputs/cross-module/default-submodule.swift -c -o %t/submodule.o
5-
// RUN: %target-build-swift -O -wmo -Xfrontend -enable-default-cmo -parse-as-library -emit-module -emit-module-path=%t/Module.swiftmodule -module-name=Module -I%t -I%S/Inputs/cross-module %S/Inputs/cross-module/default-module.swift -c -o %t/module.o
6-
// RUN: %target-build-swift -O -wmo -Xfrontend -enable-default-cmo -parse-as-library -emit-tbd -emit-tbd-path %t/ModuleTBD.tbd -emit-module -emit-module-path=%t/ModuleTBD.swiftmodule -module-name=ModuleTBD -I%t -I%S/Inputs/cross-module %S/Inputs/cross-module/default-module.swift -c -o %t/moduletbd.o -Xfrontend -tbd-install_name -Xfrontend module
4+
// RUN: %target-build-swift -O -wmo -Xfrontend -enable-default-cmo -parse-as-library -emit-module -emit-module-path=%t/Submodule.swiftmodule -module-name=Submodule -package-name Pkg %S/Inputs/cross-module/default-submodule.swift -c -o %t/submodule.o
5+
// RUN: %target-build-swift -O -wmo -Xfrontend -enable-default-cmo -parse-as-library -emit-module -emit-module-path=%t/Module.swiftmodule -module-name=Module -package-name Pkg -I%t -I%S/Inputs/cross-module %S/Inputs/cross-module/default-module.swift -c -o %t/module.o
6+
// RUN: %target-build-swift -O -wmo -Xfrontend -enable-default-cmo -parse-as-library -emit-tbd -emit-tbd-path %t/ModuleTBD.tbd -emit-module -emit-module-path=%t/ModuleTBD.swiftmodule -module-name=ModuleTBD -package-name Pkg -I%t -I%S/Inputs/cross-module %S/Inputs/cross-module/default-module.swift -c -o %t/moduletbd.o -Xfrontend -tbd-install_name -Xfrontend module
77

8-
// RUN: %target-build-swift -O -wmo -module-name=Main -I%t -I%S/Inputs/cross-module %s -emit-sil | %FileCheck %s
8+
// RUN: %target-build-swift -O -wmo -module-name=Main -package-name Pkg -I%t -I%S/Inputs/cross-module %s -emit-sil | %FileCheck %s
99

1010
// REQUIRES: swift_in_compiler
1111

0 commit comments

Comments
 (0)