Merge pull request #12445 from slavapestov/di-self-consumed-analysis-volume-1
DI: self-consumed analysis rework, volume 1
diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h
index 0d29a30..c7d37c8 100644
--- a/include/swift/SIL/SILModule.h
+++ b/include/swift/SIL/SILModule.h
@@ -100,6 +100,7 @@
using DefaultWitnessTableListType = llvm::ilist<SILDefaultWitnessTable>;
using CoverageMapListType = llvm::ilist<SILCoverageMap>;
using LinkingMode = SILOptions::LinkingMode;
+ using ActionCallback = std::function<void()>;
private:
friend KeyPathPattern;
@@ -213,6 +214,13 @@
/// The options passed into this SILModule.
SILOptions &Options;
+ /// Set if the SILModule was serialized already. It is used
+ /// to ensure that the module is serialized only once.
+ bool serialized;
+
+ /// Action to be executed for serializing the SILModule.
+ ActionCallback SerializeSILAction;
+
/// A list of clients that need to be notified when an instruction
/// invalidation message is sent.
llvm::SetVector<DeleteNotificationHandler*> NotificationHandlers;
@@ -251,6 +259,17 @@
/// registered handlers. The order of handlers is deterministic but arbitrary.
void notifyDeleteHandlers(SILNode *node);
+ /// Set a serialization action.
+ void setSerializeSILAction(ActionCallback SerializeSILAction);
+ ActionCallback getSerializeSILAction() const;
+
+ /// Set a flag indicating that this module is serialized already.
+ void setSerialized() { serialized = true; }
+ bool isSerialized() const { return serialized; }
+
+ /// Serialize a SIL module using the configured SerializeSILAction.
+ void serialize();
+
/// \brief This converts Swift types to SILTypes.
mutable Lowering::TypeConverter Types;
diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def
index a76593c..d4068b2 100644
--- a/include/swift/SILOptimizer/PassManager/Passes.def
+++ b/include/swift/SILOptimizer/PassManager/Passes.def
@@ -269,6 +269,8 @@
"Temporary pass for staging in mark_uninitialized changes.")
PASS(SimplifyUnreachableContainingBlocks, "simplify-unreachable-containing-blocks",
"Utility pass. Removes all non-term insts from blocks with unreachable terms")
+PASS(SerializeSILPass, "serialize-sil",
+ "Utility pass. Serializes the current SILModule")
PASS(BugReducerTester, "bug-reducer-tester",
"sil-bug-reducer Tool Testing by Asserting on a Sentinel Function")
PASS_RANGE(AllPasses, AADumper, BugReducerTester)
diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp
index 2f28d6b..dd04501 100644
--- a/lib/FrontendTool/FrontendTool.cpp
+++ b/lib/FrontendTool/FrontendTool.cpp
@@ -854,6 +854,46 @@
SM->verify();
}
+ // This is the action to be used to serialize SILModule.
+ // It may be invoked multiple times, but it will perform
+ // serialization only once. The serialization may either happen
+ // after high-level optimizations or after all optimizations are
+ // done, depending on the compiler setting.
+
+ auto SerializeSILModuleAction = [&]() {
+ if (!opts.ModuleOutputPath.empty() || !opts.ModuleDocOutputPath.empty()) {
+ auto DC = PrimarySourceFile ? ModuleOrSourceFile(PrimarySourceFile)
+ : Instance.getMainModule();
+ if (!opts.ModuleOutputPath.empty()) {
+ SerializationOptions serializationOpts;
+ serializationOpts.OutputPath = opts.ModuleOutputPath.c_str();
+ serializationOpts.DocOutputPath = opts.ModuleDocOutputPath.c_str();
+ serializationOpts.GroupInfoPath = opts.GroupInfoPath.c_str();
+ if (opts.SerializeBridgingHeader)
+ serializationOpts.ImportedHeader = opts.ImplicitObjCHeaderPath;
+ serializationOpts.ModuleLinkName = opts.ModuleLinkName;
+ serializationOpts.ExtraClangOptions =
+ Invocation.getClangImporterOptions().ExtraArgs;
+ serializationOpts.EnableNestedTypeLookupTable =
+ opts.EnableSerializationNestedTypeLookupTable;
+ if (!IRGenOpts.ForceLoadSymbolName.empty())
+ serializationOpts.AutolinkForceLoad = true;
+
+ // Options contain information about the developer's computer,
+ // so only serialize them if the module isn't going to be shipped to
+ // the public.
+ serializationOpts.SerializeOptionsForDebugging =
+ !moduleIsPublic || opts.AlwaysSerializeDebuggingOptions;
+
+ serialize(DC, serializationOpts, SM.get());
+ }
+ }
+ };
+
+ // Set the serialization action, so that the SIL module
+ // can be serialized at any moment, e.g. during the optimization pipeline.
+ SM->setSerializeSILAction(SerializeSILModuleAction);
+
// Perform SIL optimization passes if optimizations haven't been disabled.
// These may change across compiler versions.
{
@@ -921,32 +961,9 @@
}
if (!opts.ModuleOutputPath.empty() || !opts.ModuleDocOutputPath.empty()) {
- auto DC = PrimarySourceFile ? ModuleOrSourceFile(PrimarySourceFile) :
- Instance.getMainModule();
- if (!opts.ModuleOutputPath.empty()) {
- SerializationOptions serializationOpts;
- serializationOpts.OutputPath = opts.ModuleOutputPath.c_str();
- serializationOpts.DocOutputPath = opts.ModuleDocOutputPath.c_str();
- serializationOpts.GroupInfoPath = opts.GroupInfoPath.c_str();
- if (opts.SerializeBridgingHeader)
- serializationOpts.ImportedHeader = opts.ImplicitObjCHeaderPath;
- serializationOpts.ModuleLinkName = opts.ModuleLinkName;
- serializationOpts.ExtraClangOptions =
- Invocation.getClangImporterOptions().ExtraArgs;
- serializationOpts.EnableNestedTypeLookupTable =
- opts.EnableSerializationNestedTypeLookupTable;
- if (!IRGenOpts.ForceLoadSymbolName.empty())
- serializationOpts.AutolinkForceLoad = true;
-
- // Options contain information about the developer's computer,
- // so only serialize them if the module isn't going to be shipped to
- // the public.
- serializationOpts.SerializeOptionsForDebugging =
- !moduleIsPublic || opts.AlwaysSerializeDebuggingOptions;
-
- serialize(DC, serializationOpts, SM.get());
- }
-
+ // Serialize the SILModule if it was not serialized yet.
+ if (!SM.get()->isSerialized())
+ SM.get()->serialize();
if (Action == FrontendOptions::MergeModules ||
Action == FrontendOptions::EmitModuleOnly) {
if (shouldIndex) {
diff --git a/lib/SIL/SILModule.cpp b/lib/SIL/SILModule.cpp
index 30015f3..bfe3bf6 100644
--- a/lib/SIL/SILModule.cpp
+++ b/lib/SIL/SILModule.cpp
@@ -87,7 +87,8 @@
const DeclContext *associatedDC, bool wholeModule)
: TheSwiftModule(SwiftModule), AssociatedDeclContext(associatedDC),
Stage(SILStage::Raw), Callback(new SILModule::SerializationCallback()),
- wholeModule(wholeModule), Options(Options), Types(*this) {}
+ wholeModule(wholeModule), Options(Options), serialized(false),
+ SerializeSILAction(), Types(*this) {}
SILModule::~SILModule() {
// Decrement ref count for each SILGlobalVariable with static initializers.
@@ -762,3 +763,20 @@
return getOptions().Optimization >= SILOptions::SILOptMode::Optimize &&
isOnoneSupportModule();
}
+
+void SILModule::setSerializeSILAction(SILModule::ActionCallback Action) {
+ assert(!SerializeSILAction && "Serialization action can be set only once");
+ SerializeSILAction = Action;
+}
+
+SILModule::ActionCallback SILModule::getSerializeSILAction() const {
+ return SerializeSILAction;
+}
+
+void SILModule::serialize() {
+ assert(SerializeSILAction && "Serialization action should be set");
+ assert(!isSerialized() && "The module was serialized already");
+ SerializeSILAction();
+ setSerialized();
+}
+
diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp
index 65a4691..8162028 100644
--- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp
+++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp
@@ -30,6 +30,20 @@
namespace {
+/// Returns true if a function should be SIL serialized or emitted by IRGen.
+static bool shouldBeSerializedOrEmitted(SILFunction *F) {
+ // public_external functions are never SIL serialized or emitted by IRGen.
+ if (F->isAvailableExternally() && hasPublicVisibility(F->getLinkage()) &&
+ !F->isTransparent())
+ return false;
+
+ // [serialized] functions should always be SIL serialized.
+ if (F->isSerialized())
+ return true;
+
+ return false;
+}
+
/// This is a base class for passes that are based on function liveness
/// computations like e.g. dead function elimination.
/// It provides a common logic for computing live (i.e. reachable) functions.
@@ -117,6 +131,11 @@
return true;
}
+ // Do not consider public_external functions that do not need to be emitted
+ // into the client as anchors.
+ if (shouldBeSerializedOrEmitted(F))
+ return true;
+
return false;
}
diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp
index 724c97e..408d88c 100644
--- a/lib/SILOptimizer/PassManager/PassPipeline.cpp
+++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp
@@ -243,10 +243,16 @@
P.addEarlyInliner();
break;
case OptimizationLevelKind::MidLevel:
- // Does inline semantics-functions (except "availability"), but not
- // global-init functions.
P.addGlobalOpt();
P.addLetPropertiesOpt();
+ // It is important to serialize before any of the @_semantics
+ // functions are inlined, because otherwise the information about
+ // uses of such functions inside the module is lost,
+ // which reduces the ability of the compiler to optimize clients
+ // importing this module.
+ P.addSerializeSILPass();
+ // Does inline semantics-functions (except "availability"), but not
+ // global-init functions.
P.addPerfInliner();
break;
case OptimizationLevelKind::LowLevel:
diff --git a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt
index bb7c7a2..93137a9 100644
--- a/lib/SILOptimizer/UtilityPasses/CMakeLists.txt
+++ b/lib/SILOptimizer/UtilityPasses/CMakeLists.txt
@@ -21,6 +21,7 @@
UtilityPasses/LoopRegionPrinter.cpp
UtilityPasses/MemBehaviorDumper.cpp
UtilityPasses/RCIdentityDumper.cpp
+ UtilityPasses/SerializeSILPass.cpp
UtilityPasses/SILDebugInfoGenerator.cpp
UtilityPasses/SideEffectsDumper.cpp
UtilityPasses/SimplifyUnreachableContainingBlocks.cpp
diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp
new file mode 100644
index 0000000..729aca2
--- /dev/null
+++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp
@@ -0,0 +1,53 @@
+//===--- SerializeSILPass.cpp ---------------------------------------------===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+
+#define DEBUG_TYPE "serialize-sil"
+#include "swift/Strings.h"
+#include "swift/SILOptimizer/PassManager/Passes.h"
+#include "swift/SILOptimizer/PassManager/Transforms.h"
+
+using namespace swift;
+
+/// A utility pass to serialize a SILModule at any place inside the optimization
+/// pipeline.
+class SerializeSILPass : public SILModuleTransform {
+ /// Removes [serialized] from all functions. This allows for more
+ /// optimizations and for a better dead function elimination.
+ void removeSerializedFlagFromAllFunctions(SILModule &M) {
+ for (auto &F : M) {
+ F.setSerialized(IsSerialized_t::IsNotSerialized);
+ }
+ }
+
+public:
+ SerializeSILPass() {}
+ void run() override {
+ auto &M = *getModule();
+ // Nothing to do if the module was serialized already.
+ if (M.isSerialized())
+ return;
+
+ // Mark all reachable functions as "anchors" so that they are not
+ // removed later by the dead function elimination pass. This
+ // is required, because clients may reference any of the
+ // serialized functions or anything referenced from them. Therefore,
+ // to avoid linker errors, the object file of the current module should
+ // contain all the symbols which were alive at the time of serialization.
+ DEBUG(llvm::dbgs() << "Serializing SILModule in SerializeSILPass\n");
+ getModule()->serialize();
+ removeSerializedFlagFromAllFunctions(M);
+ }
+};
+
+SILTransform *swift::createSerializeSILPass() {
+ return new SerializeSILPass();
+}
diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp
index 8149469..5aaab40 100644
--- a/lib/Sema/TypeCheckProtocol.cpp
+++ b/lib/Sema/TypeCheckProtocol.cpp
@@ -3991,7 +3991,7 @@
Type model = conformance->getType();
TypeSubstitutionMap substitutions = model->getMemberSubstitutions(witness);
if (substitutions.empty())
- return witness->getInterfaceType();
+ return witness->getInterfaceType()->getReferenceStorageReferent();
Type type = witness->getInterfaceType();
diff --git a/test/Generics/associated_types.swift b/test/Generics/associated_types.swift
index cf00935..1d62fd1 100644
--- a/test/Generics/associated_types.swift
+++ b/test/Generics/associated_types.swift
@@ -199,3 +199,25 @@
_ = B.A.isP
}
}
+
+// SR-6097
+protocol sr6097 {
+ associatedtype A : AnyObject
+ var aProperty: A { get }
+}
+
+class C1 {}
+class C2 : sr6097 {
+ unowned let aProperty: C1 // should deduce A == C1 despite 'unowned'
+ init() { fatalError() }
+}
+
+protocol sr6097_b {
+ associatedtype A : AnyObject
+ var aProperty: A? { get }
+}
+class C3 : sr6097_b {
+ weak var aProperty: C1? // and same here, despite 'weak'
+ init() { fatalError() }
+}
+
diff --git a/test/SILOptimizer/closure_specialize_fragile.sil b/test/SILOptimizer/closure_specialize_fragile.sil
new file mode 100644
index 0000000..17e24b3
--- /dev/null
+++ b/test/SILOptimizer/closure_specialize_fragile.sil
@@ -0,0 +1,60 @@
+// RUN: %target-sil-opt %s -verify -closure-specialize -assume-parsing-unqualified-ownership-sil -o - | %FileCheck %s
+
+// Make sure we do not specialize resilientCallee.
+
+sil_stage canonical
+
+import Builtin
+import Swift
+import SwiftShims
+
+@_semantics("optimize.sil.never") public func action()
+
+@inline(__always) public func fragileCaller()
+
+public func resilientCallee(fn: () -> ())
+
+// action()
+sil [_semantics "optimize.sil.never"] @_T026closure_specialize_fragile6actionyyF : $@convention(thin) () -> () {
+bb0:
+ %0 = tuple ()
+ return %0 : $()
+} // end sil function '_T026closure_specialize_fragile6actionyyF'
+
+// CHECK-LABEL: sil [serialized] [always_inline] @_T026closure_specialize_fragile0C6CalleryyF : $@convention(thin) () -> ()
+// CHECK: function_ref @_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
+// CHECK: return
+// fragileCaller()
+sil [serialized] [always_inline] @_T026closure_specialize_fragile0C6CalleryyF : $@convention(thin) () -> () {
+bb0:
+ // function_ref resilientCallee(fn:)
+ %0 = function_ref @_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
+ // function_ref closure #1 in fragileCaller()
+ %1 = function_ref @_T026closure_specialize_fragile0C6CalleryyFyycfU_ : $@convention(thin) () -> ()
+ %2 = thin_to_thick_function %1 : $@convention(thin) () -> () to $@callee_owned () -> ()
+ %3 = apply %0(%2) : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
+ %4 = tuple ()
+ return %4 : $()
+} // end sil function '_T026closure_specialize_fragile0C6CalleryyF'
+
+// CHECK-LABEL: sil @_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
+
+// resilientCallee(fn:)
+sil @_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF : $@convention(thin) (@owned @callee_owned () -> ()) -> () {
+bb0(%0 : $@callee_owned () -> ()):
+ strong_retain %0 : $@callee_owned () -> ()
+ %3 = apply %0() : $@callee_owned () -> ()
+ strong_release %0 : $@callee_owned () -> ()
+ %5 = tuple ()
+ return %5 : $()
+} // end sil function '_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF'
+
+// closure #1 in fragileCaller()
+sil shared [serialized] @_T026closure_specialize_fragile0C6CalleryyFyycfU_ : $@convention(thin) () -> () {
+bb0:
+ // function_ref action()
+ %0 = function_ref @_T026closure_specialize_fragile6actionyyF : $@convention(thin) () -> ()
+ %1 = apply %0() : $@convention(thin) () -> ()
+ %2 = tuple ()
+ return %2 : $()
+} // end sil function '_T026closure_specialize_fragile0C6CalleryyFyycfU_'
diff --git a/test/SILOptimizer/closure_specialize_fragile.swift b/test/SILOptimizer/closure_specialize_fragile.swift
deleted file mode 100644
index d02d96a..0000000
--- a/test/SILOptimizer/closure_specialize_fragile.swift
+++ /dev/null
@@ -1,19 +0,0 @@
-// RUN: %target-swift-frontend %s -emit-sil -O -o - -verify | %FileCheck %s
-
-// Make sure we do not specialize resilientCallee.
-
-// CHECK-LABEL: sil [serialized] [always_inline] @_T026closure_specialize_fragile0C6CalleryyF : $@convention(thin) () -> ()
-// CHECK: function_ref @_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
-// CHECK: return
-
-@inline(__always) public func fragileCaller() {
- resilientCallee {
- print("Hi")
- }
-}
-
-// CHECK-LABEL: sil @_T026closure_specialize_fragile15resilientCalleeyyyc2fn_tF : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
-
-public func resilientCallee(fn: () -> ()) {
- fn()
-}
diff --git a/test/SILOptimizer/globalopt_linkage.swift b/test/SILOptimizer/globalopt_linkage.swift
index fe506b3..5c1d2ab 100644
--- a/test/SILOptimizer/globalopt_linkage.swift
+++ b/test/SILOptimizer/globalopt_linkage.swift
@@ -17,7 +17,7 @@
// CHECK: sil hidden @{{.*}}testit
// CHECK: // MyStruct.StaticVar.getter
-// CHECK-NEXT: sil private [serialized] @_{{.*}}StaticVar
+// CHECK-NEXT: sil private @_{{.*}}StaticVar
// CHECK: // Global.getter
-// CHECK-NEXT: sil private [serialized] @_{{.*}}Global
+// CHECK-NEXT: sil private @_{{.*}}Global
diff --git a/test/SILOptimizer/let_properties_opts.swift b/test/SILOptimizer/let_properties_opts.swift
index c1754f0..4795a24 100644
--- a/test/SILOptimizer/let_properties_opts.swift
+++ b/test/SILOptimizer/let_properties_opts.swift
@@ -16,7 +16,7 @@
// initialization code could be removed.
// Specifically, the initialization code for Prop1, Prop2 and Prop3 can be removed.
-// CHECK-WMO-LABEL: sil [thunk] [always_inline] @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int32, @owned Foo) -> @owned Foo
+// CHECK-WMO-LABEL: sil @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int32, @owned Foo) -> @owned Foo
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop1
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop2
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop3
@@ -26,7 +26,7 @@
// CHECK-WMO: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop3
// CHECK-WMO: return
-// CHECK-WMO-LABEL: sil [thunk] [always_inline] @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int64, @owned Foo) -> @owned Foo
+// CHECK-WMO-LABEL: sil @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int64, @owned Foo) -> @owned Foo
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop1
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop2
// CHECK-WMO-NOT: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop3
@@ -41,14 +41,14 @@
// from other modules. Therefore the initialization code could be removed.
// Specifically, the initialization code for Prop2 can be removed.
-// CHECK-LABEL: sil [thunk] [always_inline] @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int32, @owned Foo) -> @owned Foo
+// CHECK-LABEL: sil @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int32, @owned Foo) -> @owned Foo
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop0
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop1
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop2
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop3
// CHECK: return
-// CHECK-LABEL: sil [thunk] [always_inline] @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int64, @owned Foo) -> @owned Foo
+// CHECK-LABEL: sil @_T019let_properties_opts3FooC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (Int64, @owned Foo) -> @owned Foo
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop0
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop1
// CHECK: ref_element_addr %{{[0-9]+}} : $Foo, #Foo.Prop2
diff --git a/test/SILOptimizer/outliner.swift b/test/SILOptimizer/outliner.swift
index 67e9569..76e6fe3 100644
--- a/test/SILOptimizer/outliner.swift
+++ b/test/SILOptimizer/outliner.swift
@@ -53,7 +53,7 @@
gizmo.doSomething(arr)
}
-// CHECK-LABEL: sil shared [serializable] [noinline] @_T0So5GizmoC14stringPropertySQySSGvgToTeab_ : $@convention(thin) (@in_guaranteed Gizmo) -> @owned Optional<String>
+// CHECK-LABEL: sil shared [noinline] @_T0So5GizmoC14stringPropertySQySSGvgToTeab_ : $@convention(thin) (@in_guaranteed Gizmo) -> @owned Optional<String>
// CHECK: bb0(%0 : $*Gizmo):
// CHECK: %1 = load %0 : $*Gizmo
// CHECK: %2 = objc_method %1 : $Gizmo, #Gizmo.stringProperty!getter.1.foreign : (Gizmo) -> () -> String!
@@ -71,7 +71,7 @@
// CHECK: bb3(%13 : $Optional<String>):
// CHECK: return %13 : $Optional<String>
-// CHECK-LABEL: sil shared [serializable] [noinline] @_T0So5GizmoC14stringPropertySQySSGvgToTepb_ : $@convention(thin) (Gizmo) -> @owned Optional<String>
+// CHECK-LABEL: sil shared [noinline] @_T0So5GizmoC14stringPropertySQySSGvgToTepb_ : $@convention(thin) (Gizmo) -> @owned Optional<String>
// CHECK: bb0(%0 : $Gizmo):
// CHECK: %1 = objc_method %0 : $Gizmo, #Gizmo.stringProperty!getter.1.foreign : (Gizmo) -> () -> String!
// CHECK: %2 = apply %1(%0) : $@convention(objc_method) (Gizmo) -> @autoreleased Optional<NSString>
@@ -88,7 +88,7 @@
// CHECK:bb3(%12 : $Optional<String>):
// CHECK: return %12 : $Optional<String>
-// CHECK-LABEL: sil shared [serializable] [noinline] @_T0So5GizmoC14stringPropertySQySSGvsToTembnn_ : $@convention(thin) (@owned String, Gizmo) -> () {
+// CHECK-LABEL: sil shared [noinline] @_T0So5GizmoC14stringPropertySQySSGvsToTembnn_ : $@convention(thin) (@owned String, Gizmo) -> () {
// CHECK: bb0(%0 : $String, %1 : $Gizmo):
// CHECK: %2 = objc_method %1 : $Gizmo, #Gizmo.stringProperty!setter.1.foreign : (Gizmo) -> (String!) -> ()
// CHECK: %3 = function_ref @_T0SS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF : $@convention(method) (@guaranteed String) -> @owned NSString
@@ -99,7 +99,7 @@
// CHECK: strong_release %4 : $NSString
// CHECK: return %7 : $()
-// CHECK-LABEL: sil shared [serializable] [noinline] @_T0So5GizmoC12modifyStringSQySSGAD_Si10withNumberSQyypG0D6FoobartFToTembnnnb_ : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String> {
+// CHECK-LABEL: sil shared [noinline] @_T0So5GizmoC12modifyStringSQySSGAD_Si10withNumberSQyypG0D6FoobartFToTembnnnb_ : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String> {
// CHECK: bb0(%0 : $String, %1 : $Int, %2 : $Optional<AnyObject>, %3 : $Gizmo):
// CHECK: %4 = objc_method %3 : $Gizmo, #Gizmo.modifyString!1.foreign : (Gizmo) -> (String!, Int, Any!) -> String!
// CHECK: %5 = function_ref @_T0SS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF : $@convention(method) (@guaranteed String) -> @owned NSString
@@ -124,7 +124,7 @@
// CHECK: bb3(%20 : $Optional<String>):
// CHECK: return %20 : $Optional<String>
-// CHECK-LABEL: sil shared [serializable] [noinline] @_T0So5GizmoC11doSomethingSQyypGSQySaySSGGFToTembnn_ : $@convention(thin) (@owned Array<String>, Gizmo) -> @owned Optional<AnyObject> {
+// CHECK-LABEL: sil shared [noinline] @_T0So5GizmoC11doSomethingSQyypGSQySaySSGGFToTembnn_ : $@convention(thin) (@owned Array<String>, Gizmo) -> @owned Optional<AnyObject> {
// CHECK: bb0(%0 : $Array<String>, %1 : $Gizmo):
// CHECK: %2 = objc_method %1 : $Gizmo, #Gizmo.doSomething!1.foreign : (Gizmo) -> ([String]!) -> Any!
// CHECK: %3 = function_ref @_T0Sa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF : $@convention(method) <{{.*}}> (@guaranteed Array<{{.*}}>) -> @owned NSArray
diff --git a/test/Serialization/early-serialization.swift b/test/Serialization/early-serialization.swift
new file mode 100644
index 0000000..7c20c86
--- /dev/null
+++ b/test/Serialization/early-serialization.swift
@@ -0,0 +1,37 @@
+// RUN: %empty-directory(%t)
+// 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
+// RUN: %target-sil-opt -enable-sil-verify-all %t/Swift.swiftmodule -o - | %FileCheck %s
+
+// Test that early serialization works as expected:
+// - it happens before the performance inlining and thus preserves @_semantics functions
+// - it happens after generic specialization
+
+public struct Int {
+ @_inlineable
+ public init() {}
+}
+
+public struct Array<T> {
+ @_inlineable
+ public init() {}
+
+ // Check that the generic version of a @_semantics function is preserved.
+ // CHECK: sil [serialized] [_semantics "array.get_capacity"] @_T0Sa12_getCapacitySiyF : $@convention(method) <T> (Array<T>) -> Int
+ // Check that a specialized version of a function is produced
+ // CHECK: sil shared [serializable] [_semantics "array.get_capacity"] @_T0Sa12_getCapacitySiyFSi_Tgq5 : $@convention(method) (Array<Int>) -> Int
+ @_inlineable
+ @_versioned
+ @_semantics("array.get_capacity")
+ internal func _getCapacity() -> Int {
+ return Int()
+ }
+}
+
+// Check that a call of a @_semantics function was not inlined if early-serialization is enabled.
+// CHECK: sil [serialized] @_T0s28userOfSemanticsAnnotatedFuncSiSaySiGF
+// CHECK: function_ref
+// CHECK: apply
+@_inlineable
+public func userOfSemanticsAnnotatedFunc(_ a: Array<Int>) -> Int {
+ return a._getCapacity()
+}
diff --git a/test/sil-opt/sil-opt.swift b/test/sil-opt/sil-opt.swift
index 4171261..c84b8a7 100644
--- a/test/sil-opt/sil-opt.swift
+++ b/test/sil-opt/sil-opt.swift
@@ -11,7 +11,7 @@
// CHECK-NEXT: @_inlineable init
// CHECK-NEXT: }
-// CHECK: sil @unknown : $@convention(thin) () -> ()
+// CHECK: sil{{.*}} @unknown : $@convention(thin) () -> ()
// CHECK-LABEL: sil [serialized] @_T0s1XV4testyyF : $@convention(method) (X) -> ()
// CHECK: bb0
diff --git a/tools/sil-opt/SILOpt.cpp b/tools/sil-opt/SILOpt.cpp
index 6d902ba..384607a 100644
--- a/tools/sil-opt/SILOpt.cpp
+++ b/tools/sil-opt/SILOpt.cpp
@@ -383,6 +383,9 @@
if (VerifyMode)
enableDiagnosticVerifier(CI.getSourceMgr());
+ if (CI.getSILModule())
+ CI.getSILModule()->setSerializeSILAction([]{});
+
if (OptimizationGroup == OptGroup::Diagnostics) {
runSILDiagnosticPasses(*CI.getSILModule());
} else if (OptimizationGroup == OptGroup::Performance) {