Skip to content

Fix the mid-level function-pass pipeline #31424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions include/swift/SILOptimizer/PassManager/PassManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,16 +261,7 @@ class SILPassManager {
}
}

void executePassPipelinePlan(const SILPassPipelinePlan &Plan) {
for (const SILPassPipeline &Pipeline : Plan.getPipelines()) {
setStageName(Pipeline.Name);
resetAndRemoveTransformations();
for (PassKind Kind : Plan.getPipelinePasses(Pipeline)) {
addPass(Kind);
}
execute();
}
}
void executePassPipelinePlan(const SILPassPipelinePlan &Plan);

private:
void execute();
Expand Down
9 changes: 6 additions & 3 deletions include/swift/SILOptimizer/PassManager/PassPipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class SILPassPipelinePlan final {

void print(llvm::raw_ostream &os);

void startPipeline(StringRef Name = "");
void startPipeline(StringRef Name = "", bool isFunctionPassPipeline = false);
using PipelineKindIterator = decltype(Kinds)::const_iterator;
using PipelineKindRange = iterator_range<PipelineKindIterator>;
iterator_range<PipelineKindIterator>
Expand Down Expand Up @@ -128,6 +128,7 @@ struct SILPassPipeline final {
unsigned ID;
StringRef Name;
unsigned KindOffset;
bool isFunctionPassPipeline;

friend bool operator==(const SILPassPipeline &lhs,
const SILPassPipeline &rhs) {
Expand All @@ -145,9 +146,11 @@ struct SILPassPipeline final {
}
};

inline void SILPassPipelinePlan::startPipeline(StringRef Name) {
inline void SILPassPipelinePlan::
startPipeline(StringRef Name, bool isFunctionPassPipeline) {
PipelineStages.push_back(SILPassPipeline{
unsigned(PipelineStages.size()), Name, unsigned(Kinds.size())});
unsigned(PipelineStages.size()), Name, unsigned(Kinds.size()),
isFunctionPassPipeline});
}

inline SILPassPipelinePlan::PipelineKindRange
Expand Down
13 changes: 13 additions & 0 deletions lib/SILOptimizer/PassManager/PassManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,19 @@ void SILPassManager::runModulePass(unsigned TransIdx) {
}
}

void SILPassManager::executePassPipelinePlan(const SILPassPipelinePlan &Plan) {
for (const SILPassPipeline &Pipeline : Plan.getPipelines()) {
setStageName(Pipeline.Name);
resetAndRemoveTransformations();
for (PassKind Kind : Plan.getPipelinePasses(Pipeline)) {
addPass(Kind);
assert(!Pipeline.isFunctionPassPipeline
|| isa<SILFunctionTransform>(Transformations.back()));
}
execute();
}
}

void SILPassManager::execute() {
const SILOptions &Options = getOptions();

Expand Down
122 changes: 81 additions & 41 deletions lib/SILOptimizer/PassManager/PassPipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,12 @@ void addHighLevelLoopOptPasses(SILPassPipelinePlan &P) {
P.addSwiftArrayPropertyOpt();
}

// Perform classic SSA optimizations.
void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) {
// Primary FunctionPass pipeline.
//
// Inserting a module passes within this pipeline would break the pipeline
// restart functionality.
void addFunctionPasses(SILPassPipelinePlan &P,
OptimizationLevelKind OpLevel) {
// Promote box allocations to stack allocations.
P.addAllocBoxToStack();

Expand All @@ -289,12 +293,29 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) {

// Cleanup, which is important if the inliner has restarted the pass pipeline.
P.addPerformanceConstantPropagation();
P.addSimplifyCFG();
P.addSILCombine();

// Mainly for Array.append(contentsOf) optimization.
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).
Expand All @@ -310,22 +331,6 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) {
P.addEarlyInliner();
break;
case OptimizationLevelKind::MidLevel:
P.addGlobalOpt();
P.addLetPropertiesOpt();
// It is important to serialize before any of the @_semantics
// functions are inlined, because otherwise the information about
// uses of such functions inside the module is lost,
// which reduces the ability of the compiler to optimize clients
// importing this module.
P.addSerializeSILPass();

// Now strip any transparent functions that still have ownership.
if (P.getOptions().StripOwnershipAfterSerialization)
P.addOwnershipModelEliminator();

if (P.getOptions().StopOptimizationAfterSerialization)
return;

// Does inline semantics-functions (except "availability"), but not
// global-init functions.
P.addPerfInliner();
Expand Down Expand Up @@ -446,30 +451,58 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) {
P.addCMOSerializeSILPass();
}

