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) {