Skip to content

Commit 8104a14

Browse files
authored
Merge pull request swiftlang#12427 from swiftix/sil-serialization-before-optimizations5
Add support for the early serialization of SIL modules using "high-level" SIL and make it the default
2 parents 1187890 + 53754a7 commit 8104a14

File tree

16 files changed

+276
-60
lines changed

16 files changed

+276
-60
lines changed

include/swift/SIL/SILModule.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ class SILModule {
100100
using DefaultWitnessTableListType = llvm::ilist<SILDefaultWitnessTable>;
101101
using CoverageMapListType = llvm::ilist<SILCoverageMap>;
102102
using LinkingMode = SILOptions::LinkingMode;
103+
using ActionCallback = std::function<void()>;
103104

104105
private:
105106
friend KeyPathPattern;
@@ -213,6 +214,13 @@ class SILModule {
213214
/// The options passed into this SILModule.
214215
SILOptions &Options;
215216

217+
/// Set if the SILModule was serialized already. It is used
218+
/// to ensure that the module is serialized only once.
219+
bool serialized;
220+
221+
/// Action to be executed for serializing the SILModule.
222+
ActionCallback SerializeSILAction;
223+
216224
/// A list of clients that need to be notified when an instruction
217225
/// invalidation message is sent.
218226
llvm::SetVector<DeleteNotificationHandler*> NotificationHandlers;
@@ -251,6 +259,17 @@ class SILModule {
251259
/// registered handlers. The order of handlers is deterministic but arbitrary.
252260
void notifyDeleteHandlers(SILNode *node);
253261

262+
/// Set a serialization action.
263+
void setSerializeSILAction(ActionCallback SerializeSILAction);
264+
ActionCallback getSerializeSILAction() const;
265+
266+
/// Set a flag indicating that this module is serialized already.
267+
void setSerialized() { serialized = true; }
268+
bool isSerialized() const { return serialized; }
269+
270+
/// Serialize a SIL module using the configured SerializeSILAction.
271+
void serialize();
272+
254273
/// \brief This converts Swift types to SILTypes.
255274
mutable Lowering::TypeConverter Types;
256275

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@ PASS(MarkUninitializedFixup, "mark-uninitialized-fixup",
269269
"Temporary pass for staging in mark_uninitialized changes.")
270270
PASS(SimplifyUnreachableContainingBlocks, "simplify-unreachable-containing-blocks",
271271
"Utility pass. Removes all non-term insts from blocks with unreachable terms")
272+
PASS(SerializeSILPass, "serialize-sil",
273+
"Utility pass. Serializes the current SILModule")
272274
PASS(BugReducerTester, "bug-reducer-tester",
273275
"sil-bug-reducer Tool Testing by Asserting on a Sentinel Function")
274276
PASS_RANGE(AllPasses, AADumper, BugReducerTester)

lib/FrontendTool/FrontendTool.cpp

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,46 @@ static bool performCompile(CompilerInstance &Instance,
854854
SM->verify();
855855
}
856856

857+
// This is the action to be used to serialize SILModule.
858+
// It may be invoked multiple times, but it will perform
859+
// serialization only once. The serialization may either happen
860+
// after high-level optimizations or after all optimizations are
861+
// done, depending on the compiler setting.
862+
863+
auto SerializeSILModuleAction = [&]() {
864+
if (!opts.ModuleOutputPath.empty() || !opts.ModuleDocOutputPath.empty()) {
865+
auto DC = PrimarySourceFile ? ModuleOrSourceFile(PrimarySourceFile)
866+
: Instance.getMainModule();
867+
if (!opts.ModuleOutputPath.empty()) {
868+
SerializationOptions serializationOpts;
869+
serializationOpts.OutputPath = opts.ModuleOutputPath.c_str();
870+
serializationOpts.DocOutputPath = opts.ModuleDocOutputPath.c_str();
871+
serializationOpts.GroupInfoPath = opts.GroupInfoPath.c_str();
872+
if (opts.SerializeBridgingHeader)
873+
serializationOpts.ImportedHeader = opts.ImplicitObjCHeaderPath;
874+
serializationOpts.ModuleLinkName = opts.ModuleLinkName;
875+
serializationOpts.ExtraClangOptions =
876+
Invocation.getClangImporterOptions().ExtraArgs;
877+
serializationOpts.EnableNestedTypeLookupTable =
878+
opts.EnableSerializationNestedTypeLookupTable;
879+
if (!IRGenOpts.ForceLoadSymbolName.empty())
880+
serializationOpts.AutolinkForceLoad = true;
881+
882+
// Options contain information about the developer's computer,
883+
// so only serialize them if the module isn't going to be shipped to
884+
// the public.
885+
serializationOpts.SerializeOptionsForDebugging =
886+
!moduleIsPublic || opts.AlwaysSerializeDebuggingOptions;
887+
888+
serialize(DC, serializationOpts, SM.get());
889+
}
890+
}
891+
};
892+
893+
// Set the serialization action, so that the SIL module
894+
// can be serialized at any moment, e.g. during the optimization pipeline.
895+
SM->setSerializeSILAction(SerializeSILModuleAction);
896+
857897
// Perform SIL optimization passes if optimizations haven't been disabled.
858898
// These may change across compiler versions.
859899
{
@@ -921,32 +961,9 @@ static bool performCompile(CompilerInstance &Instance,
921961
}
922962

923963
if (!opts.ModuleOutputPath.empty() || !opts.ModuleDocOutputPath.empty()) {
924-
auto DC = PrimarySourceFile ? ModuleOrSourceFile(PrimarySourceFile) :
925-
Instance.getMainModule();
926-
if (!opts.ModuleOutputPath.empty()) {
927-
SerializationOptions serializationOpts;
928-
serializationOpts.OutputPath = opts.ModuleOutputPath.c_str();
929-
serializationOpts.DocOutputPath = opts.ModuleDocOutputPath.c_str();
930-
serializationOpts.GroupInfoPath = opts.GroupInfoPath.c_str();
931-
if (opts.SerializeBridgingHeader)
932-
serializationOpts.ImportedHeader = opts.ImplicitObjCHeaderPath;
933-
serializationOpts.ModuleLinkName = opts.ModuleLinkName;
934-
serializationOpts.ExtraClangOptions =
935-
Invocation.getClangImporterOptions().ExtraArgs;
936-
serializationOpts.EnableNestedTypeLookupTable =
937-
opts.EnableSerializationNestedTypeLookupTable;
938-
if (!IRGenOpts.ForceLoadSymbolName.empty())
939-
serializationOpts.AutolinkForceLoad = true;
940-
941-
// Options contain information about the developer's computer,
942-
// so only serialize them if the module isn't going to be shipped to
943-
// the public.
944-
serializationOpts.SerializeOptionsForDebugging =
945-
!moduleIsPublic || opts.AlwaysSerializeDebuggingOptions;
946-
947-
serialize(DC, serializationOpts, SM.get());
948-
}
949-
964+
// Serialize the SILModule if it was not serialized yet.
965+
if (!SM.get()->isSerialized())
966+
SM.get()->serialize();
950967
if (Action == FrontendOptions::MergeModules ||
951968
Action == FrontendOptions::EmitModuleOnly) {
952969
if (shouldIndex) {

lib/SIL/SILModule.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ SILModule::SILModule(ModuleDecl *SwiftModule, SILOptions &Options,
8787
const DeclContext *associatedDC, bool wholeModule)
8888
: TheSwiftModule(SwiftModule), AssociatedDeclContext(associatedDC),
8989
Stage(SILStage::Raw), Callback(new SILModule::SerializationCallback()),
90-
wholeModule(wholeModule), Options(Options), Types(*this) {}
90+
wholeModule(wholeModule), Options(Options), serialized(false),
91+
SerializeSILAction(), Types(*this) {}
9192

9293
SILModule::~SILModule() {
9394
// Decrement ref count for each SILGlobalVariable with static initializers.
@@ -762,3 +763,20 @@ bool SILModule::isOptimizedOnoneSupportModule() const {
762763
return getOptions().Optimization >= SILOptions::SILOptMode::Optimize &&
763764
isOnoneSupportModule();
764765
}
766+
767+
void SILModule::setSerializeSILAction(SILModule::ActionCallback Action) {
768+
assert(!SerializeSILAction && "Serialization action can be set only once");
769+
SerializeSILAction = Action;
770+
}
771+
772+
SILModule::ActionCallback SILModule::getSerializeSILAction() const {
773+
return SerializeSILAction;
774+
}
775+
776+
void SILModule::serialize() {
777+
assert(SerializeSILAction && "Serialization action should be set");
778+
assert(!isSerialized() && "The module was serialized already");
779+
SerializeSILAction();
780+
setSerialized();
781+
}
782+

lib/SILOptimizer/IPO/DeadFunctionElimination.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ STATISTIC(NumDeadFunc, "Number of dead functions eliminated");
3030

3131
namespace {
3232

33+
/// Returns true if a function should be SIL serialized or emitted by IRGen.
34+
static bool shouldBeSerializedOrEmitted(SILFunction *F) {
35+
// public_external functions are never SIL serialized or emitted by IRGen.
36+
if (F->isAvailableExternally() && hasPublicVisibility(F->getLinkage()) &&
37+
!F->isTransparent())
38+
return false;
39+
40+
// [serialized] functions should always be SIL serialized.
41+
if (F->isSerialized())
42+
return true;
43+
44+
return false;
45+
}
46+
3347
/// This is a base class for passes that are based on function liveness
3448
/// computations like e.g. dead function elimination.
3549
/// It provides a common logic for computing live (i.e. reachable) functions.
@@ -117,6 +131,11 @@ class FunctionLivenessComputation {
117131
return true;
118132
}
119133

134+
// Do not consider public_external functions that do not need to be emitted
135+
// into the client as anchors.
136+
if (shouldBeSerializedOrEmitted(F))
137+
return true;
138+
120139
return false;
121140
}
122141

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,16 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) {
243243
P.addEarlyInliner();
244244
break;
245245
case OptimizationLevelKind::MidLevel:
246-
// Does inline semantics-functions (except "availability"), but not
247-
// global-init functions.
248246
P.addGlobalOpt();
249247
P.addLetPropertiesOpt();
248+
// It is important to serialize before any of the @_semantics
249+
// functions are inlined, because otherwise the information about
250+
// uses of such functions inside the module is lost,
251+
// which reduces the ability of the compiler to optimize clients
252+
// importing this module.
253+
P.addSerializeSILPass();
254+
// Does inline semantics-functions (except "availability"), but not
255+
// global-init functions.
250256
P.addPerfInliner();
251257
break;
252258
case OptimizationLevelKind::LowLevel:

lib/SILOptimizer/UtilityPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ set(UTILITYPASSES_SOURCES
2121
UtilityPasses/LoopRegionPrinter.cpp
2222
UtilityPasses/MemBehaviorDumper.cpp
2323
UtilityPasses/RCIdentityDumper.cpp
24+
UtilityPasses/SerializeSILPass.cpp
2425
UtilityPasses/SILDebugInfoGenerator.cpp
2526
UtilityPasses/SideEffectsDumper.cpp
2627
UtilityPasses/SimplifyUnreachableContainingBlocks.cpp
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//===--- SerializeSILPass.cpp ---------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#define DEBUG_TYPE "serialize-sil"
14+
#include "swift/Strings.h"
15+
#include "swift/SILOptimizer/PassManager/Passes.h"
16+
#include "swift/SILOptimizer/PassManager/Transforms.h"
17+
18+
using namespace swift;
19+
20+
/// A utility pass to serialize a SILModule at any place inside the optimization
21+
/// pipeline.
22+
class SerializeSILPass : public SILModuleTransform {
23+
/// Removes [serialized] from all functions. This allows for more
24+
/// optimizations and for a better dead function elimination.
25+
void removeSerializedFlagFromAllFunctions(SILModule &M) {
26+
for (auto &F : M) {
27+
F.setSerialized(IsSerialized_t::IsNotSerialized);
28+
}
29+
}
30+
31+
public:
32+
SerializeSILPass() {}
33+
void run() override {
34+
auto &M = *getModule();
35+
// Nothing to do if the module was serialized already.
36+
if (M.isSerialized())
37+
return;
38+
39+
// Mark all reachable functions as "anchors" so that they are not
40+
// removed later by the dead function elimination pass. This
41+
// is required, because clients may reference any of the
42+
// serialized functions or anything referenced from them. Therefore,
43+
// to avoid linker errors, the object file of the current module should
44+
// contain all the symbols which were alive at the time of serialization.
45+
DEBUG(llvm::dbgs() << "Serializing SILModule in SerializeSILPass\n");
46+
getModule()->serialize();
47+
removeSerializedFlagFromAllFunctions(M);
48+
}
49+
};
50+
51+
SILTransform *swift::createSerializeSILPass() {
52+
return new SerializeSILPass();
53+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %target-sil-opt %s -verify -closure-specialize -assume-parsing-unqualified-ownership-sil -o - | %FileCheck %s
2+
3+
// Make sure we do not specialize resilientCallee.
4+
5+
sil_stage canonical
6+
7+
import Builtin
8+
import Swift
9+
import SwiftShims
10+
11+
@_semantics("optimize.sil.never") public func action()
12+
13+
@inline(__always) public func fragileCaller()
14+
15+
public func resilientCallee(fn: () -> ())
16+
17+
// action()
18+
sil [_semantics "optimize.sil.never"] @_T026closure_specialize_fragile6actionyyF : $@convention(thin) () -> () {
19+
bb0:
20+
%0 = tuple ()
21+
return %0 : $()
22+
} // end sil function '_T026closure_specialize_fragile6actionyyF'
23+
24+
// CHECK-LABEL: sil [serialized] [always_inline] @_T026closure_specialize_fragile0C6CalleryyF : $@convention(thin) () -> ()
25+
// CHECK: function_ref @_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
26+
// CHECK: return
27+
// fragileCaller()
28+
sil [serialized] [always_inline] @_T026closure_specialize_fragile0C6CalleryyF : $@convention(thin) () -> () {
29+
bb0:
30+
// function_ref resilientCallee(fn:)
31+
%0 = function_ref @_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
32+
// function_ref closure #1 in fragileCaller()
33+
%1 = function_ref @_T026closure_specialize_fragile0C6CalleryyFyycfU_ : $@convention(thin) () -> ()
34+
%2 = thin_to_thick_function %1 : $@convention(thin) () -> () to $@callee_owned () -> ()
35+
%3 = apply %0(%2) : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
36+
%4 = tuple ()
37+
return %4 : $()
38+
} // end sil function '_T026closure_specialize_fragile0C6CalleryyF'
39+
40+
// CHECK-LABEL: sil @_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
41+
42+
// resilientCallee(fn:)
43+
sil @_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF : $@convention(thin) (@owned @callee_owned () -> ()) -> () {
44+
bb0(%0 : $@callee_owned () -> ()):
45+
strong_retain %0 : $@callee_owned () -> ()
46+
%3 = apply %0() : $@callee_owned () -> ()
47+
strong_release %0 : $@callee_owned () -> ()
48+
%5 = tuple ()
49+
return %5 : $()
50+
} // end sil function '_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF'
51+
52+
// closure #1 in fragileCaller()
53+
sil shared [serialized] @_T026closure_specialize_fragile0C6CalleryyFyycfU_ : $@convention(thin) () -> () {
54+
bb0:
55+
// function_ref action()
56+
%0 = function_ref @_T026closure_specialize_fragile6actionyyF : $@convention(thin) () -> ()
57+
%1 = apply %0() : $@convention(thin) () -> ()
58+
%2 = tuple ()
59+
return %2 : $()
60+
} // end sil function '_T026closure_specialize_fragile0C6CalleryyFyycfU_'

test/SILOptimizer/closure_specialize_fragile.swift

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

0 commit comments

Comments
 (0)