Skip to content

Commit 47fe49a

Browse files
meg-guptaatrick
andauthored
Fix the mid-level function-pass pipeline (#31424)
* Fix the mid-level pass pipeline. Module passes need to be in a separate pipeline, otherwise the pipeline restart mechanism will be broken. This makes GlobalOpt and serialization run earlier in the pipeline. There's no explicit reason for them to be run later, in the middle of a function pass pipeline. Also, pipeline boundaries, like serialization and module passes should be explicit at the the top level function that creates the pass pipelines. * SILOptimizer: Add enforcement of function-pass pipelines. Don't allow module passes to be inserted within a function pass pipeline. This silently breaks the function pipeline both interfering with analysis and the normal pipeline restart mechanism. * Add misssing pass in addFunctionPasses Co-authored-by: Andrew Trick <[email protected]>
1 parent 902949e commit 47fe49a

File tree

7 files changed

+108
-60
lines changed

7 files changed

+108
-60
lines changed

include/swift/SILOptimizer/PassManager/PassManager.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -261,16 +261,7 @@ class SILPassManager {
261261
}
262262
}
263263

264-
void executePassPipelinePlan(const SILPassPipelinePlan &Plan) {
265-
for (const SILPassPipeline &Pipeline : Plan.getPipelines()) {
266-
setStageName(Pipeline.Name);
267-
resetAndRemoveTransformations();
268-
for (PassKind Kind : Plan.getPipelinePasses(Pipeline)) {
269-
addPass(Kind);
270-
}
271-
execute();
272-
}
273-
}
264+
void executePassPipelinePlan(const SILPassPipelinePlan &Plan);
274265

275266
private:
276267
void execute();

include/swift/SILOptimizer/PassManager/PassPipeline.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class SILPassPipelinePlan final {
8888

8989
void print(llvm::raw_ostream &os);
9090

91-
void startPipeline(StringRef Name = "");
91+
void startPipeline(StringRef Name = "", bool isFunctionPassPipeline = false);
9292
using PipelineKindIterator = decltype(Kinds)::const_iterator;
9393
using PipelineKindRange = iterator_range<PipelineKindIterator>;
9494
iterator_range<PipelineKindIterator>
@@ -128,6 +128,7 @@ struct SILPassPipeline final {
128128
unsigned ID;
129129
StringRef Name;
130130
unsigned KindOffset;
131+
bool isFunctionPassPipeline;
131132

132133
friend bool operator==(const SILPassPipeline &lhs,
133134
const SILPassPipeline &rhs) {
@@ -145,9 +146,11 @@ struct SILPassPipeline final {
145146
}
146147
};
147148

148-
inline void SILPassPipelinePlan::startPipeline(StringRef Name) {
149+
inline void SILPassPipelinePlan::
150+
startPipeline(StringRef Name, bool isFunctionPassPipeline) {
149151
PipelineStages.push_back(SILPassPipeline{
150-
unsigned(PipelineStages.size()), Name, unsigned(Kinds.size())});
152+
unsigned(PipelineStages.size()), Name, unsigned(Kinds.size()),
153+
isFunctionPassPipeline});
151154
}
152155

153156
inline SILPassPipelinePlan::PipelineKindRange

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,19 @@ void SILPassManager::runModulePass(unsigned TransIdx) {
625625
}
626626
}
627627

