mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Refactor certain functions to make them simpler. and avoid calling AST.Type.loweredType, which can fail. Instead, access the types of the function's (SIL) arguments directly. Correctly handle exploding packs that contain generic or opaque types by using AST.Type.mapOutOfEnvironment(). @substituted types cause the shouldExplode predicate to be unreliable for AST types, so restrict it to just SIL.Type. Add test cases for functions that have @substituted types. Re-enable PackSpecialization in FunctionPass pipeline. Add a check to avoid emitting a destructure_tuple of the original function's return tuple when it is void/().
1273 lines
43 KiB
C++
1273 lines
43 KiB
C++
//===--- 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/AST/SILOptions.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
#include "swift/Basic/Assertions.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> SILViewCanonicalCFG(
|
|
"sil-view-canonical-cfg", llvm::cl::init(false),
|
|
llvm::cl::desc("Enable the sil cfg viewer pass after diagnostics"));
|
|
|
|
static llvm::cl::opt<bool> SILPrintCanonicalModule(
|
|
"sil-print-canonical-module", llvm::cl::init(false),
|
|
llvm::cl::desc("Print the textual SIL module after diagnostics"));
|
|
|
|
static llvm::cl::opt<bool> SILPrintFinalOSSAModule(
|
|
"sil-print-final-ossa-module", llvm::cl::init(false),
|
|
llvm::cl::desc("Print the textual SIL module before lowering from OSSA"));
|
|
|
|
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"));
|
|
|
|
static llvm::cl::opt<bool> SILPrintSILGenModule(
|
|
"sil-print-silgen-module", llvm::cl::init(false),
|
|
llvm::cl::desc("Enable printing the module after SILGen"));
|
|
|
|
static llvm::cl::opt<bool> SILPrintFinalModule(
|
|
"sil-print-final-module", llvm::cl::init(false),
|
|
llvm::cl::desc("Enable printing the module after all SIL passes"));
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Diagnostic Pass Pipeline
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void addCFGPrinterPipeline(SILPassPipelinePlan &P, StringRef Name) {
|
|
P.startPipeline(Name);
|
|
P.addCFGPrinter();
|
|
}
|
|
|
|
static void addModulePrinterPipeline(SILPassPipelinePlan &plan,
|
|
StringRef name) {
|
|
plan.startPipeline(name);
|
|
plan.addModulePrinter();
|
|
}
|
|
|
|
static void addMandatoryDebugSerialization(SILPassPipelinePlan &P) {
|
|
P.startPipeline("Mandatory Debug Serialization");
|
|
P.addAddressLowering();
|
|
P.addOwnershipModelEliminator();
|
|
P.addMandatoryInlining();
|
|
}
|
|
|
|
static void addOwnershipModelEliminatorPipeline(SILPassPipelinePlan &P) {
|
|
P.startPipeline("Ownership Model Eliminator");
|
|
P.addAddressLowering();
|
|
P.addOwnershipModelEliminator();
|
|
}
|
|
|
|
/// Passes for performing definite initialization. Must be run together in this
|
|
/// order.
|
|
static void addDefiniteInitialization(SILPassPipelinePlan &P) {
|
|
P.addDefiniteInitialization();
|
|
P.addLetPropertyLowering();
|
|
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.addDiagnoseInvalidEscapingCaptures();
|
|
P.addReferenceBindingTransform();
|
|
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();
|
|
|
|
#ifdef SWIFT_ENABLE_SWIFT_IN_SWIFT
|
|
P.addMandatoryAllocBoxToStack();
|
|
#else
|
|
P.addLegacyAllocBoxToStack();
|
|
#endif
|
|
// Needs to run after MandatoryAllocBoxToStack, because MandatoryAllocBoxToStack
|
|
// can convert dynamic accesses to static accesses.
|
|
P.addDiagnoseStaticExclusivity();
|
|
|
|
P.addNoReturnFolding();
|
|
P.addBooleanLiteralFolding();
|
|
addDefiniteInitialization(P);
|
|
|
|
P.addAddressLowering();
|
|
|
|
// TODO: remove this once CapturePromotion deletes specialized functions itself.
|
|
P.addDiagnosticDeadFunctionElimination();
|
|
|
|
P.addFlowIsolation();
|
|
|
|
//===---
|
|
// Passes that depend on region analysis information
|
|
//
|
|
|
|
P.addSendNonSendable();
|
|
|
|
// Now that we have completed running passes that use region analysis, clear
|
|
// region analysis and emit diagnostics for unnecessary preconcurrency
|
|
// imports.
|
|
P.addRegionAnalysisInvalidationTransform();
|
|
P.addDiagnoseUnnecessaryPreconcurrencyImports();
|
|
|
|
// Lower tuple addr constructor. Eventually this can be merged into later
|
|
// passes. This ensures we do not need to update later passes for something
|
|
// that is only needed by TransferNonSendable().
|
|
P.addLowerTupleAddrConstructor();
|
|
|
|
// Automatic differentiation: canonicalize all differentiability witnesses
|
|
// and `differentiable_function` instructions.
|
|
P.addDifferentiation();
|
|
|
|
const auto &Options = P.getOptions();
|
|
P.addClosureLifetimeFixup();
|
|
|
|
//===---
|
|
// Begin Ownership Optimizations
|
|
//
|
|
// These happen after ClosureLifetimeFixup because they depend on the
|
|
// resolution of nonescaping closure lifetimes to correctly check the use
|
|
// of move-only values as captures in nonescaping closures as borrows.
|
|
|
|
// Check noImplicitCopy and move only types for objects and addresses.
|
|
P.addMoveOnlyChecker();
|
|
|
|
// FIXME: rdar://122701694 (`consuming` keyword causes verification error on
|
|
// invalid SIL types)
|
|
//
|
|
// Lower move only wrapped trivial types.
|
|
// P.addTrivialMoveOnlyTypeEliminator();
|
|
|
|
// Check no uses after consume operator of a value in an address.
|
|
P.addConsumeOperatorCopyableAddressesChecker();
|
|
// No uses after consume operator of copyable value.
|
|
P.addConsumeOperatorCopyableValuesChecker();
|
|
|
|
// Check ~Escapable.
|
|
if (P.getOptions().EnableLifetimeDependenceDiagnostics) {
|
|
P.addLifetimeDependenceDiagnostics();
|
|
}
|
|
|
|
// As a temporary measure, we also eliminate move only for non-trivial types
|
|
// until we can audit the later part of the pipeline. Eventually, this should
|
|
// occur before IRGen.
|
|
P.addMoveOnlyTypeEliminator();
|
|
|
|
//
|
|
// End Ownership Optimizations
|
|
//===---
|
|
|
|
#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()) {
|
|
if (P.getOptions().DestroyHoisting == DestroyHoistingOption::On) {
|
|
P.addDestroyAddrHoisting();
|
|
}
|
|
}
|
|
P.addMandatoryInlining();
|
|
P.addMandatorySILLinker();
|
|
|
|
// Promote loads as necessary to ensure we have enough SSA formation to emit
|
|
// SSA based diagnostics.
|
|
P.addMandatoryRedundantLoadElimination();
|
|
|
|
// 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();
|
|
|
|
// Now that we have finished performing diagnostics that rely on lexical
|
|
// scopes, if lexical lifetimes are not enabled, eliminate lexical lifetimes.
|
|
if (Options.LexicalLifetimes != LexicalLifetimesOption::On) {
|
|
P.addLexicalLifetimeEliminator();
|
|
}
|
|
|
|
P.addOptimizeHopToExecutor();
|
|
|
|
// These diagnostic passes must run before OnoneSimplification because
|
|
// they rely on completely unoptimized SIL.
|
|
P.addDiagnoseUnreachable();
|
|
P.addDiagnoseInfiniteRecursion();
|
|
P.addYieldOnceCheck();
|
|
P.addEmitDFDiagnostics();
|
|
|
|
// Only issue weak lifetime warnings for users who select object lifetime
|
|
// optimization. The risk of spurious warnings outweighs the benefits.
|
|
if (P.getOptions().CopyPropagation >= CopyPropagationOption::Optimizing) {
|
|
P.addDiagnoseLifetimeIssues();
|
|
}
|
|
|
|
// Canonical swift requires all non cond_br critical edges to be split.
|
|
P.addSplitNonCondBrCriticalEdges();
|
|
|
|
P.addMandatoryPerformanceOptimizations();
|
|
P.addOnoneSimplification();
|
|
P.addInitializeStaticGlobals();
|
|
|
|
P.addMandatoryDestroyHoisting();
|
|
|
|
// MandatoryPerformanceOptimizations might create specializations that are not
|
|
// used, and by being unused they are might have unspecialized applies.
|
|
// Eliminate them via the DeadFunctionAndGlobalElimination in embedded Swift
|
|
// to avoid getting metadata/existential use errors in them. We don't want to
|
|
// run this pass in regular Swift: Even unused functions are expected to be
|
|
// available in debug (-Onone) builds for debugging and development purposes.
|
|
if (P.getOptions().EmbeddedSwift) {
|
|
P.addDeadFunctionAndGlobalElimination();
|
|
}
|
|
|
|
P.addDiagnoseUnknownConstValues();
|
|
P.addEmbeddedSwiftDiagnostics();
|
|
|
|
/// FIXME: Ideally, we'd have this relative order:
|
|
/// 1. DiagnoseLifetimeIssues
|
|
/// 2. CopyPropagation
|
|
/// 3. AddressLowering
|
|
/// to get the maximum benefits of CopyPropagation + OpaqueValues in -Onone.
|
|
if (P.getOptions().CopyPropagation == CopyPropagationOption::Always) {
|
|
// FIXME: ComputeSideEffects helps CopyPropagation simplify across
|
|
// call-sites, but PerformanceDiagnostics is sensitive to the # of copies.
|
|
// If ManualOwnership is used in the compiler itself, we wouldn't be able
|
|
// to bootstrap the compiler on different platforms with same diagnostics.
|
|
#ifdef SWIFT_ENABLE_SWIFT_IN_SWIFT
|
|
P.addComputeSideEffects();
|
|
#endif
|
|
P.addMandatoryCopyPropagation();
|
|
}
|
|
|
|
P.addPerformanceDiagnostics();
|
|
|
|
// Run inline(always) inlining at the end of the diagnostic pipeline.
|
|
// It is considered a diagnostics pass because it will diagnose cycles in
|
|
// inline(always) function calling.
|
|
P.addInlineAlwaysInlining();
|
|
}
|
|
|
|
SILPassPipelinePlan
|
|
SILPassPipelinePlan::getSILGenPassPipeline(const SILOptions &Options) {
|
|
SILPassPipelinePlan P(Options);
|
|
P.startPipeline("SILGen Passes");
|
|
|
|
P.addSILGenCleanup();
|
|
if (P.getOptions().EnableLifetimeDependenceDiagnostics) {
|
|
P.addLifetimeDependenceInsertion();
|
|
P.addLifetimeDependenceScopeFixup();
|
|
}
|
|
if (SILViewSILGenCFG) {
|
|
addCFGPrinterPipeline(P, "SIL View SILGen CFG");
|
|
}
|
|
if (SILPrintSILGenModule) {
|
|
addModulePrinterPipeline(P, "SIL Print SILGen Module");
|
|
}
|
|
return P;
|
|
}
|
|
|
|
SILPassPipelinePlan
|
|
SILPassPipelinePlan::getDiagnosticPassPipeline(const SILOptions &Options) {
|
|
SILPassPipelinePlan P(Options);
|
|
|
|
// 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 (SILViewCanonicalCFG) {
|
|
addCFGPrinterPipeline(P, "SIL View Canonical CFG");
|
|
}
|
|
if (SILPrintCanonicalModule) {
|
|
addModulePrinterPipeline(P, "SIL Print Canonical Module");
|
|
}
|
|
return P;
|
|
}
|
|
|
|
SILPassPipelinePlan SILPassPipelinePlan::getLowerHopToActorPassPipeline(
|
|
const SILOptions &Options) {
|
|
SILPassPipelinePlan P(Options);
|
|
P.startPipeline("Lower Hop to Actor");
|
|
P.addLowerHopToActor();
|
|
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 simplifycfg.
|
|
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
|
|
// AccessEnforcementReleaseSinking results in non-canonical OSSA.
|
|
// It is only used to expose opportunities in AccessEnforcementOpts
|
|
// before CanonicalOSSA re-hoists destroys.
|
|
P.addAccessEnforcementReleaseSinking();
|
|
P.addAccessEnforcementOpts();
|
|
P.addLoopInvariantCodeMotion();
|
|
// 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
|
|
// AccessEnforcementReleaseSinking results in non-canonical OSSA.
|
|
// It is only used to expose opportunities in AccessEnforcementOpts
|
|
// before CanonicalOSSA re-hoists destroys.
|
|
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();
|
|
// End of unrolling passes.
|
|
P.addBoundsCheckOpts();
|
|
// 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();
|
|
|
|
if (P.getOptions().DestroyHoisting == DestroyHoistingOption::On) {
|
|
P.addDestroyAddrHoisting();
|
|
}
|
|
|
|
// This DCE pass is the only DCE on ownership SIL. It can cleanup OSSA related
|
|
// dead code, e.g. left behind by the ObjCBridgingOptimization.
|
|
P.addDCE();
|
|
|
|
// Optimize copies from a temporary (an "l-value") to a destination.
|
|
P.addTempLValueElimination();
|
|
|
|
// 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();
|
|
|
|
// Run the existential specializer Pass.
|
|
if (!P.getOptions().EmbeddedSwift) {
|
|
// MandatoryPerformanceOptimizations already took care of all specializations
|
|
// in embedded Swift mode, running the existential specializer might introduce
|
|
// more generic calls from non-generic functions, which breaks the assumptions
|
|
// of embedded Swift.
|
|
P.addExistentialSpecializer();
|
|
}
|
|
|
|
// Cleanup, which is important if the inliner has restarted the pass pipeline.
|
|
P.addPerformanceConstantPropagation();
|
|
|
|
addSimplifyCFGSILCombinePasses(P);
|
|
|
|
// 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). 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.addLoopInvariantCodeMotion();
|
|
P.addArrayCountPropagation();
|
|
P.addBoundsCheckOpts();
|
|
P.addDCE();
|
|
P.addCOWArrayOpts();
|
|
P.addDCE();
|
|
P.addSwiftArrayPropertyOpt();
|
|
|
|
// This string optimization can catch additional opportunities, which are
|
|
// exposed once optimized String interpolations (from the high-level string
|
|
// optimization) are cleaned up. But before the mid-level inliner inlines
|
|
// semantic calls.
|
|
P.addStringOptimization();
|
|
}
|
|
|
|
// 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();
|
|
// MandatoryPerformanceOptimizations already took care of all specializations
|
|
// in embedded Swift mode, running the generic specializer might introduce
|
|
// more generic calls from non-generic functions, which breaks the assumptions
|
|
// of embedded Swift.
|
|
if (!P.getOptions().EmbeddedSwift) {
|
|
P.addGenericSpecializer();
|
|
P.addPackSpecialization();
|
|
// Run devirtualizer after the specializer, because many
|
|
// class_method/witness_method instructions may use concrete types now.
|
|
P.addDevirtualizer();
|
|
}
|
|
P.addARCSequenceOpts();
|
|
|
|
P.addDeinitDevirtualizer();
|
|
|
|
// We earlier eliminated ownership if we are not compiling the stdlib. Now
|
|
// handle the stdlib functions, re-simplifying, eliminating ARC as we do.
|
|
P.addDestroyHoisting();
|
|
if (P.getOptions().CopyPropagation != CopyPropagationOption::Off) {
|
|
P.addCopyPropagation();
|
|
}
|
|
P.addSemanticARCOpts();
|
|
P.addCopyToBorrowOptimization();
|
|
|
|
switch (OpLevel) {
|
|
case OptimizationLevelKind::HighLevel:
|
|
// Does not inline functions with defined semantics or effects.
|
|
P.addEarlyPerfInliner();
|
|
break;
|
|
case OptimizationLevelKind::MidLevel:
|
|
case OptimizationLevelKind::LowLevel:
|
|
// Inlines everything
|
|
P.addPerfInliner();
|
|
break;
|
|
}
|
|
|
|
// Clean up Semantic ARC before we perform additional post-inliner opts.
|
|
if (P.getOptions().CopyPropagation != CopyPropagationOption::Off) {
|
|
P.addCopyPropagation();
|
|
}
|
|
P.addSemanticARCOpts();
|
|
P.addCopyToBorrowOptimization();
|
|
|
|
// 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();
|
|
}
|
|
// Optimize copies created during RLE.
|
|
P.addSemanticARCOpts();
|
|
P.addCopyToBorrowOptimization();
|
|
|
|
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.addTempRValueElimination();
|
|
|
|
P.addSimplifyCFG();
|
|
if (OpLevel == OptimizationLevelKind::LowLevel) {
|
|
// 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();
|
|
|
|
// Run a final round of ARC opts when ownership is enabled.
|
|
P.addDestroyHoisting();
|
|
if (P.getOptions().CopyPropagation != CopyPropagationOption::Off) {
|
|
P.addCopyPropagation();
|
|
}
|
|
P.addSemanticARCOpts();
|
|
P.addCopyToBorrowOptimization();
|
|
}
|
|
|
|
static void addPerfDebugSerializationPipeline(SILPassPipelinePlan &P) {
|
|
P.startPipeline("Performance Debug Serialization");
|
|
P.addPerformanceSILLinker();
|
|
}
|
|
|
|
|
|
static void addPrepareOptimizationsPipeline(SILPassPipelinePlan &P) {
|
|
P.startPipeline("PrepareOptimizationPasses");
|
|
|
|
P.addForEachLoopUnroll();
|
|
P.addSimplification();
|
|
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.addDeadFunctionAndGlobalElimination();
|
|
|
|
// Cleanup after SILGen: remove trivial copies to temporaries.
|
|
P.addTempRValueElimination();
|
|
// Cleanup after SILGen: remove unneeded borrows/copies.
|
|
if (P.getOptions().CopyPropagation >= CopyPropagationOption::Optimizing) {
|
|
P.addComputeSideEffects();
|
|
P.addCopyPropagation();
|
|
}
|
|
P.addSemanticARCOpts();
|
|
P.addCopyToBorrowOptimization();
|
|
|
|
// 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();
|
|
|
|
// 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.addTempRValueElimination();
|
|
|
|
// Add the outliner pass (Osize).
|
|
P.addOutliner();
|
|
}
|
|
|
|
// 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",
|
|
true /*isFunctionPassPipeline*/);
|
|
|
|
// Skip EagerSpecializer on embedded Swift, which already specializes
|
|
// everything. Otherwise this would create metatype references for functions
|
|
// with @_specialize attribute and those are incompatible with Emebdded Swift.
|
|
if (!P.getOptions().EmbeddedSwift) {
|
|
P.addEagerSpecializer();
|
|
}
|
|
|
|
P.addObjCBridgingOptimization();
|
|
|
|
addFunctionPasses(P, OptimizationLevelKind::HighLevel);
|
|
|
|
addHighLevelLoopOptPasses(P);
|
|
|
|
P.addStringOptimization();
|
|
P.addComputeEscapeEffects();
|
|
P.addComputeSideEffects();
|
|
}
|
|
|
|
// 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.addDeadFunctionAndGlobalElimination();
|
|
P.addPerformanceSILLinker();
|
|
P.addDeadObjectElimination();
|
|
P.addGlobalPropertyOpt();
|
|
|
|
if (P.getOptions().EnableAsyncDemotion)
|
|
P.addAsyncDemotion();
|
|
|
|
// Do the first stack promotion on high-level SIL before serialization.
|
|
//
|
|
// FIXME: why does StackPromotion need to run in the module pipeline?
|
|
P.addComputeEscapeEffects();
|
|
P.addComputeSideEffects();
|
|
P.addStackPromotion();
|
|
|
|
P.addLetPropertiesOpt();
|
|
}
|
|
|
|
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.addLoopInvariantCodeMotion();
|
|
// Run loop unrolling after inlining and constant propagation, because loop
|
|
// trip counts may have became constant.
|
|
P.addLoopInvariantCodeMotion();
|
|
P.addLoopUnroll();
|
|
}
|
|
|
|
static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) {
|
|
P.startPipeline("ClosureSpecialize");
|
|
P.addDeadFunctionAndGlobalElimination();
|
|
P.addReadOnlyGlobalVariablesPass();
|
|
P.addDeadStoreElimination();
|
|
P.addDeadObjectElimination();
|
|
|
|
// These few passes are needed to cleanup between loop unrolling and InitializeStaticGlobals.
|
|
// This is needed to fully optimize static small String constants.
|
|
P.addSimplifyCFG();
|
|
P.addSILCombine();
|
|
P.addPerformanceConstantPropagation();
|
|
P.addSimplifyCFG();
|
|
P.addSimplification();
|
|
|
|
P.addInitializeStaticGlobals();
|
|
|
|
// ComputeEffects should be done at the end of a function-pipeline. The next
|
|
// pass (LetPropertiesOpt) is a module pass, so this is the end of a function-pipeline.
|
|
P.addComputeEscapeEffects();
|
|
P.addComputeSideEffects();
|
|
|
|
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.addConstantCapturePropagation();
|
|
|
|
// TODO: replace this with the new ClosureSpecialization pass once we have OSSA at this point in the pipeline
|
|
P.addClosureSpecializer();
|
|
|
|
// Do the second stack promotion on low-level SIL.
|
|
P.addStackPromotion();
|
|
|
|
// 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);
|
|
|
|
P.addComputeEscapeEffects();
|
|
P.addComputeSideEffects();
|
|
|
|
// 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);
|
|
|
|
// The NamedReturnValueOptimization shouldn't be done before serialization.
|
|
// For details see the comment for `namedReturnValueOptimization`.
|
|
P.addNamedReturnValueOptimization();
|
|
|
|
P.addDeadObjectElimination();
|
|
P.addObjectOutliner();
|
|
P.addDeadStoreElimination();
|
|
P.addDCE();
|
|
P.addSimplification();
|
|
P.addInitializeStaticGlobals();
|
|
|
|
// dead-store-elimination can expose opportunities for dead object elimination.
|
|
P.addDeadObjectElimination();
|
|
|
|
// We've done a lot of optimizations on this function, attempt to FSO.
|
|
P.addFunctionSignatureOpts();
|
|
P.addComputeEscapeEffects();
|
|
P.addComputeSideEffects();
|
|
}
|
|
|
|
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.addLateDeadFunctionAndGlobalElimination();
|
|
|
|
// 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.addLoopInvariantCodeMotion();
|
|
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.addComputeEscapeEffects();
|
|
P.addComputeSideEffects();
|
|
P.addStackPromotion();
|
|
|
|
// Optimize overflow checks.
|
|
P.addRedundantOverflowCheckRemoval();
|
|
P.addMergeCondFails();
|
|
|
|
// Remove dead code.
|
|
P.addDCE();
|
|
P.addSILCombine();
|
|
P.addSimplifyCFG();
|
|
P.addStripObjectHeaders();
|
|
|
|
// 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.addOptimizeHopToExecutor();
|
|
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.addLoopInvariantCodeMotion();
|
|
|
|
// Only has an effect if the -assume-single-thread option is specified.
|
|
if (P.getOptions().AssumeSingleThreaded) {
|
|
P.addAssumeSingleThreaded();
|
|
}
|
|
|
|
// Emits remarks on all functions with @_assemblyVision attribute.
|
|
P.addAssemblyVisionRemarkGenerator();
|
|
|
|
// In optimized builds, do the inter-procedural analysis in a module pass.
|
|
P.addStackProtection();
|
|
|
|
// FIXME: rdar://72935649 (Miscompile on combining PruneVTables with WMO)
|
|
// 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("Lowering");
|
|
// Lower thunks.
|
|
P.addThunkLowering();
|
|
P.addLowerHopToActor(); // FIXME: earlier for more opportunities?
|
|
P.addOwnershipModelEliminator();
|
|
P.addAlwaysEmitConformanceMetadataPreservation();
|
|
P.addIRGenPrepare();
|
|
|
|
return P;
|
|
}
|
|
|
|
SILPassPipelinePlan
|
|
SILPassPipelinePlan::getIRGenPreparePassPipeline(const SILOptions &Options) {
|
|
SILPassPipelinePlan P(Options);
|
|
P.startPipeline("IRGen Preparation");
|
|
// Insert SIL passes to run during IRGen.
|
|
/*
|
|
// Simplify partial_apply instructions by expanding box construction into
|
|
// component operations.
|
|
P.addPartialApplySimplification();
|
|
*/
|
|
// Hoist generic alloc_stack instructions to the entry block to enable better
|
|
// llvm-ir generation for dynamic alloca instructions.
|
|
P.addAllocStackHoisting();
|
|
// Change large loadable types to be passed indirectly across function
|
|
// boundaries as required by the ABI.
|
|
P.addLoadableByAddress();
|
|
|
|
if (Options.EnablePackMetadataStackPromotion) {
|
|
// Insert marker instructions indicating where on-stack pack metadata
|
|
// deallocation must occur.
|
|
//
|
|
// No code motion may occur after this pass: alloc_pack_metadata must
|
|
// directly precede the instruction on behalf of which metadata will
|
|
// actually be emitted (e.g. apply).
|
|
P.addPackMetadataMarkerInserter();
|
|
}
|
|
|
|
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 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.
|
|
addHighLevelFunctionPipeline(P);
|
|
|
|
// Then if we were asked to stop optimization before lowering OSSA (causing us
|
|
// to exit early from addHighLevelFunctionPipeline), exit early.
|
|
if (P.getOptions().StopOptimizationBeforeLoweringOwnership)
|
|
return P;
|
|
|
|
addHighLevelModulePipeline(P);
|
|
|
|
// Run one last copy propagation/semantic arc opts run before serialization/us
|
|
// lowering ownership.
|
|
if (P.getOptions().CopyPropagation != CopyPropagationOption::Off) {
|
|
P.addCopyPropagation();
|
|
}
|
|
P.addSemanticARCOpts();
|
|
P.addCopyToBorrowOptimization();
|
|
|
|
P.addCrossModuleOptimization();
|
|
|
|
// 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();
|
|
|
|
if (Options.StopOptimizationAfterSerialization)
|
|
return P;
|
|
|
|
if (SILPrintFinalOSSAModule) {
|
|
addModulePrinterPipeline(P, "SIL Print Final OSSA Module");
|
|
}
|
|
P.addAutodiffClosureSpecialization();
|
|
|
|
P.addOwnershipModelEliminator();
|
|
|
|
// After serialization run the function pass pipeline to iteratively lower
|
|
// high-level constructs like @_semantics calls.
|
|
addMidLevelFunctionPipeline(P);
|
|
|
|
P.addAutodiffClosureSpecialization();
|
|
|
|
// 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 -sil-based-debuginfo option is specified.
|
|
addSILDebugInfoGeneratorPipeline(P);
|
|
|
|
// Call the CFG viewer.
|
|
if (SILViewCFG) {
|
|
addCFGPrinterPipeline(P, "SIL Before IRGen View CFG");
|
|
}
|
|
if (SILPrintFinalModule) {
|
|
addModulePrinterPipeline(P, "SIL Print Final Module");
|
|
}
|
|
|
|
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 Mandatory Optimizations");
|
|
P.addForEachLoopUnroll();
|
|
|
|
// TODO: MandatoryARCOpts should be subsumed by CopyPropagation. There should
|
|
// be no need to run another analysis of copies at -Onone.
|
|
P.addMandatoryARCOpts();
|
|
|
|
// Create pre-specializations.
|
|
// This needs to run pre-serialization because it needs to identify native
|
|
// inlinable functions from imported ones.
|
|
P.addOnonePrespecializations();
|
|
|
|
// For embedded Swift: CMO is used to serialize libraries.
|
|
P.addCrossModuleOptimization();
|
|
|
|
// First serialize the SIL if we are asked to.
|
|
P.startPipeline("Serialization");
|
|
P.addSerializeSILPass();
|
|
|
|
if (Options.StopOptimizationAfterSerialization)
|
|
return P;
|
|
|
|
// Now that we have serialized, propagate debug info.
|
|
P.addMovedAsyncVarDebugInfoPropagator();
|
|
|
|
// Even at Onone it's important to remove copies of structs, especially if they are large.
|
|
P.addMandatoryTempRValueElimination();
|
|
|
|
// If we are asked to stop optimizing before lowering ownership, do so now.
|
|
if (P.Options.StopOptimizationBeforeLoweringOwnership)
|
|
return P;
|
|
|
|
P.addOwnershipModelEliminator();
|
|
|
|
// Finally perform some small transforms.
|
|
P.startPipeline("Rest of Onone");
|
|
|
|
// There are not pre-specialized parts of the stdlib in embedded mode.
|
|
if (!Options.EmbeddedSwift) {
|
|
P.addUsePrespecialized();
|
|
}
|
|
|
|
// Has only an effect if the -assume-single-thread option is specified.
|
|
if (P.getOptions().AssumeSingleThreaded) {
|
|
P.addAssumeSingleThreaded();
|
|
}
|
|
|
|
// In Onone builds, do a function-local analysis in a function pass.
|
|
P.addFunctionStackProtection();
|
|
|
|
// This is mainly there to optimize `Builtin.isConcrete`, which must not be
|
|
// constant folded before any generic specialization.
|
|
P.addLateOnoneSimplification();
|
|
|
|
if (Options.EmbeddedSwift) {
|
|
// For embedded Swift: Remove all unspecialized functions. This is important
|
|
// to avoid having debuginfo references to these functions that we don't
|
|
// want to emit in IRGen.
|
|
P.addLateDeadFunctionAndGlobalElimination();
|
|
}
|
|
|
|
P.addCleanupDebugSteps();
|
|
|
|
// Has only an effect if the -sil-based-debuginfo option is specified.
|
|
P.addSILDebugInfoGenerator();
|
|
|
|
if (SILPrintFinalModule) {
|
|
addModulePrinterPipeline(P, "SIL Print Final Module");
|
|
}
|
|
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;
|
|
}
|