-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathPassPipeline.cpp
1265 lines (1043 loc) · 42.4 KB
/
PassPipeline.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//===--- 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"));
//===----------------------------------------------------------------------===//
// 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.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();
P.addBooleanLiteralFolding();
addDefiniteInitialization(P);
P.addAddressLowering();
// Before we run later semantic optimizations, eliminate simple functions that
// we specialized to ensure that we do not emit diagnostics twice.
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::On) {
P.addDiagnoseLifetimeIssues();
}
// Canonical swift requires all non cond_br critical edges to be split.
P.addSplitNonCondBrCriticalEdges();
P.addMandatoryPerformanceOptimizations();
P.addOnoneSimplification();
P.addInitializeStaticGlobals();
// 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.addPerformanceDiagnostics();
}
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");
}
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.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
// 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();
}
// 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();
// 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.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();
// 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.addHighLevelLICM();
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();
// 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();
if (!P.getOptions().EnableOSSAModules) {
if (P.getOptions().StopOptimizationBeforeLoweringOwnership)
return;
if (SILPrintFinalOSSAModule) {
addModulePrinterPipeline(P, "SIL Print Final OSSA Module");
}
P.addNonTransparentFunctionOwnershipModelEliminator();
}
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().EnableOSSAModules) {
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.addTempRValueOpt();
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.
if (P.getOptions().EnableOSSAModules) {
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");
// Verify AccessStorage once in OSSA before optimizing.
#ifndef NDEBUG
P.addAccessPathVerification();
#endif
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.addTempRValueOpt();
// Cleanup after SILGen: remove unneeded borrows/copies.
if (P.getOptions().CopyPropagation == CopyPropagationOption::On) {
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.addTempRValueOpt();
// 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.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.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.addCapturePropagation();
// Specialize closure.
if (P.getOptions().EnableExperimentalSwiftBasedClosureSpecialization) {
P.addExperimentalSwiftBasedClosureSpecialization();
} else {
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);
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.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.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.addLICM();
// Verify AccessStorage once again after optimizing and lowering OSSA.
#ifndef NDEBUG
// Temporarily disabled because it triggers a false alarm when building
// SwiftDocC on linux: rdar://141270464
// TODO: re-enable when the problem is fixed.
// P.addAccessPathVerification();
#endif
// 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().EnableOSSAModules) {
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