Skip to content

Commit 53754a7

Browse files
committed
Add a new simple utility optimization pass for serialization of SILModules
1 parent 882d72b commit 53754a7

File tree

11 files changed

+173
-33
lines changed

11 files changed

+173
-33
lines changed

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/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.

test/SILOptimizer/globalopt_linkage.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ _ = testit()
1717
// CHECK: sil hidden @{{.*}}testit
1818

1919
// CHECK: // MyStruct.StaticVar.getter
20-
// CHECK-NEXT: sil private [serialized] @_{{.*}}StaticVar
20+
// CHECK-NEXT: sil private @_{{.*}}StaticVar
2121

2222
// CHECK: // Global.getter
23-
// CHECK-NEXT: sil private [serialized] @_{{.*}}Global
23+
// CHECK-NEXT: sil private @_{{.*}}Global

test/SILOptimizer/let_properties_opts.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
// initialization code could be removed.
1717
// Specifically, the initialization code for Prop1, Prop2 and Prop3 can be removed.
1818

19-
// CHECK-WMO-LABEL: sil [thunk] [always_inline] @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int32, @owned Foo) -> @owned Foo
19+
// CHECK-WMO-LABEL: sil @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int32, @owned Foo) -> @owned Foo
2020
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop1
2121
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop2
2222
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop3
@@ -26,7 +26,7 @@
2626
// CHECK-WMO: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop3
2727
// CHECK-WMO: return
2828

29-
// CHECK-WMO-LABEL: sil [thunk] [always_inline] @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int64, @owned Foo) -> @owned Foo
29+
// CHECK-WMO-LABEL: sil @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int64, @owned Foo) -> @owned Foo
3030
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop1
3131
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop2
3232
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop3
@@ -41,14 +41,14 @@
4141
// from other modules. Therefore the initialization code could be removed.
4242
// Specifically, the initialization code for Prop2 can be removed.
4343

44-
// CHECK-LABEL: sil [thunk] [always_inline] @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int32, @owned Foo) -> @owned Foo
44+
// CHECK-LABEL: sil @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int32, @owned Foo) -> @owned Foo
4545
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop0
4646
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop1
4747
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop2
4848
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop3
4949
// CHECK: return
5050

51-
// CHECK-LABEL: sil [thunk] [always_inline] @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int64, @owned Foo) -> @owned Foo
51+
// CHECK-LABEL: sil @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int64, @owned Foo) -> @owned Foo
5252
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop0
5353
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop1
5454
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop2

test/SILOptimizer/outliner.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public func testOutlining() {
5353
gizmo.doSomething(arr)
5454
}
5555

