| //===--- PassPipeline.cpp - Swift Compiler SIL Pass Entrypoints -----------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2020 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 |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// This file provides implementations of a few helper functions |
| /// which provide abstracted entrypoints to the SILPasses stage. |
| /// |
| /// \note The actual SIL passes should be implemented in per-pass source files, |
| /// not in this file. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #define DEBUG_TYPE "sil-passpipeline-plan" |
| #include "swift/SILOptimizer/PassManager/PassPipeline.h" |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/Module.h" |
| #include "swift/SIL/SILModule.h" |
| #include "swift/SILOptimizer/Analysis/Analysis.h" |
| #include "swift/SILOptimizer/PassManager/Passes.h" |
| #include "swift/SILOptimizer/PassManager/Transforms.h" |
| #include "swift/SILOptimizer/Utils/InstOptUtils.h" |
| #include "llvm/ADT/Statistic.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/ErrorOr.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/YAMLParser.h" |
| #include "llvm/Support/YAMLTraits.h" |
| |
| using namespace swift; |
| |
| static llvm::cl::opt<bool> |
| SILViewCFG("sil-view-cfg", llvm::cl::init(false), |
| llvm::cl::desc("Enable the sil cfg viewer pass")); |
| |
| static llvm::cl::opt<bool> SILViewGuaranteedCFG( |
| "sil-view-guaranteed-cfg", llvm::cl::init(false), |
| llvm::cl::desc("Enable the sil cfg viewer pass after diagnostics")); |
| |
| static llvm::cl::opt<bool> SILViewSILGenCFG( |
| "sil-view-silgen-cfg", llvm::cl::init(false), |
| llvm::cl::desc("Enable the sil cfg viewer pass before diagnostics")); |
| |
| //===----------------------------------------------------------------------===// |
| // Diagnostic Pass Pipeline |
| //===----------------------------------------------------------------------===// |
| |
| static void addCFGPrinterPipeline(SILPassPipelinePlan &P, StringRef Name) { |
| P.startPipeline(Name); |
| P.addCFGPrinter(); |
| } |
| |
| static void addMandatoryDebugSerialization(SILPassPipelinePlan &P) { |
| P.startPipeline("Mandatory Debug Serialization"); |
| P.addOwnershipModelEliminator(); |
| P.addMandatoryInlining(); |
| } |
| |
| static void addOwnershipModelEliminatorPipeline(SILPassPipelinePlan &P) { |
| P.startPipeline("Ownership Model Eliminator"); |
| P.addOwnershipModelEliminator(); |
| } |
| |
| /// Passes for performing definite initialization. Must be run together in this |
| /// order. |
| static void addDefiniteInitialization(SILPassPipelinePlan &P) { |
| P.addDefiniteInitialization(); |
| P.addRawSILInstLowering(); |
| } |
| |
| // This pipeline defines a set of mandatory diagnostic passes and a set of |
| // supporting optimization passes that enable those diagnostics. These are run |
| // before any performance optimizations and in contrast to those optimizations |
| // _IS_ run when SourceKit emits diagnostics. |
| // |
| // Any passes not needed for diagnostic emission that need to run at -Onone |
| // should be in the -Onone pass pipeline and the prepare optimizations pipeline. |
| static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) { |
| P.startPipeline("Mandatory Diagnostic Passes + Enabling Optimization Passes"); |
| P.addSILGenCleanup(); |
| P.addDiagnoseInvalidEscapingCaptures(); |
| P.addDiagnoseStaticExclusivity(); |
| P.addNestedSemanticFunctionCheck(); |
| P.addCapturePromotion(); |
| |
| // Select access kind after capture promotion and before stack promotion. |
| // This guarantees that stack-promotable boxes have [static] enforcement. |
| P.addAccessEnforcementSelection(); |
| |
| P.addAllocBoxToStack(); |
| P.addNoReturnFolding(); |
| addDefiniteInitialization(P); |
| |
| // Automatic differentiation: canonicalize all differentiability witnesses |
| // and `differentiable_function` instructions. |
| P.addDifferentiation(); |
| |
| // Only run semantic arc opts if we are optimizing and if mandatory semantic |
| // arc opts is explicitly enabled. |
| // |
| // NOTE: Eventually this pass will be split into a mandatory/more aggressive |
| // pass. This will happen when OSSA is no longer eliminated before the |
| // optimizer pipeline is run implying we can put a pass that requires OSSA |
| // there. |
| const auto &Options = P.getOptions(); |
| P.addClosureLifetimeFixup(); |
| |
| #ifndef NDEBUG |
| // Add a verification pass to check our work when skipping |
| // function bodies. |
| if (Options.SkipFunctionBodies != FunctionBodySkipping::None) |
| P.addSILSkippingChecker(); |
| #endif |
| |
| if (Options.shouldOptimize()) { |
| P.addDestroyHoisting(); |
| } |
| P.addMandatoryInlining(); |
| P.addMandatorySILLinker(); |
| |
| // Promote loads as necessary to ensure we have enough SSA formation to emit |
| // SSA based diagnostics. |
| P.addPredictableMemoryAccessOptimizations(); |
| |
| // This phase performs optimizations necessary for correct interoperation of |
| // Swift os log APIs with C os_log ABIs. |
| // Pass dependencies: this pass depends on MandatoryInlining and Mandatory |
| // Linking happening before this pass and ConstantPropagation happening after |
| // this pass. |
| P.addOSLogOptimization(); |
| |
| // Diagnostic ConstantPropagation must be rerun on deserialized functions |
| // because it is sensitive to the assert configuration. |
| // Consequently, certain optimization passes beyond this point will also rerun. |
| P.addDiagnosticConstantPropagation(); |
| |
| // Now that we have emitted constant propagation diagnostics, try to eliminate |
| // dead allocations. |
| P.addPredictableDeadAllocationElimination(); |
| |
| P.addOptimizeHopToExecutor(); |
| |
| P.addDiagnoseUnreachable(); |
| P.addDiagnoseInfiniteRecursion(); |
| P.addYieldOnceCheck(); |
| P.addEmitDFDiagnostics(); |
| |
| // Canonical swift requires all non cond_br critical edges to be split. |
| P.addSplitNonCondBrCriticalEdges(); |
| } |
| |
| SILPassPipelinePlan |
| SILPassPipelinePlan::getDiagnosticPassPipeline(const SILOptions &Options) { |
| SILPassPipelinePlan P(Options); |
| |
| if (SILViewSILGenCFG) { |
| addCFGPrinterPipeline(P, "SIL View SILGen CFG"); |
| } |
| |
| // If we are asked do debug serialization, instead of running all diagnostic |
| // passes, just run mandatory inlining with dead transparent function cleanup |
| // disabled. |
| if (Options.DebugSerialization) { |
| addMandatoryDebugSerialization(P); |
| return P; |
| } |
| |
| // Otherwise run the rest of diagnostics. |
| addMandatoryDiagnosticOptPipeline(P); |
| |
| if (SILViewGuaranteedCFG) { |
| addCFGPrinterPipeline(P, "SIL View Guaranteed CFG"); |
| } |
| return P; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Ownership Eliminator Pipeline |
| //===----------------------------------------------------------------------===// |
| |
| SILPassPipelinePlan SILPassPipelinePlan::getOwnershipEliminatorPassPipeline( |
| const SILOptions &Options) { |
| SILPassPipelinePlan P(Options); |
| addOwnershipModelEliminatorPipeline(P); |
| return P; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Performance Pass Pipeline |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| // Enumerates the optimization kinds that we do in SIL. |
| enum OptimizationLevelKind { |
| LowLevel, |
| MidLevel, |
| HighLevel, |
| }; |
| |
| } // end anonymous namespace |
| |
| void addSimplifyCFGSILCombinePasses(SILPassPipelinePlan &P) { |
| P.addSimplifyCFG(); |
| P.addConditionForwarding(); |
| // Jump threading can expose opportunity for silcombine (enum -> is_enum_tag-> |
| // cond_br). |
| P.addSILCombine(); |
| // Which can expose opportunity for simplifcfg. |
| P.addSimplifyCFG(); |
| } |
| |
| /// Perform semantic annotation/loop base optimizations. |
| void addHighLevelLoopOptPasses(SILPassPipelinePlan &P) { |
| // Perform classic SSA optimizations for cleanup. |
| P.addLowerAggregateInstrs(); |
| P.addSILCombine(); |
| P.addEarlySROA(); |
| P.addMem2Reg(); |
| P.addDCE(); |
| P.addSILCombine(); |
| addSimplifyCFGSILCombinePasses(P); |
| |
| // Run high-level loop opts. |
| P.addLoopRotate(); |
| |
| // Cleanup. |
| P.addDCE(); |
| // Also CSE semantic calls. |
| P.addHighLevelCSE(); |
| P.addSILCombine(); |
| P.addSimplifyCFG(); |
| // Optimize access markers for better LICM: might merge accesses |
| // It will also set the no_nested_conflict for dynamic accesses |
| P.addAccessEnforcementReleaseSinking(); |
| P.addAccessEnforcementOpts(); |
| P.addHighLevelLICM(); |
| // Simplify CFG after LICM that creates new exit blocks |
| P.addSimplifyCFG(); |
| // LICM might have added new merging potential by hoisting |
| // we don't want to restart the pipeline - ignore the |
| // potential of merging out of two loops |
| P.addAccessEnforcementReleaseSinking(); |
| P.addAccessEnforcementOpts(); |
| // Start of loop unrolling passes. |
| P.addArrayCountPropagation(); |
| // To simplify induction variable. |
| P.addSILCombine(); |
| P.addLoopUnroll(); |
| P.addSimplifyCFG(); |
| P.addPerformanceConstantPropagation(); |
| P.addSimplifyCFG(); |
| P.addArrayElementPropagation(); |
| // End of unrolling passes. |
| P.addABCOpt(); |
| // Cleanup. |
| P.addDCE(); |
| P.addCOWArrayOpts(); |
| // Cleanup. |
| P.addDCE(); |
| P.addSwiftArrayPropertyOpt(); |
| } |
| |
| // 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(); |
| |
| // Propagate copies through stack locations. Should run after |
| // box-to-stack promotion since it is limited to propagating through |
| // stack locations. Should run before aggregate lowering since that |
| // splits up copy_addr. |
| P.addCopyForwarding(); |
| |
| // Optimize copies from a temporary (an "l-value") to a destination. |
| P.addTempLValueOpt(); |
| |
| // Split up opaque operations (copy_addr, retain_value, etc.). |
| P.addLowerAggregateInstrs(); |
| |
| // Split up operations on stack-allocated aggregates (struct, tuple). |
| if (OpLevel == OptimizationLevelKind::HighLevel) { |
| P.addEarlySROA(); |
| } else { |
| P.addSROA(); |
| } |
| |
| // Promote stack allocations to values. |
| P.addMem2Reg(); |
| |
| // We earlier eliminated ownership if we are not compiling the stdlib. Now |
| // handle the stdlib functions, re-simplifying, eliminating ARC as we do. |
| P.addSemanticARCOpts(); |
| P.addNonTransparentFunctionOwnershipModelEliminator(); |
| |
| // Run the existential specializer Pass. |
| P.addExistentialSpecializer(); |
| |
| // 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 |
| // 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 |
| // current function (after optimizing any new callees). |
| P.addDevirtualizer(); |
| P.addGenericSpecializer(); |
| // Run devirtualizer after the specializer, because many |
| // class_method/witness_method instructions may use concrete types now. |
| P.addDevirtualizer(); |
| |
| switch (OpLevel) { |
| case OptimizationLevelKind::HighLevel: |
| // Does not inline functions with defined semantics. |
| P.addEarlyInliner(); |
| break; |
| case OptimizationLevelKind::MidLevel: |
| // Does inline semantics-functions (except "availability"), but not |
| // global-init functions. |
| P.addPerfInliner(); |
| break; |
| case OptimizationLevelKind::LowLevel: |
| // Inlines everything |
| P.addLateInliner(); |
| break; |
| } |
| |
| // Promote stack allocations to values and eliminate redundant |
| // loads. |
| P.addMem2Reg(); |
| P.addPerformanceConstantPropagation(); |
| // Do a round of CFG simplification, followed by peepholes, then |
| // more CFG simplification. |
| |
| // Jump threading can expose opportunity for SILCombine (enum -> is_enum_tag-> |
| // cond_br). |
| P.addJumpThreadSimplifyCFG(); |
| P.addPhiExpansion(); |
| P.addSILCombine(); |
| // SILCombine can expose further opportunities for SimplifyCFG. |
| P.addSimplifyCFG(); |
| |
| P.addCSE(); |
| if (OpLevel == OptimizationLevelKind::HighLevel) { |
| // Early RLE does not touch loads from Arrays. This is important because |
| // later array optimizations, like ABCOpt, get confused if an array load in |
| // a loop is converted to a pattern with a phi argument. |
| P.addEarlyRedundantLoadElimination(); |
| } else { |
| P.addRedundantLoadElimination(); |
| } |
| |
| P.addCOWOpts(); |
| P.addPerformanceConstantPropagation(); |
| // Remove redundant arguments right before CSE and DCE, so that CSE and DCE |
| // can cleanup redundant and dead instructions. |
| P.addRedundantPhiElimination(); |
| P.addCSE(); |
| P.addDCE(); |
| |
| // Perform retain/release code motion and run the first ARC optimizer. |
| P.addEarlyCodeMotion(); |
| P.addReleaseHoisting(); |
| P.addARCSequenceOpts(); |
| P.addTempRValueOpt(); |
| |
| P.addSimplifyCFG(); |
| if (OpLevel == OptimizationLevelKind::LowLevel) { |
| // Remove retain/releases based on Builtin.unsafeGuaranteed |
| P.addUnsafeGuaranteedPeephole(); |
| // Only hoist releases very late. |
| P.addLateCodeMotion(); |
| } else |
| P.addEarlyCodeMotion(); |
| |
| P.addRetainSinking(); |
| // Retain sinking does not sink all retains in one round. |
| // Let it run one more time time, because it can be beneficial. |
| // FIXME: Improve the RetainSinking pass to sink more/all |
| // retains in one go. |
| P.addRetainSinking(); |
| P.addReleaseHoisting(); |
| P.addARCSequenceOpts(); |
| } |
| |
| static void addPerfDebugSerializationPipeline(SILPassPipelinePlan &P) { |
| P.startPipeline("Performance Debug Serialization"); |
| P.addPerformanceSILLinker(); |
| } |
| |
| |
| static void addPrepareOptimizationsPipeline(SILPassPipelinePlan &P) { |
| P.startPipeline("PrepareOptimizationPasses"); |
| |
| // Verify AccessedStorage once in OSSA before optimizing. |
| #ifndef NDEBUG |
| P.addAccessPathVerification(); |
| #endif |
| |
| P.addForEachLoopUnroll(); |
| P.addMandatoryCombine(); |
| P.addAccessMarkerElimination(); |
| } |
| |
| static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) { |
| P.startPipeline("EarlyModulePasses"); |
| |
| // Get rid of apparently dead functions as soon as possible so that |
| // we do not spend time optimizing them. |
| P.addDeadFunctionElimination(); |
| |
| // Cleanup after SILGen: remove trivial copies to temporaries. |
| P.addTempRValueOpt(); |
| // Cleanup after SILGen: remove unneeded borrows/copies. |
| P.addSemanticARCOpts(); |
| |
| // Devirtualizes differentiability witnesses into functions that reference them. |
| // This unblocks many other passes' optimizations (e.g. inlining) and this is |
| // not blocked by any other passes' optimizations, so do it early. |
| P.addDifferentiabilityWitnessDevirtualizer(); |
| |
| // Strip ownership from non-transparent functions when we are not compiling |
| // the stdlib module. When compiling the stdlib, we eliminate ownership on |
| // these functions later with a nromal call to |
| // P.addNonTransparentFunctionOwnershipModelEliminator(). |
| // |
| // This is done so we can push ownership through the pass pipeline first for |
| // the stdlib and then everything else. |
| if (P.getOptions().StopOptimizationBeforeLoweringOwnership) |
| return; |
| |
| P.addNonStdlibNonTransparentFunctionOwnershipModelEliminator(); |
| |
| // Start by linking in referenced functions from other modules. |
| P.addPerformanceSILLinker(); |
| |
| // Cleanup after SILGen: remove trivial copies to temporaries. This version of |
| // temp-rvalue opt is here so that we can hit copies from non-ossa code that |
| // is linked in from the stdlib. |
| P.addTempRValueOpt(); |
| |
| // Needed to serialize static initializers of globals for cross-module |
| // optimization. |
| P.addGlobalOpt(); |
| |
| // Add the outliner pass (Osize). |
| P.addOutliner(); |
| |
| P.addCrossModuleSerializationSetup(); |
| |
| // In case of cross-module-optimization, we need to serialize right after |
| // CrossModuleSerializationSetup. Eventually we want to serialize early |
| // anyway, but for now keep the SerializeSILPass at the later stage of the |
| // pipeline in case cross-module-optimization is not enabled. |
| P.addCMOSerializeSILPass(); |
| } |
| |
| // 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(); |
| |
| // stdlib ownership model elimination is done within addFunctionPasses |
| addFunctionPasses(P, OptimizationLevelKind::HighLevel); |
| |
| addHighLevelLoopOptPasses(P); |
| |
| P.addStringOptimization(); |
| } |
| |
| // 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 before serialization. |
| // |
| // FIXME: why does StackPromotion need to run in the module pipeline? |
| P.addStackPromotion(); |
| |
| P.addGlobalOpt(); |
| P.addLetPropertiesOpt(); |
| } |
| |
| 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. |
| 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. |
| P.addDeadArgSignatureOpt(); |
| |
| // A LICM pass at mid-level is mainly needed to hoist addressors of globals. |
| // It needs to be before global_init functions are inlined. |
| P.addLICM(); |
| // Run loop unrolling after inlining and constant propagation, because loop |
| // trip counts may have became constant. |
| P.addLICM(); |
| P.addLoopUnroll(); |
| } |
| |
| static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) { |
| P.startPipeline("ClosureSpecialize"); |
| P.addDeadFunctionElimination(); |
| P.addDeadStoreElimination(); |
| P.addDeadObjectElimination(); |
| |
| // These few passes are needed to cleanup between loop unrolling and GlobalOpt. |
| // This is needed to fully optimize static small String constants. |
| P.addSimplifyCFG(); |
| P.addSILCombine(); |
| P.addPerformanceConstantPropagation(); |
| P.addSimplifyCFG(); |
| |
| // Hoist globals out of loops. |
| // Global-init functions should not be inlined GlobalOpt is done. |
| P.addGlobalOpt(); |
| P.addLetPropertiesOpt(); |
| |
| // Propagate constants into closures and convert to static dispatch. This |
| // should run after specialization and inlining because we don't want to |
| // specialize a call that can be inlined. It should run before |
| // ClosureSpecialization, because constant propagation is more effective. At |
| // least one round of SSA optimization and inlining should run after this to |
| // take advantage of static dispatch. |
| P.addCapturePropagation(); |
| |
| // Specialize closure. |
| P.addClosureSpecializer(); |
| |
| // Do the second stack promotion on low-level SIL. |
| P.addStackPromotion(); |
| |
| // Speculate virtual call targets. |
| if (P.getOptions().EnableSpeculativeDevirtualization) { |
| P.addSpeculativeDevirtualization(); |
| } |
| |
| // There should be at least one SILCombine+SimplifyCFG between the |
| // ClosureSpecializer, etc. and the last inliner. Cleaning up after these |
| // passes can expose more inlining opportunities. |
| addSimplifyCFGSILCombinePasses(P); |
| |
| // We do this late since it is a pass like the inline caches that we only want |
| // to run once very late. Make sure to run at least one round of the ARC |
| // optimizer after this. |
| } |
| |
| static void addLowLevelPassPipeline(SILPassPipelinePlan &P) { |
| P.startPipeline("LowLevel,Function", true /*isFunctionPassPipeline*/); |
| |
| // Should be after FunctionSignatureOpts and before the last inliner. |
| P.addReleaseDevirtualizer(); |
| |
| addFunctionPasses(P, OptimizationLevelKind::LowLevel); |
| |
| P.addDeadObjectElimination(); |
| P.addObjectOutliner(); |
| P.addDeadStoreElimination(); |
| |
| // We've done a lot of optimizations on this function, attempt to FSO. |
| P.addFunctionSignatureOpts(); |
| } |
| |
| static void addLateLoopOptPassPipeline(SILPassPipelinePlan &P) { |
| P.startPipeline("LateLoopOpt"); |
| |
| // Delete dead code and drop the bodies of shared functions. |
| // Also, remove externally available witness tables. They are not needed |
| // anymore after the last devirtualizer run. |
| P.addLateDeadFunctionElimination(); |
| |
| // Perform the final lowering transformations. |
| P.addCodeSinking(); |
| // Optimize access markers for better LICM: might merge accesses |
| // It will also set the no_nested_conflict for dynamic accesses |
| P.addAccessEnforcementReleaseSinking(); |
| P.addAccessEnforcementOpts(); |
| P.addLICM(); |
| P.addCOWOpts(); |
| // Simplify CFG after LICM that creates new exit blocks |
| P.addSimplifyCFG(); |
| // LICM might have added new merging potential by hoisting |
| // we don't want to restart the pipeline - ignore the |
| // potential of merging out of two loops |
| P.addAccessEnforcementReleaseSinking(); |
| P.addAccessEnforcementOpts(); |
| |
| // Sometimes stack promotion can catch cases only at this late stage of the |
| // pipeline, after FunctionSignatureOpts. |
| P.addStackPromotion(); |
| |
| // Optimize overflow checks. |
| P.addRedundantOverflowCheckRemoval(); |
| P.addMergeCondFails(); |
| |
| // Remove dead code. |
| P.addDCE(); |
| P.addSILCombine(); |
| P.addSimplifyCFG(); |
| |
| // Try to hoist all releases, including epilogue releases. This should be |
| // after FSO. |
| P.addLateReleaseHoisting(); |
| } |
| |
| // Run passes that |
| // - should only run after all general SIL transformations. |
| // - have no reason to run before any other SIL optimizations. |
| // - don't require IRGen information. |
| static void addLastChanceOptPassPipeline(SILPassPipelinePlan &P) { |
| // Optimize access markers for improved IRGen after all other optimizations. |
| P.addAccessEnforcementReleaseSinking(); |
| P.addAccessEnforcementOpts(); |
| P.addAccessEnforcementWMO(); |
| P.addAccessEnforcementDom(); |
| // addAccessEnforcementDom might provide potential for LICM: |
| // A loop might have only one dynamic access now, i.e. hoistable |
| P.addLICM(); |
| |
| // Verify AccessedStorage once again after optimizing and lowering OSSA. |
| #ifndef NDEBUG |
| P.addAccessPathVerification(); |
| #endif |
| |
| // Only has an effect if the -assume-single-thread option is specified. |
| P.addAssumeSingleThreaded(); |
| |
| // Only has an effect if opt-remark is enabled. |
| P.addOptRemarkGenerator(); |
| |
| P.addPruneVTables(); |
| } |
| |
| static void addSILDebugInfoGeneratorPipeline(SILPassPipelinePlan &P) { |
| P.startPipeline("SIL Debug Info Generator"); |
| P.addSILDebugInfoGenerator(); |
| } |
| |
| /// Mandatory IRGen preparation. It is the caller's job to set the set stage to |
| /// "lowered" after running this pipeline. |
| SILPassPipelinePlan |
| SILPassPipelinePlan::getLoweringPassPipeline(const SILOptions &Options) { |
| SILPassPipelinePlan P(Options); |
| P.startPipeline("Address Lowering"); |
| P.addOwnershipModelEliminator(); |
| P.addIRGenPrepare(); |
| P.addAddressLowering(); |
| |
| return P; |
| } |
| |
| SILPassPipelinePlan |
| SILPassPipelinePlan::getIRGenPreparePassPipeline(const SILOptions &Options) { |
| SILPassPipelinePlan P(Options); |
| P.startPipeline("IRGen Preparation"); |
| // Insert SIL passes to run during IRGen. |
| // Hoist generic alloc_stack instructions to the entry block to enable better |
| // llvm-ir generation for dynamic alloca instructions. |
| P.addAllocStackHoisting(); |
| if (Options.EnableLargeLoadableTypes) { |
| P.addLoadableByAddress(); |
| } |
| return P; |
| } |
| |
| SILPassPipelinePlan |
| SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) { |
| SILPassPipelinePlan P(Options); |
| |
| if (Options.DebugSerialization) { |
| addPerfDebugSerializationPipeline(P); |
| return P; |
| } |
| |
| // Passes which run once before all other optimizations run. Those passes are |
| // _not_ intended to run later again. |
| addPrepareOptimizationsPipeline(P); |
| |
| // Eliminate immediately dead functions and then clone functions from the |
| // stdlib. |
| // |
| // This also performs early OSSA based optimizations on *all* swift code. |
| addPerfEarlyModulePassPipeline(P); |
| |
| // Then if we were asked to stop optimization before lowering OSSA (causing us |
| // to exit early from addPerfEarlyModulePassPipeline), exit early. |
| if (P.getOptions().StopOptimizationBeforeLoweringOwnership) |
| return P; |
| |
| // Then run an iteration of the high-level SSA passes. |
| // |
| // FIXME: When *not* emitting a .swiftmodule, skip the high-level function |
| // pipeline to save compile time. |
| // |
| // NOTE: Ownership is now stripped within this function for the stdlib. |
| addHighLevelFunctionPipeline(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); |
| |
| // Run another iteration of the SSA optimizations to optimize the |
| // devirtualized inline caches and constants propagated into closures |
| // (CapturePropagation). |
| addLowLevelPassPipeline(P); |
| |
| addLateLoopOptPassPipeline(P); |
| |
| addLastChanceOptPassPipeline(P); |
| |
| // Has only an effect if the -gsil option is specified. |
| addSILDebugInfoGeneratorPipeline(P); |
| |
| // Call the CFG viewer. |
| if (SILViewCFG) { |
| addCFGPrinterPipeline(P, "SIL Before IRGen View CFG"); |
| } |
| |
| return P; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Onone Pass Pipeline |
| //===----------------------------------------------------------------------===// |
| |
| SILPassPipelinePlan |
| SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) { |
| SILPassPipelinePlan P(Options); |
| |
| // These are optimizations that we do not need to enable diagnostics (or |
| // depend on other passes needed for diagnostics). Thus we can run them later |
| // and avoid having SourceKit run these passes when just emitting diagnostics |
| // in the editor. |
| P.startPipeline("non-Diagnostic Enabling Mandatory Optimizations"); |
| P.addForEachLoopUnroll(); |
| P.addMandatoryCombine(); |
| P.addGuaranteedARCOpts(); |
| |
| // First serialize the SIL if we are asked to. |
| P.startPipeline("Serialization"); |
| P.addSerializeSILPass(); |
| |
| // Now strip any transparent functions that still have ownership. |
| P.addOwnershipModelEliminator(); |
| |
| // Finally perform some small transforms. |
| P.startPipeline("Rest of Onone"); |
| P.addUsePrespecialized(); |
| |
| // Has only an effect if the -assume-single-thread option is specified. |
| P.addAssumeSingleThreaded(); |
| |
| // Create pre-specializations. |
| P.addOnonePrespecializations(); |
| |
| // Has only an effect if the -gsil option is specified. |
| P.addSILDebugInfoGenerator(); |
| |
| return P; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Serialize SIL Pass Pipeline |
| //===----------------------------------------------------------------------===// |
| |
| // Add to P a new pipeline that just serializes SIL. Meant to be used in |
| // situations where perf optzns are disabled, but we may need to serialize. |
| SILPassPipelinePlan |
| SILPassPipelinePlan::getSerializeSILPassPipeline(const SILOptions &Options) { |
| SILPassPipelinePlan P(Options); |
| P.startPipeline("Serialize SIL"); |
| P.addSerializeSILPass(); |
| return P; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Inst Count Pass Pipeline |
| //===----------------------------------------------------------------------===// |
| |
| SILPassPipelinePlan |
| SILPassPipelinePlan::getInstCountPassPipeline(const SILOptions &Options) { |
| SILPassPipelinePlan P(Options); |
| P.startPipeline("Inst Count"); |
| P.addInstCount(); |
| return P; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Pass Kind List Pipeline |
| //===----------------------------------------------------------------------===// |
| |
| void SILPassPipelinePlan::addPasses(ArrayRef<PassKind> PassKinds) { |
| for (auto K : PassKinds) { |
| // We could add to the Kind list directly, but we want to allow for |
| // additional code to be added to add* without this code needing to be |
| // updated. |
| switch (K) { |
| // Each pass gets its own add-function. |
| #define PASS(ID, TAG, NAME) \ |
| case PassKind::ID: { \ |
| add##ID(); \ |
| break; \ |
| } |
| #include "swift/SILOptimizer/PassManager/Passes.def" |
| case PassKind::invalidPassKind: |
| llvm_unreachable("Unhandled pass kind?!"); |
| } |
| } |
| } |
| |
| SILPassPipelinePlan |
| SILPassPipelinePlan::getPassPipelineForKinds(const SILOptions &Options, |
| ArrayRef<PassKind> PassKinds) { |
| SILPassPipelinePlan P(Options); |
| P.startPipeline("Pass List Pipeline"); |
| P.addPasses(PassKinds); |
| return P; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Dumping And Loading Pass Pipelines from Yaml |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| struct YAMLPassPipeline { |
| std::string name; |
| std::vector<PassKind> passes; |
| |
| YAMLPassPipeline() {} |
| YAMLPassPipeline(const SILPassPipeline &pipeline, |
| SILPassPipelinePlan::PipelineKindRange pipelineKinds) |
| : name(pipeline.Name), passes() { |
| llvm::copy(pipelineKinds, std::back_inserter(passes)); |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| namespace llvm { |
| namespace yaml { |
| |
| template <> struct ScalarEnumerationTraits<PassKind> { |
| static void enumeration(IO &io, PassKind &value) { |
| #define PASS(ID, TAG, NAME) io.enumCase(value, #TAG, PassKind::ID); |
| #include "swift/SILOptimizer/PassManager/Passes.def" |
| } |
| }; |
| |
| template <> struct MappingTraits<YAMLPassPipeline> { |
| static void mapping(IO &io, YAMLPassPipeline &info) { |
| io.mapRequired("name", info.name); |
| io.mapRequired("passes", info.passes); |
| } |
| }; |
| |
| } // namespace yaml |
| } // namespace llvm |
| |
| LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(PassKind) |
| LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(YAMLPassPipeline) |
| |
| void SILPassPipelinePlan::dump() { |
| print(llvm::errs()); |
| llvm::errs() << '\n'; |
| } |
| |
| void SILPassPipelinePlan::print(llvm::raw_ostream &os) { |
| llvm::yaml::Output out(os); |
| std::vector<YAMLPassPipeline> data; |
| transform(getPipelines(), std::back_inserter(data), |
| [&](const SILPassPipeline &pipeline) { |
| return YAMLPassPipeline(pipeline, getPipelinePasses(pipeline)); |
| }); |
| out << data; |
| } |
| |
| SILPassPipelinePlan |
| SILPassPipelinePlan::getPassPipelineFromFile(const SILOptions &options, |
| StringRef filename) { |
| std::vector<YAMLPassPipeline> yamlPipelines; |
| { |
| // Load the input file. |
| llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileBufOrErr = |
| llvm::MemoryBuffer::getFileOrSTDIN(filename); |
| if (!fileBufOrErr) { |
| llvm_unreachable("Failed to read yaml file"); |
| } |
| |
| llvm::yaml::Input in(fileBufOrErr->get()->getBuffer()); |
| in >> yamlPipelines; |
| } |
| |
| SILPassPipelinePlan silPlan(options); |
| |
| for (auto &pipeline : yamlPipelines) { |
| silPlan.startPipeline(pipeline.name); |
| silPlan.addPasses(pipeline.passes); |
| } |
| |
| return silPlan; |
| } |