diff --git a/include/swift/SILOptimizer/PassManager/PassManager.h b/include/swift/SILOptimizer/PassManager/PassManager.h index 0d4470c8a2f6d..15802da54a6bb 100644 --- a/include/swift/SILOptimizer/PassManager/PassManager.h +++ b/include/swift/SILOptimizer/PassManager/PassManager.h @@ -254,16 +254,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 3190e63503527..e855e5e031f3a 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -603,6 +603,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 f2d717f4f4f72..f3c4033c99e33 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(); @@ -432,30 +434,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) { - P.startPipeline("MidLevel"); - addSSAPasses(P, OptimizationLevelKind::MidLevel); - if (P.getOptions().StopOptimizationAfterSerialization) - return true; +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,Function", true /*isFunctionPassPipeline*/); + addFunctionPasses(P, OptimizationLevelKind::MidLevel); // Specialize partially applied functions with dead arguments as a preparation // for CapturePropagation. @@ -467,7 +497,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) { @@ -518,12 +547,12 @@ 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(); - addSSAPasses(P, OptimizationLevelKind::LowLevel); + addFunctionPasses(P, OptimizationLevelKind::LowLevel); P.addDeadObjectElimination(); P.addObjectOutliner(); @@ -651,13 +680,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); @@ -697,7 +734,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(); diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index b13249ab7361d..154f933fd1f1a 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -3065,6 +3065,10 @@ bool SimplifyCFG::run() { LLVM_DEBUG(llvm::dbgs() << "### Run SimplifyCFG on " << Fn.getName() << '\n'); + //!!! + if (Fn.hasName("$s32sil_combine_concrete_existential29testWitnessReturnOptionalSelfAA2PP_pSgyF")) { + llvm::dbgs() << "TEST WITNESS SIMPLIFYCFG\n"; + } // Disable some expensive optimizations if the function is huge. isVeryLargeFunction = (Fn.size() > 10000);