56-
// CHECK-LABEL: sil shared [serializable] [noinline] @_T0So5GizmoC14stringPropertySQySSGvgToTeab_ : $@convention(thin) (@in_guaranteed Gizmo) -> @owned Optional<String>
56+
// CHECK-LABEL: sil shared [noinline] @_T0So5GizmoC14stringPropertySQySSGvgToTeab_ : $@convention(thin) (@in_guaranteed Gizmo) -> @owned Optional<String>
5757
// CHECK: bb0(%0 : $*Gizmo):
5858
// CHECK: %1 = load %0 : $*Gizmo
5959
// CHECK: %2 = objc_method %1 : $Gizmo, #Gizmo.stringProperty!getter.1.foreign : (Gizmo) -> () -> String!
@@ -71,7 +71,7 @@ public func testOutlining() {
7171
// CHECK: bb3(%13 : $Optional<String>):
7272
// CHECK: return %13 : $Optional<String>
7373

74-
// CHECK-LABEL: sil shared [serializable] [noinline] @_T0So5GizmoC14stringPropertySQySSGvgToTepb_ : $@convention(thin) (Gizmo) -> @owned Optional<String>
74+
// CHECK-LABEL: sil shared [noinline] @_T0So5GizmoC14stringPropertySQySSGvgToTepb_ : $@convention(thin) (Gizmo) -> @owned Optional<String>
7575
// CHECK: bb0(%0 : $Gizmo):
7676
// CHECK: %1 = objc_method %0 : $Gizmo, #Gizmo.stringProperty!getter.1.foreign : (Gizmo) -> () -> String!
7777
// CHECK: %2 = apply %1(%0) : $@convention(objc_method) (Gizmo) -> @autoreleased Optional<NSString>
@@ -88,7 +88,7 @@ public func testOutlining() {
8888
// CHECK:bb3(%12 : $Optional<String>):
8989
// CHECK: return %12 : $Optional<String>
9090

91-
// CHECK-LABEL: sil shared [serializable] [noinline] @_T0So5GizmoC14stringPropertySQySSGvsToTembnn_ : $@convention(thin) (@owned String, Gizmo) -> () {
91+
// CHECK-LABEL: sil shared [noinline] @_T0So5GizmoC14stringPropertySQySSGvsToTembnn_ : $@convention(thin) (@owned String, Gizmo) -> () {
9292
// CHECK: bb0(%0 : $String, %1 : $Gizmo):
9393
// CHECK: %2 = objc_method %1 : $Gizmo, #Gizmo.stringProperty!setter.1.foreign : (Gizmo) -> (String!) -> ()
9494
// CHECK: %3 = function_ref @_T0SS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF : $@convention(method) (@guaranteed String) -> @owned NSString
@@ -99,7 +99,7 @@ public func testOutlining() {
9999
// CHECK: strong_release %4 : $NSString
100100
// CHECK: return %7 : $()
101101

102-
// CHECK-LABEL: sil shared [serializable] [noinline] @_T0So5GizmoC12modifyStringSQySSGAD_Si10withNumberSQyypG0D6FoobartFToTembnnnb_ : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String> {
102+
// CHECK-LABEL: sil shared [noinline] @_T0So5GizmoC12modifyStringSQySSGAD_Si10withNumberSQyypG0D6FoobartFToTembnnnb_ : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String> {
103103
// CHECK: bb0(%0 : $String, %1 : $Int, %2 : $Optional<AnyObject>, %3 : $Gizmo):
104104
// CHECK: %4 = objc_method %3 : $Gizmo, #Gizmo.modifyString!1.foreign : (Gizmo) -> (String!, Int, Any!) -> String!
105105
// CHECK: %5 = function_ref @_T0SS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF : $@convention(method) (@guaranteed String) -> @owned NSString
@@ -124,7 +124,7 @@ public func testOutlining() {
124124
// CHECK: bb3(%20 : $Optional<String>):
125125
// CHECK: return %20 : $Optional<String>
126126

127-
// CHECK-LABEL: sil shared [serializable] [noinline] @_T0So5GizmoC11doSomethingSQyypGSQySaySSGGFToTembnn_ : $@convention(thin) (@owned Array<String>, Gizmo) -> @owned Optional<AnyObject> {
127+
// CHECK-LABEL: sil shared [noinline] @_T0So5GizmoC11doSomethingSQyypGSQySaySSGGFToTembnn_ : $@convention(thin) (@owned Array<String>, Gizmo) -> @owned Optional<AnyObject> {
128128
// CHECK: bb0(%0 : $Array<String>, %1 : $Gizmo):
129129
// CHECK: %2 = objc_method %1 : $Gizmo, #Gizmo.doSomething!1.foreign : (Gizmo) -> ([String]!) -> Any!
130130
// CHECK: %3 = function_ref @_T0Sa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF : $@convention(method) <{{.*}}> (@guaranteed Array<{{.*}}>) -> @owned NSArray
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -O -module-name Swift -module-link-name swiftCore -parse-as-library -parse-stdlib -emit-module -sil-serialize-witness-tables -sil-serialize-vtables %s -o %t/Swift.swiftmodule
3+
// RUN: %target-sil-opt -enable-sil-verify-all %t/Swift.swiftmodule -o - | %FileCheck %s
4+
5+
// Test that early serialization works as expected:
6+
// - it happens before the performance inlining and thus preserves @_semantics functions
7+
// - it happens after generic specialization
8+
9+
public struct Int {
10+
@_inlineable
11+
public init() {}
12+
}
13+
14+
public struct Array<T> {
15+
@_inlineable
16+
public init() {}
17+
18+
// Check that the generic version of a @_semantics function is preserved.
19+
// CHECK: sil [serialized] [_semantics "array.get_capacity"] @_T0Sa12_getCapacitySiyF : $@convention(method) <T> (Array<T>) -> Int
20+
// Check that a specialized version of a function is produced
21+
// CHECK: sil shared [serializable] [_semantics "array.get_capacity"] @_T0Sa12_getCapacitySiyFSi_Tgq5 : $@convention(method) (Array<Int>) -> Int
22+
@_inlineable
23+
@_versioned
24+
@_semantics("array.get_capacity")
25+
internal func _getCapacity() -> Int {
26+
return Int()
27+
}
28+
}
29+
30+
// Check that a call of a @_semantics function was not inlined if early-serialization is enabled.
31+
// CHECK: sil [serialized] @_T0s28userOfSemanticsAnnotatedFuncSiSaySiGF
32+
// CHECK: function_ref
33+
// CHECK: apply
34+
@_inlineable
35+
public func userOfSemanticsAnnotatedFunc(_ a: Array<Int>) -> Int {
36+
return a._getCapacity()
37+
}

0 commit comments

Comments
 (0)