628+
void SILPassManager::executePassPipelinePlan(const SILPassPipelinePlan &Plan) {
629+
for (const SILPassPipeline &Pipeline : Plan.getPipelines()) {
630+
setStageName(Pipeline.Name);
631+
resetAndRemoveTransformations();
632+
for (PassKind Kind : Plan.getPipelinePasses(Pipeline)) {
633+
addPass(Kind);
634+
assert(!Pipeline.isFunctionPassPipeline
635+
|| isa<SILFunctionTransform>(Transformations.back()));
636+
}
637+
execute();
638+
}
639+
}
640+
628641
void SILPassManager::execute() {
629642
const SILOptions &Options = getOptions();
630643

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 81 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,12 @@ void addHighLevelLoopOptPasses(SILPassPipelinePlan &P) {
264264
P.addSwiftArrayPropertyOpt();
265265
}
266266

267-
// Perform classic SSA optimizations.
268-
void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) {
267+
// Primary FunctionPass pipeline.
268+
//
269+
// Inserting a module passes within this pipeline would break the pipeline
270+
// restart functionality.
271+
void addFunctionPasses(SILPassPipelinePlan &P,
272+
OptimizationLevelKind OpLevel) {
269273
// Promote box allocations to stack allocations.
270274
P.addAllocBoxToStack();
271275

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

290294
// Cleanup, which is important if the inliner has restarted the pass pipeline.
291295
P.addPerformanceConstantPropagation();
292-
P.addSimplifyCFG();
293-
P.addSILCombine();
294296

295-
// Mainly for Array.append(contentsOf) optimization.
297+
addSimplifyCFGSILCombinePasses(P);
298+
296299
P.addArrayElementPropagation();
297300

301+
// Perform a round of loop/array optimization in the mid-level pipeline after
302+
// potentially inlining semantic calls, e.g. Array append. The high level
303+
// pipeline only optimizes semantic calls *after* inlining (see
304+
// addHighLevelLoopOptPasses). For example, the high-level pipeline may
305+
// perform ArrayElementPropagation and after inlining a level of semantic
306+
// calls, the mid-level pipeline may handle uniqueness hoisting. Do this as
307+
// late as possible before inlining because it must run between runs of the
308+
// inliner when the pipeline restarts.
309+
if (OpLevel == OptimizationLevelKind::MidLevel) {
310+
P.addHighLevelLICM();
311+
P.addArrayCountPropagation();
312+
P.addABCOpt();
313+
P.addDCE();
314+
P.addCOWArrayOpts();
315+
P.addDCE();
316+
P.addSwiftArrayPropertyOpt();
317+
}
318+
298319
// Run the devirtualizer, specializer, and inliner. If any of these
299320
// makes a change we'll end up restarting the function passes on the
300321
// current function (after optimizing any new callees).
@@ -310,22 +331,6 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) {
310331
P.addEarlyInliner();
311332
break;
312333
case OptimizationLevelKind::MidLevel:
313-
P.addGlobalOpt();
314-
P.addLetPropertiesOpt();
315-
// It is important to serialize before any of the @_semantics
316-
// functions are inlined, because otherwise the information about
317-
// uses of such functions inside the module is lost,
318-
// which reduces the ability of the compiler to optimize clients
319-
// importing this module.
320-
P.addSerializeSILPass();
321-
322-
// Now strip any transparent functions that still have ownership.
323-
if (P.getOptions().StripOwnershipAfterSerialization)
324-
P.addOwnershipModelEliminator();
325-
326-
if (P.getOptions().StopOptimizationAfterSerialization)
327-
return;
328-
329334
// Does inline semantics-functions (except "availability"), but not
330335
// global-init functions.
331336
P.addPerfInliner();
@@ -446,30 +451,58 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) {
446451
P.addCMOSerializeSILPass();
447452
}
448453

449-
static void addHighLevelEarlyLoopOptPipeline(SILPassPipelinePlan &P) {
450-
P.startPipeline("HighLevel+EarlyLoopOpt");
451-
// FIXME: update this to be a function pass.
454+
// The "high-level" pipeline serves two purposes:
455+
//
456+
// 1. Optimize the standard library Swift module prior to serialization. This
457+
// reduces the amount of work during compilation of all non-stdlib clients.
458+
//
459+
// 2. Optimize caller functions before inlining semantic calls inside
460+
// callees. This provides more precise escape analysis and side effect analysis
461+
// of callee arguments.
462+
static void addHighLevelFunctionPipeline(SILPassPipelinePlan &P) {
463+
P.startPipeline("HighLevel,Function+EarlyLoopOpt");
464+
// FIXME: update EagerSpecializer to be a function pass!
452465
P.addEagerSpecializer();
453-
addSSAPasses(P, OptimizationLevelKind::HighLevel);
466+
addFunctionPasses(P, OptimizationLevelKind::HighLevel);
467+
454468
addHighLevelLoopOptPasses(P);
455469
}
456470

457-
static void addMidModulePassesStackPromotePassPipeline(SILPassPipelinePlan &P) {
458-
P.startPipeline("MidModulePasses+StackPromote");
471+
// After "high-level" function passes have processed the entire call tree, run
472+
// one round of module passes.
473+
static void addHighLevelModulePipeline(SILPassPipelinePlan &P) {
474+
P.startPipeline("HighLevel,Module+StackPromote");
459475
P.addDeadFunctionElimination();
460476
P.addPerformanceSILLinker();
461477
P.addDeadObjectElimination();
462478
P.addGlobalPropertyOpt();
463479

464-
// Do the first stack promotion on high-level SIL.
480+
// Do the first stack promotion on high-level SIL before serialization.
481+
//
482+
// FIXME: why does StackPromotion need to run in the module pipeline?
465483
P.addStackPromotion();
484+
485+
P.addGlobalOpt();
486+
P.addLetPropertiesOpt();
487+
}
488+
489+
static void addSerializePipeline(SILPassPipelinePlan &P) {
490+
P.startPipeline("Serialize");
491+
// It is important to serialize before any of the @_semantics
492+
// functions are inlined, because otherwise the information about
493+
// uses of such functions inside the module is lost,
494+
// which reduces the ability of the compiler to optimize clients
495+
// importing this module.
496+
P.addSerializeSILPass();
497+
498+
// Strip any transparent functions that still have ownership.
499+
if (P.getOptions().StripOwnershipAfterSerialization)
500+
P.addOwnershipModelEliminator();
466501
}
467502

468-
static bool addMidLevelPassPipeline(SILPassPipelinePlan &P) {
469-
P.startPipeline("MidLevel");
470-
addSSAPasses(P, OptimizationLevelKind::MidLevel);
471-
if (P.getOptions().StopOptimizationAfterSerialization)
472-
return true;
503+
static void addMidLevelFunctionPipeline(SILPassPipelinePlan &P) {
504+
P.startPipeline("MidLevel,Function", true /*isFunctionPassPipeline*/);
505+
addFunctionPasses(P, OptimizationLevelKind::MidLevel);
473506

474507
// Specialize partially applied functions with dead arguments as a preparation
475508
// for CapturePropagation.
@@ -481,7 +514,6 @@ static bool addMidLevelPassPipeline(SILPassPipelinePlan &P) {
481514
// Run loop unrolling after inlining and constant propagation, because loop
482515
// trip counts may have became constant.
483516
P.addLoopUnroll();
484-
return false;
485517
}
486518

487519
static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) {
@@ -532,12 +564,12 @@ static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) {
532564
}
533565

534566
static void addLowLevelPassPipeline(SILPassPipelinePlan &P) {
535-
P.startPipeline("LowLevel");
567+
P.startPipeline("LowLevel,Function", true /*isFunctionPassPipeline*/);
536568

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

540-
addSSAPasses(P, OptimizationLevelKind::LowLevel);
572+
addFunctionPasses(P, OptimizationLevelKind::LowLevel);
541573

542574
P.addDeadObjectElimination();
543575
P.addObjectOutliner();
@@ -652,13 +684,21 @@ SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) {
652684
addPerfEarlyModulePassPipeline(P);
653685

654686
// Then run an iteration of the high-level SSA passes.
655-
addHighLevelEarlyLoopOptPipeline(P);
656-
addMidModulePassesStackPromotePassPipeline(P);
687+
//
688+
// FIXME: When *not* emitting a .swiftmodule, skip the high-level function
689+
// pipeline to save compile time.
690+
addHighLevelFunctionPipeline(P);
691+
692+
addHighLevelModulePipeline(P);
657693

658-
// Run an iteration of the mid-level SSA passes.
659-
if (addMidLevelPassPipeline(P))
694+
addSerializePipeline(P);
695+
if (Options.StopOptimizationAfterSerialization)
660696
return P;
661697

698+
// After serialization run the function pass pipeline to iteratively lower
699+
// high-level constructs like @_semantics calls.
700+
addMidLevelFunctionPipeline(P);
701+
662702
// Perform optimizations that specialize.
663703
addClosureSpecializePassPipeline(P);
664704

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

701-
// And then strip ownership...
741+
// Now strip any transparent functions that still have ownership.
702742
if (Options.StripOwnershipAfterSerialization)
703743
P.addOwnershipModelEliminator();
704744

test/SILOptimizer/optimizer_counters.sil

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ sil @fatalError : $@convention(thin) () -> Never
1717

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

2424
// Check that module level statistics are produced.
2525
//

test/SILOptimizer/specialize_opaque_type_archetypes.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
// 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
66
// 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
77
// 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
8-
8+
// See radar for details - rdar://62560867
9+
// XFAIL: *
910
import External
1011
import External2
1112
import External3

test/SILOptimizer/stop_after_module.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ public func caller() {
1919
_blackHole(inlinableFunction(20))
2020
}
2121

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

0 commit comments

Comments
 (0)