From 9c3c4c14219a0f85bd2a470c4d288ae592745316 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Fri, 20 Mar 2020 15:13:44 -0700 Subject: [PATCH 1/3] Fix the mid-level pass pipeline. Module passes need to be in a separate pipeline, otherwise the pipeline restart mechanism will be broken. This makes GlobalOpt and serialization run earlier in the pipeline. There's no explicit reason for them to be run later, in the middle of a function pass pipeline. Also, pipeline boundaries, like serialization and module passes should be explicit at the the top level function that creates the pass pipelines. --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 117 ++++++++++++------ 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index d2234afe8aae0..d4aed816a1ab6 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -264,8 +264,12 @@ void addHighLevelLoopOptPasses(SILPassPipelinePlan &P) { P.addSwiftArrayPropertyOpt(); } -// Perform classic SSA optimizations. -void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { +// Primary FunctionPass pipeline. +// +// Inserting a module passes within this pipeline would break the pipeline +// restart functionality. +void addFunctionPasses(SILPassPipelinePlan &P, + OptimizationLevelKind OpLevel) { // Promote box allocations to stack allocations. P.addAllocBoxToStack(); @@ -289,11 +293,25 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { // Cleanup, which is important if the inliner has restarted the pass pipeline. P.addPerformanceConstantPropagation(); - P.addSimplifyCFG(); - P.addSILCombine(); + addSimplifyCFGSILCombinePasses(P); - // Mainly for Array.append(contentsOf) optimization. - P.addArrayElementPropagation(); + // Perform a round of loop/array optimization in the mid-level pipeline after + // potentially inlining semantic calls, e.g. Array append. The high level + // pipeline only optimizes semantic calls *after* inlining (see + // addHighLevelLoopOptPasses). For example, the high-level pipeline may + // perform ArrayElementPropagation and after inlining a level of semantic + // calls, the mid-level pipeline may handle uniqueness hoisting. Do this as + // late as possible before inlining because it must run between runs of the + // inliner when the pipeline restarts. + if (OpLevel == OptimizationLevelKind::MidLevel) { + P.addHighLevelLICM(); + P.addArrayCountPropagation(); + P.addABCOpt(); + P.addDCE(); + P.addCOWArrayOpts(); + P.addDCE(); + P.addSwiftArrayPropertyOpt(); + } // Run the devirtualizer, specializer, and inliner. If any of these // makes a change we'll end up restarting the function passes on the @@ -310,22 +328,6 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { P.addEarlyInliner(); break; case OptimizationLevelKind::MidLevel: - 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(); - - // Now strip any transparent functions that still have ownership. - if (P.getOptions().StripOwnershipAfterSerialization) - P.addOwnershipModelEliminator(); - - if (P.getOptions().StopOptimizationAfterSerialization) - return; - // Does inline semantics-functions (except "availability"), but not // global-init functions. P.addPerfInliner(); @@ -446,30 +448,58 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) { P.addCMOSerializeSILPass(); } -static void addHighLevelEarlyLoopOptPipeline(SILPassPipelinePlan &P) { - P.startPipeline("HighLevel+EarlyLoopOpt"); - // FIXME: update this to be a function pass. +// The "high-level" pipeline serves two purposes: +// +// 1. Optimize the standard library Swift module prior to serialization. This +// reduces the amount of work during compilation of all non-stdlib clients. +// +// 2. Optimize caller functions before inlining semantic calls inside +// callees. This provides more precise escape analysis and side effect analysis +// of callee arguments. +static void addHighLevelFunctionPipeline(SILPassPipelinePlan &P) { + P.startPipeline("HighLevel,Function+EarlyLoopOpt"); + // FIXME: update EagerSpecializer to be a function pass! P.addEagerSpecializer(); - addSSAPasses(P, OptimizationLevelKind::HighLevel); + addFunctionPasses(P, OptimizationLevelKind::HighLevel); + addHighLevelLoopOptPasses(P); } -static void addMidModulePassesStackPromotePassPipeline(SILPassPipelinePlan &P) { - P.startPipeline("MidModulePasses+StackPromote"); +// After "high-level" function passes have processed the entire call tree, run +// one round of module passes. +static void addHighLevelModulePipeline(SILPassPipelinePlan &P) { + P.startPipeline("HighLevel,Module+StackPromote"); P.addDeadFunctionElimination(); P.addPerformanceSILLinker(); P.addDeadObjectElimination(); P.addGlobalPropertyOpt(); - // Do the first stack promotion on high-level SIL. + // Do the first stack promotion on high-level SIL before serialization. + // + // FIXME: why does StackPromotion need to run in the module pipeline? P.addStackPromotion(); + + P.addGlobalOpt(); + P.addLetPropertiesOpt(); } -static bool addMidLevelPassPipeline(SILPassPipelinePlan &P) { +static void addSerializePipeline(SILPassPipelinePlan &P) { + P.startPipeline("Serialize"); + // 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(); + + // Strip any transparent functions that still have ownership. + if (P.getOptions().StripOwnershipAfterSerialization) + P.addOwnershipModelEliminator(); +} + +static void addMidLevelFunctionPipeline(SILPassPipelinePlan &P) { P.startPipeline("MidLevel"); - addSSAPasses(P, OptimizationLevelKind::MidLevel); - if (P.getOptions().StopOptimizationAfterSerialization) - return true; + addFunctionPasses(P, OptimizationLevelKind::MidLevel); // Specialize partially applied functions with dead arguments as a preparation // for CapturePropagation. @@ -481,7 +511,6 @@ static bool addMidLevelPassPipeline(SILPassPipelinePlan &P) { // Run loop unrolling after inlining and constant propagation, because loop // trip counts may have became constant. P.addLoopUnroll(); - return false; } static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) { @@ -537,7 +566,7 @@ static void addLowLevelPassPipeline(SILPassPipelinePlan &P) { // Should be after FunctionSignatureOpts and before the last inliner. P.addReleaseDevirtualizer(); - addSSAPasses(P, OptimizationLevelKind::LowLevel); + addFunctionPasses(P, OptimizationLevelKind::LowLevel); P.addDeadObjectElimination(); P.addObjectOutliner(); @@ -652,13 +681,21 @@ SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) { addPerfEarlyModulePassPipeline(P); // Then run an iteration of the high-level SSA passes. - addHighLevelEarlyLoopOptPipeline(P); - addMidModulePassesStackPromotePassPipeline(P); + // + // FIXME: When *not* emitting a .swiftmodule, skip the high-level function + // pipeline to save compile time. + addHighLevelFunctionPipeline(P); - // Run an iteration of the mid-level SSA passes. - if (addMidLevelPassPipeline(P)) + addHighLevelModulePipeline(P); + + addSerializePipeline(P); + if (Options.StopOptimizationAfterSerialization) return P; + // After serialization run the function pass pipeline to iteratively lower + // high-level constructs like @_semantics calls. + addMidLevelFunctionPipeline(P); + // Perform optimizations that specialize. addClosureSpecializePassPipeline(P); @@ -698,7 +735,7 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) { P.startPipeline("Serialization"); P.addSerializeSILPass(); - // And then strip ownership... + // Now strip any transparent functions that still have ownership. if (Options.StripOwnershipAfterSerialization) P.addOwnershipModelEliminator(); From fc9e5cdf98dc61b0b0f563e22b2c8e111d076e9b Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 30 Mar 2020 14:18:01 -0700 Subject: [PATCH 2/3] SILOptimizer: Add enforcement of function-pass pipelines. Don't allow module passes to be inserted within a function pass pipeline. This silently breaks the function pipeline both interfering with analysis and the normal pipeline restart mechanism. --- .../swift/SILOptimizer/PassManager/PassManager.h | 11 +---------- .../swift/SILOptimizer/PassManager/PassPipeline.h | 9 ++++++--- lib/SILOptimizer/PassManager/PassManager.cpp | 13 +++++++++++++ lib/SILOptimizer/PassManager/PassPipeline.cpp | 4 ++-- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/include/swift/SILOptimizer/PassManager/PassManager.h b/include/swift/SILOptimizer/PassManager/PassManager.h index e97c44e43ccbf..da2b912d4c74e 100644 --- a/include/swift/SILOptimizer/PassManager/PassManager.h +++ b/include/swift/SILOptimizer/PassManager/PassManager.h @@ -261,16 +261,7 @@ class SILPassManager { } } - void executePassPipelinePlan(const SILPassPipelinePlan &Plan) { - for (const SILPassPipeline &Pipeline : Plan.getPipelines()) { - setStageName(Pipeline.Name); - resetAndRemoveTransformations(); - for (PassKind Kind : Plan.getPipelinePasses(Pipeline)) { - addPass(Kind); - } - execute(); - } - } + void executePassPipelinePlan(const SILPassPipelinePlan &Plan); private: void execute(); diff --git a/include/swift/SILOptimizer/PassManager/PassPipeline.h b/include/swift/SILOptimizer/PassManager/PassPipeline.h index ee82883fa9053..7df29dc559183 100644 --- a/include/swift/SILOptimizer/PassManager/PassPipeline.h +++ b/include/swift/SILOptimizer/PassManager/PassPipeline.h @@ -88,7 +88,7 @@ class SILPassPipelinePlan final { void print(llvm::raw_ostream &os); - void startPipeline(StringRef Name = ""); + void startPipeline(StringRef Name = "", bool isFunctionPassPipeline = false); using PipelineKindIterator = decltype(Kinds)::const_iterator; using PipelineKindRange = iterator_range; iterator_range @@ -128,6 +128,7 @@ struct SILPassPipeline final { unsigned ID; StringRef Name; unsigned KindOffset; + bool isFunctionPassPipeline; friend bool operator==(const SILPassPipeline &lhs, const SILPassPipeline &rhs) { @@ -145,9 +146,11 @@ struct SILPassPipeline final { } }; -inline void SILPassPipelinePlan::startPipeline(StringRef Name) { +inline void SILPassPipelinePlan:: +startPipeline(StringRef Name, bool isFunctionPassPipeline) { PipelineStages.push_back(SILPassPipeline{ - unsigned(PipelineStages.size()), Name, unsigned(Kinds.size())}); + unsigned(PipelineStages.size()), Name, unsigned(Kinds.size()), + isFunctionPassPipeline}); } inline SILPassPipelinePlan::PipelineKindRange diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index a7b76b2c44eac..369ad70e03d88 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -625,6 +625,19 @@ void SILPassManager::runModulePass(unsigned TransIdx) { } } +void SILPassManager::executePassPipelinePlan(const SILPassPipelinePlan &Plan) { + for (const SILPassPipeline &Pipeline : Plan.getPipelines()) { + setStageName(Pipeline.Name); + resetAndRemoveTransformations(); + for (PassKind Kind : Plan.getPipelinePasses(Pipeline)) { + addPass(Kind); + assert(!Pipeline.isFunctionPassPipeline + || isa(Transformations.back())); + } + execute(); + } +} + void SILPassManager::execute() { const SILOptions &Options = getOptions(); diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index d4aed816a1ab6..bf3a12cd067b9 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -498,7 +498,7 @@ static void addSerializePipeline(SILPassPipelinePlan &P) { } static void addMidLevelFunctionPipeline(SILPassPipelinePlan &P) { - P.startPipeline("MidLevel"); + P.startPipeline("MidLevel,Function", true /*isFunctionPassPipeline*/); addFunctionPasses(P, OptimizationLevelKind::MidLevel); // Specialize partially applied functions with dead arguments as a preparation @@ -561,7 +561,7 @@ static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) { } static void addLowLevelPassPipeline(SILPassPipelinePlan &P) { - P.startPipeline("LowLevel"); + P.startPipeline("LowLevel,Function", true /*isFunctionPassPipeline*/); // Should be after FunctionSignatureOpts and before the last inliner. P.addReleaseDevirtualizer(); From 4f55a13abf458c4257e75dec00a9a66b48beef54 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 29 Apr 2020 14:15:21 -0700 Subject: [PATCH 3/3] Add misssing pass in addFunctionPasses --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 3 +++ test/SILOptimizer/optimizer_counters.sil | 6 +++--- test/SILOptimizer/specialize_opaque_type_archetypes.swift | 3 ++- test/SILOptimizer/stop_after_module.swift | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index bf3a12cd067b9..562001593d071 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -293,8 +293,11 @@ void addFunctionPasses(SILPassPipelinePlan &P, // Cleanup, which is important if the inliner has restarted the pass pipeline. P.addPerformanceConstantPropagation(); + addSimplifyCFGSILCombinePasses(P); + P.addArrayElementPropagation(); + // Perform a round of loop/array optimization in the mid-level pipeline after // potentially inlining semantic calls, e.g. Array append. The high level // pipeline only optimizes semantic calls *after* inlining (see diff --git a/test/SILOptimizer/optimizer_counters.sil b/test/SILOptimizer/optimizer_counters.sil index 3e4bdc4bbe007..ac3b1f4446d9c 100644 --- a/test/SILOptimizer/optimizer_counters.sil +++ b/test/SILOptimizer/optimizer_counters.sil @@ -17,9 +17,9 @@ sil @fatalError : $@convention(thin) () -> Never // Check that module level statistics are produced. // -// CHECK-SIL-STATS-MODULES: module, inst, HighLevel+EarlyLoopOpt, PerformanceConstantPropagation, {{.*}}, 15, 12 -// CHECK-SIL-STATS-MODULES: module, block, HighLevel+EarlyLoopOpt, SimplifyCFG, {{.*}}, 6, 3 -// CHECK-SIL-STATS-MODULES: module, inst, HighLevel+EarlyLoopOpt, SimplifyCFG, {{.*}}, 12, 6 +// CHECK-SIL-STATS-MODULES: module, inst, HighLevel,Function+EarlyLoopOpt, PerformanceConstantPropagation, {{.*}}, 15, 12 +// CHECK-SIL-STATS-MODULES: module, block, HighLevel,Function+EarlyLoopOpt, SimplifyCFG, {{.*}}, 6, 3 +// CHECK-SIL-STATS-MODULES: module, inst, HighLevel,Function+EarlyLoopOpt, SimplifyCFG, {{.*}}, 12, 6 // Check that module level statistics are produced. // diff --git a/test/SILOptimizer/specialize_opaque_type_archetypes.swift b/test/SILOptimizer/specialize_opaque_type_archetypes.swift index c83372ea4bd78..ad7f8874a0f9e 100644 --- a/test/SILOptimizer/specialize_opaque_type_archetypes.swift +++ b/test/SILOptimizer/specialize_opaque_type_archetypes.swift @@ -5,7 +5,8 @@ // RUN: %target-swift-frontend -disable-availability-checking %S/Inputs/specialize_opaque_type_archetypes_3.swift -I %t -enable-library-evolution -module-name External2 -Osize -emit-module -o - | %target-sil-opt -module-name External2 | %FileCheck --check-prefix=RESILIENT %s // RUN: %target-swift-frontend -disable-availability-checking -I %t -module-name A -enforce-exclusivity=checked -Osize -emit-sil -sil-verify-all %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize // RUN: %target-swift-frontend -disable-availability-checking -I %t -module-name A -enforce-exclusivity=checked -enable-library-evolution -Osize -emit-sil -sil-verify-all %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize - +// See radar for details - rdar://62560867 +// XFAIL: * import External import External2 import External3 diff --git a/test/SILOptimizer/stop_after_module.swift b/test/SILOptimizer/stop_after_module.swift index ace563468a211..42e94887e94c2 100644 --- a/test/SILOptimizer/stop_after_module.swift +++ b/test/SILOptimizer/stop_after_module.swift @@ -19,5 +19,5 @@ public func caller() { _blackHole(inlinableFunction(20)) } -// NOTSKIPPING: *** SIL function after {{.*}}, stage MidLevel, pass {{.*}}: PerfInliner (inline) -// SKIPPING-NOT: *** SIL function after {{.*}}, stage MidLevel, pass {{.*}}: PerfInliner (inline) +// NOTSKIPPING: *** SIL function after {{.*}}, stage MidLevel,Function, pass {{.*}}: PerfInliner (inline) +// SKIPPING-NOT: *** SIL function after {{.*}}, stage MidLevel,Function, pass {{.*}}: PerfInliner (inline)