static void addHighLevelEarlyLoopOptPipeline(SILPassPipelinePlan &P) {
P.startPipeline("HighLevel+EarlyLoopOpt");
// FIXME: update this to be a function pass.
// The "high-level" pipeline serves two purposes:
//
// 1. Optimize the standard library Swift module prior to serialization. This
// reduces the amount of work during compilation of all non-stdlib clients.
//
// 2. Optimize caller functions before inlining semantic calls inside
// callees. This provides more precise escape analysis and side effect analysis
// of callee arguments.
static void addHighLevelFunctionPipeline(SILPassPipelinePlan &P) {
P.startPipeline("HighLevel,Function+EarlyLoopOpt");
// FIXME: update EagerSpecializer to be a function pass!
P.addEagerSpecializer();
addSSAPasses(P, OptimizationLevelKind::HighLevel);
addFunctionPasses(P, OptimizationLevelKind::HighLevel);

addHighLevelLoopOptPasses(P);
}

static void addMidModulePassesStackPromotePassPipeline(SILPassPipelinePlan &P) {
P.startPipeline("MidModulePasses+StackPromote");
// After "high-level" function passes have processed the entire call tree, run
// one round of module passes.
static void addHighLevelModulePipeline(SILPassPipelinePlan &P) {
P.startPipeline("HighLevel,Module+StackPromote");
P.addDeadFunctionElimination();
P.addPerformanceSILLinker();
P.addDeadObjectElimination();
P.addGlobalPropertyOpt();

// Do the first stack promotion on high-level SIL.
// Do the first stack promotion on high-level SIL before serialization.
//
// FIXME: why does StackPromotion need to run in the module pipeline?
P.addStackPromotion();

P.addGlobalOpt();
P.addLetPropertiesOpt();
}

static void addSerializePipeline(SILPassPipelinePlan &P) {
P.startPipeline("Serialize");
// It is important to serialize before any of the @_semantics
// functions are inlined, because otherwise the information about
// uses of such functions inside the module is lost,
// which reduces the ability of the compiler to optimize clients
// importing this module.
P.addSerializeSILPass();

// Strip any transparent functions that still have ownership.
if (P.getOptions().StripOwnershipAfterSerialization)
P.addOwnershipModelEliminator();
}

static bool addMidLevelPassPipeline(SILPassPipelinePlan &P) {
P.startPipeline("MidLevel");
addSSAPasses(P, OptimizationLevelKind::MidLevel);
if (P.getOptions().StopOptimizationAfterSerialization)
return true;
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.
Expand All @@ -481,7 +514,6 @@ static bool addMidLevelPassPipeline(SILPassPipelinePlan &P) {
// Run loop unrolling after inlining and constant propagation, because loop
// trip counts may have became constant.
P.addLoopUnroll();
return false;
}

static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) {
Expand Down Expand Up @@ -532,12 +564,12 @@ static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) {
}

static void addLowLevelPassPipeline(SILPassPipelinePlan &P) {
P.startPipeline("LowLevel");
P.startPipeline("LowLevel,Function", true /*isFunctionPassPipeline*/);

// Should be after FunctionSignatureOpts and before the last inliner.
P.addReleaseDevirtualizer();

addSSAPasses(P, OptimizationLevelKind::LowLevel);
addFunctionPasses(P, OptimizationLevelKind::LowLevel);

P.addDeadObjectElimination();
P.addObjectOutliner();
Expand Down Expand Up @@ -652,13 +684,21 @@ SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) {
addPerfEarlyModulePassPipeline(P);

// Then run an iteration of the high-level SSA passes.
addHighLevelEarlyLoopOptPipeline(P);
addMidModulePassesStackPromotePassPipeline(P);
//
// FIXME: When *not* emitting a .swiftmodule, skip the high-level function
// pipeline to save compile time.
addHighLevelFunctionPipeline(P);

addHighLevelModulePipeline(P);

// Run an iteration of the mid-level SSA passes.
if (addMidLevelPassPipeline(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);

Expand Down Expand Up @@ -698,7 +738,7 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) {
P.startPipeline("Serialization");
P.addSerializeSILPass();

// And then strip ownership...
// Now strip any transparent functions that still have ownership.
if (Options.StripOwnershipAfterSerialization)
P.addOwnershipModelEliminator();

Expand Down
6 changes: 3 additions & 3 deletions test/SILOptimizer/optimizer_counters.sil
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ sil @fatalError : $@convention(thin) () -> Never

// Check that module level statistics are produced.
//
// CHECK-SIL-STATS-MODULES: module, inst, HighLevel+EarlyLoopOpt, PerformanceConstantPropagation, {{.*}}, 15, 12
// CHECK-SIL-STATS-MODULES: module, block, HighLevel+EarlyLoopOpt, SimplifyCFG, {{.*}}, 6, 3
// CHECK-SIL-STATS-MODULES: module, inst, HighLevel+EarlyLoopOpt, SimplifyCFG, {{.*}}, 12, 6
// CHECK-SIL-STATS-MODULES: module, inst, HighLevel,Function+EarlyLoopOpt, PerformanceConstantPropagation, {{.*}}, 15, 12
// CHECK-SIL-STATS-MODULES: module, block, HighLevel,Function+EarlyLoopOpt, SimplifyCFG, {{.*}}, 6, 3
// CHECK-SIL-STATS-MODULES: module, inst, HighLevel,Function+EarlyLoopOpt, SimplifyCFG, {{.*}}, 12, 6

// Check that module level statistics are produced.
//
Expand Down
3 changes: 2 additions & 1 deletion test/SILOptimizer/specialize_opaque_type_archetypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
// RUN: %target-swift-frontend -disable-availability-checking %S/Inputs/specialize_opaque_type_archetypes_3.swift -I %t -enable-library-evolution -module-name External2 -Osize -emit-module -o - | %target-sil-opt -module-name External2 | %FileCheck --check-prefix=RESILIENT %s
// RUN: %target-swift-frontend -disable-availability-checking -I %t -module-name A -enforce-exclusivity=checked -Osize -emit-sil -sil-verify-all %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize
// RUN: %target-swift-frontend -disable-availability-checking -I %t -module-name A -enforce-exclusivity=checked -enable-library-evolution -Osize -emit-sil -sil-verify-all %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize

// See radar for details - rdar://62560867
// XFAIL: *
import External
import External2
import External3
Expand Down
4 changes: 2 additions & 2 deletions test/SILOptimizer/stop_after_module.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ public func caller() {
_blackHole(inlinableFunction(20))
}

// NOTSKIPPING: *** SIL function after {{.*}}, stage MidLevel, pass {{.*}}: PerfInliner (inline)
// SKIPPING-NOT: *** SIL function after {{.*}}, stage MidLevel, pass {{.*}}: PerfInliner (inline)
// NOTSKIPPING: *** SIL function after {{.*}}, stage MidLevel,Function, pass {{.*}}: PerfInliner (inline)
// SKIPPING-NOT: *** SIL function after {{.*}}, stage MidLevel,Function, pass {{.*}}: PerfInliner (inline)