text: //===--- 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;
  }
  